def test_throttle_period_expired_using_timestamp_in_seconds(self): timestamp = int(time.time()) is_expired = throttle_period_expired(timestamp=(timestamp + 29), throttle=30) assert_false(is_expired) is_expired = throttle_period_expired(timestamp=(timestamp - 31), throttle=30) assert_true(is_expired)
def test_throttle_period_expired_using_datetime(self): timestamp = datetime.datetime.utcnow() is_expired = throttle_period_expired(timestamp=(timestamp + datetime.timedelta(seconds=29)), throttle=30) assert_false(is_expired) is_expired = throttle_period_expired(timestamp=(timestamp - datetime.timedelta(seconds=31)), throttle=30) assert_true(is_expired)
def test_throttle_period_expired_using_datetime(self): timestamp = timezone.now() is_expired = throttle_period_expired(timestamp=(timestamp + datetime.timedelta(seconds=29)), throttle=30) assert_false(is_expired) is_expired = throttle_period_expired(timestamp=(timestamp - datetime.timedelta(seconds=31)), throttle=30) assert_true(is_expired)
def notify_added_contributor(node, contributor, auth=None, throttle=None): throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Exclude forks and templates because the user forking/templating the project gets added # via 'add_contributor' but does not need to get notified. # Only email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.template_node and not node.is_fork and (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor)))): contributor_record = contributor.contributor_added_email_records.get(node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, mails.CONTRIBUTOR_ADDED, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor) ) contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp() contributor.save()
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'): throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Exclude forks and templates because the user forking/templating the project gets added # via 'add_contributor' but does not need to get notified. # Only email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.template_node and not node.is_fork and (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor)))): email_template = getattr(mails, 'CONTRIBUTOR_ADDED_{}'.format(email_template.upper())) contributor_record = contributor.contributor_added_email_records.get(node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, email_template, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor) ) contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp() contributor.save() elif not contributor.is_registered: unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
def notify_added_contributor(node, contributor, auth=None, throttle=None): throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Exclude forks and templates because the user forking/templating the project gets added # via 'add_contributor' but does not need to get notified. # Only email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.template_node and not node.is_fork and (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor)))): contributor_record = contributor.contributor_added_email_records.get(node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, mails.CONTRIBUTOR_ADDED, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '' ) contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp() contributor.save()
def resend_confirmation(auth): user = auth.user data = request.get_json() validate_user(data, user) if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError( httplib.BAD_REQUEST, data={"message_long": "Too many requests. Please wait a while before sending another confirmation email."}, ) try: primary = data["email"]["primary"] confirmed = data["email"]["confirmed"] address = data["email"]["address"].strip().lower() except KeyError: raise HTTPError(httplib.BAD_REQUEST) if primary or confirmed: raise HTTPError(httplib.BAD_REQUEST, data={"message_long": "Cannnot resend confirmation for confirmed emails"}) user.add_unconfirmed_email(address) # TODO: This setting is now named incorrectly. if settings.CONFIRM_REGISTRATIONS_BY_EMAIL: send_confirm_email(user, email=address) user.email_last_sent = datetime.datetime.utcnow() user.save() return _profile_view(user, is_profile=True)
def resend_confirmation(auth): user = auth.user data = request.get_json() validate_user(data, user) if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before sending another confirmation email.'}) try: primary = data['email']['primary'] confirmed = data['email']['confirmed'] address = data['email']['address'].strip().lower() except KeyError: raise HTTPError(httplib.BAD_REQUEST) if primary or confirmed: raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'Cannnot resend confirmation for confirmed emails'}) user.add_unconfirmed_email(address) # TODO: This setting is now named incorrectly. if settings.CONFIRM_REGISTRATIONS_BY_EMAIL: send_confirm_email(user, email=address) user.email_last_sent = timezone.now() user.save() return _profile_view(user, is_profile=True)
def before_request(): # TODO: Fix circular import from framework.auth.core import get_user from framework.auth import cas from website.util import time as util_time # Central Authentication Server Ticket Validation and Authentication ticket = request.args.get('ticket') if ticket: service_url = furl.furl(request.url) service_url.args.pop('ticket') # Attempt to authenticate wih CAS, and return a proper redirect response return cas.make_response_from_ticket(ticket=ticket, service_url=service_url.url) if request.authorization: user = get_user(email=request.authorization.username, password=request.authorization.password) # Create an empty session # TODO: Shoudn't need to create a session for Basic Auth user_session = Session() set_session(user_session) if user: user_addon = user.get_addon('twofactor') if user_addon and user_addon.is_confirmed: otp = request.headers.get('X-OSF-OTP') if otp is None or not user_addon.verify_code(otp): # Must specify two-factor authentication OTP code or invalid two-factor authentication OTP code. user_session.data['auth_error_code'] = http.UNAUTHORIZED return user_session.data['auth_user_username'] = user.username user_session.data['auth_user_id'] = user._primary_key user_session.data['auth_user_fullname'] = user.fullname else: # Invalid key: Not found in database user_session.data['auth_error_code'] = http.UNAUTHORIZED return cookie = request.cookies.get(settings.COOKIE_NAME) if cookie: try: session_id = itsdangerous.Signer( settings.SECRET_KEY).unsign(cookie) user_session = Session.load(session_id) or Session(_id=session_id) except itsdangerous.BadData: return if not util_time.throttle_period_expired(user_session.date_created, settings.OSF_SESSION_TIMEOUT): if user_session.data.get( 'auth_user_id') and 'api' not in request.url: database['user'].update( {'_id': user_session.data.get('auth_user_id')}, {'$set': { 'date_last_login': datetime.utcnow() }}, w=0) set_session(user_session) else: remove_session(user_session)
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600): """Send an email for claiming a user account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. """ claimer_email = email.lower().strip() unclaimed_record = user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = user.get_claim_url(node._primary_key, external=True) # If given email is the same provided by user, just send to that email if unclaimed_record.get('email') == claimer_email: mail_tpl = mails.INVITE to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email user.save() else: # Otherwise have the referrer forward the email to the user # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(400, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer_email user.save() claim_url = user.get_claim_url(node._primary_key, external=True) if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( claimer_email, pending_mail, user=user, referrer=referrer, fullname=unclaimed_record['name'], node=node ) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username mails.send_mail( to_addr, mail_tpl, user=user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name'] ) return to_addr
def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 3600): """ A registered user claiming the unclaimed user account as an contributor to a project. Send an email for claiming the account to the referrer and notify the claimer. :param claimer: the claimer :param unclaimed_user: the user account to claim :param node: the project node where the user account is claimed :param throttle: the time period in seconds before another claim for the account can be made :return: :raise: http.BAD_REQUEST """ unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(http.BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer.username unclaimed_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unclaimed_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unclaimed_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'): if email_template == 'false': return throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.parent_node or node.parent_node and not node.parent_node.is_contributor(contributor)): preprint_provider = None if email_template == 'preprint': email_template, preprint_provider = find_preprint_provider(node) if not email_template or not preprint_provider: return email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')( email_template, preprint_provider.name) elif node.is_preprint: email_template = getattr( mails, 'CONTRIBUTOR_ADDED_PREPRINT_NODE_FROM_OSF'.format( email_template.upper())) else: email_template = getattr( mails, 'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper())) contributor_record = contributor.contributor_added_email_records.get( node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, email_template, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none= check_if_all_global_subscriptions_are_none(contributor), branded_service=preprint_provider) contributor.contributor_added_email_records[ node._id]['last_sent'] = get_timestamp() contributor.save() elif not contributor.is_registered: unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
def forgot_password_post(): """ View for user to submit forgot password form. HTTP Method: POST :return {} """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if not form.validate(): # Don't go anywhere forms.push_errors_to_status(form.errors) else: email = form.email.data status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to ' 'reset the OSF password has been sent to {0}. If you do not receive an email and believe ' 'you should have, please contact OSF Support. ').format(email) kind = 'success' # check if the user exists user_obj = get_user(email=email) if user_obj: # rate limit forgot_password_post if not throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): status_message = 'You have recently requested to change your password. Please wait a few minutes ' \ 'before trying again.' kind = 'error' else: # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email. # if the user account is not claimed yet if (user_obj.is_invited and user_obj.unclaimed_records and not user_obj.date_last_login and not user_obj.is_claimed and not user_obj.is_registered): status_message = 'You cannot reset password on this account. Please contact OSF Support.' kind = 'error' else: # new random verification key (v2) user_obj.verification_key_v2 = generate_verification_key(verification_type='password') user_obj.email_last_sent = datetime.datetime.utcnow() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for( 'reset_password_get', uid=user_obj._id, token=user_obj.verification_key_v2['token'] ) ) mails.send_mail( to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link ) status.push_status_message(status_message, kind=kind, trust=False) return {}
def before_request(): from framework.auth import cas from website.util import time as util_time # Central Authentication Server Ticket Validation and Authentication ticket = request.args.get('ticket') if ticket: service_url = furl.furl(request.url) service_url.args.pop('ticket') # Attempt autn wih CAS, and return a proper redirect response return cas.make_response_from_ticket(ticket=ticket, service_url=service_url.url) if request.authorization: # TODO: Fix circular import from framework.auth.core import get_user user = get_user( email=request.authorization.username, password=request.authorization.password ) # Create empty session # TODO: Shoudn't need to create a session for Basic Auth session = Session() set_session(session) if user: user_addon = user.get_addon('twofactor') if user_addon and user_addon.is_confirmed: otp = request.headers.get('X-OSF-OTP') if otp is None or not user_addon.verify_code(otp): # Must specify two-factor authentication OTP code or invalid two-factor # authentication OTP code. session.data['auth_error_code'] = http.UNAUTHORIZED return session.data['auth_user_username'] = user.username session.data['auth_user_id'] = user._primary_key session.data['auth_user_fullname'] = user.fullname else: # Invalid key: Not found in database session.data['auth_error_code'] = http.UNAUTHORIZED return cookie = request.cookies.get(settings.COOKIE_NAME) if cookie: try: session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie) session = Session.load(session_id) or Session(_id=session_id) except itsdangerous.BadData: return if not util_time.throttle_period_expired(session.date_created, settings.OSF_SESSION_TIMEOUT): if session.data.get('auth_user_id') and 'api' not in request.url: database['user'].update({'_id': session.data.get('auth_user_id')}, {'$set': {'date_last_login': datetime.utcnow()}}, w=0) set_session(session) else: remove_session(session)
def forgot_password_post(auth, **kwargs): """ View for user to submit forgot password form. HTTP Method: POST """ # If user is already logged in, redirect to dashboard page. if auth.logged_in: return redirect(web_url_for('dashboard')) form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to ' 'reset the OSF password has been sent to {0}. If you do not receive an email and believe ' 'you should have, please contact OSF Support. ').format(email) # check if the user exists user_obj = get_user(email=email) if user_obj: # check forgot_password rate limit if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): # new random verification key, allows OSF to check whether the reset_password request is valid, # this verification key is used twice, one for GET reset_password and one for POST reset_password # and it will be destroyed when POST reset_password succeeds user_obj.verification_key = generate_verification_key() user_obj.email_last_sent = datetime.datetime.utcnow() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for( 'reset_password_get', verification_key=user_obj.verification_key ) ) mails.send_mail( to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link ) status.push_status_message(status_message, kind='success', trust=False) else: status.push_status_message('You have recently requested to change your password. Please wait a ' 'few minutes before trying again.', kind='error', trust=False) else: status.push_status_message(status_message, kind='success', trust=False) else: forms.push_errors_to_status(form.errors) # Don't go anywhere return {}
def request_deactivation(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError( http.BAD_REQUEST, data={ "message_long": "Too many requests. Please wait a while before sending another account deactivation request.", "error_type": "throttle_error", }, ) mails.send_mail(to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=auth.user) user.email_last_sent = datetime.datetime.utcnow() user.save() return {"message": "Sent account deactivation request"}
def request_export(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before sending another account export request.', 'error_type': 'throttle_error'}) mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.REQUEST_EXPORT, user=auth.user, ) user.email_last_sent = timezone.now() user.save() return {'message': 'Sent account export request'}
def request_export(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before sending another account export request.', 'error_type': 'throttle_error'}) mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_EXPORT, user=auth.user, ) user.email_last_sent = datetime.datetime.utcnow() user.save() return {'message': 'Sent account export request'}
def request_deactivation(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError(http.BAD_REQUEST, data={ 'message_long': 'Too many requests. Please wait a while before sending another account deactivation request.', 'error_type': 'throttle_error' }) mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=auth.user, ) user.email_last_sent = timezone.now() user.requested_deactivation = True user.save() return {'message': 'Sent account deactivation request'}
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'): if email_template == 'false': return throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.parent_node or node.parent_node and not node.parent_node.is_contributor(contributor)): preprint_provider = None if email_template == 'preprint': email_template, preprint_provider = find_preprint_provider(node) if not email_template or not preprint_provider: return email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')(email_template, preprint_provider.name) else: email_template = getattr(mails, 'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper())) contributor_record = contributor.contributor_added_email_records.get(node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, email_template, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor), branded_service=preprint_provider ) contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp() contributor.save() elif not contributor.is_registered: unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
def resend_confirmation_post(auth): """ View for user to submit resend confirmation form. HTTP Method: POST """ # If user is already logged in, log user out if auth.logged_in: return auth_logout(redirect_url=request.url) form = ResendConfirmationForm(request.form) if form.validate(): clean_email = form.email.data user = get_user(email=clean_email) status_message = ( 'If there is an OSF account associated with this unconfirmed email address {0}, ' 'a confirmation email has been resent to it. If you do not receive an email and believe ' 'you should have, please contact OSF Support.').format(clean_email) kind = 'success' if user: if throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): try: send_confirm_email(user, clean_email, renew=True) except KeyError: # already confirmed, redirect to dashboard status_message = 'This email {0} has already been confirmed.'.format( clean_email) kind = 'warning' user.email_last_sent = timezone.now() user.save() else: status_message = ( 'You have recently requested to resend your confirmation email. ' 'Please wait a few minutes before trying again.') kind = 'error' status.push_status_message(status_message, kind=kind, trust=False) else: forms.push_errors_to_status(form.errors) # Don't go anywhere return {'form': form}
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600): unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key) # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError( 400, data=dict( message_long= 'User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer.username unreg_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unreg_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unreg_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unreg_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def request_deactivation(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError( http.BAD_REQUEST, data={ 'message_long': 'Too many requests. Please wait a while before sending another account deactivation request.', 'error_type': 'throttle_error' }) mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=auth.user, ) user.email_last_sent = datetime.datetime.utcnow() user.save() return {'message': 'Sent account deactivation request'}
def forgot_password_post(): """Attempt to send user password reset or return respective error. """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data status_message = ( 'If there is an OSF account associated with {0}, an email with instructions on how to reset ' 'the OSF password has been sent to {0}. If you do not receive an email and believe you ' 'should have, please contact OSF Support. ').format(email) user_obj = get_user(email=email) if user_obj: if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): user_obj.verification_key = security.random_string(20) user_obj.email_last_sent = datetime.datetime.utcnow() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for('reset_password', verification_key=user_obj.verification_key)) mails.send_mail(to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link) status.push_status_message(status_message, kind='success', trust=False) else: status.push_status_message( 'You have recently requested to change your password. Please wait a little ' 'while before trying again.', kind='error', trust=False) else: status.push_status_message(status_message, kind='success', trust=False) forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600): unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key) # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(400, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer.username unreg_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unreg_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unreg_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unreg_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def resend_confirmation_post(auth): """ View for user to submit resend confirmation form. HTTP Method: POST """ # If user is already logged in, log user out if auth.logged_in: return auth_logout(redirect_url=request.url) form = ResendConfirmationForm(request.form) if form.validate(): clean_email = form.email.data user = get_user(email=clean_email) status_message = ('If there is an OSF account associated with this unconfirmed email {0}, ' 'a confirmation email has been resent to it. If you do not receive an email and believe ' 'you should have, please contact OSF Support.').format(clean_email) kind = 'success' if user: if throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): try: send_confirm_email(user, clean_email, renew=True) except KeyError: # already confirmed, redirect to dashboard status_message = 'This email {0} has already been confirmed.'.format(clean_email) kind = 'warning' user.email_last_sent = datetime.datetime.utcnow() user.save() else: status_message = ('You have recently requested to resend your confirmation email. ' 'Please wait a few minutes before trying again.') kind = 'error' status.push_status_message(status_message, kind=kind, trust=False) else: forms.push_errors_to_status(form.errors) # Don't go anywhere return {'form': form}
def forgot_password_post(): """Attempt to send user password reset or return respective error. """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to reset ' 'the OSF password has been sent to {0}. If you do not receive an email and believe you ' 'should have, please contact OSF Support. ').format(email) user_obj = get_user(email=email) if user_obj: if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): user_obj.verification_key = security.random_string(20) user_obj.email_last_sent = datetime.datetime.utcnow() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for( 'reset_password', verification_key=user_obj.verification_key ) ) mails.send_mail( to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link ) status.push_status_message(status_message, kind='success', trust=False) else: status.push_status_message('You have recently requested to change your password. Please wait a little ' 'while before trying again.', kind='error', trust=False) else: status.push_status_message(status_message, kind='success', trust=False) forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def get_campaigns(): global CAMPAIGNS global CAMPAIGNS_LAST_REFRESHED if not CAMPAIGNS or (not mutex.locked() and throttle_period_expired( CAMPAIGNS_LAST_REFRESHED, CAMPAIGN_REFRESH_THRESHOLD)): with mutex: # Native campaigns: PREREG and ERPC newest_campaigns = { 'prereg': { 'system_tag': 'prereg_challenge_campaign', 'redirect_url': furl.furl(DOMAIN).add(path='prereg/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_PREREG, 'login_type': 'native', }, 'erpc': { 'system_tag': 'erp_challenge_campaign', 'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC, 'login_type': 'native', }, } # Institution Login newest_campaigns.update({ 'institution': { 'system_tag': 'institution_campaign', 'redirect_url': '', 'login_type': 'institution', }, }) # Proxy campaigns: Preprints, both OSF and branded ones preprint_providers = PreprintProvider.objects.all() for provider in preprint_providers: if provider._id == 'osf': template = 'osf' name = 'OSF' url_path = 'preprints/' else: template = 'branded' name = provider.name url_path = 'preprints/{}'.format(provider._id) campaign = '{}-preprints'.format(provider._id) system_tag = '{}_preprints'.format(provider._id) newest_campaigns.update({ campaign: { 'system_tag': system_tag, 'redirect_url': furl.furl(DOMAIN).add(path=url_path).url, 'confirmation_email_template': mails.CONFIRM_EMAIL_PREPRINTS(template, name), 'login_type': 'proxy', 'provider': name, } }) CAMPAIGNS = newest_campaigns CAMPAIGNS_LAST_REFRESHED = timezone.now() return CAMPAIGNS
def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 3600, email_template="default"): """ Unregistered user claiming a user account as an contributor to a project. Send an email for claiming the account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User unclaimed_user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. :param str email_template: the email template to use :return :raise http.BAD_REQUEST """ claimer_email = email.lower().strip() unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record["referrer_id"]) claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # When adding the contributor, the referrer provides both name and email. # The given email is the same provided by user, just send to that email. if unclaimed_record.get("email") == claimer_email: mail_tpl = getattr(mails, "INVITE_{}".format(email_template.upper())) to_addr = claimer_email unclaimed_record["claimer_email"] = claimer_email unclaimed_user.save() # When adding the contributor, the referred only provides the name. # The account is later claimed by some one who provides the email. # Send email to the referrer and ask her/him to forward the email to the user. else: # check throttle timestamp = unclaimed_record.get("last_sent") if not throttle_period_expired(timestamp, throttle): raise HTTPError( http.BAD_REQUEST, data=dict(message_long="User account can only be claimed with an existing user once every 24 hours"), ) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type="claim") unclaimed_record["last_sent"] = get_timestamp() unclaimed_record["token"] = verification_key["token"] unclaimed_record["expires"] = verification_key["expires"] unclaimed_record["claimer_email"] = claimer_email unclaimed_user.save() claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # send an email to the invited user without `claim_url` if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( claimer_email, pending_mail, user=unclaimed_user, referrer=referrer, fullname=unclaimed_record["name"], node=node, ) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username # send an email to the referrer with `claim_url` mails.send_mail( to_addr, mail_tpl, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record["name"], ) return to_addr
def before_request(): # TODO: Fix circular import from framework.auth.core import get_user from framework.auth import cas from website.util import time as util_time Session = apps.get_model('osf.Session') # Central Authentication Server Ticket Validation and Authentication ticket = request.args.get('ticket') if ticket: service_url = furl.furl(request.url) service_url.args.pop('ticket') # Attempt to authenticate wih CAS, and return a proper redirect response return cas.make_response_from_ticket(ticket=ticket, service_url=service_url.url) if request.authorization: user = get_user( email=request.authorization.username, password=request.authorization.password ) # Create an empty session # TODO: Shoudn't need to create a session for Basic Auth user_session = Session() set_session(user_session) if user: user_addon = user.get_addon('twofactor') if user_addon and user_addon.is_confirmed: otp = request.headers.get('X-OSF-OTP') if otp is None or not user_addon.verify_code(otp): # Must specify two-factor authentication OTP code or invalid two-factor authentication OTP code. user_session.data['auth_error_code'] = http.UNAUTHORIZED return user_session.data['auth_user_username'] = user.username user_session.data['auth_user_fullname'] = user.fullname if user_session.data.get('auth_user_id', None) != user._primary_key: user_session.data['auth_user_id'] = user._primary_key user_session.save() else: # Invalid key: Not found in database user_session.data['auth_error_code'] = http.UNAUTHORIZED return cookie = request.cookies.get(settings.COOKIE_NAME) if cookie: try: session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie) user_session = Session.load(session_id) or Session(_id=session_id) except itsdangerous.BadData: return if not util_time.throttle_period_expired(user_session.created, settings.OSF_SESSION_TIMEOUT): # Update date last login when making non-api requests if user_session.data.get('auth_user_id') and 'api' not in request.url: OSFUser = apps.get_model('osf.OSFUser') ( OSFUser.objects .filter(guids___id__isnull=False, guids___id=user_session.data['auth_user_id']) # Throttle updates .filter(Q(date_last_login__isnull=True) | Q(date_last_login__lt=timezone.now() - dt.timedelta(seconds=settings.DATE_LAST_LOGIN_THROTTLE))) ).update(date_last_login=timezone.now()) set_session(user_session) else: remove_session(user_session)
def test_throttle_period_expired_no_timestamp(self): is_expired = throttle_period_expired(timestamp=None, throttle=30) assert_true(is_expired)
def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 3600, email_template='default'): """ Unregistered user claiming a user account as an contributor to a project. Send an email for claiming the account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User unclaimed_user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. :param str email_template: the email template to use :return :raise http.BAD_REQUEST """ claimer_email = email.lower().strip() unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # Option 1: # When adding the contributor, the referrer provides both name and email. # The given email is the same provided by user, just send to that email. preprint_provider = None if unclaimed_record.get('email') == claimer_email: # check email template for branded preprints if email_template == 'preprint': email_template, preprint_provider = find_preprint_provider(node) if not email_template or not preprint_provider: return mail_tpl = getattr(mails, 'INVITE_PREPRINT')(email_template, preprint_provider) else: mail_tpl = getattr(mails, 'INVITE_DEFAULT'.format(email_template.upper())) to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() # Option 2: # TODO: [new improvement ticket] this option is disabled from preprint but still available on the project page # When adding the contributor, the referred only provides the name. # The account is later claimed by some one who provides the email. # Send email to the referrer and ask her/him to forward the email to the user. else: # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(http.BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # send an email to the invited user without `claim_url` if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( claimer_email, pending_mail, user=unclaimed_user, referrer=referrer, fullname=unclaimed_record['name'], node=node ) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username # Send an email to the claimer (Option 1) or to the referrer (Option 2) with `claim_url` mails.send_mail( to_addr, mail_tpl, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name'], branded_service_name=preprint_provider ) return to_addr
def get_campaigns(): global CAMPAIGNS global CAMPAIGNS_LAST_REFRESHED if not CAMPAIGNS or (not mutex.locked() and throttle_period_expired(CAMPAIGNS_LAST_REFRESHED, CAMPAIGN_REFRESH_THRESHOLD)): with mutex: # Native campaigns: PREREG and ERPC newest_campaigns = { 'prereg': { 'system_tag': 'prereg_challenge_campaign', 'redirect_url': furl.furl(DOMAIN).add(path='prereg/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_PREREG, 'login_type': 'native', }, 'erpc': { 'system_tag': 'erp_challenge_campaign', 'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC, 'login_type': 'native', }, } # Institution Login newest_campaigns.update({ 'institution': { 'system_tag': 'institution_campaign', 'redirect_url': '', 'login_type': 'institution', }, }) # Proxy campaigns: Preprints, both OSF and branded ones preprint_providers = PreprintProvider.objects.all() for provider in preprint_providers: if provider._id == 'osf': template = 'osf' name = 'OSF' url_path = 'preprints/' external_url = None else: template = 'branded' name = provider.name url_path = 'preprints/{}'.format(provider._id) external_url = provider.domain campaign = '{}-preprints'.format(provider._id) system_tag = '{}_preprints'.format(provider._id) newest_campaigns.update({ campaign: { 'system_tag': system_tag, 'redirect_url': furl.furl(DOMAIN).add(path=url_path).url, 'external_url': external_url, 'confirmation_email_template': mails.CONFIRM_EMAIL_PREPRINTS(template, name), 'login_type': 'proxy', 'provider': name, } }) # Proxy campaigns: Registries, OSF only # TODO: refactor for futher branded registries when there is a model for registries providers newest_campaigns.update({ 'osf-registries': { 'system_tag': 'osf_registries', 'redirect_url': furl.furl(DOMAIN).add(path='registries/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_REGISTRIES_OSF, 'login_type': 'proxy', 'provider': 'osf', } }) CAMPAIGNS = newest_campaigns CAMPAIGNS_LAST_REFRESHED = timezone.now() return CAMPAIGNS
def get_campaigns(): global CAMPAIGNS global CAMPAIGNS_LAST_REFRESHED logger = logging.getLogger(__name__) if not CAMPAIGNS or throttle_period_expired(CAMPAIGNS_LAST_REFRESHED, CAMPAIGN_REFRESH_THRESHOLD): # Native campaigns: PREREG and ERPC CAMPAIGNS = { 'prereg': { 'system_tag': 'prereg_challenge_campaign', 'redirect_url': furl.furl(DOMAIN).add(path='prereg/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_PREREG, 'login_type': 'native', }, 'erpc': { 'system_tag': 'erp_challenge_campaign', 'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url, 'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC, 'login_type': 'native', }, } # Institution Login CAMPAIGNS.update({ 'institution': { 'system_tag': 'institution_campaign', 'redirect_url': '', 'login_type': 'institution', }, }) # Proxy campaigns: Preprints, both OSF and branded ones try: preprint_providers = PreprintProvider.find(Q('_id', 'ne', None)) for provider in preprint_providers: if provider._id == 'osf': template = 'osf' name = 'OSF' url_path = 'preprints/' else: template = 'branded' name = provider.name url_path = 'preprints/{}'.format(provider._id) campaign = '{}-preprints'.format(provider._id) system_tag = '{}_preprints'.format(provider._id) CAMPAIGNS.update({ campaign: { 'system_tag': system_tag, 'redirect_url': furl.furl(DOMAIN).add(path=url_path).url, 'confirmation_email_template': mails.CONFIRM_EMAIL_PREPRINTS(template, name), 'login_type': 'proxy', 'provider': name, } }) except (NoResultsFound or QueryException or ImproperConfigurationError) as e: logger.warn('An error has occurred during campaign initialization: {}', e) CAMPAIGNS_LAST_REFRESHED = datetime.utcnow() return CAMPAIGNS
def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 3600, email_template='default'): """ Unregistered user claiming a user account as an contributor to a project. Send an email for claiming the account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User unclaimed_user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. :param str email_template: the email template to use :return :raise http.BAD_REQUEST """ claimer_email = email.lower().strip() unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) referrer = OSFUser.load(unclaimed_record['referrer_id']) claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # Option 1: # When adding the contributor, the referrer provides both name and email. # The given email is the same provided by user, just send to that email. preprint_provider = None if unclaimed_record.get('email') == claimer_email: # check email template for branded preprints if email_template == 'preprint': email_template, preprint_provider = find_preprint_provider(node) if not email_template or not preprint_provider: return mail_tpl = getattr(mails, 'INVITE_PREPRINT')(email_template, preprint_provider.name) else: mail_tpl = getattr(mails, 'INVITE_DEFAULT'.format(email_template.upper())) to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() # Option 2: # TODO: [new improvement ticket] this option is disabled from preprint but still available on the project page # When adding the contributor, the referred only provides the name. # The account is later claimed by some one who provides the email. # Send email to the referrer and ask her/him to forward the email to the user. else: # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError( http.BAD_REQUEST, data=dict( message_long= 'User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer_email unclaimed_user.save() claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True) # send an email to the invited user without `claim_url` if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail(claimer_email, pending_mail, user=unclaimed_user, referrer=referrer, fullname=unclaimed_record['name'], node=node) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username # Send an email to the claimer (Option 1) or to the referrer (Option 2) with `claim_url` mails.send_mail(to_addr, mail_tpl, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name'], branded_service=preprint_provider) return to_addr