Skip to content

Commit

Permalink
feat: add client scope client-specific role mappings (#605)
Browse files Browse the repository at this point in the history
Co-authored-by: Justin Ryan <j.ryan@mwam.com>
  • Loading branch information
carillonator and Justin Ryan authored Nov 3, 2024
1 parent 51d34c4 commit 52b7165
Show file tree
Hide file tree
Showing 3 changed files with 399 additions and 5 deletions.
223 changes: 218 additions & 5 deletions src/keycloak/keycloak_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2602,7 +2602,9 @@ def get_realm_roles_of_client_scope(self, client_id):
return raise_error_from_response(data_raw, KeycloakGetError)

def assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles):
"""Assign client roles to a client's scope.
"""Assign client roles to a client's dedicated scope.
To assign roles to a client scope, use add_client_specific_roles_to_client_scope.
:param client_id: id of client (not client-id) who is assigned the roles
:type client_id: str
Expand All @@ -2626,7 +2628,9 @@ def assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id,
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])

def delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles):
"""Delete client roles of a client's scope.
"""Delete client roles of a client's dedicated scope.
To delete roles from a client scope, use remove_client_specific_roles_of_client_scope.
:param client_id: id of client (not client-id) who is assigned the roles
:type client_id: str
Expand All @@ -2650,7 +2654,9 @@ def delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id,
return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])

def get_client_roles_of_client_scope(self, client_id, client_roles_owner_id):
"""Get all client roles for a client's scope.
"""Get all client roles for a client's dedicated scope.
To get roles for a client scope, use get_client_specific_roles_of_client_scope.
:param client_id: id of client (not client-id)
:type client_id: str
Expand Down Expand Up @@ -3574,6 +3580,104 @@ def add_default_optional_client_scope(self, scope_id):
)
return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])

def add_client_specific_roles_to_client_scope(
self, client_scope_id, client_roles_owner_id, roles
):
"""Assign client roles to a client scope.
To assign roles to a client's dedicated scope, use assign_client_roles_to_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
:type client_roles_owner_id: str
:param roles: roles list or role (use RoleRepresentation, must include id and name)
:type roles: list
:return: Keycloak server response
:rtype: dict
"""
payload = roles if isinstance(roles, list) else [roles]
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
"client-id": client_roles_owner_id,
}
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT.format(**params_path),
data=json.dumps(payload),
)
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])

def remove_client_specific_roles_of_client_scope(
self, client_scope_id, client_roles_owner_id, roles
):
"""Delete client roles of a client scope.
To delete roles from a client's dedicated scope, use delete_client_roles_of_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
:type client_roles_owner_id: str
:param roles: roles list or role (use RoleRepresentation, must include id and name)
:type roles: list
:return: Keycloak server response
:rtype: dict
"""
payload = roles if isinstance(roles, list) else [roles]
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
"client-id": client_roles_owner_id,
}
data_raw = self.connection.raw_delete(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT.format(**params_path),
data=json.dumps(payload),
)
return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])

def get_client_specific_roles_of_client_scope(self, client_scope_id, client_roles_owner_id):
"""Get client roles for a client scope, for a specific client.
To get roles for a client's dedicated scope, use get_client_roles_of_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
:type client_roles_owner_id: str
:return: Keycloak server response (array RoleRepresentation)
:rtype: dict
"""
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
"client-id": client_roles_owner_id,
}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT.format(**params_path)
)
return raise_error_from_response(data_raw, KeycloakGetError)

def get_all_roles_of_client_scope(self, client_scope_id):
"""Get all client roles for a client scope.
To get roles for a client's dedicated scope,
use get_client_roles_of_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:return: Keycloak server response (array RoleRepresentation)
:rtype: dict
"""
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS.format(**params_path)
)
return raise_error_from_response(data_raw, KeycloakGetError)

def get_mappers_from_client(self, client_id):
"""List of all client mappers.
Expand Down Expand Up @@ -6816,7 +6920,9 @@ async def a_get_realm_roles_of_client_scope(self, client_id):
return raise_error_from_response(data_raw, KeycloakGetError)

async def a_assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles):
"""Assign client roles to a client's scope asynchronously.
"""Assign client roles to a client's dedicated scope asynchronously.
To assign roles to a client scope, use a_add_client_specific_roles_to_client_scope.
:param client_id: id of client (not client-id) who is assigned the roles
:type client_id: str
Expand All @@ -6840,7 +6946,9 @@ async def a_assign_client_roles_to_client_scope(self, client_id, client_roles_ow
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])

async def a_delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles):
"""Delete client roles of a client's scope asynchronously.
"""Delete client roles of a client's dedicated scope asynchronously.
To remove roles from a client scope, use a_remove_client_specific_roles_of_client_scope.
:param client_id: id of client (not client-id) who is assigned the roles
:type client_id: str
Expand All @@ -6866,6 +6974,8 @@ async def a_delete_client_roles_of_client_scope(self, client_id, client_roles_ow
async def a_get_client_roles_of_client_scope(self, client_id, client_roles_owner_id):
"""Get all client roles for a client's scope asynchronously.
To get roles from a client scope, use a_get_client_roles_of_client_scope.
:param client_id: id of client (not client-id)
:type client_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
Expand Down Expand Up @@ -7794,6 +7904,109 @@ async def a_add_default_optional_client_scope(self, scope_id):
)
return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])

async def a_add_client_specific_roles_to_client_scope(
self, client_scope_id, client_roles_owner_id, roles
):
"""Assign client roles to a client scope asynchronously.
To assign roles to a client's dedicated scope, use
a_assign_client_roles_to_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
:type client_roles_owner_id: str
:param roles: roles list or role (use RoleRepresentation, must include id and name)
:type roles: list
:return: Keycloak server response
:rtype: dict
"""
payload = roles if isinstance(roles, list) else [roles]
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
"client-id": client_roles_owner_id,
}
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT.format(**params_path),
data=json.dumps(payload),
)
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])

async def a_remove_client_specific_roles_of_client_scope(
self, client_scope_id, client_roles_owner_id, roles
):
"""Delete client roles of a client scope asynchronously.
To delete roles from a client's dedicated scope,
use a_delete_client_roles_of_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
:type client_roles_owner_id: str
:param roles: roles list or role (use RoleRepresentation, must include id and name)
:type roles: list
:return: Keycloak server response
:rtype: dict
"""
payload = roles if isinstance(roles, list) else [roles]
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
"client-id": client_roles_owner_id,
}
data_raw = await self.connection.a_raw_delete(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT.format(**params_path),
data=json.dumps(payload),
)
return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])

async def a_get_client_specific_roles_of_client_scope(
self, client_scope_id, client_roles_owner_id
):
"""Get all client roles for a client scope asynchronously.
To get roles for a client's dedicated scope,
use a_get_client_roles_of_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:param client_roles_owner_id: id of client (not client-id) who has the roles
:type client_roles_owner_id: str
:return: Keycloak server response (array RoleRepresentation)
:rtype: dict
"""
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
"client-id": client_roles_owner_id,
}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT.format(**params_path)
)
return raise_error_from_response(data_raw, KeycloakGetError)

async def a_get_all_roles_of_client_scope(self, client_scope_id):
"""Get all client roles for a client scope.
To get roles for a client's dedicated scope,
use a_get_client_roles_of_client_scope.
:param client_scope_id: client scope id
:type client_scope_id: str
:return: Keycloak server response (array RoleRepresentation)
:rtype: dict
"""
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS.format(**params_path)
)
return raise_error_from_response(data_raw, KeycloakGetError)

async def a_get_mappers_from_client(self, client_id):
"""List of all client mappers asynchronously.
Expand Down
5 changes: 5 additions & 0 deletions src/keycloak/urls_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@
URL_ADMIN_CLIENT_SCOPE = URL_ADMIN_CLIENT_SCOPES + "/{scope-id}"
URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER = URL_ADMIN_CLIENT_SCOPE + "/protocol-mappers/models"
URL_ADMIN_CLIENT_SCOPES_MAPPERS = URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER + "/{protocol-mapper-id}"
URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS = URL_ADMIN_CLIENT_SCOPE + "/scope-mappings"
URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_REALM = URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS + "/realm"
URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT = (
URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS + "/clients/{client-id}"
)

URL_ADMIN_REALM_ROLES = "admin/realms/{realm-name}/roles"
URL_ADMIN_REALM_ROLES_SEARCH = URL_ADMIN_REALM_ROLES + "?search={search-text}"
Expand Down
Loading

0 comments on commit 52b7165

Please sign in to comment.