aboutsummaryrefslogtreecommitdiff
path: root/gn_auth
diff options
context:
space:
mode:
Diffstat (limited to 'gn_auth')
-rw-r--r--gn_auth/__init__.py65
-rw-r--r--gn_auth/auth/authentication/users.py4
-rw-r--r--gn_auth/auth/authorisation/checks.py2
-rw-r--r--gn_auth/auth/authorisation/privileges.py4
-rw-r--r--gn_auth/migrations.py33
-rw-r--r--gn_auth/settings.py18
6 files changed, 121 insertions, 5 deletions
diff --git a/gn_auth/__init__.py b/gn_auth/__init__.py
index e69de29..4ef2892 100644
--- a/gn_auth/__init__.py
+++ b/gn_auth/__init__.py
@@ -0,0 +1,65 @@
+import os
+import sys
+import logging
+
+from flask import Flask
+
+from . import settings
+
+class ConfigurationError(Exception):
+ """Raised in case of a configuration error."""
+
+def __check_secret_key__(app: Flask) -> None:
+ """Verify secret key is not empty."""
+ if app.config.get("SECRET_KEY", "") == "":
+ raise ConfigurationError("The `SECRET_KEY` settings cannot be empty.")
+
+def check_mandatory_settings(app: Flask) -> None:
+ """Verify that mandatory settings are defined in the application"""
+ undefined = tuple(
+ setting for setting in (
+ "SECRET_KEY", "SQL_URI", "AUTH_DB", "AUTH_MIGRATIONS",
+ "OAUTH2_SCOPE")
+ if setting not in app.config)
+ if len(undefined) > 0:
+ raise ConfigurationError(
+ "You must provide values for the following settings: " +
+ "\t* " + "\n\t* ".join(undefined))
+
+ __check_secret_key__(app)
+
+def override_settings_with_envvars(
+ app: Flask, ignore: tuple[str, ...]=tuple()) -> None:
+ """Override settings in `app` with those in ENVVARS"""
+ for setting in (key for key in app.config if key not in ignore):
+ app.config[setting] = os.environ.get(setting) or app.config[setting]
+
+def setup_logging_handlers(app: Flask) -> None:
+ """Setup the loggging handlers."""
+ stderr_handler = logging.StreamHandler(stream=sys.stderr)
+ app.logger.addHandler(stderr_handler)
+
+ root_logger = logging.getLogger()
+ root_logger.addHandler(stderr_handler)
+ root_logger.setLevel(app.config["LOGLEVEL"])
+
+def create_app(config: dict = {}) -> Flask:
+ """Create and return a new flask application."""
+ app = Flask(__name__)
+
+ # ====== Setup configuration ======
+ app.config.from_object(settings) # Default settings
+ # Override defaults with startup settings
+ app.config.update(config)
+ # Override app settings with site-local settings
+ if "GN_AUTH_CONF" in os.environ:
+ app.config.from_envvar("GN_AUTH_CONF")
+
+ override_settings_with_envvars(app)
+ # ====== END: Setup configuration ======
+
+ check_mandatory_settings(app)
+
+ setup_logging_handlers(app)
+
+ return app
diff --git a/gn_auth/auth/authentication/users.py b/gn_auth/auth/authentication/users.py
index 0e72ed2..327820e 100644
--- a/gn_auth/auth/authentication/users.py
+++ b/gn_auth/auth/authentication/users.py
@@ -5,8 +5,8 @@ from typing import Any, Tuple, NamedTuple
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
-from gn3.auth import db
-from gn3.auth.authorisation.errors import NotFoundError
+from gn_auth.auth import db
+from gn_auth.auth.authorisation.errors import NotFoundError
class User(NamedTuple):
"""Class representing a user."""
diff --git a/gn_auth/auth/authorisation/checks.py b/gn_auth/auth/authorisation/checks.py
index 1c87c02..02c6810 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 gn3.auth import db
+from gn_auth.auth import db
from . import privileges as auth_privs
from .errors import InvalidData, AuthorisationError
diff --git a/gn_auth/auth/authorisation/privileges.py b/gn_auth/auth/authorisation/privileges.py
index dbb4129..1b0f06c 100644
--- a/gn_auth/auth/authorisation/privileges.py
+++ b/gn_auth/auth/authorisation/privileges.py
@@ -1,8 +1,8 @@
"""Handle privileges"""
from typing import Any, Iterable, NamedTuple
-from gn3.auth import db
-from gn3.auth.authentication.users import User
+from gn_auth.auth import db
+from gn_auth.auth.authentication.users import User
class Privilege(NamedTuple):
"""Class representing a privilege: creates immutable objects."""
diff --git a/gn_auth/migrations.py b/gn_auth/migrations.py
new file mode 100644
index 0000000..3451e07
--- /dev/null
+++ b/gn_auth/migrations.py
@@ -0,0 +1,33 @@
+"""Run the migrations in the app, rather than with yoyo CLI."""
+from pathlib import Path
+from typing import Union
+
+from yoyo import read_migrations
+from yoyo.backends import DatabaseBackend
+from yoyo.migrations import Migration, MigrationList
+
+class MigrationNotFound(Exception):
+ """Raised if a migration is not found at the given path."""
+ def __init__(self, migration_path: Path):
+ """Initialise the exception."""
+ super().__init__(f"Could not find migration '{migration_path}'")
+
+def apply_migrations(backend: DatabaseBackend, migrations: MigrationList):
+ "Apply the provided migrations."
+ with backend.lock():
+ backend.apply_migrations(backend.to_apply(migrations))
+
+def rollback_migrations(backend: DatabaseBackend, migrations: MigrationList):
+ "Rollback the provided migrations."
+ with backend.lock():
+ backend.rollback_migrations(backend.to_rollback(migrations))
+
+def get_migration(migration_path: Union[Path, str]) -> Migration:
+ """Retrieve a migration at thi given `migration_path`."""
+ migration_path = Path(migration_path)
+ if migration_path.exists():
+ for migration in read_migrations(str(migration_path.parent)):
+ if Path(migration.path) == migration_path:
+ return migration
+
+ raise MigrationNotFound(migration_path)
diff --git a/gn_auth/settings.py b/gn_auth/settings.py
new file mode 100644
index 0000000..71ffd5d
--- /dev/null
+++ b/gn_auth/settings.py
@@ -0,0 +1,18 @@
+"""Default application settings."""
+import os
+
+# LOGLEVEL
+LOGLEVEL = "WARNING"
+
+# Flask settings
+SECRET_KEY = ""
+
+# Database settings
+SQL_URI = "mysql://webqtlout:webqtlout@localhost/db_webqtl"
+AUTH_DB = f"{os.environ.get('HOME')}/genenetwork/gn3_files/db/auth.db"
+AUTH_MIGRATIONS = "migrations/auth"
+
+# OAuth2 settings
+OAUTH2_SCOPE = (
+ "profile", "group", "role", "resource", "user", "masquerade",
+ "introspect")