Add destroy and regenerate secret actions to self-service portal
This commit is contained in:
parent
cbfcdbd823
commit
df83515e6d
6 changed files with 78 additions and 7 deletions
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
46
sso/views.py
46
sso/views.py
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
17
templates/confirm_destroy.html
Normal file
17
templates/confirm_destroy.html
Normal 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 %}
|
7
templates/confirm_regenerate.html
Normal file
7
templates/confirm_regenerate.html
Normal 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 %}
|
Loading…
Reference in a new issue