You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit adds a second authorization layer that evaluates what
authenticated users are allowed to do, separate from whether they can
authenticate at all. The authorization system sits between endpoint
handlers and the existing auth dependency injection.
# Architecture
The system uses two resolver interfaces that can be swapped based on
auth module (although `AccessResolver` shouldn't change much across
different auth modules):
- `RolesResolver`: given an `AuthTuple`, determine what roles the user has
- `AccessResolver`: given roles and an action, determine if access is allowed
For JWT auth, roles are extracted by applying user-provided rules
containing JSONPath expressions to JWT claims . Each role rule specifies
a JSONPath, operator (equals/contains/in), a value, and the resulting
roles to apply if the rule matches the user claims.
This lets you map claim values like `department: "engineering"` to roles
like "developer". Roles are arbitrary except for the special `*` role
which applies to everyone automatically.
Access rules then map roles to permitted actions.
# Implementation
The `@authorize(action)` decorator wraps endpoint functions and performs the check:
1. Extract `AuthTuple` from endpoint dependencies
2. Resolve user roles from auth credentials using the `RolesResolver`
3. Check if those roles permit the requested action using the
`AccessResolver`
4. Raise 403 if denied, continue if allowed
All endpoints now declare what action they perform
(`READ_CONVERSATIONS`, `FEEDBACK`, etc.) through the `@authorize`
decorator. The middleware automatically handles the permission check
before the endpoint runs.
The middleware also populates the `request: Request` `state` property
with the `authorized_actions` set, which contains all actions the user
is allowed to perform based on their roles. Endpoints can then inspect
this property to dynamically adjust their behavior based on what the
user is allowed to do, for special actions such as listing others'
conversations (as opposed to listing only the user's own conversations)
or deleting conversations. These behaviors which are more "complicated"
than just whether the endpoint is accessible or not, as they depend on
the actual endpoint logic.
# Backwards compatibility
For non-JWT auth modules, we will default to no-op resolvers that allow
all access, maintaining current behavior.
# Technical notes
- All endpoints should accept `auth: Any =
Depends(get_auth_dependency())` to use the authorization system. The
parameter must be named `auth` to be recognized by the middleware.
# Config
Example configuration:
```yaml
authentication:
module: jwk-token
jwk_config:
url: ${SSO_BASE_URL}/protocol/openid-connect/certs
jwt_configuration:
user_id_claim: ${USER_ID_CLAIM}
username_claim: ${USERNAME_CLAIM}
role_rules:
- jsonpath: "$.realm_access.roles[*]"
operator: "contains"
value: "redhat:employees"
roles: ["redhat_employee"]
- jsonpath: "$.realm_access.roles[*]"
operator: "contains"
value: "candlepin_system_access_view_edit_all"
roles: ["read_only_admin"]
authorization:
access_rules:
- role: "admin"
actions:
- query_other_conversations
- delete_other_conversations
- role: "read_only_admin"
actions:
- list_other_conversations
- read_other_conversations
- role: "redhat_employee"
actions:
- get_models
- get_config
- role: "*"
actions:
- query
- streaming_query
- get_conversation
- list_conversations
- delete_conversation
- feedback
- get_metrics
- info
```
0 commit comments