Python: Implementing a taxonomy based access control — second part
On the first part we discovered how to check which ressources are allowed for a given rule.
On the first part we discovered how to check which ressources are allowed for a given rule.
Now it is time to ensure an action is allowed for the current user!
Protecting an endpoint
Like for the first part, we will start by showing what is done before explaining how it is done.

Illustration by the author, an endpoint protected by access control
The @register_route decorator is decorator that allow to register an endpoint at run time, you can have more intel about it on my previous article.
The @authentification decorator has a double usage, it first ensure the user is authenticated, it also inject the composite context object that is used both to access databases and to have intel about who is authenticated.
The last decorator, @action_required is what matters for us now. We wan’t the current user to have the create action enabled for the data/mongodb/
Details about how the action_required decorator works.
For the current user we list all associated “principals” each user has at least one principal, based on its user name, it has also at least one other principal for each groups it belongs to (multiple principals if the group is a nested one).
So we start by:
- listing the user principals
- retrieving policies associated with those principals,
- then filtering policies to only those that grant the expected “action”,
- and at the end filtering remaining policies to verify if they match the current resource path (here the url route)
If at the end there is at least one policy remaining, it’s that the user is granted the right to access this particular resource!

Screenshot by the author (yes there is still room to improvement)
A bit of source code for inspiration
def fetch_policies(self) -> List[Policy]:
from applications.pinceau6.models.principal_policies import PrincipalPolicies
principal_list = [self.principal, *self.groups]
global_context = GlobalContext.get_instance()
mongodb_handler = MongoDBHandler.from_default(global_context)
principal_policies = mongodb_handler.load_multiples(
PrincipalPolicies.MODEL, {"principal": {"$in": principal_list}}
)
all_policies = []
for principal_policy in principal_policies:
all_policies += principal_policy.policies
return all_policies
def action_required(action: str, resource: str, mode=ActionRequiredMode):
def my_decorator(func):
@wraps(func)
def decorated_view(*args, **kwargs):
from core.context.composite_context import CompositeContext
from core.context.user_context import UserContext
context = cast(CompositeContext, kwargs.get("context"))
user_context = context.cast_as(UserContext)
resource_parts = []
for rsc_part in resource.split("/"):
if rsc_part.startswith("<") and rsc_part.endswith(">"):
resource_parts.append(kwargs[rsc_part[1:-1]])
else:
resource_parts.append(rsc_part)
if not user_context.is_allowed(action, "/".join(resource_parts)):
abort(404)
return func(*args, **kwargs)
return decorated_view
return my_decorator
Sorry guys I don’t share all my source code with you, but I still need to preserve some secrets ;) (and subjects to make articles about).
That’s all folks