about summary refs log tree commit diff
path: root/gn_auth
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2024-07-19 10:23:28 -0500
committerFrederick Muriuki Muriithi2024-07-31 09:30:24 -0500
commit6b18e1f0b05222d84fd0b06a8e5c2780df6958d5 (patch)
tree4806d860eaf68e2624b70b1f08dbba3eaf91c0f4 /gn_auth
parentc56dc39193909062d25e3270d9ab0ec3467a47ee (diff)
downloadgn-auth-6b18e1f0b05222d84fd0b06a8e5c2780df6958d5.tar.gz
Enable registration of a public-jwks-uri for every client
Diffstat (limited to 'gn_auth')
-rw-r--r--gn_auth/auth/authorisation/users/admin/views.py113
-rw-r--r--gn_auth/templates/admin/register-client.html10
-rw-r--r--gn_auth/templates/admin/view-oauth2-client.html70
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%}