summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2021-06-22 22:45:17 -0400
committerB. Stack <bgstack15@gmail.com>2021-06-22 22:45:17 -0400
commitde829904162705b8f782ec9b93632a8e20b6b811 (patch)
tree5f1e82c67ffc508ba718e1cd028c4a750f34a590
parentadd ldap support (diff)
downloadsession_app-de829904162705b8f782ec9b93632a8e20b6b811.tar.gz
session_app-de829904162705b8f782ec9b93632a8e20b6b811.tar.bz2
session_app-de829904162705b8f782ec9b93632a8e20b6b811.zip
add basic auth, but lacks request for auth
Endpoint /login/basic/ works now with a POST, but a GET does not make it prompt a browser for username/password yet.
-rw-r--r--INTERACT.md12
-rwxr-xr-xsession_app.py.publish92
2 files changed, 76 insertions, 28 deletions
diff --git a/INTERACT.md b/INTERACT.md
index c36b238..0ead9fe 100644
--- a/INTERACT.md
+++ b/INTERACT.md
@@ -12,7 +12,7 @@ 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.
+Get kerberos ticket and then visit kerberos login url.
$ kinit ${USER}
$ klist
@@ -23,7 +23,7 @@ Get kerberos ticket and then visit login url. This /login redirects to /login/ke
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
+ $ curl -L http://d2-03a.ipa.example.com:5000/login/kerberos --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.
@@ -49,3 +49,11 @@ Visit protected page now that we have a session.
For submitting to the form, pass in form data using fields `username`, `password`, and optionally `logintype` which can be defined within the application. An included option is `ldap`. Kerberos auth through the form is not supported.
curl -L -X POST http://d2-03a:5000/login/ --data 'username=bgstack15&password=qwerty' -b ~/cookiejar.txt -c ~/cookiejar.txt
+
+Basic auth can be provided as a POST to /login/basic/.
+
+ $ curl -X POST -L http://d2-03a:5000/login/basic/ -b ~/cookiejar.txt -c ~/cookiejar.txt --user 'bgstack15'
+ Enter host password for user 'bgstack15':
+ <meta http-equiv="Refresh" content="1; url=/protected/">success with ldap
+ $ curl -X POST -L http://d2-03a:5000/login/basic/ -b ~/cookiejar.txt -c ~/cookiejar.txt --header "Authorization: Basic $( printf '%s' "${username}:${pw}" | base64 )"
+ <meta http-equiv="Refresh" content="1; url=/protected/">success with ldap
diff --git a/session_app.py.publish b/session_app.py.publish
index 4a806ed..31df674 100755
--- a/session_app.py.publish
+++ b/session_app.py.publish
@@ -9,11 +9,14 @@
# 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
+# store "formdata" in session for changing the basic auth to form data for the ldap login https://stackoverflow.com/a/56904875/3569534
# Improve:
# move all configs to config file
# move all references to references section
-# accept a /login/basic endpoint with Authorization: header, use ldap
+# make /login/basic actually request http auth if user/pw not provided, or is a GET
+# try inspecting flask-httpauth auth.login_required section?
# accept a bind credential so we can perform lookups of users who match "uid=%s" under a basedn.
+# accept a ldap dns domain name, and a SRV lookup for _tcp._ldap
# Run:
# FLASK_APP=session_app.py FLASK_DEBUG=1 flask run --host 0.0.0.0
# Dependencies:
@@ -109,8 +112,22 @@ def requires_authn_ldap(function):
'''
@wraps(function)
def decorated(*args, **kwargs):
- username = request.form['username']
- pw = request.form['password']
+ # formdata is in session if we are coming from login_basic()
+ form = session.get('formdata', None)
+ if form:
+ print(f"DEBUG: requires_authn_ldap form={form}")
+ session.pop('formdata')
+ if 'username' in form:
+ username = form['username']
+ if 'password' in form:
+ pw = form['password']
+ else:
+ # then we are coming from the form with POST data
+ if 'username' not in request.form or 'password' not in request.form:
+ return _unauthorized_ldap()
+ username = request.form['username']
+ pw = request.form['password']
+ #print(f"DEBUG: requires_authn_ldap with username={username} and pw={pw}")
ll = ldap_login(username,pw)
if ll:
return function(ll.user,*args, **kwargs)
@@ -162,20 +179,12 @@ def login(user="None"):
# default, show login form
return redirect(url_for("login_form"))
elif request.method == "POST":
- # 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
+ if request.authorization:
+ return redirect(url_for("login_basic"),code=307)
+ return handle_login_ldap_from_non_ldap(request)
def ldap_login(username,password):
- print(f"Trying user {username} with pw '{password}'")
+ #print(f"DEBUG: Trying user {username} with pw '{password}'")
user = session_ldap.authenticated_user(
app.config['LDAP_URI'],
app.config['LDAP_USER_FORMAT'],
@@ -217,18 +226,34 @@ def login_ldap(user,groups=[]):
resp = login_generic(session,resp,user,groups)
return resp
-@app.route("/login/form", methods=['GET'])
-@app.route("/login/form/", methods=['GET'])
+@app.route("/login/form", methods=['POST','GET'])
+@app.route("/login/form/", methods=['POST','GET'])
def login_form():
- options = {
- "ldap": "ldap",
- "other": "other"
- }
- return render_template("login_form.html",
- login_url = url_for("login"),
- options=options,
- kerberos_url = url_for("login_kerberos")
- )
+ if request.method == "GET":
+ options = {
+ "ldap": "ldap",
+ "other": "other"
+ }
+ return render_template("login_form.html",
+ login_url = url_for("login"),
+ options=options,
+ kerberos_url = url_for("login_kerberos")
+ )
+ else:
+ # assume it is a POST
+ return redirect(url_for("login_ldap"), code=307)
+
+def handle_login_ldap_from_non_ldap(request):
+ # set default logintype for user
+ logintype = "ldap"
+ # redirect to whichever option was chosen in the drop-down
+ if 'logintype' in request.form:
+ logintype = request.form['logintype']
+ 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
@app.route("/logout")
@app.route("/logout/")
@@ -242,6 +267,21 @@ def logout():
resp.set_cookie('timestamp','',expires=0)
return resp
+@app.route("/login/basic",methods=['POST'])
+@app.route("/login/basic/",methods=['POST'])
+def login_basic():
+ if not request.authorization:
+ return Response(f"No username and password provided.",401)
+ if 'username' not in request.authorization:
+ return Response(f"No username provided.",401)
+ if 'password' not in request.authorization:
+ return Response(f"No password provided.",401)
+ username = request.authorization.username
+ pw = request.authorization.password
+ form={'username':username,'password':pw}
+ session['formdata'] = form
+ return redirect(url_for("login_ldap"),code=307)
+
## This bumps the session lifetime to two minutes farther out from each web request with this session.
#@app.before_request
#def make_session_permanent():
bgstack15