diff --git a/auth-api/src/auth_api/models/org.py b/auth-api/src/auth_api/models/org.py index 1ac6fa4046..5e670d4ece 100644 --- a/auth-api/src/auth_api/models/org.py +++ b/auth-api/src/auth_api/models/org.py @@ -15,6 +15,7 @@ Basic users will have an internal Org that is not created explicitly, but implicitly upon User account creation. """ +from typing import List from flask import current_app from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, and_, cast, event, func, text from sqlalchemy.orm import contains_eager, relationship @@ -158,12 +159,19 @@ def search_org(cls, search: OrgSearch, environment: str): return pagination.items, pagination.total @classmethod - def search_orgs_by_business_identifier(cls, business_identifier, pagination_info: PaginationInfo, environment): + def search_orgs_by_business_identifier(cls, + business_identifier, + pagination_info: PaginationInfo, + environment, + excluded_org_types: List[str] = None + ): """Find all orgs affiliated with provided business identifier.""" query = db.session.query(Org) query = cls._search_for_statuses(query, []) query = cls._search_by_business_identifier(query, business_identifier, environment) + if excluded_org_types: + query = query.filter(Org.type_code.notin_(excluded_org_types)) pagination = query.order_by(Org.name.desc()) \ .paginate(per_page=pagination_info.limit, page=pagination_info.page) diff --git a/auth-api/src/auth_api/resources/v1/org.py b/auth-api/src/auth_api/resources/v1/org.py index d6f39d0594..cc146353aa 100644 --- a/auth-api/src/auth_api/resources/v1/org.py +++ b/auth-api/src/auth_api/resources/v1/org.py @@ -38,7 +38,7 @@ from auth_api.services.authorization import Authorization as AuthorizationService from auth_api.tracer import Tracer from auth_api.utils.endpoints_enums import EndpointEnum -from auth_api.utils.enums import AccessType, NotificationType, PatchActions, Status +from auth_api.utils.enums import AccessType, NotificationType, OrgType, PatchActions, Status from auth_api.utils.role_validator import validate_roles from auth_api.utils.roles import ALL_ALLOWED_ROLES, CLIENT_ADMIN_ROLES, STAFF, USER, Role # noqa: I005 from auth_api.utils.util import get_request_environment @@ -390,17 +390,20 @@ def post_organization_affiliation(org_id): @_jwt.has_one_of_roles( [Role.SYSTEM.value, Role.STAFF_VIEW_ACCOUNTS.value, Role.PUBLIC_USER.value]) def get_org_details_by_affiliation(business_identifier): - """Search orgs by BusinessIdentifier and return org Name and UUID.""" + """Search non staff orgs by BusinessIdentifier and return org Name, branch Name and UUID.""" environment = get_request_environment() pagination_info = PaginationInfo( limit=int(request.args.get('limit', 10)), page=int(request.args.get('page', 1)) ) - + excluded_org_types = [OrgType.STAFF.value, OrgType.SBC_STAFF.value] try: - data = OrgService.search_orgs_by_affiliation(business_identifier, pagination_info, environment) + data = OrgService.search_orgs_by_affiliation( + business_identifier, pagination_info, environment, excluded_org_types + ) - org_details = [{'name': org.name, 'uuid': org.uuid} for org in data['orgs']] + org_details = \ + [{'name': org.name, 'uuid': org.uuid, 'branchName': org.branch_name} for org in data['orgs']] response, status = {'orgs_details': org_details}, http_status.HTTP_200_OK except BusinessException as exception: diff --git a/auth-api/src/auth_api/services/org.py b/auth-api/src/auth_api/services/org.py index 660890c290..06d6069404 100644 --- a/auth-api/src/auth_api/services/org.py +++ b/auth-api/src/auth_api/services/org.py @@ -730,10 +730,16 @@ def search_orgs(search: OrgSearch, environment): # pylint: disable=too-many-loc return orgs_result @staticmethod - def search_orgs_by_affiliation(business_identifier, - pagination_info: PaginationInfo, environment): + def search_orgs_by_affiliation( + business_identifier, pagination_info: PaginationInfo, environment, excluded_org_types + ): """Search for orgs based on input parameters.""" - orgs, total = OrgModel.search_orgs_by_business_identifier(business_identifier, pagination_info, environment) + orgs, total = OrgModel.search_orgs_by_business_identifier( + business_identifier, + pagination_info, + environment, + excluded_org_types + ) return { 'orgs': orgs, diff --git a/auth-api/tests/unit/api/test_org.py b/auth-api/tests/unit/api/test_org.py index 8058a0c406..4b60747fba 100644 --- a/auth-api/tests/unit/api/test_org.py +++ b/auth-api/tests/unit/api/test_org.py @@ -2136,11 +2136,11 @@ def test_new_active_search(client, jwt, session, keycloak_mock): [('T12dfhsff1', CorpType.BC.value, 'NR 1234567'), ('T12dfhsff2', CorpType.GP.value, 'NR 1234566')], ['NR 1234567', 'NR 1234566'], []), ('affiliations_order', [], [], - [], [('abcde1', CorpType.BC.value, 'NR 123456'), - ('abcde2', CorpType.BC.value, 'NR 123457'), - ('abcde3', CorpType.BC.value, 'NR 123458'), - ('abcde4', CorpType.BC.value, 'NR 123459')], - [datetime(2021, 1, 1), datetime(2022, 2, 1), datetime(2022, 3, 1), datetime(2023, 2, 1)]), + [], [('abcde1', CorpType.BC.value, 'NR 123456'), + ('abcde2', CorpType.BC.value, 'NR 123457'), + ('abcde3', CorpType.BC.value, 'NR 123458'), + ('abcde4', CorpType.BC.value, 'NR 123459')], + [datetime(2021, 1, 1), datetime(2022, 2, 1), datetime(2022, 3, 1), datetime(2023, 2, 1)]), ('all', [('BC1234567', CorpType.BC.value), ('BC1234566', CorpType.BC.value)], [('T12dfhsff1', CorpType.BC.value), ('T12dfhsff2', CorpType.GP.value)], [('T12dfhsff3', CorpType.BC.value, 'NR 1234567'), ('T12dfhsff4', CorpType.GP.value, 'NR 1234566')], @@ -2248,12 +2248,15 @@ def _create_orgs_entities_and_affiliations(client, jwt, count): headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.public_user_role) client.post('/api/v1/users', headers=headers, content_type='application/json') - new_org = TestOrgInfo.org_details + new_org = TestOrgInfo.org_details.copy() new_org['name'] = new_org['name'] + ' ' + str(i) + new_org['branchName'] = 'branch-for-' + new_org['name'] rv = client.post('/api/v1/orgs', data=json.dumps(new_org), headers=headers, content_type='application/json') dictionary = json.loads(rv.data) org_id = dictionary['id'] + new_org['id'] = org_id + created_orgs.append(new_org) client.post('/api/v1/orgs/{}/affiliations'.format(org_id), headers=headers, data=json.dumps(TestAffliationInfo.affiliation3), content_type='application/json') @@ -2292,4 +2295,47 @@ def test_get_orgs_by_affiliation(client, jwt, session, keycloak_mock, for co in created_orgs: names = [od['name'] for od in orgs_details] + branches = [od['branchName'] for od in orgs_details] assert co['name'] in names + assert co['branchName'] in branches + + for od in orgs_details: + assert 'name' in od + assert 'branchName' in od + assert 'uuid' in od + assert 'id' not in od + + +def test_get_orgs_by_affiliation_filtering_out_staff_orgs(app, client, jwt, session, keycloak_mock): + """Assert that fetching orgs by affiliation do not return staff orgs.""" + orig_val_max_number_of_orgs = app.config.get('MAX_NUMBER_OF_ORGS') + app.config.update(MAX_NUMBER_OF_ORGS=10) + create_org_count = 6 + + created_orgs = _create_orgs_entities_and_affiliations(client, jwt, create_org_count) + app.config.update(MAX_NUMBER_OF_ORGS=orig_val_max_number_of_orgs) + + org3 = created_orgs[2] + org5 = created_orgs[4] + + convert_org_to_staff_org(org3['id'], OrgType.SBC_STAFF.value) + convert_org_to_staff_org(org5['id'], OrgType.STAFF.value) + + staff_org_names = [org3['name'], org5['name']] + expected_org_count = create_org_count - len(staff_org_names) + + # Create a system token + headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.system_role) + rv = client.get('/api/v1/orgs/affiliation/{}'.format(TestAffliationInfo.affiliation3.get('businessIdentifier')), + headers=headers, content_type='application/json') + + assert rv.status_code == http_status.HTTP_200_OK + assert schema_utils.validate(rv.json, 'orgs_response')[1] + + response = json.loads(rv.data) + + assert len(response.get('orgsDetails')) == expected_org_count # without org 3 and 5 + orgs_details = response.get('orgsDetails') + + for od in orgs_details: + assert od['name'] not in staff_org_names diff --git a/auth-web/src/components/auth/manage-business/manage-business-dialog/AccountAuthorizationRequest.vue b/auth-web/src/components/auth/manage-business/manage-business-dialog/AccountAuthorizationRequest.vue index de70ac048a..4369f3661e 100644 --- a/auth-web/src/components/auth/manage-business/manage-business-dialog/AccountAuthorizationRequest.vue +++ b/auth-web/src/components/auth/manage-business/manage-business-dialog/AccountAuthorizationRequest.vue @@ -26,10 +26,31 @@ :disabled="accounts.length < 2" class="business-identifier mb-n2" :items="accounts" - item-text="name" item-value="uuid" @change="emitSelected" - /> + > + + + You can add a message that will be included as part of your authorization request. { // verify that account is selected and selector disabled expect(wrapper.find('#account-authorization-request-request-account-select').attributes().disabled).toBeDefined() - expect(wrapper.find('.v-select__selection--comma').text()) - .toBe(orgsDetailsByAffiliationSingleItemResponse.orgsDetails[0].name) - expect(wrapper.findAll('.v-list-item__title').length === - orgsDetailsByAffiliationSingleItemResponse.orgsDetails.length) + expect(wrapper.find('[data-test="account-authorization-request-selection"]').text()) + .toContain(orgsDetailsByAffiliationSingleItemResponse.orgsDetails[0].name) + expect(wrapper.findAll('[data-test="account-authorization-request-option"]').exists()).toBe(false) }) it('renders enabled select with no preselected item, when multiple affiliated accounts found', async () => { @@ -104,7 +103,7 @@ describe('AccountAuthorizationRequest tests', () => { // verify that account is selected and selector disabled expect(wrapper.find('#account-authorization-request-request-account-select').attributes().disabled).toBeUndefined() - expect(wrapper.findAll('.v-list-item__title').length === + console.assert(wrapper.findAll('[data-test="account-authorization-request-option"]').length === orgsDetailsByAffiliationMultipleItemsResponse.orgsDetails.length) }) })