mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-09 23:20:27 +00:00
Merge pull request #345 from ngoduykhanh/user_role_adjustment
Adding Operator role and Code adjustment
This commit is contained in:
commit
c8d72f5bba
10
.lgtm.yml
Normal file
10
.lgtm.yml
Normal file
@ -0,0 +1,10 @@
|
||||
extraction:
|
||||
python:
|
||||
python_setup:
|
||||
version: 3
|
||||
index:
|
||||
exclude:
|
||||
- .git
|
||||
- upload
|
||||
- flask
|
||||
- node_modules
|
@ -29,7 +29,7 @@ login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
db = SQLAlchemy(app) # database
|
||||
migrate = Migrate(app, db) # flask-migrate
|
||||
oauth = OAuth(app) # oauth
|
||||
oauth_client = OAuth(app) # oauth
|
||||
|
||||
if app.config.get('SAML_ENABLED') and app.config.get('SAML_ENCRYPT'):
|
||||
from app.lib import certutil
|
||||
|
@ -1,11 +1,13 @@
|
||||
from functools import wraps
|
||||
from flask import g, request, redirect, url_for
|
||||
from flask import g, redirect, url_for
|
||||
|
||||
from app import app
|
||||
from app.models import Role, Setting
|
||||
from app.models import 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 +16,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 +49,30 @@ 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
|
||||
|
||||
|
||||
def can_create_domain(f):
|
||||
"""
|
||||
Grant access if:
|
||||
- user is in Operator role or higher, or
|
||||
- allow_user_create_domain is on
|
||||
"""
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if g.user.role.name not in ['Administrator', 'Operator'] and not Setting().get('allow_user_create_domain'):
|
||||
return redirect(url_for('error', code=401))
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import logging
|
||||
|
||||
class logger(object):
|
||||
|
@ -1,5 +1,4 @@
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
import hashlib
|
||||
@ -10,12 +9,10 @@ from urllib.parse import urlparse
|
||||
from datetime import datetime, timedelta
|
||||
from threading import Thread
|
||||
|
||||
from .certutil import *
|
||||
from .certutil import KEY_FILE, CERT_FILE
|
||||
|
||||
if app.config['SAML_ENABLED']:
|
||||
from onelogin.saml2.auth import OneLogin_Saml2_Auth
|
||||
from onelogin.saml2.utils import OneLogin_Saml2_Utils
|
||||
from onelogin.saml2.settings import OneLogin_Saml2_Settings
|
||||
from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
|
||||
idp_timestamp = datetime(1970, 1, 1)
|
||||
idp_data = None
|
||||
@ -227,7 +224,7 @@ def prepare_flask_request(request):
|
||||
|
||||
def init_saml_auth(req):
|
||||
own_url = ''
|
||||
if req['https'] is 'on':
|
||||
if req['https'] == 'on':
|
||||
own_url = 'https://'
|
||||
else:
|
||||
own_url = 'http://'
|
||||
@ -285,3 +282,12 @@ def init_saml_auth(req):
|
||||
settings['organization']['en-US']['url'] = own_url
|
||||
auth = OneLogin_Saml2_Auth(req, settings)
|
||||
return auth
|
||||
|
||||
|
||||
def display_setting_state(value):
|
||||
if value == 1:
|
||||
return "ON"
|
||||
elif value == 0:
|
||||
return "OFF"
|
||||
else:
|
||||
return "UNKNOWN"
|
||||
|
220
app/models.py
220
app/models.py
@ -1,17 +1,16 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import ldap
|
||||
import ldap.filter
|
||||
import time
|
||||
import base64
|
||||
import bcrypt
|
||||
import itertools
|
||||
import traceback
|
||||
import pyotp
|
||||
import re
|
||||
import dns.reversename
|
||||
import dns.inet
|
||||
import dns.name
|
||||
import sys
|
||||
import logging as logger
|
||||
|
||||
from ast import literal_eval
|
||||
@ -150,7 +149,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 +164,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 +180,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 +207,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 +268,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:
|
||||
@ -430,9 +433,9 @@ class User(db.Model):
|
||||
User.query.filter(User.username == self.username).delete()
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot delete user {0} from DB'.format(self.username))
|
||||
logging.error('Cannot delete user {0} from DB. DETAIL: {1}'.format(self.username, e))
|
||||
return False
|
||||
|
||||
def revoke_privilege(self):
|
||||
@ -447,34 +450,21 @@ class User(db.Model):
|
||||
DomainUser.query.filter(DomainUser.user_id == user_id).delete()
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot revoke user {0} privielges'.format(self.username))
|
||||
logging.error('Cannot revoke user {0} privielges. DETAIL: {1}'.format(self.username, e))
|
||||
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):
|
||||
@ -580,9 +570,9 @@ class Account(db.Model):
|
||||
db.session.commit()
|
||||
return True
|
||||
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot delete account {0} from DB'.format(self.username))
|
||||
logging.error('Cannot delete account {0} from DB. DETAIL: {1}'.format(self.username, e))
|
||||
return False
|
||||
|
||||
def get_user(self):
|
||||
@ -611,18 +601,18 @@ class Account(db.Model):
|
||||
for uid in removed_ids:
|
||||
AccountUser.query.filter(AccountUser.user_id == uid).filter(AccountUser.account_id==account_id).delete()
|
||||
db.session.commit()
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot revoke user privielges on account {0}'.format(self.name))
|
||||
logging.error('Cannot revoke user privielges on account {0}. DETAIL: {1}'.format(self.name, e))
|
||||
|
||||
try:
|
||||
for uid in added_ids:
|
||||
au = AccountUser(account_id, uid)
|
||||
db.session.add(au)
|
||||
db.session.commit()
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot grant user privileges to account {0}'.format(self.name))
|
||||
logging.error('Cannot grant user privileges to account {0}. DETAIL: {1}'.format(self.name, e))
|
||||
|
||||
def revoke_privileges_by_id(self, user_id):
|
||||
"""
|
||||
@ -643,9 +633,9 @@ class Account(db.Model):
|
||||
db.session.add(au)
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot add user privielges on account {0}'.format(self.name))
|
||||
logging.error('Cannot add user privielges on account {0}. DETAIL: {1}'.format(self.name, e))
|
||||
return False
|
||||
|
||||
def remove_user(self, user):
|
||||
@ -656,9 +646,9 @@ class Account(db.Model):
|
||||
AccountUser.query.filter(AccountUser.user_id == user.id).filter(AccountUser.account_id == self.id).delete()
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot revoke user privielges on account {0}'.format(self.name))
|
||||
logging.error('Cannot revoke user privielges on account {0}. DETAIL: {1}'.format(self.name, e))
|
||||
return False
|
||||
|
||||
|
||||
@ -707,8 +697,8 @@ class DomainSetting(db.Model):
|
||||
self.value = value
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
logging.error('Unable to set DomainSetting value')
|
||||
except Exception as e:
|
||||
logging.error('Unable to set DomainSetting value. DETAIL: {0}'.format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
db.session.rollback()
|
||||
return False
|
||||
@ -784,7 +774,8 @@ class Domain(db.Model):
|
||||
try:
|
||||
domain = Domain.query.filter(Domain.name==name).first()
|
||||
return domain.id
|
||||
except:
|
||||
except Exception as e:
|
||||
logging.error('Domain does not exist. ERROR: {0}'.format(e))
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
@ -818,8 +809,8 @@ class Domain(db.Model):
|
||||
# then remove domain
|
||||
Domain.query.filter(Domain.name == d).delete()
|
||||
db.session.commit()
|
||||
except:
|
||||
logging.error('Can not delete domain from DB')
|
||||
except Exception as e:
|
||||
logging.error('Can not delete domain from DB. DETAIL: {0}'.format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
db.session.rollback()
|
||||
|
||||
@ -911,7 +902,7 @@ class Domain(db.Model):
|
||||
return {'status': 'ok', 'msg': 'Added domain successfully'}
|
||||
except Exception as e:
|
||||
logging.error('Cannot add domain {0}'.format(domain_name))
|
||||
logging.debug(traceback.print_exc())
|
||||
logging.debug(traceback.format_exc())
|
||||
return {'status': 'error', 'msg': 'Cannot add this domain.'}
|
||||
|
||||
def update_soa_setting(self, domain_name, soa_edit_api):
|
||||
@ -980,7 +971,7 @@ class Domain(db.Model):
|
||||
domain_users.append(tmp.username)
|
||||
if 0 != len(domain_users):
|
||||
self.name = domain_reverse_name
|
||||
self.grant_privielges(domain_users)
|
||||
self.grant_privileges(domain_users)
|
||||
return {'status': 'ok', 'msg': 'New reverse lookup domain created with granted privilages'}
|
||||
return {'status': 'ok', 'msg': 'New reverse lookup domain created without users'}
|
||||
return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'}
|
||||
@ -1009,12 +1000,12 @@ class Domain(db.Model):
|
||||
headers = {}
|
||||
headers['X-API-Key'] = self.PDNS_API_KEY
|
||||
try:
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain_name)), headers=headers, method='DELETE')
|
||||
utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain_name)), headers=headers, method='DELETE')
|
||||
logging.info('Delete domain {0} successfully'.format(domain_name))
|
||||
return {'status': 'ok', 'msg': 'Delete domain successfully'}
|
||||
except Exception as e:
|
||||
logging.error('Cannot delete domain {0}'.format(domain_name))
|
||||
logging.debug(traceback.print_exc())
|
||||
logging.debug(traceback.format_exc())
|
||||
return {'status': 'error', 'msg': 'Cannot delete domain'}
|
||||
|
||||
def get_user(self):
|
||||
@ -1027,7 +1018,7 @@ class Domain(db.Model):
|
||||
user_ids.append(q[0].user_id)
|
||||
return user_ids
|
||||
|
||||
def grant_privielges(self, new_user_list):
|
||||
def grant_privileges(self, new_user_list):
|
||||
"""
|
||||
Reconfigure domain_user table
|
||||
"""
|
||||
@ -1044,18 +1035,18 @@ class Domain(db.Model):
|
||||
for uid in removed_ids:
|
||||
DomainUser.query.filter(DomainUser.user_id == uid).filter(DomainUser.domain_id==domain_id).delete()
|
||||
db.session.commit()
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot revoke user privielges on domain {0}'.format(self.name))
|
||||
logging.error('Cannot revoke user privielges on domain {0}. DETAIL: {1}'.format(self.name, e))
|
||||
|
||||
try:
|
||||
for uid in added_ids:
|
||||
du = DomainUser(domain_id, uid)
|
||||
db.session.add(du)
|
||||
db.session.commit()
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error('Cannot grant user privielges to domain {0}'.format(self.name))
|
||||
logging.error('Cannot grant user privielges to domain {0}. DETAIL: {1}'.format(self.name, e))
|
||||
|
||||
def update_from_master(self, domain_name):
|
||||
"""
|
||||
@ -1066,9 +1057,10 @@ class Domain(db.Model):
|
||||
headers = {}
|
||||
headers['X-API-Key'] = self.PDNS_API_KEY
|
||||
try:
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}/axfr-retrieve'.format(domain.name)), headers=headers, method='PUT')
|
||||
utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}/axfr-retrieve'.format(domain.name)), headers=headers, method='PUT')
|
||||
return {'status': 'ok', 'msg': 'Update from Master successfully'}
|
||||
except:
|
||||
except Exception as e:
|
||||
logging.error('Cannot update from master. DETAIL: {0}'.format(e))
|
||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||
else:
|
||||
return {'status': 'error', 'msg': 'This domain doesnot exist'}
|
||||
@ -1087,7 +1079,8 @@ class Domain(db.Model):
|
||||
return {'status': 'error', 'msg': 'DNSSEC is not enabled for this domain'}
|
||||
else:
|
||||
return {'status': 'ok', 'dnssec': jdata}
|
||||
except:
|
||||
except Exception as e:
|
||||
logging.error('Cannot get domain dnssec. DETAIL: {0}'.format(e))
|
||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||
else:
|
||||
return {'status': 'error', 'msg': 'This domain doesnot exist'}
|
||||
@ -1120,8 +1113,9 @@ class Domain(db.Model):
|
||||
|
||||
return {'status': 'ok'}
|
||||
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
except Exception as e:
|
||||
logging.error('Cannot enable dns sec. DETAIL: {}'.format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||
|
||||
else:
|
||||
@ -1151,8 +1145,9 @@ class Domain(db.Model):
|
||||
|
||||
return {'status': 'ok'}
|
||||
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
except Exception as e:
|
||||
logging.error('Cannot delete dnssec key. DETAIL: {0}'.format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator','domain': domain.name, 'id': key_id}
|
||||
|
||||
else:
|
||||
@ -1190,10 +1185,9 @@ class Domain(db.Model):
|
||||
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))
|
||||
logging.info('Account changed for domain {0} successfully'.format(domain_name))
|
||||
return {'status': 'ok', 'msg': 'account changed successfully'}
|
||||
|
||||
except Exception as e:
|
||||
@ -1202,8 +1196,6 @@ class Domain(db.Model):
|
||||
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
|
||||
@ -1273,8 +1265,8 @@ class Record(object):
|
||||
headers['X-API-Key'] = self.PDNS_API_KEY
|
||||
try:
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain)), headers=headers)
|
||||
except:
|
||||
logging.error("Cannot fetch domain's record data from remote powerdns api")
|
||||
except Exception as e:
|
||||
logging.error("Cannot fetch domain's record data from remote powerdns api. DETAIL: {0}".format(e))
|
||||
return False
|
||||
|
||||
if self.NEW_SCHEMA:
|
||||
@ -1573,7 +1565,6 @@ class Record(object):
|
||||
self.add(domain_reverse_name)
|
||||
for r in deleted_records:
|
||||
if r['type'] in ['A', 'AAAA']:
|
||||
r_name = r['name'] + '.'
|
||||
r_content = r['content']
|
||||
reverse_host_address = dns.reversename.from_address(r_content).to_text()
|
||||
domain_reverse_name = d.get_reverse_domain_name(reverse_host_address)
|
||||
@ -1606,8 +1597,8 @@ class Record(object):
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain)), headers=headers, method='PATCH', data=data)
|
||||
logging.debug(jdata)
|
||||
return {'status': 'ok', 'msg': 'Record was removed successfully'}
|
||||
except:
|
||||
logging.error("Cannot remove record {0}/{1}/{2} from domain {3}".format(self.name, self.type, self.data, domain))
|
||||
except Exception as e:
|
||||
logging.error("Cannot remove record {0}/{1}/{2} from domain {3}. DETAIL: {4}".format(self.name, self.type, self.data, domain, e))
|
||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||
|
||||
def is_allowed_edit(self):
|
||||
@ -1683,7 +1674,7 @@ class Record(object):
|
||||
]
|
||||
}
|
||||
try:
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain)), headers=headers, method='PATCH', data=data)
|
||||
utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain)), headers=headers, method='PATCH', data=data)
|
||||
logging.debug("dyndns data: {0}".format(data))
|
||||
return {'status': 'ok', 'msg': 'Record was updated successfully'}
|
||||
except Exception as e:
|
||||
@ -1730,8 +1721,8 @@ class Server(object):
|
||||
try:
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/{0}/config'.format(self.server_id)), headers=headers, method='GET')
|
||||
return jdata
|
||||
except:
|
||||
logging.error("Can not get server configuration.")
|
||||
except Exception as e:
|
||||
logging.error("Can not get server configuration. DETAIL: {0}".format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
return []
|
||||
|
||||
@ -1745,8 +1736,8 @@ class Server(object):
|
||||
try:
|
||||
jdata = utils.fetch_json(urljoin(self.PDNS_STATS_URL, self.API_EXTENDED_URL + '/servers/{0}/statistics'.format(self.server_id)), headers=headers, method='GET')
|
||||
return jdata
|
||||
except:
|
||||
logging.error("Can not get server statistics.")
|
||||
except Exception as e:
|
||||
logging.error("Can not get server statistics. DETAIL: {0}".format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
return []
|
||||
|
||||
@ -1784,13 +1775,13 @@ class History(db.Model):
|
||||
Remove all history from DB
|
||||
"""
|
||||
try:
|
||||
num_rows_deleted = db.session.query(History).delete()
|
||||
db.session.query(History).delete()
|
||||
db.session.commit()
|
||||
logging.info("Removed all history")
|
||||
return True
|
||||
except:
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logging.error("Cannot remove history")
|
||||
logging.error("Cannot remove history. DETAIL: {0}".format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@ -1798,7 +1789,6 @@ class Setting(db.Model):
|
||||
id = db.Column(db.Integer, primary_key = True)
|
||||
name = db.Column(db.String(64))
|
||||
value = db.Column(db.Text())
|
||||
view = db.Column(db.String(64))
|
||||
|
||||
defaults = {
|
||||
'maintenance': False,
|
||||
@ -1808,9 +1798,10 @@ class Setting(db.Model):
|
||||
'default_record_table_size': 15,
|
||||
'default_domain_table_size': 10,
|
||||
'auto_ptr': False,
|
||||
'allow_quick_edit': True,
|
||||
'record_quick_edit': True,
|
||||
'pretty_ipv6_ptr': False,
|
||||
'dnssec_admins_only': False,
|
||||
'allow_user_create_domain': False,
|
||||
'bg_domain_updates': False,
|
||||
'site_name': 'PowerDNS-Admin',
|
||||
'pdns_api_url': '',
|
||||
@ -1827,8 +1818,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': '',
|
||||
@ -1873,8 +1865,8 @@ class Setting(db.Model):
|
||||
maintenance.value = mode
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
logging.error('Cannot set maintenance to {0}'.format(mode))
|
||||
except Exception as e:
|
||||
logging.error('Cannot set maintenance to {0}. DETAIL: {1}'.format(mode, e))
|
||||
logging.debug(traceback.format_exec())
|
||||
db.session.rollback()
|
||||
return False
|
||||
@ -1894,8 +1886,8 @@ class Setting(db.Model):
|
||||
current_setting.value = "True"
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
logging.error('Cannot toggle setting {0}'.format(setting))
|
||||
except Exception as e:
|
||||
logging.error('Cannot toggle setting {0}. DETAIL: {1}'.format(setting, e))
|
||||
logging.debug(traceback.format_exec())
|
||||
db.session.rollback()
|
||||
return False
|
||||
@ -1913,8 +1905,8 @@ class Setting(db.Model):
|
||||
current_setting.value = value
|
||||
db.session.commit()
|
||||
return True
|
||||
except:
|
||||
logging.error('Cannot edit setting {0}'.format(setting))
|
||||
except Exception as e:
|
||||
logging.error('Cannot edit setting {0}. DETAIL: {1}'.format(setting, e))
|
||||
logging.debug(traceback.format_exec())
|
||||
db.session.rollback()
|
||||
return False
|
||||
@ -1933,20 +1925,22 @@ class Setting(db.Model):
|
||||
return list(set(self.get_forward_records_allow_to_edit() + self.get_reverse_records_allow_to_edit()))
|
||||
|
||||
def get_forward_records_allow_to_edit(self):
|
||||
records = literal_eval(self.get('forward_records_allow_edit'))
|
||||
return [r for r in records if records[r]]
|
||||
records = self.get('forward_records_allow_edit')
|
||||
f_records = literal_eval(records) if isinstance(records, str) else records
|
||||
r_name = [r for r in f_records if f_records[r]]
|
||||
# Sort alphabetically if python version is smaller than 3.6
|
||||
if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 6):
|
||||
r_name.sort()
|
||||
return r_name
|
||||
|
||||
def get_reverse_records_allow_to_edit(self):
|
||||
records = literal_eval(self.get('reverse_records_allow_edit'))
|
||||
return [r for r in records if records[r]]
|
||||
|
||||
def get_view(self, view):
|
||||
r = {}
|
||||
settings = Setting.query.filter(Setting.view == view).all()
|
||||
for setting in settings:
|
||||
d = setting.__dict__
|
||||
r[d['name']] = d['value']
|
||||
return r
|
||||
records = self.get('reverse_records_allow_edit')
|
||||
r_records = literal_eval(records) if isinstance(records, str) else records
|
||||
r_name = [r for r in r_records if r_records[r]]
|
||||
# Sort alphabetically if python version is smaller than 3.6
|
||||
if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 6):
|
||||
r_name.sort()
|
||||
return r_name
|
||||
|
||||
|
||||
class DomainTemplate(db.Model):
|
||||
|
@ -1,8 +1,7 @@
|
||||
from ast import literal_eval
|
||||
from flask import request, session, redirect, url_for
|
||||
from flask_oauthlib.client import OAuth
|
||||
|
||||
from app import app, oauth
|
||||
from app import app, oauth_client
|
||||
from app.models import Setting
|
||||
|
||||
# TODO:
|
||||
@ -13,7 +12,7 @@ def github_oauth():
|
||||
if not Setting().get('github_oauth_enabled'):
|
||||
return None
|
||||
|
||||
github = oauth.remote_app(
|
||||
github = oauth_client.remote_app(
|
||||
'github',
|
||||
consumer_key = Setting().get('github_oauth_key'),
|
||||
consumer_secret = Setting().get('github_oauth_secret'),
|
||||
@ -48,7 +47,7 @@ def google_oauth():
|
||||
if not Setting().get('google_oauth_enabled'):
|
||||
return None
|
||||
|
||||
google = oauth.remote_app(
|
||||
google = oauth_client.remote_app(
|
||||
'google',
|
||||
consumer_key=Setting().get('google_oauth_client_id'),
|
||||
consumer_secret=Setting().get('google_oauth_client_secret'),
|
||||
|
@ -1,7 +1,6 @@
|
||||
var dnssecKeyList = []
|
||||
|
||||
function applyChanges(data, url, showResult, refreshPage) {
|
||||
var success = false;
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url : url,
|
||||
@ -22,16 +21,20 @@ 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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function applyRecordChanges(data, domain) {
|
||||
var success = false;
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url : $SCRIPT_ROOT + '/domain/' + domain + '/apply',
|
||||
@ -62,8 +65,6 @@ function applyRecordChanges(data, domain) {
|
||||
}
|
||||
|
||||
function getTableData(table) {
|
||||
var rData = []
|
||||
|
||||
// reformat - pretty format
|
||||
var records = []
|
||||
table.rows().every(function() {
|
||||
@ -81,16 +82,14 @@ function getTableData(table) {
|
||||
|
||||
function saveRow(oTable, nRow) {
|
||||
|
||||
var status = 'Disabled';
|
||||
var jqInputs = $(oTable.row(nRow).node()).find("input");
|
||||
var jqSelect = $(oTable.row(nRow).node()).find("select");
|
||||
|
||||
if (jqSelect[1].value == 'false') {
|
||||
status = 'Active';
|
||||
} else {
|
||||
status = 'Disabled';
|
||||
}
|
||||
|
||||
|
||||
oTable.cell(nRow,0).data(jqInputs[0].value);
|
||||
oTable.cell(nRow,1).data(jqSelect[0].value);
|
||||
oTable.cell(nRow,2).data(status);
|
||||
@ -109,12 +108,12 @@ function saveRow(oTable, nRow) {
|
||||
|
||||
function restoreRow(oTable, nRow) {
|
||||
var aData = oTable.row(nRow).data();
|
||||
var jqTds = $('>td', nRow);
|
||||
oTable.row(nRow).data(aData);
|
||||
oTable.draw();
|
||||
}
|
||||
|
||||
function editRow(oTable, nRow) {
|
||||
var isDisabled = 'true';
|
||||
var aData = oTable.row(nRow).data();
|
||||
var jqTds = oTable.cells(nRow,'').nodes();
|
||||
var record_types = "";
|
||||
@ -134,9 +133,6 @@ function editRow(oTable, nRow) {
|
||||
if (aData[2] == 'Active'){
|
||||
isDisabled = 'false';
|
||||
}
|
||||
else {
|
||||
isDisabled = 'true';
|
||||
}
|
||||
|
||||
SelectElement('record_type', aData[1]);
|
||||
SelectElement('record_status', isDisabled);
|
||||
@ -167,13 +163,14 @@ function enable_dns_sec(url) {
|
||||
function getdnssec(url, domain){
|
||||
|
||||
$.getJSON(url, function(data) {
|
||||
var dnssec_footer = '';
|
||||
var modal = $("#modal_dnssec_info");
|
||||
|
||||
if (data['status'] == 'error'){
|
||||
modal.find('.modal-body p').text(data['msg']);
|
||||
}
|
||||
else {
|
||||
dnssec_msg = '';
|
||||
var dnssec_msg = '';
|
||||
var dnssec = data['dnssec'];
|
||||
|
||||
if (dnssec.length == 0 && parseFloat(PDNS_VERSION) >= 4.1) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
<h3 class="box-title">History Management</h3>
|
||||
</div>
|
||||
<div class="box-body clearfix">
|
||||
<button type="button" class="btn btn-flat btn-danger pull-right" data-toggle="modal" data-target="#modal_clear_history">
|
||||
<button type="button" class="btn btn-flat btn-danger pull-right" data-toggle="modal" data-target="#modal_clear_history" {% if current_user.role != 'Administrator' %}disabled{% endif %}>
|
||||
Clear History <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Email</th>
|
||||
<th>Admin</th>
|
||||
<th>Role</th>
|
||||
<th>Privileges</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
@ -49,18 +49,22 @@
|
||||
<td>{{ user.lastname }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<input type="checkbox" id="{{ user.username }}" class="admin_toggle" {% if user.role.name=='Administrator' %}checked{% endif %} {% if user.username==current_user.username %}disabled{% endif %}>
|
||||
<select id="{{ user.username }}" class="user_role" {% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
|
||||
{% for role in roles %}
|
||||
<option value="{{ role.name }}" {% if role.id==user.role.id %}selected{% endif %}>{{ role.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning button_revoke" id="{{ user.username }}">
|
||||
<button type="button" class="btn btn-flat btn-warning button_revoke" id="{{ user.username }}" {% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
|
||||
Revoke <i class="fa fa-lock"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td width="15%">
|
||||
<button type="button" class="btn btn-flat btn-success button_edit" onclick="window.location.href='{{ url_for('admin_edituser', user_username=user.username) }}'">
|
||||
<button type="button" class="btn btn-flat btn-success button_edit" onclick="window.location.href='{{ url_for('admin_edituser', user_username=user.username) }}'" {% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
|
||||
Edit <i class="fa fa-lock"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ user.username }}" {% if user.username==current_user.username %}disabled{% endif %}>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ user.username }}" {% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
@ -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);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -133,6 +133,11 @@
|
||||
<input type="text" class="form-control" name="ldap_admin_group" id="ldap_admin_group" placeholder="e.g. cn=sysops,dc=mydomain,dc=com" data-error="Please input LDAP DN for Admin group" value="{{ SETTING.get('ldap_admin_group') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ldap_operator_group">Operator group</label>
|
||||
<input type="text" class="form-control" name="ldap_operator_group" id="ldap_operator_group" placeholder="e.g. cn=operators,dc=mydomain,dc=com" data-error="Please input LDAP DN for Operator group" value="{{ SETTING.get('ldap_operator_group') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ldap_user_group">User group</label>
|
||||
<input type="text" class="form-control" name="ldap_user_group" id="ldap_user_group" placeholder="e.g. cn=users,dc=mydomain,dc=com" data-error="Please input LDAP DN for User group" value="{{ SETTING.get('ldap_user_group') }}">
|
||||
@ -197,6 +202,9 @@
|
||||
<li>
|
||||
Admin group - Your LDAP admin group.
|
||||
</li>
|
||||
<li>
|
||||
Operator group - Your LDAP operator group.
|
||||
</li>
|
||||
<li>
|
||||
User group - Your LDAP user group.
|
||||
</li>
|
||||
|
@ -34,23 +34,22 @@
|
||||
<tbody>
|
||||
{% for setting in settings %}
|
||||
<tr class="odd ">
|
||||
<td>{{ setting.name }}</td>
|
||||
{% if setting.value == "True" or setting.value == "False" %}
|
||||
<td>{{ setting.value }}</td>
|
||||
{% else %}
|
||||
<td><input name="value" id="value" value="{{ setting.value }}"></td>
|
||||
{% endif %}
|
||||
<td>{{ setting }}</td>
|
||||
{% if SETTING.get(setting) in [True, False] %}
|
||||
<td>{{ SETTING.get(setting)|display_setting_state }}</td>
|
||||
<td width="6%">
|
||||
{% if setting.value == "True" or setting.value == "False" %}
|
||||
<button type="button" class="btn btn-flat btn-warning setting-toggle-button" id="{{ setting.name }}">
|
||||
<button type="button" class="btn btn-flat btn-warning setting-toggle-button" id="{{ setting }}">
|
||||
Toggle <i class="fa fa-info"></i>
|
||||
</button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-flat btn-warning setting-save-button" id="{{ setting.name }}">
|
||||
</td>
|
||||
{% else %}
|
||||
<td><input name="value" id="value" value="{{ SETTING.get(setting) }}"></td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-warning setting-save-button" id="{{ setting }}">
|
||||
Save <i class="fa fa-info"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@ -69,7 +68,7 @@
|
||||
<script>
|
||||
// set up history data table
|
||||
$("#tbl_settings").DataTable({
|
||||
"paging" : true,
|
||||
"paging" : false,
|
||||
"lengthChange" : false,
|
||||
"searching" : true,
|
||||
"ordering" : true,
|
||||
|
@ -41,7 +41,7 @@
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<label class="control-label" for="pdns_api_key">PDNS API KEY</label>
|
||||
<input type="text" class="form-control" placeholder="PowerDNS API key" name="pdns_api_key" data-error="Please input a valid PowerDNS API key" required value="{{ pdns_api_key }}">
|
||||
<input type="password" class="form-control" placeholder="PowerDNS API key" name="pdns_api_key" data-error="Please input a valid PowerDNS API key" required value="{{ pdns_api_key }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
|
@ -108,26 +108,28 @@
|
||||
<li class="{{ 'active' if active_page == 'dashboard' else '' }}">
|
||||
<a href="{{ url_for('dashboard') }}"><i class="fa fa-dashboard"></i> Dashboard</a>
|
||||
</li>
|
||||
{% if current_user.role.name == 'Administrator' %}
|
||||
{% if SETTING.get('allow_user_create_domain') or current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<li class="{{ 'active' if active_page == 'new_domain' else '' }}">
|
||||
<a href="{{ url_for('domain_add') }}"><i class="fa fa-plus"></i> New Domain</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<li class="header">ADMINISTRATION</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_console' else '' }}">
|
||||
<a href="{{ url_for('admin') }}"><i class="fa fa-wrench"></i> Admin Console</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_domain_template' else '' }}">
|
||||
<a href="{{ url_for('templates') }}"><i class="fa fa-clone"></i> Domain Templates</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_users' else '' }}">
|
||||
<a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-users"></i> Users</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_accounts' else '' }}">
|
||||
<a href="{{ url_for('admin_manageaccount') }}"><i class="fa fa-industry"></i> Accounts</a>
|
||||
<a href="{{ url_for('admin_pdns') }}"><i class="fa fa-info-circle"></i> PDNS</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_history' else '' }}">
|
||||
<a href="{{ url_for('admin_history') }}"><i class="fa fa-calendar"></i> History</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_domain_template' else '' }}">
|
||||
<a href="{{ url_for('templates') }}"><i class="fa fa-clone"></i> Domain Templates</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_accounts' else '' }}">
|
||||
<a href="{{ url_for('admin_manageaccount') }}"><i class="fa fa-industry"></i> Accounts</a>
|
||||
</li>
|
||||
<li class="{{ 'active' if active_page == 'admin_users' else '' }}">
|
||||
<a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-users"></i> Users</a>
|
||||
</li>
|
||||
<li class="{{ 'treeview active' if active_page == 'admin_settings' else 'treeview' }}">
|
||||
<a href="#">
|
||||
<i class="fa fa-cog"></i> Settings
|
||||
@ -138,8 +140,10 @@
|
||||
<ul class="treeview-menu" {% if active_page == 'admin_settings' %}style="display: block;"{% endif %}>
|
||||
<li><a href="{{ url_for('admin_setting_basic') }}"><i class="fa fa-circle-o"></i></i> Basic</a></li>
|
||||
<li><a href="{{ url_for('admin_setting_records') }}"><i class="fa fa-circle-o"></i> Records</a></li>
|
||||
{% if current_user.role.name == 'Administrator' %}
|
||||
<li><a href="{{ url_for('admin_setting_pdns') }}"><i class="fa fa-circle-o"></i> PDNS</a></li>
|
||||
<li><a href="{{ url_for('admin_setting_authentication') }}"><i class="fa fa-circle-o"></i> Authentication</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
@ -19,7 +19,7 @@
|
||||
{% block content %}
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
{% if current_user.role.name == 'Administrator' %}
|
||||
{% if current_user.role.name in ['Administrator', 'Operator'] %}
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<div class="box">
|
||||
@ -69,7 +69,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<a href="{{ url_for('admin') }}">
|
||||
<a href="{{ url_for('admin_pdns') }}">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3><span style="font-size: 18px">{{ uptime|display_second_to_time }}</span></h3>
|
||||
@ -102,17 +102,17 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for history in histories %}
|
||||
<tr class="odd">
|
||||
<td>{{ history.created_by }}</td>
|
||||
<td>{{ history.msg }}</td>
|
||||
<td>{{ history.created_on }}</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-primary history-info-button" value='{{ history.detail|replace("[]","None") }}'>
|
||||
Info <i class="fa fa-info"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="odd">
|
||||
<td>{{ history.created_by }}</td>
|
||||
<td>{{ history.msg }}</td>
|
||||
<td>{{ history.created_on }}</td>
|
||||
<td width="6%">
|
||||
<button type="button" class="btn btn-flat btn-primary history-info-button" value='{{ history.detail|replace("[]","None") }}'>
|
||||
Info <i class="fa fa-info"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -136,7 +136,7 @@
|
||||
<th>Serial</th>
|
||||
<th>Master</th>
|
||||
<th>Account</th>
|
||||
<th {% if current_user.role.name !='Administrator' %}width="6%"{% else %}width="25%"{% endif %}>Action</th>
|
||||
<th {% if current_user.role.name not in ['Administrator','Operator'] %}width="6%"{% else %}width="25%"{% endif %}>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -156,7 +156,7 @@
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
PDNS_VERSION = '{{ SETTING.get("pdns_version") }}'
|
||||
PDNS_VERSION = '{{ SETTING.get("pdns_version") }}';
|
||||
// set up history data table
|
||||
$("#tbl_history").DataTable({
|
||||
"paging" : false,
|
||||
@ -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);
|
||||
|
@ -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'] %}
|
||||
<td width="25%">
|
||||
<button type="button" class="btn btn-flat btn-success button_template" id="{{ domain.name }}">
|
||||
Template <i class="fa fa-clone"></i>
|
||||
|
@ -57,13 +57,13 @@
|
||||
{{ record.type }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.status }}
|
||||
{{ record.status }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.ttl }}
|
||||
{{ record.ttl }}
|
||||
</td>
|
||||
<td>
|
||||
{{ record.data }}
|
||||
{{ record.data }}
|
||||
</td>
|
||||
{% if domain.type != 'Slave' %}
|
||||
<td width="6%">
|
||||
@ -104,7 +104,6 @@
|
||||
{% endblock %}
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
PDNS_VERSION = '{{ SETTING.get("pdns_version") }}'
|
||||
// superglobals
|
||||
window.records_allow_edit = {{ editable_records|tojson }};
|
||||
window.nEditing = null;
|
||||
@ -361,7 +360,7 @@
|
||||
record_data.val(data);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
modal.modal('show');
|
||||
} else if (record_type == "SOA") {
|
||||
var modal = $("#modal_custom_record");
|
||||
if (record_data.val() == "") {
|
||||
|
@ -174,7 +174,7 @@
|
||||
<h3 class="box-title">Domain Deletion</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All records and user privileges which associated to this domain will also be removed. This change cannot be reverted.</p>
|
||||
<p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All records and user privileges associated with this domain will also be removed. This change cannot be reverted.</p>
|
||||
<button type="button" class="btn btn-flat btn-danger pull-left delete_domain" id="{{ domain.name }}">
|
||||
<i class="fa fa-trash"></i> DELETE DOMAIN {{ domain.name }}
|
||||
</button>
|
||||
|
@ -20,22 +20,22 @@
|
||||
<section class="content">
|
||||
{% with errors = get_flashed_messages(category_filter=["error"]) %} {% if errors %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert"
|
||||
aria-hidden="true">×</button>
|
||||
<h4>
|
||||
<i class="icon fa fa-ban"></i> Error!
|
||||
</h4>
|
||||
<div class="alert-message block-message error">
|
||||
<a class="close" href="#">x</a>
|
||||
<ul>
|
||||
{%- for msg in errors %}
|
||||
<li>{{ msg }}</li> {% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert"
|
||||
aria-hidden="true">×</button>
|
||||
<h4>
|
||||
<i class="icon fa fa-ban"></i> Error!
|
||||
</h4>
|
||||
<div class="alert-message block-message error">
|
||||
<a class="close" href="#">x</a>
|
||||
<ul>
|
||||
{%- for msg in errors %}
|
||||
<li>{{ msg }}</li> {% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %} {% endwith %}
|
||||
<div class="row">
|
||||
@ -45,11 +45,11 @@
|
||||
<h3 class="box-title">Templates</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="{{ url_for('create_template') }}">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left">
|
||||
Create Template <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{{ url_for('create_template') }}">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left">
|
||||
Create Template <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="tbl_template_list" class="table table-bordered table-striped">
|
||||
@ -75,15 +75,15 @@
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('edit_template', template=template.name) }}">
|
||||
<button type="button" class="btn btn-flat btn-warning button_edit" id="">
|
||||
Edit <i class="fa fa-edit"></i>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{{ url_for('delete_template', template=template.name) }}">
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="">
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</a>
|
||||
<button type="button" class="btn btn-flat btn-warning button_edit" id="btn_edit">
|
||||
Edit <i class="fa fa-edit"></i>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{{ url_for('delete_template', template=template.name) }}">
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="btn_delete">
|
||||
Delete <i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -115,12 +115,23 @@
|
||||
"lengthMenu": " _MENU_ records"
|
||||
},
|
||||
"retrieve" : true,
|
||||
"columnDefs": [{
|
||||
"targets": [ 7 ],
|
||||
"visible": false,
|
||||
"searchable": false
|
||||
}]
|
||||
|
||||
"columnDefs": [
|
||||
{
|
||||
type: 'natural',
|
||||
targets: [0, 4]
|
||||
},
|
||||
{
|
||||
// hidden column so that we can add new records on top
|
||||
// regardless of whatever sorting is done
|
||||
visible: false,
|
||||
targets: [ 7 ]
|
||||
},
|
||||
{
|
||||
className: "length-break",
|
||||
targets: [ 4 ]
|
||||
}
|
||||
],
|
||||
"orderFixed": [[7, 'asc']]
|
||||
});
|
||||
|
||||
// handle delete button
|
||||
@ -132,15 +143,19 @@
|
||||
var nRow = $(this).parents('tr')[0];
|
||||
var info = "Are you sure you want to delete " + record + "?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_delete_confirm').click(function() {
|
||||
table.row(nRow).remove().draw();
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
|
||||
$("#button_delete_confirm").unbind().one('click', function(e) {
|
||||
table.row(nRow).remove().draw();
|
||||
modal.modal('hide');
|
||||
});
|
||||
|
||||
$("#button_delete_cancel").unbind().one('click', function(e) {
|
||||
modal.modal('hide');
|
||||
});
|
||||
});
|
||||
// handle edit button
|
||||
$(document.body).on("click", ".button_edit, .row_record", function(e) {
|
||||
// handle edit button and record click
|
||||
$(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function(e) {
|
||||
e.stopPropagation();
|
||||
if ($(this).is('tr')) {
|
||||
var nRow = $(this)[0];
|
||||
@ -176,7 +191,9 @@
|
||||
var template = $(this).prop('id');
|
||||
var info = "Are you sure you want to apply your changes?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_apply_confirm').click(function() {
|
||||
|
||||
// following unbind("click") is to avoid multiple times execution
|
||||
modal.find('#button_apply_confirm').unbind("click").click(function() {
|
||||
var data = getTableData(table);
|
||||
applyChanges(data, '/template/' + template + '/apply', true);
|
||||
modal.modal('hide');
|
||||
@ -188,15 +205,19 @@
|
||||
// handle add record button
|
||||
$(document.body).on("click", ".button_add_record", function (e) {
|
||||
if (nNew || nEditing) {
|
||||
// TODO: replace this alert with modal
|
||||
alert("Previous record not saved. Please save it before adding more record.")
|
||||
var modal = $("#modal_error");
|
||||
modal.find('.modal-body p').text("Previous record not saved. Please save it before adding more record.");
|
||||
modal.modal('show');
|
||||
return;
|
||||
}
|
||||
var table = $("#tbl_records").DataTable();
|
||||
// clear search first
|
||||
$("#tbl_records").DataTable().search('').columns().search('').draw();
|
||||
|
||||
var aiNew = table.row.add(['', 'A', 'Active', 3600, '', '', '', '']).draw();
|
||||
var nRow = aiNew.index();
|
||||
editRow(table, nRow);
|
||||
// add new row
|
||||
var default_type = records_allow_edit[0]
|
||||
var nRow = jQuery('#tbl_records').dataTable().fnAddData(['', default_type, 'Active', 3600, '', '', '', '0']);
|
||||
editRow($("#tbl_records").DataTable(), nRow);
|
||||
document.getElementById("edit-row-focus").focus();
|
||||
nEditing = nRow;
|
||||
nNew = true;
|
||||
});
|
||||
@ -229,20 +250,50 @@
|
||||
$(document.body).on("focus", "#current_edit_record_data", function (e) {
|
||||
var record_type = $(this).parents("tr").find('#record_type').val();
|
||||
var record_data = $(this);
|
||||
if (record_type == "MX") {
|
||||
if (record_type == "CAA") {
|
||||
var modal = $("#modal_custom_record");
|
||||
if (record_data.val() == "") {
|
||||
var form = " <label for=\"caa_flag\">CAA Flag</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\"> \
|
||||
<label for=\"caa_tag\">CAA Tag</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\"> \
|
||||
<label for=\"caa_value\">CAA Value</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\"> \
|
||||
";
|
||||
} else {
|
||||
var parts = record_data.val().split(" ");
|
||||
var form = " <label for=\"caa_flag\">CAA Flag</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\" value=\"" + parts[0] + "\"> \
|
||||
<label for=\"caa_tag\">CAA Tag</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\" value=\"" + parts[1] + "\"> \
|
||||
<label for=\"caa_value\">CAA Value</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\" value=\"" + parts[2] + "\"> \
|
||||
";
|
||||
}
|
||||
modal.find('.modal-body p').html(form);
|
||||
modal.find('#button_save').click(function() {
|
||||
caa_flag = modal.find('#caa_flag').val();
|
||||
caa_tag = modal.find('#caa_tag').val();
|
||||
caa_value = modal.find('#caa_value').val();
|
||||
data = caa_flag + " " + caa_tag + " " + '"' + caa_value + '"';
|
||||
record_data.val(data);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
} else if (record_type == "MX") {
|
||||
var modal = $("#modal_custom_record");
|
||||
if (record_data.val() == "") {
|
||||
var form = " <label for=\"mx_priority\">MX Priority</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"10\"> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\"> \
|
||||
<label for=\"mx_server\">MX Server</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"postfix.example.com\"> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\"> \
|
||||
";
|
||||
} else {
|
||||
var parts = record_data.val().split(" ");
|
||||
var form = " <label for=\"mx_priority\">MX Priority</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"10\" value=\"" + parts[0] + "\"> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\" value=\"" + parts[0] + "\"> \
|
||||
<label for=\"mx_server\">MX Server</label> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"postfix.example.com\" value=\"" + parts[1] + "\"> \
|
||||
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\" value=\"" + parts[1] + "\"> \
|
||||
";
|
||||
}
|
||||
modal.find('.modal-body p').html(form);
|
||||
@ -360,7 +411,7 @@
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-default pull-left"
|
||||
<button type="button" class="btn btn-flat btn-default pull-left" id="button_delete_cancel"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
|
||||
</div>
|
||||
|
@ -161,8 +161,7 @@
|
||||
// handle checkbox toggling
|
||||
$('.otp_toggle').on('ifToggled', function(event) {
|
||||
var enable_otp = $(this).prop('checked');
|
||||
var username = $(this).prop('id');
|
||||
postdata = {
|
||||
var postdata = {
|
||||
'action' : 'enable_otp',
|
||||
'data' : {
|
||||
'enable_otp' : enable_otp
|
||||
|
180
app/views.py
180
app/views.py
@ -1,5 +1,4 @@
|
||||
import base64
|
||||
import json
|
||||
import logging as logger
|
||||
import os
|
||||
import traceback
|
||||
@ -10,22 +9,19 @@ from functools import wraps
|
||||
from io import BytesIO
|
||||
from ast import literal_eval
|
||||
|
||||
import jinja2
|
||||
import qrcode as qrc
|
||||
import qrcode.image.svg as qrc_svg
|
||||
from flask import g, request, make_response, jsonify, render_template, session, redirect, url_for, send_from_directory, abort, flash
|
||||
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, Account, Domain, Record, Role, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
|
||||
from app import app, login_manager
|
||||
from app.lib import utils
|
||||
from app.oauth import github_oauth, google_oauth
|
||||
from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec
|
||||
from app.decorators import admin_role_required, operator_role_required, can_access_domain, can_configure_dnssec, can_create_domain
|
||||
|
||||
if app.config['SAML_ENABLED']:
|
||||
from onelogin.saml2.auth import OneLogin_Saml2_Auth
|
||||
from onelogin.saml2.utils import OneLogin_Saml2_Utils
|
||||
|
||||
google = None
|
||||
@ -38,6 +34,7 @@ app.jinja_env.filters['display_record_name'] = utils.display_record_name
|
||||
app.jinja_env.filters['display_master_name'] = utils.display_master_name
|
||||
app.jinja_env.filters['display_second_to_time'] = utils.display_time
|
||||
app.jinja_env.filters['email_to_gravatar_url'] = utils.email_to_gravatar_url
|
||||
app.jinja_env.filters['display_setting_state'] = utils.display_setting_state
|
||||
|
||||
|
||||
@app.context_processor
|
||||
@ -68,7 +65,7 @@ def before_request():
|
||||
|
||||
# check site maintenance mode
|
||||
maintenance = Setting().get('maintenance')
|
||||
if maintenance and current_user.is_authenticated and current_user.role.name != 'Administrator':
|
||||
if maintenance and current_user.is_authenticated and current_user.role.name not in ['Administrator', 'Operator']:
|
||||
return render_template('maintenance.html')
|
||||
|
||||
|
||||
@ -284,7 +281,6 @@ def saml_authorized():
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
@login_manager.unauthorized_handler
|
||||
def login():
|
||||
LOGIN_TITLE = app.config['LOGIN_TITLE'] if 'LOGIN_TITLE' in app.config.keys() else ''
|
||||
SAML_ENABLED = app.config.get('SAML_ENABLED')
|
||||
|
||||
if g.user is not None and current_user.is_authenticated:
|
||||
@ -454,7 +450,7 @@ def dashboard():
|
||||
BG_DOMAIN_UPDATE = Setting().get('bg_domain_updates')
|
||||
if not BG_DOMAIN_UPDATE:
|
||||
logging.debug('Update domains in foreground')
|
||||
d = Domain().update()
|
||||
Domain().update()
|
||||
else:
|
||||
logging.debug('Update domains in background')
|
||||
|
||||
@ -476,7 +472,7 @@ def dashboard():
|
||||
@app.route('/dashboard-domains', methods=['GET'])
|
||||
@login_required
|
||||
def dashboard_domains():
|
||||
if current_user.role.name == 'Administrator':
|
||||
if current_user.role.name in ['Administrator', 'Operator']:
|
||||
domains = Domain.query
|
||||
else:
|
||||
domains = User(id=current_user.id).get_domain_query()
|
||||
@ -508,7 +504,7 @@ def dashboard_domains():
|
||||
start = "" if search.startswith("^") else "%"
|
||||
end = "" if search.endswith("$") else "%"
|
||||
|
||||
if current_user.role.name == 'Administrator':
|
||||
if current_user.role.name in ['Administrator', 'Operator']:
|
||||
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))
|
||||
@ -572,7 +568,7 @@ def domain(domain_name):
|
||||
# can not get any record, API server might be down
|
||||
return redirect(url_for('error', code=500))
|
||||
|
||||
quick_edit = Setting().get('allow_quick_edit')
|
||||
quick_edit = Setting().get('record_quick_edit')
|
||||
records_allow_to_edit = Setting().get_records_allow_to_edit()
|
||||
forward_records_allow_to_edit = Setting().get_forward_records_allow_to_edit()
|
||||
reverse_records_allow_to_edit = Setting().get_reverse_records_allow_to_edit()
|
||||
@ -580,7 +576,7 @@ def domain(domain_name):
|
||||
|
||||
if StrictVersion(Setting().get('pdns_version')) >= StrictVersion('4.0.0'):
|
||||
for jr in jrecords:
|
||||
if jr['type'] in Setting().get_records_allow_to_edit():
|
||||
if jr['type'] in records_allow_to_edit:
|
||||
for subrecord in jr['records']:
|
||||
record = Record(name=jr['name'], type=jr['type'], status='Disabled' if subrecord['disabled'] else 'Active', ttl=jr['ttl'], data=subrecord['content'])
|
||||
records.append(record)
|
||||
@ -591,7 +587,7 @@ def domain(domain_name):
|
||||
return render_template('domain.html', domain=domain, records=records, editable_records=editable_records, quick_edit=quick_edit)
|
||||
else:
|
||||
for jr in jrecords:
|
||||
if jr['type'] in Setting().get_records_allow_to_edit():
|
||||
if jr['type'] in records_allow_to_edit:
|
||||
record = Record(name=jr['name'], type=jr['type'], status='Disabled' if jr['disabled'] else 'Active', ttl=jr['ttl'], data=jr['content'])
|
||||
records.append(record)
|
||||
if not re.search('ip6\.arpa|in-addr\.arpa$', domain_name):
|
||||
@ -603,7 +599,7 @@ def domain(domain_name):
|
||||
|
||||
@app.route('/admin/domain/add', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@can_create_domain
|
||||
def domain_add():
|
||||
templates = DomainTemplate.query.all()
|
||||
if request.method == 'POST':
|
||||
@ -632,6 +628,11 @@ def domain_add():
|
||||
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, 'account_id': account_id}), created_by=current_user.username)
|
||||
history.add()
|
||||
|
||||
# grant user access to the domain
|
||||
Domain(name=domain_name).grant_privileges([current_user.username])
|
||||
|
||||
# apply template if needed
|
||||
if domain_template != '0':
|
||||
template = DomainTemplate.query.filter(DomainTemplate.id == domain_template).first()
|
||||
template_records = DomainTemplateRecord.query.filter(DomainTemplateRecord.template_id == domain_template).all()
|
||||
@ -651,7 +652,7 @@ def domain_add():
|
||||
else:
|
||||
return render_template('errors/400.html', msg=result['msg']), 400
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return redirect(url_for('error', code=500))
|
||||
|
||||
else:
|
||||
@ -661,7 +662,7 @@ def domain_add():
|
||||
|
||||
@app.route('/admin/domain/<path:domain_name>/delete', methods=['GET'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def domain_delete(domain_name):
|
||||
d = Domain()
|
||||
result = d.delete(domain_name)
|
||||
@ -677,7 +678,7 @@ def domain_delete(domain_name):
|
||||
|
||||
@app.route('/admin/domain/<path:domain_name>/manage', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def domain_management(domain_name):
|
||||
if request.method == 'GET':
|
||||
domain = Domain.query.filter(Domain.name == domain_name).first()
|
||||
@ -697,12 +698,9 @@ def domain_management(domain_name):
|
||||
# username in right column
|
||||
new_user_list = request.form.getlist('domain_multi_user[]')
|
||||
|
||||
# get list of user ids to compare
|
||||
d = Domain(name=domain_name)
|
||||
domain_user_ids = d.get_user()
|
||||
|
||||
# grant/revoke user privielges
|
||||
d.grant_privielges(new_user_list)
|
||||
d = Domain(name=domain_name)
|
||||
d.grant_privileges(new_user_list)
|
||||
|
||||
history = History(msg='Change domain {0} access control'.format(domain_name), detail=str({'user_has_access': new_user_list}), created_by=current_user.username)
|
||||
history.add()
|
||||
@ -712,13 +710,13 @@ def domain_management(domain_name):
|
||||
|
||||
@app.route('/admin/domain/<path:domain_name>/change_soa_setting', methods=['POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def domain_change_soa_edit_api(domain_name):
|
||||
domain = Domain.query.filter(Domain.name == domain_name).first()
|
||||
if not domain:
|
||||
return redirect(url_for('error', code=404))
|
||||
new_setting = request.form.get('soa_edit_api')
|
||||
if new_setting == None:
|
||||
if new_setting is None:
|
||||
return redirect(url_for('error', code=500))
|
||||
if new_setting == '0':
|
||||
return redirect(url_for('domain_management', domain_name=domain_name))
|
||||
@ -738,7 +736,7 @@ def domain_change_soa_edit_api(domain_name):
|
||||
|
||||
@app.route('/admin/domain/<path:domain_name>/change_account', methods=['POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def domain_change_account(domain_name):
|
||||
domain = Domain.query.filter(Domain.name == domain_name).first()
|
||||
if not domain:
|
||||
@ -787,7 +785,7 @@ def record_apply(domain_name):
|
||||
else:
|
||||
return make_response(jsonify( result ), 400)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
|
||||
|
||||
|
||||
@ -810,13 +808,13 @@ def record_update(domain_name):
|
||||
else:
|
||||
return make_response(jsonify( {'status': 'error', 'msg': result['msg']} ), 500)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
|
||||
|
||||
|
||||
@app.route('/domain/<path:domain_name>/record/<path:record_name>/type/<path:record_type>/delete', methods=['GET'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def record_delete(domain_name, record_name, record_type):
|
||||
try:
|
||||
r = Record(name=record_name, type=record_type)
|
||||
@ -824,7 +822,7 @@ def record_delete(domain_name, record_name, record_type):
|
||||
if result['status'] == 'error':
|
||||
print(result['msg'])
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return redirect(url_for('error', code=500)), 500
|
||||
return redirect(url_for('domain', domain_name=domain_name))
|
||||
|
||||
@ -866,14 +864,14 @@ def domain_dnssec_disable(domain_name):
|
||||
dnssec = domain.get_domain_dnssec(domain_name)
|
||||
|
||||
for key in dnssec['dnssec']:
|
||||
response = domain.delete_dnssec_key(domain_name,key['id']);
|
||||
domain.delete_dnssec_key(domain_name,key['id']);
|
||||
|
||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'DNSSEC removed.' } ))
|
||||
|
||||
|
||||
@app.route('/domain/<path:domain_name>/managesetting', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_setdomainsetting(domain_name):
|
||||
if request.method == 'POST':
|
||||
#
|
||||
@ -907,14 +905,14 @@ def admin_setdomainsetting(domain_name):
|
||||
else:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
|
||||
|
||||
|
||||
@app.route('/templates', methods=['GET', 'POST'])
|
||||
@app.route('/templates/list', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def templates():
|
||||
templates = DomainTemplate.query.all()
|
||||
return render_template('template.html', templates=templates)
|
||||
@ -922,7 +920,7 @@ def templates():
|
||||
|
||||
@app.route('/template/create', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def create_template():
|
||||
if request.method == 'GET':
|
||||
return render_template('template_add.html')
|
||||
@ -938,6 +936,7 @@ def create_template():
|
||||
if DomainTemplate.query.filter(DomainTemplate.name == name).first():
|
||||
flash("A template with the name {0} already exists!".format(name), 'error')
|
||||
return redirect(url_for('create_template'))
|
||||
|
||||
t = DomainTemplate(name=name, description=description)
|
||||
result = t.create()
|
||||
if result['status'] == 'ok':
|
||||
@ -948,14 +947,13 @@ def create_template():
|
||||
flash(result['msg'], 'error')
|
||||
return redirect(url_for('create_template'))
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return redirect(url_for('error', code=500))
|
||||
return redirect(url_for('templates'))
|
||||
|
||||
|
||||
@app.route('/template/createfromzone', methods=['POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def create_template_from_zone():
|
||||
try:
|
||||
jdata = request.json
|
||||
@ -1003,33 +1001,35 @@ def create_template_from_zone():
|
||||
if result_records['status'] == 'ok':
|
||||
return make_response(jsonify({'status': 'ok', 'msg': result['msg']}), 200)
|
||||
else:
|
||||
result = t.delete_template()
|
||||
t.delete_template()
|
||||
return make_response(jsonify({'status': 'error', 'msg': result_records['msg']}), 500)
|
||||
|
||||
else:
|
||||
return make_response(jsonify({'status': 'error', 'msg': result['msg']}), 500)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500)
|
||||
|
||||
|
||||
@app.route('/template/<path:template>/edit', methods=['GET'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def edit_template(template):
|
||||
try:
|
||||
t = DomainTemplate.query.filter(DomainTemplate.name == template).first()
|
||||
records_allow_to_edit = Setting().get_records_allow_to_edit()
|
||||
quick_edit = Setting().get('record_quick_edit')
|
||||
if t is not None:
|
||||
records = []
|
||||
for jr in t.records:
|
||||
if jr.type in records_allow_to_edit:
|
||||
record = DomainTemplateRecord(name=jr.name, type=jr.type, status='Disabled' if jr.status else 'Active', ttl=jr.ttl, data=jr.data)
|
||||
records.append(record)
|
||||
record = DomainTemplateRecord(name=jr.name, type=jr.type, status='Disabled' if jr.status else 'Active', ttl=jr.ttl, data=jr.data)
|
||||
records.append(record)
|
||||
|
||||
return render_template('template_edit.html', template=t.name, records=records, editable_records=records_allow_to_edit)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
return render_template('template_edit.html', template=t.name, records=records, editable_records=records_allow_to_edit, quick_edit=quick_edit)
|
||||
except Exception as e:
|
||||
logging.error('Cannot open domain template page. DETAIL: {0}'.format(e))
|
||||
logging.debug(traceback.format_exc())
|
||||
return redirect(url_for('error', code=500))
|
||||
return redirect(url_for('templates'))
|
||||
|
||||
@ -1060,13 +1060,13 @@ def apply_records(template):
|
||||
else:
|
||||
return make_response(jsonify(result), 400)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500)
|
||||
|
||||
|
||||
@app.route('/template/<path:template>/delete', methods=['GET'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def delete_template(template):
|
||||
try:
|
||||
t = DomainTemplate.query.filter(DomainTemplate.name == template).first()
|
||||
@ -1080,15 +1080,15 @@ def delete_template(template):
|
||||
flash(result['msg'], 'error')
|
||||
return redirect(url_for('templates'))
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return redirect(url_for('error', code=500))
|
||||
return redirect(url_for('templates'))
|
||||
|
||||
|
||||
@app.route('/admin', methods=['GET', 'POST'])
|
||||
@app.route('/admin/pdns', methods=['GET'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
def admin():
|
||||
@operator_role_required
|
||||
def admin_pdns():
|
||||
if not Setting().get('pdns_api_url') or not Setting().get('pdns_api_key') or not Setting().get('pdns_version'):
|
||||
return redirect(url_for('admin_setting_pdns'))
|
||||
|
||||
@ -1111,7 +1111,7 @@ def admin():
|
||||
@app.route('/admin/user/edit/<user_username>', methods=['GET', 'POST'])
|
||||
@app.route('/admin/user/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_edituser(user_username=None):
|
||||
if request.method == 'GET':
|
||||
if not user_username:
|
||||
@ -1150,11 +1150,12 @@ def admin_edituser(user_username=None):
|
||||
|
||||
@app.route('/admin/manageuser', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_manageuser():
|
||||
if request.method == 'GET':
|
||||
roles = Role.query.all()
|
||||
users = User.query.order_by(User.username).all()
|
||||
return render_template('admin_manageuser.html', users=users)
|
||||
return render_template('admin_manageuser.html', users=users, roles=roles)
|
||||
|
||||
if request.method == 'POST':
|
||||
#
|
||||
@ -1197,30 +1198,42 @@ def admin_manageuser():
|
||||
else:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot revoke user privilege.' } ), 500)
|
||||
|
||||
elif jdata['action'] == 'set_admin':
|
||||
elif jdata['action'] == 'update_user_role':
|
||||
username = data['username']
|
||||
role_name = data['role_name']
|
||||
|
||||
if username == current_user.username:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'You cannot change you own admin rights.' } ), 400)
|
||||
is_admin = data['is_admin']
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'You cannot change you own roles.' } ), 400)
|
||||
|
||||
user = User.query.filter(User.username==username).first()
|
||||
if not user:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'User does not exist.' } ), 404)
|
||||
|
||||
if user.role.name == 'Administrator' and current_user.role.name != 'Administrator':
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'You do not have permission to change Administrator users role.' } ), 400)
|
||||
|
||||
if role_name == 'Administrator' and current_user.role.name != 'Administrator':
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'You do not have permission to promote a user to Administrator role.' } ), 400)
|
||||
|
||||
user = User(username=username)
|
||||
result = user.set_admin(is_admin)
|
||||
if result:
|
||||
history = History(msg='Change user role of {0}'.format(username), created_by=current_user.username)
|
||||
result = user.set_role(role_name)
|
||||
if result['status']:
|
||||
history = History(msg='Change user role of {0} to {1}'.format(username, role_name), created_by=current_user.username)
|
||||
history.add()
|
||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Changed user role successfully.' } ), 200)
|
||||
else:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot change user role.' } ), 500)
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot change user role. {0}'.format(result['msg']) } ), 500)
|
||||
else:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_exc())
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
|
||||
|
||||
|
||||
@app.route('/admin/account/edit/<account_name>', methods=['GET', 'POST'])
|
||||
@app.route('/admin/account/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_editaccount(account_name=None):
|
||||
users = User.query.all()
|
||||
|
||||
@ -1274,7 +1287,7 @@ def admin_editaccount(account_name=None):
|
||||
|
||||
@app.route('/admin/manageaccount', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_manageaccount():
|
||||
if request.method == 'GET':
|
||||
accounts = Account.query.order_by(Account.name).all()
|
||||
@ -1302,21 +1315,23 @@ def admin_manageaccount():
|
||||
else:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
|
||||
except:
|
||||
logging.error(traceback.print_exc())
|
||||
logging.error(traceback.format_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
|
||||
@operator_role_required
|
||||
def admin_history():
|
||||
if request.method == 'POST':
|
||||
if current_user.role != 'Administrator':
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'You do not have permission to remove history.' } ), 401)
|
||||
|
||||
h = History()
|
||||
result = h.remove_all()
|
||||
if result:
|
||||
history = History(msg='Remove all histories', created_by=current_user.username)
|
||||
history.add()
|
||||
|
||||
return make_response(jsonify( { 'status': 'ok', 'msg': 'Changed user role successfully.' } ), 200)
|
||||
else:
|
||||
return make_response(jsonify( { 'status': 'error', 'msg': 'Can not remove histories.' } ), 500)
|
||||
@ -1328,16 +1343,29 @@ def admin_history():
|
||||
|
||||
@app.route('/admin/setting/basic', methods=['GET'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_setting_basic():
|
||||
if request.method == 'GET':
|
||||
settings = Setting.query.filter(Setting.view=='basic').all()
|
||||
settings = ['maintenance',
|
||||
'fullscreen_layout',
|
||||
'record_helper',
|
||||
'login_ldap_first',
|
||||
'default_record_table_size',
|
||||
'default_domain_table_size',
|
||||
'auto_ptr',
|
||||
'record_quick_edit',
|
||||
'pretty_ipv6_ptr',
|
||||
'dnssec_admins_only',
|
||||
'allow_user_create_domain',
|
||||
'bg_domain_updates',
|
||||
'site_name']
|
||||
|
||||
return render_template('admin_setting_basic.html', settings=settings)
|
||||
|
||||
|
||||
@app.route('/admin/setting/basic/<path:setting>/edit', methods=['POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_setting_basic_edit(setting):
|
||||
jdata = request.json
|
||||
new_value = jdata['value']
|
||||
@ -1351,7 +1379,7 @@ def admin_setting_basic_edit(setting):
|
||||
|
||||
@app.route('/admin/setting/basic/<path:setting>/toggle', methods=['POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_setting_basic_toggle(setting):
|
||||
result = Setting().toggle(setting)
|
||||
if (result):
|
||||
@ -1383,11 +1411,14 @@ def admin_setting_pdns():
|
||||
|
||||
@app.route('/admin/setting/dns-records', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_role_required
|
||||
@operator_role_required
|
||||
def admin_setting_records():
|
||||
if request.method == 'GET':
|
||||
f_records = literal_eval(Setting().get('forward_records_allow_edit'))
|
||||
r_records = literal_eval(Setting().get('reverse_records_allow_edit'))
|
||||
_fr = Setting().get('forward_records_allow_edit')
|
||||
_rr = Setting().get('reverse_records_allow_edit')
|
||||
f_records = literal_eval(_fr) if isinstance(_fr, str) else _fr
|
||||
r_records = literal_eval(_rr) if isinstance(_rr, str) else _rr
|
||||
|
||||
return render_template('admin_setting_records.html', f_records=f_records, r_records=r_records)
|
||||
elif request.method == 'POST':
|
||||
fr = {}
|
||||
@ -1438,6 +1469,7 @@ def admin_setting_authentication():
|
||||
Setting().set('ldap_filter_username', request.form.get('ldap_filter_username'))
|
||||
Setting().set('ldap_sg_enabled', True if request.form.get('ldap_sg_enabled')=='ON' else False)
|
||||
Setting().set('ldap_admin_group', request.form.get('ldap_admin_group'))
|
||||
Setting().set('ldap_operator_group', request.form.get('ldap_operator_group'))
|
||||
Setting().set('ldap_user_group', request.form.get('ldap_user_group'))
|
||||
result = {'status': True, 'msg': 'Saved successfully'}
|
||||
elif conf_type == 'google':
|
||||
|
@ -41,6 +41,11 @@ else
|
||||
fi
|
||||
|
||||
echo "===> Update PDNS API connection info"
|
||||
# initial setting if not available in the DB
|
||||
mysql -h${PDA_DB_HOST} -u${PDA_DB_USER} -p${PDA_DB_PASSWORD} ${PDA_DB_NAME} -e "INSERT INTO setting (name, value) SELECT * FROM (SELECT 'pdns_api_url', 'http://${PDNS_HOST}:8081') AS tmp WHERE NOT EXISTS (SELECT name FROM setting WHERE name = 'pdns_api_url') LIMIT 1;"
|
||||
mysql -h${PDA_DB_HOST} -u${PDA_DB_USER} -p${PDA_DB_PASSWORD} ${PDA_DB_NAME} -e "INSERT INTO setting (name, value) SELECT * FROM (SELECT 'pdns_api_key', '${PDNS_API_KEY}') AS tmp WHERE NOT EXISTS (SELECT name FROM setting WHERE name = 'pdns_api_key') LIMIT 1;"
|
||||
|
||||
# update pdns api setting if .env is changed.
|
||||
mysql -h${PDA_DB_HOST} -u${PDA_DB_USER} -p${PDA_DB_PASSWORD} ${PDA_DB_NAME} -e "UPDATE setting SET value='http://${PDNS_HOST}:8081' WHERE name='pdns_api_url';"
|
||||
mysql -h${PDA_DB_HOST} -u${PDA_DB_USER} -p${PDA_DB_PASSWORD} ${PDA_DB_NAME} -e "UPDATE setting SET value='${PDNS_API_KEY}' WHERE name='pdns_api_key';"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from app import app, db
|
||||
from app.models import Role, Setting, DomainTemplate
|
||||
from app import db
|
||||
from app.models import Role, DomainTemplate
|
||||
|
||||
admin_role = Role(name='Administrator', description='Administrator')
|
||||
user_role = Role(name='User', description='User')
|
||||
|
@ -0,0 +1,30 @@
|
||||
"""Remove all setting in the DB
|
||||
|
||||
Revision ID: 31a4ed468b18
|
||||
Revises: 4a666113c7bb
|
||||
Create Date: 2018-08-21 17:12:30.058782
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '31a4ed468b18'
|
||||
down_revision = '4a666113c7bb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# delete all settings from "setting" table.
|
||||
# PDA should work without initial settings in the DB
|
||||
# Once user change the settings from UI, they will be
|
||||
# written to the DB.
|
||||
op.execute("DELETE FROM setting")
|
||||
|
||||
# drop view column since we don't need it
|
||||
op.drop_column('setting', 'view')
|
||||
|
||||
def downgrade():
|
||||
op.add_column('setting', sa.Column('view', sa.String(length=64), nullable=True))
|
61
migrations/versions/4a666113c7bb_add_operator_role.py
Normal file
61
migrations/versions/4a666113c7bb_add_operator_role.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Adding Operator Role
|
||||
|
||||
Revision ID: 4a666113c7bb
|
||||
Revises: 1274ed462010
|
||||
Create Date: 2018-08-30 13:28:06.836208
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4a666113c7bb'
|
||||
down_revision = '1274ed462010'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def update_data():
|
||||
setting_table = sa.sql.table('setting',
|
||||
sa.sql.column('id', sa.Integer),
|
||||
sa.sql.column('name', sa.String),
|
||||
sa.sql.column('value', sa.String),
|
||||
sa.sql.column('view', sa.String)
|
||||
)
|
||||
|
||||
# add new settings
|
||||
op.bulk_insert(setting_table,
|
||||
[
|
||||
{'id': 44, 'name': 'ldap_operator_group', 'value': '', 'view': 'authentication'},
|
||||
{'id': 45, 'name': 'allow_user_create_domain', 'value': 'False', 'view': 'basic'},
|
||||
{'id': 46, 'name': 'record_quick_edit', 'value': 'True', 'view': 'basic'},
|
||||
]
|
||||
)
|
||||
|
||||
role_table = sa.sql.table('role',
|
||||
sa.sql.column('id', sa.Integer),
|
||||
sa.sql.column('name', sa.String),
|
||||
sa.sql.column('description', sa.String)
|
||||
)
|
||||
|
||||
# add new role
|
||||
op.bulk_insert(role_table,
|
||||
[
|
||||
{'id': 3, 'name': 'Operator', 'description': 'Operator'}
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
update_data()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# remove user Operator role
|
||||
op.execute("UPDATE user SET role_id = 2 WHERE role_id=3")
|
||||
op.execute("DELETE FROM role WHERE name = 'Operator'")
|
||||
|
||||
# delete settings
|
||||
op.execute("DELETE FROM setting WHERE name = 'ldap_operator_group'")
|
||||
op.execute("DELETE FROM setting WHERE name = 'allow_user_create_domain'")
|
6
run.py
6
run.py
@ -1,11 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
from app import app
|
||||
from config import PORT
|
||||
|
||||
try:
|
||||
from config import BIND_ADDRESS
|
||||
except:
|
||||
BIND_ADDRESS = '127.0.0.1'
|
||||
from config import BIND_ADDRESS
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug = True, host=BIND_ADDRESS, port=PORT)
|
||||
|
@ -10,8 +10,6 @@
|
||||
##############################################################
|
||||
|
||||
### Imports
|
||||
from app import app
|
||||
from app.lib import log
|
||||
from app.models import Domain
|
||||
from config import BG_DOMAIN_UPDATES
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user