summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2021-06-20 16:12:54 -0400
committerB. Stack <bgstack15@gmail.com>2021-06-20 16:12:54 -0400
commitdbe21e7fc9395f9f30fe3299ce09d59dc8a693e9 (patch)
tree752ca58d983d8c9ed2a36bb93eafd8775237160d
downloadsession_app-dbe21e7fc9395f9f30fe3299ce09d59dc8a693e9.tar.gz
session_app-dbe21e7fc9395f9f30fe3299ce09d59dc8a693e9.tar.bz2
session_app-dbe21e7fc9395f9f30fe3299ce09d59dc8a693e9.zip
initial commit
-rw-r--r--.gitignore4
-rw-r--r--INTERACT.md49
-rwxr-xr-xsession_app.py.publish190
-rw-r--r--templates/index.html10
-rw-r--r--templates/view.html9
5 files changed, 262 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d6b427a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+__pycache__
+session_app.py
+*.keytab
+*.log
diff --git a/INTERACT.md b/INTERACT.md
new file mode 100644
index 0000000..b3c3914
--- /dev/null
+++ b/INTERACT.md
@@ -0,0 +1,49 @@
+Start server in a separate shell session.
+
+ $ FLASK_APP=session_app.py FLASK_DEBUG=1 flask run --host 0.0.0.0
+
+Reset any cookies and kerberos tickets.
+
+ $ kdestroy -A
+ $ rm ~/cookiejar.txt
+
+Try visiting protected page without authorization.
+
+ $ curl -L http://d2-03a.ipa.example.com:5000/protected -b ~/cookiejar.txt -c ~/cookiejar.txt
+ requires session
+
+Get kerberos ticket and then visit login url. This /login redirects to /login/kerberos by default.
+
+ $ kinit ${USER}
+ $ klist
+ Ticket cache: FILE:/tmp/krb5cc_960600001_Hjgmv7lby2
+ Default principal: bgstack15@IPA.EXAMPLE.COM
+
+ Valid starting Expires Service principal
+ 06/20/21 16:04:10 06/21/21 16:04:07 krbtgt/IPA.EXAMPLE.COM@IPA.EXAMPLE.COM
+ 06/20/21 16:04:15 06/21/21 16:04:07 HTTP/d2-03a.ipa.example.com@IPA.EXAMPLE.COM
+
+ $ curl -L http://d2-03a.ipa.example.com:5000/login --negotiate -u ':' -b ~/cookiejar.txt -c ~/cookiejar.txt
+ <meta http-equiv="Refresh" content="1; url=/protected/">success with kerberos
+
+Visit protected page now that we have a session.
+
+ $ cat ~/cookiejar.txt
+ # Netscape HTTP Cookie File
+ # https://curl.se/docs/http-cookies.html
+ # This file was generated by libcurl! Edit at your own risk.
+
+ d2-03a.ipa.example.com FALSE / FALSE 0 user "bgstack15@IPA.EXAMPLE.COM"
+ d2-03a.ipa.example.com FALSE / FALSE 0 type kerberos
+ d2-03a.ipa.example.com FALSE / FALSE 0 timestamp 2021-06-20T20:06:15Z
+ #HttpOnly_d2-03a.ipa.example.com FALSE / FALSE 1624219691 session eyJfcGVybWFuZW50Ijp0cnVlLCJlbmRfdGltZSI6IjIwMjEtMDYtMjBUMjA6MDY6MTVaIiwidXNlciI6ImJnaXJ0b25ASVBBLlNNSVRIMTIyLkNPTSJ9.YM-fsw.ZeI4ec-d7D64IEJ9Ab4RfpXfLt4
+
+ $ curl -L http://d2-03a.ipa.example.com:5000/protected -b ~/cookiejar.txt -c ~/cookiejar.txt
+ <html>
+ <title>View Session Cookie</title>
+ Username: bgstack15@IPA.EXAMPLE.COM<br/>
+ Session expires: 2021-06-20T20:06:15Z<br/>
+ Logged in through: kerberos
+ </html>
+
+2021-06-20 ldap basic auth, and a login form are still pending.
diff --git a/session_app.py.publish b/session_app.py.publish
new file mode 100755
index 0000000..915693a
--- /dev/null
+++ b/session_app.py.publish
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+# Startdate: 2021-06-17
+# goals:
+# accept kerberos or ldap "authorization: basic gowinablz;nuiowekj==" auth, to create a cookie for a session that lasts for 15 minutes. use the cookie to get to protected URLs
+# References:
+# https://code.tutsplus.com/tutorials/flask-authentication-with-ldap--cms-23101
+# https://www.techlifediary.com/python-web-development-tutorial-using-flask-session-cookies/
+# delete cookie https://stackoverflow.com/a/14386413/3569534
+# timeout sessions https://stackoverflow.com/a/11785722/3569534
+# 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?
+# 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_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
+
+DEBUG=True
+app = Flask(__name__)
+app.config.from_object(__name__)
+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)
+
+def requires_session(function):
+ '''
+ Requires a valid session, provided by cookie!
+ '''
+ @wraps(function)
+ def decorated(*args, **kwargs):
+ if not session:
+ return Response("requires session",401)
+ else:
+ if 'user' not in session:
+ return Response("User is not in this session.",401)
+ s_user = session['user']
+ c_user = request.cookies.get('user')
+ print(f"session user: {s_user}")
+ print(f"cookie user: {c_user}")
+ if session['user'] != c_user:
+ return Response("Wrong user for this session!.",401)
+ # otherwise, everything is good!
+ #return Response(f"session user: {s_user}<br/>cookie user: {c_user}", 200)
+ # return to the passed function, from https://github.com/ArtemAngelchev/flask-basicauth-ldap/blob/master/flask_basicauth_ldap.py
+ return function(*args,**kwargs)
+ # catch-all
+ return Response("requires session",401)
+ return decorated
+
+# imported from flask_kerberos and modified, because I want custom 401 message
+def requires_authn_kerberos(function):
+ '''
+ Require that the wrapped view function only be called by users
+ authenticated with Kerberos. 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):
+ header = request.headers.get("Authorization")
+ if header:
+ ctx = stack.top
+ token = ''.join(header.split()[1:])
+ rc = _gssapi_authenticate(token)
+ if rc == kerberos.AUTH_GSS_COMPLETE:
+ response = function(ctx.kerberos_user, *args, **kwargs)
+ response = make_response(response)
+ if ctx.kerberos_token is not None:
+ response.headers['WWW-Authenticate'] = ' '.join(['negotiate', ctx.kerberos_token])
+ return response
+ elif rc != kerberos.AUTH_GSS_CONTINUE:
+ return _forbidden()
+ return _unauthorized_kerberos()
+ return decorated
+
+def _unauthorized_kerberos():
+ '''
+ Indicate that authentication is required
+ '''
+ # 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'})
+
+@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():
+ return protected_page_real()
+
+def protected_page_real():
+ s_user = session['user']
+ c_user = request.cookies.get('user')
+ cookie=request.cookies
+ print(cookie)
+ return render_template('view.html', c_user = c_user, s_user=s_user, cookie=cookie)
+
+@app.route("/login/")
+#@requires_authn_kerberos
+def login(user="None"):
+ # prefer kerberos
+ return redirect(url_for("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")
+ 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)
+ session.permanent = True
+ session['user']=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)
+ resp.set_cookie('type',"ldap")
+ session['user']=user
+ resp.set_cookie('timestamp',app.permanent_session_lifetime)
+ return resp
+
+@app.route("/logout")
+def logout():
+ resp = Response(f"logged out")
+ # Doing anything with session here leaves a cookie.
+ #session['user']=""
+ resp.set_cookie('user','',expires=0)
+ resp.set_cookie('type','',expires=0)
+ resp.set_cookie('session','',expires=0)
+ resp.set_cookie('timestamp','',expires=0)
+ return resp
+
+## This bumps the session lifetime to two minutes farther out from each web request with this session.
+#@app.before_request
+#def make_session_permanent():
+# session.permanent = True
+# session['end_time'] = datetime.datetime.now()+app.permanent_session_lifetime
+
+# keytab from `/usr/sbin/ipa-getkeytab -p HTTP/d2-03a.ipa.example.com -k session.keytab`
+os.environ['KRB5_KTNAME'] = "./session.keytab"
+os.environ['KRB5_TRACE'] = "./kerberos.log"
+init_kerberos(app, hostname="d2-03a.ipa.internal.com", service="HTTP")
+if __name__ == '__main__':
+ init_kerberos(app, hostname="d2-03a.ipa.internal.com", service="HTTP")
+ app.run(host='0.0.0.0',debug=True)
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..daa6893
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Front page</title>
+</head>
+<body>
+Welcome to this sample application! To access protected pages you need to <a href="/login/">log in</a>. Accepted methods are kerberos and ldap.
+
+{{ session }}
+</body>
+</html>
diff --git a/templates/view.html b/templates/view.html
new file mode 100644
index 0000000..5c14175
--- /dev/null
+++ b/templates/view.html
@@ -0,0 +1,9 @@
+<html>
+<title>View Session Cookie</title>
+Username: {{ s_user }}<br/>{#
+Cookie user: {{ c_user }}<br/>
+Session: {{ session }}<br/>
+Cookies: {{ cookie }}<br/>#}
+Session expires: {{ cookie.timestamp }}<br/>
+Logged in through: {{ cookie.type }}
+</html>
bgstack15