Add destroy and regenerate secret actions to self-service portal

master
informatic 2020-05-31 19:53:42 +02:00
parent cbfcdbd823
commit df83515e6d
6 changed files with 78 additions and 7 deletions

View File

@ -30,6 +30,11 @@ class Client(db.Model, OAuth2ClientMixin):
scopes = scope_to_list(scope)
return list_to_scope([s for s in scopes if s in allowed])
def revoke_tokens(self):
"""Revoke all active access/refresh tokens and authorization codes"""
Token.query.filter(Token.client_id == self.client_id).delete()
AuthorizationCode.query.filter(AuthorizationCode.client_id == self.client_id).delete()
class AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin):
__tablename__ = "oauth2_code"

View File

@ -4,7 +4,6 @@ env = Env()
env.read_env()
SQLALCHEMY_TRACK_MODIFICATIONS = False
WTF_CSRF_CHECK_DEFAULT = False
# This needs to be disabled when we use an additional proxy in front of our app
WTF_CSRF_SSL_STRICT = env.bool("WTF_CSRF_SSL_STRICT", default=False)

View File

@ -39,8 +39,6 @@ def profile():
@bp.route("/token/<int:id>/revoke", methods=["POST"])
@login_required
def token_revoke(id):
csrf.protect()
token = Token.query.filter(
Token.user_id == current_user.username, Token.id == id
).first()
@ -64,7 +62,7 @@ def login():
flash("Logged in successfully.")
return redirect(next or url_for("profile"))
return redirect(next or url_for(".profile"))
return render_template("login_oauth.html", form=form, next=next)
@ -89,6 +87,7 @@ def client_create():
db.session.add(client)
db.session.commit()
flash('Client has been created.', 'success')
return redirect(url_for(".client_edit", client_id=client.id))
return render_template("client_edit.html", form=form)
@ -106,11 +105,48 @@ def client_edit(client_id):
if form.validate_on_submit():
client.set_client_metadata(form.data)
db.session.commit()
flash('Client has been changed.', 'success')
return redirect(url_for(".client_edit", client_id=client.id))
return render_template("client_edit.html", client=client, form=form)
@bp.route("/client/<client_id>/destroy", methods=["GET", "POST"])
def client_destroy(client_id):
client = get_object_or_404(
Client, Client.id == client_id, Client.owner_id == current_user.get_user_id()
)
if request.method == 'POST':
db.session.delete(client)
client.revoke_tokens()
db.session.commit()
flash('Client destroyed.', 'success')
return redirect(url_for('.profile'))
return render_template("confirm_destroy.html", client=client)
@bp.route("/client/<client_id>/regenerate", methods=["GET", "POST"])
def client_regenerate_secret(client_id):
client = get_object_or_404(
Client, Client.id == client_id, Client.owner_id == current_user.get_user_id()
)
if request.method == 'POST':
print(request.form)
client.client_secret = generate_token()
if request.form.get('revoke') == 'yes':
client.revoke_tokens()
db.session.commit()
flash('Client secret regenerated.', 'success')
return redirect(url_for('.client_edit', client_id=client.id))
return render_template("confirm_regenerate.html", client=client)
# OAuth API
@bp.route("/oauth/authorize", methods=["GET", "POST"])
@login_required
@ -134,8 +170,6 @@ def authorize():
scopes=grant.request.scope.split()
)
csrf.protect()
if request.form["confirm"]:
grant_user = current_user
else:
@ -145,6 +179,7 @@ def authorize():
@bp.route("/oauth/token", methods=["GET", "POST"])
@csrf.exempt
def issue_token():
return authorization.create_token_response()
@ -169,7 +204,6 @@ def api_profile():
@require_oauth("profile:read openid", "OR")
def api_userinfo():
user = current_token.user
# user = LDAPUserProxy(flask.request.oauth.user)
return jsonify(
sub=user.username,
name=user.gecos,

View File

@ -43,10 +43,19 @@
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-4 col-md-offset-4">
<a href="{{ url_for('.client_regenerate_secret', client_id=client.id) }}" class="btn btn-warning btn-block">Regenerate client secret</a>
</div>
<div class="col-md-4">
<a href="{{ url_for('.client_destroy', client_id=client.id) }}" class="btn btn-danger btn-block">Destroy</a>
</div>
</div>
{{ static_field('openid_configuration', 'OpenID Connect Discovery Endpoint', url_for('.openid_configuration', _external=True)) }}
{{ static_field('token_endpoint', 'Token Endpoint', url_for('.issue_token', _external=True)) }}
{{ static_field('authorize_endpoint', 'Authorize Endpoint', url_for('.authorize', _external=True)) }}
{{ static_field('userinfo_endpoint', 'UserInfo Endpoint', url_for('.api_userinfo', _external=True)) }}
{% endif %}
</form>
</div>

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% from "_helpers.html" import csrf_field %}
{% block content %}
<div class="container">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<h2 class="page-header">
{% block description %}You are about to destroy client "{{ client.client_name }}"{% endblock %}
</h2>
<p>This action is irreversible.</p>
<form action="" class="form-horizontal" method="POST">
{{ csrf_field() }}
{% block submit %}<button type="submit" class="btn btn-danger btn-block">Destroy</button>{% endblock %}
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends "confirm_destroy.html" %}
{% block description %}You are about to regenerate client secret for "{{ client.client_name }}"{% endblock %}
{% block submit %}
<input type="checkbox" name="revoke" value="yes" id="revoke" /> <label for="revoke">Revoke all access tokens</label>
<button type="submit" class="btn btn-warning btn-block">Regenerate client secret</button>
{% endblock %}