asfquart has built-in decorators for easily handling AAA (Authentication, Authorization, and Access) for requests. These decorators have been built with the organizational structure of the ASF in mind, and allow you to tailor access to each end-point to suit your specific requirements. These decorators will, unless otherwise specified, automatically initiate an OAuth flow if unauthenticated access is attempted.
At present, asfquart features the following auth requirements:
asfquart.auth.Requirements.committer
: User must be a committer of any project to accessasfquart.auth.Requirements.member
: User must be a member of the Foundation to accessasfquart.auth.Requirements.chair
: User must be a chair of one or more projectsasfquart.auth.Requirements.mfa_enabled
: User must be logged in using a method that requires multi-factor authentication
These requirements can be passed to the asfquart.auth.require
decorator to create a list of requirements
that must pass in order to make use of the endpoint.
By default, requirements are implictly in the all_of
category, meaning they are AND'ed together.
You can also OR requirements by using the any_of
flag instead:
@asfquart.auth.require # Require a valid session of any kind (implicitly, committer)
async def func():
pass
@asfquart.auth.require({req1, req2}) # Chain two requirements for this endpoint, implicitly AND
async def func():
pass
@asfquart.auth.require(all_of={req1, req2}) # Same but with explicit AND
@asfquart.auth.require(any_of={req1, req2}) # Same but with explicit OR instead
# You can also use both, such as requiring (req1 AND req2) AND (either req3 OR req4)
@asfquart.auth.require(all_of={req1, req2}, any_of={req3,req4})
The example below shows how to cordon off specific end-points to certain groups of users:
import asfquart
from asfquart.auth import Requirements as R
APP = asfquart.APP
# URL that requires some sort of ASF auth (oauth, ldap)
@APP.route("/foo")
@asfquart.auth.require # Bare decorator means just require a valid session
async def view_that_requires_auth():
pass
# URL that requires 2FA (implies oauth since ldap doesn't have 2fa)
@APP.route("/foo2fa")
@asfquart.auth.require({R.mfa_enabled})
async def view_that_requires_2fa_auth():
pass
# URL that requires a certain org role (2FA implied??)
@APP.route("/foorole")
@asfquart.auth.require({R.member})
async def view_that_requires_member_role():
pass
# URL that needs at least one of multiple requirements, using the any_of directive
@APP.route("/multirole")
@asfquart.auth.require(any_of={R.member, R.chair}) # Either chair or member (or both) required
async def view_that_requires_some_role():
pass