From 3457d9214a84a504a53eb6f77830adba4bafa4e4 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Fri, 31 Aug 2018 11:57:06 +0700 Subject: [PATCH] Adding Operator role --- app/decorators.py | 34 +++++++- app/models.py | 68 +++++++-------- app/static/custom/js/custom.js | 7 +- app/templates/admin_manageuser.html | 40 ++++----- .../admin_setting_authentication.html | 8 ++ app/templates/base.html | 24 +++--- app/templates/dashboard.html | 32 +++---- app/templates/dashboard_domain.html | 4 +- app/views.py | 86 +++++++++++-------- .../4a666113c7bb_add_operator_role.py | 58 +++++++++++++ 10 files changed, 228 insertions(+), 133 deletions(-) create mode 100644 migrations/versions/4a666113c7bb_add_operator_role.py diff --git a/app/decorators.py b/app/decorators.py index 673eea6..c563e00 100644 --- a/app/decorators.py +++ b/app/decorators.py @@ -6,6 +6,9 @@ from app.models import Role, Setting def admin_role_required(f): + """ + Grant access if user is in Administrator role + """ @wraps(f) def decorated_function(*args, **kwargs): if g.user.role.name != 'Administrator': @@ -14,10 +17,28 @@ def admin_role_required(f): return decorated_function -def can_access_domain(f): +def operator_role_required(f): + """ + Grant access if user is in Operator role or higher + """ @wraps(f) def decorated_function(*args, **kwargs): - if g.user.role.name != 'Administrator': + if g.user.role.name not in ['Administrator', 'Operator']: + return redirect(url_for('error', code=401)) + return f(*args, **kwargs) + return decorated_function + + +def can_access_domain(f): + """ + Grant access if: + - user is in Operator role or higher, or + - user is in granted Account, or + - user is in granted Domain + """ + @wraps(f) + def decorated_function(*args, **kwargs): + if g.user.role.name not in ['Administrator', 'Operator']: domain_name = kwargs.get('domain_name') user_domain = [d.name for d in g.user.get_domain()] @@ -29,10 +50,15 @@ def can_access_domain(f): def can_configure_dnssec(f): + """ + Grant access if: + - user is in Operator role or higher, or + - dnssec_admins_only is off + """ @wraps(f) def decorated_function(*args, **kwargs): - if g.user.role.name != 'Administrator' and Setting().get('dnssec_admins_only'): - return redirect(url_for('error', code=401)) + if g.user.role.name not in ['Administrator', 'Operator'] and Setting().get('dnssec_admins_only'): + return redirect(url_for('error', code=401)) return f(*args, **kwargs) return decorated_function diff --git a/app/models.py b/app/models.py index 5eec5ab..2117ea1 100644 --- a/app/models.py +++ b/app/models.py @@ -150,7 +150,7 @@ class User(db.Model): logging.error(e) logging.debug('baseDN: {0}'.format(baseDN)) logging.debug(traceback.format_exc()) - raise + def ldap_auth(self, ldap_username, password): try: @@ -165,6 +165,8 @@ class User(db.Model): """ Validate user credential """ + role_name = 'User' + if method == 'LOCAL': user_info = User.query.filter(User.username == self.username).first() @@ -179,12 +181,12 @@ class User(db.Model): return False if method == 'LDAP': - isadmin = False LDAP_TYPE = Setting().get('ldap_type') LDAP_BASE_DN = Setting().get('ldap_base_dn') LDAP_FILTER_BASIC = Setting().get('ldap_filter_basic') LDAP_FILTER_USERNAME = Setting().get('ldap_filter_username') LDAP_ADMIN_GROUP = Setting().get('ldap_admin_group') + LDAP_OPERATOR_GROUP = Setting().get('ldap_operator_group') LDAP_USER_GROUP = Setting().get('ldap_user_group') LDAP_GROUP_SECURITY_ENABLED = Setting().get('ldap_sg_enabled') @@ -206,24 +208,30 @@ class User(db.Model): try: if LDAP_TYPE == 'ldap': if (self.ldap_search(searchFilter, LDAP_ADMIN_GROUP)): - isadmin = True + role_name = 'Administrator' logging.info('User {0} is part of the "{1}" group that allows admin access to PowerDNS-Admin'.format(self.username, LDAP_ADMIN_GROUP)) + elif (self.ldap_search(searchFilter, LDAP_OPERATOR_GROUP)): + role_name = 'Operator' + logging.info('User {0} is part of the "{1}" group that allows operator access to PowerDNS-Admin'.format(self.username, LDAP_OPERATOR_GROUP)) elif (self.ldap_search(searchFilter, LDAP_USER_GROUP)): logging.info('User {0} is part of the "{1}" group that allows user access to PowerDNS-Admin'.format(self.username, LDAP_USER_GROUP)) else: - logging.error('User {0} is not part of the "{1}" or "{2}" groups that allow access to PowerDNS-Admin'.format(self.username, LDAP_ADMIN_GROUP, LDAP_USER_GROUP)) + logging.error('User {0} is not part of the "{1}", "{2}" or "{3}" groups that allow access to PowerDNS-Admin'.format(self.username, LDAP_ADMIN_GROUP, LDAP_OPERATOR_GROUP, LDAP_USER_GROUP)) return False elif LDAP_TYPE == 'ad': user_ldap_groups = [g.decode("utf-8") for g in ldap_result[0][0][1]['memberOf']] logging.debug('user_ldap_groups: {0}'.format(user_ldap_groups)) if (LDAP_ADMIN_GROUP in user_ldap_groups): - isadmin = True + role_name = 'Administrator' logging.info('User {0} is part of the "{1}" group that allows admin access to PowerDNS-Admin'.format(self.username, LDAP_ADMIN_GROUP)) + elif (LDAP_OPERATOR_GROUP in user_ldap_groups): + role_name = 'Operator' + logging.info('User {0} is part of the "{1}" group that allows operator access to PowerDNS-Admin'.format(self.username, LDAP_OPERATOR_GROUP)) elif (LDAP_USER_GROUP in user_ldap_groups): logging.info('User {0} is part of the "{1}" group that allows user access to PowerDNS-Admin'.format(self.username, LDAP_USER_GROUP)) else: - logging.error('User {0} is not part of the "{1}" or "{2}" groups that allow access to PowerDNS-Admin'.format(self.username, LDAP_ADMIN_GROUP, LDAP_USER_GROUP)) + logging.error('User {0} is not part of the "{1}", "{2}" or "{3}" groups that allow access to PowerDNS-Admin'.format(self.username, LDAP_ADMIN_GROUP, LDAP_OPERATOR_GROUP, LDAP_USER_GROUP)) return False else: logging.error('Invalid LDAP type') @@ -261,21 +269,17 @@ class User(db.Model): logging.debug(traceback.format_exc()) # first register user will be in Administrator role - self.role_id = Role.query.filter_by(name='User').first().id if User.query.count() == 0: self.role_id = Role.query.filter_by(name='Administrator').first().id - - # user will be in Administrator role if part of LDAP Admin group - if LDAP_GROUP_SECURITY_ENABLED: - if isadmin == True: - self.role_id = Role.query.filter_by(name='Administrator').first().id + else: + self.role_id = Role.query.filter_by(name=role_name).first().id self.create_user() logging.info('Created user "{0}" in the DB'.format(self.username)) - # user already exists in database, set their admin status based on group membership (if enabled) + # user already exists in database, set their role based on group membership (if enabled) if LDAP_GROUP_SECURITY_ENABLED: - self.set_admin(isadmin) + self.set_role(role_name) return True else: @@ -453,28 +457,15 @@ class User(db.Model): return False return False - def set_admin(self, is_admin): - """ - Set role for a user: - is_admin == True => Administrator - is_admin == False => User - """ - user_role_name = 'Administrator' if is_admin else 'User' - role = Role.query.filter(Role.name==user_role_name).first() - - try: - if role: - user = User.query.filter(User.username==self.username).first() - user.role_id = role.id - db.session.commit() - return True - else: - return False - except: - db.session.roleback() - logging.error('Cannot change user role in DB') - logging.debug(traceback.format_exc()) - return False + def set_role(self, role_name): + role = Role.query.filter(Role.name==role_name).first() + if role: + user = User.query.filter(User.username==self.username).first() + user.role_id = role.id + db.session.commit() + return {'status': True, 'msg': 'Set user role successfully'} + else: + return {'status': False, 'msg': 'Role does not exist'} class Account(db.Model): @@ -1825,8 +1816,9 @@ class Setting(db.Model): 'ldap_filter_basic': '', 'ldap_filter_username': '', 'ldap_sg_enabled': False, - 'ldap_admin_group': False, - 'ldap_user_group': False, + 'ldap_admin_group': '', + 'ldap_operator_group': '', + 'ldap_user_group': '', 'github_oauth_enabled': False, 'github_oauth_key': '', 'github_oauth_secret': '', diff --git a/app/static/custom/js/custom.js b/app/static/custom/js/custom.js index 6442bb2..e079130 100644 --- a/app/static/custom/js/custom.js +++ b/app/static/custom/js/custom.js @@ -22,9 +22,14 @@ function applyChanges(data, url, showResult, refreshPage) { }, error : function(jqXHR, status) { + // console.log(jqXHR); + // var modal = $("#modal_error"); + // modal.find('.modal-body p').text(jqXHR["responseText"]); + // modal.modal('show'); console.log(jqXHR); var modal = $("#modal_error"); - modal.find('.modal-body p').text(jqXHR["responseText"]); + var responseJson = jQuery.parseJSON(jqXHR.responseText); + modal.find('.modal-body p').text(responseJson['msg']); modal.modal('show'); } }); diff --git a/app/templates/admin_manageuser.html b/app/templates/admin_manageuser.html index 653738e..a3aa85a 100644 --- a/app/templates/admin_manageuser.html +++ b/app/templates/admin_manageuser.html @@ -36,7 +36,7 @@ First Name Last Name Email - Admin + Role Privileges Action @@ -49,18 +49,22 @@ {{ user.lastname }} {{ user.email }} - + - - - @@ -93,14 +97,6 @@ "pageLength": 10 }); - // avoid losing icheck box style when database refreshed - $('#tbl_users').on('draw.dt', function () { - $('.admin_toggle').iCheck({ - handle: 'checkbox', - checkboxClass: 'icheckbox_square-blue' - }); - }); - // handle revocation of privileges $(document.body).on('click', '.button_revoke', function() { var modal = $("#modal_revoke"); @@ -129,24 +125,18 @@ }); - // initialize pretty checkboxes - $('.admin_toggle').iCheck({ - checkboxClass : 'icheckbox_square-blue', - increaseArea : '20%' // optional - }); - - // handle checkbox toggling - $(document.body).on('ifToggled', '.admin_toggle', function() { - var is_admin = $(this).prop('checked'); + // handle user role changing + $('.user_role').on('change', function() { + var role_name = this.value; var username = $(this).prop('id'); postdata = { - 'action' : 'set_admin', + 'action' : 'update_user_role', 'data' : { 'username' : username, - 'is_admin' : is_admin + 'role_name' : role_name } }; - applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser'); + applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', showResult=true); }); {% endblock %} diff --git a/app/templates/admin_setting_authentication.html b/app/templates/admin_setting_authentication.html index e929065..e522178 100644 --- a/app/templates/admin_setting_authentication.html +++ b/app/templates/admin_setting_authentication.html @@ -133,6 +133,11 @@ +
+ + + +
@@ -197,6 +202,9 @@
  • Admin group - Your LDAP admin group.
  • +
  • + Operator group - Your LDAP operator group. +
  • User group - Your LDAP user group.
  • diff --git a/app/templates/base.html b/app/templates/base.html index 98845ea..0b1e220 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -108,26 +108,26 @@
  • Dashboard
  • - {% if current_user.role.name == 'Administrator' %} + {% if current_user.role.name in ['Administrator', 'Operator'] %}
  • New Domain
  • ADMINISTRATION
  • - Admin Console -
  • -
  • - Domain Templates -
  • -
  • - Users -
  • -
  • - Accounts + PDNS
  • History
  • +
  • + Domain Templates +
  • +
  • + Accounts +
  • +
  • + Users +
  • Settings @@ -138,8 +138,10 @@
  • {% endif %} diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index b1d3400..871ff26 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -19,7 +19,7 @@ {% block content %}
    - {% if current_user.role.name == 'Administrator' %} + {% if current_user.role.name in ['Administrator', 'Operator'] %}
    @@ -69,7 +69,7 @@
    - +

    {{ uptime|display_second_to_time }}

    @@ -102,17 +102,17 @@ {% for history in histories %} - - {{ history.created_by }} - {{ history.msg }} - {{ history.created_on }} - - - - - {% endfor %} + + {{ history.created_by }} + {{ history.msg }} + {{ history.created_on }} + + + + + {% endfor %}
    @@ -136,7 +136,7 @@ Serial Master Account - Action + Action @@ -182,7 +182,7 @@ "ordering" : true, "columnDefs": [ { "orderable": false, "targets": [-1] } - {% if current_user.role.name != 'Administrator' %},{ "visible": false, "targets": [-2] }{% endif %} + {% if current_user.role.name not in ['Administrator', 'Operator'] %},{ "visible": false, "targets": [-2] }{% endif %} ], "processing" : true, "serverSide" : true, @@ -236,7 +236,7 @@ modal.modal('show'); }); - {% if current_user.role.name == 'Administrator' or not SETTING.get('dnssec_admins_only') %} + {% if current_user.role.name in ['Administrator', 'Operator'] or not SETTING.get('dnssec_admins_only') %} $(document.body).on("click", ".button_dnssec", function() { var domain = $(this).prop('id'); getdnssec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec', domain); diff --git a/app/templates/dashboard_domain.html b/app/templates/dashboard_domain.html index 6d42edb..8f1c1fe 100644 --- a/app/templates/dashboard_domain.html +++ b/app/templates/dashboard_domain.html @@ -23,13 +23,13 @@ {% endmacro %} {% macro account(domain) %} - {% if current_user.role.name =='Administrator' %} + {% if current_user.role.name in ['Administrator', 'Operator'] %} {% if domain.account_description != "" %}{{ domain.account.description }} {% endif %}[{{ domain.account.name }}] {% endif %} {% endmacro %} {% macro actions(domain) %} - {% if current_user.role.name =='Administrator' %} + {% if current_user.role.name in ['Administrator', 'Operator'] %}