add oauth2 token managment, partial password change, bugfixes
This commit is contained in:
parent
6c388c8129
commit
6334c993a9
19 changed files with 236 additions and 137 deletions
|
@ -1,5 +1,5 @@
|
|||
from flask import current_app
|
||||
from .form.login import PasswordForm, TotpForm, Fido2Form
|
||||
from .form.auth import PasswordForm, TotpForm, Fido2Form
|
||||
from ldap3 import Server, Connection
|
||||
from ldap3.core.exceptions import LDAPException
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from flask_wtf import FlaskForm
|
|||
from wtforms import StringField, SubmitField, TextField, \
|
||||
TextAreaField, PasswordField, IntegerField, FloatField, \
|
||||
DateTimeField, DateField, FormField, BooleanField, \
|
||||
SelectField, Form as NoCsrfForm
|
||||
SelectField, Form as NoCsrfForm, SelectMultipleField
|
||||
from wtforms.widgets.html5 import NumberInput, DateInput
|
||||
from wtforms.validators import DataRequired, NumberRange, Optional, NoneOf, Length
|
||||
from datetime import datetime
|
||||
|
@ -27,3 +27,11 @@ class TotpForm(FlaskForm):
|
|||
class Fido2Form(FlaskForm):
|
||||
fido2 = TextField(gettext('Fido2'), default="Javascript Required")
|
||||
submit = SubmitField(gettext('Authorize'))
|
||||
|
||||
|
||||
class ConsentForm(FlaskForm):
|
||||
# scopes = SelectMultipleField(gettext('scopes'))
|
||||
# audiences = SelectMultipleField(gettext('audiences'))
|
||||
remember = BooleanField(gettext('remember me'))
|
||||
submit = SubmitField()
|
||||
|
|
@ -6,7 +6,7 @@ from wtforms import StringField, SubmitField, TextField, \
|
|||
SelectField, Form as NoCsrfForm, HiddenField
|
||||
from wtforms.widgets.html5 import NumberInput, DateInput
|
||||
from wtforms.validators import DataRequired, NumberRange, \
|
||||
Optional, NoneOf, Length
|
||||
Optional, NoneOf, Length, EqualTo
|
||||
|
||||
|
||||
class ClientCertForm(FlaskForm):
|
||||
|
@ -34,5 +34,11 @@ class TOTPDeleteForm(FlaskForm):
|
|||
submit = SubmitField(gettext('Delete'))
|
||||
|
||||
|
||||
class PasswordChangeForm(FlaskForm):
|
||||
old_password = PasswordField(gettext('Old Password'), validators=[DataRequired()])
|
||||
password = PasswordField(gettext('New Password'), validators=[DataRequired()])
|
||||
password_repeat = PasswordField(gettext('Repeat Password'), validators=[DataRequired(),EqualTo('password')])
|
||||
submit = SubmitField(gettext('Change Password'))
|
||||
|
||||
class OidcAuthenticationConfirm(FlaskForm):
|
||||
submit = SubmitField(gettext('Continue'))
|
||||
|
|
|
@ -6,24 +6,12 @@ from flask.helpers import make_response
|
|||
from flask.templating import render_template
|
||||
from oic.oic.message import TokenErrorResponse, UserInfoErrorResponse, EndSessionRequest
|
||||
|
||||
from pyop.access_token import AccessToken, BearerTokenError
|
||||
from pyop.exceptions import InvalidAuthenticationRequest, InvalidAccessToken, InvalidClientAuthentication, OAuthError, \
|
||||
InvalidSubjectIdentifier, InvalidClientRegistrationRequest
|
||||
from pyop.util import should_fragment_encode
|
||||
|
||||
from flask import Blueprint, render_template, request, url_for
|
||||
from flask_login import login_required, login_user, logout_user
|
||||
from werkzeug.utils import redirect
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
from base64 import b64decode, b64encode
|
||||
import ory_hydra_client as hydra
|
||||
from requests_oauthlib.oauth2_session import OAuth2Session
|
||||
import requests
|
||||
|
||||
from ..model import User, SecurityUser
|
||||
from ..model import User
|
||||
from ..model_db import User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..auth_providers import LdapAuthProvider
|
||||
|
||||
|
||||
|
@ -37,8 +25,9 @@ def userinfo():
|
|||
user_db = DbUser.query.get(token_info.sub)
|
||||
user = User.query().by_username(user_db.username)
|
||||
|
||||
public_url = current_app.config.get('HYDRA_PUBLIC_URL')
|
||||
r = requests.get(
|
||||
"http://127.0.0.1:4444/userinfo",
|
||||
f"{public_url}/userinfo",
|
||||
headers={
|
||||
'authorization': request.headers['authorization']})
|
||||
userinfo = r.json()
|
||||
|
|
|
@ -4,19 +4,11 @@ from urllib.parse import urlencode, parse_qs
|
|||
import flask
|
||||
from flask import Blueprint, redirect
|
||||
from flask import current_app, session
|
||||
from flask import jsonify
|
||||
from flask.helpers import make_response
|
||||
from flask.templating import render_template
|
||||
from oic.oic.message import TokenErrorResponse, UserInfoErrorResponse, EndSessionRequest
|
||||
from flask_babel import gettext
|
||||
|
||||
from pyop.access_token import AccessToken, BearerTokenError
|
||||
from pyop.exceptions import InvalidAuthenticationRequest, InvalidAccessToken, InvalidClientAuthentication, OAuthError, \
|
||||
InvalidSubjectIdentifier, InvalidClientRegistrationRequest
|
||||
from pyop.util import should_fragment_encode
|
||||
|
||||
from flask import Blueprint, render_template, request, url_for
|
||||
from flask import request, url_for
|
||||
from flask_login import login_required, login_user, logout_user
|
||||
from werkzeug.utils import redirect
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
from base64 import b64decode, b64encode
|
||||
|
@ -24,35 +16,48 @@ import http
|
|||
|
||||
from ..model import User, SecurityUser
|
||||
from ..model_db import db, User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..form.auth import ConsentForm, LoginForm
|
||||
from ..auth_providers import AUTH_PROVIDER_LIST
|
||||
|
||||
|
||||
auth_views = Blueprint('auth', __name__, url_prefix='/auth')
|
||||
|
||||
|
||||
@auth_views.route('/consent', methods=['GET', 'POST'])
|
||||
def consent():
|
||||
"""Always grant consent."""
|
||||
# DUMMPY ONLY
|
||||
|
||||
remember_me = True
|
||||
remember_for = 60*60*24*7 # remember for 7 days
|
||||
form = ConsentForm()
|
||||
remember_for = 60*60*24*7 # remember for 7 days
|
||||
|
||||
consent_request = current_app.hydra_api.get_consent_request(request.args['consent_challenge'])
|
||||
consent_request = current_app.hydra_api.get_consent_request(
|
||||
request.args['consent_challenge'])
|
||||
requested_scope = consent_request.requested_scope
|
||||
resp = current_app.hydra_api.accept_consent_request(consent_request.challenge, body={
|
||||
'grant_scope': requested_scope,
|
||||
'remember': remember_me,
|
||||
'remember_for': remember_for,
|
||||
})
|
||||
return redirect(resp.redirect_to)
|
||||
requested_audiences = consent_request.requested_access_token_audience
|
||||
|
||||
if form.validate_on_submit() or consent_request.skip:
|
||||
resp = current_app.hydra_api.accept_consent_request(
|
||||
consent_request.challenge, body={
|
||||
'grant_scope': requested_scope,
|
||||
'grant_access_token_audience': requested_audiences,
|
||||
'remember': form.data['remember'],
|
||||
# 'remember_for': remember_for,
|
||||
})
|
||||
return redirect(resp.redirect_to)
|
||||
return render_template(
|
||||
'auth/consent.html.j2',
|
||||
form=form,
|
||||
client=consent_request.client,
|
||||
requested_scope=requested_scope,
|
||||
requested_audiences=requested_audiences)
|
||||
|
||||
|
||||
@auth_views.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
login_challenge = request.args.get('login_challenge')
|
||||
login_request = current_app.hydra_api.get_login_request(login_challenge)
|
||||
|
||||
|
||||
if login_request.skip:
|
||||
resp = current_app.hydra_api.accept_login_request(
|
||||
login_challenge,
|
||||
|
@ -66,8 +71,9 @@ def login():
|
|||
else:
|
||||
session['user'] = None
|
||||
session['auth_providers'] = []
|
||||
return redirect(url_for('auth.login_auth', login_challenge=login_challenge))
|
||||
return render_template('frontend/login.html.j2', form=form)
|
||||
return redirect(
|
||||
url_for('auth.login_auth', login_challenge=login_challenge))
|
||||
return render_template('auth/login.html.j2', form=form)
|
||||
|
||||
|
||||
@auth_views.route('/login/auth', methods=['GET', 'POST'])
|
||||
|
@ -114,7 +120,7 @@ def login_auth():
|
|||
except TypeError:
|
||||
_next = None
|
||||
return redirect(_next or url_for('frontend.index'))
|
||||
return render_template('frontend/login_auth.html.j2', forms=auth_forms)
|
||||
return render_template('auth/login_auth.html.j2', forms=auth_forms)
|
||||
|
||||
|
||||
@auth_views.route("/logout")
|
||||
|
@ -122,6 +128,7 @@ def logout():
|
|||
logout_challenge = request.args.get('logout_challenge')
|
||||
logout_request = current_app.hydra_api.get_logout_request(logout_challenge)
|
||||
resp = current_app.hydra_api.accept_logout_request(logout_challenge)
|
||||
logout_user()
|
||||
return redirect(resp.redirect_to)
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ from flask_dance.consumer import OAuth2ConsumerBlueprint
|
|||
|
||||
from ..model import User, SecurityUser, Totp
|
||||
from ..model_db import OAuth, db, User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..form.frontend import ClientCertForm, TOTPForm, TOTPDeleteForm
|
||||
from ..form.frontend import ClientCertForm, TOTPForm, TOTPDeleteForm, PasswordChangeForm
|
||||
from ..auth_providers import AUTH_PROVIDER_LIST
|
||||
|
||||
|
||||
|
@ -218,3 +217,42 @@ def totp_delete(totp_name):
|
|||
|
||||
return jsonify({
|
||||
'status': 'ok'})
|
||||
|
||||
|
||||
|
||||
@frontend_views.route('/password_change')
|
||||
@login_required
|
||||
def password_change():
|
||||
|
||||
form = PasswordChangeForm()
|
||||
return render_template('frontend/password_change.html.j2', form=form)
|
||||
|
||||
|
||||
@frontend_views.route('/password_change', methods=['POST'])
|
||||
@login_required
|
||||
def password_change_post():
|
||||
form = PasswordChangeForm()
|
||||
if form.validate():
|
||||
return jsonify({})
|
||||
return jsonify({'errors': form.errors})
|
||||
|
||||
|
||||
@frontend_views.route('/oauth2_token')
|
||||
@login_required
|
||||
def oauth2_tokens():
|
||||
|
||||
subject = current_app.oauth.session.get('/userinfo').json()['sub']
|
||||
consent_sessions = current_app.hydra_api.list_subject_consent_sessions(
|
||||
subject)
|
||||
|
||||
return render_template('frontend/oauth2_tokens.html.j2', consent_sessions=consent_sessions)
|
||||
|
||||
@frontend_views.route('/oauth2_token/<client_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def oauth2_token_revoke(client_id: str):
|
||||
subject = current_app.oauth.session.get('/userinfo').json()['sub']
|
||||
current_app.hydra_api.revoke_consent_sessions(
|
||||
subject,
|
||||
client=client_id)
|
||||
|
||||
return jsonify({})
|
||||
|
|
|
@ -1,35 +1,10 @@
|
|||
import flask
|
||||
from flask import Blueprint, redirect, request
|
||||
from flask import current_app, session
|
||||
from flask import jsonify
|
||||
from flask.helpers import make_response
|
||||
from flask.templating import render_template
|
||||
from oic.oic.message import TokenErrorResponse, UserInfoErrorResponse, EndSessionRequest
|
||||
|
||||
from pyop.access_token import AccessToken, BearerTokenError
|
||||
from pyop.exceptions import InvalidAuthenticationRequest, InvalidAccessToken, InvalidClientAuthentication, OAuthError, \
|
||||
InvalidSubjectIdentifier, InvalidClientRegistrationRequest
|
||||
from pyop.util import should_fragment_encode
|
||||
|
||||
from flask import Blueprint, render_template, request, url_for
|
||||
from flask_login import login_required, login_user, logout_user
|
||||
from werkzeug.utils import redirect
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
from base64 import b64decode, b64encode
|
||||
import ory_hydra_client as hydra
|
||||
from requests_oauthlib.oauth2_session import OAuth2Session
|
||||
import requests
|
||||
from flask import current_app, Blueprint
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
from ..model import User, SecurityUser
|
||||
from ..model_db import User as DbUser
|
||||
from ..form.login import LoginForm
|
||||
from ..auth_providers import LdapAuthProvider
|
||||
|
||||
|
||||
pki_views = Blueprint('pki', __name__, url_prefix='/')
|
||||
|
||||
|
||||
@pki_views.route('/<service_name>.crl')
|
||||
def crl(service_name: str):
|
||||
service = current_app.lenticular_services[service_name]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue