aboutsummaryrefslogtreecommitdiff
path: root/gn_auth
diff options
context:
space:
mode:
Diffstat (limited to 'gn_auth')
-rw-r--r--gn_auth/auth/authentication/oauth2/endpoints/revocation.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/endpoints/utilities.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/grants/authorisation_code_grant.py4
-rw-r--r--gn_auth/auth/authentication/oauth2/grants/password_grant.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/models/authorization_code.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/models/oauth2client.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/models/oauth2token.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/resource_server.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/server.py2
-rw-r--r--gn_auth/auth/authentication/oauth2/views.py4
-rw-r--r--gn_auth/auth/authentication/users.py2
-rw-r--r--gn_auth/auth/authorisation/checks.py2
-rw-r--r--gn_auth/auth/authorisation/data/genotypes.py4
-rw-r--r--gn_auth/auth/authorisation/data/mrna.py4
-rw-r--r--gn_auth/auth/authorisation/data/phenotypes.py10
-rw-r--r--gn_auth/auth/authorisation/data/views.py6
-rw-r--r--gn_auth/auth/authorisation/groups/data.py14
-rw-r--r--gn_auth/auth/authorisation/groups/models.py2
-rw-r--r--gn_auth/auth/authorisation/groups/views.py8
-rw-r--r--gn_auth/auth/authorisation/privileges.py2
-rw-r--r--gn_auth/auth/authorisation/resources/checks.py2
-rw-r--r--gn_auth/auth/authorisation/resources/models.py4
-rw-r--r--gn_auth/auth/authorisation/resources/views.py2
-rw-r--r--gn_auth/auth/authorisation/roles/models.py2
-rw-r--r--gn_auth/auth/authorisation/roles/views.py2
-rw-r--r--gn_auth/auth/authorisation/users/admin/ui.py2
-rw-r--r--gn_auth/auth/authorisation/users/admin/views.py4
-rw-r--r--gn_auth/auth/authorisation/users/collections/views.py4
-rw-r--r--gn_auth/auth/authorisation/users/masquerade/models.py2
-rw-r--r--gn_auth/auth/authorisation/users/masquerade/views.py2
-rw-r--r--gn_auth/auth/authorisation/users/models.py2
-rw-r--r--gn_auth/auth/authorisation/users/views.py4
-rw-r--r--gn_auth/templates/admin/dashboard.html24
-rw-r--r--gn_auth/templates/admin/list-oauth2-clients.html56
-rw-r--r--gn_auth/templates/admin/login.html32
-rw-r--r--gn_auth/templates/admin/register-client.html78
-rw-r--r--gn_auth/templates/admin/registered-client.html21
-rw-r--r--gn_auth/templates/admin/view-oauth2-client.html75
-rw-r--r--gn_auth/templates/base.html24
-rw-r--r--gn_auth/templates/common-macros.html7
-rw-r--r--gn_auth/templates/oauth2/authorise-user.html48
-rw-r--r--gn_auth/templates/oauth2/oauth2_error.html16
42 files changed, 436 insertions, 55 deletions
diff --git a/gn_auth/auth/authentication/oauth2/endpoints/revocation.py b/gn_auth/auth/authentication/oauth2/endpoints/revocation.py
index ad9e67c..240ca30 100644
--- a/gn_auth/auth/authentication/oauth2/endpoints/revocation.py
+++ b/gn_auth/auth/authentication/oauth2/endpoints/revocation.py
@@ -3,7 +3,7 @@
from flask import current_app
from authlib.oauth2.rfc7009 import RevocationEndpoint as _RevocationEndpoint
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.oauth2.models.oauth2token import (
save_token, OAuth2Token, revoke_token)
diff --git a/gn_auth/auth/authentication/oauth2/endpoints/utilities.py b/gn_auth/auth/authentication/oauth2/endpoints/utilities.py
index d32f9b3..29ace7c 100644
--- a/gn_auth/auth/authentication/oauth2/endpoints/utilities.py
+++ b/gn_auth/auth/authentication/oauth2/endpoints/utilities.py
@@ -4,7 +4,7 @@ from typing import Any, Optional
from flask import current_app
from pymonad.maybe import Nothing
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.oauth2.models.oauth2token import (
OAuth2Token, token_by_access_token, token_by_refresh_token)
diff --git a/gn_auth/auth/authentication/oauth2/grants/authorisation_code_grant.py b/gn_auth/auth/authentication/oauth2/grants/authorisation_code_grant.py
index 6d8112e..e866c41 100644
--- a/gn_auth/auth/authentication/oauth2/grants/authorisation_code_grant.py
+++ b/gn_auth/auth/authentication/oauth2/grants/authorisation_code_grant.py
@@ -9,8 +9,8 @@ from flask import current_app as app
from authlib.oauth2.rfc6749 import grants
from authlib.oauth2.rfc7636 import create_s256_code_challenge
-from gn_auth.auth import db
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authentication.users import User
from ..models.oauth2client import OAuth2Client
diff --git a/gn_auth/auth/authentication/oauth2/grants/password_grant.py b/gn_auth/auth/authentication/oauth2/grants/password_grant.py
index 75fc122..79382fd 100644
--- a/gn_auth/auth/authentication/oauth2/grants/password_grant.py
+++ b/gn_auth/auth/authentication/oauth2/grants/password_grant.py
@@ -3,7 +3,7 @@
from flask import current_app as app
from authlib.oauth2.rfc6749 import grants
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.users import valid_login, user_by_email
from gn_auth.auth.authorisation.errors import NotFoundError
diff --git a/gn_auth/auth/authentication/oauth2/models/authorization_code.py b/gn_auth/auth/authentication/oauth2/models/authorization_code.py
index 98b5d0f..6c586f3 100644
--- a/gn_auth/auth/authentication/oauth2/models/authorization_code.py
+++ b/gn_auth/auth/authentication/oauth2/models/authorization_code.py
@@ -5,7 +5,7 @@ from typing import NamedTuple
from pymonad.maybe import Just, Maybe, Nothing
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from .oauth2client import OAuth2Client
diff --git a/gn_auth/auth/authentication/oauth2/models/oauth2client.py b/gn_auth/auth/authentication/oauth2/models/oauth2client.py
index 07618dd..ea86772 100644
--- a/gn_auth/auth/authentication/oauth2/models/oauth2client.py
+++ b/gn_auth/auth/authentication/oauth2/models/oauth2client.py
@@ -6,7 +6,7 @@ from typing import Sequence, Optional, NamedTuple
from pymonad.maybe import Just, Maybe, Nothing
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.users import User, users, user_by_id, same_password
from gn_auth.auth.authorisation.errors import NotFoundError
diff --git a/gn_auth/auth/authentication/oauth2/models/oauth2token.py b/gn_auth/auth/authentication/oauth2/models/oauth2token.py
index 725c096..6f9dc12 100644
--- a/gn_auth/auth/authentication/oauth2/models/oauth2token.py
+++ b/gn_auth/auth/authentication/oauth2/models/oauth2token.py
@@ -5,7 +5,7 @@ from typing import NamedTuple, Optional
from pymonad.maybe import Just, Maybe, Nothing
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.users import User, user_by_id
from gn_auth.auth.authorisation.errors import NotFoundError
diff --git a/gn_auth/auth/authentication/oauth2/resource_server.py b/gn_auth/auth/authentication/oauth2/resource_server.py
index 32c463f..c062b28 100644
--- a/gn_auth/auth/authentication/oauth2/resource_server.py
+++ b/gn_auth/auth/authentication/oauth2/resource_server.py
@@ -4,7 +4,7 @@ from flask import current_app as app
from authlib.oauth2.rfc6750 import BearerTokenValidator as _BearerTokenValidator
from authlib.integrations.flask_oauth2 import ResourceProtector
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.oauth2.models.oauth2token import token_by_access_token
class BearerTokenValidator(_BearerTokenValidator):
diff --git a/gn_auth/auth/authentication/oauth2/server.py b/gn_auth/auth/authentication/oauth2/server.py
index b085219..12366a6 100644
--- a/gn_auth/auth/authentication/oauth2/server.py
+++ b/gn_auth/auth/authentication/oauth2/server.py
@@ -8,7 +8,7 @@ from authlib.oauth2.rfc6749.errors import InvalidClientError
from authlib.integrations.flask_oauth2 import AuthorizationServer
# from authlib.oauth2.rfc7636 import CodeChallenge
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from .models.oauth2client import client
from .models.oauth2token import OAuth2Token, save_token
diff --git a/gn_auth/auth/authentication/oauth2/views.py b/gn_auth/auth/authentication/oauth2/views.py
index 43d9e49..ac74739 100644
--- a/gn_auth/auth/authentication/oauth2/views.py
+++ b/gn_auth/auth/authentication/oauth2/views.py
@@ -14,8 +14,8 @@ from flask import (
render_template,
current_app as app)
-from gn_auth.auth import db
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authorisation.errors import ForbiddenAccess
from .resource_server import require_oauth
diff --git a/gn_auth/auth/authentication/users.py b/gn_auth/auth/authentication/users.py
index 327820e..46cd838 100644
--- a/gn_auth/auth/authentication/users.py
+++ b/gn_auth/auth/authentication/users.py
@@ -5,7 +5,7 @@ from typing import Any, Tuple, NamedTuple
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authorisation.errors import NotFoundError
class User(NamedTuple):
diff --git a/gn_auth/auth/authorisation/checks.py b/gn_auth/auth/authorisation/checks.py
index 02c6810..46a51fe 100644
--- a/gn_auth/auth/authorisation/checks.py
+++ b/gn_auth/auth/authorisation/checks.py
@@ -4,7 +4,7 @@ from typing import Callable
from flask import request, current_app as app
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from . import privileges as auth_privs
from .errors import InvalidData, AuthorisationError
diff --git a/gn_auth/auth/authorisation/data/genotypes.py b/gn_auth/auth/authorisation/data/genotypes.py
index 41822a1..61963a6 100644
--- a/gn_auth/auth/authorisation/data/genotypes.py
+++ b/gn_auth/auth/authorisation/data/genotypes.py
@@ -5,7 +5,7 @@ from typing import Iterable
from MySQLdb.cursors import DictCursor
import gn_auth.auth.db as authdb
-import gn_auth.db_utils as gn3db
+import gn_auth.auth.db.mariadb as gn3db
from gn_auth.auth.dictify import dictify
from gn_auth.auth.authorisation.checks import authorised_p
from gn_auth.auth.authorisation.groups.models import Group
@@ -22,7 +22,7 @@ def linked_genotype_data(conn: authdb.DbConnection) -> Iterable[dict]:
"group(s)."),
oauth2_scope="profile group resource")
def ungrouped_genotype_data(# pylint: disable=[too-many-arguments]
- authconn: authdb.DbConnection, gn3conn: gn3db.Connection,
+ authconn: authdb.DbConnection, gn3conn: gn3db.DbConnection,
search_query: str, selected: tuple[dict, ...] = tuple(),
limit: int = 10000, offset: int = 0) -> tuple[
dict, ...]:
diff --git a/gn_auth/auth/authorisation/data/mrna.py b/gn_auth/auth/authorisation/data/mrna.py
index 0b08571..79ea7c0 100644
--- a/gn_auth/auth/authorisation/data/mrna.py
+++ b/gn_auth/auth/authorisation/data/mrna.py
@@ -4,7 +4,7 @@ from typing import Iterable
from MySQLdb.cursors import DictCursor
import gn_auth.auth.db as authdb
-import gn_auth.db_utils as gn3db
+import gn_auth.auth.db.mariadb as gn3db
from gn_auth.auth.dictify import dictify
from gn_auth.auth.authorisation.checks import authorised_p
from gn_auth.auth.authorisation.groups.models import Group
@@ -21,7 +21,7 @@ def linked_mrna_data(conn: authdb.DbConnection) -> Iterable[dict]:
"group(s)."),
oauth2_scope="profile group resource")
def ungrouped_mrna_data(# pylint: disable=[too-many-arguments]
- authconn: authdb.DbConnection, gn3conn: gn3db.Connection,
+ authconn: authdb.DbConnection, gn3conn: gn3db.DbConnection,
search_query: str, selected: tuple[dict, ...] = tuple(),
limit: int = 10000, offset: int = 0) -> tuple[
dict, ...]:
diff --git a/gn_auth/auth/authorisation/data/phenotypes.py b/gn_auth/auth/authorisation/data/phenotypes.py
index 2f4c564..d3cc33a 100644
--- a/gn_auth/auth/authorisation/data/phenotypes.py
+++ b/gn_auth/auth/authorisation/data/phenotypes.py
@@ -5,13 +5,13 @@ from typing import Any, Iterable
from MySQLdb.cursors import DictCursor
import gn_auth.auth.db as authdb
-import gn_auth.db_utils as gn3db
+import gn_auth.auth.db.mariadb as gn3db
from gn_auth.auth.dictify import dictify
from gn_auth.auth.authorisation.checks import authorised_p
from gn_auth.auth.authorisation.groups.models import Group
def linked_phenotype_data(
- authconn: authdb.DbConnection, gn3conn: gn3db.Connection,
+ authconn: authdb.DbConnection, gn3conn: gn3db.DbConnection,
species: str = "") -> Iterable[dict[str, Any]]:
"""Retrieve phenotype data linked to user groups."""
authkeys = ("SpeciesId", "InbredSetId", "PublishFreezeId", "PublishXRefId")
@@ -52,7 +52,7 @@ def linked_phenotype_data(
"group(s)."),
oauth2_scope="profile group resource")
def ungrouped_phenotype_data(
- authconn: authdb.DbConnection, gn3conn: gn3db.Connection):
+ authconn: authdb.DbConnection, gn3conn: gn3db.DbConnection):
"""Retrieve phenotype data that is not linked to any user group."""
with gn3conn.cursor() as cursor:
params = tuple(
@@ -82,7 +82,7 @@ def ungrouped_phenotype_data(
return tuple()
-def __traits__(gn3conn: gn3db.Connection, params: tuple[dict, ...]) -> tuple[dict, ...]:
+def __traits__(gn3conn: gn3db.DbConnection, params: tuple[dict, ...]) -> tuple[dict, ...]:
"""An internal utility function. Don't use outside of this module."""
if len(params) < 1:
return tuple()
@@ -115,7 +115,7 @@ def __traits__(gn3conn: gn3db.Connection, params: tuple[dict, ...]) -> tuple[dic
"group(s)."),
oauth2_scope="profile group resource")
def link_phenotype_data(
- authconn:authdb.DbConnection, gn3conn: gn3db.Connection, group: Group,
+ authconn:authdb.DbConnection, gn3conn: gn3db.DbConnection, group: Group,
traits: tuple[dict, ...]) -> dict:
"""Link phenotype traits to a user group."""
with authdb.cursor(authconn) as cursor:
diff --git a/gn_auth/auth/authorisation/data/views.py b/gn_auth/auth/authorisation/data/views.py
index a25e9a2..9e55dd8 100644
--- a/gn_auth/auth/authorisation/data/views.py
+++ b/gn_auth/auth/authorisation/data/views.py
@@ -10,13 +10,13 @@ from MySQLdb.cursors import DictCursor
from authlib.integrations.flask_oauth2.errors import _HTTPException
from flask import request, jsonify, Response, Blueprint, current_app as app
-import gn_auth.db_utils as gn3db
+import gn_auth.auth.db.mariadb as gn3db
from gn_auth import jobs
from gn_auth.commands import run_async_cmd
from gn_auth.db.traits import build_trait_name
-from gn_auth.auth import db
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authorisation.checks import require_json
from gn_auth.auth.authorisation.errors import InvalidData, NotFoundError
diff --git a/gn_auth/auth/authorisation/groups/data.py b/gn_auth/auth/authorisation/groups/data.py
index d05747b..2d9ddb5 100644
--- a/gn_auth/auth/authorisation/groups/data.py
+++ b/gn_auth/auth/authorisation/groups/data.py
@@ -1,14 +1,14 @@
"""Handles the resource objects' data."""
from MySQLdb.cursors import DictCursor
-from gn_auth import db_utils as gn3db
-from gn_auth.auth import db as authdb
+from gn_auth.auth.db import mariadb as gn3db
+from gn_auth.auth.db import sqlite3 as authdb
from gn_auth.auth.authorisation.groups import Group
from gn_auth.auth.authorisation.checks import authorised_p
from gn_auth.auth.authorisation.errors import NotFoundError
def __fetch_mrna_data_by_ids__(
- conn: gn3db.Connection, dataset_ids: tuple[str, ...]) -> tuple[
+ conn: gn3db.DbConnection, dataset_ids: tuple[str, ...]) -> tuple[
dict, ...]:
"""Fetch mRNA Assay data by ID."""
with conn.cursor(DictCursor) as cursor:
@@ -26,7 +26,7 @@ def __fetch_mrna_data_by_ids__(
raise NotFoundError("Could not find mRNA Assay data with the given ID.")
def __fetch_geno_data_by_ids__(
- conn: gn3db.Connection, dataset_ids: tuple[str, ...]) -> tuple[
+ conn: gn3db.DbConnection, dataset_ids: tuple[str, ...]) -> tuple[
dict, ...]:
"""Fetch genotype data by ID."""
with conn.cursor(DictCursor) as cursor:
@@ -44,7 +44,7 @@ def __fetch_geno_data_by_ids__(
raise NotFoundError("Could not find Genotype data with the given ID.")
def __fetch_pheno_data_by_ids__(
- conn: gn3db.Connection, dataset_ids: tuple[str, ...]) -> tuple[
+ conn: gn3db.DbConnection, dataset_ids: tuple[str, ...]) -> tuple[
dict, ...]:
"""Fetch phenotype data by ID."""
with conn.cursor(DictCursor) as cursor:
@@ -66,7 +66,7 @@ def __fetch_pheno_data_by_ids__(
"Could not find Phenotype/Publish data with the given IDs.")
def __fetch_data_by_id(
- conn: gn3db.Connection, dataset_type: str,
+ conn: gn3db.DbConnection, dataset_type: str,
dataset_ids: tuple[str, ...]) -> tuple[dict, ...]:
"""Fetch data from MySQL by IDs."""
fetch_fns = {
@@ -82,7 +82,7 @@ def __fetch_data_by_id(
"group(s)."),
oauth2_scope="profile group resource")
def link_data_to_group(
- authconn: authdb.DbConnection, gn3conn: gn3db.Connection,
+ authconn: authdb.DbConnection, gn3conn: gn3db.DbConnection,
dataset_type: str, dataset_ids: tuple[str, ...], group: Group) -> tuple[
dict, ...]:
"""Link the given data to the specified group."""
diff --git a/gn_auth/auth/authorisation/groups/models.py b/gn_auth/auth/authorisation/groups/models.py
index 9008bef..0ffd3a7 100644
--- a/gn_auth/auth/authorisation/groups/models.py
+++ b/gn_auth/auth/authorisation/groups/models.py
@@ -7,7 +7,7 @@ from typing import Any, Sequence, Iterable, Optional, NamedTuple
from flask import g
from pymonad.maybe import Just, Maybe, Nothing
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.dictify import dictify
from gn_auth.auth.authentication.users import User, user_by_id
diff --git a/gn_auth/auth/authorisation/groups/views.py b/gn_auth/auth/authorisation/groups/views.py
index d1724cc..a63e09d 100644
--- a/gn_auth/auth/authorisation/groups/views.py
+++ b/gn_auth/auth/authorisation/groups/views.py
@@ -7,11 +7,11 @@ from functools import partial
from MySQLdb.cursors import DictCursor
from flask import request, jsonify, Response, Blueprint, current_app
-from gn_auth.auth import db
-from gn_auth import db_utils as gn3db
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.db import mariadb as gn3db
from gn_auth.auth.dictify import dictify
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db.sqlite3 import with_db_connection
from .data import link_data_to_group
from .models import (
@@ -169,7 +169,7 @@ def unlinked_genotype_data(
return tuple(dict(row) for row in cursor.fetchall())
def unlinked_phenotype_data(
- authconn: db.DbConnection, gn3conn: gn3db.Connection,
+ authconn: db.DbConnection, gn3conn: gn3db.DbConnection,
group: Group) -> tuple[dict, ...]:
"""
Retrieve all phenotype data linked to a group but not linked to any
diff --git a/gn_auth/auth/authorisation/privileges.py b/gn_auth/auth/authorisation/privileges.py
index 1b0f06c..a348b92 100644
--- a/gn_auth/auth/authorisation/privileges.py
+++ b/gn_auth/auth/authorisation/privileges.py
@@ -1,7 +1,7 @@
"""Handle privileges"""
from typing import Any, Iterable, NamedTuple
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.users import User
class Privilege(NamedTuple):
diff --git a/gn_auth/auth/authorisation/resources/checks.py b/gn_auth/auth/authorisation/resources/checks.py
index a341dbd..0c57b17 100644
--- a/gn_auth/auth/authorisation/resources/checks.py
+++ b/gn_auth/auth/authorisation/resources/checks.py
@@ -3,7 +3,7 @@ from uuid import UUID
from functools import reduce
from typing import Sequence
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authentication.users import User
def __organise_privileges_by_resource_id__(rows):
diff --git a/gn_auth/auth/authorisation/resources/models.py b/gn_auth/auth/authorisation/resources/models.py
index d96979f..8c035c9 100644
--- a/gn_auth/auth/authorisation/resources/models.py
+++ b/gn_auth/auth/authorisation/resources/models.py
@@ -5,10 +5,10 @@ from uuid import UUID, uuid4
from functools import reduce, partial
from typing import Any, Dict, Sequence, Optional, NamedTuple
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.dictify import dictify
from gn_auth.auth.authentication.users import User
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db.sqlite3 import with_db_connection
from .checks import authorised_for
diff --git a/gn_auth/auth/authorisation/resources/views.py b/gn_auth/auth/authorisation/resources/views.py
index 9645303..157d5a3 100644
--- a/gn_auth/auth/authorisation/resources/views.py
+++ b/gn_auth/auth/authorisation/resources/views.py
@@ -6,7 +6,7 @@ from functools import reduce
from flask import request, jsonify, Response, Blueprint, current_app as app
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db.sqlite3 import with_db_connection
from .checks import authorised_for
from .models import (
diff --git a/gn_auth/auth/authorisation/roles/models.py b/gn_auth/auth/authorisation/roles/models.py
index 742a90e..0fecfc1 100644
--- a/gn_auth/auth/authorisation/roles/models.py
+++ b/gn_auth/auth/authorisation/roles/models.py
@@ -5,7 +5,7 @@ from typing import Any, Sequence, Iterable, NamedTuple
from pymonad.either import Left, Right, Either
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.dictify import dictify
from gn_auth.auth.authentication.users import User
from gn_auth.auth.authorisation.errors import AuthorisationError
diff --git a/gn_auth/auth/authorisation/roles/views.py b/gn_auth/auth/authorisation/roles/views.py
index 6510365..4fc51f7 100644
--- a/gn_auth/auth/authorisation/roles/views.py
+++ b/gn_auth/auth/authorisation/roles/views.py
@@ -3,7 +3,7 @@ import uuid
from flask import jsonify, Response, Blueprint, current_app
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.dictify import dictify
from .models import user_role
diff --git a/gn_auth/auth/authorisation/users/admin/ui.py b/gn_auth/auth/authorisation/users/admin/ui.py
index 4351a68..9087412 100644
--- a/gn_auth/auth/authorisation/users/admin/ui.py
+++ b/gn_auth/auth/authorisation/users/admin/ui.py
@@ -3,7 +3,7 @@ from functools import wraps
from flask import flash, url_for, redirect
from gn_auth.auth.authentication.users import User
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authorisation.roles.models import user_roles
from gn_auth.session import logged_in, session_user, clear_session_info
diff --git a/gn_auth/auth/authorisation/users/admin/views.py b/gn_auth/auth/authorisation/users/admin/views.py
index ab04f91..4a37bc5 100644
--- a/gn_auth/auth/authorisation/users/admin/views.py
+++ b/gn_auth/auth/authorisation/users/admin/views.py
@@ -18,8 +18,8 @@ from flask import (
from gn_auth import session
-from gn_auth.auth import db
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authentication.oauth2.models.oauth2client import (
save_client,
diff --git a/gn_auth/auth/authorisation/users/collections/views.py b/gn_auth/auth/authorisation/users/collections/views.py
index 3dcc10f..9900205 100644
--- a/gn_auth/auth/authorisation/users/collections/views.py
+++ b/gn_auth/auth/authorisation/users/collections/views.py
@@ -4,8 +4,8 @@ from uuid import UUID
from redis import Redis
from flask import jsonify, request, Response, Blueprint, current_app
-from gn_auth.auth import db
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db import sqlite3 as db
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authorisation.checks import require_json
from gn_auth.auth.authorisation.errors import NotFoundError
diff --git a/gn_auth/auth/authorisation/users/masquerade/models.py b/gn_auth/auth/authorisation/users/masquerade/models.py
index c45d761..9779764 100644
--- a/gn_auth/auth/authorisation/users/masquerade/models.py
+++ b/gn_auth/auth/authorisation/users/masquerade/models.py
@@ -5,7 +5,7 @@ from datetime import datetime
from flask import current_app as app
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authorisation.errors import ForbiddenAccess
from gn_auth.auth.authorisation.roles.models import user_roles
diff --git a/gn_auth/auth/authorisation/users/masquerade/views.py b/gn_auth/auth/authorisation/users/masquerade/views.py
index 9e42945..7bd8ddb 100644
--- a/gn_auth/auth/authorisation/users/masquerade/views.py
+++ b/gn_auth/auth/authorisation/users/masquerade/views.py
@@ -4,7 +4,7 @@ from functools import partial
from flask import request, jsonify, Response, Blueprint
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db.sqlite3 import with_db_connection
from gn_auth.auth.authorisation.errors import InvalidData
from gn_auth.auth.authorisation.checks import require_json
diff --git a/gn_auth/auth/authorisation/users/models.py b/gn_auth/auth/authorisation/users/models.py
index 71fa390..61489cf 100644
--- a/gn_auth/auth/authorisation/users/models.py
+++ b/gn_auth/auth/authorisation/users/models.py
@@ -2,7 +2,7 @@
import uuid
from functools import reduce
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authorisation.roles.models import Role
from gn_auth.auth.authorisation.checks import authorised_p
from gn_auth.auth.authorisation.privileges import Privilege
diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py
index 7783dca..0646e3a 100644
--- a/gn_auth/auth/authorisation/users/views.py
+++ b/gn_auth/auth/authorisation/users/views.py
@@ -7,9 +7,9 @@ import sqlite3
from email_validator import validate_email, EmailNotValidError
from flask import request, jsonify, Response, Blueprint, current_app
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.dictify import dictify
-from gn_auth.auth.db_utils import with_db_connection
+from gn_auth.auth.db.sqlite3 import with_db_connection
from .models import list_users
from .masquerade.views import masq
diff --git a/gn_auth/templates/admin/dashboard.html b/gn_auth/templates/admin/dashboard.html
new file mode 100644
index 0000000..7798022
--- /dev/null
+++ b/gn_auth/templates/admin/dashboard.html
@@ -0,0 +1,24 @@
+{%extends "base.html"%}
+
+{%block title%}Genenetwork3: Admin Dashboard{%endblock%}
+
+{%block pagetitle%}Admin Dashboard{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<ul class="nav">
+ <li>
+ <a href="{{url_for('oauth2.admin.register_client')}}"
+ title="Register a new OAuth2 client.">Register OAuth2 Client</a>
+ </li>
+ <li>
+ <a href="{{url_for('oauth2.admin.list_clients')}}"
+ title="List OAuth2 clients.">List OAuth2 Client</a>
+ </li>
+ <li>
+ <a href="{{url_for('oauth2.admin.logout')}}"
+ title="Log out of the system.">Logout</a>
+ </li>
+</ul>
+{%endblock%}
diff --git a/gn_auth/templates/admin/list-oauth2-clients.html b/gn_auth/templates/admin/list-oauth2-clients.html
new file mode 100644
index 0000000..0104f0d
--- /dev/null
+++ b/gn_auth/templates/admin/list-oauth2-clients.html
@@ -0,0 +1,56 @@
+{%extends "base.html"%}
+
+{%block title%}Genenetwork3: OAuth2 Clients{%endblock%}
+
+{%block pagetitle%}OAuth2 Clients{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<table class="table table-hover table-striped cell-border no-footer">
+ <caption>List of registered OAuth2 clients</caption>
+ <thead>
+ <tr>
+ <th>Client ID</th>
+ <th>Client Name</th>
+ <th>Default Redirect URI</th>
+ <th>Owner</th>
+ <th colspan="2">Actions</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {%for client in clients%}
+ <tr>
+ <td>{{client.client_id}}</td>
+ <td>{{client.client_metadata.client_name}}</td>
+ <td>{{client.client_metadata.default_redirect_uri}}</td>
+ <td>{{client.user.name}} ({{client.user.email}})</td>
+ <td>
+ <a href="{{url_for('oauth2.admin.view_client', client_id=client.client_id)}}"
+ title"View/Edit client {{client.client_metadata.client_name}}"
+ class="btn btn-info">
+ View/Edit
+ </a>
+ </td>
+ <td>
+ <form id="frm:delete:{{client.client_id}}"
+ action="{{url_for('oauth2.admin.delete_client')}}"
+ method="POST">
+ <input type="hidden" name="client_id" value="{{client.client_id}}" />
+ <input type="submit" value="Delete"
+ title"Delete client {{client.client_metadata.client_name}}"
+ class="btn btn-danger" />
+ </form>
+ </td>
+ </tr>
+ {%else%}
+ <tr>
+ <td colspan="4" style="text-align: center;">
+ No registered OAuth2 clients!
+ </td>
+ </tr>
+ {%endfor%}
+ </tbody>
+</table>
+{%endblock%}
diff --git a/gn_auth/templates/admin/login.html b/gn_auth/templates/admin/login.html
new file mode 100644
index 0000000..ac217ab
--- /dev/null
+++ b/gn_auth/templates/admin/login.html
@@ -0,0 +1,32 @@
+{%extends "base.html"%}
+
+{%block title%}Log in to Genenetwork3{%endblock%}
+
+{%block pagetitle%}Admin Log In{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<form method="POST" action="{{url_for('oauth2.admin.login')}}">
+
+ <fieldset>
+ <legend>User Credentials</legend>
+
+ <input name="next_uri" type="hidden" value={{next_uri}}>
+
+ <fieldset class="form-group">
+ <label for="txt:email" class="form-label">Email Address</label>
+ <input name="email" type="email" id="txt:email" required="required"
+ placeholder="your@email.address" class="form-control" />
+ </fieldset>
+
+ <fieldset class="form-group">
+ <label for="txt:password" class="form-label">Password</label>
+ <input name="password" type="password" id="txt:password"
+ required="required" class="form-control" />
+ </fieldset>
+ </fieldset>
+
+ <input type="submit" value="log in" class="btn btn-primary" />
+</form>
+{%endblock%}
diff --git a/gn_auth/templates/admin/register-client.html b/gn_auth/templates/admin/register-client.html
new file mode 100644
index 0000000..daac977
--- /dev/null
+++ b/gn_auth/templates/admin/register-client.html
@@ -0,0 +1,78 @@
+{%extends "base.html"%}
+
+{%block title%}Genenetwork3: Register OAuth2 Client{%endblock%}
+
+{%block pagetitle%}Register OAuth2 Client{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<form method="POST" action="{{url_for('oauth2.admin.register_client')}}">
+
+ <fieldset>
+ <legend>Select client scope</legend>
+
+ {%for scp in scope%}
+ <input name="scope[]" id="chk:{{scp}}"type="checkbox" value="{{scp}}"
+ {%if scp=="profile"%}checked="checked"{%endif%} />
+ <label for="chk:{{scp}}">{{scp}}</label><br />
+ {%endfor%}
+
+ </fieldset>
+
+ <fieldset>
+ <legend>Basic OAuth2 client information</legend>
+
+
+ <label for="txt:client-name">Client name</label>
+ <input name="client_name" type="text" id="txt:client-name"
+ required="required" />
+ <br /><br />
+
+ <label for="txt:redirect-uri">Redirect URI</label>
+ <input name="redirect_uri" type="text" id="txt:redirect-uri"
+ required="required" />
+ <br /><br />
+
+ <label for="txt:other-redirect-uris">
+ Other redirect URIs (Enter one URI per line)</label>
+ <br />
+ <textarea name="other_redirect_uris" id="txt:other-redirect-uris"
+ cols="80" rows="10"
+ title="Enter one URI per line."></textarea>
+ <br /><br />
+ <fieldset>
+ <legend>Supported grant types</legend>
+ <input name="grants[]"
+ type="checkbox"
+ value="authorization_code"
+ id="chk:authorization-code"
+ checked="checked" />
+ <label for="chk:authorization-code">Authorization Code</label>
+ <br /><br />
+
+ <input name="grants[]"
+ type="checkbox"
+ value="refresh_token"
+ id="chk:refresh-token" />
+ <label for="chk:refresh-token">Refresh Token</label>
+ </fieldset>
+ </fieldset>
+
+ <fieldset>
+ <legend>User information</legend>
+
+ <p>The user to register this client for</p>
+ <select name="user" required="required">
+ {%for user in users%}
+ <option value="{{user.user_id}}"
+ {%if user.user_id==current_user.user_id%}
+ selected="selected"
+ {%endif%}>{{user.name}} ({{user.email}})</option>
+ {%endfor%}
+ </select>
+ </fieldset>
+
+ <input type="submit" value="register client" />
+</form>
+{%endblock%}
diff --git a/gn_auth/templates/admin/registered-client.html b/gn_auth/templates/admin/registered-client.html
new file mode 100644
index 0000000..5c46f4d
--- /dev/null
+++ b/gn_auth/templates/admin/registered-client.html
@@ -0,0 +1,21 @@
+{%extends "base.html"%}
+
+{%block title%}Genenetwork3: Register OAuth2 Client{%endblock%}
+
+{%block pagetitle%}Register OAuth2 Client{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<p>Client has been registered successfully.</p>
+
+<p>Please save the following client details somewhere. There is no way to
+ retrieve the the <strong>CLIENT_SECRET</strong> once you leave this page.</p>
+
+<dl>
+ <dt>CLIENT_ID</dt>
+ <dd>{{client.client_id}}</dd>
+ <dt>CLIENT_SECRET</dt>
+ <dd>{{client_secret}}</dd>
+</dl>
+{%endblock%}
diff --git a/gn_auth/templates/admin/view-oauth2-client.html b/gn_auth/templates/admin/view-oauth2-client.html
new file mode 100644
index 0000000..b90428d
--- /dev/null
+++ b/gn_auth/templates/admin/view-oauth2-client.html
@@ -0,0 +1,75 @@
+{%extends "base.html"%}
+
+{%block title%}Genenetwork3: View OAuth2 Client{%endblock%}
+
+{%block pagetitle%}View OAuth2 Client{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+{%if client.is_nothing()%}
+<p>No such client</p>
+{%else%}
+{%set client = client.value%}
+<form method="POST" action="{{url_for('oauth2.admin.edit_client')}}">
+ <legend>View/Edit Oauth2 Client</legend>
+ <input type="hidden" name="client_id" value="{{client.client_id}}" />
+ <div>
+ <p><strong>Client ID: <strong> {{client.client_id}}</p>
+ <p><strong>Client Name: <strong> {{client.client_metadata.client_name}}</p>
+ </div>
+ <fieldset>
+ <legend>Scope</legend>
+ {%for scp in scope%}
+ <input name="scope[]" id="chk:{{scp}}" type="checkbox" value="{{scp}}"
+ {%if scp in client.client_metadata.scope%}
+ checked="checked"
+ {%endif%} />
+ <label for="chk:{{scp}}">{{scp}}</label><br />
+ {%endfor%}
+ </fieldset>
+
+ <fieldset>
+ <legend>Redirect URIs</legend>
+ <label for="txt:default-redirect-uri">Default Redirect URI</label>
+ <br />
+ <input type="text" name="default_redirect_uri" id="txt:default-redirect-uri"
+ value="{{client.client_metadata.default_redirect_uri}}"
+ required="required">
+ <br /><br />
+
+ <label for="txta:other-redirect-uris">Other Redirect URIs</label>
+ <br />
+ <textarea id="txta:other-redirect-uris"
+ name="other_redirect_uris"
+ cols="80" rows="10"
+ title="Enter one URI per line."
+ >{{"\r\n".join(client.client_metadata.redirect_uris)}}</textarea>
+ </fieldset>
+
+ <fieldset>
+ <legend>Grants</legend>
+ <input name="grants[]"
+ type="checkbox"
+ value="authorization_code"
+ id="chk:authorization-code"
+ {%if "authorization_code" in client.client_metadata.grant_types%}
+ checked="checked"
+ {%endif%} />
+ <label for="chk:authorization-code">Authorization Code</label>
+ <br /><br />
+
+ <input name="grants[]"
+ type="checkbox"
+ value="refresh_token"
+ id="chk:refresh-token"
+ {%if "refresh_token" in client.client_metadata.grant_types%}
+ checked="checked"
+ {%endif%} />
+ <label for="chk:refresh-token">Refresh Token</label>
+ </fieldset>
+
+ <input type="submit" value="update client" />
+</form>
+{%endif%}
+{%endblock%}
diff --git a/gn_auth/templates/base.html b/gn_auth/templates/base.html
new file mode 100644
index 0000000..db08545
--- /dev/null
+++ b/gn_auth/templates/base.html
@@ -0,0 +1,24 @@
+{% from "common-macros.html" import flash_messages%}
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+ <title>Genenetwork 3: {%block title%}{%endblock%}</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="https://genenetwork.org/static/new/css/bootstrap-custom.css" />
+ <link rel="stylesheet" type="text/css" href="/static/css/styles.css" />
+ {%block css%}{%endblock%}
+ </head>
+
+ <body>
+ <h1>Genenetwork3: {%block pagetitle%}{%endblock%}</h1>
+
+ <div class="container">
+ {%block content%}{%endblock%}
+ </div>
+ {%block js%}{%endblock%}
+ <body>
+</html>
diff --git a/gn_auth/templates/common-macros.html b/gn_auth/templates/common-macros.html
new file mode 100644
index 0000000..1d9f302
--- /dev/null
+++ b/gn_auth/templates/common-macros.html
@@ -0,0 +1,7 @@
+{%macro flash_messages()%}
+<div class="alert-messages">
+ {%for category,message in get_flashed_messages(with_categories=true)%}
+ <div class="alert {{category}}" role="alert">{{message}}</div>
+ {%endfor%}
+</div>
+{%endmacro%}
diff --git a/gn_auth/templates/oauth2/authorise-user.html b/gn_auth/templates/oauth2/authorise-user.html
new file mode 100644
index 0000000..b9284e5
--- /dev/null
+++ b/gn_auth/templates/oauth2/authorise-user.html
@@ -0,0 +1,48 @@
+{%extends "base.html"%}
+
+{%block title%}Authorise User{%endblock%}
+
+{%block pagetitle%}Authenticate to the API Server{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<form method="POST" action="{{url_for('oauth2.auth.authorise')}}">
+ <input type="hidden" name="response_type" value="{{response_type}}" />
+ <input type="hidden" name="scope" value="{{scope | join(' ')}}" />
+ <input type="hidden" name="client_id" value="{{client.client_id}}" />
+ <p>
+ You are authorising "{{client.client_metadata.client_name}}" to access
+ Genenetwork 3 with the following scope:
+ </p>
+ <fieldset>
+ <legend>Scope</legend>
+ {%for scp in scope%}
+ <div class="checkbox disabled">
+ <label for="scope:{{scp}}">
+ <input id="scope:{{scp}}" type="checkbox" name="scope[]" value="{{scp}}"
+ checked="checked" disabled="disabled" />
+ {{scp}}
+ </label>
+ </div>
+ {%endfor%}
+ </fieldset>
+
+ <fieldset>
+ <legend>User Credentials</legend>
+ <fieldset class="form-group">
+ <label for="user:email" class="form-label">Email</label>
+ <input type="email" name="user:email" id="user:email" required="required"
+ class="form-control"/>
+ </fieldset>
+
+ <fieldset class="form-group">
+ <label for="user:password" class="form-label">Password</label>
+ <input type="password" name="user:password" id="user:password"
+ required="required" class="form-control" />
+ </fieldset>
+ </fieldset>
+
+ <input type="submit" value="authorise" class="btn btn-primary" />
+</form>
+{%endblock%}
diff --git a/gn_auth/templates/oauth2/oauth2_error.html b/gn_auth/templates/oauth2/oauth2_error.html
new file mode 100644
index 0000000..ec9a500
--- /dev/null
+++ b/gn_auth/templates/oauth2/oauth2_error.html
@@ -0,0 +1,16 @@
+{%extends "base.html"%}
+
+{%block title%}OAuth2 Error{%endblock%}
+
+{%block pagetitle%}Error: {{error.status_code}}{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+There was an error trying to fulfill your request:
+
+<p>
+ <strong>{{error.error}}</strong>:
+ {{error.description}}
+</p>
+{%endblock%}