#13 Ticket 1 - use ds sso tokens
Merged 4 years ago by firstyear. Opened 4 years ago by firstyear.
firstyear/389-ds-portal 1-secure-cookie  into  master

file modified
-1
@@ -1,4 +1,3 @@ 

  flask

- cryptography

  python-ldap

  python-dateutil

file modified
+24 -45
@@ -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: ???

Thanks @mreynolds! I'll start on patternfly next I think to make it look good :D

Pull-Request has been merged by firstyear

4 years ago
Metadata