Beispiel #1
0
    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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
    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)
Beispiel #5
0
 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
Beispiel #6
0
    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 ')
Beispiel #7
0
    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 ')
Beispiel #8
0
    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}