aboutsummaryrefslogtreecommitdiff
path: root/gn3/auth
diff options
context:
space:
mode:
Diffstat (limited to 'gn3/auth')
-rw-r--r--gn3/auth/authentication/oauth2/models/oauth2client.py33
-rw-r--r--gn3/auth/authentication/users.py16
-rw-r--r--gn3/auth/authorisation/users/admin/views.py20
3 files changed, 61 insertions, 8 deletions
diff --git a/gn3/auth/authentication/oauth2/models/oauth2client.py b/gn3/auth/authentication/oauth2/models/oauth2client.py
index 14c4c94..564ed32 100644
--- a/gn3/auth/authentication/oauth2/models/oauth2client.py
+++ b/gn3/auth/authentication/oauth2/models/oauth2client.py
@@ -1,13 +1,13 @@
"""OAuth2 Client model."""
import json
-import uuid
import datetime
+from uuid import UUID
from typing import Sequence, Optional, NamedTuple
from pymonad.maybe import Just, Maybe, Nothing
from gn3.auth import db
-from gn3.auth.authentication.users import User, user_by_id, same_password
+from gn3.auth.authentication.users import User, users, user_by_id, same_password
from gn3.auth.authorisation.errors import NotFoundError
@@ -18,7 +18,7 @@ class OAuth2Client(NamedTuple):
This is defined according to the mixin at
https://docs.authlib.org/en/latest/specs/rfc6749.html#authlib.oauth2.rfc6749.ClientMixin
"""
- client_id: uuid.UUID
+ client_id: UUID
client_secret: str
client_id_issued_at: datetime.datetime
client_secret_expires_at: datetime.datetime
@@ -129,7 +129,7 @@ class OAuth2Client(NamedTuple):
"""Return the default redirect uri"""
return self.client_metadata.get("default_redirect_uri", "")
-def client(conn: db.DbConnection, client_id: uuid.UUID,
+def client(conn: db.DbConnection, client_id: UUID,
user: Optional[User] = None) -> Maybe:
"""Retrieve a client by its ID"""
with db.cursor(conn) as cursor:
@@ -145,7 +145,7 @@ def client(conn: db.DbConnection, client_id: uuid.UUID,
the_user = None
return Just(
- OAuth2Client(uuid.UUID(result["client_id"]),
+ OAuth2Client(UUID(result["client_id"]),
result["client_secret"],
datetime.datetime.fromtimestamp(
result["client_id_issued_at"]),
@@ -156,7 +156,7 @@ def client(conn: db.DbConnection, client_id: uuid.UUID,
return Nothing
-def client_by_id_and_secret(conn: db.DbConnection, client_id: uuid.UUID,
+def client_by_id_and_secret(conn: db.DbConnection, client_id: UUID,
client_secret: str) -> OAuth2Client:
"""Retrieve a client by its ID and secret"""
with db.cursor(conn) as cursor:
@@ -171,7 +171,7 @@ def client_by_id_and_secret(conn: db.DbConnection, client_id: uuid.UUID,
datetime.datetime.fromtimestamp(
row["client_secret_expires_at"]),
json.loads(row["client_metadata"]),
- user_by_id(conn, uuid.UUID(row["user_id"])))
+ user_by_id(conn, UUID(row["user_id"])))
raise NotFoundError("Could not find client with the given credentials.")
@@ -203,3 +203,22 @@ def save_client(conn: db.DbConnection, the_client: OAuth2Client) -> OAuth2Client
"user_id": str(the_client.user.user_id)
})
return the_client
+
+def oauth2_clients(conn: db.DbConnection) -> tuple[OAuth2Client, ...]:
+ """Fetch a list of all OAuth2 clients."""
+ with db.cursor(conn) as cursor:
+ cursor.execute("SELECT * FROM oauth2_clients")
+ clients_rs = cursor.fetchall()
+ the_users = {
+ usr.user_id: usr for usr in users(
+ conn, tuple({UUID(result["user_id"]) for result in clients_rs}))
+ }
+ return tuple(OAuth2Client(UUID(result["client_id"]),
+ result["client_secret"],
+ datetime.datetime.fromtimestamp(
+ result["client_id_issued_at"]),
+ datetime.datetime.fromtimestamp(
+ result["client_secret_expires_at"]),
+ json.loads(result["client_metadata"]),
+ the_users[UUID(result["user_id"])])
+ for result in clients_rs)
diff --git a/gn3/auth/authentication/users.py b/gn3/auth/authentication/users.py
index 8b4f115..0e72ed2 100644
--- a/gn3/auth/authentication/users.py
+++ b/gn3/auth/authentication/users.py
@@ -110,3 +110,19 @@ def set_user_password(
"ON CONFLICT (user_id) DO UPDATE SET password=:hash"),
{"user_id": str(user.user_id), "hash": hashed_password})
return user, hashed_password
+
+def users(conn: db.DbConnection,
+ ids: tuple[UUID, ...] = tuple()) -> tuple[User, ...]:
+ """
+ Fetch all users with the given `ids`. If `ids` is empty, return ALL users.
+ """
+ params = ", ".join(["?"] * len(ids))
+ with db.cursor(conn) as cursor:
+ query = "SELECT * FROM users" + (
+ f" WHERE user_id IN ({params})"
+ if len(ids) > 0 else "")
+ print(query)
+ cursor.execute(query, tuple(str(the_id) for the_id in ids))
+ return tuple(User(UUID(row["user_id"]), row["email"], row["name"])
+ for row in cursor.fetchall())
+ return tuple()
diff --git a/gn3/auth/authorisation/users/admin/views.py b/gn3/auth/authorisation/users/admin/views.py
index ee76354..11152d2 100644
--- a/gn3/auth/authorisation/users/admin/views.py
+++ b/gn3/auth/authorisation/users/admin/views.py
@@ -1,5 +1,6 @@
"""UI for admin stuff"""
import uuid
+import json
import random
import string
from functools import partial
@@ -22,7 +23,8 @@ from gn3.auth.db_utils import with_db_connection
from gn3.auth.authentication.oauth2.models.oauth2client import (
save_client,
- OAuth2Client)
+ OAuth2Client,
+ oauth2_clients)
from gn3.auth.authentication.users import (
User,
user_by_id,
@@ -44,6 +46,7 @@ def update_expires():
return None
@admin.route("/dashboard", methods=["GET"])
+@is_admin
def dashboard():
"""Admin dashboard."""
return render_template("admin/dashboard.html")
@@ -151,3 +154,18 @@ def register_client():
"admin/registered-client.html",
client=client,
client_secret = raw_client_secret)
+
+def __parse_client__(sqlite3Row) -> dict:
+ """Parse the client details into python datatypes."""
+ return {
+ **dict(sqlite3Row),
+ "client_metadata": json.loads(sqlite3Row["client_metadata"])
+ }
+
+@admin.route("/list-client", methods=["GET"])
+@is_admin
+def list_clients():
+ """List all registered OAuth2 clients."""
+ return render_template(
+ "admin/list-oauth2-clients.html",
+ clients=with_db_connection(oauth2_clients))