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 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 test_create_user_add_membership_reenable(session, auth_mock, keycloak_mock, monkeypatch): # pylint:disable=unused-argument """Assert that an admin can add a 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) claims = TestJwtClaims.get_test_real_user(user.keycloak_guid) patch_token_info(claims, monkeypatch) anon_member = TestAnonymousMembership.generate_random_user(USER) membership = [anon_member] users = UserService.create_user_and_add_membership(membership, org.id) user_name = IdpHint.BCROS.value + '/' + membership[0]['username'] assert len(users['users']) == 1 assert users['users'][0]['username'] == user_name assert users['users'][0]['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 1 member assert len(members) == 2 # assert cant be readded users = UserService.create_user_and_add_membership(membership, org.id) assert users['users'][0]['http_status'] == 409 assert users['users'][0]['error'] == 'The username is already taken' # deactivate everything and try again anon_user = UserModel.find_by_username(user_name) anon_user.status = Status.INACTIVE.value anon_user.save() membership_model = MembershipModel.find_membership_by_userid(anon_user.id) membership_model.status = Status.INACTIVE.value update_user_request = KeycloakUser() update_user_request.user_name = membership[0]['username'] update_user_request.enabled = False KeycloakService.update_user(update_user_request) org2 = factory_org_model(org_info=TestOrgInfo.org_anonymous_2, org_type_info={'code': 'BASIC'}, org_status_info=None, payment_type_info=None) factory_membership_model(user.id, org2.id) factory_product_model(org2.id, product_code=ProductCode.DIR_SEARCH.value) users = UserService.create_user_and_add_membership(membership, org2.id) assert users['users'][0]['http_status'] == 409 assert users['users'][0]['error'] == 'The username is already taken' # add to same org.Should work users = UserService.create_user_and_add_membership(membership, org.id) 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
def delete_anonymous_user(user_name, token_info: Dict = None): """ Delete User Profile. 1) check if the token user is admin/owner of the current user 2) disable the user from kc 3) set user status as INACTIVE 4) set membership as inactive """ admin_user: UserModel = UserModel.find_by_jwt_token(token_info) if not admin_user: raise BusinessException(Error.DATA_NOT_FOUND, None) if admin_user.status == UserStatus.INACTIVE.value: raise BusinessException(Error.DELETE_FAILED_INACTIVE_USER, None) # handle validations. user = UserModel.find_by_username(user_name) membership = MembershipModel.find_membership_by_userid(user.id) org_id = membership.org_id is_valid_action = False # admin/owner deleteion admin_user_membership = MembershipModel.find_membership_by_user_and_org( admin_user.id, org_id) if admin_user_membership.membership_type_code in [ADMIN]: is_valid_action = True # staff admin deleteion is_staff_admin = token_info and Role.STAFF_CREATE_ACCOUNTS.value in token_info.get( 'realm_access').get('roles') if is_staff_admin: is_valid_action = True # self deletion if user.keycloak_guid == admin_user.keycloak_guid: is_valid_action = True # is the only owner getting deleted if is_valid_action and membership.membership_type_code == ADMIN: count_of_owners = MembershipModel.get_count_active_owner_org_id( org_id) if count_of_owners == 1: is_valid_action = False if not is_valid_action: raise BusinessException(Error.INVALID_USER_CREDENTIALS, None) user.is_terms_of_use_accepted = False user.status = UserStatus.INACTIVE.value user.save() membership.status = Status.INACTIVE.value membership.save() update_user_request = KeycloakUser() update_user_request.user_name = user_name.replace( IdpHint.BCROS.value + '/', '') update_user_request.enabled = False KeycloakService.update_user(update_user_request)
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 _update_relationship(self, origin_url: str = None): """Retrieve the relationship record and update the status.""" task_model: TaskModel = self._model current_app.logger.debug('<update_task_relationship ') is_approved: bool = task_model.relationship_status == TaskRelationshipStatus.ACTIVE.value is_hold: bool = task_model.status == TaskStatus.HOLD.value if task_model.relationship_type == TaskRelationshipType.ORG.value: # Update Org relationship org_id = task_model.relationship_id if not is_hold: self._update_org(is_approved=is_approved, org_id=org_id, origin_url=origin_url, task_action=task_model.action) else: # Task with ACCOUNT_REVIEW action cannot be put on hold if task_model.action != TaskAction.AFFIDAVIT_REVIEW.value: raise BusinessException(Error.INVALID_INPUT, None) # no updates on org yet.put the task on hold and send mail to user org: OrgModel = OrgModel.find_by_org_id(org_id) # send on-hold mail notification Task._notify_admin_about_hold(task_model, org=org) # TODO Update user.verified flag elif task_model.relationship_type == TaskRelationshipType.PRODUCT.value: # Update Product relationship product_subscription_id = task_model.relationship_id account_id = task_model.account_id self._update_product_subscription(is_approved=is_approved, product_subscription_id=product_subscription_id, org_id=account_id) elif task_model.relationship_type == TaskRelationshipType.USER.value: user_id = task_model.relationship_id if not is_hold: self._update_bceid_admin(is_approved=is_approved, user_id=user_id) else: user: UserModel = UserModel.find_by_id(user_id) membership = MembershipModel.find_membership_by_userid(user_id) # Send mail to admin about hold with reasons Task._notify_admin_about_hold(user=user, task_model=task_model, is_new_bceid_admin_request=True, membership_id=membership.id) # If action is affidavit review, mark the user as verified. if is_approved and task_model.action == TaskAction.AFFIDAVIT_REVIEW.value and task_model.user: task_model.user.verified = True task_model.user.save() current_app.logger.debug('>update_task_relationship ')
def _update_bceid_admin(is_approved: bool, user_id: int): """Approve/Reject BCeId Admin User and Affidavit.""" from auth_api.services import Affidavit # pylint:disable=cyclic-import, import-outside-toplevel current_app.logger.debug('<update_bceid_admin_to_org ') # Update user user: UserModel = UserModel.find_by_id(user_id) user.status = Status.ACTIVE.value if is_approved else Status.INACTIVE.value # Update membership membership = MembershipModel.find_membership_by_userid(user_id) membership.status = Status.ACTIVE.value if is_approved else Status.REJECTED.value # Update affidavit Affidavit.approve_or_reject_bceid_admin(admin_user_id=user_id, is_approved=is_approved, user=user) current_app.logger.debug('>update_bceid_admin_to_org ')
def create_user_and_add_membership( memberships: List[dict], org_id, token_info: Dict = None, # pylint: disable=too-many-locals, too-many-statements, too-many-branches single_mode: bool = False): """ Create user(s) in the DB and upstream keycloak. accepts a list of memberships ie.a list of objects with username,password and membershipTpe single_mode can be used if called method already perfomed the authenticaiton single_mode= true is used now incase of invitation for admin users scenarion other cases should be invoked with single_mode=false """ User._validate_and_throw_exception(memberships, org_id, single_mode, token_info) current_app.logger.debug('create_user') users = [] for membership in memberships: username = membership['username'] current_app.logger.debug(f'create user username: {username}') create_user_request = User._create_kc_user(membership) db_username = IdpHint.BCROS.value + '/' + username user_model = UserModel.find_by_username(db_username) re_enable_user = False existing_kc_user = KeycloakService.get_user_by_username(username) enabled_in_kc = getattr(existing_kc_user, 'enabled', True) if getattr(user_model, 'status', None) == Status.INACTIVE.value and not enabled_in_kc: membership_model = MembershipModel.find_membership_by_userid( user_model.id) re_enable_user = membership_model.org_id == org_id if user_model and not re_enable_user: current_app.logger.debug('Existing users found in DB') users.append( User._get_error_dict(username, Error.USER_ALREADY_EXISTS)) continue if membership.get('update_password_on_login', True): # by default , reset needed create_user_request.update_password_on_login() try: if re_enable_user: kc_user = KeycloakService.update_user(create_user_request) else: kc_user = KeycloakService.add_user( create_user_request, throw_error_if_exists=True) except BusinessException as err: current_app.logger.error( 'create_user in keycloak failed :duplicate user {}', err) users.append( User._get_error_dict(username, Error.USER_ALREADY_EXISTS)) continue except HTTPError as err: current_app.logger.error('create_user in keycloak failed {}', err) users.append( User._get_error_dict(username, Error.FAILED_ADDING_USER_ERROR)) continue try: if re_enable_user: user_model.status = Status.ACTIVE.value user_model.flush() membership_model.status = Status.ACTIVE.value membership_model.membership_type_code = membership[ 'membershipType'] membership_model.flush() else: user_model = User._create_new_user_and_membership( db_username, kc_user, membership, org_id) db.session.commit( ) # commit is for session ;need not to invoke for every object user_dict = User(user_model).as_dict() user_dict.update({ 'http_status': http_status.HTTP_201_CREATED, 'error': '' }) users.append(user_dict) except Exception as e: # pylint: disable=broad-except current_app.logger.error( 'Error on create_user_and_add_membership: {}', e) db.session.rollback() if re_enable_user: User._update_user_in_kc(create_user_request) else: KeycloakService.delete_user_by_username( create_user_request.user_name) users.append( User._get_error_dict(username, Error.FAILED_ADDING_USER_ERROR)) continue return {'users': users}