diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b1bb66a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +app/static/* linguist-vendored + diff --git a/app/__init__.py b/app/__init__.py index 20dcab9..2073794 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,9 +1,17 @@ from werkzeug.contrib.fixers import ProxyFix from flask import Flask, request, session, redirect, url_for from flask_login import LoginManager -from flask_sqlalchemy import SQLAlchemy +from flask_sqlalchemy import SQLAlchemy as SA from flask_migrate import Migrate + +# subclass SQLAlchemy to enable pool_pre_ping +class SQLAlchemy(SA): + def apply_pool_defaults(self, app, options): + SA.apply_pool_defaults(self, app, options) + options["pool_pre_ping"] = True + + app = Flask(__name__) app.config.from_object('config') app.wsgi_app = ProxyFix(app.wsgi_app) diff --git a/app/models.py b/app/models.py index 3dc2414..ce0cd86 100644 --- a/app/models.py +++ b/app/models.py @@ -355,25 +355,48 @@ class User(db.Model): db.session.rollback() return False + def get_account_query(self): + """ + Get query for account to which the user is associated. + """ + return db.session.query(Account) \ + .outerjoin(AccountUser, Account.id==AccountUser.account_id) \ + .filter(AccountUser.user_id==self.id) + + def get_account(self): + """ + Get all accounts to which the user is associated. + """ + return self.get_account_query() + def get_domain_query(self): - return db.session.query(User, DomainUser, Domain) \ - .filter(User.id == self.id) \ - .filter(User.id == DomainUser.user_id) \ - .filter(Domain.id == DomainUser.domain_id) + """ + Get query for domain to which the user has access permission. + This includes direct domain permission AND permission through + account membership + """ + return db.session.query(Domain) \ + .outerjoin(DomainUser, Domain.id==DomainUser.domain_id) \ + .outerjoin(Account, Domain.account_id==Account.id) \ + .outerjoin(AccountUser, Account.id==AccountUser.account_id) \ + .filter(db.or_(DomainUser.user_id==User.id, AccountUser.user_id==User.id)) \ + .filter(User.id==self.id) def get_domain(self): """ Get domains which user has permission to access """ - return [q[2] for q in self.get_domain_query()] + return self.get_domain_query() def delete(self): """ Delete a user """ - # revoke all user privileges first + # revoke all user privileges and account associations first self.revoke_privilege() + for a in self.get_account(): + a.revoke_privileges_by_id(self.id) try: User.query.filter(User.username == self.username).delete() @@ -426,6 +449,161 @@ class User(db.Model): return False +class Account(db.Model): + __tablename__ = 'account' + id = db.Column(db.Integer, primary_key = True) + name = db.Column(db.String(40), index=True, unique=True, nullable=False) + description = db.Column(db.String(128)) + contact = db.Column(db.String(128)) + mail = db.Column(db.String(128)) + domains = db.relationship("Domain", back_populates="account") + + def __init__(self, name=None, description=None, contact=None, mail=None): + self.name = name + self.description = description + self.contact = contact + self.mail = mail + + if self.name is not None: + self.name = ''.join(c for c in self.name.lower() if c in "abcdefghijklmnopqrstuvwxyz0123456789") + + def __repr__(self): + return ''.format(self.name) + + def get_name_by_id(self, account_id): + """ + Convert account_id to account_name + """ + account = Account.query.filter(Account.id == account_id).first() + if account is None: + return '' + + return account.name + + def get_id_by_name(self, account_name): + """ + Convert account_name to account_id + """ + account = Account.query.filter(Account.name == account_name).first() + if account is None: + return None + + return account.id + + def unassociate_domains(self): + """ + Remove associations to this account from all domains + """ + account = Account.query.filter(Account.name == self.name).first() + for domain in account.domains: + domain.assoc_account(None) + + def create_account(self): + """ + Create a new account + """ + # Sanity check - account name + if self.name == "": + return {'status': False, 'msg': 'No account name specified'} + + # check that account name is not already used + account = Account.query.filter(Account.name == self.name).first() + if account: + return {'status': False, 'msg': 'Account already exists'} + + db.session.add(self) + db.session.commit() + return {'status': True, 'msg': 'Account created successfully'} + + def update_account(self): + """ + Update an existing account + """ + # Sanity check - account name + if self.name == "": + return {'status': False, 'msg': 'No account name specified'} + + # read account and check that it exists + account = Account.query.filter(Account.name == self.name).first() + if not account: + return {'status': False, 'msg': 'Account does not exist'} + + account.description = self.description + account.contact = self.contact + account.mail = self.mail + + db.session.commit() + return {'status': True, 'msg': 'Account updated successfully'} + + def delete_account(self): + """ + Delete an account + """ + # unassociate all domains and users first + self.unassociate_domains() + self.grant_privileges([]) + + try: + Account.query.filter(Account.name == self.name).delete() + db.session.commit() + return True + + except: + db.session.rollback() + logging.error('Cannot delete account {0} from DB'.format(self.username)) + return False + + def get_user(self): + """ + Get users (id) associated with this account + """ + user_ids = [] + query = db.session.query(AccountUser, Account).filter(User.id==AccountUser.user_id).filter(Account.id==AccountUser.account_id).filter(Account.name==self.name).all() + for q in query: + user_ids.append(q[0].user_id) + return user_ids + + def grant_privileges(self, new_user_list): + """ + Reconfigure account_user table + """ + account_id = self.get_id_by_name(self.name) + + account_user_ids = self.get_user() + new_user_ids = [u.id for u in User.query.filter(User.username.in_(new_user_list)).all()] if new_user_list else [] + + removed_ids = list(set(account_user_ids).difference(new_user_ids)) + added_ids = list(set(new_user_ids).difference(account_user_ids)) + + try: + for uid in removed_ids: + AccountUser.query.filter(AccountUser.user_id == uid).filter(AccountUser.account_id==account_id).delete() + db.session.commit() + except: + db.session.rollback() + logging.error('Cannot revoke user privielges on account {0}'.format(self.name)) + + try: + for uid in added_ids: + au = AccountUser(account_id, uid) + db.session.add(au) + db.session.commit() + except: + db.session.rollback() + logging.error('Cannot grant user privileges to account {0}'.format(self.name)) + + def revoke_privileges_by_id(self, user_id): + """ + Remove a single user from prigilege list based on user_id + """ + new_uids = [u for u in self.get_user() if u != user_id] + users = [] + for uid in new_uids: + users.append(User(id=uid).get_user_info_by_id().username) + + self.grant_privileges(users) + + class Role(db.Model): id = db.Column(db.Integer, primary_key = True) name = db.Column(db.String(64), index=True, unique=True) @@ -446,6 +624,7 @@ class Role(db.Model): def __repr__(self): return ''.format(self.name) + class DomainSetting(db.Model): __tablename__ = 'domain_setting' id = db.Column(db.Integer, primary_key = True) @@ -485,9 +664,11 @@ class Domain(db.Model): notified_serial = db.Column(db.Integer) last_check = db.Column(db.Integer) dnssec = db.Column(db.Integer) + account_id = db.Column(db.Integer, db.ForeignKey('account.id')) + account = db.relationship("Account", back_populates="domains") settings = db.relationship('DomainSetting', back_populates='domain') - def __init__(self, id=None, name=None, master=None, type='NATIVE', serial=None, notified_serial=None, last_check=None, dnssec=None): + def __init__(self, id=None, name=None, master=None, type='NATIVE', serial=None, notified_serial=None, last_check=None, dnssec=None, account_id=None): self.id = id self.name = name self.master = master @@ -496,6 +677,7 @@ class Domain(db.Model): self.notified_serial = notified_serial self.last_check = last_check self.dnssec = dnssec + self.account_id = account_id def __repr__(self): return ''.format(self.name) @@ -575,6 +757,7 @@ class Domain(db.Model): # update/add new domain for data in jdata: + account_id = Account().get_id_by_name(data['account']) d = dict_db_domain.get(data['name'].rstrip('.'), None) changed = False if d: @@ -584,7 +767,8 @@ class Domain(db.Model): or d.serial != data['serial'] or d.notified_serial != data['notified_serial'] or d.last_check != ( 1 if data['last_check'] else 0 ) - or d.dnssec != data['dnssec'] ): + or d.dnssec != data['dnssec'] + or d.account_id != account_id ): d.master = str(data['masters']) d.type = data['kind'] @@ -592,6 +776,7 @@ class Domain(db.Model): d.notified_serial = data['notified_serial'] d.last_check = 1 if data['last_check'] else 0 d.dnssec = 1 if data['dnssec'] else 0 + d.account_id = account_id changed = True else: @@ -604,19 +789,20 @@ class Domain(db.Model): d.notified_serial = data['notified_serial'] d.last_check = data['last_check'] d.dnssec = 1 if data['dnssec'] else 0 + d.account_id = account_id db.session.add(d) changed = True if changed: try: db.session.commit() - except: + except Exception as e: db.session.rollback() return {'status': 'ok', 'msg': 'Domain table has been updated successfully'} except Exception as e: logging.error('Can not update domain table. Error: {0}'.format(e)) return {'status': 'error', 'msg': 'Can not update domain table'} - def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[]): + def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[], account_name=None): """ Add a domain to power dns """ @@ -638,7 +824,8 @@ class Domain(db.Model): "kind": domain_type, "masters": domain_master_ips, "nameservers": domain_ns, - "soa_edit_api": soa_edit_api + "soa_edit_api": soa_edit_api, + "account": account_name } try: @@ -647,6 +834,7 @@ class Domain(db.Model): logging.error(jdata['error']) return {'status': 'error', 'msg': jdata['error']} else: + self.update() logging.info('Added domain {0} successfully'.format(domain_name)) return {'status': 'ok', 'msg': 'Added domain successfully'} except Exception as e: @@ -798,7 +986,6 @@ class Domain(db.Model): db.session.rollback() logging.error('Cannot grant user privielges to domain {0}'.format(self.name)) - def update_from_master(self, domain_name): """ Update records from Master DNS server @@ -900,6 +1087,60 @@ class Domain(db.Model): else: return {'status': 'error', 'msg': 'This domain doesnot exist'} + def assoc_account(self, account_id): + """ + Associate domain with a domain, specified by account id + """ + domain_name = self.name + + # Sanity check - domain name + if domain_name == "": + return {'status': False, 'msg': 'No domain name specified'} + + # read domain and check that it exists + domain = Domain.query.filter(Domain.name == domain_name).first() + if not domain: + return {'status': False, 'msg': 'Domain does not exist'} + + headers = {} + headers['X-API-Key'] = PDNS_API_KEY + + account_name = Account().get_name_by_id(account_id) + + post_data = { + "account": account_name + } + + try: + jdata = utils.fetch_json( + urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain_name)), headers=headers, + method='PUT', data=post_data) + + if 'error' in jdata.keys(): + logging.error(jdata['error']) + return {'status': 'error', 'msg': jdata['error']} + + else: + self.update() + logging.info('account changed for domain {0} successfully'.format(domain_name)) + return {'status': 'ok', 'msg': 'account changed successfully'} + + except Exception as e: + logging.debug(e) + logging.debug(traceback.format_exc()) + logging.error('Cannot change account for domain {0}'.format(domain_name)) + return {'status': 'error', 'msg': 'Cannot change account for this domain.'} + + return {'status': True, 'msg': 'Domain association successful'} + + def get_account(self): + """ + Get current account associated with this domain + """ + domain = Domain.query.filter(Domain.name == self.name).first() + + return domain.account + class DomainUser(db.Model): __tablename__ = 'domain_user' @@ -915,6 +1156,20 @@ class DomainUser(db.Model): return ''.format(self.domain_id, self.user_id) +class AccountUser(db.Model): + __tablename__ = 'account_user' + id = db.Column(db.Integer, primary_key = True) + account_id = db.Column(db.Integer, db.ForeignKey('account.id'), nullable = False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable = False) + + def __init__(self, account_id, user_id): + self.account_id = account_id + self.user_id = user_id + + def __repr__(self): + return ''.format(self.account_id, self.user_id) + + class Record(object): """ This is not a model, it's just an object diff --git a/app/templates/admin_editaccount.html b/app/templates/admin_editaccount.html new file mode 100644 index 0000000..1f31650 --- /dev/null +++ b/app/templates/admin_editaccount.html @@ -0,0 +1,118 @@ +{% extends "base.html" %} +{% block title %}DNS Control Panel - Edit Account{% endblock %} + +{% block dashboard_stat %} + +
+

+ Account + {% if create %}New account{% else %}{{ account.name }}{% endif %} +

+ +
+{% endblock %} + +{% block content %} +
+
+
+
+
+

{% if create %}Add{% else %}Edit{% endif %} account

+
+ + +
+ +
+ {% if error %} +
+ +

Error!

+ {{ error }} +
+ {{ error }} + {% endif %} +
+ + + + {% if invalid_accountname %} + Cannot be blank and must only contain alphanumeric characters. + {% elif duplicate_accountname %} + Account name already in use. + {% endif %} +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+

Access Control

+
+
+

Users on the right have access to manage records in all domains + associated with the account.

+

Click on users to move between columns.

+
+ +
+
+ +
+
+
+
+
+
+

Help with creating a new account

+
+
+

+ An account allows grouping of domains belonging to a particular entity, such as a customer or department.
+ A domain can be assigned to an account upon domain creation or through the domain administration page. +

+

Fill in all the fields to the in the form to the left.

+

+ Name is an account identifier. It will be stored as all lowercase letters (no spaces, special characters etc).
+ Description is a user friendly name for this account.
+ Contact person is the name of a contact person at the account.
+ Mail Address is an e-mail address for the contact person. +

+
+
+
+
+
+{% endblock %} +{% block extrascripts %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/admin_manageaccount.html b/app/templates/admin_manageaccount.html new file mode 100644 index 0000000..7af5125 --- /dev/null +++ b/app/templates/admin_manageaccount.html @@ -0,0 +1,127 @@ +{% extends "base.html" %} {% block title %} +DNS Control Panel - Account Management +{% endblock %} {% block dashboard_stat %} +
+

+ Accounts Manage accounts +

+ +
+{% endblock %} {% block content %} +
+
+
+
+
+

Account Management

+
+ +
+ + + + + + + + + + + + {% for account in accounts %} + + + + + + + + {% endfor %} + +
NameDescriptionContactMailAction
{{ account.name }}{{ account.description }}{{ account.contact }}{{ account.mail }} + + +
+
+ +
+ +
+ +
+ +
+{% endblock %} +{% block extrascripts %} + +{% endblock %} +{% block modals %} + +{% endblock %} diff --git a/app/templates/base.html b/app/templates/base.html index 1ba7b56..e1d1451 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -129,6 +129,7 @@
  • Admin Console
  • Domain Templates
  • Users
  • +
  • Accounts
  • History
  • Settings
  • {% endif %} diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index fa77882..6ce2180 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -134,6 +134,7 @@ Type Serial Master + Account Action @@ -178,6 +179,10 @@ "lengthChange" : true, "searching" : true, "ordering" : true, + "columnDefs": [ + { "orderable": false, "targets": [-1] } + {% if current_user.role.name != 'Administrator' %},{ "visible": false, "targets": [-2] }{% endif %} + ], "processing" : true, "serverSide" : true, "ajax" : "{{ url_for('dashboard_domains') }}", diff --git a/app/templates/dashboard_domain.html b/app/templates/dashboard_domain.html index 2834207..6d42edb 100644 --- a/app/templates/dashboard_domain.html +++ b/app/templates/dashboard_domain.html @@ -22,6 +22,12 @@ {% if domain.master == '[]'%}N/A{% else %}{{ domain.master|display_master_name }}{% endif %} {% endmacro %} +{% macro account(domain) %} + {% if current_user.role.name =='Administrator' %} + {% if domain.account_description != "" %}{{ domain.account.description }} {% endif %}[{{ domain.account.name }}] + {% endif %} +{% endmacro %} + {% macro actions(domain) %} {% if current_user.role.name =='Administrator' %} diff --git a/app/templates/domain_add.html b/app/templates/domain_add.html index d64beb1..64ed76c 100644 --- a/app/templates/domain_add.html +++ b/app/templates/domain_add.html @@ -31,6 +31,12 @@
    +
    diff --git a/app/templates/domain_management.html b/app/templates/domain_management.html index bf35849..aa99915 100644 --- a/app/templates/domain_management.html +++ b/app/templates/domain_management.html @@ -71,6 +71,32 @@
    +
    +
    +
    +
    +

    Account

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    diff --git a/app/views.py b/app/views.py index d7b15a1..ebb5b2d 100644 --- a/app/views.py +++ b/app/views.py @@ -17,7 +17,7 @@ from flask_login import login_user, logout_user, current_user, login_required from werkzeug import secure_filename from werkzeug.security import gen_salt -from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord +from .models import User, Account, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord from app import app, login_manager, github, google from app.lib import utils from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec @@ -465,7 +465,6 @@ def dashboard(): else: logging.debug('Update domains in background') - # stats for dashboard domain_count = Domain.query.count() users = User.query.all() @@ -492,7 +491,7 @@ def dashboard_domains(): template = app.jinja_env.get_template("dashboard_domain.html") render = template.make_module(vars={"current_user": current_user}) - columns = [Domain.name, Domain.dnssec, Domain.type, Domain.serial, Domain.master] + columns = [Domain.name, Domain.dnssec, Domain.type, Domain.serial, Domain.master, Domain.account] # History.created_on.desc() order_by = [] for i in range(len(columns)): @@ -515,7 +514,13 @@ def dashboard_domains(): if search: start = "" if search.startswith("^") else "%" end = "" if search.endswith("$") else "%" - domains = domains.filter(Domain.name.ilike(start + search.strip("^$") + end)) + + if current_user.role.name == 'Administrator': + domains = domains.outerjoin(Account).filter(Domain.name.ilike(start + search.strip("^$") + end) | + Account.name.ilike(start + search.strip("^$") + end) | + Account.description.ilike(start + search.strip("^$") + end)) + else: + domains = domains.filter(Domain.name.ilike(start + search.strip("^$") + end)) filtered_count = domains.count() @@ -525,9 +530,6 @@ def dashboard_domains(): if length != -1: domains = domains[start:start + length] - if current_user.role.name != 'Administrator': - domains = [d[2] for d in domains] - data = [] for domain in domains: data.append([ @@ -536,6 +538,7 @@ def dashboard_domains(): render.type(domain), render.serial(domain), render.master(domain), + render.account(domain), render.actions(domain), ]) @@ -612,6 +615,7 @@ def domain_add(): domain_type = request.form.getlist('radio_type')[0] domain_template = request.form.getlist('domain_template')[0] soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0] + account_id = request.form.getlist('accountid')[0] if ' ' in domain_name or not domain_name or not domain_type: return render_template('errors/400.html', msg="Please correct your input"), 400 @@ -623,10 +627,13 @@ def domain_add(): domain_master_ips = domain_master_string.split(',') else: domain_master_ips = [] + + account_name = Account().get_name_by_id(account_id) + d = Domain() - result = d.add(domain_name=domain_name, domain_type=domain_type, soa_edit_api=soa_edit_api, domain_master_ips=domain_master_ips) + result = d.add(domain_name=domain_name, domain_type=domain_type, soa_edit_api=soa_edit_api, domain_master_ips=domain_master_ips, account_name=account_name) if result['status'] == 'ok': - history = History(msg='Add domain {0}'.format(domain_name), detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips}), created_by=current_user.username) + history = History(msg='Add domain {0}'.format(domain_name), detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips, 'account_id': account_id}), created_by=current_user.username) history.add() if domain_template != '0': template = DomainTemplate.query.filter(DomainTemplate.id == domain_template).first() @@ -649,7 +656,10 @@ def domain_add(): except: logging.error(traceback.print_exc()) return redirect(url_for('error', code=500)) - return render_template('domain_add.html', templates=templates) + + else: + accounts = Account.query.all() + return render_template('domain_add.html', templates=templates, accounts=accounts) @app.route('/admin/domain//delete', methods=['GET']) @@ -677,12 +687,14 @@ def domain_management(domain_name): if not domain: return redirect(url_for('error', code=404)) users = User.query.all() + accounts = Account.query.all() # get list of user ids to initilize selection data d = Domain(name=domain_name) domain_user_ids = d.get_user() + account = d.get_account() - return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids) + return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, domain_account=account) if request.method == 'POST': # username in right column @@ -718,9 +730,32 @@ def domain_change_soa_edit_api(domain_name): status = d.update_soa_setting(domain_name=domain_name, soa_edit_api=new_setting) if status['status'] != None: users = User.query.all() + accounts = Account.query.all() d = Domain(name=domain_name) domain_user_ids = d.get_user() - return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, status=status) + account = d.get_account() + return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, domain_account=account) + else: + return redirect(url_for('error', code=500)) + + +@app.route('/admin/domain//change_account', methods=['POST']) +@login_required +@admin_role_required +def domain_change_account(domain_name): + domain = Domain.query.filter(Domain.name == domain_name).first() + if not domain: + return redirect(url_for('error', code=404)) + + account_id = request.form.get('accountid') + status = domain.assoc_account(account_id) + if status['status']: + users = User.query.all() + accounts = Account.query.all() + d = Domain(name=domain_name) + domain_user_ids = d.get_user() + account = d.get_account() + return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, domain_account=account) else: return redirect(url_for('error', code=500)) @@ -1158,6 +1193,95 @@ def admin_manageuser(): return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400) +@app.route('/admin/account/edit/', methods=['GET', 'POST']) +@app.route('/admin/account/edit', methods=['GET', 'POST']) +@login_required +@admin_role_required +def admin_editaccount(account_name=None): + users = User.query.all() + + if request.method == 'GET': + if account_name is None: + return render_template('admin_editaccount.html', users=users, create=1) + + else: + account = Account.query.filter(Account.name == account_name).first() + account_user_ids = account.get_user() + return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=0) + + if request.method == 'POST': + fdata = request.form + new_user_list = request.form.getlist('account_multi_user') + + # on POST, synthesize account and account_user_ids from form data + if not account_name: + account_name = fdata['accountname'] + + account = Account(name=account_name, description=fdata['accountdescription'], contact=fdata['accountcontact'], mail=fdata['accountmail']) + account_user_ids = [] + for username in new_user_list: + userid = User(username=username).get_user_info_by_username().id + account_user_ids.append(userid) + + create = int(fdata['create']) + if create: + # account __init__ sanitizes and lowercases the name, so to manage expectations + # we let the user reenter the name until it's not empty and it's valid (ignoring the case) + if account.name == "" or account.name != account_name.lower(): + return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=create, invalid_accountname=True) + + if Account.query.filter(Account.name == account.name).first(): + return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=create, duplicate_accountname=True) + + result = account.create_account() + history = History(msg='Create account {0}'.format(account.name), created_by=current_user.username) + + else: + result = account.update_account() + history = History(msg='Update account {0}'.format(account.name), created_by=current_user.username) + + if result['status']: + account.grant_privileges(new_user_list) + history.add() + return redirect(url_for('admin_manageaccount')) + + return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=create, error=result['msg']) + + +@app.route('/admin/manageaccount', methods=['GET', 'POST']) +@login_required +@admin_role_required +def admin_manageaccount(): + if request.method == 'GET': + accounts = Account.query.order_by(Account.name).all() + return render_template('admin_manageaccount.html', accounts=accounts) + + if request.method == 'POST': + # + # post data should in format + # {'action': 'delete_account', 'data': 'accountname'} + # + try: + jdata = request.json + data = jdata['data'] + + if jdata['action'] == 'delete_account': + account = Account(name=data) + result = account.delete_account() + if result: + history = History(msg='Delete account {0}'.format(data), created_by=current_user.username) + history.add() + return make_response(jsonify( { 'status': 'ok', 'msg': 'Account has been removed.' } ), 200) + else: + return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot remove account.' } ), 500) + + else: + return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400) + except: + logging.error(traceback.print_exc()) + return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400) + + @app.route('/admin/history', methods=['GET', 'POST']) @login_required @admin_role_required