diff --git a/sso/models.py b/sso/models.py index a403a8d..f62c957 100644 --- a/sso/models.py +++ b/sso/models.py @@ -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" diff --git a/sso/settings.py b/sso/settings.py index b7c3a7a..8477de0 100644 --- a/sso/settings.py +++ b/sso/settings.py @@ -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) diff --git a/sso/views.py b/sso/views.py index 2da5c41..fd34a63 100644 --- a/sso/views.py +++ b/sso/views.py @@ -39,8 +39,6 @@ def profile(): @bp.route("/token//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//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//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, diff --git a/templates/client_edit.html b/templates/client_edit.html index 55a38d6..7eac5d9 100644 --- a/templates/client_edit.html +++ b/templates/client_edit.html @@ -43,10 +43,19 @@ + {{ 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 %} diff --git a/templates/confirm_destroy.html b/templates/confirm_destroy.html new file mode 100644 index 0000000..a96cdff --- /dev/null +++ b/templates/confirm_destroy.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% from "_helpers.html" import csrf_field %} + +{% block content %} +
+
+ +

This action is irreversible.

+
+ {{ csrf_field() }} + {% block submit %}{% endblock %} +
+
+
+{% endblock %} diff --git a/templates/confirm_regenerate.html b/templates/confirm_regenerate.html new file mode 100644 index 0000000..06a7b79 --- /dev/null +++ b/templates/confirm_regenerate.html @@ -0,0 +1,7 @@ +{% extends "confirm_destroy.html" %} + +{% block description %}You are about to regenerate client secret for "{{ client.client_name }}"{% endblock %} +{% block submit %} + + +{% endblock %}