def test_as_dict(session): # pylint: disable=unused-argument """Assert that a user is rendered correctly as a dictionary.""" user_model = factory_user_model( username='******', roles='{edit,uma_authorization,basic}', keycloak_guid='1b20db59-19a0-4727-affe-c6f64309fd04') user = UserService(user_model) dictionary = user.as_dict() assert dictionary['username'] == 'testuser' assert dictionary['roles'] == '{edit,uma_authorization,basic}' assert dictionary[ 'keycloak_guid'] == '1b20db59-19a0-4727-affe-c6f64309fd04'
def test_user_find_by_username(session): # pylint: disable=unused-argument """Assert that a user can be found by username.""" user_model = factory_user_model() user = UserService(user_model) user = UserService.find_by_username(None) assert user is None user = UserService.find_by_username(TestUserInfo.user_test['username']) assert user is not None dictionary = user.as_dict() assert dictionary['username'] == TestUserInfo.user_test['username']
def test_user_find_by_username(session): # pylint: disable=unused-argument """Assert that a user can be found by username.""" user_model = factory_user_model( username='******', roles='{edit,uma_authorization,basic}', keycloak_guid='1b20db59-19a0-4727-affe-c6f64309fd04') user = UserService(user_model) user = UserService.find_by_username('testuser') assert user is not None dictionary = user.as_dict() assert dictionary['username'] == 'testuser'
def test_update_invitation(session, auth_mock, keycloak_mock, monkeypatch): # pylint:disable=unused-argument """Update the specified invitation with new data.""" with patch.object(InvitationService, 'send_invitation', return_value=None): user = factory_user_model(TestUserInfo.user_test) patch_token_info({'sub': user.keycloak_guid}, monkeypatch) org = OrgService.create_org(TestOrgInfo.org1, user_id=user.id) org_dictionary = org.as_dict() invitation_info = factory_invitation(org_dictionary['id']) new_invitation = InvitationService.create_invitation( invitation_info, User(user), '') updated_invitation = new_invitation.update_invitation(User(user), '').as_dict() assert updated_invitation['status'] == 'PENDING'
def test_create_user_and_add_same_user_name_error_in_kc( session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that same user name cannot be added twice.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) membership = [TestAnonymousMembership.generate_random_user(OWNER)] UserService.create_user_and_add_membership(membership, org.id, skip_auth=True) with pytest.raises(BusinessException) as exception: UserService.create_user_and_add_membership(membership, org.id, skip_auth=True) assert exception.value.code == Error.FAILED_ADDING_USER_IN_KEYCLOAK.name
def test_get_orgs(session): # pylint:disable=unused-argument """Assert that orgs for a user can be retrieved.""" user_model = factory_user_model( username='******', roles='{edit,uma_authorization,basic}', keycloak_guid='1b20db59-19a0-4727-affe-c6f64309fd04') user = UserService(user_model) OrgService.create_org(TEST_ORG_INFO, user_id=user.identifier) response = user.get_orgs() assert response['orgs'] assert len(response['orgs']) == 1 assert response['orgs'][0]['name'] == TEST_ORG_INFO['name']
def test_bcros_user_update_by_token(session): # pylint: disable=unused-argument """Assert that a user can be created by token.""" user_model = factory_user_model(TestUserInfo.user_bcros) user = UserService(user_model) dictionary = user.as_dict() assert dictionary.get('keycloakGuid', None) is None user = UserService.save_from_jwt_token(TestJwtClaims.anonymous_bcros_role) assert user is not None dictionary = user.as_dict() assert dictionary['username'] == TestJwtClaims.anonymous_bcros_role[ 'preferred_username'] assert dictionary['keycloakGuid'] == TestJwtClaims.anonymous_bcros_role[ 'sub']
def test_reset_password_by_member(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that the password cant be changed by member.""" 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) admin_claims = TestJwtClaims.get_test_real_user(user.keycloak_guid) membership = [TestAnonymousMembership.generate_random_user(USER)] users = UserService.create_user_and_add_membership(membership, org.id, token_info=admin_claims) user_name = users['users'][0]['username'] user_info = {'username': user_name, 'password': '******'} with pytest.raises(HTTPException) as excinfo: UserService.reset_password_for_anon_user(user_info, user_name, token_info=TestJwtClaims.public_user_role) assert excinfo.exception.code == 403
def test_reset_password(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that the password can be changed.""" 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)] users = UserService.create_user_and_add_membership(membership, org.id, token_info=claims) user_name = users['users'][0]['username'] user_info = {'username': user_name, 'password': '******'} kc_user = UserService.reset_password_for_anon_user(user_info, user_name, claims) # cant assert anything else since password wont be gotten back assert kc_user.user_name == user_name.replace(f'{IdpHint.BCROS.value}/', '').lower()
def test_create_user_and_add_membership_multiple_error_skip_auth_mode( session, auth_mock, keycloak_mock, monkeypatch): # pylint:disable=unused-argument """Assert that multiple user cannot be created in single_mode mode.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) membership = [ TestAnonymousMembership.generate_random_user(USER), TestAnonymousMembership.generate_random_user(COORDINATOR) ] with pytest.raises(BusinessException) as exception: patch_token_info(TestJwtClaims.public_user_role, monkeypatch) UserService.create_user_and_add_membership(membership, org.id, single_mode=True) assert exception.value.code == Error.INVALID_USER_CREDENTIALS.name
def test_create_user_and_add_membership_multiple_error_skip_auth_mode( session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that multiple user cannot be created in skip_auth mode.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) membership = [ TestAnonymousMembership.generate_random_user(MEMBER), TestAnonymousMembership.generate_random_user(ADMIN) ] with pytest.raises(BusinessException) as exception: UserService.create_user_and_add_membership(membership, org.id, TestJwtClaims.edit_role, skip_auth=True) assert exception.value.code == Error.INVALID_USER_CREDENTIALS.name
def test_update_contact_for_user(session): # pylint: disable=unused-argument """Assert that a contact can be updated for a user.""" user_with_token = TestUserInfo.user_test user_with_token['keycloak_guid'] = TestJwtClaims.user_test['sub'] factory_user_model(user_info=user_with_token) contact = UserService.add_contact(TestJwtClaims.user_test, TestContactInfo.contact1).as_dict() assert contact is not None updated_contact = UserService.update_contact(TestJwtClaims.user_test, TestContactInfo.contact2).as_dict() assert updated_contact is not None assert updated_contact['email'] == TestContactInfo.contact2['email']
def test_delete_bcros_valdiations(client, jwt, session, keycloak_mock): """Assert different conditions of user deletion.""" admin_user = TestUserInfo.user_bcros_active 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) owner_claims = TestJwtClaims.get_test_real_user(user.keycloak_guid) member = TestAnonymousMembership.generate_random_user(MEMBER) admin = TestAnonymousMembership.generate_random_user(ADMIN) membership = [member, admin] UserService.create_user_and_add_membership(membership, org.id, token_info=owner_claims) owner_headers = factory_auth_header(jwt=jwt, claims=owner_claims) member_username = IdpHint.BCROS.value + '/' + member['username'] admin_username = IdpHint.BCROS.value + '/' + admin['username'] admin_claims = TestJwtClaims.get_test_real_user(uuid.uuid4(), admin_username, access_ype=AccessType.ANONYMOUS.value) admin_headers = factory_auth_header(jwt=jwt, claims=admin_claims) member_claims = TestJwtClaims.get_test_real_user(uuid.uuid4(), member_username, access_ype=AccessType.ANONYMOUS.value) member_headers = factory_auth_header(jwt=jwt, claims=member_claims) # set up JWTS for member and admin client.post('/api/v1/users', headers=admin_headers, content_type='application/json') client.post('/api/v1/users', headers=member_headers, content_type='application/json') # delete only owner ;failure rv = client.delete(f"/api/v1/users/{admin_user['username']}", headers=owner_headers, content_type='application/json') assert rv.status_code == http_status.HTTP_401_UNAUTHORIZED # admin trying to delete member: Failure rv = client.delete(f'/api/v1/users/{member_username}', headers=admin_headers, content_type='application/json') assert rv.status_code == http_status.HTTP_401_UNAUTHORIZED # member delete admin: failure rv = client.delete(f'/api/v1/users/{admin_username}', headers=member_headers, content_type='application/json') assert rv.status_code == http_status.HTTP_401_UNAUTHORIZED # a self delete ;should work ;mimics leave team for anonymous user rv = client.delete(f'/api/v1/users/{member_username}', headers=member_headers, content_type='application/json') assert rv.status_code == http_status.HTTP_204_NO_CONTENT rv = client.delete(f'/api/v1/users/{admin_username}', headers=admin_headers, content_type='application/json') assert rv.status_code == http_status.HTTP_204_NO_CONTENT # add one more admin new_owner = TestAnonymousMembership.generate_random_user(OWNER) membership = [new_owner] UserService.create_user_and_add_membership(membership, org.id, token_info=owner_claims) rv = client.delete(f"/api/v1/users/{IdpHint.BCROS.value + '/' + new_owner['username']}", headers=owner_headers, content_type='application/json') assert rv.status_code == http_status.HTTP_204_NO_CONTENT
def test_update_invitation_verify_different_tokens(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Update the specified invitation with new data.""" with patch.object(InvitationService, 'send_invitation', return_value=None): user = factory_user_model(TestUserInfo.user_test) org = OrgService.create_org(TestOrgInfo.org1, user_id=user.id) org_dictionary = org.as_dict() invitation_info = factory_invitation(org_dictionary['id']) new_invitation = InvitationService.create_invitation(invitation_info, User(user), {}, '') old_token = new_invitation.as_dict().get('token') with freeze_time( lambda: datetime.now() + timedelta(seconds=1)): # to give time difference..or else token will be same.. updated_invitation = new_invitation.update_invitation(User(user), {}, '').as_dict() new_token = updated_invitation.get('token') assert old_token != new_token assert updated_invitation['status'] == 'PENDING'
def test_user_find_by_token(session): # pylint: disable=unused-argument """Assert that a user can be found by token.""" user_with_token = TestUserInfo.user_test user_with_token['keycloak_guid'] = TestJwtClaims.user_test['sub'] factory_user_model(user_info=user_with_token) found_user = UserService.find_by_jwt_token(None) assert found_user is None found_user = UserService.find_by_jwt_token(TestJwtClaims.user_test) assert found_user is not None dictionary = found_user.as_dict() assert dictionary['username'] == TestJwtClaims.user_test[ 'preferred_username'] assert dictionary['keycloakGuid'] == TestJwtClaims.user_test['sub']
def test_create_user_and_add_same_user_name_error_in_db( session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that same user name cannot be added twice.""" org = factory_org_model(org_info=TestOrgInfo.org_anonymous) user = factory_user_model(TestUserInfo.user_bcros) factory_membership_model(user.id, org.id) new_members = TestAnonymousMembership.generate_random_user(OWNER) new_members['username'] = user.username.replace(f'{IdpHint.BCROS.value}/', '') membership = [new_members] with pytest.raises(BusinessException) as exception: UserService.create_user_and_add_membership(membership, org.id, skip_auth=True) assert exception.value.code == Error.DATA_ALREADY_EXISTS.name
def test_validate_token_accepted(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Validate invalid invitation token.""" with patch.object(InvitationService, 'send_invitation', return_value=None): user = factory_user_model(TestUserInfo.user_test) org = OrgService.create_org(TestOrgInfo.org1, user_id=user.id) org_dictionary = org.as_dict() user_invitee = factory_user_model(TestUserInfo.user1) invitation_info = factory_invitation(org_dictionary['id']) new_invitation = InvitationService.create_invitation(invitation_info, User(user_invitee), {}, '').as_dict() confirmation_token = InvitationService.generate_confirmation_token(new_invitation['id']) InvitationService.accept_invitation(new_invitation['id'], User(user_invitee), '') with pytest.raises(BusinessException) as exception: InvitationService.validate_token(confirmation_token) assert exception.value.code == Error.ACTIONED_INVITATION.name
def post(): """Post a new org using the request body. If the org already exists, update the attributes. """ token = g.jwt_oidc_token_info request_json = request.get_json() valid_format, errors = schema_utils.validate(request_json, 'org') if not valid_format: return { 'message': schema_utils.serialize(errors) }, http_status.HTTP_400_BAD_REQUEST try: user = UserService.find_by_jwt_token(token) if user is None: response, status = {'message': 'Not authorized to perform this action'}, \ http_status.HTTP_401_UNAUTHORIZED return response, status bearer_token = request.headers['Authorization'].replace( 'Bearer ', '') response, status = OrgService.create_org( request_json, user.identifier, token, bearer_token=bearer_token).as_dict( ), http_status.HTTP_201_CREATED except BusinessException as exception: response, status = { 'code': exception.code, 'message': exception.message }, exception.status_code return response, status
def test_delete_contact_for_user(session): # pylint: disable=unused-argument """Assert that a contact can be deleted for a user.""" factory_user_model(user_info=TestUserInfo.user_test) user = UserService.add_contact(TestJwtClaims.user_test, TestContactInfo.contact1) assert user is not None dictionary = user.as_dict() assert dictionary['contacts'] assert len(dictionary['contacts']) == 1 updated_user = UserService.delete_contact(TestJwtClaims.user_test) assert updated_user is not None dictionary = updated_user.as_dict() assert dictionary.get('contacts') == []
def test_delete_contact_for_user(session): # pylint: disable=unused-argument """Assert that a contact can be deleted for a user.""" user_with_token = TestUserInfo.user_test user_with_token['keycloak_guid'] = TestJwtClaims.user_test['sub'] factory_user_model(user_info=user_with_token) contact = UserService.add_contact(TestJwtClaims.user_test, TestContactInfo.contact1).as_dict() assert contact is not None deleted_contact = UserService.delete_contact(TestJwtClaims.user_test).as_dict() assert deleted_contact is not None contacts = UserService.get_contacts(TestJwtClaims.user_test) assert contacts.get('contacts') == []
def test_create_invitation(session, auth_mock, keycloak_mock, monkeypatch): # pylint:disable=unused-argument """Assert that an Invitation can be created.""" with patch.object(InvitationService, 'send_invitation', return_value=None) as mock_notify: user = factory_user_model(TestUserInfo.user_test) patch_token_info({'sub': user.keycloak_guid}, monkeypatch) org = OrgService.create_org(TestOrgInfo.org1, user_id=user.id) org_dictionary = org.as_dict() invitation_info = factory_invitation(org_dictionary['id']) with patch.object(ActivityLogPublisher, 'publish_activity', return_value=None) as mock_alp: invitation = InvitationService.create_invitation( invitation_info, User(user), '') mock_alp.assert_called_with( Activity(action=ActivityAction.INVITE_TEAM_MEMBER.value, org_id=ANY, name=invitation_info['recipientEmail'], id=ANY, value='USER')) invitation_dictionary = invitation.as_dict() assert invitation_dictionary['recipient_email'] == invitation_info[ 'recipientEmail'] assert invitation_dictionary['id'] mock_notify.assert_called()
def test_user_save_by_token(session): # pylint: disable=unused-argument """Assert that a user can be created by token.""" user = UserService.save_from_jwt_token(TestJwtClaims.user_test) assert user is not None dictionary = user.as_dict() assert dictionary['username'] == TestJwtClaims.user_test['preferred_username'] assert dictionary['keycloakGuid'] == TestJwtClaims.user_test['sub']
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 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'] == membership[0]['username'] assert users['users'][0]['type'] == 'ANONYMOUS' assert users['users'][1]['username'] == 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 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 test_delete_contact_user_link(session, auth_mock, keycloak_mock): # pylint:disable=unused-argument """Assert that a contact can not be deleted if contact link exists.""" user_with_token = TestUserInfo.user_test user_with_token['keycloak_guid'] = TestJwtClaims.edit_role['sub'] user_model = factory_user_model(user_info=user_with_token) user = UserService(user_model) org = OrgService.create_org(TestOrgInfo.org1, user_id=user.identifier) org_dictionary = org.as_dict() org_id = org_dictionary['id'] contact = factory_contact_model() contact_link = ContactLinkModel() contact_link.contact = contact contact_link.user = user_model contact_link.org = org._model # pylint:disable=protected-access contact_link = contact_link.flush() contact_link.commit() deleted_contact = UserService.delete_contact(TestJwtClaims.edit_role) assert deleted_contact is None delete_contact_link = ContactLinkModel.find_by_user_id(user.identifier) assert not delete_contact_link exist_contact_link = ContactLinkModel.find_by_org_id(org_id) assert exist_contact_link
def patch(org_id, membership_id): # pylint:disable=unused-argument """Update a membership record with new member role.""" token = g.jwt_oidc_token_info role = request.get_json().get('role') membership_status = request.get_json().get('status') notify_user = request.get_json().get('notifyUser') updated_fields_dict = {} origin = request.environ.get('HTTP_ORIGIN', 'localhost') try: if role is not None: updated_role = MembershipService.get_membership_type_by_code(role) updated_fields_dict['membership_type'] = updated_role if membership_status is not None: updated_fields_dict['membership_status'] = \ MembershipService.get_membership_status_by_code(membership_status) membership = MembershipService.find_membership_by_id(membership_id, token) is_own_membership = membership.as_dict()['user']['username'] == \ UserService.find_by_jwt_token(token).as_dict()['username'] if not membership: response, status = {'message': 'The requested membership record could not be found.'}, \ http_status.HTTP_404_NOT_FOUND else: response, status = membership.update_membership(updated_fields=updated_fields_dict, token_info=token ).as_dict(), http_status.HTTP_200_OK # if user status changed to active , mail the user if membership_status == Status.ACTIVE.name: membership.send_notification_to_member(origin, NotificationType.MEMBERSHIP_APPROVED.value) elif notify_user and updated_role and updated_role.code != MEMBER and not is_own_membership: membership.send_notification_to_member(origin, NotificationType.ROLE_CHANGED.value) return response, status except BusinessException as exception: response, status = {'code': exception.code, 'message': exception.message}, exception.status_code return response, status
def test_user_find_by_username_no_model_object(session): # pylint: disable=unused-argument """Assert that the business can't be found with no model.""" username = TestUserInfo.user_test['username'] user = UserService.find_by_username(username) assert user is None
def test_delete_user_where_org_has_another_owner(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='ADMIN', membership_type_status=Status.ACTIVE.value) membership.save() membership.commit() # with pytest.raises(BusinessException) as exception: 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 test_user_find_by_username_missing_username(session): # pylint: disable=unused-argument """Assert that the business can't be found by incorrect username.""" user_model = factory_user_model(user_info=TestUserInfo.user_test) user = UserService(user_model) user = UserService.find_by_username('foo') assert user is None