diff --git a/CHANGELOG.md b/CHANGELOG.md index 85db97ea..21a7ed58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,19 @@ ## v24.36 ### Pre-releases +- v24.36-alpha2 - v24.36-alpha1 +- v24.29-alpha7 +- v24.29-alpha6 + +### Fix +- Sort assigned tenants and roles alphabetically (#417, `v24.36-alpha2`) +- Do not check tenant existence when unassigning tenant (#415, `v24.29-alpha8`) +- Hotfix: Session expiration in userinfo must match access token expiration (#414, `v24.29-alpha7`) ### Features - Duplicating roles (#416, `v24.36-alpha1`) +- Run Batman with warning when there is no ElasticSearch URL (#413, `v24.29-alpha6`) --- @@ -14,8 +23,8 @@ ## v24.29 ### Pre-releases -- v24.29-alpha7 -- v24.29-alpha6 +- v24.29-beta2 +- v24.29-beta - v24.29-alpha5 - v24.29-alpha4 - v24.29-alpha3 @@ -23,13 +32,10 @@ - v24.29-alpha1 ### Fix -- Do not check tenant existence when unassigning tenant (#415, `v24.29-alpha8`) -- Hotfix: Session expiration in userinfo must match access token expiration (#414, `v24.29-alpha7`) - Non-editable items are marked with read_only flag (#411, `v24.29-alpha5`) - Handle session decryption error (#410, `v24.29-alpha2`) ### Features -- Run Batman with warning when there is no ElasticSearch URL (#413, `v24.29-alpha6`) - Disable editing of managed resources and clients (#409, `v24.29-alpha4`) - Global roles propagated to tenants (#395, `v24.29-alpha3`) - Sort clients alphabetically by client name (#408, `v24.29-alpha2`) diff --git a/seacatauth/authz/role/service.py b/seacatauth/authz/role/service.py index 6c5318b9..e2104629 100644 --- a/seacatauth/authz/role/service.py +++ b/seacatauth/authz/role/service.py @@ -441,18 +441,31 @@ async def _validate_role_resources(self, role_id: str, propagated: bool, resourc raise asab.exceptions.ValidationError(message) - async def get_roles_by_credentials(self, credentials_id: str, tenants: list = None): + async def get_roles_by_credentials( + self, + credentials_id: str, + tenants: list = None, + limit: typing.Optional[int] = None, + page: int = 0 + ): """ Returns a list of roles assigned to the given `credentials_id`. Includes roles that match the given `tenant` plus global roles. """ + collection = await self.StorageService.collection(self.CredentialsRolesCollection) + query_filter = { + "c": credentials_id, + "t": {"$in": [None, *(tenants or [])]} + } + cursor = collection.find(query_filter) + cursor.sort("r", 1) + if limit is not None: + cursor.skip(limit * page) + cursor.limit(limit) + result = [] - coll = await self.StorageService.collection(self.CredentialsRolesCollection) - async for obj in coll.find({ - 'c': credentials_id, - 't': {"$in": [None, *(tenants or [])]} - }): - result.append(obj["r"]) + async for assignment in cursor: + result.append(assignment["r"]) return result diff --git a/seacatauth/tenant/handler.py b/seacatauth/tenant/handler.py index 521ba374..13775345 100644 --- a/seacatauth/tenant/handler.py +++ b/seacatauth/tenant/handler.py @@ -254,14 +254,16 @@ async def get_tenants_by_credentials(self, request): """ Get list of tenants memberships of the requested credentials """ - tenants = set(await self.TenantService.get_tenants(request.match_info["credentials_id"])) + tenants = await self.TenantService.get_tenants(request.match_info["credentials_id"]) if not request.can_access_all_tenants: - my_tenants = await self.TenantService.get_tenants(request.Session.Credentials.Id) - tenants.intersection_update(my_tenants) - - return asab.web.rest.json_response( - request, list(tenants) - ) + my_tenants = set(await self.TenantService.get_tenants(request.Session.Credentials.Id)) + # Preserve ordering + tenants = [ + tenant for tenant in tenants + if tenant in my_tenants + ] + + return asab.web.rest.json_response(request, tenants) @asab.web.rest.json_schema_handler(schemas.GET_TENANTS_BATCH) diff --git a/seacatauth/tenant/providers/mongodb.py b/seacatauth/tenant/providers/mongodb.py index c74e286d..204543d6 100644 --- a/seacatauth/tenant/providers/mongodb.py +++ b/seacatauth/tenant/providers/mongodb.py @@ -167,11 +167,9 @@ async def get(self, tenant_id) -> Optional[dict]: async def iterate_assigned(self, credatials_id: str, page: int = 10, limit: int = None): collection = await self.MongoDBStorageService.collection(self.AssignCollection) - - filter = {'c': credatials_id} + filter = {"c": credatials_id} cursor = collection.find(filter) - - cursor.sort('_id', 1) + cursor.sort("t", 1) if limit is not None: cursor.skip(limit * page) cursor.limit(limit) diff --git a/seacatauth/tenant/service.py b/seacatauth/tenant/service.py index d3b9202d..e7a6526b 100644 --- a/seacatauth/tenant/service.py +++ b/seacatauth/tenant/service.py @@ -29,7 +29,7 @@ def __init__(self, app, service_name="seacatauth.TenantService"): def create_provider(self, provider_id, config_section_name): assert (self.TenantsProvider is None) # We support only one tenant provider for now _, creds, provider_type, provider_name = config_section_name.rsplit(":", 3) - if provider_type == 'mongodb': + if provider_type == "mongodb": from .providers.mongodb import MongoDBTenantProvider provider = MongoDBTenantProvider(self.App, provider_id, config_section_name) @@ -156,7 +156,7 @@ async def get_tenants(self, credentials_id: str): # TODO: This has to be cached agressivelly result = [] async for obj in self.TenantsProvider.iterate_assigned(credentials_id): - result.append(obj['t']) + result.append(obj["t"]) return result