diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 5c0dc5bc5..15fba3d86 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,3 +1,9 @@ +## May 7, 2024 + +- **Feature** Ensure users can belong to more than one tenant [🎟️ DESENG-478](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-478) + - Tested users can belong to more than one tenant. + - Overide the default tenant filter for staff_user table to check with user_group_membership table. + ## May 6, 2024 - **Feature** Auto-populate question descriptions [🎟️ DESENG-596](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-596) diff --git a/met-api/src/met_api/models/staff_user.py b/met-api/src/met_api/models/staff_user.py index bc9af40e7..4607797c9 100644 --- a/met-api/src/met_api/models/staff_user.py +++ b/met-api/src/met_api/models/staff_user.py @@ -6,16 +6,18 @@ from typing import Optional +from flask import g from sqlalchemy import Column, ForeignKey, String, asc, desc, func from sqlalchemy.orm import column_property from sqlalchemy.sql import text from sqlalchemy.sql.operators import ilike_op +from met_api.models.user_group_membership import UserGroupMembership from met_api.utils.enums import UserStatus from met_api.utils.roles import Role from met_api.utils.token_info import TokenInfo -from .base_model import BaseModel +from .base_model import TENANT_ID, BaseModel from .db import db from .pagination_options import PaginationOptions @@ -66,6 +68,17 @@ def get_all_paginated(cls, pagination_options: PaginationOptions, search_text='' page = query.paginate(page=pagination_options.page, per_page=pagination_options.size) return page.items, page.total + @classmethod + def _add_tenant_filter(cls, query): + """Add tenant filtering to the query based on user group membership.""" + has_tenant_id = hasattr(cls, TENANT_ID) + has_g_tenant_id = hasattr(g, TENANT_ID) and g.tenant_id + if has_tenant_id and has_g_tenant_id: + return query.join( + UserGroupMembership, UserGroupMembership.staff_user_external_id == cls.external_id + ).filter(UserGroupMembership.tenant_id == g.tenant_id) + return query + @classmethod def get_by_id(cls, _id, include_inactive=False) -> Optional[StaffUser]: """Get a user by id.""" diff --git a/met-api/tests/unit/api/test_user.py b/met-api/tests/unit/api/test_user.py index ade06e618..b6e81da64 100644 --- a/met-api/tests/unit/api/test_user.py +++ b/met-api/tests/unit/api/test_user.py @@ -67,9 +67,16 @@ def test_get_staff_users(client, jwt, session, setup_admin_user_and_claims, setu staff_2 = dict(TestUserInfo.user_staff_1) staff_other_tenant = dict(TestUserInfo.user_staff_2) staff_other_tenant['tenant_id'] = factory_tenant_model(TestTenantInfo.tenant2).id - factory_staff_user_model(user_info=staff_1) - factory_staff_user_model(user_info=staff_2) - factory_staff_user_model(user_info=staff_other_tenant) + user1 = factory_staff_user_model(user_info=staff_1) + # Mapping user1 to its tenant via user group membership + factory_user_group_membership_model(str(user1.external_id), user1.tenant_id) + user2 = factory_staff_user_model(user_info=staff_2) + # Mapping user2 to its tenant via user group membership + factory_user_group_membership_model(str(user2.external_id), user2.tenant_id) + + user3 = factory_staff_user_model(user_info=staff_other_tenant) + # Mapping user3 to its tenant via user group membership + factory_user_group_membership_model(str(user3.external_id), user3.tenant_id) # Check that staff admins can see users within the same tenant _, claims = setup_admin_user_and_claims