Add destroy and regenerate secret actions to self-service portal
parent
cbfcdbd823
commit
df83515e6d
|
@ -30,6 +30,11 @@ class Client(db.Model, OAuth2ClientMixin):
|
||||||
scopes = scope_to_list(scope)
|
scopes = scope_to_list(scope)
|
||||||
return list_to_scope([s for s in scopes if s in allowed])
|
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):
|
class AuthorizationCode(db.Model, OAuth2AuthorizationCodeMixin):
|
||||||
__tablename__ = "oauth2_code"
|
__tablename__ = "oauth2_code"
|
||||||
|
|
|
@ -4,7 +4,6 @@ env = Env()
|
||||||
env.read_env()
|
env.read_env()
|
||||||
|
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
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
|
# 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)
|
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"])
|
@bp.route("/token/<int:id>/revoke", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def token_revoke(id):
|
def token_revoke(id):
|
||||||
csrf.protect()
|
|
||||||
|
|
||||||
token = Token.query.filter(
|
token = Token.query.filter(
|
||||||
Token.user_id == current_user.username, Token.id == id
|
Token.user_id == current_user.username, Token.id == id
|
||||||
).first()
|
).first()
|
||||||
|
@ -64,7 +62,7 @@ def login():
|
||||||
|
|
||||||
flash("Logged in successfully.")
|
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)
|
return render_template("login_oauth.html", form=form, next=next)
|
||||||
|
|
||||||
|
@ -89,6 +87,7 @@ def client_create():
|
||||||
|
|
||||||
db.session.add(client)
|
db.session.add(client)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
flash('Client has been created.', 'success')
|
||||||
return redirect(url_for(".client_edit", client_id=client.id))
|
return redirect(url_for(".client_edit", client_id=client.id))
|
||||||
|
|
||||||
return render_template("client_edit.html", form=form)
|
return render_template("client_edit.html", form=form)
|
||||||
|
@ -106,11 +105,48 @@ def client_edit(client_id):
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
client.set_client_metadata(form.data)
|
client.set_client_metadata(form.data)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
flash('Client has been changed.', 'success')
|
||||||
return redirect(url_for(".client_edit", client_id=client.id))
|
return redirect(url_for(".client_edit", client_id=client.id))
|
||||||
|
|
||||||
return render_template("client_edit.html", client=client, form=form)
|
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
|
# OAuth API
|
||||||
@bp.route("/oauth/authorize", methods=["GET", "POST"])
|
@bp.route("/oauth/authorize", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -134,8 +170,6 @@ def authorize():
|
||||||
scopes=grant.request.scope.split()
|
scopes=grant.request.scope.split()
|
||||||
)
|
)
|
||||||
|
|
||||||
csrf.protect()
|
|
||||||
|
|
||||||
if request.form["confirm"]:
|
if request.form["confirm"]:
|
||||||
grant_user = current_user
|
grant_user = current_user
|
||||||
else:
|
else:
|
||||||
|
@ -145,6 +179,7 @@ def authorize():
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/oauth/token", methods=["GET", "POST"])
|
@bp.route("/oauth/token", methods=["GET", "POST"])
|
||||||
|
@csrf.exempt
|
||||||
def issue_token():
|
def issue_token():
|
||||||
return authorization.create_token_response()
|
return authorization.create_token_response()
|
||||||
|
|
||||||
|
@ -169,7 +204,6 @@ def api_profile():
|
||||||
@require_oauth("profile:read openid", "OR")
|
@require_oauth("profile:read openid", "OR")
|
||||||
def api_userinfo():
|
def api_userinfo():
|
||||||
user = current_token.user
|
user = current_token.user
|
||||||
# user = LDAPUserProxy(flask.request.oauth.user)
|
|
||||||
return jsonify(
|
return jsonify(
|
||||||
sub=user.username,
|
sub=user.username,
|
||||||
name=user.gecos,
|
name=user.gecos,
|
||||||
|
|
|
@ -43,10 +43,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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('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('token_endpoint', 'Token Endpoint', url_for('.issue_token', _external=True)) }}
|
||||||
{{ static_field('authorize_endpoint', 'Authorize Endpoint', url_for('.authorize', _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)) }}
|
{{ static_field('userinfo_endpoint', 'UserInfo Endpoint', url_for('.api_userinfo', _external=True)) }}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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 %}
|
|
@ -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 New Issue