diff --git a/README.md b/README.md
index 7d402fe..fde76d0 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
# PowerDNS-Admin
PowerDNS Web-GUI - Built by Flask
+[![Build Status](https://travis-ci.org/thomasDOTde/PowerDNS-Admin.svg?branch=master)](https://travis-ci.org/thomasDOTde/PowerDNS-Admin)
#### Features:
- Multiple domain management
@@ -76,6 +77,14 @@ Web application configuration is stored in `config.py` file. Let's clone it from
(flask)$ vim config.py
```
+You can configure group based security by tweaking the below parameters in `config.py`. Groups membership comes from LDAP.
+Setting `LDAP_GROUP_SECURITY` to True enables group-based security. With this enabled only members of the two groups listed below are allowed to login. Members of `LDAP_ADMIN_GROUP` will get the Administrator role and members of `LDAP_USER_GROUP` will get the User role. Sample config below:
+```
+LDAP_GROUP_SECURITY = True
+LDAP_ADMIN_GROUP = 'CN=PowerDNS-Admin Admin,OU=Custom,DC=ivan,DC=local'
+LDAP_USER_GROUP = 'CN=PowerDNS-Admin User,OU=Custom,DC=ivan,DC=local'
+```
+
Create database after having proper configs
```
(flask)% ./create_db.py
diff --git a/app/models.py b/app/models.py
index fc88c7e..a7f9872 100644
--- a/app/models.py
+++ b/app/models.py
@@ -20,12 +20,21 @@ from lib import utils
from lib.log import logger
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
+LDAP_URI = app.config['LDAP_URI']
+LDAP_USERNAME = app.config['LDAP_USERNAME']
+LDAP_PASSWORD = app.config['LDAP_PASSWORD']
+LDAP_SEARCH_BASE = app.config['LDAP_SEARCH_BASE']
+LDAP_TYPE = app.config['LDAP_TYPE']
if 'LDAP_TYPE' in app.config.keys():
LDAP_URI = app.config['LDAP_URI']
LDAP_USERNAME = app.config['LDAP_USERNAME']
LDAP_PASSWORD = app.config['LDAP_PASSWORD']
LDAP_SEARCH_BASE = app.config['LDAP_SEARCH_BASE']
LDAP_TYPE = app.config['LDAP_TYPE']
+ LDAP_GROUP_SECURITY = app.config['LDAP_GROUP_SECURITY']
+ if LDAP_GROUP_SECURITY == True:
+ LDAP_ADMIN_GROUP = app.config['LDAP_ADMIN_GROUP']
+ LDAP_USER_GROUP = app.config['LDAP_USER_GROUP']
LDAP_FILTER = app.config['LDAP_FILTER']
LDAP_USERNAMEFIELD = app.config['LDAP_USERNAMEFIELD']
else:
@@ -216,27 +225,69 @@ class User(db.Model):
# create user if not exist in the db
if not User.query.filter(User.username == self.username).first():
try:
- # try to get user's firstname & lastname from LDAP
- # this might be changed in the future
- self.firstname = result[0][0][1]['givenName'][0]
- self.lastname = result[0][0][1]['sn'][0]
- self.email = result[0][0][1]['mail'][0]
- except Exception:
- self.firstname = self.username
- self.lastname = ''
+ ldap_username = result[0][0][0]
+ l.simple_bind_s(ldap_username, self.password)
+ if LDAP_GROUP_SECURITY:
+ try:
+ if LDAP_TYPE == 'ldap':
+ uid = result[0][0][1]['uid'][0]
+ groupSearchFilter = "(&(objectClass=posixGroup)(memberUid=%s))" % uid
+ else:
+ groupSearchFilter = "(&(objectcategory=group)(member=%s))" % ldap_username
+ groups = self.ldap_search(groupSearchFilter, LDAP_SEARCH_BASE)
+ allowedlogin = False
+ isadmin = False
+ for group in groups:
+ logging.debug(group)
+ if (group[0][0] == LDAP_ADMIN_GROUP):
+ allowedlogin = True
+ isadmin = True
+ logging.info('User %s is part of the "%s" group that allows admin access to PowerDNS-Admin' % (self.username,LDAP_ADMIN_GROUP))
+ if (group[0][0] == LDAP_USER_GROUP):
+ allowedlogin = True
+ logging.info('User %s is part of the "%s" group that allows user access to PowerDNS-Admin' % (self.username,LDAP_USER_GROUP))
+ if allowedlogin == False:
+ logging.error('User %s is not part of the "%s" or "%s" groups that allow access to PowerDNS-Admin' % (self.username,LDAP_ADMIN_GROUP,LDAP_USER_GROUP))
+ return False
+ except:
+ logging.error('LDAP group lookup for user "%s" has failed' % self.username)
+ logging.info('User "%s" logged in successfully' % self.username)
+
+ # create user if not exist in the db
+ if User.query.filter(User.username == self.username).first() == None:
+ try:
+ # try to get user's firstname & lastname from LDAP
+ # this might be changed in the future
+ self.firstname = result[0][0][1]['givenName'][0]
+ self.lastname = result[0][0][1]['sn'][0]
+ except:
+ self.firstname = self.username
+ self.lastname = ''
- # 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
+ # first registered user will be in Administrator role or if part of LDAP Admin group
+ if (User.query.count() == 0):
+ self.role_id = Role.query.filter_by(name='Administrator').first().id
+ else:
+ self.role_id = Role.query.filter_by(name='User').first().id
+
+ #
+ if LDAP_GROUP_SECURITY:
+ if isadmin == True:
+ self.role_id = Role.query.filter_by(name='Administrator').first().id
- self.create_user()
- logging.info('Created user "%s" in the DB' % self.username)
-
- return True
-
- logging.error('Unsupported authentication method')
- return False
+ self.create_user()
+ logging.info('Created user "%s" in the DB' % self.username)
+ else:
+ # user already exists in database, set their admin status based on group membership (if enabled)
+ if LDAP_GROUP_SECURITY:
+ self.set_admin(isadmin)
+ return True
+ except:
+ logging.error('User "%s" input a wrong password' % self.username)
+ return False
+ else:
+ logging.error('Unsupported authentication method')
+ return False
def create_user(self):
"""
diff --git a/app/templates/domain.html b/app/templates/domain.html
index 89a2ce2..6becd9b 100644
--- a/app/templates/domain.html
+++ b/app/templates/domain.html
@@ -265,7 +265,37 @@
$(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 = " \
+ \
+ \
+ \
+ \
+ \
+ ";
+ } else {
+ var parts = record_data.val().split(" ");
+ var form = " \
+ \
+ \
+ \
+ \
+ \
+ ";
+ }
+ 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 = " \
diff --git a/app/views.py b/app/views.py
index 120e25a..e624c16 100644
--- a/app/views.py
+++ b/app/views.py
@@ -900,12 +900,12 @@ def dyndns_update():
domain = None
domain_segments = hostname.split('.')
for index in range(len(domain_segments)):
- domain_segments.pop(0)
full_domain = '.'.join(domain_segments)
potential_domain = Domain.query.filter(Domain.name == full_domain).first()
if potential_domain in domains:
domain = potential_domain
break
+ domain_segments.pop(0)
if not domain:
history = History(msg="DynDNS update: attempted update of %s but it does not exist for this user" % hostname, created_by=current_user.username)
diff --git a/config_template.py b/config_template.py
index cdde7b4..0e79766 100644
--- a/config_template.py
+++ b/config_template.py
@@ -41,6 +41,9 @@ LDAP_URI = 'ldaps://your-ldap-server:636'
LDAP_USERNAME = 'cn=dnsuser,ou=users,ou=services,dc=duykhanh,dc=me'
LDAP_PASSWORD = 'dnsuser'
LDAP_SEARCH_BASE = 'ou=System Admins,ou=People,dc=duykhanh,dc=me'
+LDAP_GROUP_SECURITY = False
+LDAP_ADMIN_GROUP = 'CN=PowerDNS-Admin Admin,OU=Custom,DC=ivan,DC=local'
+LDAP_USER_GROUP = 'CN=PowerDNS-Admin User,OU=Custom,DC=ivan,DC=local'
# Additional options only if LDAP_TYPE=ldap
LDAP_USERNAMEFIELD = 'uid'
LDAP_FILTER = '(objectClass=inetorgperson)'
@@ -100,7 +103,7 @@ PDNS_API_KEY = 'you never know'
PDNS_VERSION = '3.4.7'
# RECORDS ALLOWED TO EDIT
-RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT']
+RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'PTR', 'SPF', 'SRV', 'TXT']
# EXPERIMENTAL FEATURES
PRETTY_IPV6_PTR = False