Custom Backends and Users¶
How to create your custom Backend¶
To create a custom Backend compatible with medallion you should subclass
medallion.backends.Backend
. This object provides the basic skeleton used to
handle each of the endpoint requests. For further examples of on how to build a
custom backend look under the medallion/backends/
directory.
How to load your custom Backend¶
Backends are loaded based on the content of the backend
map element in the
medallion
configuration file. Built-in backend implementations can be
loaded by simply setting the module_class
key to the subclass’ names. Extra
keyword arguments can be passed to the backend implementation by including them
in the backend
map element. A simple example of loading the built-in
MemoryBackend
and passing it a keyword argument looks like:
{
"backend": {
"module_class": "MemoryBackend",
"filename": "../test/data/default_data.json"
}
}
Loading custom backends can be a little more complicated depending on how the
backend class is implemented. If the custom backend subclasses the base
Backend
and is somehow imported prior to starting the medallion
flask
application, the configuration file may simply refer to it by name in the
module_class
key. This might be useful in environments where it is
preferable to use something like the Python site
module rather than
installing an extra package.
{
"backend": {
"module_class": "MyCustomBackend",
}
}
To make loading out-of-tree backend implementations easier, the
medallion.backends
entrypoint is defined which may be used by other
packages to point to external modules or classes which should be loaded by the
backend machinery. This should be defined in your package’s setup.py
like:
setup(
# ...
entry_points={
"medallion.backends": [
"MyEPName = mypackage.with_backends:MyCustomBackend",
],
}
)
The entrypoint will be loaded and if it refers to a class object it will be
registered with the base Backend
class using the entrypoint’s name. If the
entrypoint is a subclass of the base Backend
, it will also be registered
using the name of the loaded class object, supporting the use of implementation
which do not subclass the base Backend
properly. The entrypoint might also
validly point to a module to be loaded, in which case any backend
implementations must be subclasses of the base Backend
and they will be
registered under their class names.
A previous implementation allowed a dotted module path to be specified in the
module
key of the backend
map in the configuration. This behaviour is
deprecated but will continue to work with a warning. This is done to allow
implementations to pivot to using the entry-point mechanism instead. An example
configuration snippet for this approach looks like:
{
"backend": {
"module": "mypackage.with_backends",
"module_class": "MyCustomBackend",
}
}
Another way to provide a custom backend using flask proxy could be:
import MyCustomBackend
from flask import current_app
from medallion import application_instance, set_config
MyCustomBackend.init() # Do some setup before attaching to application... (Imagine other steps happening here)
with application_instance.app_context():
current_app.medallion_backend = MyCustomBackend
# Do some other stuff...
set_config(application_instance, {...})
application_instance.run()
How to use a different authentication library¶
If you need or prefer a library different from Flask-HTTPAuth
, you can override it by modifying the auth
global to your preference. Now, if you want to keep changes at a minimum throughout the library. You can wrap the behavior inside another class, but remember all changes need to be performed before the call to run()
. For example,
from flask import current_app
from medallion import application_instance, auth, set_config, init_backend
# This is a dummy implementation of Flask Auth that always returns false
dummy_auth = class DummyAuth(object):
def login_required(self, f):
@wraps(f)
def decorated_function(*args, **kwargs):
return f(*args, **kwargs)
return decorated_function
def get_password():
return None # Custom stuff to get password using other libraries, users_config can go here.
# Set the default implementation to the dummy auth
auth = dummy_auth()
set_config(application_instance, {...})
init_backend(application_instance, {...})
application_instance.run()
How to use a different backend to control users¶
Our implementation of a users authentication system is not suitable for a production environment. Thus requiring to write custom code to handle credential authentication, sessions, etc. Most likely you will require the changes described in the section above on How to use a different authentication library, plus changing the users_config
.
import MyCustomDBforUsers
from flask import current_app
from medallion import application_instance, set_config
# This is a dummy implementation of Flask Auth that always returns false
dummy_auth = class DummyAuth(object):
def login_required(self, f):
@wraps(f)
def decorated_function(*args, **kwargs):
return f(*args, **kwargs)
return decorated_function
def get_password():
# Usage of MyCustomDBforUsers would likely happen here.
return something # Custom stuff to get password using other libraries, users_config functionality.
# Set the default implementation to the dummy auth
auth = dummy_auth()
db = MyCustomDBforUsers.init() # Do some setup before attaching to application... (Imagine other steps happening here)
with application_instance.app_context():
current_app.users_config = db # This will make it available inside the Flask instance in case you decide to perform changes to the internal blueprints.
init_backend(application_instance, {...})
application_instance.run()