Skip to content

Commit 4d8cded

Browse files
authored
Using Roles instead of Keycloak Groups (#548)
* IR Board API * using roles instead of groups * roles to groups change
1 parent 4e1f26f commit 4d8cded

File tree

6 files changed

+34
-32
lines changed

6 files changed

+34
-32
lines changed

compliance-api/src/compliance_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def setup_jwt_manager(app_context, jwt_manager):
130130
def custom_role_callback(claims):
131131
"""Return the roles from claims."""
132132
# Extract roles from the realm_access claim in the JWT token
133-
return claims.get("groups", [])
133+
return claims.get("resource_access").get("epic-compliance", {}).get("roles", [])
134134

135135
app_context.config["JWT_ROLE_CALLBACK"] = custom_role_callback
136136
# if os.getenv("FLASK_ENV") == "development":

compliance-api/src/compliance_api/auth.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from flask_jwt_oidc import JwtManager
2020

2121
from compliance_api.exceptions import PermissionDeniedError
22-
from compliance_api.utils.constant import GROUP_MAP
2322

2423

2524
jwt = (
@@ -56,8 +55,8 @@ def decorated(f):
5655
@Auth.require
5756
@wraps(f)
5857
def wrapper(*args, **kwargs):
59-
mapped_groups = _map_permission_to_groups(permissions)
60-
if jwt.contains_role(roles=mapped_groups):
58+
mapped_permissions = Auth.map_permission_to_groups(permissions)
59+
if jwt.contains_role(roles=mapped_permissions):
6160
return f(*args, **kwargs)
6261

6362
raise PermissionDeniedError(
@@ -77,13 +76,13 @@ def has_role(cls, role):
7776
@classmethod
7877
def has_permission(cls, permissions):
7978
"""Check to see if the user has right permissions."""
80-
mapped_groups = _map_permission_to_groups(permissions)
81-
return jwt.contains_role(roles=mapped_groups)
79+
mapped_permissions = Auth.map_permission_to_groups(permissions)
80+
return jwt.contains_role(roles=mapped_permissions)
8281

83-
84-
def _map_permission_to_groups(permissions):
85-
"""Map the permissions to user groups in keycloak."""
86-
return [GROUP_MAP[role] for role in permissions]
82+
@staticmethod
83+
def map_permission_to_groups(permissions):
84+
"""Map the permissions to user groups in keycloak."""
85+
return [role.value for role in permissions]
8786

8887

8988
auth = Auth()
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
"""Constants."""
22

3-
from .enum import PermissionEnum
4-
5-
63
AUTH_APP = "COMPLIANCE"
74
INPUT_DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
85
UNAPPROVED_PROJECT_NAME = "Unapproved Project"
96
UNAPPROVED_PROJECT_CODE = "UNPRVD"
10-
GROUP_MAP = {
11-
PermissionEnum.SUPERUSER: "/COMPLIANCE/SUPERUSER",
12-
PermissionEnum.VIEWER: "/COMPLIANCE/VIEWER",
13-
PermissionEnum.USER: "/COMPLIANCE/USER",
14-
PermissionEnum.ADMIN: "/COMPLIANCE/ADMIN",
15-
}
167
DELETE_DIC_PARAMS = {"is_active": False, "is_deleted": True}
178
OFFICE_NAME = "Environmental Assessment Office"
189
OFFICE_BRANCH = "Compliance and Enforcement Branch"

compliance-api/src/compliance_api/utils/enum.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ContextEnum(Enum):
3737
class PermissionEnum(Enum):
3838
"""Enum for Staff User Permissions."""
3939

40-
VIEWER = "Viewer"
41-
USER = "User"
42-
SUPERUSER = "Superuser"
43-
ADMIN = "Admin"
40+
VIEWER = "viewer"
41+
USER = "user"
42+
SUPERUSER = "super_user"
43+
ADMIN = "admin"

compliance-api/tests/utilities/factory_scenario/token_claim.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class TokenJWTClaims(dict, Enum):
2222
"preferred_username": fake.user_name(),
2323
"groups": ["/COMPLIANCE/VIEWER"],
2424
"realm_access": {"roles": []},
25+
"resource_access": {"epic-compliance": {"roles": ["viewer"]}},
2526
}
2627
super_user = {
2728
"iss": CONFIG.JWT_OIDC_TEST_ISSUER,
@@ -31,4 +32,5 @@ class TokenJWTClaims(dict, Enum):
3132
"preferred_username": fake.user_name(),
3233
"groups": ["/COMPLIANCE/SUPERUSER"],
3334
"realm_access": {"roles": []},
35+
"resource_access": {"epic-compliance": {"roles": ["super_user"]}},
3436
}

compliance-web/src/hooks/useAuthorization.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import { StaffUser } from "@/models/Staff";
22
import { jwtDecode, JwtPayload } from "jwt-decode";
33
import { useAuth } from "react-oidc-context";
4+
import { OidcConfig } from "@/utils/config";
45

56
interface CustomJwtPayload extends JwtPayload {
6-
groups?: string[];
7+
resource_access?: {
8+
[key: string]: {
9+
roles: string[];
10+
};
11+
};
712
}
813

914
export const KC_USER_GROUPS = {
10-
SUPERUSER: "/COMPLIANCE/SUPERUSER",
11-
USER: "/COMPLIANCE/USER",
12-
ADMIN: "/COMPLIANCE/ADMIN",
13-
VIEWER: "/COMPLIANCE/VIEWER",
15+
SUPERUSER: "super_user",
16+
USER: "user",
17+
ADMIN: "admin",
18+
VIEWER: "viewer",
1419
};
1520

1621
export const useIsRolesAllowed = (
@@ -23,10 +28,15 @@ export const useIsRolesAllowed = (
2328
return false;
2429
}
2530

26-
const { groups = [] } = jwtDecode<CustomJwtPayload>(authUser.access_token);
27-
28-
// Check if the user has any of the required roles
29-
const isRoleAllowed = roles.some((role) => groups.includes(role));
31+
const payload = jwtDecode<CustomJwtPayload>(authUser.access_token);
32+
33+
// Get roles from resource_access if available
34+
const resourceRoles = payload.resource_access?.[OidcConfig.client_id]?.roles || [];
35+
36+
// Check if the user has any of the required roles (from groups or resource_access)
37+
const isRoleAllowed = roles.some((role) =>
38+
resourceRoles.includes(role)
39+
);
3040

3141
// Check if the logged-in user is part of the provided users list
3242
const isUserAllowed =

0 commit comments

Comments
 (0)