diff options
Diffstat (limited to 'session_app.py.publish')
-rwxr-xr-x | session_app.py.publish | 130 |
1 files changed, 80 insertions, 50 deletions
diff --git a/session_app.py.publish b/session_app.py.publish index 520f676..4a806ed 100755 --- a/session_app.py.publish +++ b/session_app.py.publish @@ -10,25 +10,23 @@ # future: https://code.tutsplus.com/tutorials/flask-authentication-with-ldap--cms-23101 # better timeout session: https://stackoverflow.com/a/49891626/3569534 # Improve: -# purge sessions after 15 minutes? +# move all configs to config file +# move all references to references section +# accept a /login/basic endpoint with Authorization: header, use ldap +# accept a bind credential so we can perform lookups of users who match "uid=%s" under a basedn. # Run: # FLASK_APP=session_app.py FLASK_DEBUG=1 flask run --host 0.0.0.0 # Dependencies: # apt-get install python3-flask # pip3 install Flask-kerberos kerberos -from flask import Flask, Response, redirect, url_for, render_template, request - +from flask import Flask, Response, redirect, url_for, render_template, request, _request_ctx_stack as stack, make_response, session from flask_kerberos import init_kerberos, requires_authentication, _unauthorized, _forbidden, _gssapi_authenticate -from flask import _request_ctx_stack as stack, make_response, session -#from flask.ext.login import LoginManager import kerberos from functools import wraps -from socket import gethostname import binascii, datetime - -from functools import wraps import os +import session_ldap DEBUG=True app = Flask(__name__) @@ -37,10 +35,12 @@ app.debug=True secret_key_value = os.urandom(24) secret_key_value_hex_encoded = binascii.hexlify(secret_key_value) app.config['SECRET_KEY'] = secret_key_value_hex_encoded -#app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=7) -#session.permanent = True -minutes = 2 -app.permanent_session_lifetime=datetime.timedelta(minutes=minutes) +app.config['LDAP_URI'] = "ldaps://dns1.ipa.internal.com:636" +app.config['LDAP_USER_BASEDN'] = "cn=users,cn=accounts,dc=ipa,dc=internal,dc=com" +app.config['LDAP_GROUP_BASEDN'] = "cn=groups,cn=accounts,dc=ipa,dc=internal,dc=com" +app.config['LDAP_USER_FORMAT'] = "uid=%s,cn=users,cn=accounts,dc=ipa,dc=internal,dc=com" +app.config['minutes'] = 2 +app.permanent_session_lifetime=datetime.timedelta(minutes=app.config['minutes']) def requires_session(function): ''' @@ -97,6 +97,27 @@ def requires_authn_kerberos(function): return _unauthorized_kerberos() return decorated +def requires_authn_ldap(function): + ''' + Require that the wrapped view function only be called by users + authenticated with ldap. The view function will have the authenticated + users principal passed to it as its first argument. + :param function: flask view function + :type function: function + :returns: decorated function + :rtype: function + ''' + @wraps(function) + def decorated(*args, **kwargs): + username = request.form['username'] + pw = request.form['password'] + ll = ldap_login(username,pw) + if ll: + return function(ll.user,*args, **kwargs) + else: + return _unauthorized_ldap() + return decorated + def _unauthorized_kerberos(): ''' Indicate that authentication is required @@ -104,20 +125,13 @@ def _unauthorized_kerberos(): # from https://billstclair.com/html-redirect2.html return Response(f'<meta http-equiv="Refresh" content="4; url={url_for("login_ldap")}">Unauthorized! No kerberos auth provided. Trying <a href="{url_for("login_ldap")}">ldap</a> automatically in a moment.', 401, {'WWW-Authenticate': 'Negotiate'}) +def _unauthorized_ldap(): + return Response(f'<meta http-equiv="Refresh" content="4; url={url_for("login")}">Unauthorized! Invalid ldap credentials... returning to login form', 401) + @app.route("/") def index(): return render_template('index.html') -@app.route("/open/") -def open(): - header = request.headers.get("Authorization") - if header: - print("Header!") - token = ''.join(header.split()[1:]) - print("token",token) - print("something") - return "<html><body>here</body></html>", 200 - @app.route("/protected/") @requires_session def protected_page(): @@ -131,11 +145,11 @@ def protected_page_real(): return render_template('view.html', c_user = c_user, s_user=s_user, cookie=cookie) @app.route("/login/new") +@app.route("/login/new/") def login_new(): return redirect(url_for("login", new="")) @app.route("/login/", methods=['POST','GET']) -#@requires_authn_kerberos def login(user="None"): if request.method == "GET": if 'user' in session and request.cookies.get('user') == session['user'] and (not 'new' in request.args): @@ -148,31 +162,44 @@ def login(user="None"): # default, show login form return redirect(url_for("login_form")) elif request.method == "POST": - # so far only the login form sends a POST to this endpoint. - username=request.form['username'] - pw=request.form['password'] - #pw="******" - args="" - for i in request.args: - args += str(i) - #resp = Response(f"Login functionality still in progress. <br/>Args: {args}<br/>data: {request.data}</br>query_string: {request.query_string}<br/>values: {request.values}",200) - ldap_result = ldap_login(username,pw) - resp = Response(f"Login functionality still in progress. <br/>username: {username}<br/>password: {pw}<br/>form: {request.form}<br/>ldap result:{ldap_result}",200) - return resp - + # redirect to whichever option was chosen in the drop-down + if 'logintype' in request.form: + logintype = request.form['logintype'] + else: + # choose default logintype for user + logintype = "ldap" + if "ldap" == logintype: + # preserve POST with code 307 https://stackoverflow.com/a/15480983/3569534 + return redirect(url_for("login_ldap"), code=307) + else: + return f"Authentication method {logintype} not supported yet.",400 + def ldap_login(username,password): - response = f"Trying user {username} with pw '{password}'" - print(response) - return response - + print(f"Trying user {username} with pw '{password}'") + user = session_ldap.authenticated_user( + app.config['LDAP_URI'], + app.config['LDAP_USER_FORMAT'], + username, + password + ) + if user: + return user + else: + return False + return False @app.route("/login/kerberos") +@app.route("/login/kerberos/") @requires_authn_kerberos def login_kerberos(user): resp = Response(f'<meta http-equiv="Refresh" content="1; url={url_for("protected_page")}">success with kerberos') #resp.headers['login'] = "from-kerberos" - resp.set_cookie('user',user) resp.set_cookie('type',"kerberos") + resp = login_generic(session,resp,user,None) + return resp + +def login_generic(session,resp,user,groups=[]): + resp.set_cookie('user',user) end_time = datetime.datetime.now(datetime.timezone.utc) + app.permanent_session_lifetime end_time_str = datetime.datetime.strftime(end_time,"%FT%TZ") resp.set_cookie('timestamp',end_time_str) @@ -181,27 +208,30 @@ def login_kerberos(user): session['end_time'] = end_time_str return resp -# WORKHERE: ldap auth -# WIP 2021-06-18 17:42; make this unauthenticated GET send to a form. @app.route("/login/ldap", methods=['POST','GET']) -#@app.route("/login/ldap/<user>") -def login_ldap(user = "none"): - resp = Response(f"success, from user {user}") - resp.headers['login'] = "from-ldap" - resp.set_cookie('user',user) +@app.route("/login/ldap/", methods=['POST','GET']) +@requires_authn_ldap +def login_ldap(user,groups=[]): + resp = Response(f'<meta http-equiv="Refresh" content="1; url={url_for("protected_page")}">success with ldap') resp.set_cookie('type',"ldap") - session['user']=user - resp.set_cookie('timestamp',app.permanent_session_lifetime) + resp = login_generic(session,resp,user,groups) return resp +@app.route("/login/form", methods=['GET']) @app.route("/login/form/", methods=['GET']) def login_form(): options = { "ldap": "ldap", + "other": "other" } - return render_template("login_form.html",login_url=url_for("login"),options=options) + return render_template("login_form.html", + login_url = url_for("login"), + options=options, + kerberos_url = url_for("login_kerberos") + ) @app.route("/logout") +@app.route("/logout/") def logout(): resp = Response(f"logged out") # Doing anything with session here leaves a cookie. |