Merge branch 'master' into activedirectory_authent

This commit is contained in:
odumasFR 2018-10-09 08:31:18 +02:00 committed by GitHub
commit de3d1b3665
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 46 additions and 38 deletions

View File

@ -30,12 +30,12 @@ def get_idp_data():
global idp_data, idp_timestamp global idp_data, idp_timestamp
lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME']) lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME'])
if idp_timestamp+lifetime < datetime.now(): if idp_timestamp+lifetime < datetime.now():
background_thread = Thread(target=retreive_idp_data) background_thread = Thread(target=retrieve_idp_data)
background_thread.start() background_thread.start()
return idp_data return idp_data
def retreive_idp_data(): def retrieve_idp_data():
global idp_data, idp_timestamp global idp_data, idp_timestamp
if 'SAML_IDP_SSO_BINDING' in app.config: if 'SAML_IDP_SSO_BINDING' in app.config:
new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING']) new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING'])
@ -44,9 +44,9 @@ def retreive_idp_data():
if new_idp_data is not None: if new_idp_data is not None:
idp_data = new_idp_data idp_data = new_idp_data
idp_timestamp = datetime.now() idp_timestamp = datetime.now()
print("SAML: IDP Metadata successfully retreived from: " + app.config['SAML_METADATA_URL']) print("SAML: IDP Metadata successfully retrieved from: " + app.config['SAML_METADATA_URL'])
else: else:
print("SAML: IDP Metadata could not be retreived") print("SAML: IDP Metadata could not be retrieved")
if 'TIMEOUT' in app.config.keys(): if 'TIMEOUT' in app.config.keys():

View File

@ -102,7 +102,7 @@ class User(db.Model):
return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt())
def check_password(self, hashed_password): def check_password(self, hashed_password):
# Check hased password. Useing bcrypt, the salt is saved into the hash itself # Check hased password. Using bcrypt, the salt is saved into the hash itself
if (self.plain_text_password): if (self.plain_text_password):
return bcrypt.checkpw(self.plain_text_password.encode('utf-8'), hashed_password.encode('utf-8')) return bcrypt.checkpw(self.plain_text_password.encode('utf-8'), hashed_password.encode('utf-8'))
return False return False
@ -451,7 +451,7 @@ class User(db.Model):
def revoke_privilege(self): def revoke_privilege(self):
""" """
Revoke all privielges from a user Revoke all privileges from a user
""" """
user = User.query.filter(User.username == self.username).first() user = User.query.filter(User.username == self.username).first()
@ -463,7 +463,7 @@ class User(db.Model):
return True return True
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
logging.error('Cannot revoke user {0} privielges. DETAIL: {1}'.format(self.username, e)) logging.error('Cannot revoke user {0} privileges. DETAIL: {1}'.format(self.username, e))
return False return False
return False return False
@ -614,7 +614,7 @@ class Account(db.Model):
db.session.commit() db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
logging.error('Cannot revoke user privielges on account {0}. DETAIL: {1}'.format(self.name, e)) logging.error('Cannot revoke user privileges on account {0}. DETAIL: {1}'.format(self.name, e))
try: try:
for uid in added_ids: for uid in added_ids:
@ -627,7 +627,7 @@ class Account(db.Model):
def revoke_privileges_by_id(self, user_id): def revoke_privileges_by_id(self, user_id):
""" """
Remove a single user from prigilege list based on user_id Remove a single user from privilege list based on user_id
""" """
new_uids = [u for u in self.get_user() if u != user_id] new_uids = [u for u in self.get_user() if u != user_id]
users = [] users = []
@ -646,7 +646,7 @@ class Account(db.Model):
return True return True
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
logging.error('Cannot add user privielges on account {0}. DETAIL: {1}'.format(self.name, e)) logging.error('Cannot add user privileges on account {0}. DETAIL: {1}'.format(self.name, e))
return False return False
def remove_user(self, user): def remove_user(self, user):
@ -659,7 +659,7 @@ class Account(db.Model):
return True return True
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
logging.error('Cannot revoke user privielges on account {0}. DETAIL: {1}'.format(self.name, e)) logging.error('Cannot revoke user privileges on account {0}. DETAIL: {1}'.format(self.name, e))
return False return False
@ -983,7 +983,7 @@ class Domain(db.Model):
if 0 != len(domain_users): if 0 != len(domain_users):
self.name = domain_reverse_name self.name = domain_reverse_name
self.grant_privileges(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 with granted privileges'}
return {'status': 'ok', 'msg': 'New reverse lookup domain created without users'} return {'status': 'ok', 'msg': 'New reverse lookup domain created without users'}
return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'} return {'status': 'ok', 'msg': 'Reverse lookup domain already exists'}
@ -1048,7 +1048,7 @@ class Domain(db.Model):
db.session.commit() db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
logging.error('Cannot revoke user privielges on domain {0}. DETAIL: {1}'.format(self.name, e)) logging.error('Cannot revoke user privileges on domain {0}. DETAIL: {1}'.format(self.name, e))
try: try:
for uid in added_ids: for uid in added_ids:
@ -1057,7 +1057,7 @@ class Domain(db.Model):
db.session.commit() db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
logging.error('Cannot grant user privielges to domain {0}. DETAIL: {1}'.format(self.name, e)) logging.error('Cannot grant user privileges to domain {0}. DETAIL: {1}'.format(self.name, e))
def update_from_master(self, domain_name): def update_from_master(self, domain_name):
""" """
@ -1819,6 +1819,7 @@ class Setting(db.Model):
'allow_user_create_domain': False, 'allow_user_create_domain': False,
'bg_domain_updates': False, 'bg_domain_updates': False,
'site_name': 'PowerDNS-Admin', 'site_name': 'PowerDNS-Admin',
'session_timeout': 10,
'pdns_api_url': '', 'pdns_api_url': '',
'pdns_api_key': '', 'pdns_api_key': '',
'pdns_version': '4.1.1', 'pdns_version': '4.1.1',

View File

@ -129,7 +129,7 @@ function editRow(oTable, nRow) {
jqTds[5].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_save">Save</button>'; jqTds[5].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_save">Save</button>';
jqTds[6].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>'; jqTds[6].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>';
// set current value of dropdows column // set current value of dropdown column
if (aData[2] == 'Active'){ if (aData[2] == 'Active'){
isDisabled = 'false'; isDisabled = 'false';
} }

View File

@ -104,7 +104,7 @@
var info = "Are you sure you want to revoke all privileges for " + username + ". They will not able to access any domain."; var info = "Are you sure you want to revoke all privileges for " + username + ". They will not able to access any domain.";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_revoke_confirm').click(function() { modal.find('#button_revoke_confirm').click(function() {
var postdata = {'action': 'revoke_user_privielges', 'data': username} var postdata = {'action': 'revoke_user_privileges', 'data': username}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser'); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser');
modal.modal('hide'); modal.modal('hide');
}) })

View File

@ -505,7 +505,7 @@
} }
}); });
// init validation reqirement at first time page load // init validation requirement at first time page load
{% if SETTING.get('google_oauth_enabled') %} {% if SETTING.get('google_oauth_enabled') %}
$('#google_oauth_client_id').prop('required', true); $('#google_oauth_client_id').prop('required', true);
$('#google_oauth_client_secret').prop('required', true); $('#google_oauth_client_secret').prop('required', true);
@ -540,7 +540,7 @@
} }
}); });
// init validation reqirement at first time page load // init validation requirement at first time page load
{% if SETTING.get('google_oauth_enabled') %} {% if SETTING.get('google_oauth_enabled') %}
$('#github_oauth_key').prop('required', true); $('#github_oauth_key').prop('required', true);
$('#github_oauth_secret').prop('required', true); $('#github_oauth_secret').prop('required', true);

View File

@ -63,7 +63,7 @@
</div> </div>
<div class="box-body"> <div class="box-body">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<p>You must configure the API connection information before PowerDNS-Admiin can query your PowerDNS data. Following fields are required:</p> <p>You must configure the API connection information before PowerDNS-Admin can query your PowerDNS data. Following fields are required:</p>
<dt>PDNS API URL</dt> <dt>PDNS API URL</dt>
<dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd> <dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd>
<dt>PDNS API KEY</dt> <dt>PDNS API KEY</dt>

View File

@ -137,7 +137,7 @@
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<!-- TODO: add password and password confirmation comparisson check --> <!-- TODO: add password and password confirmation comparison check -->
<script> <script>
$(function() { $(function() {

View File

@ -3,6 +3,7 @@ import logging as logger
import os import os
import traceback import traceback
import re import re
import datetime
from distutils.util import strtobool from distutils.util import strtobool
from distutils.version import StrictVersion from distutils.version import StrictVersion
from functools import wraps from functools import wraps
@ -68,6 +69,11 @@ def before_request():
if maintenance and current_user.is_authenticated and current_user.role.name not in ['Administrator', 'Operator']: if maintenance and current_user.is_authenticated and current_user.role.name not in ['Administrator', 'Operator']:
return render_template('maintenance.html') return render_template('maintenance.html')
# Manage session timeout
session.permanent = True
app.permanent_session_lifetime = datetime.timedelta(minutes=int(Setting().get('session_timeout')))
session.modified = True
g.user = current_user
@login_manager.user_loader @login_manager.user_loader
def load_user(id): def load_user(id):
@ -689,7 +695,7 @@ def domain_management(domain_name):
users = User.query.all() users = User.query.all()
accounts = Account.query.all() accounts = Account.query.all()
# get list of user ids to initilize selection data # get list of user ids to initialize selection data
d = Domain(name=domain_name) d = Domain(name=domain_name)
domain_user_ids = d.get_user() domain_user_ids = d.get_user()
account = d.get_account() account = d.get_account()
@ -700,7 +706,7 @@ def domain_management(domain_name):
# username in right column # username in right column
new_user_list = request.form.getlist('domain_multi_user[]') new_user_list = request.form.getlist('domain_multi_user[]')
# grant/revoke user privielges # grant/revoke user privileges
d = Domain(name=domain_name) d = Domain(name=domain_name)
d.grant_privileges(new_user_list) d.grant_privileges(new_user_list)
@ -787,7 +793,7 @@ def record_apply(domain_name):
else: else:
return make_response(jsonify( result ), 400) return make_response(jsonify( result ), 400)
except Exception as e: except Exception as e:
logging.error('Canot apply record changes. Error: {0}'.format(e)) logging.error('Cannot apply record changes. Error: {0}'.format(e))
logging.debug(traceback.format_exc()) logging.debug(traceback.format_exc())
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500) return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
@ -1198,13 +1204,13 @@ def admin_manageuser():
else: else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot remove user.' } ), 500) return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot remove user.' } ), 500)
elif jdata['action'] == 'revoke_user_privielges': elif jdata['action'] == 'revoke_user_privileges':
user = User(username=data) user = User(username=data)
result = user.revoke_privilege() result = user.revoke_privilege()
if result: if result:
history = History(msg='Revoke {0} user privielges'.format(data), created_by=current_user.username) history = History(msg='Revoke {0} user privileges'.format(data), created_by=current_user.username)
history.add() history.add()
return make_response(jsonify( { 'status': 'ok', 'msg': 'Revoked user privielges.' } ), 200) return make_response(jsonify( { 'status': 'ok', 'msg': 'Revoked user privileges.' } ), 200)
else: else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot revoke user privilege.' } ), 500) return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot revoke user privilege.' } ), 500)
@ -1369,7 +1375,8 @@ def admin_setting_basic():
'dnssec_admins_only', 'dnssec_admins_only',
'allow_user_create_domain', 'allow_user_create_domain',
'bg_domain_updates', 'bg_domain_updates',
'site_name'] 'site_name',
'session_timeout' ]
return render_template('admin_setting_basic.html', settings=settings) return render_template('admin_setting_basic.html', settings=settings)

View File

@ -25,13 +25,13 @@ SQLA_DB_HOST = '127.0.0.1'
SQLA_DB_NAME = 'pda' SQLA_DB_NAME = 'pda'
SQLALCHEMY_TRACK_MODIFICATIONS = True SQLALCHEMY_TRACK_MODIFICATIONS = True
# DATBASE - MySQL # DATABASE - MySQL
SQLALCHEMY_DATABASE_URI = 'mysql://'+SQLA_DB_USER+':'+SQLA_DB_PASSWORD+'@'+SQLA_DB_HOST+'/'+SQLA_DB_NAME SQLALCHEMY_DATABASE_URI = 'mysql://'+SQLA_DB_USER+':'+SQLA_DB_PASSWORD+'@'+SQLA_DB_HOST+'/'+SQLA_DB_NAME
# DATABSE - SQLite # DATABASE - SQLite
# SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db') # SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
# SAML Authnetication # SAML Authentication
SAML_ENABLED = False SAML_ENABLED = False
SAML_DEBUG = True SAML_DEBUG = True
SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml') SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml')
@ -93,10 +93,10 @@ SAML_ATTRIBUTE_ACCOUNT = 'https://example.edu/pdns-account'
SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>' SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
SAML_SP_CONTACT_NAME = '<contact name>' SAML_SP_CONTACT_NAME = '<contact name>'
SAML_SP_CONTACT_MAIL = '<contact mail>' SAML_SP_CONTACT_MAIL = '<contact mail>'
#Cofigures if SAML tokens should be encrypted. #Configures if SAML tokens should be encrypted.
#If enabled a new app certificate will be generated on restart #If enabled a new app certificate will be generated on restart
SAML_SIGN_REQUEST = False SAML_SIGN_REQUEST = False
#Use SAML standard logout mechanism retreived from idp metadata #Use SAML standard logout mechanism retrieved from idp metadata
#If configured false don't care about SAML session on logout. #If configured false don't care about SAML session on logout.
#Logout from PowerDNS-Admin only and keep SAML session authenticated. #Logout from PowerDNS-Admin only and keep SAML session authenticated.
SAML_LOGOUT = False SAML_LOGOUT = False

View File

@ -24,7 +24,7 @@ SQLALCHEMY_DATABASE_URI = 'mysql://'+DB_USER+':'+DB_PASSWORD+'@'+DB_HOST+'/'+DB_
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository') SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
SQLALCHEMY_TRACK_MODIFICATIONS = True SQLALCHEMY_TRACK_MODIFICATIONS = True
# SAML Authnetication # SAML Authentication
SAML_ENABLED = False SAML_ENABLED = False
SAML_DEBUG = True SAML_DEBUG = True
SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml') SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml')
@ -86,10 +86,10 @@ SAML_ATTRIBUTE_ACCOUNT = 'https://example.edu/pdns-account'
SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>' SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
SAML_SP_CONTACT_NAME = '<contact name>' SAML_SP_CONTACT_NAME = '<contact name>'
SAML_SP_CONTACT_MAIL = '<contact mail>' SAML_SP_CONTACT_MAIL = '<contact mail>'
#Cofigures if SAML tokens should be encrypted. #Configures if SAML tokens should be encrypted.
#If enabled a new app certificate will be generated on restart #If enabled a new app certificate will be generated on restart
SAML_SIGN_REQUEST = False SAML_SIGN_REQUEST = False
#Use SAML standard logout mechanism retreived from idp metadata #Use SAML standard logout mechanism retrieved from idp metadata
#If configured false don't care about SAML session on logout. #If configured false don't care about SAML session on logout.
#Logout from PowerDNS-Admin only and keep SAML session authenticated. #Logout from PowerDNS-Admin only and keep SAML session authenticated.
SAML_LOGOUT = False SAML_LOGOUT = False

View File

@ -28,7 +28,7 @@ RUN apt-get install -y netcat
# lib for building mysql db driver # lib for building mysql db driver
RUN apt-get install -y libmysqlclient-dev RUN apt-get install -y libmysqlclient-dev
# lib for buiding ldap and ssl-based application # lib for building ldap and ssl-based application
RUN apt-get install -y libsasl2-dev libldap2-dev libssl-dev RUN apt-get install -y libsasl2-dev libldap2-dev libssl-dev
# lib for building python3-saml # lib for building python3-saml

View File

@ -82,7 +82,7 @@ def downgrade():
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
## NOTE: ## NOTE:
## - Drop action does not work on sqlite3 ## - Drop action does not work on sqlite3
## - This action touchs the `setting` table which loaded in views.py ## - This action touches the `setting` table which loaded in views.py
## during app initlization, so the downgrade function won't work ## during app initlization, so the downgrade function won't work
## unless we temporary remove importing `views` from `app/__init__.py` ## unless we temporary remove importing `views` from `app/__init__.py`
op.drop_column('setting', 'view') op.drop_column('setting', 'view')

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#################################################################################################################################### ####################################################################################################################################
# A CLI Script to update list of domains instead from the UI. Can be usefull for people who want to execute updates from a cronjob # A CLI Script to update list of domains instead from the UI. Can be useful for people who want to execute updates from a cronjob
# #
# Tip: # Tip:
# When running from a cron, use flock (you might need to install it) to be sure only one process is running a time. eg: # When running from a cron, use flock (you might need to install it) to be sure only one process is running a time. eg: