about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-08-07 07:58:09 +0300
committerFrederick Muriuki Muriithi2023-08-07 09:26:12 +0300
commit6d9c61dc0072b96b12153e64940b465306f25bfb (patch)
treea4957c7967e56b4527c1e926744a65a3326935ce
parent6ab6d46ab4b1611ed72bdbce85cf9324ce69b305 (diff)
downloadgn-auth-6d9c61dc0072b96b12153e64940b465306f25bfb.tar.gz
Change imports to new unified db module.
-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
-rw-r--r--main.py2
-rw-r--r--tests/unit/auth/fixtures/group_fixtures.py2
-rw-r--r--tests/unit/auth/fixtures/migration_fixtures.py2
-rw-r--r--tests/unit/auth/fixtures/oauth2_client_fixtures.py2
-rw-r--r--tests/unit/auth/fixtures/resource_fixtures.py2
-rw-r--r--tests/unit/auth/fixtures/role_fixtures.py2
-rw-r--r--tests/unit/auth/fixtures/user_fixtures.py2
-rw-r--r--tests/unit/auth/test_credentials.py2
-rw-r--r--tests/unit/auth/test_groups.py2
-rw-r--r--tests/unit/auth/test_migrations_add_data_to_table.py2
-rw-r--r--tests/unit/auth/test_migrations_add_remove_columns.py2
-rw-r--r--tests/unit/auth/test_migrations_create_tables.py2
-rw-r--r--tests/unit/auth/test_migrations_drop_tables.py2
-rw-r--r--tests/unit/auth/test_migrations_indexes.py2
-rw-r--r--tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py2
-rw-r--r--tests/unit/auth/test_privileges.py2
-rw-r--r--tests/unit/auth/test_resources.py2
-rw-r--r--tests/unit/auth/test_roles.py2
-rw-r--r--tests/unit/auth/test_token.py2
61 files changed, 455 insertions, 74 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%}
diff --git a/main.py b/main.py
index 4a4850c..7f3b256 100644
--- a/main.py
+++ b/main.py
@@ -13,7 +13,7 @@ from yoyo import get_backend, read_migrations
 from gn_auth import migrations
 from gn_auth import create_app
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authentication.users import hash_password
 
 from scripts import register_sys_admin as rsysadm# type: ignore[import]
diff --git a/tests/unit/auth/fixtures/group_fixtures.py b/tests/unit/auth/fixtures/group_fixtures.py
index b2bae24..c7775d9 100644
--- a/tests/unit/auth/fixtures/group_fixtures.py
+++ b/tests/unit/auth/fixtures/group_fixtures.py
@@ -3,7 +3,7 @@ import uuid
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authorisation.groups import Group, GroupRole
 from gn_auth.auth.authorisation.resources import Resource, ResourceCategory
 
diff --git a/tests/unit/auth/fixtures/migration_fixtures.py b/tests/unit/auth/fixtures/migration_fixtures.py
index 2a67c01..16a31cd 100644
--- a/tests/unit/auth/fixtures/migration_fixtures.py
+++ b/tests/unit/auth/fixtures/migration_fixtures.py
@@ -4,7 +4,7 @@ from yoyo.backends import DatabaseBackend
 from yoyo import get_backend, read_migrations
 from yoyo.migrations import Migration, MigrationList
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import apply_migrations, rollback_migrations
 
 @pytest.fixture(scope="session")
diff --git a/tests/unit/auth/fixtures/oauth2_client_fixtures.py b/tests/unit/auth/fixtures/oauth2_client_fixtures.py
index 70cd2c2..263fc94 100644
--- a/tests/unit/auth/fixtures/oauth2_client_fixtures.py
+++ b/tests/unit/auth/fixtures/oauth2_client_fixtures.py
@@ -5,7 +5,7 @@ import datetime
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authentication.users import hash_password
 from gn_auth.auth.authentication.oauth2.models.oauth2client import OAuth2Client
 
diff --git a/tests/unit/auth/fixtures/resource_fixtures.py b/tests/unit/auth/fixtures/resource_fixtures.py
index efe617e..af22733 100644
--- a/tests/unit/auth/fixtures/resource_fixtures.py
+++ b/tests/unit/auth/fixtures/resource_fixtures.py
@@ -1,7 +1,7 @@
 """Fixtures and utilities for resource-related tests"""
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 
 from .group_fixtures import TEST_RESOURCES
 
diff --git a/tests/unit/auth/fixtures/role_fixtures.py b/tests/unit/auth/fixtures/role_fixtures.py
index c97716f..ddcbba5 100644
--- a/tests/unit/auth/fixtures/role_fixtures.py
+++ b/tests/unit/auth/fixtures/role_fixtures.py
@@ -3,7 +3,7 @@ import uuid
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authorisation.roles import Role
 from gn_auth.auth.authorisation.privileges import Privilege
 
diff --git a/tests/unit/auth/fixtures/user_fixtures.py b/tests/unit/auth/fixtures/user_fixtures.py
index 21708a5..531a321 100644
--- a/tests/unit/auth/fixtures/user_fixtures.py
+++ b/tests/unit/auth/fixtures/user_fixtures.py
@@ -3,7 +3,7 @@ import uuid
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authentication.users import User, hash_password
 
 TEST_USERS = (
diff --git a/tests/unit/auth/test_credentials.py b/tests/unit/auth/test_credentials.py
index 8011ec2..1953228 100644
--- a/tests/unit/auth/test_credentials.py
+++ b/tests/unit/auth/test_credentials.py
@@ -3,7 +3,7 @@ import pytest
 from yoyo.migrations import MigrationList
 from hypothesis import given, settings, strategies, HealthCheck
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authentication import credentials_in_database
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 
diff --git a/tests/unit/auth/test_groups.py b/tests/unit/auth/test_groups.py
index 8a47cf8..af33f85 100644
--- a/tests/unit/auth/test_groups.py
+++ b/tests/unit/auth/test_groups.py
@@ -4,7 +4,7 @@ from uuid import UUID
 import pytest
 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.users import User
 from gn_auth.auth.authorisation.roles import Role
 from gn_auth.auth.authorisation.privileges import Privilege
diff --git a/tests/unit/auth/test_migrations_add_data_to_table.py b/tests/unit/auth/test_migrations_add_data_to_table.py
index fa46cc9..b686f25 100644
--- a/tests/unit/auth/test_migrations_add_data_to_table.py
+++ b/tests/unit/auth/test_migrations_add_data_to_table.py
@@ -1,7 +1,7 @@
 """Test data insertion when migrations are run."""
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 from tests.unit.auth.conftest import (
     apply_single_migration, rollback_single_migration, migrations_up_to)
diff --git a/tests/unit/auth/test_migrations_add_remove_columns.py b/tests/unit/auth/test_migrations_add_remove_columns.py
index a44563a..af85652 100644
--- a/tests/unit/auth/test_migrations_add_remove_columns.py
+++ b/tests/unit/auth/test_migrations_add_remove_columns.py
@@ -1,7 +1,7 @@
 """Test migrations that alter tables adding/removing columns."""
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 from tests.unit.auth.conftest import (
     apply_single_migration, rollback_single_migration, migrations_up_to)
diff --git a/tests/unit/auth/test_migrations_create_tables.py b/tests/unit/auth/test_migrations_create_tables.py
index f56489a..bd0164e 100644
--- a/tests/unit/auth/test_migrations_create_tables.py
+++ b/tests/unit/auth/test_migrations_create_tables.py
@@ -1,7 +1,7 @@
 """Test migrations that create tables"""
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 from tests.unit.auth.conftest import (
     apply_single_migration, rollback_single_migration, migrations_up_to)
diff --git a/tests/unit/auth/test_migrations_drop_tables.py b/tests/unit/auth/test_migrations_drop_tables.py
index 4ce0d45..6fa6f2f 100644
--- a/tests/unit/auth/test_migrations_drop_tables.py
+++ b/tests/unit/auth/test_migrations_drop_tables.py
@@ -2,7 +2,7 @@
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 from tests.unit.auth.conftest import (
     apply_single_migration, rollback_single_migration, migrations_up_to)
diff --git a/tests/unit/auth/test_migrations_indexes.py b/tests/unit/auth/test_migrations_indexes.py
index dcec7c4..1c543c4 100644
--- a/tests/unit/auth/test_migrations_indexes.py
+++ b/tests/unit/auth/test_migrations_indexes.py
@@ -1,7 +1,7 @@
 """Test that indexes are created and removed."""
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 from tests.unit.auth.conftest import (
     apply_single_migration, rollback_single_migration, migrations_up_to)
diff --git a/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py b/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py
index 8a92ae1..c34a549 100644
--- a/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py
+++ b/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py
@@ -3,7 +3,7 @@ Test that the `resource_categories` table is initialised with the startup data.
 """
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.migrations import get_migration, apply_migrations, rollback_migrations
 from tests.unit.auth.conftest import (
     apply_single_migration, rollback_single_migration, migrations_up_to)
diff --git a/tests/unit/auth/test_privileges.py b/tests/unit/auth/test_privileges.py
index a40e875..b37a1a7 100644
--- a/tests/unit/auth/test_privileges.py
+++ b/tests/unit/auth/test_privileges.py
@@ -1,7 +1,7 @@
 """Test the privileges module"""
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authorisation.privileges import Privilege, user_privileges
 
 from tests.unit.auth import conftest
diff --git a/tests/unit/auth/test_resources.py b/tests/unit/auth/test_resources.py
index 12b8d95..9d2671c 100644
--- a/tests/unit/auth/test_resources.py
+++ b/tests/unit/auth/test_resources.py
@@ -3,7 +3,7 @@ import uuid
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 
 from gn_auth.auth.authorisation.groups import Group
 from gn_auth.auth.authorisation.errors import AuthorisationError
diff --git a/tests/unit/auth/test_roles.py b/tests/unit/auth/test_roles.py
index 31d5e8e..67654d8 100644
--- a/tests/unit/auth/test_roles.py
+++ b/tests/unit/auth/test_roles.py
@@ -3,7 +3,7 @@ import uuid
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 from gn_auth.auth.authorisation.privileges import Privilege
 from gn_auth.auth.authorisation.errors import AuthorisationError
 from gn_auth.auth.authorisation.roles.models import Role, user_roles, create_role
diff --git a/tests/unit/auth/test_token.py b/tests/unit/auth/test_token.py
index 4a864fe..f66954f 100644
--- a/tests/unit/auth/test_token.py
+++ b/tests/unit/auth/test_token.py
@@ -2,7 +2,7 @@
 
 import pytest
 
-from gn_auth.auth import db
+from gn_auth.auth.db import sqlite3 as db
 
 SUCCESS_RESULT = {
     "status_code": 200,