diff options
-rw-r--r-- | python/env.example | 2 | ||||
-rw-r--r-- | python/fatcat_web/auth.py | 57 | ||||
-rw-r--r-- | python/fatcat_web/routes.py | 11 | ||||
-rw-r--r-- | python/fatcat_web/templates/auth_ia_login.html | 31 | ||||
-rw-r--r-- | python/fatcat_web/templates/auth_login.html | 1 | ||||
-rw-r--r-- | python/fatcat_web/templates/base.html | 2 | ||||
-rw-r--r-- | python/fatcat_web/web_config.py | 8 |
7 files changed, 103 insertions, 9 deletions
diff --git a/python/env.example b/python/env.example index c139df07..c13c6246 100644 --- a/python/env.example +++ b/python/env.example @@ -5,4 +5,6 @@ ELASTICSEARCH_BACKEND="http://localhost:9200" ELASTICSEARCH_INDEX="fatcat" GITLAB_CLIENT_ID="" GITLAB_CLIENT_SECRET="" +IA_XAUTH_CLIENT_ID="" +IA_XAUTH_CLIENT_SECRET="" SENTRY_DSN="" diff --git a/python/fatcat_web/auth.py b/python/fatcat_web/auth.py index 8b57a8c0..8035cbe5 100644 --- a/python/fatcat_web/auth.py +++ b/python/fatcat_web/auth.py @@ -1,16 +1,19 @@ +from collections import namedtuple +import requests +import pymacaroons from flask import Flask, render_template, send_from_directory, request, \ url_for, abort, g, redirect, jsonify, session, flash from fatcat_web import login_manager, api, priv_api, Config from flask_login import logout_user, login_user, UserMixin -import pymacaroons import fatcat_client def handle_logout(): logout_user() - for k in ('editor', 'token'): + for k in ('editor', 'api_token'): if k in session: session.pop(k) + session.clear() def handle_token_login(token): try: @@ -73,14 +76,59 @@ def handle_oauth(remote, token, user_info): login_user(load_user(editor.editor_id)) return redirect("/auth/account") - raise some_error + # XXX: what should this actually be? + raise Exception("didn't receive OAuth user_info") + +def handle_ia_xauth(email, password): + resp = requests.post(Config.IA_XAUTH_URI, + params={'op': 'authenticate'}, + json={ + 'version': '1', + 'email': email, + 'password': password, + 'access': Config.IA_XAUTH_CLIENT_ID, + 'secret': Config.IA_XAUTH_CLIENT_SECRET, + }) + if resp.status_code == 401 or (not resp.json().get('success')): + flash("Internet Archive email/password didn't match: {}".format(resp.json()['values']['reason'])) + return render_template('auth_ia_login.html', email=email), resp.status_code + elif resp.status_code != 200: + flash("Internet Archive login failed (internal error?)") + # TODO: log.warn + print("IA XAuth fail: {}".format(resp.content)) + return render_template('auth_ia_login.html', email=email), resp.status_code + # Successful login; now fetch info... + resp = requests.post(Config.IA_XAUTH_URI, + params={'op': 'info'}, + json={ + 'version': '1', + 'email': email, + 'access': Config.IA_XAUTH_CLIENT_ID, + 'secret': Config.IA_XAUTH_CLIENT_SECRET, + }) + if resp.status_code != 200: + flash("Internet Archive login failed (internal error?)") + # TODO: log.warn + print("IA XAuth fail: {}".format(resp.content)) + return render_template('auth_ia_login.html', email=email), resp.status_code + ia_info = resp.json()['values'] + + # and pass off "as if" we did OAuth successfully + FakeOAuthRemote = namedtuple('FakeOAuthRemote', ['name', 'OAUTH_CONFIG']) + remote = FakeOAuthRemote(name='archive', OAUTH_CONFIG={'api_base_url': Config.IA_XAUTH_URI}) + oauth_info = { + 'preferred_username': ia_info['screenname'], + 'iss': Config.IA_XAUTH_URI, + 'sub': ia_info['itemname'], + } + return handle_oauth(remote, None, oauth_info) @login_manager.user_loader def load_user(editor_id): # looks for extra info in session, and updates the user object with that. # If session isn't loaded/valid, should return None - if not 'editor' in session or not 'api_token' in session: + if (not session.get('editor')) or (not session.get('api_token')): return None editor = session['editor'] token = session['api_token'] @@ -90,3 +138,4 @@ def load_user(editor_id): user.username = editor['username'] user.token = token return user + diff --git a/python/fatcat_web/routes.py b/python/fatcat_web/routes.py index ebf7e88a..789d7bed 100644 --- a/python/fatcat_web/routes.py +++ b/python/fatcat_web/routes.py @@ -5,7 +5,7 @@ from flask import Flask, render_template, send_from_directory, request, \ url_for, abort, g, redirect, jsonify, session, flash from flask_login import login_required from fatcat_web import app, api, auth_api -from fatcat_web.auth import handle_token_login, handle_logout, load_user +from fatcat_web.auth import handle_token_login, handle_logout, load_user, handle_ia_xauth from fatcat_client.rest import ApiException from fatcat_web.search import do_search @@ -381,6 +381,14 @@ def login(): # show the user a list of login options return render_template('auth_login.html') +@app.route('/auth/ia/login', methods=['GET', 'POST']) +def ia_xauth_login(): + if 'email' in request.form: + # if a login attempt... + return handle_ia_xauth(request.form.get('email'), request.form.get('password')) + # else show form + return render_template('auth_ia_login.html') + @app.route('/auth/token_login', methods=['GET', 'POST']) def token_login(): # show the user a list of login options @@ -409,7 +417,6 @@ def change_username(): @app.route('/auth/logout') def logout(): - # TODO: clear extra session info handle_logout() return render_template('auth_logout.html') diff --git a/python/fatcat_web/templates/auth_ia_login.html b/python/fatcat_web/templates/auth_ia_login.html new file mode 100644 index 00000000..ebf08021 --- /dev/null +++ b/python/fatcat_web/templates/auth_ia_login.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} +{% block body %} +<h1>Login with Internet Archive account</h1> + +<p>Warning: still experimental! + +<br> +<br> +<br> + +{% if current_user.is_authenticated %} + <div class="ui negative message"> + <div class="header">You are already logged in!</div> + <p>You should logout first. Re-authenticating would be undefined behavior. + </div> +{% else %} + <form class="" role="login" action="/auth/ia/login" method="post"> + <div class="ui form"> + <div class="ui input huge fluid"> + <input type="email" placeholder="user@domain.tdl..." name="email" {% if email %}value="{{ email }}"{% endif %} aria-label="email for login"> + </div> + <div class="ui action input huge fluid"> + <input type="password" placeholder="password" name="password" aria-label="internet archive password"> + <button class="ui button">Login</button> + </div> + </div> + </div> + </form> +{% endif %} + +{% endblock %} diff --git a/python/fatcat_web/templates/auth_login.html b/python/fatcat_web/templates/auth_login.html index 98b1c7c4..9ccae816 100644 --- a/python/fatcat_web/templates/auth_login.html +++ b/python/fatcat_web/templates/auth_login.html @@ -12,6 +12,7 @@ <p>Other options... <ul> <li><a href="/auth/token_login">Using auth token</a> (admin/operator) + <li><a href="/auth/ia/login">With Internet Archive account</a> (experimental) </ul> {% endblock %} diff --git a/python/fatcat_web/templates/base.html b/python/fatcat_web/templates/base.html index e3824213..3b324cba 100644 --- a/python/fatcat_web/templates/base.html +++ b/python/fatcat_web/templates/base.html @@ -55,7 +55,7 @@ {% if messages %} <div class="ui message"> {# Needs more javascript: <i class="close icon"></i> #} - <div class="header">Now Hear This!</div> + <div class="header">Now Hear This...</div> <ul class="list"> {% for message in messages %} <li>{{ message }} diff --git a/python/fatcat_web/web_config.py b/python/fatcat_web/web_config.py index 85134762..0ae43a3a 100644 --- a/python/fatcat_web/web_config.py +++ b/python/fatcat_web/web_config.py @@ -31,8 +31,12 @@ class Config(object): FLASK_SECRET_KEY = os.environ.get("FLASK_SECRET_KEY", default=None) SECRET_KEY = FLASK_SECRET_KEY - GITLAB_CLIENT_ID = os.environ.get("GITLAB_CLIENT_ID", default="bogus") - GITLAB_CLIENT_SECRET = os.environ.get("GITLAB_CLIENT_SECRET", default="bogus") + GITLAB_CLIENT_ID = os.environ.get("GITLAB_CLIENT_ID", default=None) + GITLAB_CLIENT_SECRET = os.environ.get("GITLAB_CLIENT_SECRET", default=None) + + IA_XAUTH_URI = "https://archive.org/services/xauthn/" + IA_XAUTH_CLIENT_ID = os.environ.get("IA_XAUTH_CLIENT_ID", default=None) + IA_XAUTH_CLIENT_SECRET = os.environ.get("IA_XAUTH_CLIENT_SECRET", default=None) # protect cookies (which include API tokens) SESSION_COOKIE_HTTPONLY = True |