def send_invitation(invitation: InvitationModel, org_name, user, app_url): """Send the email notification.""" current_app.logger.debug('<send_invitation') subject = '[BC Registries & Online Services] {} {} has invited you to join a team'.format( user['firstname'], user['lastname']) sender = CONFIG.MAIL_FROM_ID recipient = invitation.recipient_email token_confirm_url = '{}/validatetoken/{}'.format( app_url, invitation.token) template = ENV.get_template( 'email_templates/business_invitation_email.html') sent_response = send_email( subject, sender, recipient, template.render( invitation=invitation, url=token_confirm_url, user=user, org_name=org_name, logo_url=f'{app_url}/{CONFIG.REGISTRIES_LOGO_IMAGE_NAME}')) if not sent_response: invitation.invitation_status_code = 'FAILED' invitation.save() current_app.logger.debug('>send_invitation failed') raise BusinessException(Error.FAILED_INVITATION, None) current_app.logger.debug('>send_invitation')
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 send_invitation(invitation: InvitationModel, org_name, user, app_url): """Send the email notification.""" current_app.logger.debug('<send_invitation') mail_configs = Invitation.get_invitation_configs( invitation.type, org_name) subject = mail_configs.get('subject').format(user['firstname'], user['lastname']) sender = CONFIG.MAIL_FROM_ID recipient = invitation.recipient_email token_confirm_url = '{}/{}/{}'.format( app_url, mail_configs.get('token_confirm_path'), invitation.token) template = ENV.get_template( f"email_templates/{mail_configs.get('template_name')}.html") sent_response = send_email( subject, sender, recipient, template.render( invitation=invitation, url=token_confirm_url, user=user, org_name=org_name, logo_url=f'{app_url}/{CONFIG.REGISTRIES_LOGO_IMAGE_NAME}')) if not sent_response: invitation.invitation_status_code = 'FAILED' invitation.save() current_app.logger.debug('>send_invitation failed') raise BusinessException(Error.FAILED_INVITATION, None) current_app.logger.debug('>send_invitation')
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_by_org_id(org_id, status, token_info: Dict = None): """Get invitations for an org.""" check_auth(token_info, org_id=org_id, one_of_roles=(OWNER, ADMIN)) collection = [] if status == 'ALL': invitations = InvitationModel.find_invitations_by_org(org_id) else: invitations = InvitationModel.find_pending_invitations_by_org( org_id) for invitation in invitations: collection.append(Invitation(invitation).as_dict()) return collection
def create_invitation(invitation_info: Dict, user, token_info: Dict, invitation_origin): """Create a new invitation.""" # Ensure that the current user is ADMIN or COORDINATOR on each org being invited to context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH org_id = invitation_info['membership'][0]['orgId'] # get the org and check the access_type org = OrgModel.find_by_org_id(org_id) if not org: raise BusinessException(Error.DATA_NOT_FOUND, None) check_auth(token_info, org_id=org_id, one_of_roles=(ADMIN, COORDINATOR, STAFF)) org_name = org.name invitation_type = InvitationType.DIRECTOR_SEARCH.value if org.access_type == AccessType.ANONYMOUS.value \ else InvitationType.STANDARD.value if org.access_type == AccessType.ANONYMOUS.value: # anonymous account never get bceid or bcsc choices mandatory_login_source = LoginSource.BCROS.value else: default_login_option_based_on_accesstype = LoginSource.BCSC.value if \ org.access_type == AccessType.REGULAR.value else LoginSource.BCEID.value role = invitation_info['membership'][0]['membershipType'] account_login_options = AccountLoginOptionsModel.find_active_by_org_id(org.id) mandatory_login_source = LoginSource.BCSC.value if \ role == ADMIN else getattr(account_login_options, 'login_source', default_login_option_based_on_accesstype) invitation = InvitationModel.create_from_dict(invitation_info, user.identifier, invitation_type) confirmation_token = Invitation.generate_confirmation_token(invitation.id, invitation.type) invitation.token = confirmation_token invitation.login_source = mandatory_login_source invitation.save() Invitation.send_invitation(invitation, org_name, user.as_dict(), '{}/{}'.format(invitation_origin, context_path), mandatory_login_source) return Invitation(invitation)
def create_invitation(invitation_info: Dict, user, token_info: Dict, invitation_origin): """Create a new invitation.""" # Ensure that the current user is OWNER or ADMIN on each org being invited to context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH for membership in invitation_info['membership']: org_id = membership['orgId'] if invitation_info.get( 'type') == InvitationType.DIRECTOR_SEARCH.value: check_auth(token_info, org_id=org_id, equals_role=STAFF_ADMIN) else: check_auth(token_info, org_id=org_id, one_of_roles=(OWNER, ADMIN)) # TODO doesnt work when invited to multiple teams.. Re-work the logic when multiple teams introduced org_name = OrgModel.find_by_org_id( invitation_info['membership'][0]['orgId']).name invitation = InvitationModel.create_from_dict(invitation_info, user.identifier) confirmation_token = Invitation.generate_confirmation_token( invitation.id, invitation.type) invitation.token = confirmation_token invitation.save() Invitation.send_invitation( invitation, org_name, user.as_dict(), '{}/{}'.format(invitation_origin, context_path)) return Invitation(invitation)
def create_invitation(invitation_info: Dict, user, token_info: Dict, invitation_origin): """Create a new invitation.""" # Ensure that the current user is OWNER or ADMIN on each org being invited to context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH org_id = invitation_info['membership'][0]['orgId'] # get the org and check the access_type org = OrgModel.find_by_org_id(org_id) if not org: raise BusinessException(Error.DATA_NOT_FOUND, None) if org.access_type == AccessType.ANONYMOUS.value: check_auth(token_info, org_id=org_id, equals_role=STAFF_ADMIN) elif org.access_type == AccessType.BCSC.value: check_auth(token_info, org_id=org_id, one_of_roles=(OWNER, ADMIN)) org_name = org.name invitation_type = InvitationType.DIRECTOR_SEARCH.value if org.access_type == AccessType.ANONYMOUS.value \ else InvitationType.STANDARD.value invitation = InvitationModel.create_from_dict(invitation_info, user.identifier, invitation_type) confirmation_token = Invitation.generate_confirmation_token( invitation.id, invitation.type) invitation.token = confirmation_token invitation.save() Invitation.send_invitation( invitation, org_name, user.as_dict(), '{}/{}'.format(invitation_origin, context_path)) return Invitation(invitation)
def validate_token(token): """Check whether the passed token is valid.""" serializer = URLSafeTimedSerializer(CONFIG.EMAIL_TOKEN_SECRET_KEY) token_valid_for = int( CONFIG.TOKEN_EXPIRY_PERIOD ) * 3600 * 24 if CONFIG.TOKEN_EXPIRY_PERIOD else 3600 * 24 * 7 try: invitation_id = serializer.loads( token, salt=CONFIG.EMAIL_SECURITY_PASSWORD_SALT, max_age=token_valid_for).get('id') except: # noqa: E722 raise BusinessException(Error.EXPIRED_INVITATION, None) 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) return Invitation(invitation)
def notify_admin(user, invitation_id, membership_id, invitation_origin): """Admins should be notified if user has responded to invitation.""" current_app.logger.debug('<notify_admin') admin_list = UserService.get_admins_for_membership(membership_id) invitation: InvitationModel = InvitationModel.find_invitation_by_id( invitation_id) context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH # Don't send email in case no admin exist in the org. (staff sent invitation) if len(admin_list) >= 1: admin_emails = ','.join([ str(x.contacts[0].contact.email) for x in admin_list if x.contacts ]) else: # No admin, find Sender email to notify sender (staff) admin_emails = invitation.sender.email if admin_emails != '': Invitation.send_admin_notification( user.as_dict(), '{}/{}'.format(invitation_origin, context_path), admin_emails, invitation.membership[0].org.name, invitation.membership[0].org.id) current_app.logger.debug('>notify_admin') return Invitation(invitation)
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 test_find_invitation_by_id(session): # pylint:disable=unused-argument """Assert that an Invitation can retrieved by its id.""" invitation = factory_invitation_model(session=session, status='PENDING') session.add(invitation) session.commit() retrieved_invitation = InvitationModel.find_invitation_by_id(invitation.id) assert retrieved_invitation assert retrieved_invitation.id == invitation.id
def test_invitations_by_status(session): # pylint:disable=unused-argument """Assert that an Invitation can retrieved by the user id.""" invitation = factory_invitation_model(session=session, status='PENDING') session.add(invitation) session.commit() retrieved_invitation = InvitationModel.find_invitations_by_status( invitation.sender_id, 'FAILED') assert len(retrieved_invitation) == 0
def delete_invitation(invitation_id, token_info: Dict = None): """Delete the specified invitation.""" # Ensure that the current user is ADMIN or COORDINATOR for each org in the invitation invitation = InvitationModel.find_invitation_by_id(invitation_id) if invitation is None: raise BusinessException(Error.DATA_NOT_FOUND, None) for membership in invitation.membership: org_id = membership.org_id check_auth(token_info, org_id=org_id, one_of_roles=(ADMIN, COORDINATOR, STAFF)) invitation.delete()
def send_invitation( invitation: InvitationModel, org_name, org_id, user, # pylint: disable=too-many-arguments app_url, login_source, org_status=None, query_params: Dict[str, any] = None): """Send the email notification.""" current_app.logger.debug('<send_invitation') mail_configs = Invitation._get_invitation_configs( org_name, login_source, org_status) recipient = invitation.recipient_email token_confirm_url = f"{app_url}/{mail_configs.get('token_confirm_path')}/{invitation.token}" if query_params: token_confirm_url += f'?{urlencode(query_params)}' role = invitation.membership[0].membership_type.display_name data = { 'accountId': org_id, 'emailAddresses': recipient, 'contextUrl': token_confirm_url, 'userFirstName': user.get('firstname', None), 'userLastName': user.get('lastname', None), 'orgName': org_name, 'role': role } try: publish_to_mailer( notification_type=mail_configs.get('notification_type'), org_id=org_id, data=data) except BusinessException as exception: invitation.invitation_status_code = 'FAILED' invitation.save() current_app.logger.debug('>send_invitation failed') current_app.logger.debug(exception) raise BusinessException(Error.FAILED_INVITATION, None) from exception current_app.logger.debug('>send_invitation')
def test_find_pending_invitations_by_org(session): # pylint:disable=unused-argument """Assert that an Invitation can retrieved by the org id.""" invitation = factory_invitation_model(session=session, status='PENDING') session.add(invitation) session.commit() retrieved_invitation = InvitationModel.find_pending_invitations_by_org( invitation.membership[0].org_id) assert len(retrieved_invitation) == 1 assert retrieved_invitation[ 0].recipient_email == invitation.recipient_email
def test_create_from_dict_no_schema(session): # pylint:disable=unused-argument """Assert that an Entity can not be created without schema.""" user = User(username='******', keycloak_guid='1b20db59-19a0-4727-affe-c6f64309fd04') session.add(user) session.commit() result_invitation = InvitationModel.create_from_dict( None, user.id, 'STANDARD') assert result_invitation is None
def test_find_invitations_by_user(session): # pylint:disable=unused-argument """Assert that an Invitation can retrieved by the user id.""" invitation = factory_invitation_model(session=session, status='PENDING') session.add(invitation) session.commit() retrieved_invitation = InvitationModel.find_invitations_by_user( invitation.sender_id) assert len(retrieved_invitation) > 0 assert retrieved_invitation[ 0].recipient_email == invitation.recipient_email assert retrieved_invitation[0].token == invitation.token
def notify_admin(user, invitation_id, membership_id, invitation_origin): """Admins should be notified if user has responded to invitation.""" current_app.logger.debug('<notify_admin') admin_list = UserService.get_admins_for_membership(membership_id) invitation: InvitationModel = InvitationModel.find_invitation_by_id(invitation_id) context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH admin_emails = ','.join([str(x.contacts[0].contact.email) for x in admin_list if x.contacts]) Invitation.send_admin_notification(user.as_dict(), '{}/{}'.format(invitation_origin, context_path), admin_emails, invitation.membership[0].org.name) current_app.logger.debug('>notify_admin') return Invitation(invitation)
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 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_find_invitations_by_org(session): # pylint:disable=unused-argument """Assert that Invitations for a specified org can be retrieved.""" invitation = factory_invitation_model(session=session, status='PENDING') session.add(invitation) session.commit() found_invitations = InvitationModel.find_invitations_by_org( invitation.membership[0].org_id) assert found_invitations assert len(found_invitations) == 1 assert found_invitations[0].membership[0].org_id == invitation.membership[ 0].org_id assert invitation.invitation_status_code == 'PENDING'
def find_invitation_by_id(invitation_id, token_info: Dict = None): """Find an existing invitation with the provided id.""" if invitation_id is None: return None invitation = InvitationModel.find_invitation_by_id(invitation_id) if not invitation: return None # Ensure that the current user is an ADMIN or COORDINATOR on each org in the invite being retrieved for membership in invitation.membership: org_id = membership.org_id check_auth(token_info, org_id=org_id, one_of_roles=(ADMIN, COORDINATOR, STAFF)) return Invitation(invitation)
def test_send_invitation_exception(session, notify_mock, keycloak_mock): # pylint:disable=unused-argument """Send an existing invitation with exception.""" user = factory_user_model(TestUserInfo.user_test) user_dictionary = User(user).as_dict() org = OrgService.create_org(TestOrgInfo.org1, user_id=user.id) org_dictionary = org.as_dict() invitation_info = factory_invitation(org_dictionary['id']) invitation = InvitationModel.create_from_dict(invitation_info, user.id, 'STANDARD') with patch.object(notification, 'send_email', return_value=False): with pytest.raises(BusinessException) as exception: InvitationService.send_invitation(invitation, org_dictionary['name'], user_dictionary, '', '') assert exception.value.code == Error.FAILED_INVITATION.name
def factory_invitation_model(session, status, sent_date=datetime.now()): """Produce a templated invitation model.""" user = User(username='******', roles='{edit, uma_authorization, staff}', keycloak_guid='1b20db59-19a0-4727-affe-c6f64309fd04') session.add(user) session.commit() org_type = OrgTypeModel(code='TEST', desc='Test') session.add(org_type) session.commit() org_status = OrgStatusModel(code='TEST', desc='Test') session.add(org_status) session.commit() preferred_payment = PaymentTypeModel(code='TEST', desc='Test') session.add(preferred_payment) session.commit() org = OrgModel() org.name = 'Test Org' org.org_type = org_type org.org_status = org_status org.preferred_payment = preferred_payment org.save() invitation = InvitationModel() invitation.recipient_email = '*****@*****.**' invitation.sender = user invitation.sent_date = sent_date invitation.invitation_status_code = status invitation.token = 'ABCD' invitation_membership = InvitationMembershipModel() invitation_membership.org_id = org.id invitation_membership.membership_type_code = 'USER' invitation.membership.append(invitation_membership) invitation.save() return invitation
def create_invitation(invitation_info: Dict, user, # pylint: disable=too-many-locals token_info: Dict, invitation_origin): """Create a new invitation.""" # Ensure that the current user is ADMIN or COORDINATOR on each org being invited to context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH org_id = invitation_info['membership'][0]['orgId'] # get the org and check the access_type org: OrgModel = OrgModel.find_by_org_id(org_id) if not org: raise BusinessException(Error.DATA_NOT_FOUND, None) check_auth(token_info, org_id=org_id, one_of_roles=(ADMIN, COORDINATOR, STAFF)) org_name = org.name invitation_type = Invitation._get_inv_type(org) if org.access_type == AccessType.ANONYMOUS.value: # anonymous account never get bceid or bcsc choices mandatory_login_source = LoginSource.BCROS.value elif org.access_type == AccessType.GOVM.value: mandatory_login_source = LoginSource.STAFF.value else: default_login_option_based_on_accesstype = LoginSource.BCSC.value if \ org.access_type == AccessType.REGULAR.value else LoginSource.BCEID.value role = invitation_info['membership'][0]['membershipType'] account_login_options = AccountLoginOptionsModel.find_active_by_org_id(org.id) mandatory_login_source = LoginSource.BCSC.value if \ role == ADMIN else getattr(account_login_options, 'login_source', default_login_option_based_on_accesstype) invitation = InvitationModel.create_from_dict(invitation_info, user.identifier, invitation_type) confirmation_token = Invitation.generate_confirmation_token(invitation.id, invitation.type) invitation.token = confirmation_token invitation.login_source = mandatory_login_source invitation.save() Invitation.send_invitation(invitation, org_name, user.as_dict(), '{}/{}'.format(invitation_origin, context_path), mandatory_login_source, org_status=org.status_code) # notify admin if staff adds team members is_staff_access = token_info and 'staff' in token_info.get('realm_access', {}).get('roles', None) if is_staff_access and invitation_type == InvitationType.STANDARD.value: publish_to_mailer('teamMemberInvited', org_id) return Invitation(invitation)
def accept_invitation(invitation_id, user, origin): """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) # TODO : isnt this only one?remove for loop 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 # 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 accept_invitation(invitation_id, user: UserService, origin, add_membership: bool = True, **kwargs): """Add user, role and org from the invitation to membership.""" current_app.logger.debug('>accept_invitation') user_from_context: UserContext = kwargs['user_context'] 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 (login_source := user_from_context.login_source ) is not None: # bcros comes with out token if invitation.login_source != login_source: raise BusinessException(Error.INVALID_USER_CREDENTIALS, None)
def test_create_from_dict(session): # pylint:disable=unused-argument """Assert that an Entity can be created from schema.""" user = User(username='******', roles='{edit, uma_authorization, staff}', keycloak_guid='1b20db59-19a0-4727-affe-c6f64309fd04') session.add(user) session.commit() org_type = OrgTypeModel(code='TEST', desc='Test') session.add(org_type) session.commit() org_status = OrgStatusModel(code='TEST', desc='Test') session.add(org_status) session.commit() preferred_payment = PaymentTypeModel(code='TEST', desc='Test') session.add(preferred_payment) session.commit() org = OrgModel() org.name = 'Test Org' org.org_type = org_type org.org_status = org_status org.preferred_payment = preferred_payment org.save() invitation_info = { 'recipientEmail': '*****@*****.**', 'membership': [{ 'membershipType': 'USER', 'orgId': org.id }] } result_invitation = InvitationModel.create_from_dict( invitation_info, user.id, 'STANDARD') assert result_invitation.id is not None
def create_invitation(invitation_info: Dict, user, invitation_origin, **kwargs): # pylint: disable=too-many-locals """Create a new invitation.""" user_from_context: UserContext = kwargs['user_context'] # Ensure that the current user is ADMIN or COORDINATOR on each org being invited to context_path = CONFIG.AUTH_WEB_TOKEN_CONFIRM_PATH org_id = invitation_info['membership'][0]['orgId'] # get the org and check the access_type org: OrgModel = OrgModel.find_by_org_id(org_id) if not org: raise BusinessException(Error.DATA_NOT_FOUND, None) check_auth(org_id=org_id, one_of_roles=(ADMIN, COORDINATOR, STAFF)) org_name = org.name invitation_type = Invitation._get_inv_type(org) if org.access_type == AccessType.ANONYMOUS.value: # anonymous account never get bceid or bcsc choices mandatory_login_source = LoginSource.BCROS.value elif org.access_type == AccessType.GOVM.value: mandatory_login_source = LoginSource.STAFF.value else: default_login_option_based_on_accesstype = LoginSource.BCSC.value if \ org.access_type == AccessType.REGULAR.value else LoginSource.BCEID.value role = invitation_info['membership'][0]['membershipType'] account_login_options = AccountLoginOptionsModel.find_active_by_org_id( org.id) mandatory_login_source = LoginSource.BCSC.value if \ role == ADMIN else getattr(account_login_options, 'login_source', default_login_option_based_on_accesstype) invitation = InvitationModel.create_from_dict(invitation_info, user.identifier, invitation_type) confirmation_token = Invitation.generate_confirmation_token( invitation.id, invitation.type) invitation.token = confirmation_token invitation.login_source = mandatory_login_source invitation.save() Invitation.send_invitation(invitation, org_name, org.id, user.as_dict(), '{}/{}'.format(invitation_origin, context_path), mandatory_login_source, org_status=org.status_code) # notify admin if staff adds team members if user_from_context.is_staff( ) and invitation_type == InvitationType.STANDARD.value: try: current_app.logger.debug( '<send_team_member_invitation_notification') publish_to_mailer(notification_type='teamMemberInvited', org_id=org_id) current_app.logger.debug( 'send_team_member_invitation_notification>') except Exception as e: # noqa=B901 current_app.logger.error( '<send_team_member_invitation_notification failed') raise BusinessException(Error.FAILED_NOTIFICATION, None) from e return Invitation(invitation)