def test_get_or_create_user_exists(self): user = UserFactory() fetched, created = get_or_create_user(user.fullname, user.username, True) assert_false(created) assert_equal(user._id, fetched._id) assert_false('is_spam' in fetched.system_tags)
def authenticate(self, request): try: payload = jwt.decode( jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256' ) except (jwt.InvalidTokenError, TypeError): raise AuthenticationFailed # The JWT `data` payload is expected in the following structure. # # {"provider": { # "idp": "https://login.circle.edu/idp/shibboleth", # "id": "CIR", # "user": { # "middleNames": "", # "familyName": "", # "givenName": "", # "fullname": "Circle User", # "suffix": "", # "username": "******" # } # }} data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: raise AuthenticationFailed('Invalid institution id specified "{}"'.format(provider['id'])) username = provider['user']['username'] fullname = provider['user']['fullname'] user, created = get_or_create_user(fullname, username, reset_password=False) if created: user.given_name = provider['user'].get('givenName') user.middle_names = provider['user'].get('middleNames') user.family_name = provider['user'].get('familyName') user.suffix = provider['user'].get('suffix') user.date_last_login = datetime.utcnow() user.save() # User must be saved in order to have a valid _id user.register(username) send_mail( to_addr=user.username, mail=WELCOME_OSF4I, mimetype='html', user=user ) if institution not in user.affiliated_institutions: user.affiliated_institutions.append(institution) user.save() return user, None
def authenticate(self, request): try: payload = jwt.decode(jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256') except (jwt.InvalidTokenError, TypeError): raise AuthenticationFailed # The JWT `data` payload is expected in the following structure. # # {"provider": { # "idp": "https://login.circle.edu/idp/shibboleth", # "id": "CIR", # "user": { # "middleNames": "", # "familyName": "", # "givenName": "", # "fullname": "Circle User", # "suffix": "", # "username": "******" # } # }} data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: raise AuthenticationFailed( 'Invalid institution id specified "{}"'.format(provider['id'])) username = provider['user']['username'] fullname = provider['user']['fullname'] user, created = get_or_create_user(fullname, username, reset_password=False) if created: user.given_name = provider['user'].get('givenName') user.middle_names = provider['user'].get('middleNames') user.family_name = provider['user'].get('familyName') user.suffix = provider['user'].get('suffix') user.date_last_login = datetime.utcnow() user.save() # User must be saved in order to have a valid _id user.register(username) send_mail(to_addr=user.username, mail=WELCOME_OSF4I, mimetype='html', user=user) if institution not in user.affiliated_institutions: user.affiliated_institutions.append(institution) user.save() return user, None
def test_get_or_create_user_is_spam(self): fullname = 'John Deacon' username = '******' fetched, created = get_or_create_user(fullname, username, is_spam=True) assert_true(created) assert_equal(fetched.fullname, fullname) assert_equal(fetched.username, username) assert_true('is_spam' in fetched.system_tags)
def test_get_or_create_user_not_exists(self): fullname = 'Roger Taylor' username = '******' fetched, created = get_or_create_user(fullname, username, is_spam=False) assert_true(created) assert_equal(fetched.fullname, fullname) assert_equal(fetched.username, username) assert_false('is_spam' in fetched.system_tags)
def test_get_or_create_user_not_exists(self): fullname = 'Roger Taylor' username = '******' fetched, created = get_or_create_user(fullname, username, False) assert_true(created) assert_equal(fetched.fullname, fullname) assert_equal(fetched.username, username) assert_false('is_spam' in fetched.system_tags)
def test_get_or_create_user_is_spam(self): fullname = 'John Deacon' username = '******' fetched, created = get_or_create_user(fullname, username, True) assert_true(created) assert_equal(fetched.fullname, fullname) assert_equal(fetched.username, username) assert_true('is_spam' in fetched.system_tags)
def test_get_or_create_user_not_exists(self): fullname = 'Roger Taylor' username = '******' fetched, created = get_or_create_user(fullname, username, is_spam=False) fetched.save() # in order to access m2m fields, e.g. tags assert_true(created) assert_equal(fetched.fullname, fullname) assert_equal(fetched.username, username) assert_false('is_spam' in fetched.system_tags)
def test_get_or_create_user_is_spam(self): fullname = 'John Deacon' username = '******' fetched, created = get_or_create_user(fullname, username, is_spam=True) fetched.save() # in order to access m2m fields, e.g. tags assert_true(created) assert_equal(fetched.fullname, fullname) assert_equal(fetched.username, username) assert_true('is_spam' in fetched.system_tags)
def add_poster_by_email(conference, message): """ :param Conference conference: :param ConferenceMessage message: """ # Fail if no attachments if not message.attachments: return send_mail( message.sender_email, CONFERENCE_FAILED, fullname=message.sender_display, ) nodes_created = [] users_created = [] with transaction.atomic(): user, user_created = get_or_create_user( message.sender_display, message.sender_email, is_spam=message.is_spam, ) if user_created: user.save( ) # need to save in order to access m2m fields (e.g. tags) users_created.append(user) user.add_system_tag('osf4m') user.update_date_last_login() user.save() # must save the user first before accessing user._id set_password_url = web_url_for( 'reset_password_get', uid=user._id, token=user.verification_key_v2['token'], _absolute=True, ) else: set_password_url = None node, node_created = Node.objects.get_or_create( title__iexact=message.subject, is_deleted=False, _contributors__guids___id=user._id, defaults={ 'title': message.subject, 'creator': user }) if node_created: nodes_created.append(node) node.add_system_tag('osf4m') node.save() utils.provision_node(conference, message, node, user) utils.record_message(message, nodes_created, users_created) # Prevent circular import error from framework.auth import signals as auth_signals if user_created: auth_signals.user_confirmed.send(user) utils.upload_attachments(user, node, message.attachments) download_url = node.web_url_for( 'addon_view_or_download_file', path=message.attachments[0].filename, provider='osfstorage', action='download', _absolute=True, ) # Send confirmation email send_mail( message.sender_email, CONFERENCE_SUBMITTED, conf_full_name=conference.name, conf_view_url=web_url_for( 'conference_results', meeting=message.conference_name, _absolute=True, ), fullname=message.sender_display, user_created=user_created, set_password_url=set_password_url, profile_url=user.absolute_url, node_url=node.absolute_url, file_url=download_url, presentation_type=message.conference_category.lower(), is_spam=message.is_spam, ) if node_created and user_created: signals.osf4m_user_created.send(user, conference=conference, node=node)
def authenticate(self, request): """ Handle CAS institution authentication request. The JWT `data` payload is expected in the following structure: { "provider": { "idp": "", "id": "", "user": { "username": "", "fullname": "", "familyName": "", "givenName": "", "middleNames": "", "suffix": "", } } } :param request: the POST request :return: user, None if authentication succeed :raises: AuthenticationFailed if authentication fails """ try: payload = jwt.decode( jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256', ) except (jwt.InvalidTokenError, TypeError, jwe.exceptions.MalformedData): raise AuthenticationFailed data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: raise AuthenticationFailed( 'Invalid institution id specified "{}"'.format(provider['id'])) username = provider['user'].get('username') fullname = provider['user'].get('fullname') given_name = provider['user'].get('givenName') family_name = provider['user'].get('familyName') middle_names = provider['user'].get('middleNames') suffix = provider['user'].get('suffix') # use given name and family name to build full name if not provided if given_name and family_name and not fullname: fullname = given_name + ' ' + family_name # institution must provide `fullname`, otherwise we fail the authentication and inform sentry if not fullname: message = 'Institution login failed: fullname required' \ ' for user {} from institution {}'.format(username, provider['id']) sentry.log_message(message) raise AuthenticationFailed(message) # `get_or_create_user()` guesses names from fullname # replace the guessed ones if the names are provided from the authentication user, created = get_or_create_user(fullname, username, reset_password=False) if created: if given_name: user.given_name = given_name if family_name: user.family_name = family_name if middle_names: user.middle_names = middle_names if suffix: user.suffix = suffix user.update_date_last_login() # Relying on front-end validation until `accepted_tos` is added to the JWT payload user.accepted_terms_of_service = timezone.now() # save and register user user.save() user.register(username) # send confirmation email send_mail( to_addr=user.username, mail=WELCOME_OSF4I, mimetype='html', user=user, domain=DOMAIN, osf_support_email=OSF_SUPPORT_EMAIL, storage_flag_is_active=waffle.flag_is_active( request, features.STORAGE_I18N), ) if not user.is_affiliated_with_institution(institution): user.affiliated_institutions.add(institution) user.save() return user, None
def test_get_or_create_user_exists(self): user = UserFactory() fetched, created = get_or_create_user(user.fullname, user.username, is_spam=True) assert_false(created) assert_equal(user._id, fetched._id) assert_false('is_spam' in fetched.system_tags)
def authenticate(self, request): """ Handle CAS institution authentication request. The JWT `data` payload is expected in the following structure: { "provider": { "idp": "", "id": "", "user": { "username": "", "fullname": "", "familyName": "", "givenName": "", "middleNames": "", "suffix": "", "department": "", "isMemberOf": "", # Shared SSO "selectiveSsoFilter": "", # Selective SSO } } } Note that if authentication failed, HTTP 403 Forbidden is returned no matter what type of exception is raised. In this method, we use `AuthenticationFailed` when the payload is not correctly encrypted/encoded since it is the "authentication" between CAS and this endpoint. We use `PermissionDenied` for all other exceptions that happened afterwards. :param request: the POST request :return: user, None if authentication succeed :raises: AuthenticationFailed or PermissionDenied if authentication fails """ # Verify / decrypt / decode the payload try: payload = jwt.decode( jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256', ) except (jwt.InvalidTokenError, TypeError, jwe.exceptions.MalformedData): raise AuthenticationFailed( detail='InstitutionSsoRequestNotAuthorized') # Load institution and user data data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: message = 'Institution SSO Error: invalid institution ID [{}]'.format( provider['id']) logger.error(message) sentry.log_message(message) raise PermissionDenied(detail='InstitutionSsoInvalidInstitution') username = provider['user'].get('username') fullname = provider['user'].get('fullname') given_name = provider['user'].get('givenName') family_name = provider['user'].get('familyName') middle_names = provider['user'].get('middleNames') suffix = provider['user'].get('suffix') department = provider['user'].get('department') selective_sso_filter = provider['user'].get('selectiveSsoFilter') # Check selective login first if provider['id'] in INSTITUTION_SELECTIVE_SSO_MAP: if selective_sso_filter != INSTITUTION_SELECTIVE_SSO_MAP[ provider['id']]: message = f'Institution SSO Error: user [email={username}] is not allowed for ' \ f'institution SSO [id={institution._id}] due to selective SSO rules' logger.error(message) sentry.log_message(message) raise PermissionDenied( detail='InstitutionSsoSelectiveNotAllowed') logger.info( f'Institution SSO: selective SSO verified for user [email={username}] ' f'at institution [id={institution._id}]', ) # Check secondary institutions which uses the SSO of primary ones secondary_institution = None if provider['id'] in INSTITUTION_SHARED_SSO_MAP: switch_map = INSTITUTION_SHARED_SSO_MAP[provider['id']] criteria_type = switch_map.get('criteria') if criteria_type == 'attribute': attribute_name = switch_map.get('attribute') attribute_value = provider['user'].get(attribute_name) if attribute_value: secondary_institution_id = switch_map.get( 'institutions', {}, ).get(attribute_value) logger.info( 'Institution SSO: primary=[{}], secondary=[{}], ' 'username=[{}]'.format(provider['id'], secondary_institution_id, username)) secondary_institution = Institution.load( secondary_institution_id) if not secondary_institution: # Log errors and inform Sentry but do not raise an exception if OSF fails # to load the secondary institution from database message = 'Institution SSO Error: invalid secondary institution [{}]; ' \ 'primary=[{}], username=[{}]'.format(attribute_value, provider['id'], username) logger.error(message) sentry.log_message(message) else: # SSO from primary institution only logger.info( 'Institution SSO: primary=[{}], secondary=[None], ' 'username=[{}]'.format(provider['id'], username)) else: message = 'Institution SSO Error: invalid criteria [{}]; ' \ 'primary=[{}], username=[{}]'.format(criteria_type, provider['id'], username) logger.error(message) sentry.log_message(message) # Use given name and family name to build full name if it is not provided if given_name and family_name and not fullname: fullname = given_name + ' ' + family_name # Non-empty full name is required. Fail the auth and inform sentry if not provided. if not fullname: message = 'Institution SSO Error: missing fullname ' \ 'for user [{}] from institution [{}]'.format(username, provider['id']) logger.error(message) sentry.log_message(message) raise PermissionDenied(detail='InstitutionSsoMissingUserNames') # Get an existing user or create a new one. If a new user is created, the user object is # confirmed but not registered,which is temporarily of an inactive status. If an existing # user is found, it is also possible that the user is inactive (e.g. unclaimed, disabled, # unconfirmed, etc.). user, created = get_or_create_user(fullname, username, reset_password=False) # Existing but inactive users need to be either "activated" or failed the auth activation_required = False new_password_required = False if not created: try: drf.check_user(user) logger.info( 'Institution SSO: active user [{}]'.format(username)) except exceptions.UnclaimedAccountError: # Unclaimed user (i.e. a user that has been added as an unregistered contributor) user.unclaimed_records = {} activation_required = True # Unclaimed users have an unusable password when being added as an unregistered # contributor. Thus a random usable password must be assigned during activation. new_password_required = True logger.warning( 'Institution SSO: unclaimed contributor [{}]'.format( username)) except exceptions.UnconfirmedAccountError: if user.has_usable_password(): # Unconfirmed user from default username / password signup user.email_verifications = {} activation_required = True # Unconfirmed users already have a usable password set by the creator during # sign-up. However, it must be overwritten by a new random one so the creator # (if he is not the real person) can not access the account after activation. new_password_required = True logger.warning( 'Institution SSO: unconfirmed user [{}]'.format( username)) else: # Login take-over has not been implemented for unconfirmed user created via # external IdP login (ORCiD). message = 'Institution SSO Error: SSO is not eligible for an unconfirmed account [{}] ' \ 'created via IdP login'.format(username) sentry.log_message(message) logger.error(message) raise PermissionDenied( detail='InstitutionSsoAccountNotConfirmed') except exceptions.DeactivatedAccountError: # Deactivated user: login is not allowed for deactivated users message = 'Institution SSO Error: SSO is not eligible for a deactivated account: [{}]'.format( username) sentry.log_message(message) logger.error(message) raise PermissionDenied(detail='InstitutionSsoAccountDisabled') except exceptions.MergedAccountError: # Merged user: this shouldn't happen since merged users do not have an email message = 'Institution SSO Error: SSO is not eligible for a merged account: [{}]'.format( username) sentry.log_message(message) logger.error(message) raise PermissionDenied(detail='InstitutionSsoAccountMerged') except exceptions.InvalidAccountError: # Other invalid status: this shouldn't happen unless the user happens to be in a # temporary state. Such state requires more updates before the user can be saved # to the database. (e.g. `get_or_create_user()` creates a temporary-state user.) message = 'Institution SSO Error: SSO is not eligible for an inactive account [{}] ' \ 'with an unknown or invalid status'.format(username) sentry.log_message(message) logger.error(message) raise PermissionDenied(detail='InstitutionSsoInvalidAccount') else: logger.info('Institution SSO: new user [{}]'.format(username)) # The `department` field is updated each login when it was changed. user_guid = user.guids.first()._id if department: if user.department != department: user.department = department user.save() logger.info( 'Institution SSO: user w/ dept: user=[{}], email=[{}], inst=[{}], ' 'dept=[{}]'.format(user_guid, username, institution._id, department)) else: logger.info( 'Institution SSO: user w/o dept: user=[{}], email=[{}], ' 'inst=[{}]'.format(user_guid, username, institution._id)) # Both created and activated accounts need to be updated and registered if created or activation_required: if given_name: user.given_name = given_name if family_name: user.family_name = family_name if middle_names: user.middle_names = middle_names if suffix: user.suffix = suffix # Users claimed or confirmed via institution SSO should have their full name updated if activation_required: user.fullname = fullname user.update_date_last_login() # Register and save user password = str(uuid.uuid4()) if new_password_required else None user.register(username, password=password) user.save() # Send confirmation email for all three: created, confirmed and claimed send_mail( to_addr=user.username, mail=WELCOME_OSF4I, user=user, domain=DOMAIN, osf_support_email=OSF_SUPPORT_EMAIL, storage_flag_is_active=waffle.flag_is_active( request, features.STORAGE_I18N), ) # Affiliate the user to the primary institution if not previously affiliated if not user.is_affiliated_with_institution(institution): user.affiliated_institutions.add(institution) user.save() # Affiliate the user to the secondary institution if not previously affiliated if secondary_institution and not user.is_affiliated_with_institution( secondary_institution): user.affiliated_institutions.add(secondary_institution) user.save() return user, None
def test_get_or_create_user_with_blacklisted_domain(self): fullname = 'Kanye West' username = '******' with assert_raises(BlacklistedEmailError) as e: get_or_create_user(fullname, username, is_spam=True) assert_equal(str(e.exception), 'Invalid Email')
def dispatch(self, request, *args, **kwargs): eppn = request.environ['HTTP_AUTH_EPPN'] seps = eppn.split(SHIB_EPPN_SCOPING_SEPARATOR)[-1] institution = Institution.objects.filter( domains__contains=[str(seps)]).first() if not institution: return redirect('auth:login') if not eppn: message = 'login failed: permission denied.' logging.info(message) messages.error(self.request, message) return redirect('auth:login') eppn_user = get_user(eppn=eppn) if eppn_user: try: others = eppn_user.affiliated_institutions.exclude( id=institution.id).get() except Institution.DoesNotExist: pass else: eppn_user.affiliated_institutions.remove(others) eppn_user.affiliated_institutions.add(institution) if 'GakuninRDMAdmin' in request.environ['HTTP_AUTH_ENTITLEMENT']: # login success # code is below this if/else tree eppn_user.is_staff = True eppn_user.save() else: # login failure occurs and the screen transits to the error screen # not sure about this code eppn_user.is_staff = False eppn_user.save() message = 'login failed: permission denied.' logging.info(message) messages.error(self.request, message) return redirect('auth:login') else: if 'GakuninRDMAdmin' not in request.environ[ 'HTTP_AUTH_ENTITLEMENT']: message = 'login failed: no user with matching eppn' messages.error(self.request, message) return redirect('auth:login') else: new_user, created = get_or_create_user( request.environ['HTTP_AUTH_DISPLAYNAME'] or 'NO NAME', eppn, reset_password=False) USE_EPPN = login_by_eppn() if USE_EPPN: new_user.eppn = eppn new_user.have_email = False else: new_user.eppn = None new_user.have_email = True new_user.is_staff = True new_user.eppn = eppn new_user.have_email = False new_user.save() new_user.affiliated_institutions.add(institution) eppn_user = new_user guid = Guid.objects.get( object_id=eppn_user.id, content_type_id=ContentType.objects.get_for_model(OSFUser).id) if not userkey_generation_check(guid._id): userkey_generation(guid._id) login(request, eppn_user, backend='api.base.authentication.backends.ODMBackend') # Transit to the administrator's home screen return redirect(self.get_success_url())
def test_get_or_create_user_with_blacklisted_domain(self): fullname = 'Kanye West' username = '******' with assert_raises(ValidationError) as e: get_or_create_user(fullname, username, True) assert_equal(e.exception.message, 'Invalid Email')
def authenticate(self, request): """ Handle CAS institution authentication request. The JWT `data` payload is expected in the following structure: { "provider": { "idp": "", "id": "", "user": { "username": "", "fullname": "", "familyName": "", "givenName": "", "middleNames": "", "suffix": "", } } } :param request: the POST request :return: user, None if authentication succeed :raises: AuthenticationFailed if authentication fails """ # Verify / decrypt / decode the payload try: payload = jwt.decode( jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256', ) except (jwt.InvalidTokenError, TypeError, jwe.exceptions.MalformedData): raise AuthenticationFailed # Load institution and user data data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: raise AuthenticationFailed('Invalid institution id: "{}"'.format(provider['id'])) username = provider['user'].get('username') fullname = provider['user'].get('fullname') given_name = provider['user'].get('givenName') family_name = provider['user'].get('familyName') middle_names = provider['user'].get('middleNames') suffix = provider['user'].get('suffix') department = provider['user'].get('department') # Use given name and family name to build full name if it is not provided if given_name and family_name and not fullname: fullname = given_name + ' ' + family_name # Non-empty full name is required. Fail the auth and inform sentry if not provided. if not fullname: message = 'Institution login failed: fullname required for ' \ 'user "{}" from institution "{}"'.format(username, provider['id']) sentry.log_message(message) raise AuthenticationFailed(message) # Get an existing user or create a new one. If a new user is created, the user object is # confirmed but not registered,which is temporarily of an inactive status. If an existing # user is found, it is also possible that the user is inactive (e.g. unclaimed, disabled, # unconfirmed, etc.). user, created = get_or_create_user(fullname, username, reset_password=False) # Existing but inactive users need to be either "activated" or failed the auth activation_required = False new_password_required = False if not created: try: drf.check_user(user) logger.info('Institution SSO: active user "{}"'.format(username)) except exceptions.UnclaimedAccountError: # Unclaimed user (i.e. a user that has been added as an unregistered contributor) user.unclaimed_records = {} activation_required = True # Unclaimed users have an unusable password when being added as an unregistered # contributor. Thus a random usable password must be assigned during activation. new_password_required = True logger.info('Institution SSO: unclaimed contributor "{}"'.format(username)) except exceptions.UnconfirmedAccountError: if user.has_usable_password(): # Unconfirmed user from default username / password signup user.email_verifications = {} activation_required = True # Unconfirmed users already have a usable password set by the creator during # sign-up. However, it must be overwritten by a new random one so the creator # (if he is not the real person) can not access the account after activation. new_password_required = True logger.info('Institution SSO: unconfirmed user "{}"'.format(username)) else: # Login take-over has not been implemented for unconfirmed user created via # external IdP login (ORCiD). message = 'Institution SSO is not eligible for an unconfirmed account ' \ 'created via external IdP login: username = "******"'.format(username) sentry.log_message(message) logger.error(message) return None, None except exceptions.DeactivatedAccountError: # Deactivated user: login is not allowed for deactivated users message = 'Institution SSO is not eligible for a deactivated account: ' \ 'username = "******"'.format(username) sentry.log_message(message) logger.error(message) return None, None except exceptions.MergedAccountError: # Merged user: this shouldn't happen since merged users do not have an email message = 'Institution SSO is not eligible for a merged account: ' \ 'username = "******"'.format(username) sentry.log_message(message) logger.error(message) return None, None except exceptions.InvalidAccountError: # Other invalid status: this shouldn't happen unless the user happens to be in a # temporary state. Such state requires more updates before the user can be saved # to the database. (e.g. `get_or_create_user()` creates a temporary-state user.) message = 'Institution SSO is not eligible for an inactive account with ' \ 'an unknown or invalid status: username = "******"'.format(username) sentry.log_message(message) logger.error(message) return None, None else: logger.info('Institution SSO: new user "{}"'.format(username)) # The `department` field is updated each login when it was changed. if department and user.department != department: user.department = department user.save() # Both created and activated accounts need to be updated and registered if created or activation_required: if given_name: user.given_name = given_name if family_name: user.family_name = family_name if middle_names: user.middle_names = middle_names if suffix: user.suffix = suffix # Users claimed or confirmed via institution SSO should have their full name updated if activation_required: user.fullname = fullname user.update_date_last_login() # Relying on front-end validation until `accepted_tos` is added to the JWT payload user.accepted_terms_of_service = timezone.now() # Register and save user password = str(uuid.uuid4()) if new_password_required else None user.register(username, password=password) user.save() # Send confirmation email for all three: created, confirmed and claimed send_mail( to_addr=user.username, mail=WELCOME_OSF4I, mimetype='html', user=user, domain=DOMAIN, osf_support_email=OSF_SUPPORT_EMAIL, storage_flag_is_active=waffle.flag_is_active(request, features.STORAGE_I18N), ) # Affiliate the user if not previously affiliated if not user.is_affiliated_with_institution(institution): user.affiliated_institutions.add(institution) user.save() return user, None
def add_poster_by_email(conference, message): """ :param Conference conference: :param ConferenceMessage message: """ # Fail if no attachments if not message.attachments: return send_mail( message.sender_email, CONFERENCE_FAILED, fullname=message.sender_display, can_change_preferences=False, logo=settings.OSF_MEETINGS_LOGO ) with transaction.atomic(): user, user_created = get_or_create_user( message.sender_display, message.sender_email, is_spam=message.is_spam, ) if user_created: if utils.is_valid_email(user.fullname): user.fullname = user._id # Users cannot use an email as their full name user.save() # need to save in order to access m2m fields (e.g. tags) user.add_system_tag('osf4m') user.update_date_last_login() user.save() # must save the user first before accessing user._id set_password_url = web_url_for( 'reset_password_get', uid=user._id, token=user.verification_key_v2['token'], _absolute=True, ) else: set_password_url = None # Always create a new meeting node node = Node.objects.create( title=message.subject, creator=user ) node.add_system_tag('osf4m') node.save() utils.provision_node(conference, message, node, user) created_user = user if user_created else None utils.record_message(message, node, created_user) # Prevent circular import error from framework.auth import signals as auth_signals if user_created: auth_signals.user_confirmed.send(user) utils.upload_attachments(user, node, message.attachments) download_url = node.web_url_for( 'addon_view_or_download_file', path=message.attachments[0].filename, provider='osfstorage', action='download', _absolute=True, ) # Send confirmation email send_mail( message.sender_email, CONFERENCE_SUBMITTED, conf_full_name=conference.name, conf_view_url=web_url_for( 'conference_results', meeting=message.conference_name, _absolute=True, ), fullname=message.sender_display, user_created=user_created, set_password_url=set_password_url, profile_url=user.absolute_url, node_url=node.absolute_url, file_url=download_url, presentation_type=message.conference_category.lower(), is_spam=message.is_spam, can_change_preferences=False, logo=settings.OSF_MEETINGS_LOGO ) if user_created: signals.osf4m_user_created.send(user, conference=conference, node=node)
def test_get_or_create_user_with_blacklisted_domain(self): fullname = 'Kanye West' username = '******' with assert_raises(BlacklistedEmailError) as e: get_or_create_user(fullname, username, is_spam=True) assert_equal(e.exception.message, 'Invalid Email')
def add_poster_by_email(conference, message): """ :param Conference conference: :param ConferenceMessage message: """ # Fail if no attachments if not message.attachments: return send_mail( message.sender_email, CONFERENCE_FAILED, fullname=message.sender_display, ) created = [] with TokuTransaction(): user, user_created = get_or_create_user( message.sender_display, message.sender_email, message.is_spam, ) if user_created: created.append(user) user.system_tags.append('osf4m') set_password_url = web_url_for( 'reset_password_get', verification_key=user.verification_key, _absolute=True, ) user.date_last_login = datetime.utcnow() user.save() else: set_password_url = None node, node_created = utils.get_or_create_node(message.subject, user) if node_created: created.append(node) node.system_tags.append('osf4m') node.save() utils.provision_node(conference, message, node, user) utils.record_message(message, created) # Prevent circular import error from framework.auth import signals as auth_signals if user_created: auth_signals.user_confirmed.send(user) utils.upload_attachments(user, node, message.attachments) download_url = node.web_url_for( 'addon_view_or_download_file', path=message.attachments[0].filename, provider='osfstorage', action='download', _absolute=True, ) # Send confirmation email send_mail( message.sender_email, CONFERENCE_SUBMITTED, conf_full_name=conference.name, conf_view_url=web_url_for( 'conference_results', meeting=message.conference_name, _absolute=True, ), fullname=message.sender_display, user_created=user_created, set_password_url=set_password_url, profile_url=user.absolute_url, node_url=node.absolute_url, file_url=download_url, presentation_type=message.conference_category.lower(), is_spam=message.is_spam, ) if node_created and user_created: signals.osf4m_user_created.send(user, conference=conference, node=node)
def add_poster_by_email(conference, message): """ :param Conference conference: :param ConferenceMessage message: """ # Fail if no attachments if not message.attachments: return send_mail( message.sender_email, CONFERENCE_FAILED, fullname=message.sender_display, ) created = [] with TokuTransaction(): user, user_created = get_or_create_user( message.sender_display, message.sender_email, message.is_spam, ) if user_created: created.append(user) user.system_tags.append('osf4m') set_password_url = web_url_for( 'reset_password', verification_key=user.verification_key, _absolute=True, ) user.date_last_login = datetime.utcnow() user.save() else: set_password_url = None node, node_created = utils.get_or_create_node(message.subject, user) if node_created: created.append(node) node.system_tags.append('osf4m') node.save() utils.provision_node(conference, message, node, user) utils.record_message(message, created) # Prevent circular import error from framework.auth import signals as auth_signals if user_created: auth_signals.user_confirmed.send(user) utils.upload_attachments(user, node, message.attachments) download_url = node.web_url_for( 'addon_view_or_download_file', path=message.attachments[0].filename, provider='osfstorage', action='download', _absolute=True, ) # Send confirmation email send_mail( message.sender_email, CONFERENCE_SUBMITTED, conf_full_name=conference.name, conf_view_url=web_url_for( 'conference_results', meeting=message.conference_name, _absolute=True, ), fullname=message.sender_display, user_created=user_created, set_password_url=set_password_url, profile_url=user.absolute_url, node_url=node.absolute_url, file_url=download_url, presentation_type=message.conference_category.lower(), is_spam=message.is_spam, ) if node_created and user_created: signals.osf4m_user_created.send(user, conference=conference, node=node)
def authenticate(self, request): """ Handle CAS institution authentication request. The JWT `data` payload is expected in the following structure: { "provider": { "idp": "", "id": "", "user": { "username": "", "fullname": "", "familyName": "", "givenName": "", "middleNames": "", "suffix": "", } } } :param request: the POST request :return: user, None if authentication succeed :raises: AuthenticationFailed if authentication fails """ try: payload = jwt.decode( jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256', ) except (jwt.InvalidTokenError, TypeError): raise AuthenticationFailed data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: raise AuthenticationFailed('Invalid institution id specified "{}"'.format(provider['id'])) username = provider['user'].get('username') fullname = provider['user'].get('fullname') given_name = provider['user'].get('givenName') family_name = provider['user'].get('familyName') middle_names = provider['user'].get('middleNames') suffix = provider['user'].get('suffix') # use given name and family name to build full name if not provided if given_name and family_name and not fullname: fullname = given_name + ' ' + family_name # institution must provide `fullname`, otherwise we fail the authentication and inform sentry if not fullname: message = 'Institution login failed: fullname required' \ ' for user {} from institution {}'.format(username, provider['id']) sentry.log_message(message) raise AuthenticationFailed(message) # `get_or_create_user()` guesses names from fullname # replace the guessed ones if the names are provided from the authentication user, created = get_or_create_user(fullname, username, reset_password=False) if created: if given_name: user.given_name = given_name if family_name: user.family_name = family_name if middle_names: user.middle_names = middle_names if suffix: user.suffix = suffix user.update_date_last_login() # Relying on front-end validation until `accepted_tos` is added to the JWT payload user.accepted_terms_of_service = timezone.now() # save and register user user.save() user.register(username) # send confirmation email send_mail( to_addr=user.username, mail=WELCOME_OSF4I, mimetype='html', user=user, domain=DOMAIN, osf_support_email=OSF_SUPPORT_EMAIL, storage_flag_is_active=waffle.flag_is_active(request, features.STORAGE_I18N), ) if not user.is_affiliated_with_institution(institution): user.affiliated_institutions.add(institution) user.save() return user, None
def add_poster_by_email(conference, message): """ :param Conference conference: :param ConferenceMessage message: """ # Fail if no attachments if not message.attachments: return send_mail( message.sender_email, CONFERENCE_FAILED, fullname=message.sender_display, ) nodes_created = [] users_created = [] with transaction.atomic(): user, user_created = get_or_create_user( message.sender_display, message.sender_email, is_spam=message.is_spam, ) if user_created: user.save() # need to save in order to access m2m fields (e.g. tags) users_created.append(user) user.add_system_tag('osf4m') user.update_date_last_login() user.save() # must save the user first before accessing user._id set_password_url = web_url_for( 'reset_password_get', uid=user._id, token=user.verification_key_v2['token'], _absolute=True, ) else: set_password_url = None node, node_created = Node.objects.get_or_create( title__iexact=message.subject, is_deleted=False, _contributors__guids___id=user._id, defaults={ 'title': message.subject, 'creator': user } ) if node_created: nodes_created.append(node) node.add_system_tag('osf4m') node.save() utils.provision_node(conference, message, node, user) utils.record_message(message, nodes_created, users_created) # Prevent circular import error from framework.auth import signals as auth_signals if user_created: auth_signals.user_confirmed.send(user) utils.upload_attachments(user, node, message.attachments) download_url = node.web_url_for( 'addon_view_or_download_file', path=message.attachments[0].filename, provider='osfstorage', action='download', _absolute=True, ) # Send confirmation email send_mail( message.sender_email, CONFERENCE_SUBMITTED, conf_full_name=conference.name, conf_view_url=web_url_for( 'conference_results', meeting=message.conference_name, _absolute=True, ), fullname=message.sender_display, user_created=user_created, set_password_url=set_password_url, profile_url=user.absolute_url, node_url=node.absolute_url, file_url=download_url, presentation_type=message.conference_category.lower(), is_spam=message.is_spam, ) if node_created and user_created: signals.osf4m_user_created.send(user, conference=conference, node=node)