| |
@@ -4,15 +4,10 @@
|
| |
import copy
|
| |
import sys
|
| |
|
| |
- from cryptography.fernet import Fernet
|
| |
- from cryptography.hazmat.backends import default_backend
|
| |
- from cryptography.hazmat.primitives import hashes
|
| |
- from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
| |
-
|
| |
from flask import Flask, session, redirect, url_for, escape, request, render_template
|
| |
|
| |
from lib389 import DirSrv
|
| |
- from lib389.idm.account import Accounts
|
| |
+ from lib389.idm.account import Account, Accounts
|
| |
from lib389.idm.user import nsUserAccounts
|
| |
from lib389._mapped_object import _gen_or, _gen_filter, _term_gen
|
| |
|
| |
@@ -28,9 +23,6 @@
|
| |
if not config.has_option('dsportal', 'basedn'):
|
| |
app.logger.error("Missing config.ini option dsportal:basedn\n")
|
| |
raise Exception("Missing config.ini option dsportal:basedn")
|
| |
- if not config.has_option('dsportal', 'fernet_cookie_key'):
|
| |
- app.logger.error("Missing config.ini option dsportal:fernet_cookie_key\n")
|
| |
- raise Exception("Missing config.ini option dsportal:fernet_cookie_key")
|
| |
if not config.has_option('dsportal', 'cookie_signing_key'):
|
| |
app.logger.error("Missing config.ini option dsportal:cookie_signing_key\n")
|
| |
raise Exception("Missing config.ini option dsportal:cookie_signing_key")
|
| |
@@ -41,19 +33,8 @@
|
| |
CONFIG = {
|
| |
'ldapurl': config['dsportal']['ldapurl'],
|
| |
'basedn': config['dsportal']['basedn'],
|
| |
- 'cookie_key': base64.b64decode(config['dsportal']['fernet_cookie_key'])
|
| |
}
|
| |
|
| |
- KDF = PBKDF2HMAC(
|
| |
- algorithm=hashes.SHA256(),
|
| |
- length=32,
|
| |
- salt=b'',
|
| |
- iterations=100000,
|
| |
- backend=default_backend()
|
| |
- )
|
| |
- COOKIE_KEY = base64.urlsafe_b64encode(KDF.derive(CONFIG['cookie_key']))
|
| |
- FERNET = Fernet(COOKIE_KEY)
|
| |
-
|
| |
ACCOUNT = {
|
| |
'class': config['account']['class'],
|
| |
'name_attr': config['account']['name_attr'],
|
| |
@@ -98,9 +79,14 @@
|
| |
|
| |
def _bind_dn(ldapurl, binddn, password):
|
| |
inst = _get_ds_instance(ldapurl, binddn, password)
|
| |
- inst.open()
|
| |
- inst.unbind_s()
|
| |
- return True
|
| |
+ token = None
|
| |
+ try:
|
| |
+ inst.open()
|
| |
+ acct = Account(inst, binddn)
|
| |
+ token = acct.request_sso_token()
|
| |
+ finally:
|
| |
+ inst.unbind_s()
|
| |
+ return token
|
| |
|
| |
@app.errorhandler(404)
|
| |
@app.route('/notfound')
|
| |
@@ -117,9 +103,7 @@
|
| |
# the main page. We may need to return a status code the ajax can interpret
|
| |
try:
|
| |
dn = session['dn']
|
| |
- enc_token = session['token']
|
| |
- app.logger.debug(f'{enc_token}')
|
| |
- token = FERNET.decrypt(enc_token).decode()
|
| |
+ token = session['token']
|
| |
except Exception as e:
|
| |
app.logger.debug(e)
|
| |
app.logger.error('Failed to decrypt auth token or get account details.')
|
| |
@@ -168,9 +152,7 @@
|
| |
def account_dn_password_update(req_dn):
|
| |
try:
|
| |
dn = session['dn']
|
| |
- enc_token = session['token']
|
| |
- app.logger.debug(f'{enc_token}')
|
| |
- token = FERNET.decrypt(enc_token).decode()
|
| |
+ token = session['token']
|
| |
except Exception as e:
|
| |
app.logger.debug(e)
|
| |
app.logger.error('Failed to decrypt auth token or get account details.')
|
| |
@@ -213,12 +195,6 @@
|
| |
acct = nsaccts.get(dn=req_dn)
|
| |
acct.change_password(cur_pw, new_pw)
|
| |
|
| |
- # WARNING: Because we current have the pw in the token, we need to re-issue it here else
|
| |
- # we'd log the user out. In the futur version when fernet comes from 389-ds, this won't
|
| |
- # be needed!
|
| |
- token = FERNET.encrypt(str.encode(new_pw))
|
| |
- session['token'] = token
|
| |
-
|
| |
except Exception as e:
|
| |
app.logger.debug(e)
|
| |
return '', 500
|
| |
@@ -231,15 +207,22 @@
|
| |
def index_password():
|
| |
try:
|
| |
dn = session['dn']
|
| |
- enc_token = session['token']
|
| |
- app.logger.debug(f'{enc_token}')
|
| |
- token = FERNET.decrypt(enc_token).decode()
|
| |
+ token = session['token']
|
| |
except Exception as e:
|
| |
app.logger.debug(e)
|
| |
app.logger.error('Failed to decrypt auth token or get account details.')
|
| |
# TODO: Put a session invalid message here.
|
| |
return redirect(url_for('login'))
|
| |
|
| |
+ inst = _get_ds_instance(CONFIG['ldapurl'], dn, token)
|
| |
+ try:
|
| |
+ inst.open()
|
| |
+ except Exception as e:
|
| |
+ app.logger.debug(e)
|
| |
+ return '', 500
|
| |
+ finally:
|
| |
+ inst.unbind_s()
|
| |
+
|
| |
app.logger.debug(f'{dn}, {token}')
|
| |
return render_template('password.html', dn=dn)
|
| |
|
| |
@@ -248,16 +231,13 @@
|
| |
# Are they authenticated?
|
| |
try:
|
| |
dn = session['dn']
|
| |
- enc_token = session['token']
|
| |
- app.logger.debug(f'{enc_token}')
|
| |
- token = FERNET.decrypt(enc_token).decode()
|
| |
+ token = session['token']
|
| |
except Exception as e:
|
| |
app.logger.debug(e)
|
| |
app.logger.error('Failed to decrypt auth token or get account details.')
|
| |
# TODO: Put a session invalid message here.
|
| |
return redirect(url_for('login'))
|
| |
|
| |
-
|
| |
app.logger.debug(f'{dn}, {token}')
|
| |
|
| |
inst = _get_ds_instance(CONFIG['ldapurl'], dn, token)
|
| |
@@ -303,10 +283,9 @@
|
| |
app.logger.debug(f'authenticating as {dn}')
|
| |
|
| |
# TODO: Handle incorrect password differently.
|
| |
- if _bind_dn(CONFIG['ldapurl'], dn, pw):
|
| |
+ token = _bind_dn(CONFIG['ldapurl'], dn, pw)
|
| |
+ if token is not None:
|
| |
session['dn'] = dn
|
| |
- # Enc the pw
|
| |
- token = FERNET.encrypt(str.encode(pw))
|
| |
session['token'] = token
|
| |
return redirect(url_for('index'))
|
| |
except Exception as e:
|
| |
Bug Description: The proof of concept used an encrypted token
containing the users password to allow the bind. This is not
exactly optimal.
Fix Description: Now that sso tokens have been merged to 389-ds
we can rely on those for rebinding instead.
https://pagure.io/389-ds-portal/issue/1
Author: William Brown william@blackhats.net.au
Review by: ???