def delete_org( org_id, token_info: Dict = None, ): """Soft-Deletes an Org. It should not be deletable if there are members or business associated with the org """ # Check authorization for the user current_app.logger.debug('<org Inactivated') check_auth(token_info, one_of_roles=OWNER, org_id=org_id) org: OrgModel = OrgModel.find_by_org_id(org_id) if not org: raise BusinessException(Error.DATA_NOT_FOUND, None) count_members = len([ member for member in org.members if member.status in VALID_STATUSES ]) if count_members > 1 or len(org.affiliated_entities) >= 1: raise BusinessException(Error.ORG_CANNOT_BE_DISSOLVED, None) org.delete() # Remove user from thr group if the user doesn't have any other orgs membership user = UserModel.find_by_jwt_token(token=token_info) if len(MembershipModel.find_orgs_for_user(user.id)) == 0: KeycloakService.remove_from_account_holders_group( user.keycloak_guid) current_app.logger.debug('org Inactivated>')
def test_create_user_and_add_membership_admin_bulk_mode_multiple( session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that an admin can add a group of members.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) user = factory_user_model() factory_membership_model(user.id, org.id) claims = TestJwtClaims.get_test_real_user(user.keycloak_guid) membership = [ TestAnonymousMembership.generate_random_user(MEMBER), TestAnonymousMembership.generate_random_user(ADMIN) ] users = UserService.create_user_and_add_membership(membership, org.id, token_info=claims) assert len(users['users']) == 2 assert users['users'][0][ 'username'] == IdpHint.BCROS.value + '/' + membership[0]['username'] assert users['users'][0]['type'] == 'ANONYMOUS' assert users['users'][1][ 'username'] == IdpHint.BCROS.value + '/' + membership[1]['username'] assert users['users'][1]['type'] == 'ANONYMOUS' members = MembershipModel.find_members_by_org_id(org.id) # staff didnt create members..so count is count of owner+other 2 members assert len(members) == 3
def create_org(org_info: dict, user_id): """Create a new organization.""" current_app.logger.debug('<create_org ') existing_similar__org = OrgModel.find_similar_org_by_name(org_info['name']) if existing_similar__org is not None: raise BusinessException(Error.DATA_CONFLICT, None) org = OrgModel.create_from_dict(camelback2snake(org_info)) org.save() current_app.logger.info(f'<created_org org_id:{org.id}') # create the membership record for this user membership = MembershipModel(org_id=org.id, user_id=user_id, membership_type_code='OWNER', membership_type_status=Status.ACTIVE.value) membership.save() return Org(org)
def test_add_back_a_delete_bcros(client, jwt, session, keycloak_mock): """Assert different conditions of user deletion.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) user = factory_user_model(user_info=TestUserInfo.user_bcros_active) factory_membership_model(user.id, org.id) factory_product_model(org.id, product_code=ProductCode.DIR_SEARCH.value) owner_claims = TestJwtClaims.get_test_real_user(user.keycloak_guid) member = TestAnonymousMembership.generate_random_user(USER) membership = [ member, TestAnonymousMembership.generate_random_user(COORDINATOR) ] UserService.create_user_and_add_membership(membership, org.id, token_info=owner_claims) headers = factory_auth_header(jwt=jwt, claims=owner_claims) member_user_id = IdpHint.BCROS.value + '/' + member.get('username') rv = client.delete(f'/api/v1/users/{member_user_id}', headers=headers, content_type='application/json') assert rv.status_code == http_status.HTTP_204_NO_CONTENT kc_user = KeycloakService.get_user_by_username(member.get('username')) assert kc_user.enabled is False user_model = UserService.find_by_username(member_user_id) assert user_model.as_dict().get('user_status') == UserStatus.INACTIVE.value membership = MembershipModel.find_membership_by_userid( user_model.identifier) assert membership.status == Status.INACTIVE.value
def get_invitations_for_org(org_id, status=None, **kwargs): """Get invitations for an org.""" user_from_context: UserContext = kwargs['user_context'] org_model = OrgModel.find_by_org_id(org_id) if not org_model: return None if status: status = InvitationStatus[status] # If staff return full list if user_from_context.is_staff(): return InvitationModel.find_pending_invitations_by_org(org_id) current_user: UserService = UserService.find_by_jwt_token() current_user_membership: MembershipModel = \ MembershipModel.find_membership_by_user_and_org(user_id=current_user.identifier, org_id=org_id) # If no active membership return empty array if current_user_membership is None or \ current_user_membership.status != Status.ACTIVE.value: return [] # Ensure either ADMIN or COORDINATOR if current_user_membership.membership_type_code == USER: return [] return InvitationModel.find_invitations_by_org(org_id=org_id, status=status)
def get_invitations_for_org(org_id, status=None, token_info: Dict = None): """Get invitations for an org.""" org_model = OrgModel.find_by_org_id(org_id) if not org_model: return None if status: status = InvitationStatus[status] # If staff return full list if 'staff' in token_info.get('realm_access').get('roles'): return InvitationModel.find_pending_invitations_by_org(org_id) current_user: UserService = UserService.find_by_jwt_token(token_info) current_user_membership: MembershipModel = \ MembershipModel.find_membership_by_user_and_org(user_id=current_user.identifier, org_id=org_id) # If no active membership return empty array if current_user_membership is None or \ current_user_membership.status != Status.ACTIVE.value: return [] # Ensure either ADMIN or COORDINATOR if current_user_membership.membership_type_code == USER: return [] return InvitationModel.find_invitations_by_org(org_id=org_id, status=status)
def test_delete_user_where_org_has_affiliations(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that a user can be deleted.""" user_model = factory_user_model(user_info=TestUserInfo.user_test) contact = factory_contact_model() contact_link = ContactLinkModel() contact_link.contact = contact contact_link.user = user_model contact_link = contact_link.flush() contact_link.commit() org = OrgService.create_org(TestOrgInfo.org1, user_id=user_model.id).as_dict() org_id = org['id'] entity = factory_entity_model(entity_info=TestEntityInfo.entity_lear_mock) affiliation = AffiliationModel(org_id=org_id, entity_id=entity.id) affiliation.save() with pytest.raises(BusinessException) as exception: UserService.delete_user(TestJwtClaims.user_test) assert exception.code == Error.DELETE_FAILED_ONLY_OWNER updated_user = UserModel.find_by_jwt_token(TestJwtClaims.user_test) contacts = UserService.get_contacts(TestJwtClaims.user_test) assert len(contacts) == 1 user_orgs = MembershipModel.find_orgs_for_user(updated_user.id) for org in user_orgs: assert org.status_code == 'ACTIVE'
def reset_password_for_anon_user(user_info: dict, user_name, token_info: Dict = None): """Reset the password of the user.""" user = UserModel.find_by_username(user_name) membership = MembershipModel.find_membership_by_userid(user.id) org_id = membership.org_id org = OrgModel.find_by_org_id(org_id) if not org or org.access_type != AccessType.ANONYMOUS.value: raise BusinessException(Error.INVALID_INPUT, None) check_auth(org_id=org_id, token_info=token_info, one_of_roles=(ADMIN, STAFF)) update_user_request = KeycloakUser() update_user_request.user_name = user_name.replace( IdpHint.BCROS.value + '/', '') update_user_request.password = user_info['password'] update_user_request.update_password_on_login() try: kc_user = KeycloakService.update_user(update_user_request) except HTTPError as err: current_app.logger.error('update_user in keycloak failed {}', err) raise BusinessException(Error.UNDEFINED_ERROR, err) return kc_user
def get_admins_for_membership(membership_id, status=Status.ACTIVE.value): """Get admins for an org.""" membership = MembershipModel.find_membership_by_id(membership_id) org_id = membership.org_id return UserModel.find_users_by_org_id_by_status_by_roles( org_id, CLIENT_ADMIN_ROLES, status)
def test_create_user_and_add_membership_admin_bulk_mode_multiple( session, auth_mock, keycloak_mock, monkeypatch): # pylint:disable=unused-argument """Assert that an admin can add a group of members.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) user = factory_user_model() factory_membership_model(user.id, org.id) factory_product_model(org.id, product_code=ProductCode.DIR_SEARCH.value) claims = TestJwtClaims.get_test_real_user(user.keycloak_guid) membership = [ TestAnonymousMembership.generate_random_user(USER), TestAnonymousMembership.generate_random_user(COORDINATOR) ] patch_token_info(claims, monkeypatch) users = UserService.create_user_and_add_membership(membership, org.id) assert len(users['users']) == 2 assert users['users'][0][ 'username'] == IdpHint.BCROS.value + '/' + membership[0]['username'] assert users['users'][0]['type'] == Role.ANONYMOUS_USER.name assert users['users'][1][ 'username'] == IdpHint.BCROS.value + '/' + membership[1]['username'] assert users['users'][1]['type'] == Role.ANONYMOUS_USER.name members = MembershipModel.find_members_by_org_id(org.id) # staff didnt create members..so count is count of owner+other 2 members assert len(members) == 3
def __remove_org_membership(org, user_id): is_user_an_owner: bool = False org_has_other_owners: bool = False user_membership: MembershipModel = None for member in MembershipModel.find_members_by_org_id(org.id): if member.user_id == user_id: user_membership = member if member.membership_type_code == OWNER: is_user_an_owner = True elif member.membership_type_code == OWNER: org_has_other_owners = True current_app.logger.info( f'Org :{org.name} --> User Owner : {is_user_an_owner},Has other owners :{org_has_other_owners}' ) if is_user_an_owner and not org_has_other_owners: current_app.logger.info('Affiliated entities : {}'.format( len(org.affiliated_entities))) if len(org.affiliated_entities) == 0: org.status_code = OrgStatus.INACTIVE.value org.flush() else: # Roll back the transaction as there could be situation where a change in one org # membership is flushed, but the next one fails. In this case roll back whole transaction org.rollback() raise BusinessException(Error.DELETE_FAILED_ONLY_OWNER, None) else: user_membership.status = Status.INACTIVE.value user_membership.flush()
def test_create_user_and_add_transaction_membership_1(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert transactions works fine.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) membership = [TestAnonymousMembership.generate_random_user(ADMIN)] with patch('auth_api.models.User.flush', side_effect=Exception('mocked error')): users = UserService.create_user_and_add_membership(membership, org.id, single_mode=True) user_name = IdpHint.BCROS.value + '/' + membership[0]['username'] assert len(users['users']) == 1 assert users['users'][0]['username'] == membership[0]['username'] assert users['users'][0]['http_status'] == 500 assert users['users'][0]['error'] == 'Adding User Failed' # make sure no records are created user = UserModel.find_by_username(user_name) assert user is None user = UserModel.find_by_username(membership[0]['username']) assert user is None members = MembershipModel.find_members_by_org_id(org.id) # only one member should be there since its a STAFF created org assert len(members) == 0
def _add_or_remove_group(model: MembershipModel): """Add or remove the user from/to account holders group.""" if model.membership_status.id == Status.ACTIVE.value: KeycloakService.join_account_holders_group(model.user.keycloak_guid) elif model.membership_status.id == Status.INACTIVE.value and len( MembershipModel.find_orgs_for_user(model.user.id)) == 0: # Check if the user has any other active org membership, if none remove from the group KeycloakService.remove_from_account_holders_group(model.user.keycloak_guid)
def test_delete_user_where_user_is_member_on_org(session, auth_mock, keycloak_mock, monkeypatch): # pylint:disable=unused-argument """Assert that a user can be deleted.""" # Create a user and org user_model = factory_user_model(user_info=TestUserInfo.user_test) contact = factory_contact_model() contact_link = ContactLinkModel() contact_link.contact = contact contact_link.user = user_model contact_link.commit() patch_token_info(TestJwtClaims.get_test_user(user_model.keycloak_guid), monkeypatch) org = OrgService.create_org(TestOrgInfo.org1, user_id=user_model.id) org_dictionary = org.as_dict() org_id = org_dictionary['id'] entity = factory_entity_model(entity_info=TestEntityInfo.entity_lear_mock) affiliation = AffiliationModel(org_id=org_id, entity_id=entity.id) affiliation.save() # Create another user and add membership to the above org user_model2 = factory_user_model(user_info=TestUserInfo.user2) contact = factory_contact_model() contact_link = ContactLinkModel() contact_link.contact = contact contact_link.user = user_model2 contact_link.commit() membership = MembershipModel(org_id=org_id, user_id=user_model2.id, membership_type_code='USER', membership_type_status=Status.ACTIVE.value) membership.save() patch_token_info(TestJwtClaims.get_test_user(user_model2.keycloak_guid), monkeypatch) UserService.delete_user() updated_user = UserModel.find_by_jwt_token() assert len(updated_user.contacts) == 0 user_orgs = MembershipModel.find_orgs_for_user(updated_user.id) for org in user_orgs: assert org.status_code == 'INACTIVE'
def _create_new_user_and_membership(db_username, kc_user, membership, org_id): user_model: UserModel = UserModel(username=db_username, is_terms_of_use_accepted=False, status=Status.ACTIVE.value, type=AccessType.ANONYMOUS.value, email=membership.get('email', None), firstname=kc_user.first_name, lastname=kc_user.last_name) user_model.flush() membership_model = MembershipModel( org_id=org_id, user_id=user_model.id, membership_type_code=membership['membershipType'], membership_type_status=Status.ACTIVE.value) membership_model.flush() return user_model
def get_members_for_org( org_id, status=Status.ACTIVE.name, # pylint:disable=too-many-return-statements membership_roles=ALL_ALLOWED_ROLES, **kwargs): """Get members of org.Fetches using status and roles.""" org_model = OrgModel.find_by_org_id(org_id) if not org_model: return None user_from_context: UserContext = kwargs['user_context'] status = Status.ACTIVE.value if status is None else Status[status].value membership_roles = ALL_ALLOWED_ROLES if membership_roles is None else membership_roles # If staff return full list if user_from_context.is_staff(): return MembershipModel.find_members_by_org_id_by_status_by_roles( org_id, membership_roles, status) current_user: UserService = UserService.find_by_jwt_token() current_user_membership: MembershipModel = \ MembershipModel.find_membership_by_user_and_org(user_id=current_user.identifier, org_id=org_id) # If no active or pending membership return empty array if current_user_membership is None or \ current_user_membership.status == Status.INACTIVE.value or \ current_user_membership.status == Status.REJECTED.value: return [] # If pending approval, return empty for active, array of self only for pending if current_user_membership.status == Status.PENDING_APPROVAL.value: return [current_user_membership ] if status == Status.PENDING_APPROVAL.value else [] # If active status for current user, then check organizational role if current_user_membership.status == Status.ACTIVE.value: if current_user_membership.membership_type_code in (ADMIN, COORDINATOR): return MembershipModel.find_members_by_org_id_by_status_by_roles( org_id, membership_roles, status) return MembershipModel.find_members_by_org_id_by_status_by_roles(org_id, membership_roles, status) \ if status == Status.ACTIVE.value else [] return []
def get_members_for_org( org_id, status=Status.ACTIVE, # pylint:disable=too-many-return-statements membership_roles=ALL_ALLOWED_ROLES, token_info: Dict = None): """Get members of org.Fetches using status and roles.""" org_model = OrgModel.find_by_org_id(org_id) if not org_model: return None status = Status.ACTIVE.value if status is None else Status[status].value membership_roles = ALL_ALLOWED_ROLES if membership_roles is None else membership_roles # If staff return full list if 'staff' in token_info.get('realm_access').get('roles'): return MembershipModel.find_members_by_org_id_by_status_by_roles( org_id, membership_roles, status) current_user: UserService = UserService.find_by_jwt_token(token_info) current_user_membership: MembershipModel = \ MembershipModel.find_membership_by_user_and_org(user_id=current_user.identifier, org_id=org_id) # If no active or pending membership return empty array if current_user_membership is None or \ current_user_membership.status == Status.INACTIVE.value or \ current_user_membership.status == Status.REJECTED.value: return [] # If pending approval, return empty for active, array of self only for pending if current_user_membership.status == Status.PENDING_APPROVAL.value: return [current_user_membership ] if status == Status.PENDING_APPROVAL.value else [] # If active status for current user, then check organizational role if current_user_membership.status == Status.ACTIVE.value: if current_user_membership.membership_type_code == ADMIN or \ current_user_membership.membership_type_code == COORDINATOR: return MembershipModel.find_members_by_org_id_by_status_by_roles( org_id, membership_roles, status) return MembershipModel.find_members_by_org_id_by_status_by_roles(org_id, membership_roles, status) \ if status == Status.ACTIVE.value else [] return []
def _create_new_user_and_membership(db_username, kc_user, membership, org_id): user_model: UserModel = UserModel(username=db_username, is_terms_of_use_accepted=False, status=Status.ACTIVE.value, type=Role.ANONYMOUS_USER.name, email=membership.get('email', None), firstname=kc_user.first_name, lastname=kc_user.last_name, login_source=LoginSource.BCROS.value) user_model.flush() membership_model = MembershipModel(org_id=org_id, user_id=user_model.id, membership_type_code=membership['membershipType'], membership_type_status=Status.ACTIVE.value) membership_model.flush() name = {'first_name': user_model.firstname, 'last_name': user_model.lastname} ActivityLogPublisher.publish_activity(Activity(org_id, ActivityAction.APPROVE_TEAM_MEMBER.value, name=json.dumps(name), value=membership['membershipType'], id=user_model.id)) return user_model
def create_org(org_info: dict, user_id, token_info: Dict = None): """Create a new organization.""" current_app.logger.debug('<create_org ') is_staff_admin = token_info and 'staff_admin' in token_info.get( 'realm_access').get('roles') if not is_staff_admin: # staff can create any number of orgs count = OrgModel.get_count_of_org_created_by_user_id(user_id) if count >= current_app.config.get('MAX_NUMBER_OF_ORGS'): raise BusinessException(Error.MAX_NUMBER_OF_ORGS_LIMIT, None) if org_info.get('accessType', None) == AccessType.ANONYMOUS.value: raise BusinessException(Error.USER_CANT_CREATE_ANONYMOUS_ORG, None) existing_similar__org = OrgModel.find_similar_org_by_name( org_info['name']) if existing_similar__org is not None: raise BusinessException(Error.DATA_CONFLICT, None) org = OrgModel.create_from_dict(camelback2snake(org_info)) if is_staff_admin: org.access_type = AccessType.ANONYMOUS.value org.billable = False else: org.access_type = AccessType.BCSC.value org.billable = True org.save() current_app.logger.info(f'<created_org org_id:{org.id}') # create the membership record for this user if its not created by staff and access_type is anonymous if not is_staff_admin and org_info.get( 'access_type') != AccessType.ANONYMOUS: membership = MembershipModel( org_id=org.id, user_id=user_id, membership_type_code='OWNER', membership_type_status=Status.ACTIVE.value) membership.save() # Add the user to account_holders group KeycloakService.join_account_holders_group() # TODO Remove later, create payment settings now with default values AccountPaymentModel.create_from_dict({'org_id': org.id}) return Org(org)
def accept_invitation(invitation_id, user: UserService, origin, add_membership: bool = True, token_info: Dict = None): """Add user, role and org from the invitation to membership.""" current_app.logger.debug('>accept_invitation') invitation: InvitationModel = InvitationModel.find_invitation_by_id(invitation_id) if invitation is None: raise BusinessException(Error.DATA_NOT_FOUND, None) if invitation.invitation_status_code == 'ACCEPTED': raise BusinessException(Error.ACTIONED_INVITATION, None) if invitation.invitation_status_code == 'EXPIRED': raise BusinessException(Error.EXPIRED_INVITATION, None) if getattr(token_info, 'loginSource', None) is not None: # bcros comes with out token login_source = token_info.get('loginSource', None) if invitation.login_source != login_source: raise BusinessException(Error.INVALID_USER_CREDENTIALS, None) if add_membership: for membership in invitation.membership: membership_model = MembershipModel() membership_model.org_id = membership.org_id membership_model.user_id = user.identifier membership_model.membership_type = membership.membership_type # check to ensure an invitation for this user/org has not already been processed existing_membership = MembershipService \ .get_membership_for_org_and_user(org_id=membership_model.org_id, user_id=membership_model.user_id) if existing_membership: raise BusinessException(Error.DATA_ALREADY_EXISTS, None) org_model: OrgModel = OrgModel.find_by_org_id(membership.org_id) # GOVM users gets direct approval since they are IDIR users. membership_model.status = Invitation._get_status_based_on_org(org_model) membership_model.save() try: Invitation.notify_admin(user, invitation_id, membership_model.id, origin) except BusinessException as exception: current_app.logger.error('<send_notification_to_admin failed', exception.message) invitation.accepted_date = datetime.now() invitation.invitation_status = InvitationStatusModel.get_status_by_code('ACCEPTED') invitation.save() # Call keycloak to add the user to the group. if user: group_name: str = KeycloakService.join_users_group(token_info) KeycloakService.join_account_holders_group(user.keycloak_guid) if group_name == GROUP_GOV_ACCOUNT_USERS: # TODO Remove this if gov account users needs Terms of Use. tos_document = DocumentsModel.fetch_latest_document_by_type(DocumentType.TERMS_OF_USE.value) user.update_terms_of_use(token_info, True, tos_document.version_id) # Add contact to the user. user.add_contact(token_info, dict(email=token_info.get('email', None))) current_app.logger.debug('<accept_invitation') return Invitation(invitation)
def _modify_task(user): """Clone on-hold task to a new active task; handle user-based and org-based tasks.""" # find users org. ideally only one org org_list = MembershipModel.find_orgs_for_user(user.identifier) org: OrgModel = next(iter(org_list or []), None) if org: # check if there is any holding tasks # Find if the corresponding task is NEW_ACCOUNT_STAFF_REVIEW / GOVN type, clone and close it task_model: TaskModel = TaskModel.find_by_task_for_account( org.id, TaskStatus.HOLD.value) if task_model is None: # Else, find if there are any associated task of BCEID_ADMIN type, clone and close it task_model: TaskModel = TaskModel.find_by_user_and_status( org.id, TaskStatus.HOLD.value) if task_model: task_info = { 'name': org.name, 'relationshipId': task_model.relationship_id, 'relatedTo': user.identifier, 'dateSubmitted': task_model.date_submitted, 'relationshipType': task_model.relationship_type, 'type': task_model.type, 'action': task_model.action, 'status': TaskStatus.OPEN.value, 'relationship_status': TaskRelationshipStatus.PENDING_STAFF_REVIEW.value, 'account_id': task_model.account_id } new_task = TaskService.create_task(task_info=task_info, do_commit=False) # Send notification mail to staff review task from auth_api.services import Org as OrgService # pylint:disable=cyclic-import, import-outside-toplevel if task_model.relationship_type == TaskRelationshipType.USER.value: OrgService.send_staff_review_account_reminder( relationship_id=user.identifier, task_relationship_type=TaskRelationshipType.USER.value) else: OrgService.send_staff_review_account_reminder( relationship_id=org.id) remarks = [ f'User Uploaded New affidavit .Created New task id: {new_task.identifier}' ] TaskService.close_task(task_model.id, remarks)
def accept_invitation(invitation_id, user, origin, add_membership: bool = True): """Add user, role and org from the invitation to membership.""" current_app.logger.debug('>accept_invitation') invitation: InvitationModel = InvitationModel.find_invitation_by_id( invitation_id) if invitation is None: raise BusinessException(Error.DATA_NOT_FOUND, None) if invitation.invitation_status_code == 'ACCEPTED': raise BusinessException(Error.ACTIONED_INVITATION, None) if invitation.invitation_status_code == 'EXPIRED': raise BusinessException(Error.EXPIRED_INVITATION, None) if add_membership: for membership in invitation.membership: membership_model = MembershipModel() membership_model.org_id = membership.org_id membership_model.user_id = user.identifier membership_model.membership_type = membership.membership_type # check to ensure an invitation for this user/org has not already been processed existing_membership = MembershipService \ .get_membership_for_org_and_user(org_id=membership_model.org_id, user_id=membership_model.user_id) if existing_membership: raise BusinessException(Error.DATA_ALREADY_EXISTS, None) # user needs to get approval is_auto_approval = OrgSettingsModel.is_admin_auto_approved_invitees( membership.org_id) if is_auto_approval: membership_model.status = Status.ACTIVE.value else: membership_model.status = Status.PENDING_APPROVAL.value membership_model.save() if not is_auto_approval: try: Invitation.notify_admin(user, invitation_id, membership_model.id, origin) except BusinessException as exception: current_app.logger.error( '<send_notification_to_admin failed', exception.message) invitation.accepted_date = datetime.now() invitation.invitation_status = InvitationStatusModel.get_status_by_code( 'ACCEPTED') invitation.save() current_app.logger.debug('<accept_invitation') return Invitation(invitation)
def test_delete_user_is_member_returns_204(client, jwt, session, keycloak_mock): # pylint:disable=unused-argument """Test if the user is the member of a team assert status is 204.""" user_model = factory_user_model(user_info=TestUserInfo.user_test) contact = factory_contact_model() contact_link = ContactLinkModel() contact_link.contact = contact contact_link.user = user_model contact_link.commit() user_model2 = factory_user_model(user_info=TestUserInfo.user2) contact = factory_contact_model() contact_link = ContactLinkModel() contact_link.contact = contact contact_link.user = user_model2 contact_link.commit() claims = copy.deepcopy(TestJwtClaims.public_user_role.value) claims['sub'] = str(user_model2.keycloak_guid) org = OrgService.create_org(TestOrgInfo.org1, user_id=user_model.id, token_info=claims) org_dictionary = org.as_dict() org_id = org_dictionary['id'] entity = factory_entity_model(entity_info=TestEntityInfo.entity_lear_mock) affiliation = AffiliationModel(org_id=org_id, entity_id=entity.id) affiliation.save() membership = MembershipModel(org_id=org_id, user_id=user_model2.id, membership_type_code='USER', membership_type_status=Status.ACTIVE.value) membership.save() headers = factory_auth_header(jwt=jwt, claims=claims) rv = client.delete('/api/v1/users/@me', headers=headers, content_type='application/json') assert rv.status_code == http_status.HTTP_204_NO_CONTENT
def find_membership_by_id(cls, membership_id, token_info: Dict = None): """Retrieve a membership record by id.""" membership = MembershipModel.find_membership_by_id(membership_id) if membership: # Ensure that this user is an ADMIN or OWNER on the org associated with this membership # or that the membership is for the current user if membership.user.username != token_info.get('username'): check_auth(org_id=membership.org_id, token_info=token_info, one_of_roles=(ADMIN, OWNER)) return Membership(membership) return None
def find_membership_by_id(cls, membership_id, **kwargs): """Retrieve a membership record by id.""" user_from_context: UserContext = kwargs['user_context'] membership = MembershipModel.find_membership_by_id(membership_id) if membership: # Ensure that this user is an COORDINATOR or ADMIN on the org associated with this membership # or that the membership is for the current user if membership.user.username != user_from_context.user_name: check_auth(org_id=membership.org_id, one_of_roles=(COORDINATOR, ADMIN, STAFF)) return Membership(membership) return None
def delete_otp_for_user(user_name, origin_url: str = None): """Reset the OTP of the user.""" # TODO - handle when the multiple teams implemented for bceid.. user = UserModel.find_by_username(user_name) membership = MembershipModel.find_membership_by_userid(user.id) org_id = membership.org_id check_auth(org_id=org_id, one_of_roles=(ADMIN, COORDINATOR, STAFF)) try: KeycloakService.reset_otp(str(user.keycloak_guid)) User.send_otp_authenticator_reset_notification(user.email, origin_url, org_id) except HTTPError as err: current_app.logger.error('update_user in keycloak failed {}', err) raise BusinessException(Error.UNDEFINED_ERROR, err) from err
def accept_invitation(invitation_id, user, origin, add_membership: bool = True, token_info: Dict = None): """Add user, role and org from the invitation to membership.""" current_app.logger.debug('>accept_invitation') invitation: InvitationModel = InvitationModel.find_invitation_by_id( invitation_id) if invitation is None: raise BusinessException(Error.DATA_NOT_FOUND, None) if invitation.invitation_status_code == 'ACCEPTED': raise BusinessException(Error.ACTIONED_INVITATION, None) if invitation.invitation_status_code == 'EXPIRED': raise BusinessException(Error.EXPIRED_INVITATION, None) if getattr(token_info, 'loginSource', None) is not None: # bcros comes with out token login_source = token_info.get('loginSource', None) if invitation.login_source != login_source: raise BusinessException(Error.INVALID_USER_CREDENTIALS, None) if add_membership: for membership in invitation.membership: membership_model = MembershipModel() membership_model.org_id = membership.org_id membership_model.user_id = user.identifier membership_model.membership_type = membership.membership_type # check to ensure an invitation for this user/org has not already been processed existing_membership = MembershipService \ .get_membership_for_org_and_user(org_id=membership_model.org_id, user_id=membership_model.user_id) if existing_membership: raise BusinessException(Error.DATA_ALREADY_EXISTS, None) membership_model.status = Status.PENDING_APPROVAL.value membership_model.save() try: Invitation.notify_admin(user, invitation_id, membership_model.id, origin) except BusinessException as exception: current_app.logger.error( '<send_notification_to_admin failed', exception.message) invitation.accepted_date = datetime.now() invitation.invitation_status = InvitationStatusModel.get_status_by_code( 'ACCEPTED') invitation.save() current_app.logger.debug('<accept_invitation') return Invitation(invitation)
def test_create_user_and_add_membership_owner_skip_auth_mode(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that an owner can be added as anonymous.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) membership = [TestAnonymousMembership.generate_random_user(ADMIN)] users = UserService.create_user_and_add_membership(membership, org.id, single_mode=True) assert len(users['users']) == 1 assert users['users'][0]['username'] == IdpHint.BCROS.value + '/' + membership[0]['username'] assert users['users'][0]['type'] == Role.ANONYMOUS_USER.name members = MembershipModel.find_members_by_org_id(org.id) # only one member should be there since its a STAFF created org assert len(members) == 1 assert members[0].membership_type_code == ADMIN
def get_orgs(user_id): """Return the orgs associated with this user.""" # TODO DO_NOT_USE this def if there is a database transaction involved, # as the below logic removes object from model orgs = MembershipModel.find_orgs_for_user(user_id) # because members are fetched using backpopulates,cant add these conditions programmatically. # so resorting to manually looping # noqa:E501 for org in orgs: # user can have multiple memberships.if the user getting denied first and added again, # it will be multiple memberships..filter out denied records # noqa:E501 # fix for https://github.com/bcgov/entity/issues/1951 # noqa:E501 org.members = list( filter(lambda member: (member.user_id == user_id and (member.status in VALID_STATUSES)), org.members)) return orgs
def delete_user(token): """Delete User Profile. Does the following 1) Find the user using token 2) Find all org membership for the user 3) Check if the current user is the only owner for any org - If yes, deny the action 4) Mark the membership as inactive on all orgs for the user 5) Delete the contact information for the user and the accepted terms of service 6) Mark the user record as inactive """ current_app.logger.debug('<delete_user') user: UserModel = UserModel.find_by_jwt_token(token) if not user: raise BusinessException(Error.DATA_NOT_FOUND, None) if user.status == UserStatus.INACTIVE.value: raise BusinessException(Error.DELETE_FAILED_INACTIVE_USER, None) user_orgs: List[OrgModel] = MembershipModel.find_orgs_for_user(user.id) current_app.logger.info('Found {} orgs for the user'.format( len(user_orgs) if user_orgs else 0)) if user_orgs: for org in user_orgs: current_app.logger.debug( f'Org : {org.name}, Status : {org.status_code}') if org.status_code == Status.ACTIVE.name: User.__remove_org_membership(org, user.id) # Delete contact User.__delete_contact(user=user) # Set the user status as inactive user.status = UserStatus.INACTIVE.value # Remove accepted terms user.terms_of_use_accepted_version = None user.terms_of_use_version = None user.is_terms_of_use_accepted = False user.save() # Remove user from account_holders group KeycloakService.remove_from_account_holders_group(user.keycloak_guid) current_app.logger.debug('<delete_user')