def register_user(**kwargs): """ Register new user account. HTTP Method: POST :param-json str email1: :param-json str email2: :param-json str password: :param-json str fullName: :param-json str campaign: :raises: HTTPError(http.BAD_REQUEST) if validation fails or user already exists """ # Verify that email address match. # Note: Both `landing.mako` and `register.mako` already have this check on the form. Users can not submit the form # if emails do not match. However, this check should not be removed given we may use the raw api call directly. json_data = request.get_json() if str(json_data['email1']).lower() != str(json_data['email2']).lower(): raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Email addresses must match.')) # Verify that captcha is valid if settings.RECAPTCHA_SITE_KEY and not validate_recaptcha( json_data.get('g-recaptcha-response'), remote_ip=request.remote_addr): raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Invalid Captcha')) try: full_name = request.json['fullName'] full_name = strip_html(full_name) campaign = json_data.get('campaign') if campaign and campaign not in campaigns.get_campaigns(): campaign = None user = framework_auth.register_unconfirmed( request.json['email1'], request.json['password'], full_name, campaign=campaign, ) framework_auth.signals.user_registered.send(user) except (ValidationValueError, DuplicateEmailError): raise HTTPError( http.BAD_REQUEST, data=dict(message_long=language.ALREADY_REGISTERED.format( email=markupsafe.escape(request.json['email1'])))) except ValidationError as e: raise HTTPError(http.BAD_REQUEST, data=dict(message_long=e.message)) if settings.CONFIRM_REGISTRATIONS_BY_EMAIL: send_confirm_email(user, email=user.username) message = language.REGISTRATION_SUCCESS.format(email=user.username) return {'message': message} else: return {'message': 'You may now log in.'}
def validate_campaign(campaign): """ Non-view helper function that validates `campaign`. :param campaign: the campaign to validate :return: True if valid, False otherwise """ return campaign and campaign in campaigns.get_campaigns()
def test_confirm_email_get_with_campaign(self): for key, value in campaigns.get_campaigns().items(): user = UnconfirmedUserFactory() user.system_tags.append(value.get('system_tag')) user.save() token = user.get_confirmation_token(user.username) kwargs = { 'uid': user._id, } with self.app.app.test_request_context(), mock_auth(user): res = auth_views.confirm_email_get(token, **kwargs) assert_equal(res.status_code, http.FOUND) assert_equal(res.location, campaigns.campaign_url_for(key))
def external_login_confirm_email_get(auth, uid, token): """ View for email confirmation links when user first login through external identity provider. HTTP Method: GET When users click the confirm link, they are expected not to be logged in. If not, they will be logged out first and redirected back to this view. After OSF verifies the link and performs all actions, they will be automatically logged in through CAS and redirected back to this view again being authenticated. :param auth: the auth context :param uid: the user's primary key :param token: the verification token """ user = OSFUser.load(uid) if not user: raise HTTPError(http.BAD_REQUEST) destination = request.args.get('destination') if not destination: raise HTTPError(http.BAD_REQUEST) # if user is already logged in if auth and auth.user: # if it is a wrong user if auth.user._id != user._id: return auth_logout(redirect_url=request.url) # if it is the expected user new = request.args.get('new', None) if destination in campaigns.get_campaigns(): # external domain takes priority campaign_url = campaigns.external_campaign_url_for(destination) if not campaign_url: campaign_url = campaigns.campaign_url_for(destination) return redirect(campaign_url) if new: status.push_status_message(language.WELCOME_MESSAGE, kind='default', jumbotron=True, trust=True) return redirect(web_url_for('dashboard')) # token is invalid if token not in user.email_verifications: raise HTTPError(http.BAD_REQUEST) verification = user.email_verifications[token] email = verification['email'] provider = verification['external_identity'].keys()[0] provider_id = verification['external_identity'][provider].keys()[0] # wrong provider if provider not in user.external_identity: raise HTTPError(http.BAD_REQUEST) external_status = user.external_identity[provider][provider_id] try: ensure_external_identity_uniqueness(provider, provider_id, user) except ValidationError as e: raise HTTPError(http.FORBIDDEN, e.message) if not user.is_registered: user.register(email) if not user.emails.filter(address=email.lower()): user.emails.create(address=email.lower()) user.date_last_logged_in = timezone.now() user.external_identity[provider][provider_id] = 'VERIFIED' user.social[provider.lower()] = provider_id del user.email_verifications[token] user.verification_key = generate_verification_key() user.save() service_url = request.url if external_status == 'CREATE': mails.send_mail(to_addr=user.username, mail=mails.WELCOME, mimetype='html', user=user) service_url += '&{}'.format(urllib.urlencode({'new': 'true'})) elif external_status == 'LINK': mails.send_mail( user=user, to_addr=user.username, mail=mails.EXTERNAL_LOGIN_LINK_SUCCESS, external_id_provider=provider, ) # redirect to CAS and authenticate the user with the verification key return redirect( cas.get_login_url(service_url, username=user.username, verification_key=user.verification_key))
def external_login_email_post(): """ View to handle email submission for first-time oauth-login user. HTTP Method: POST """ form = ResendConfirmationForm(request.form) session = get_session() if not session.is_external_first_login: raise HTTPError(http.UNAUTHORIZED) external_id_provider = session.data['auth_user_external_id_provider'] external_id = session.data['auth_user_external_id'] fullname = session.data['auth_user_fullname'] service_url = session.data['service_url'] destination = 'dashboard' for campaign in campaigns.get_campaigns(): if campaign != 'institution': # Handle different url encoding schemes between `furl` and `urlparse/urllib`. # OSF use `furl` to parse service url during service validation with CAS. However, `web_url_for()` uses # `urlparse/urllib` to generate service url. `furl` handles `urlparser/urllib` generated urls while ` but # not vice versa. campaign_url = furl.furl(campaigns.campaign_url_for(campaign)).url if campaigns.is_proxy_login(campaign): # proxy campaigns: OSF Preprints and branded ones if check_service_url_with_proxy_campaign(service_url, campaign_url): destination = campaign # continue to check branded preprints even service url matches osf preprints if campaign != 'osf-preprints': break elif service_url.startswith(campaign_url): # osf campaigns: OSF Prereg and ERPC destination = campaign break if form.validate(): clean_email = form.email.data user = get_user(email=clean_email) external_identity = { external_id_provider: { external_id: None, }, } try: ensure_external_identity_uniqueness(external_id_provider, external_id, user) except ValidationError as e: raise HTTPError(http.FORBIDDEN, e.message) if user: # 1. update user oauth, with pending status external_identity[external_id_provider][external_id] = 'LINK' if external_id_provider in user.external_identity: user.external_identity[external_id_provider].update(external_identity[external_id_provider]) else: user.external_identity.update(external_identity) # 2. add unconfirmed email and send confirmation email user.add_unconfirmed_email(clean_email, external_identity=external_identity) user.save() send_confirm_email( user, clean_email, external_id_provider=external_id_provider, external_id=external_id, destination=destination ) # 3. notify user message = language.EXTERNAL_LOGIN_EMAIL_LINK_SUCCESS.format( external_id_provider=external_id_provider, email=user.username ) kind = 'success' # 4. remove session and osf cookie remove_session(session) else: # 1. create unconfirmed user with pending status external_identity[external_id_provider][external_id] = 'CREATE' user = User.create_unconfirmed( username=clean_email, password=str(uuid.uuid4()), fullname=fullname, external_identity=external_identity, campaign=None ) # TODO: [#OSF-6934] update social fields, verified social fields cannot be modified user.save() # 3. send confirmation email send_confirm_email( user, user.username, external_id_provider=external_id_provider, external_id=external_id, destination=destination ) # 4. notify user message = language.EXTERNAL_LOGIN_EMAIL_CREATE_SUCCESS.format( external_id_provider=external_id_provider, email=user.username ) kind = 'success' # 5. remove session remove_session(session) status.push_status_message(message, kind=kind, trust=False) else: forms.push_errors_to_status(form.errors) # Don't go anywhere return { 'form': form, 'external_id_provider': external_id_provider }
def register_user(**kwargs): """ Register new user account. HTTP Method: POST :param-json str email1: :param-json str email2: :param-json str password: :param-json str fullName: :param-json str campaign: :raises: HTTPError(http.BAD_REQUEST) if validation fails or user already exists """ # Verify that email address match. # Note: Both `landing.mako` and `register.mako` already have this check on the form. Users can not submit the form # if emails do not match. However, this check should not be removed given we may use the raw api call directly. json_data = request.get_json() if str(json_data['email1']).lower() != str(json_data['email2']).lower(): raise HTTPError( http.BAD_REQUEST, data=dict(message_long='Email addresses must match.') ) # Verify that captcha is valid if settings.RECAPTCHA_SITE_KEY and not validate_recaptcha(json_data.get('g-recaptcha-response'), remote_ip=request.remote_addr): raise HTTPError( http.BAD_REQUEST, data=dict(message_long='Invalid Captcha') ) try: full_name = request.json['fullName'] full_name = strip_html(full_name) campaign = json_data.get('campaign') if campaign and campaign not in campaigns.get_campaigns(): campaign = None user = framework_auth.register_unconfirmed( request.json['email1'], request.json['password'], full_name, campaign=campaign, ) framework_auth.signals.user_registered.send(user) except (ValidationValueError, DuplicateEmailError): raise HTTPError( http.BAD_REQUEST, data=dict( message_long=language.ALREADY_REGISTERED.format( email=markupsafe.escape(request.json['email1']) ) ) ) except ValidationError as e: raise HTTPError( http.BAD_REQUEST, data=dict(message_long=e.message) ) if settings.CONFIRM_REGISTRATIONS_BY_EMAIL: send_confirm_email(user, email=user.username) message = language.REGISTRATION_SUCCESS.format(email=user.username) return {'message': message} else: return {'message': 'You may now log in.'}
def external_login_confirm_email_get(auth, uid, token): """ View for email confirmation links when user first login through external identity provider. HTTP Method: GET When users click the confirm link, they are expected not to be logged in. If not, they will be logged out first and redirected back to this view. After OSF verifies the link and performs all actions, they will be automatically logged in through CAS and redirected back to this view again being authenticated. :param auth: the auth context :param uid: the user's primary key :param token: the verification token """ user = User.load(uid) if not user: raise HTTPError(http.BAD_REQUEST) destination = request.args.get('destination') if not destination: raise HTTPError(http.BAD_REQUEST) # if user is already logged in if auth and auth.user: # if it is a wrong user if auth.user._id != user._id: return auth_logout(redirect_url=request.url) # if it is the expected user new = request.args.get('new', None) if destination in campaigns.get_campaigns(): return redirect(campaigns.campaign_url_for(destination)) if new: status.push_status_message(language.WELCOME_MESSAGE, kind='default', jumbotron=True, trust=True) return redirect(web_url_for('dashboard')) # token is invalid if token not in user.email_verifications: raise HTTPError(http.BAD_REQUEST) verification = user.email_verifications[token] email = verification['email'] provider = verification['external_identity'].keys()[0] provider_id = verification['external_identity'][provider].keys()[0] # wrong provider if provider not in user.external_identity: raise HTTPError(http.BAD_REQUEST) external_status = user.external_identity[provider][provider_id] try: ensure_external_identity_uniqueness(provider, provider_id, user) except ValidationError as e: raise HTTPError(http.FORBIDDEN, e.message) if not user.is_registered: user.set_password(uuid.uuid4(), notify=False) user.register(email) if email.lower() not in user.emails: user.emails.append(email.lower()) user.date_last_logged_in = datetime.datetime.utcnow() user.external_identity[provider][provider_id] = 'VERIFIED' user.social[provider.lower()] = provider_id del user.email_verifications[token] user.verification_key = generate_verification_key() user.save() service_url = request.url if external_status == 'CREATE': mails.send_mail( to_addr=user.username, mail=mails.WELCOME, mimetype='html', user=user ) service_url += '&{}'.format(urllib.urlencode({'new': 'true'})) elif external_status == 'LINK': mails.send_mail( user=user, to_addr=user.username, mail=mails.EXTERNAL_LOGIN_LINK_SUCCESS, external_id_provider=provider, ) # redirect to CAS and authenticate the user with the verification key return redirect(cas.get_login_url( service_url, username=user.username, verification_key=user.verification_key ))
def test_get_campaigns_update_not_expired(self): campaigns.get_campaigns() self.refresh = campaigns.CAMPAIGNS_LAST_REFRESHED campaigns.get_campaigns() assert_equal(self.refresh, campaigns.CAMPAIGNS_LAST_REFRESHED)
def test_get_campaigns_init(self): campaign_dict = campaigns.get_campaigns() assert_equal(len(campaign_dict), len(self.campaign_lists)) for campaign in campaign_dict: assert_in(campaign, self.campaign_lists) assert_not_equal(self.refresh, campaigns.CAMPAIGNS_LAST_REFRESHED)
def test_get_campaigns_update_expired(self): campaigns.get_campaigns() self.refresh = datetime.utcnow() - timedelta(minutes=5) campaigns.CAMPAIGNS_LAST_REFRESHED = self.refresh campaigns.get_campaigns() assert_not_equal(self.refresh, campaigns.CAMPAIGNS_LAST_REFRESHED)
def test_get_campaigns_update_expired(self): campaigns.get_campaigns() self.refresh = timezone.now() - timedelta(minutes=5) campaigns.CAMPAIGNS_LAST_REFRESHED = self.refresh campaigns.get_campaigns() assert_not_equal(self.refresh, campaigns.CAMPAIGNS_LAST_REFRESHED)
def external_login_email_post(): """ View to handle email submission for first-time oauth-login user. HTTP Method: POST """ form = ResendConfirmationForm(request.form) session = get_session() if not session.is_external_first_login: raise HTTPError(http.UNAUTHORIZED) external_id_provider = session.data['auth_user_external_id_provider'] external_id = session.data['auth_user_external_id'] fullname = session.data['auth_user_fullname'] service_url = session.data['service_url'] # TODO: @cslzchen use user tags instead of destination destination = 'dashboard' for campaign in campaigns.get_campaigns(): if campaign != 'institution': # Handle different url encoding schemes between `furl` and `urlparse/urllib`. # OSF use `furl` to parse service url during service validation with CAS. However, `web_url_for()` uses # `urlparse/urllib` to generate service url. `furl` handles `urlparser/urllib` generated urls while ` but # not vice versa. campaign_url = furl.furl(campaigns.campaign_url_for(campaign)).url external_campaign_url = furl.furl( campaigns.external_campaign_url_for(campaign)).url if campaigns.is_proxy_login(campaign): # proxy campaigns: OSF Preprints and branded ones if check_service_url_with_proxy_campaign( str(service_url), campaign_url, external_campaign_url): destination = campaign # continue to check branded preprints even service url matches osf preprints if campaign != 'osf-preprints': break elif service_url.startswith(campaign_url): # osf campaigns: OSF Prereg and ERPC destination = campaign break if form.validate(): clean_email = form.email.data user = get_user(email=clean_email) external_identity = { external_id_provider: { external_id: None, }, } try: ensure_external_identity_uniqueness(external_id_provider, external_id, user) except ValidationError as e: raise HTTPError(http.FORBIDDEN, e.message) if user: # 1. update user oauth, with pending status external_identity[external_id_provider][external_id] = 'LINK' if external_id_provider in user.external_identity: user.external_identity[external_id_provider].update( external_identity[external_id_provider]) else: user.external_identity.update(external_identity) # 2. add unconfirmed email and send confirmation email user.add_unconfirmed_email(clean_email, external_identity=external_identity) user.save() send_confirm_email(user, clean_email, external_id_provider=external_id_provider, external_id=external_id, destination=destination) # 3. notify user message = language.EXTERNAL_LOGIN_EMAIL_LINK_SUCCESS.format( external_id_provider=external_id_provider, email=user.username) kind = 'success' # 4. remove session and osf cookie remove_session(session) else: # 1. create unconfirmed user with pending status external_identity[external_id_provider][external_id] = 'CREATE' user = OSFUser.create_unconfirmed( username=clean_email, password=None, fullname=fullname, external_identity=external_identity, campaign=None) # TODO: [#OSF-6934] update social fields, verified social fields cannot be modified user.save() # 3. send confirmation email send_confirm_email(user, user.username, external_id_provider=external_id_provider, external_id=external_id, destination=destination) # 4. notify user message = language.EXTERNAL_LOGIN_EMAIL_CREATE_SUCCESS.format( external_id_provider=external_id_provider, email=user.username) kind = 'success' # 5. remove session remove_session(session) status.push_status_message(message, kind=kind, trust=False) else: forms.push_errors_to_status(form.errors) # Don't go anywhere return {'form': form, 'external_id_provider': external_id_provider}
def setUp(self): super(TestRegistrationThroughCampaigns, self).setUp() campaigns.get_campaigns() # Set up global CAMPAIGNS