Repository navigation
Recommended pattern for resolving request context after authentication but before permissions? #10077
|
Hi everyone, I've run into a scenario where I'm not sure what the cleanest DRF approach would be. I need something like this: For example, the client sends a request.member
request.workspace
request.tenantThe main reason is that some permissions depend on the resolved membership: class HasModuleAccess(BasePermission):
def has_permission(self, request, view):
return member_has_module(request.member, ...)I also want to resolve this only once per request instead of querying the same membership again in permissions, feature checks, and views. I've considered a couple of approaches. Extending the authentication classclass WorkspaceAuthentication(TokenAuthentication):
def authenticate(self, request):
result = super().authenticate(request)
if result is None:
return None
user, token = result
member = resolve_workspace_member(
user,
request.headers.get('Workspace-ID')
)
request.member = member
request.workspace = member.workspace
request.tenant = member.workspace.tenant
return user, tokenThis works because authentication happens before permissions, but resolving application context doesn't really feel like authentication. Resolving it in a permissionclass ResolveWorkspaceContext(BasePermission):
def has_permission(self, request, view):
member = resolve_workspace_member(
request.user,
request.headers.get('Workspace-ID')
)
request.member = member
request.workspace = member.workspace
request.tenant = member.workspace.tenant
return TrueThis also works, but now the permission has side effects and other permissions depend on it running first. I've also looked at middleware and the I found discussion #7770, which seems related. So my questions are:
Thanks! |
Replies: 1 comment
|
Override class WorkspaceContextMixin:
def perform_authentication(self, request):
super().perform_authentication(request)
request.member = None
if request.user.is_authenticated:
request.member = resolve_workspace_member(
request.user, request.headers.get("Workspace-ID"))Put the mixin before the DRF base class and it works for viewsets too. |
Override
perform_authentication()on a base view.APIView.initial()runs it beforecheck_permissions()(views.py), so callsuper()first and load the membership right after:Put the mixin before the DRF base class and it works for viewsets too.