mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2025-01-08 11:25:40 +00:00
Merge branch 'fix-saml'
This commit is contained in:
commit
47d5858fc6
@ -19,7 +19,7 @@ if app.config['SAML_ENABLED']:
|
|||||||
from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
|
from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
|
||||||
idp_timestamp = datetime(1970, 1, 1)
|
idp_timestamp = datetime(1970, 1, 1)
|
||||||
idp_data = None
|
idp_data = None
|
||||||
idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'])
|
idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None))
|
||||||
if idp_data is None:
|
if idp_data is None:
|
||||||
print('SAML: IDP Metadata initial load failed')
|
print('SAML: IDP Metadata initial load failed')
|
||||||
exit(-1)
|
exit(-1)
|
||||||
@ -37,7 +37,7 @@ def get_idp_data():
|
|||||||
|
|
||||||
def retreive_idp_data():
|
def retreive_idp_data():
|
||||||
global idp_data, idp_timestamp
|
global idp_data, idp_timestamp
|
||||||
new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'])
|
new_idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None))
|
||||||
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()
|
||||||
@ -205,7 +205,7 @@ def email_to_gravatar_url(email="", size=100):
|
|||||||
|
|
||||||
def prepare_flask_request(request):
|
def prepare_flask_request(request):
|
||||||
# If server is behind proxys or balancers use the HTTP_X_FORWARDED fields
|
# If server is behind proxys or balancers use the HTTP_X_FORWARDED fields
|
||||||
url_data = urlparse.urlparse(request.url)
|
url_data = urlparse(request.url)
|
||||||
return {
|
return {
|
||||||
'https': 'on' if request.scheme == 'https' else 'off',
|
'https': 'on' if request.scheme == 'https' else 'off',
|
||||||
'http_host': request.host,
|
'http_host': request.host,
|
||||||
@ -229,7 +229,10 @@ def init_saml_auth(req):
|
|||||||
metadata = get_idp_data()
|
metadata = get_idp_data()
|
||||||
settings = {}
|
settings = {}
|
||||||
settings['sp'] = {}
|
settings['sp'] = {}
|
||||||
settings['sp']['NameIDFormat'] = idp_data['sp']['NameIDFormat']
|
if 'SAML_NAMEID_FORMAT' in app.config:
|
||||||
|
settings['sp']['NameIDFormat'] = app.config['SAML_NAMEID_FORMAT']
|
||||||
|
else:
|
||||||
|
settings['sp']['NameIDFormat'] = idp_data.get('sp', {}).get('NameIDFormat', 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified')
|
||||||
settings['sp']['entityId'] = app.config['SAML_SP_ENTITY_ID']
|
settings['sp']['entityId'] = app.config['SAML_SP_ENTITY_ID']
|
||||||
cert = open(CERT_FILE, "r").readlines()
|
cert = open(CERT_FILE, "r").readlines()
|
||||||
key = open(KEY_FILE, "r").readlines()
|
key = open(KEY_FILE, "r").readlines()
|
||||||
|
@ -608,6 +608,32 @@ class Account(db.Model):
|
|||||||
users.append(User(id=uid).get_user_info_by_id().username)
|
users.append(User(id=uid).get_user_info_by_id().username)
|
||||||
|
|
||||||
self.grant_privileges(users)
|
self.grant_privileges(users)
|
||||||
|
def add_user(self, user):
|
||||||
|
"""
|
||||||
|
Add a single user to Account by User
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
au = AccountUser(self.id, user.id)
|
||||||
|
db.session.add(au)
|
||||||
|
db.session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db.session.rollback()
|
||||||
|
logging.error('Cannot add user privielges on account {0}'.format(self.name))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def remove_user(self, user):
|
||||||
|
"""
|
||||||
|
Remove a single user from Account by User
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
AccountUser.query.filter(AccountUser.user_id == user.id).filter(AccountUser.account_id == self.id).delete()
|
||||||
|
db.session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db.session.rollback()
|
||||||
|
logging.error('Cannot revoke user privielges on account {0}'.format(self.name))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Role(db.Model):
|
class Role(db.Model):
|
||||||
|
63
app/views.py
63
app/views.py
@ -17,7 +17,7 @@ from flask_login import login_user, logout_user, current_user, login_required
|
|||||||
from werkzeug import secure_filename
|
from werkzeug import secure_filename
|
||||||
from werkzeug.security import gen_salt
|
from werkzeug.security import gen_salt
|
||||||
|
|
||||||
from .models import User, Account, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
|
from .models import User, Account, Domain, Record, Role, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
|
||||||
from app import app, login_manager, github, google
|
from app import app, login_manager, github, google
|
||||||
from app.lib import utils
|
from app.lib import utils
|
||||||
from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec
|
from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec
|
||||||
@ -227,20 +227,65 @@ def saml_authorized():
|
|||||||
self_url = self_url+req['script_name']
|
self_url = self_url+req['script_name']
|
||||||
if 'RelayState' in request.form and self_url != request.form['RelayState']:
|
if 'RelayState' in request.form and self_url != request.form['RelayState']:
|
||||||
return redirect(auth.redirect_to(request.form['RelayState']))
|
return redirect(auth.redirect_to(request.form['RelayState']))
|
||||||
user = User.query.filter_by(username=session['samlNameId'].lower()).first()
|
if app.config.get('SAML_ATTRIBUTE_USERNAME', False):
|
||||||
|
username = session['samlUserdata'][app.config['SAML_ATTRIBUTE_USERNAME']][0].lower()
|
||||||
|
else:
|
||||||
|
username = session['samlNameId'].lower()
|
||||||
|
user = User.query.filter_by(username=username).first()
|
||||||
if not user:
|
if not user:
|
||||||
# create user
|
# create user
|
||||||
user = User(username=session['samlNameId'],
|
user = User(username=username,
|
||||||
plain_text_password = None,
|
plain_text_password = None,
|
||||||
email=session['samlNameId'])
|
email=session['samlNameId'])
|
||||||
user.create_local_user()
|
user.create_local_user()
|
||||||
session['user_id'] = user.id
|
session['user_id'] = user.id
|
||||||
if session['samlUserdata'].has_key("email"):
|
email_attribute_name = app.config.get('SAML_ATTRIBUTE_EMAIL', 'email')
|
||||||
user.email = session['samlUserdata']["email"][0].lower()
|
givenname_attribute_name = app.config.get('SAML_ATTRIBUTE_GIVENNAME', 'givenname')
|
||||||
if session['samlUserdata'].has_key("givenname"):
|
surname_attribute_name = app.config.get('SAML_ATTRIBUTE_SURNAME', 'surname')
|
||||||
user.firstname = session['samlUserdata']["givenname"][0]
|
account_attribute_name = app.config.get('SAML_ATTRIBUTE_ACCOUNT', None)
|
||||||
if session['samlUserdata'].has_key("surname"):
|
admin_attribute_name = app.config.get('SAML_ATTRIBUTE_ADMIN', None)
|
||||||
user.lastname = session['samlUserdata']["surname"][0]
|
if email_attribute_name in session['samlUserdata']:
|
||||||
|
user.email = session['samlUserdata'][email_attribute_name][0].lower()
|
||||||
|
if givenname_attribute_name in session['samlUserdata']:
|
||||||
|
user.firstname = session['samlUserdata'][givenname_attribute_name][0]
|
||||||
|
if surname_attribute_name in session['samlUserdata']:
|
||||||
|
user.lastname = session['samlUserdata'][surname_attribute_name][0]
|
||||||
|
if admin_attribute_name:
|
||||||
|
user_accounts = set(user.get_account())
|
||||||
|
saml_accounts = []
|
||||||
|
for account_name in session['samlUserdata'].get(account_attribute_name, []):
|
||||||
|
clean_name = ''.join(c for c in account_name.lower() if c in "abcdefghijklmnopqrstuvwxyz0123456789")
|
||||||
|
if len(clean_name) > Account.name.type.length:
|
||||||
|
logging.error("Account name {0} too long. Truncated.".format(clean_name))
|
||||||
|
account = Account.query.filter_by(name=clean_name).first()
|
||||||
|
if not account:
|
||||||
|
account = Account(name=clean_name.lower(), description='', contact='', mail='')
|
||||||
|
account.create_account()
|
||||||
|
history = History(msg='Account {0} created'.format(account.name), created_by='SAML Assertion')
|
||||||
|
history.add()
|
||||||
|
saml_accounts.append(account)
|
||||||
|
saml_accounts = set(saml_accounts)
|
||||||
|
for account in saml_accounts - user_accounts:
|
||||||
|
account.add_user(user)
|
||||||
|
history = History(msg='Adding {0} to account {1}'.format(user.username, account.name), created_by='SAML Assertion')
|
||||||
|
history.add()
|
||||||
|
for account in user_accounts - saml_accounts:
|
||||||
|
account.remove_user(user)
|
||||||
|
history = History(msg='Removing {0} from account {1}'.format(user.username, account.name), created_by='SAML Assertion')
|
||||||
|
history.add()
|
||||||
|
if admin_attribute_name:
|
||||||
|
if 'true' in session['samlUserdata'].get(admin_attribute_name, []):
|
||||||
|
admin_role = Role.query.filter_by(name='Administrator').first().id
|
||||||
|
if user.role_id != admin_role:
|
||||||
|
user.role_id = admin_role
|
||||||
|
history = History(msg='Promoting {0} to administrator'.format(user.username), created_by='SAML Assertion')
|
||||||
|
history.add()
|
||||||
|
else:
|
||||||
|
user_role = Role.query.filter_by(name='User').first().id
|
||||||
|
if user.role_id != user_role:
|
||||||
|
user.role_id = user_role
|
||||||
|
history = History(msg='Demoting {0} to user'.format(user.username), created_by='SAML Assertion')
|
||||||
|
history.add()
|
||||||
user.plain_text_password = None
|
user.plain_text_password = None
|
||||||
user.update_profile()
|
user.update_profile()
|
||||||
session['external_auth'] = True
|
session['external_auth'] = True
|
||||||
|
@ -97,6 +97,53 @@ SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml')
|
|||||||
SAML_METADATA_URL = 'https://<hostname>/FederationMetadata/2007-06/FederationMetadata.xml'
|
SAML_METADATA_URL = 'https://<hostname>/FederationMetadata/2007-06/FederationMetadata.xml'
|
||||||
#Cache Lifetime in Seconds
|
#Cache Lifetime in Seconds
|
||||||
SAML_METADATA_CACHE_LIFETIME = 1
|
SAML_METADATA_CACHE_LIFETIME = 1
|
||||||
|
|
||||||
|
## EntityID of the IdP to use. Only needed if more than one IdP is
|
||||||
|
## in the SAML_METADATA_URL
|
||||||
|
### Default: First (only) IdP in the SAML_METADATA_URL
|
||||||
|
### Example: https://idp.example.edu/idp
|
||||||
|
#SAML_IDP_ENTITY_ID = 'https://idp.example.edu/idp'
|
||||||
|
## NameID format to request
|
||||||
|
### Default: The SAML NameID Format in the metadata if present,
|
||||||
|
### otherwise urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||||
|
### Example: urn:oid:0.9.2342.19200300.100.1.1
|
||||||
|
#SAML_NAMEID_FORMAT = 'urn:oid:0.9.2342.19200300.100.1.1'
|
||||||
|
|
||||||
|
## Attribute to use for Email address
|
||||||
|
### Default: email
|
||||||
|
### Example: urn:oid:0.9.2342.19200300.100.1.3
|
||||||
|
#SAML_ATTRIBUTE_EMAIL = 'urn:oid:0.9.2342.19200300.100.1.3'
|
||||||
|
|
||||||
|
## Attribute to use for Given name
|
||||||
|
### Default: givenname
|
||||||
|
### Example: urn:oid:2.5.4.42
|
||||||
|
#SAML_ATTRIBUTE_GIVENNAME = 'urn:oid:2.5.4.42'
|
||||||
|
|
||||||
|
## Attribute to use for Surname
|
||||||
|
### Default: surname
|
||||||
|
### Example: urn:oid:2.5.4.4
|
||||||
|
#SAML_ATTRIBUTE_SURNAME = 'urn:oid:2.5.4.4'
|
||||||
|
|
||||||
|
## Attribute to use for username
|
||||||
|
### Default: Use NameID instead
|
||||||
|
### Example: urn:oid:0.9.2342.19200300.100.1.1
|
||||||
|
#SAML_ATTRIBUTE_USERNAME = 'urn:oid:0.9.2342.19200300.100.1.1'
|
||||||
|
|
||||||
|
## Attribute to get admin status from
|
||||||
|
### Default: Don't control admin with SAML attribute
|
||||||
|
### Example: https://example.edu/pdns-admin
|
||||||
|
### If set, look for the value 'true' to set a user as an administrator
|
||||||
|
### If not included in assertion, or set to something other than 'true',
|
||||||
|
### the user is set as a non-administrator user.
|
||||||
|
#SAML_ATTRIBUTE_ADMIN = 'https://example.edu/pdns-admin'
|
||||||
|
|
||||||
|
## Attribute to get account names from
|
||||||
|
### Default: Don't control accounts with SAML attribute
|
||||||
|
### If set, the user will be added and removed from accounts to match
|
||||||
|
### what's in the login assertion. Accounts that don't exist will
|
||||||
|
### be created and the user added to them.
|
||||||
|
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>'
|
||||||
|
Loading…
Reference in New Issue
Block a user