diff options
author | Frederick Muriuki Muriithi | 2024-07-19 10:23:28 -0500 |
---|---|---|
committer | Frederick Muriuki Muriithi | 2024-07-31 09:30:24 -0500 |
commit | 6b18e1f0b05222d84fd0b06a8e5c2780df6958d5 (patch) | |
tree | 4806d860eaf68e2624b70b1f08dbba3eaf91c0f4 | |
parent | c56dc39193909062d25e3270d9ab0ec3467a47ee (diff) | |
download | gn-auth-6b18e1f0b05222d84fd0b06a8e5c2780df6958d5.tar.gz |
Enable registration of a public-jwks-uri for every client
-rw-r--r-- | gn_auth/auth/authorisation/users/admin/views.py | 113 | ||||
-rw-r--r-- | gn_auth/templates/admin/register-client.html | 10 | ||||
-rw-r--r-- | gn_auth/templates/admin/view-oauth2-client.html | 70 |
3 files changed, 29 insertions, 164 deletions
diff --git a/gn_auth/auth/authorisation/users/admin/views.py b/gn_auth/auth/authorisation/users/admin/views.py index 0ab69e9..85aeb50 100644 --- a/gn_auth/auth/authorisation/users/admin/views.py +++ b/gn_auth/auth/authorisation/users/admin/views.py @@ -3,14 +3,12 @@ import uuid import json import random import string -from pathlib import Path from typing import Optional from functools import partial from dataclasses import asdict from urllib.parse import urlparse from datetime import datetime, timezone, timedelta -from authlib.jose import KeySet, JsonWebKey from email_validator import validate_email, EmailNotValidError from flask import ( flash, @@ -178,6 +176,9 @@ def check_register_client_form(form): "scope[]", "You need to select at least one scope option."),) + if not uri_valid(form.get("client_jwk_uri", "")): + errors = errors + ("The provided client's public JWKs URI is invalid.",) + errors = tuple(item for item in errors if item is not None) if bool(errors): raise RegisterClientError(errors) @@ -225,7 +226,8 @@ def register_client(): "default_redirect_uri": default_redirect_uri, "redirect_uris": [default_redirect_uri] + form.get("other_redirect_uri", "").split(), "response_type": __response_types__(tuple(grant_types)), - "scope": form.getlist("scope[]") + "scope": form.getlist("scope[]"), + "public-jwks-uri": form.get("client_jwk_uri", "") }, user = with_db_connection(partial( user_by_id, user_id=uuid.UUID(form["user"]))) @@ -262,108 +264,6 @@ def view_client(client_id: uuid.UUID): scope=app.config["OAUTH2_SCOPE"], granttypes=_FORM_GRANT_TYPES_) -@admin.route("/register-client-public-key", methods=["POST"]) -@is_admin -def register_client_public_key(): - """Register a client's SSL key""" - form = request.form - admin_dashboard_uri = redirect(url_for("oauth2.admin.dashboard")) - view_client_uri = redirect(url_for("oauth2.admin.view_client", - client_id=form["client_id"])) - if not bool(form.get("client_id")): - flash("No client selected.", "alert-danger") - return admin_dashboard_uri - - try: - _client = with_db_connection(partial( - oauth2_client, client_id=uuid.UUID(form["client_id"]))) - if _client.is_nothing(): - raise ValueError("No such client.") - _client = _client.value - except ValueError: - flash("Invalid client ID provided.", "alert-danger") - return admin_dashboard_uri - try: - _key = JsonWebKey.import_key(form["client_ssl_key"].strip()) - except ValueError: - flash("Invalid key provided!", "alert-danger") - return view_client_uri - - keypath = Path(app.config["CLIENTS_SSL_PUBLIC_KEYS_DIR"]).joinpath( - f"{_key.thumbprint()}.pem") - if not keypath.exists(): - with open(keypath, mode="w", encoding="utf8") as _kpth: - _kpth.write(form["client_ssl_key"]) - - with_db_connection(partial(save_client, the_client=OAuth2Client( - client_id=_client.client_id, - client_secret=_client.client_secret, - client_id_issued_at=_client.client_id_issued_at, - client_secret_expires_at=_client.client_secret_expires_at, - client_metadata={ - **_client.client_metadata, - "public_keys": list(set( - _client.client_metadata.get("public_keys", []) + - [str(keypath)]))}, - user=_client.user))) - flash("Client key successfully registered.", "alert-success") - return view_client_uri - - -@admin.route("/delete-client-public-key", methods=["POST"]) -@is_admin -def delete_client_public_key(): - """Delete a client's SSL key""" - form = request.form - admin_dashboard_uri = redirect(url_for("oauth2.admin.dashboard")) - view_client_uri = redirect(url_for("oauth2.admin.view_client", - client_id=form["client_id"])) - if not bool(form.get("client_id")): - flash("No client selected.", "alert-danger") - return admin_dashboard_uri - - try: - _client = with_db_connection(partial( - oauth2_client, client_id=uuid.UUID(form["client_id"]))) - if _client.is_nothing(): - raise ValueError("No such client.") - _client = _client.value - except ValueError: - flash("Invalid client ID provided.", "alert-danger") - return admin_dashboard_uri - - if form.get("ssl_key", None) is None: - flash("The key must be provided.", "alert-danger") - return view_client_uri - - try: - def find_by_kid(keyset: KeySet, kid: str) -> JsonWebKey: - for key in keyset.keys: - if key.thumbprint() == kid: - return key - raise ValueError('Invalid JSON Web Key Set') - _key = find_by_kid(_client.jwks, form.get("ssl_key")) - except ValueError: - flash("Could not delete: No such public key.", "alert-danger") - return view_client_uri - - _keys = (_key for _key in _client.jwks.keys - if _key.thumbprint() != form["ssl_key"]) - _keysdir = Path(app.config["CLIENTS_SSL_PUBLIC_KEYS_DIR"]) - with_db_connection(partial(save_client, the_client=OAuth2Client( - client_id=_client.client_id, - client_secret=_client.client_secret, - client_id_issued_at=_client.client_id_issued_at, - client_secret_expires_at=_client.client_secret_expires_at, - client_metadata={ - **_client.client_metadata, - "public_keys": list(set( - _keysdir.joinpath(f"{_key.thumbprint()}.pem") - for _key in _keys))}, - user=_client.user))) - flash("Key deleted.", "alert-success") - return view_client_uri - @admin.route("/edit-client", methods=["POST"]) @is_admin @@ -391,7 +291,8 @@ def edit_client(): [form["redirect_uri"]] + form["other_redirect_uris"].split("\r\n"))), "grant_types": form.getlist("grants[]"), - "scope": form.getlist("scope[]") + "scope": form.getlist("scope[]"), + "public-jwks-uri": form.get("client_jwk_uri", "") } with_db_connection(partial(save_client, the_client=OAuth2Client( the_client.client_id, diff --git a/gn_auth/templates/admin/register-client.html b/gn_auth/templates/admin/register-client.html index 5e24148..bfe56f8 100644 --- a/gn_auth/templates/admin/register-client.html +++ b/gn_auth/templates/admin/register-client.html @@ -84,6 +84,16 @@ </select> </div> + <legend>Other metadata</legend> + <div class="form-group"> + <label class="form-group" for="txt-client-jwk-uri"> + Client's Public JWKs</label> + <input type="text" + id="txt-client-jwk-uri" + name="client_jwk_uri" + class="form-control" /> + </div> + <input type="submit" value="register client" class="btn btn-primary" /> </form> {%endblock%} diff --git a/gn_auth/templates/admin/view-oauth2-client.html b/gn_auth/templates/admin/view-oauth2-client.html index 6da8291..c250ee3 100644 --- a/gn_auth/templates/admin/view-oauth2-client.html +++ b/gn_auth/templates/admin/view-oauth2-client.html @@ -73,68 +73,22 @@ {%endif%} /> {{granttype.name}} </label> - - <input type="submit" class="btn btn-primary" value="update client" /> -</form> - -<hr /> -<h2>Signing/Verification SSL Keys</h2> -<table> - <caption>Registered Public Keys</caption> - <thead> - <tr> - <th>JWK Thumbprint</th> - <th>Actions</th> - </tr> - </thead> - - <tbody> - {%for sslkey in client.jwks.keys:%} - <tr> - <td>{{sslkey.thumbprint()}}</td> - <td> - <form method="POST" - action="{{url_for('oauth2.admin.delete_client_public_key')}}"> - <input type="hidden" - name="client_id" - value="{{client.client_id}}" /> - <input type="hidden" - name="ssl_key" - value="{{sslkey.thumbprint()}}" /> - <input type="submit" - class="btn btn-danger" - value="delete key" /> - </form> - </td> - </tr> - {%else%} - <tr> - <td class="alert-warning" - colspan="2"> - There are no registered SSL keys for this client. - </td> - </tr> </div> {%endfor%} - </tbody> -</table> -<form id="frm-client-add-ssl-key" - method="POST" - action="{{url_for('oauth2.admin.register_client_public_key')}}"> - <legend>Register new SSL key</legend> - <input type="hidden" name="client_id" value="{{client.client_id}}" /> - <fieldset> - <label for="txt-area-client-ssl-key">Client's Public Key</label> - <textarea id="txt-area-client-ssl-key" - name="client_ssl_key" - required="required" - class="form-control" - rows="10"></textarea> - </fieldset> </div> - <br /> - <input type="submit" class="btn btn-primary" value="register key" /> + <legend>Other metadata</legend> + <div class="form-group"> + <label class="form-group" for="txt-client-jwk-uri"> + Client's Public JWKs</label> + <input type="text" + id="txt-client-jwk-uri" + name="client_jwk_uri" + class="form-control" + value="{{client.client_metadata.get('public-jwks-uri', '')}}" /> + </div> + + <input type="submit" class="btn btn-primary" value="update client" /> </form> {%endif%} |