Designing RBAC with permissions and tenant-scoped checks on every endpoint

Backend Live Coding November 13, 2026 python

Tables users, roles, permissions, user_roles, role_permissions.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ======================================================================
# Q: Design RBAC. How do you check permissions on each endpoint?
#
# Tables users, roles, permissions, user_roles, role_permissions. Permissions
# use the resource:action format (assets:read, reports:export). The check is
# done on permissions, not on role names, so a role can change without a deploy.
# Default deny.
# RBAC says "what is allowed". You also need an ownership/tenant check (does the
# object belong to the user?), otherwise you have BOLA, number 1 on the OWASP
# API Top 10. When the rules depend on attributes (department, country,
# classification level), we move to ABAC or a policy engine (e.g. OPA).
# ======================================================================

def require_permission(perm: str):
    def checker(user: User = Depends(current_user)) -> User:
        if perm not in user.permissions:      # a set, loaded once per request
            raise HTTPException(status.HTTP_403_FORBIDDEN)
        return user
    return checker

@app.get('/v1/reports/{rid}/export')
def export_report(rid: int,
                  user: User = Depends(require_permission('reports:export')),
                  session: Session = Depends(get_session)):
    report = get_scoped(session, Report, rid, user.tenant_id)   # + tenant check
    audit_log(session, user, 'reports:export', report.id)       # compliance
    return stream_csv(report)