API
Default Settings
Define deault settings for all extensions.
Also define a function to load all settings as usual.
- flask_tern.settings.SECRET_KEY = b'i\xfb\xea\xb1\x9be\xd0\xe1\xb3W&\xe7:4m\xe6'
value to use to sign / encrypt session cookie. If unset, the app will auto generate a value. This value needs to be configured if load balancing across multiple processes is used.
- flask_tern.settings.SESSION_TYPE = None
Session backend type. The default is to use cookies as session storage.
- flask_tern.settings.SESSION_FILE_DIR = '/Users/uqgweis/Documents/Projects/workspaces/bioimages/flask_tern/docs/flask_session'
If SESSION_TYPE = “filesystem”, this option specifies the storage location for session data. as default it uses the flask_session folder within current working directory.
- flask_tern.settings.SESSION_FILE_THRESHOLD = 500
If SESSION_TYPE = “filesystem”, this option sets the maximum number of items the session stores before it starts deleting some. default is 500.
- flask_tern.settings.SESSION_FILE_MODE = 384
If ‘SESSION_TYPE = “filesystem”` this option sets the file mode for the session files. as default it uses 0o600. If given as string it will be parsed as integer or if prefixed with 0o as octal number.
- flask_tern.settings.SESSION_REDIS = None
If SESSION_TYPE = “redis”, this option should be a configured
redis.Redisinstance. If loaded from environment variables, it can be specified as connection url as described inredis:redis.from_url()
- flask_tern.settings.SQLALCHEMY_DATABASE_URI = None
It is required to configure this setting if SQLAlchemy is being used.
- flask_tern.settings.SQLALCHEMY_TRACK_MODIFICATIONS = False
avoid SQLAlchemy warning
- flask_tern.settings.SQLALCHEMY_ENGINE_OPTIONS = {'pool_pre_ping': True, 'pool_recycle': 300, 'pool_size': 5}
default SQLAlchemy connection pool configuration (can only be overriden with a settings file)
- flask_tern.settings.DOI_URL = None
deault DOI URL
- flask_tern.settings.CACHE_TYPE = 'flask_caching.backends.nullcache.NullCache'
default CACHE_TYPE used for Flask-Caching extension.
Configuration
- flask_tern.utils.config.load_settings(app: Flask, env_prefix: str = 'FLASK_TERN_', defaults: dict = None, config: dict | ModuleType | object = None)
Function intended to be used to configure a Flask app instance.
- Parameters:
Options are applied in the following order to the app instance. Later steps override values form previous steps. This method loads settings via
flask.Config.from_object().apply defaults from bundled etensions
apply defaults in
flask_tern.settingspassed in defaults
call
convert_settngs()apply passed in config
Usage
app = Flask(__name__) load_settings(app, "MY_APP_", {}, {})
- flask_tern.utils.config.as_bool(value: str | bool) bool
Convert value to bool.
Accepts
True,1andonas True and everything else as false. It uses case insensitive comparison.
- flask_tern.utils.config.add_setting_parser(name: str, parser: Callable[[str], any])
Adds a parser from environmante variable to settings data type.
- Parameters:
This allows applications to easily add custom parsers for extra options. The provided parser function could do anything. E.g. parse the value as json, interpret it as filename, etc…
The parser method will be caled with the value of the environment variable (may be empty, but should never be
None). The return value can be anything which will eventually be stored in Flask application configuration (Configuration Handling) under setting name.Usage
# my_app.settings.py from flask_tern.utils.config import add_setting_parser # set default for MAX_COUNT MAX_COUNT = 2 # add parser for this which converts ``str`` to ``int`` add_setting_parser("MAX_COUNT", int)
- flask_tern.utils.config.convert_settings(app: ~flask.app.Flask, type_map: dict = {'ELASTICSEARCH_SSL_SHOW_WARN': <function as_bool>, 'ELASTICSEARCH_VERIFY_CERTS': <function as_bool>, 'OIDC_USE_REFRESH_TOKEN': <function as_bool>, 'SESSION_COOKIE_HTTPONLY': <function as_bool>, 'SESSION_COOKIE_SECURE': <function as_bool>, 'SESSION_FILE_MODE': <function <lambda>>, 'SESSION_FILE_THRESHOLD': <class 'int'>, 'SESSION_PERMANENT': <function as_bool>, 'SESSION_REDIS': <function from_url>, 'SQLALCHEMY_TRACK_MODIFICATIONS': <function as_bool>})
Goes through registered value converters and tries to parse into python type.
Extensions:
- flask_tern.init_app(app: Flask)
A helper method that initalises all included extensions based on config options.
- Parameters:
app (Flask) – The Flask app to configure
Logging: always enabled
SQLAlchemy: enabled if
SQLALCHEMY_DATABASE_URIis setElasticsearch: enabled if
ELASTICSEARCH_URLis setSession: enabled if
SESSION_TYPEis setCache: always enabled
CORS: always enabled
ProxyFix: always enabled
HealthCheck: adds builtin check methods if the respective configuration is set
SQLALCHEMY_DATABASE_URI: addhealthcheck.check.check_db()ELASTICSEARCH_URL: addhealthcheck.checks.check_es()APIKEY_SERVICE: addhealtcheck.checks.check_apikey()OIDC_DISCOVERY_URL: addhealthcheck.checks.check_keycloak`()DOI_URL: addhealthcheck.checks.check_doi`()
OIDC Auth: enabled if
ODIC_CLIENT_IDis set.
SQLAlchemy
- flask_tern.db.DeclarativeBase
We define our own declarative base class. Using this base class makes it possible to re-use db models outside of flask context
- flask_tern.db.db = <SQLAlchemy>
global
flask_sqlalchemy.SQLAlchemyobject.
- flask_tern.db.init_app(app)
Initialise Flask-SQLAlchemy extension.
Elasticsearch
- flask_tern.elasticsearch.init_app(app: Flask)
Configure elasticsearch_dsl connections pool along a Flask application.
Main purpose is to have conistent place on how to set up extension and third party libraries along a Flask application, and also to unify configuration loading.
This method just uses app.config to create a default elasticsearch_dsl connection.
- Parameters:
app (Flask) – Flask app to configure.
Usage
from flask_tern import elasticsearch def create_app(): app = Flask(__name__) elasticsearch.init_app(app) return app
- flask_tern.elasticsearch.settings.ELASTICSEARCH_URL = None
Elasticsearch URL. This url should include credentials and index name if required.
- flask_tern.elasticsearch.settings.ELASTICSEARCH_VERIFY_CERTS = True
Enable / disable certificate validation in case fol https connections to Elasticsearch.
- flask_tern.elasticsearch.settings.ELASTICSEARCH_SSL_SHOW_WARN = True
Enable / disable untrusted SSL certificate varnings in case certificate verification das been disabled.
Caching
- flask_tern.cache.cache = <flask_caching.Cache object>
global
flaskcache:flask_caching.Cacheinstance
- flask_tern.cache.init_app(app)
Configure Flask-Caching extension.
Authentication
- flask_tern.auth.init_app(app)
This method is used to configure authentication via OIDC.
- Parameters:
app – Flask app to configure
It configures authlib Flask integration to verify tokens against the provided OIDC provider.
Authlib’s Flask integration relies on session storage. Please make sure to use a secure session implementation to avoid any issues.
Typical usage would be:
from flask_tern import auth def create_app(): app = Flask(__name__) auth.init_app(app) return app
- flask_tern.auth.require_login(view_func: Callable) Callable[[], Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], Headers | Mapping[str, str | List[str] | Tuple[str, ...]] | Sequence[Tuple[str, str | List[str] | Tuple[str, ...]]]] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int, Headers | Mapping[str, str | List[str] | Tuple[str, ...]] | Sequence[Tuple[str, str | List[str] | Tuple[str, ...]]]] | WSGIApplication]
Redirect to login page if not logged in.
If
flask_tern.auth.current_useris not set, then it will generate a redirect to a named endpointoidc_login.login(as defined in blueprint flask_tern.auth.login.oidc_login). The current url is passed as url parameter return_url to the endpoint. After successful login, the user can be redirected back to the original url. If the user is not viewing from the web browser, we can return a 403 response.This wrapper can only be used with simple GET requests, as passing a post body on redirect may not work:
@require_login def view(): return "ok"
# noqa: DAR101, DAR201
- Parameters:
view_func (Callable)
- Return type:
Callable[[], Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], Headers | Mapping[str, str | List[str] | Tuple[str, …]] | Sequence[Tuple[str, str | List[str] | Tuple[str, …]]]] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int, Headers | Mapping[str, str | List[str] | Tuple[str, …]] | Sequence[Tuple[str, str | List[str] | Tuple[str, …]]]] | WSGIApplication]
- flask_tern.auth.require_roles(roles: Iterable[str]) Callable[[Callable], Callable]
Require an authenticated user with a specific set of roles.
This wrapper ensures that there is an authenticated user, and all roles set in this decorator are present on the current users role attribute:
@require_roles(["admin", "writer"]) def view(): return "ok"
# noqa: DAR101, DAR201
- flask_tern.auth.require_user(view_func: Callable) Callable[[], Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], Headers | Mapping[str, str | List[str] | Tuple[str, ...]] | Sequence[Tuple[str, str | List[str] | Tuple[str, ...]]]] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int, Headers | Mapping[str, str | List[str] | Tuple[str, ...]] | Sequence[Tuple[str, str | List[str] | Tuple[str, ...]]]] | WSGIApplication]
View decorator which enforces an authenticated user.
If
flask_tern.auth.current_useris not set, then callflask.abort(), which raises a 403 HTTP Exception. If we have a valid user, then execute the wrapped view method:@require_user def view(): return "ok"
# noqa: DAR201, DAR101
- Parameters:
view_func (Callable)
- Return type:
Callable[[], Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], Headers | Mapping[str, str | List[str] | Tuple[str, …]] | Sequence[Tuple[str, str | List[str] | Tuple[str, …]]]] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int] | Tuple[Response | str | bytes | List[Any] | Mapping[str, Any] | Iterator[str] | Iterator[bytes], int, Headers | Mapping[str, str | List[str] | Tuple[str, …]] | Sequence[Tuple[str, str | List[str] | Tuple[str, …]]]] | WSGIApplication]
- class flask_tern.auth.user.User(id: str, name: str, email: str | None = None, email_verified: bool = False, roles: List[str] | None = None, scopes: List[str] | None = None, claims: Dict[str, Any] | None = None)
A class representing an authenticated user.
id and name are required attributes.
- Parameters:
- get(key, default=None)
Backwards compatible methods to allow dict based access to attributes.
Supports dict methed like .get(key, default)
- Parameters:
key – attribute to return
default – default value if not attribute is not available
- Returns:
Value of attribute
- flask_tern.auth.apikey.get_userinfo_from_apikey(auth_info: dict) User
Method used by APP to verify ApiKey against api key service.
- Parameters:
auth_info (dict) – dictionary with details about credentials provided in request
- Returns:
User object filled with details about user
- Return type:
ApiKey service returns user info for given api key. This auth method passes the given apikey on to apikey service for validation.
- flask_tern.auth.login.oidc_login = <Blueprint 'oidc_login'>
A blueprint providing views to handle browser session login
- flask_tern.auth.login.get_userinfo_from_session(auth_info)
Return User object based on authinfo.
- Parameters:
auth_info – dict, A dictionary as returned by
flask_tern.auth.utils.get_authorization(). With keys type: bearer and token: the encoded access token.- Returns:
‘User’ object with attributes set from auth_info
- flask_tern.auth.login.login()
Generate redirect url for oauth login.
accepts url parameter return_url or uses Referrer header to remember return_url
If there is no return_url set, it use the url for a route named
root, which needs to be configured on the Flask app.- Returns:
Redirect to authorize url.
- flask_tern.auth.login.authorize()
Parse and validate authorization response.
On success return redirect user back to original URL and set session cookie
If there is no return_url set, it use the url for a route named
root, which needs to be configured on the Flask app.- Returns:
Redirect to stored return url, or application root route.
- flask_tern.auth.login.logout()
Clear logged in session.
If there is no return_url set, it use the url for a route named
root, which needs to be configured on the Flask app.- Returns:
Redirect to return url.
- flask_tern.auth.oidc.get_userinfo_from_accesstoken(auth_info) User
Extract user information and roles from access token.
- Parameters:
auth_info – dict, A dictionary as returned by
flask_tern.auth.utils.get_authorization(). With keys type: bearer and token: the encoded access token.- Returns:
‘User’ object with attributes set from auth_info
- Return type:
- flask_tern.auth.utils.get_authorization() dict | None
Find and extract authentication information from current request.
This method looks for an Autharization header or a valid session. It returns either None or a dictionary with keys type describing the authentication method and additional keys depending on the value of ‘type’.
- Returns:
Dictionary containinf information about credentials provided.
- Return type:
dict | None
- flask_tern.auth.settings.USERINFO_MAP = {'apikey-v1': <function get_userinfo_from_apikey>, 'bearer': <function get_userinfo_from_accesstoken>, 'session': <function get_userinfo_from_session>}
A set of methods to extract user information based on authentication method. E.g. bearer looks at HTTP Authorization header with bearer tokens, and session uses sessions and cookies to store curretn login status. Other options can easily be implemented. See Authentication for details
- flask_tern.auth.settings.OIDC_DISCOVERY_URL = None
The URL which serves an OIDC discovery document. This usually ends with /.well-known/openid-configuration.
- flask_tern.auth.settings.OIDC_CLIENT_ID = None
OIDC client id
- flask_tern.auth.settings.OIDC_CLIENT_SECRET = None
OIDC client secret
- flask_tern.auth.settings.OIDC_SCOPE = 'openid profile email'
OIDC scope. This needs to include openid to enable OpenID Connect protocol.
- flask_tern.auth.settings.OIDC_USE_REFRESH_TOKEN = False
If set to true, the application will store a refresh token in the session, and will keep the access and id token valid (until refresh token or session timeout)
- flask_tern.auth.settings.APIKEY_SERVICE = None
5000/api/v1.0. The URL must not end with /.
- Type:
The API endpoint of the API Key service e.g. http
- Type:
//localhost
Logging
- flask_tern.logging.init_app(app: Flask)
Configure logging.
This method configures the logging for the Flask application. It configures a logger named
app.name.If
LOG_LEVELis set, it will be used. IfLOG_LEVELis not set, and the application runs in debug mode, then set log level toDEBUG. Otherwise fall back toINFOlevel.LOG_LEVEL: configure root logger level. This is either a string or one of the constants from Logging Levels.
- Parameters:
app (Flask) – Flask app to configure
from flask_tern import logging as app_logging def create_app(): app = Flask(__name__) app_logging.init_app(app) return app
- flask_tern.logging.log_audit(*args, **kwargs)
Use this method to log audit messages.
Parameters are passed on to python log function. Usually the result of create_audit_event should be passed in as the only parameter.
- Parameters:
args – positional args passed to log function.
kwargs – keyword args passed to log function.
- flask_tern.logging.record_audit_target(id, **kw)
Store target information in current application context.
The
audit_log()decorator will pick up this information and add it to the audit log object.The information is stored in
flask.gunder keyaudit_target.- Parameters:
id – identifier for target
kw – additional properties added to target
- flask_tern.logging.audit_ignore(audit_ignore=True)
Call to disable audit log via
audit_log()decorator.This is useful in case automatic audit logging should be turned off for this request.
- Parameters:
audit_ignore – Sets the request global g.audit_ignore flag to
TrueorFalse
- flask_tern.logging.audit_success(success=True, reason=None)
Call to set audit_outcome manually.
- Parameters:
success – If true outcome will be “success”, else outcome will be “failure”.
reason – A dictionary containing “reasonType” and “reasonCode” and/or “policyType” and “policyId”
- flask_tern.logging.create_audit_event(action: str, outcome, target: str | dict = None) dict
Create an Audit Event object (dictionary).
The resulting object can be passed to log_audit directly.
- flask_tern.logging.audit_log(action: str, target: str | dict = None)
Wrap view methods to create audit logs.
- Parameters:
This method automatically sets the
outcomefield tosuccessor in case the view method throws an exception tofailure. Userecord_audit_target()to add additional information about the target.# noqa: DAR201 # don’t validate return
OpenAPI
- flask_tern.openapi.OpenApiBlueprint(name: str, import_name: str) Blueprint
A Blueprint factory to serve APIs described by an OpenAPI spec document.
It provides a few routes per default:
/: A route named
api_rootwhich redicets to the Swagger UI endpoint./ui: a route to render a Swagger UI page
/openapi.yaml: serves the openapi document as yaml
/openapi.json: serves the openapi document as yaml
The openapi.yaml file is loaded via py:func:flask:flask.Blueprint.open_resource
- flask_tern.openapi.validate(validate_request=True, validate_response=True)
Decorate view methods to enable openapi request / response validation.
Using this decorator enforces request and/or response validation against the openapi spec.
- Parameters:
validate_request – enable / disable request validation
validate_response – enable / disable response validation
# noqa: DAR201 ignore missing returns
- flask_tern.openapi.settings.SWAGGER_UI_VERSION = '5.0.0-alpha.14'
The Swagger UI version used to show the openapi documentation.
ProxyFix
Setup ProxyFix middleware.
For details prease see Tell Flask it is Behind a Proxy.
Proxyfix middleware is wrapped here to make it configurable via environment variables.
See py:mod:flask_tern.proxyfix.settings.
The following headers are considered by ProxyFix middleware:
X-Forwarded-For sets REMOTE_ADDR.
X-Forwarded-Proto sets wsgi.url_scheme.
X-Forwarded-Host sets HTTP_HOST, SERVER_NAME, and SERVER_PORT.
X-Forwarded-Port sets HTTP_HOST and SERVER_PORT.
X-Forwarded-Prefix sets SCRIPT_NAME.
- flask_tern.proxyfix.init_app(app: Flask)
This method is used to configure Tell Flask it is Behind a Proxy middleware.
It sets the number of values for various X-Forward-XXX header values considered by the middleware.
Usage
from flask_tern import proxyfix def create_app(): app = Flask(__name__) proxyfix.init_app(app) return app
- Parameters:
app (Flask) – The flask app to configure.
- flask_tern.proxyfix.settings.PROXYFIX_X_FOR = 1
Number of values to trust for X-Forwarded-For.
- flask_tern.proxyfix.settings.PROXYFIX_X_PROTO = 1
Number of values to trust for X-Forwarded-Proto.
- flask_tern.proxyfix.settings.PROXYFIX_X_HOST = 0
Number of values to trust for X-Forwarded-Host.
- flask_tern.proxyfix.settings.PROXYFIX_X_PORT = 0
Number of values to trust for X-Forwarded-Port.
- flask_tern.proxyfix.settings.PROXYFIX_X_PREFIX = 0
Number of values to trust for X-Forwarded-Prefix.
Testing
This module defines some re-usable test fixtures.
These test fixtures are useful to make testing apps built with this extension a little bit easier.
- flask_tern.testing.fixtures.monkeypatch_session(request)
Experimental (https://github.com/pytest-dev/pytest/issues/363).
- flask_tern.testing.fixtures.basic_auth(app) dict
A fixture to set up some well defined users which can be used with basic authentication during testing.
- Returns:
Dictionary with users and their attributes available for testing.
- Return type:
Usage
def my_test_func(client, basic_auth): r = client.post( "/api/url", headers={"Authorization"}: basic_auth["user"]["auth"]}, )
- flask_tern.testing.fixtures.db(app)
Configure and return Flask-SQLAlchemy extesions.
If
SQLALCHEMY_URLis configured globally, then this fixture is not needed. It is useful to enable SQLAlchemy extension conditionally for specific tests.Todo
Check if this fixture is really useful in apps. (Seems like it’s only useful for this library itself.)
- Returns:
Instance of Flask-SQLAlechym extension configured for testing.
- flask_tern.testing.fixtures.cache(app)
Configure and return Flask-Caching extesions.
Configures
CACHE_TYPE = "flask_caching.backends.SimpleCache"and returns the Flask-Caching instance to use in tests.Todo
Check if this fixture is really useful in apps. (Seems like it’s only useful for this library itself.)
- Returns:
Instance of Flask-Caching extension configured for testing.
- flask_tern.testing.fixtures.views(app) dict
Add some simple views for testing to Flask app.
/public: returns ok
/: endpoint named root and returns ok
/private: requires valid user, and returns user dict
Todo
Check if this fixture is really useful in apps. (Seems like it’s only useful for this library itself.)
Usage
from flask_tern.testing.fixtures import vievs def test_view(client, views): response = client.get(views["public"]) # root, private assert response.status_code = 200 assert response.text = "ok"
- Returns:
Mapping for view name to url to use in tests.
- Return type: