From 340edc629e6f6f8947f703384b607d7fb0f528b8 Mon Sep 17 00:00:00 2001 From: Pierre Jaury Date: Sun, 24 Apr 2016 19:17:40 +0200 Subject: [PATCH] Implement admin and manager management --- admin/freeposte/admin/__init__.py | 3 +- admin/freeposte/admin/forms.py | 10 ++++ admin/freeposte/admin/models.py | 15 ++---- .../admin/templates/admin/admins.html | 1 - .../admin/templates/admin/create.html | 23 +++++++++ .../freeposte/admin/templates/admin/list.html | 32 ++++++++++++ .../admin/templates/domain/admins.html | 0 .../admin/templates/domain/list.html | 2 +- .../admin/templates/manager/create.html | 27 ++++++++++ .../admin/templates/manager/list.html | 36 ++++++++++++++ .../admin/templates/{admin => }/services.html | 0 admin/freeposte/admin/templates/sidebar.html | 2 +- admin/freeposte/admin/views/administrators.py | 29 ----------- admin/freeposte/admin/views/admins.py | 48 ++++++++++++++++++ admin/freeposte/admin/views/base.py | 17 ++++++- admin/freeposte/admin/views/managers.py | 49 +++++++++++++++++++ 16 files changed, 250 insertions(+), 44 deletions(-) delete mode 100644 admin/freeposte/admin/templates/admin/admins.html create mode 100644 admin/freeposte/admin/templates/admin/create.html create mode 100644 admin/freeposte/admin/templates/admin/list.html delete mode 100644 admin/freeposte/admin/templates/domain/admins.html create mode 100644 admin/freeposte/admin/templates/manager/create.html create mode 100644 admin/freeposte/admin/templates/manager/list.html rename admin/freeposte/admin/templates/{admin => }/services.html (100%) delete mode 100644 admin/freeposte/admin/views/administrators.py create mode 100644 admin/freeposte/admin/views/admins.py create mode 100644 admin/freeposte/admin/views/managers.py diff --git a/admin/freeposte/admin/__init__.py b/admin/freeposte/admin/__init__.py index 92aa50a2..179d5155 100644 --- a/admin/freeposte/admin/__init__.py +++ b/admin/freeposte/admin/__init__.py @@ -21,7 +21,8 @@ def inject_user(): # Import views from freeposte.admin.views import \ - administrators, \ + admins, \ + managers, \ base, \ aliases, \ users, \ diff --git a/admin/freeposte/admin/forms.py b/admin/freeposte/admin/forms.py index b6ad7414..dbd1eab9 100644 --- a/admin/freeposte/admin/forms.py +++ b/admin/freeposte/admin/forms.py @@ -61,3 +61,13 @@ class AliasForm(Form): destination = fields.StringField('Destination') comment = fields.StringField('Comment') submit = fields.SubmitField('Create') + + +class AdminForm(Form): + admin = fields.StringField('Admin address', [validators.Email()]) + submit = fields.SubmitField('Submit') + + +class ManagerForm(Form): + manager = fields.StringField('Manager address', [validators.Email()]) + submit = fields.SubmitField('Submit') diff --git a/admin/freeposte/admin/models.py b/admin/freeposte/admin/models.py index 28f05c60..3a4eefd9 100644 --- a/admin/freeposte/admin/models.py +++ b/admin/freeposte/admin/models.py @@ -7,15 +7,10 @@ from datetime import datetime import re -# Many-to-many association table for domain administrators -admins = db.Table('admin', +# Many-to-many association table for domain managers +managers = db.Table('manager', db.Column('domain_name', db.String(80), db.ForeignKey('domain.name')), - db.Column('user_domain_name', db.String(80)), - db.Column('user_localpart', db.String(80)), - db.ForeignKeyConstraint( - ('user_domain_name', 'user_localpart'), - ('user.domain_name', 'user.localpart') - ) + db.Column('user_address', db.String(80), db.ForeignKey('user.address')) ) @@ -34,8 +29,8 @@ class Domain(Base): """ A DNS domain that has mail addresses associated to it. """ name = db.Column(db.String(80), primary_key=True, nullable=False) - admins = db.relationship('User', secondary=admins, - backref=db.backref('admin_of'), lazy='dynamic') + managers = db.relationship('User', secondary=managers, + backref=db.backref('manager_of'), lazy='dynamic') max_users = db.Column(db.Integer, nullable=False, default=0) max_aliases = db.Column(db.Integer, nullable=False, default=0) diff --git a/admin/freeposte/admin/templates/admin/admins.html b/admin/freeposte/admin/templates/admin/admins.html deleted file mode 100644 index a500fb36..00000000 --- a/admin/freeposte/admin/templates/admin/admins.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "working.html" %} diff --git a/admin/freeposte/admin/templates/admin/create.html b/admin/freeposte/admin/templates/admin/create.html new file mode 100644 index 00000000..c974e28b --- /dev/null +++ b/admin/freeposte/admin/templates/admin/create.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block title %} +Add a global administrator +{% endblock %} + +{% block box_content %} +
+ {{ form.hidden_tag() }} + {{ macros.form_field(form.admin, id='admin') }} + {{ macros.form_field(form.submit) }} + +
+{% endblock %} diff --git a/admin/freeposte/admin/templates/admin/list.html b/admin/freeposte/admin/templates/admin/list.html new file mode 100644 index 00000000..eeaa1d12 --- /dev/null +++ b/admin/freeposte/admin/templates/admin/list.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} + +{% block title %} +Global administrators +{% endblock %} + +{% block main_action %} +Add administrator +{% endblock %} + +{% block box %} + + + + + + + + + {% for admin in admins %} + + + + + + + {% endfor %} + +
ActionsAddressCreatedLast edit
+ + {{ admin }}{{ admin.created_at }}{{ admin.updated_at or '' }}
+{% endblock %} diff --git a/admin/freeposte/admin/templates/domain/admins.html b/admin/freeposte/admin/templates/domain/admins.html deleted file mode 100644 index e69de29b..00000000 diff --git a/admin/freeposte/admin/templates/domain/list.html b/admin/freeposte/admin/templates/domain/list.html index 4547f421..ac380a54 100644 --- a/admin/freeposte/admin/templates/domain/list.html +++ b/admin/freeposte/admin/templates/domain/list.html @@ -32,7 +32,7 @@ Domain list     -   +   {{ domain.name }} {{ domain.users | count }} / {{ domain.max_users or '∞' }} diff --git a/admin/freeposte/admin/templates/manager/create.html b/admin/freeposte/admin/templates/manager/create.html new file mode 100644 index 00000000..aa70bd7d --- /dev/null +++ b/admin/freeposte/admin/templates/manager/create.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + +{% block title %} +Add a manager +{% endblock %} + +{% block subtitle %} +{{ domain }} +{% endblock %} + +{% block box_content %} +
+ {{ form.hidden_tag() }} + {{ macros.form_field(form.manager, id='manager') }} + {{ macros.form_field(form.submit) }} + +
+{% endblock %} diff --git a/admin/freeposte/admin/templates/manager/list.html b/admin/freeposte/admin/templates/manager/list.html new file mode 100644 index 00000000..2e978906 --- /dev/null +++ b/admin/freeposte/admin/templates/manager/list.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} + +{% block title %} +Manager list +{% endblock %} + +{% block subtitle %} +{{ domain.name }} +{% endblock %} + +{% block main_action %} +Add manager +{% endblock %} + +{% block box %} + + + + + + + + + {% for manager in domain.managers %} + + + + + + + {% endfor %} + +
ActionsAddressCreatedLast edit
+ + {{ manager }}{{ manager.created_at }}{{ manager.updated_at or '' }}
+{% endblock %} diff --git a/admin/freeposte/admin/templates/admin/services.html b/admin/freeposte/admin/templates/services.html similarity index 100% rename from admin/freeposte/admin/templates/admin/services.html rename to admin/freeposte/admin/templates/services.html diff --git a/admin/freeposte/admin/templates/sidebar.html b/admin/freeposte/admin/templates/sidebar.html index 7621b03a..577443af 100644 --- a/admin/freeposte/admin/templates/sidebar.html +++ b/admin/freeposte/admin/templates/sidebar.html @@ -41,7 +41,7 @@
  • - + Adminitrators
  • diff --git a/admin/freeposte/admin/views/administrators.py b/admin/freeposte/admin/views/administrators.py deleted file mode 100644 index 828e06f1..00000000 --- a/admin/freeposte/admin/views/administrators.py +++ /dev/null @@ -1,29 +0,0 @@ -from freeposte import dockercli -from freeposte.admin import app, db, models, forms, utils -from flask.ext import login as flask_login - -import os -import pprint -import flask -import json - - -@app.route('/services', methods=['GET']) -@flask_login.login_required -def services(): - utils.require_global_admin() - containers = {} - for brief in dockercli.containers(all=True): - if brief['Image'].startswith('freeposte/'): - container = dockercli.inspect_container(brief['Id']) - container['Image'] = dockercli.inspect_image(container['Image']) - name = container['Config']['Labels']['com.docker.compose.service'] - containers[name] = container - pprint.pprint(container) - return flask.render_template('admin/services.html', containers=containers) - - -@app.route('/admins', methods=['GET']) -@flask_login.login_required -def admins(): - return flask.render_template('admin/admins.html') diff --git a/admin/freeposte/admin/views/admins.py b/admin/freeposte/admin/views/admins.py new file mode 100644 index 00000000..d7b93fa0 --- /dev/null +++ b/admin/freeposte/admin/views/admins.py @@ -0,0 +1,48 @@ +from freeposte.admin import app, db, models, forms +from flask.ext import login as flask_login + +import os +import pprint +import flask +import json + + +@app.route('/admin/list', methods=['GET']) +@flask_login.login_required +def admin_list(): + admins = models.User.query.filter_by(global_admin=True) + return flask.render_template('admin/list.html', admins=admins) + + +@app.route('/admin/create', methods=['GET', 'POST']) +@flask_login.login_required +def admin_create(): + form = forms.AdminForm() + if form.validate_on_submit(): + user = models.User.query.filter_by(address=form.admin.data).first() + if user: + user.global_admin = True + db.session.add(user) + db.session.commit() + flask.flash('User %s is now admin' % user) + return flask.redirect(flask.url_for('.admin_list')) + else: + flask.flash('No such user', 'error') + return flask.render_template('admin/create.html', form=form) + + +@app.route('/admin/delete/', methods=['GET']) +@flask_login.login_required +def admin_delete(admin): + user = models.User.query.filter_by(address=admin).first() + if user: + user.global_admin = False + db.session.add(user) + db.session.commit() + flask.flash('User %s is no longer admin' % user) + return flask.redirect(flask.url_for('.admin_list')) + else: + flask.flash('No such user', 'error') + flask.flash('Alias %s deleted' % alias) + return flask.redirect( + flask.url_for('.alias_list', domain_name=alias.domain.name)) diff --git a/admin/freeposte/admin/views/base.py b/admin/freeposte/admin/views/base.py index 142fddcf..4bf7d68a 100644 --- a/admin/freeposte/admin/views/base.py +++ b/admin/freeposte/admin/views/base.py @@ -1,4 +1,5 @@ -from freeposte.admin import app, db, models, forms +from freeposte import dockercli +from freeposte.admin import app, db, models, forms, utils from flask.ext import login as flask_login import os @@ -29,3 +30,17 @@ def login(): def logout(): flask_login.logout_user() return flask.redirect(flask.url_for('.index')) + + +@app.route('/services', methods=['GET']) +@flask_login.login_required +def services(): + utils.require_global_admin() + containers = {} + for brief in dockercli.containers(all=True): + if brief['Image'].startswith('freeposte/'): + container = dockercli.inspect_container(brief['Id']) + container['Image'] = dockercli.inspect_image(container['Image']) + name = container['Config']['Labels']['com.docker.compose.service'] + containers[name] = container + return flask.render_template('services.html', containers=containers) diff --git a/admin/freeposte/admin/views/managers.py b/admin/freeposte/admin/views/managers.py new file mode 100644 index 00000000..4e5938bd --- /dev/null +++ b/admin/freeposte/admin/views/managers.py @@ -0,0 +1,49 @@ +from freeposte.admin import app, db, models, forms, utils +from flask.ext import login as flask_login + +import os +import flask +import wtforms_components + + +@app.route('/manager/list/', methods=['GET']) +@flask_login.login_required +def manager_list(domain_name): + domain = utils.get_domain_admin(domain_name) + return flask.render_template('manager/list.html', domain=domain) + + +@app.route('/manager/create/', methods=['GET', 'POST']) +@flask_login.login_required +def manager_create(domain_name): + domain = utils.get_domain_admin(domain_name) + form = forms.ManagerForm() + if form.validate_on_submit(): + user = utils.get_user(form.manager.data, admin=True) + if user in domain.managers: + flask.flash('User %s is already manager' % user, 'error') + else: + domain.managers.append(user) + db.session.add(domain) + db.session.commit() + flask.flash('User %s can now manage %s' % (user, domain.name)) + return flask.redirect( + flask.url_for('.manager_list', domain_name=domain.name)) + return flask.render_template('manager/create.html', + domain=domain, form=form) + + +@app.route('/manager/delete/', methods=['GET']) +@flask_login.login_required +def manager_delete(manager): + user = utils.get_user(manager, admin=True) + domain = utils.get_domain_admin(user.domain_name) + if user in domain.managers: + domain.managers.remove(user) + db.session.add(domain) + db.session.commit() + flask.flash('User %s can no longer manager %s' % (user, domain)) + else: + flask.flash('User %s is not manager' % user, 'error') + return flask.redirect( + flask.url_for('.manager_list', domain_name=domain.name))