Example #1
0
def reset_password(auth, **kwargs):
    if auth.logged_in:
        return auth_logout(redirect_url=request.url)
    verification_key = kwargs['verification_key']
    form = ResetPasswordForm(request.form)

    user_obj = get_user(verification_key=verification_key)
    if not user_obj:
        error_data = {'message_short': 'Invalid url.',
            'message_long': 'The verification key in the URL is invalid or '
            'has expired.'}
        raise HTTPError(400, data=error_data)

    if request.method == 'POST' and form.validate():
        # new random verification key, allows CAS to authenticate the user w/o password one time only.
        user_obj.verification_key = security.random_string(20)
        user_obj.set_password(form.password.data)
        user_obj.save()
        status.push_status_message('Password reset', 'success')
        # Redirect to CAS and authenticate the user with a verification key.
        return redirect(cas.get_login_url(
            web_url_for('user_account', _absolute=True),
            auto=True,
            username=user_obj.username,
            verification_key=user_obj.verification_key
        ))

    forms.push_errors_to_status(form.errors)
    return {
        'verification_key': verification_key,
    }
Example #2
0
def resend_confirmation_post():
    """
    View for user to submit resend confirmation form.
    HTTP Method: POST
    """

    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:
            try:
                send_confirm_email(user, clean_email)
            except KeyError:
                # already confirmed, redirect to dashboard
                status_message = 'This email {0} has already been confirmed.'.format(clean_email)
                kind = 'warning'
        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}
Example #3
0
def forgot_password():
    form = ForgotPasswordForm(request.form, prefix='forgot_password')

    if form.validate():
        email = form.email.data
        user_obj = get_user(username=email)
        if user_obj:
            user_obj.verification_key = security.random_string(20)
            user_obj.save()
            reset_link = "http://{0}{1}".format(
                request.host,
                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('Reset email sent to {0}'.format(email))
        else:
            status.push_status_message('Email {email} not found'.format(email=email))

    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #4
0
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
        user_obj = get_user(email=email)
        if user_obj:
            user_obj.verification_key = security.random_string(20)
            user_obj.save()
            reset_link = "http://{0}{1}".format(
                request.host,
                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(
            ('An email with instructions on how to reset the password '
             'for the account associated with {0} has been sent. If you '
             'do not receive an email and believe you should have please '
             'contact OSF Support.').format(email), 'success')

    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #5
0
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
        user_obj = get_user(email=email)
        if user_obj:
            user_obj.verification_key = security.random_string(20)
            user_obj.save()
            reset_link = "http://{0}{1}".format(
                request.host,
                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((
            '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), 'success')

    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #6
0
def auth_register_post():
    if not settings.ALLOW_REGISTRATION:
        status.push_status_message(language.REGISTRATION_UNAVAILABLE)
        return redirect('/')
    form = RegistrationForm(request.form, prefix='register')

    # Process form
    if form.validate():
        try:
            user = framework.auth.register_unconfirmed(
                form.username.data,
                form.password.data,
                form.fullname.data)
            framework.auth.signals.user_registered.send(user)
        except (ValidationValueError, DuplicateEmailError):
            status.push_status_message(
                language.ALREADY_REGISTERED.format(email=form.username.data))
            return auth_login()
        if user:
            if settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
                send_confirm_email(user, email=user.username)
                message = language.REGISTRATION_SUCCESS.format(email=user.username)
                status.push_status_message(message, 'success')
                return auth_login()
            else:
                return redirect('/login/first/')
    else:
        forms.push_errors_to_status(form.errors)
        return auth_login()
Example #7
0
def reset_password(auth, **kwargs):
    if auth.logged_in:
        logout()
    verification_key = kwargs['verification_key']
    form = ResetPasswordForm(request.form)

    user_obj = get_user(verification_key=verification_key)
    if not user_obj:
        error_data = {
            'message_short':
            'Invalid url.',
            'message_long':
            'The verification key in the URL is invalid or '
            'has expired.'
        }
        raise HTTPError(400, data=error_data)

    if request.method == 'POST' and form.validate():
        user_obj.verification_key = None
        user_obj.set_password(form.password.data)
        user_obj.save()
        status.push_status_message('Password reset')
        return redirect('/account/')

    forms.push_errors_to_status(form.errors)
    return {
        'verification_key': verification_key,
    }
Example #8
0
def reset_password(auth, **kwargs):
    if auth.logged_in:
        return auth_logout(redirect_url=request.url)
    verification_key = kwargs['verification_key']
    form = ResetPasswordForm(request.form)

    user_obj = get_user(verification_key=verification_key)
    if not user_obj:
        error_data = {
            'message_short':
            'Invalid url.',
            'message_long':
            'The verification key in the URL is invalid or '
            'has expired.'
        }
        raise HTTPError(400, data=error_data)

    if request.method == 'POST' and form.validate():
        # new random verification key, allows CAS to authenticate the user w/o password one time only.
        user_obj.verification_key = security.random_string(20)
        user_obj.set_password(form.password.data)
        user_obj.save()
        status.push_status_message('Password reset', 'success')
        # Redirect to CAS and authenticate the user with a verification key.
        return redirect(
            cas.get_login_url(web_url_for('user_account', _absolute=True),
                              auto=True,
                              username=user_obj.username,
                              verification_key=user_obj.verification_key))

    forms.push_errors_to_status(form.errors)
    return {
        'verification_key': verification_key,
    }
Example #9
0
def auth_register_post():
    if not settings.ALLOW_REGISTRATION:
        status.push_status_message(language.REGISTRATION_UNAVAILABLE)
        return redirect('/')
    form = RegistrationForm(request.form, prefix='register')

    # Process form
    if form.validate():
        try:
            user = framework.auth.register_unconfirmed(form.username.data,
                                                       form.password.data,
                                                       form.fullname.data)
            framework.auth.signals.user_registered.send(user)
        except (ValidationValueError, DuplicateEmailError):
            status.push_status_message(
                language.ALREADY_REGISTERED.format(email=form.username.data))
            return auth_login()
        if user:
            if settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
                send_confirm_email(user, email=user.username)
                message = language.REGISTRATION_SUCCESS.format(
                    email=user.username)
                status.push_status_message(message, 'success')
                return auth_login()
            else:
                return redirect('/login/first/')
    else:
        forms.push_errors_to_status(form.errors)
        return auth_login()
Example #10
0
def forgot_password():
    """Return forgot password page upon GET request. If POST, attempt to send
    user password reset or return respective error.
    """
    if request.method == 'GET':
        return {}

    form = ForgotPasswordForm(request.form, prefix='forgot_password')

    if form.validate():
        email = form.email.data
        user_obj = get_user(email=email)
        if user_obj:
            user_obj.verification_key = security.random_string(20)
            user_obj.save()
            reset_link = "http://{0}{1}".format(
                request.host,
                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(
            'An email with instructions on how to reset the password for the '
            'account associated with {0} has been sent. If you do not receive '
            'an email and believe you should have please '
            'contact OSF Support.'.format(email))

    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #11
0
def forgot_password():
    """Return forgot password page upon GET request. If POST, attempt to send
    user password reset or return respective error.
    """
    if request.method == 'GET':
        return {}

    form = ForgotPasswordForm(request.form, prefix='forgot_password')

    if form.validate():
        email = form.email.data
        user_obj = get_user(username=email)
        if user_obj:
            user_obj.verification_key = security.random_string(20)
            user_obj.save()
            reset_link = "http://{0}{1}".format(
                request.host,
                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('Reset email sent to {0}'.format(email))
        else:
            status.push_status_message(
                'Email {email} not found'.format(email=email))

    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #12
0
def claim_user_form(auth, **kwargs):
    """View for rendering the set password page for a claimed user.

    Must have ``token`` as a querystring argument.

    Renders the set password form, validates it, and sets the user's password.
    """
    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(
            web_url_for('claim_user_registered', uid=uid, pid=pid,
                        token=token))

    user = User.load(uid)  # The unregistered user
    # user ID is invalid. Unregistered user is not in database
    if not user:
        raise HTTPError(http.BAD_REQUEST)
    # If claim token not valid, redirect to registration page
    if not verify_claim_token(user, token, pid):
        return redirect(web_url_for('auth_login'))
    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get(
        'claimer_email') or unclaimed_record.get('email')
    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if form.validate():
            username, password = claimer_email, form.password.data
            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = security.random_string(20)
            user.save()
            # Authenticate user and redirect to project page
            node = Node.load(pid)
            status.push_status_message(
                language.CLAIMED_CONTRIBUTOR.format(node=node),
                kind='success',
                trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            return redirect(
                cas.get_login_url(web_url_for('user_profile', _absolute=True),
                                  auto=True,
                                  username=user.username,
                                  verification_key=user.verification_key))
        else:
            forms.push_errors_to_status(form.errors)
    return {
        'firstname': user.given_name,
        'email': claimer_email if claimer_email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #13
0
def claim_user_registered(auth, node, **kwargs):
    """View that prompts user to enter their password in order to claim
    contributorship on a project.

    A user must be logged in.
    """
    current_user = auth.user

    sign_out_url = web_url_for('auth_login', logout=True, next=request.url)
    if not current_user:
        return redirect(sign_out_url)
    # Logged in user should not be a contributor the project
    if node.is_contributor(current_user):
        logout_url = web_url_for('auth_logout', redirect_url=request.url)
        data = {
            'message_short': 'Already a contributor',
            'message_long': ('The logged-in user is already a contributor to this '
                'project. Would you like to <a href="{}">log out</a>?').format(logout_url)
        }
        raise HTTPError(http.BAD_REQUEST, data=data)
    uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token']
    unreg_user = User.load(uid)
    if not verify_claim_token(unreg_user, token, pid=node._primary_key):
        raise HTTPError(http.BAD_REQUEST)

    # Store the unreg_user data on the session in case the user registers
    # a new account
    session.data['unreg_user'] = {
        'uid': uid, 'pid': pid, 'token': token
    }

    form = PasswordForm(request.form)
    if request.method == 'POST':
        if form.validate():
            if current_user.check_password(form.password.data):
                node.replace_contributor(old=unreg_user, new=current_user)
                node.save()
                status.push_status_message(
                    'You are now a contributor to this project.',
                    kind='success',
                    trust=False
                )
                return redirect(node.url)
            else:
                status.push_status_message(language.LOGIN_FAILED, kind='warning', trust=False)
        else:
            forms.push_errors_to_status(form.errors)
    if is_json_request():
        form_ret = forms.utils.jsonify(form)
        user_ret = profile_utils.serialize_user(current_user, full=False)
    else:
        form_ret = form
        user_ret = current_user
    return {
        'form': form_ret,
        'user': user_ret,
        'signOutUrl': sign_out_url
    }
Example #14
0
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 claim_user_form(auth, **kwargs):
    """
    View for rendering the set password page for a claimed user.
    Must have ``token`` as a querystring argument.
    Renders the set password form, validates it, and sets the user's password.
    HTTP Method: GET, POST
    """

    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')
    user = User.load(uid)

    # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error
    if not user or not verify_claim_token(user, token, pid):
        error_data = {
            'message_short': 'Invalid url.',
            'message_long': 'Claim user does not exists, the token in the URL is invalid or has expired.'
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(web_url_for('claim_user_registered',
            uid=uid, pid=pid, token=token))

    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get('claimer_email') or unclaimed_record.get('email')
    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if not form.validate():
            forms.push_errors_to_status(form.errors)
        elif settings.RECAPTCHA_SITE_KEY and not validate_recaptcha(request.form.get('g-recaptcha-response'), remote_ip=request.remote_addr):
            status.push_status_message('Invalid captcha supplied.', kind='error')
        else:
            username, password = claimer_email, form.password.data
            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = generate_verification_key()
            user.save()
            # Authenticate user and redirect to project page
            status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind='success', trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            return redirect(cas.get_login_url(
                web_url_for('view_project', pid=pid, _absolute=True),
                username=user.username,
                verification_key=user.verification_key
            ))

    return {
        'firstname': user.given_name,
        'email': claimer_email if claimer_email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #16
0
def claim_user_form(auth, **kwargs):
    """
    View for rendering the set password page for a claimed user.
    Must have ``token`` as a querystring argument.
    Renders the set password form, validates it, and sets the user's password.
    HTTP Method: GET, POST
    """

    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')
    user = User.load(uid)

    # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error
    if not user or not verify_claim_token(user, token, pid):
        error_data = {
            'message_short': 'Invalid url.',
            'message_long': 'Claim user does not exists, the token in the URL is invalid or has expired.'
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(web_url_for('claim_user_registered',
            uid=uid, pid=pid, token=token))

    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get('claimer_email') or unclaimed_record.get('email')
    form = SetEmailAndPasswordForm(request.form, token=token)

    if request.method == 'POST':
        if form.validate():
            username, password = claimer_email, form.password.data
            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = generate_verification_key()
            user.save()
            # Authenticate user and redirect to project page
            status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind='success', trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            return redirect(cas.get_login_url(
                web_url_for('view_project', pid=pid, _absolute=True),
                username=user.username,
                verification_key=user.verification_key
            ))
        else:
            forms.push_errors_to_status(form.errors)

    return {
        'firstname': user.given_name,
        'email': claimer_email if claimer_email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #17
0
def auth_login(auth, registration_form=None, forgot_password_form=None, **kwargs):
    """If GET request, show login page. If POST, attempt to log user in if
    login form passsed; else send forgot password email.

    """
    if auth.logged_in:
        if not request.args.get('logout'):
            return redirect('/dashboard/')
        logout()
    direct_call = registration_form or forgot_password_form
    if request.method == 'POST' and not direct_call:
        form = SignInForm(request.form)
        if form.validate():
            twofactor_code = None
            if 'twofactor' in website.settings.ADDONS_REQUESTED:
                twofactor_code = form.two_factor.data
            try:
                response = login(
                    form.username.data,
                    form.password.data,
                    twofactor_code
                )
                return response
            except exceptions.LoginDisabledError:
                status.push_status_message(language.DISABLED, 'error')
            except exceptions.LoginNotAllowedError:
                status.push_status_message(language.UNCONFIRMED, 'warning')
                # Don't go anywhere
                return {'next_url': ''}
            except exceptions.PasswordIncorrectError:
                status.push_status_message(language.LOGIN_FAILED)
            except exceptions.TwoFactorValidationError:
                status.push_status_message(language.TWO_FACTOR_FAILED)
        forms.push_errors_to_status(form.errors)

    if kwargs.get('first', False):
        status.push_status_message('You may now log in')

    # Get next URL from GET / POST data
    next_url = request.args.get(
        'next',
        request.form.get(
            'next_url',
            ''
        )
    )
    status_message = request.args.get('status', '')
    if status_message == 'expired':
        status.push_status_message('The private link you used is expired.')

    code = http.OK
    if next_url:
        status.push_status_message(language.MUST_LOGIN)
        # Don't raise error if user is being logged out
        if not request.args.get('logout'):
            code = http.UNAUTHORIZED
    return {'next_url': next_url}, code
Example #18
0
def claim_user_registered(auth, node, **kwargs):
    """
    View that prompts user to enter their password in order to claim being a contributor on a project.
    A user must be logged in.
    """

    current_user = auth.user

    sign_out_url = web_url_for("auth_register", logout=True, next=request.url)
    if not current_user:
        return redirect(sign_out_url)

    # Logged in user should not be a contributor the project
    if node.is_contributor(current_user):
        logout_url = web_url_for("auth_logout", redirect_url=request.url)
        data = {
            "message_short": "Already a contributor",
            "message_long": (
                "The logged-in user is already a contributor to this "
                'project. Would you like to <a href="{}">log out</a>?'
            ).format(logout_url),
        }
        raise HTTPError(http.BAD_REQUEST, data=data)

    uid, pid, token = kwargs["uid"], kwargs["pid"], kwargs["token"]
    unreg_user = User.load(uid)
    if not verify_claim_token(unreg_user, token, pid=node._primary_key):
        error_data = {
            "message_short": "Invalid url.",
            "message_long": "The token in the URL is invalid or has expired.",
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    # Store the unreg_user data on the session in case the user registers
    # a new account
    session.data["unreg_user"] = {"uid": uid, "pid": pid, "token": token}

    form = PasswordForm(request.form)
    if request.method == "POST":
        if form.validate():
            if current_user.check_password(form.password.data):
                node.replace_contributor(old=unreg_user, new=current_user)
                node.save()
                status.push_status_message("You are now a contributor to this project.", kind="success", trust=False)
                return redirect(node.url)
            else:
                status.push_status_message(language.LOGIN_FAILED, kind="warning", trust=False)
        else:
            forms.push_errors_to_status(form.errors)
    if is_json_request():
        form_ret = forms.utils.jsonify(form)
        user_ret = profile_utils.serialize_user(current_user, full=False)
    else:
        form_ret = form
        user_ret = current_user
    return {"form": form_ret, "user": user_ret, "signOutUrl": sign_out_url}
Example #19
0
def claim_user_form(auth, **kwargs):
    """View for rendering the set password page for a claimed user.

    Must have ``token`` as a querystring argument.

    Renders the set password form, validates it, and sets the user's password.
    """
    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(web_url_for('claim_user_registered',
            uid=uid, pid=pid, token=token))

    user = User.load(uid)  # The unregistered user
    # user ID is invalid. Unregistered user is not in database
    if not user:
        raise HTTPError(http.BAD_REQUEST)
    # If claim token not valid, redirect to registration page
    if not verify_claim_token(user, token, pid):
        return redirect(web_url_for('auth_login'))
    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get('claimer_email') or unclaimed_record.get('email')
    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if form.validate():
            username, password = claimer_email, form.password.data
            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = security.random_string(20)
            user.save()
            # Authenticate user and redirect to project page
            node = Node.load(pid)
            status.push_status_message(language.CLAIMED_CONTRIBUTOR.format(node=node),
                                       kind='success',
                                       trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            return redirect(cas.get_login_url(
                web_url_for('user_profile', _absolute=True),
                auto=True,
                username=user.username,
                verification_key=user.verification_key
            ))
        else:
            forms.push_errors_to_status(form.errors)
    return {
        'firstname': user.given_name,
        'email': claimer_email if claimer_email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #20
0
def reset_password_post(uid=None, token=None):
    """
    View for user to submit reset password form.
    HTTP Method: POST

    :param uid: the user id
    :param token: the token in verification key
    :return:
    :raises: HTTPError(http.BAD_REQUEST) if verification key for the user is invalid, has expired or was used
    """

    form = ResetPasswordForm(request.form)

    # Check if request bears a valid pair of `uid` and `token`
    user_obj = OSFUser.load(uid)
    if not (user_obj and user_obj.verify_password_token(token=token)):
        error_data = {
            'message_short':
            'Invalid Request.',
            'message_long':
            'The requested URL is invalid, has expired, or was already used',
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    if not form.validate():
        # Don't go anywhere
        forms.push_errors_to_status(form.errors)
    else:
        # clear verification key (v2)
        user_obj.verification_key_v2 = {}
        # new verification key (v1) for CAS
        user_obj.verification_key = generate_verification_key(
            verification_type=None)
        try:
            user_obj.set_password(form.password.data)
            user_obj.save()
        except exceptions.ChangePasswordError as error:
            for message in error.messages:
                status.push_status_message(message,
                                           kind='warning',
                                           trust=False)
        else:
            status.push_status_message('Password reset',
                                       kind='success',
                                       trust=False)
            # redirect to CAS and authenticate the user automatically with one-time verification key.
            return redirect(
                cas.get_login_url(web_url_for('user_account', _absolute=True),
                                  username=user_obj.username,
                                  verification_key=user_obj.verification_key))

    return {
        'uid': user_obj._id,
        'token': user_obj.verification_key_v2['token'],
    }
Example #21
0
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 {}
Example #22
0
def auth_login(auth,
               registration_form=None,
               forgot_password_form=None,
               **kwargs):
    """If GET request, show login page. If POST, attempt to log user in if
    login form passsed; else send forgot password email.

    """
    if auth.logged_in:
        if not request.args.get('logout'):
            return redirect('/dashboard/')
        logout()
    direct_call = registration_form or forgot_password_form
    if request.method == 'POST' and not direct_call:
        form = SignInForm(request.form)
        if form.validate():
            twofactor_code = None
            if 'twofactor' in website.settings.ADDONS_REQUESTED:
                twofactor_code = form.two_factor.data
            try:
                response = login(form.username.data, form.password.data,
                                 twofactor_code)
                return response
            except exceptions.LoginDisabledError:
                status.push_status_message(language.DISABLED, 'error')
            except exceptions.LoginNotAllowedError:
                status.push_status_message(language.UNCONFIRMED, 'warning')
                # Don't go anywhere
                return {'next_url': ''}
            except exceptions.PasswordIncorrectError:
                status.push_status_message(language.LOGIN_FAILED)
            except exceptions.TwoFactorValidationError:
                status.push_status_message(language.TWO_FACTOR_FAILED)
        forms.push_errors_to_status(form.errors)

    if kwargs.get('first', False):
        status.push_status_message('You may now log in')

    # Get next URL from GET / POST data
    next_url = request.args.get('next', request.form.get('next_url', ''))
    status_message = request.args.get('status', '')
    if status_message == 'expired':
        status.push_status_message('The private link you used is expired.')

    code = http.OK
    if next_url:
        status.push_status_message(language.MUST_LOGIN)
        # Don't raise error if user is being logged out
        if not request.args.get('logout'):
            code = http.UNAUTHORIZED
    return {'next_url': next_url}, code
Example #23
0
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 {}
Example #24
0
def merge_user_post(auth, **kwargs):
    '''View for merging an account. Takes either JSON or form data.

    Request data should include a "merged_username" and "merged_password" properties
    for the account to be merged in.
    '''
    master = auth.user
    if request.json:
        merged_username = request.json.get('merged_username')
        merged_password = request.json.get('merged_password')
    else:
        form = MergeAccountForm(request.form)
        if not form.validate():
            forms.push_errors_to_status(form.errors)
            return merge_user_get(**kwargs)
        master_password = form.user_password.data
        if not master.check_password(master_password):
            status.push_status_message(
                'Could not authenticate. Please check your username and password.',
                trust=False)
            return merge_user_get(**kwargs)
        merged_username = form.merged_username.data
        merged_password = form.merged_password.data
    try:
        merged_user = User.find_one(Q('username', 'eq', merged_username))
    except NoResultsFound:
        status.push_status_message(
            'Could not find that user. Please check the username and password.',
            trust=False)
        return merge_user_get(**kwargs)
    if master and merged_user:
        if merged_user.check_password(merged_password):
            master.merge_user(merged_user)
            master.save()
            if request.form:
                status.push_status_message(
                    'Successfully merged {0} with this account'.format(
                        merged_username),
                    kind='success',
                    trust=False)
                return redirect('/settings/')
            return {'status': 'success'}
        else:
            status.push_status_message(
                'Could not find that user. Please check the username and password.',
                trust=False)
            return merge_user_get(**kwargs)
    else:
        raise HTTPError(http.BAD_REQUEST)
Example #25
0
def claim_user_form(auth, **kwargs):
    """View for rendering the set password page for a claimed user.

    Must have ``token`` as a querystring argument.

    Renders the set password form, validates it, and sets the user's password.
    """
    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(
            web_url_for('claim_user_registered', uid=uid, pid=pid,
                        token=token))

    user = User.load(uid)  # The unregistered user
    # user ID is invalid. Unregistered user is not in database
    if not user:
        raise HTTPError(http.BAD_REQUEST)
    # If claim token not valid, redirect to registration page
    if not verify_claim_token(user, token, pid):
        return redirect('/account/')
    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    email = unclaimed_record['email']
    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if form.validate():
            username, password = form.username.data, form.password.data
            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.save()
            # Authenticate user and redirect to project page
            response = redirect('/settings/')
            node = Node.load(pid)
            status.push_status_message(
                language.CLAIMED_CONTRIBUTOR.format(node=node), 'success')
            return authenticate(user, response)
        else:
            forms.push_errors_to_status(form.errors)
    return {
        'firstname': user.given_name,
        'email': email if email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #26
0
def reset_password_post(uid=None, token=None):
    """
    View for user to submit reset password form.
    HTTP Method: POST

    :param uid: the user id
    :param token: the token in verification key
    :return:
    :raises: HTTPError(http.BAD_REQUEST) if verification key for the user is invalid, has expired or was used
    """

    form = ResetPasswordForm(request.form)

    # Check if request bears a valid pair of `uid` and `token`
    user_obj = User.load(uid)
    if not (user_obj and user_obj.verify_password_token(token=token)):
        error_data = {
            'message_short': 'Invalid Request.',
            'message_long': 'The requested URL is invalid, has expired, or was already used',
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    if not form.validate():
        # Don't go anywhere
        forms.push_errors_to_status(form.errors)
    else:
        # clear verification key (v2)
        user_obj.verification_key_v2 = {}
        # new verification key (v1) for CAS
        user_obj.verification_key = generate_verification_key(verification_type=None)
        try:
            user_obj.set_password(form.password.data)
            user_obj.save()
        except exceptions.ChangePasswordError as error:
            for message in error.messages:
                status.push_status_message(message, kind='warning', trust=False)
        else:
            status.push_status_message('Password reset', kind='success', trust=False)
            # redirect to CAS and authenticate the user automatically with one-time verification key.
            return redirect(cas.get_login_url(
                web_url_for('user_account', _absolute=True),
                username=user_obj.username,
                verification_key=user_obj.verification_key
            ))

    return {
        'uid': user_obj._id,
        'token': user_obj.verification_key_v2['token'],
    }
Example #27
0
def claim_user_form(auth, **kwargs):
    """View for rendering the set password page for a claimed user.

    Must have ``token`` as a querystring argument.

    Renders the set password form, validates it, and sets the user's password.
    """
    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(web_url_for('claim_user_registered',
            uid=uid, pid=pid, token=token))

    user = User.load(uid)  # The unregistered user
    # user ID is invalid. Unregistered user is not in database
    if not user:
        raise HTTPError(http.BAD_REQUEST)
    # If claim token not valid, redirect to registration page
    if not verify_claim_token(user, token, pid):
        return redirect('/account/')
    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    email = unclaimed_record['email']
    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if form.validate():
            username, password = email, form.password.data
            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.save()
            # Authenticate user and redirect to project page
            response = redirect('/settings/')
            node = Node.load(pid)
            status.push_status_message(language.CLAIMED_CONTRIBUTOR.format(node=node),
                'success')
            return authenticate(user, response)
        else:
            forms.push_errors_to_status(form.errors)
    return {
        'firstname': user.given_name,
        'email': email if email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #28
0
def reset_password_post(auth, verification_key=None, **kwargs):
    """
    View for user to submit reset password form.
    HTTP Method: POST
    :raises: HTTPError(http.BAD_REQUEST) if verification_key is invalid
    """

    # If user is already logged in, log user out
    if auth.logged_in:
        return auth_logout(redirect_url=request.url)

    form = ResetPasswordForm(request.form)

    # Check if request bears a valid verification_key
    user_obj = get_user(verification_key=verification_key)
    if not user_obj:
        error_data = {
            'message_short': 'Invalid url.',
            'message_long': 'The verification key in the URL is invalid or has expired.'
        }
        raise HTTPError(400, data=error_data)

    if form.validate():
        # new random verification key, allows CAS to authenticate the user w/o password, one-time only.
        # this overwrite also invalidates the verification key generated by forgot_password_post
        user_obj.verification_key = generate_verification_key()
        try:
            user_obj.set_password(form.password.data)
            user_obj.save()
        except exceptions.ChangePasswordError as error:
            for message in error.messages:
                status.push_status_message(message, kind='warning', trust=False)
        else:
            status.push_status_message('Password reset', kind='success', trust=False)
            # redirect to CAS and authenticate the user with the one-time verification key.
            return redirect(cas.get_login_url(
                web_url_for('user_account', _absolute=True),
                username=user_obj.username,
                verification_key=user_obj.verification_key
            ))
    else:
        forms.push_errors_to_status(form.errors)
        # Don't go anywhere

    return {
        'verification_key': verification_key
    }, 400
Example #29
0
def reset_password_post(auth, verification_key=None, **kwargs):
    """
    View for user to submit reset password form.
    HTTP Method: POST
    :raises: HTTPError(http.BAD_REQUEST) if verification_key is invalid
    """

    # If user is already logged in, log user out
    if auth.logged_in:
        return auth_logout(redirect_url=request.url)

    form = ResetPasswordForm(request.form)

    # Check if request bears a valid verification_key
    user_obj = get_user(verification_key=verification_key)
    if not user_obj:
        error_data = {
            'message_short': 'Invalid url.',
            'message_long': 'The verification key in the URL is invalid or has expired.'
        }
        raise HTTPError(400, data=error_data)

    if form.validate():
        # new random verification key, allows CAS to authenticate the user w/o password, one-time only.
        # this overwrite also invalidates the verification key generated by forgot_password_post
        user_obj.verification_key = generate_verification_key()
        try:
            user_obj.set_password(form.password.data)
            user_obj.save()
        except exceptions.ChangePasswordError as error:
            for message in error.messages:
                status.push_status_message(message, kind='warning', trust=False)
        else:
            status.push_status_message('Password reset', kind='success', trust=False)
            # redirect to CAS and authenticate the user with the one-time verification key.
            return redirect(cas.get_login_url(
                web_url_for('user_account', _absolute=True),
                username=user_obj.username,
                verification_key=user_obj.verification_key
            ))
    else:
        forms.push_errors_to_status(form.errors)
        # Don't go anywhere

    return {
        'verification_key': verification_key
    }, 400
Example #30
0
def merge_user_post(**kwargs):
    '''View for merging an account. Takes either JSON or form data.

    Request data should include a "merged_username" and "merged_password" properties
    for the account to be merged in.
    '''
    master = get_current_user()
    if request.json:
        merged_username = request.json.get("merged_username")
        merged_password = request.json.get("merged_password")
    else:
        form = MergeAccountForm(request.form)
        if not form.validate():
            forms.push_errors_to_status(form.errors)
            return merge_user_get(**kwargs)
        master_password = form.user_password.data
        if not master.check_password(master_password):
            status.push_status_message(
                "Could not authenticate. Please check your username and password."
            )
            return merge_user_get(**kwargs)
        merged_username = form.merged_username.data
        merged_password = form.merged_password.data
    try:
        merged_user = User.find_one(Q("username", "eq", merged_username))
    except NoResultsFound:
        status.push_status_message(
            "Could not find that user. Please check the username and password."
        )
        return merge_user_get(**kwargs)
    if master and merged_user:
        if merged_user.check_password(merged_password):
            master.merge_user(merged_user)
            master.save()
            if request.form:
                status.push_status_message(
                    "Successfully merged {0} with this account".format(
                        merged_username))
                return redirect("/settings/")
            return {"status": "success"}
        else:
            status.push_status_message(
                "Could not find that user. Please check the username and password."
            )
            return merge_user_get(**kwargs)
    else:
        raise HTTPError(http.BAD_REQUEST)
Example #31
0
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}
Example #32
0
def merge_user_post(auth, **kwargs):
    '''View for merging an account. Takes either JSON or form data.

    Request data should include a "merged_username" and "merged_password" properties
    for the account to be merged in.
    '''
    master = auth.user
    if request.json:
        merged_username = request.json.get("merged_username")
        merged_password = request.json.get("merged_password")
    else:
        form = MergeAccountForm(request.form)
        if not form.validate():
            forms.push_errors_to_status(form.errors)
            return merge_user_get(**kwargs)
        master_password = form.user_password.data
        if not master.check_password(master_password):
            status.push_status_message("Could not authenticate. Please check your username and password.", trust=False)
            return merge_user_get(**kwargs)
        merged_username = form.merged_username.data
        merged_password = form.merged_password.data
    try:
        merged_user = User.find_one(Q("username", "eq", merged_username))
    except NoResultsFound:
        status.push_status_message("Could not find that user. Please check the username and password.", trust=False)
        return merge_user_get(**kwargs)
    if master and merged_user:
        if merged_user.check_password(merged_password):
            master.merge_user(merged_user)
            master.save()
            if request.form:
                status.push_status_message("Successfully merged {0} with this account".format(merged_username),
                                           kind='success',
                                           trust=False)
                return redirect("/settings/")
            return {"status": "success"}
        else:
            status.push_status_message("Could not find that user. Please check the username and password.",
                                       trust=False)
            return merge_user_get(**kwargs)
    else:
        raise HTTPError(http.BAD_REQUEST)
Example #33
0
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:
            #TODO: Remove this rate limiting and replace it with something that doesn't write to the User model
            now = datetime.datetime.utcnow()
            last_attempt = user_obj.forgot_password_last_post or now - datetime.timedelta(seconds=FORGOT_PASSWORD_MINIMUM_TIME)
            user_obj.forgot_password_last_post = now
            time_since_last_attempt = now - last_attempt
            if time_since_last_attempt.seconds >= FORGOT_PASSWORD_MINIMUM_TIME:
                user_obj.verification_key = security.random_string(20)
                user_obj.save()
                reset_link = "http://{0}{1}".format(
                    request.host,
                    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, 'success')
            else:
                user_obj.save()
                status.push_status_message('You have recently requested to change your password. Please wait a little '
                                           'while before trying again.', 'error')
        else:
            status.push_status_message(status_message, 'success')
    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #34
0
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)
Example #35
0
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}
Example #36
0
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:
            #TODO: Remove this rate limiting and replace it with something that doesn't write to the User model
            now = datetime.datetime.utcnow()
            last_attempt = user_obj.forgot_password_last_post or now - datetime.timedelta(
                seconds=FORGOT_PASSWORD_MINIMUM_TIME)
            user_obj.forgot_password_last_post = now
            time_since_last_attempt = now - last_attempt
            if time_since_last_attempt.seconds >= FORGOT_PASSWORD_MINIMUM_TIME:
                user_obj.verification_key = security.random_string(20)
                user_obj.save()
                reset_link = "http://{0}{1}".format(
                    request.host,
                    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, 'success')
            else:
                user_obj.save()
                status.push_status_message(
                    'You have recently requested to change your password. Please wait a little '
                    'while before trying again.', 'error')
        else:
            status.push_status_message(status_message, 'success')
    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Example #37
0
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)
Example #38
0
def reset_password(**kwargs):

    verification_key = kwargs['verification_key']
    form = ResetPasswordForm(request.form)

    user_obj = get_user(verification_key=verification_key)
    if not user_obj:
        error_data = {'message_short': 'Invalid url.',
            'message_long': 'The verification key in the URL is invalid or '
            'has expired.'}
        raise exceptions.HTTPError(400, data=error_data)

    if request.method == 'POST' and form.validate():
        user_obj.verification_key = None
        user_obj.set_password(form.password.data)
        user_obj.save()
        status.push_status_message('Password reset')
        return redirect('/account/')

    forms.push_errors_to_status(form.errors)
    return {
        'verification_key': verification_key,
    }
Example #39
0
def resend_confirmation():
    """View for resending an email confirmation email.
    """
    form = ResendConfirmationForm(request.form)
    if request.method == 'POST':
        if form.validate():
            clean_email = form.email.data
            user = get_user(email=clean_email)
            if not user:
                return {'form': form}
            try:
                send_confirm_email(user, clean_email)
            except KeyError:  # already confirmed, redirect to dashboard
                status_message = 'Email has already been confirmed.'
                kind = 'warning'
            else:
                status_message = 'Resent email to {0}'.format(clean_email)
                kind = 'success'
            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}
Example #40
0
def resend_confirmation():
    """View for resending an email confirmation email.
    """
    form = ResendConfirmationForm(request.form)
    if request.method == 'POST':
        if form.validate():
            clean_email = form.email.data
            user = get_user(email=clean_email)
            if not user:
                return {'form': form}
            try:
                send_confirm_email(user, clean_email)
            except KeyError:  # already confirmed, redirect to dashboard
                status_message = 'Email has already been confirmed.'
                type_ = 'warning'
            else:
                status_message = 'Resent email to <em>{0}</em>'.format(clean_email)
                type_ = 'success'
            status.push_status_message(status_message, type_)
        else:
            forms.push_errors_to_status(form.errors)
    # Don't go anywhere
    return {'form': form}
Example #41
0
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}
Example #42
0
def claim_user_form(auth, **kwargs):
    """
    View for rendering the set password page for a claimed user.
    Must have ``token`` as a querystring argument.
    Renders the set password form, validates it, and sets the user's password.
    HTTP Method: GET, POST
    """

    uid, pid = kwargs["uid"], kwargs["pid"]
    token = request.form.get("token") or request.args.get("token")
    user = User.load(uid)

    # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error
    if not user or not verify_claim_token(user, token, pid):
        error_data = {
            "message_short": "Invalid url.",
            "message_long": "Claim user does not exists, the token in the URL is invalid or has expired.",
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(web_url_for("claim_user_registered", uid=uid, pid=pid, token=token))

    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record["name"]
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get("claimer_email") or unclaimed_record.get("email")

    # If there is a registered user with this email, redirect to 're-enter password' page
    found_by_email = User.find_by_email(claimer_email)
    user_from_email = found_by_email[0] if found_by_email else None
    if user_from_email and user_from_email.is_registered:
        return redirect(web_url_for("claim_user_registered", uid=uid, pid=pid, token=token))

    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == "POST":
        if not form.validate():
            forms.push_errors_to_status(form.errors)
        elif settings.RECAPTCHA_SITE_KEY and not validate_recaptcha(
            request.form.get("g-recaptcha-response"), remote_ip=request.remote_addr
        ):
            status.push_status_message("Invalid captcha supplied.", kind="error")
        else:
            username, password = claimer_email, form.password.data
            if not username:
                raise HTTPError(
                    http.BAD_REQUEST,
                    data=dict(
                        message_long="No email associated with this account. Please claim this "
                        "account on the project to which you were invited."
                    ),
                )

            user.register(username=username, password=password)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = generate_verification_key()
            user.save()
            # Authenticate user and redirect to project page
            status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind="success", trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            return redirect(
                cas.get_login_url(
                    web_url_for("view_project", pid=pid, _absolute=True),
                    username=user.username,
                    verification_key=user.verification_key,
                )
            )

    return {
        "firstname": user.given_name,
        "email": claimer_email if claimer_email else "",
        "fullname": user.fullname,
        "form": forms.utils.jsonify(form) if is_json_request() else form,
    }
Example #43
0
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']

    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)
            # 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)
            # 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
    }
Example #44
0
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']

    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)
            # 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)
            # 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
    }
Example #45
0
def _forgot_password_post(mail_template, reset_route, institutional=False):
    """
    View for user to submit forgot password form (standard or institutional).  Validates submitted
    form and sends reset-password link via email if valid.  If user has submitted another password
    reset request recently, declines to create a new one and asks the user to not submit again for
    awhile.

    Standard and institutional forgot-password requests behave similarly but use slightly different
    language and interfaces. When an institution is deactivated, the user should be given the
    opportunity to reclaim their account. CAS co-ops the forgot-password functionality to send a
    "set a new password" email link to the institutional user.  The language of the email has been
    adjusted from the standard context, the response html the status message from the reset
    action is displayed as regular text, and the password form is not shown.

    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'
            # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email.
            elif user_obj.is_active:
                # new random verification key (v2)
                user_obj.verification_key_v2 = generate_verification_key(verification_type='password')
                user_obj.email_last_sent = timezone.now()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for(
                        reset_route,
                        uid=user_obj._id,
                        token=user_obj.verification_key_v2['token']
                    )
                )
                mails.send_mail(
                    to_addr=email,
                    mail=mail_template,
                    reset_link=reset_link,
                    can_change_preferences=False,
                )

        # institutional forgot password page displays the message as main text, not as an alert
        if institutional:
            # pass isError instead of kind to template to decouple python error flag from template's
            # css class
            return {'message': status_message, 'isError': (kind == 'error'),
                    'institutional': institutional}

        status.push_status_message(status_message, kind=kind, trust=False)

    return {}
Example #46
0
def claim_user_registered(auth, node, **kwargs):
    """
    View that prompts user to enter their password in order to claim being a contributor on a project.
    A user must be logged in.
    """

    current_user = auth.user

    sign_out_url = cas.get_logout_url(service_url=cas.get_login_url(
        service_url=request.url))
    if not current_user:
        return redirect(sign_out_url)

    # Logged in user should not be a contributor the project
    if hasattr(node, 'is_contributor') and node.is_contributor(current_user):
        data = {
            'message_short':
            'Already a contributor',
            'message_long':
            ('The logged-in user is already a contributor to this '
             'project. Would you like to <a href="{}">log out</a>?'
             ).format(sign_out_url)
        }
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data)

    # Logged in user is already a member of the OSF Group
    if hasattr(node, 'is_member') and node.is_member(current_user):
        data = {
            'message_short':
            'Already a member',
            'message_long':
            ('The logged-in user is already a member of this OSF Group. '
             'Would you like to <a href="{}">log out</a>?'
             ).format(sign_out_url)
        }
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=data)

    uid, pid, token = kwargs['uid'], kwargs['pid'], kwargs['token']
    unreg_user = OSFUser.load(uid)
    if not verify_claim_token(unreg_user, token, pid=node._primary_key):
        error_data = {
            'message_short': 'Invalid url.',
            'message_long': 'The token in the URL is invalid or has expired.'
        }
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data)

    # Store the unreg_user data on the session in case the user registers
    # a new account
    session.data['unreg_user'] = {'uid': uid, 'pid': pid, 'token': token}
    session.save()

    # If a user is already validated though external auth, it is OK to claim
    should_claim = check_external_auth(auth.user)
    form = PasswordForm(request.form)
    if request.method == 'POST':
        if form.validate():
            if current_user.check_password(form.password.data):
                should_claim = True
            else:
                status.push_status_message(language.LOGIN_FAILED,
                                           kind='warning',
                                           trust=False)
        else:
            forms.push_errors_to_status(form.errors)
    if should_claim:
        node.replace_contributor(old=unreg_user, new=current_user)
        node.save()
        if isinstance(node, OSFGroup):
            status.push_status_message(
                'You are now a member of this OSFGroup.',
                kind='success',
                trust=False)
        else:
            status.push_status_message(
                'You are now a contributor to this project.',
                kind='success',
                trust=False)
        return redirect(node.url)
    if is_json_request():
        form_ret = forms.utils.jsonify(form)
        user_ret = profile_utils.serialize_user(current_user, full=False)
    else:
        form_ret = form
        user_ret = current_user
    return {'form': form_ret, 'user': user_ret, 'signOutUrl': sign_out_url}
Example #47
0
def claim_user_form(auth, **kwargs):
    """
    View for rendering the set password page for a claimed user.
    Must have ``token`` as a querystring argument.
    Renders the set password form, validates it, and sets the user's password.
    HTTP Method: GET, POST
    """

    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')
    user = OSFUser.load(uid)

    # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error
    if not user or not verify_claim_token(user, token, pid):
        error_data = {
            'message_short':
            'Invalid url.',
            'message_long':
            'Claim user does not exists, the token in the URL is invalid or has expired.'
        }
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=error_data)

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(
            web_url_for('claim_user_registered', uid=uid, pid=pid,
                        token=token))

    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get(
        'claimer_email') or unclaimed_record.get('email')
    # If there is a registered user with this email, redirect to 're-enter password' page
    try:
        user_from_email = OSFUser.objects.get(
            emails__address=claimer_email.lower().strip(
            )) if claimer_email else None
    except OSFUser.DoesNotExist:
        user_from_email = None
    if user_from_email and user_from_email.is_registered:
        return redirect(
            web_url_for('claim_user_registered', uid=uid, pid=pid,
                        token=token))

    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if not form.validate():
            forms.push_errors_to_status(form.errors)
        elif settings.RECAPTCHA_SITE_KEY and not validate_recaptcha(
                request.form.get('g-recaptcha-response'),
                remote_ip=request.remote_addr):
            status.push_status_message('Invalid captcha supplied.',
                                       kind='error')
        else:
            username, password = claimer_email, form.password.data
            if not username:
                raise HTTPError(
                    http_status.HTTP_400_BAD_REQUEST,
                    data=dict(
                        message_long=
                        'No email associated with this account. Please claim this '
                        'account on the project to which you were invited.'))

            user.register(
                username=username,
                password=password,
                accepted_terms_of_service=form.accepted_terms_of_service.data)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = generate_verification_key()
            user.save()
            # Authenticate user and redirect to project page
            status.push_status_message(language.CLAIMED_CONTRIBUTOR,
                                       kind='success',
                                       trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            provider = PreprintProvider.load(pid)
            redirect_url = None
            if provider:
                redirect_url = web_url_for('auth_login',
                                           next=provider.landing_url,
                                           _absolute=True)
            else:
                redirect_url = web_url_for('resolve_guid',
                                           guid=pid,
                                           _absolute=True)

            return redirect(
                cas.get_login_url(redirect_url,
                                  username=user.username,
                                  verification_key=user.verification_key))

    return {
        'firstname': user.given_name,
        'email': claimer_email if claimer_email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
        'osf_contact_email': settings.OSF_CONTACT_EMAIL,
    }
Example #48
0
def claim_user_form(auth, **kwargs):
    """
    View for rendering the set password page for a claimed user.
    Must have ``token`` as a querystring argument.
    Renders the set password form, validates it, and sets the user's password.
    HTTP Method: GET, POST
    """

    uid, pid = kwargs['uid'], kwargs['pid']
    token = request.form.get('token') or request.args.get('token')
    user = OSFUser.load(uid)

    # If unregistered user is not in database, or url bears an invalid token raise HTTP 400 error
    if not user or not verify_claim_token(user, token, pid):
        error_data = {
            'message_short': 'Invalid url.',
            'message_long': 'Claim user does not exists, the token in the URL is invalid or has expired.'
        }
        raise HTTPError(http.BAD_REQUEST, data=error_data)

    # If user is logged in, redirect to 're-enter password' page
    if auth.logged_in:
        return redirect(web_url_for('claim_user_registered',
            uid=uid, pid=pid, token=token))

    unclaimed_record = user.unclaimed_records[pid]
    user.fullname = unclaimed_record['name']
    user.update_guessed_names()
    # The email can be the original referrer email if no claimer email has been specified.
    claimer_email = unclaimed_record.get('claimer_email') or unclaimed_record.get('email')
    # If there is a registered user with this email, redirect to 're-enter password' page
    try:
        user_from_email = OSFUser.objects.get(emails__address=claimer_email.lower().strip()) if claimer_email else None
    except OSFUser.DoesNotExist:
        user_from_email = None
    if user_from_email and user_from_email.is_registered:
        return redirect(web_url_for('claim_user_registered', uid=uid, pid=pid, token=token))

    form = SetEmailAndPasswordForm(request.form, token=token)
    if request.method == 'POST':
        if not form.validate():
            forms.push_errors_to_status(form.errors)
        elif settings.RECAPTCHA_SITE_KEY and not validate_recaptcha(request.form.get('g-recaptcha-response'), remote_ip=request.remote_addr):
            status.push_status_message('Invalid captcha supplied.', kind='error')
        else:
            username, password = claimer_email, form.password.data
            if not username:
                raise HTTPError(http.BAD_REQUEST, data=dict(
                    message_long='No email associated with this account. Please claim this '
                    'account on the project to which you were invited.'
                ))

            user.register(username=username, password=password, accepted_terms_of_service=form.accepted_terms_of_service.data)
            # Clear unclaimed records
            user.unclaimed_records = {}
            user.verification_key = generate_verification_key()
            user.save()
            # Authenticate user and redirect to project page
            status.push_status_message(language.CLAIMED_CONTRIBUTOR, kind='success', trust=True)
            # Redirect to CAS and authenticate the user with a verification key.
            provider = PreprintProvider.load(pid)
            redirect_url = None
            if provider:
                redirect_url = web_url_for('auth_login', next=provider.landing_url, _absolute=True)
            else:
                redirect_url = web_url_for('resolve_guid', guid=pid, _absolute=True)

            return redirect(cas.get_login_url(
                redirect_url,
                username=user.username,
                verification_key=user.verification_key
            ))

    return {
        'firstname': user.given_name,
        'email': claimer_email if claimer_email else '',
        'fullname': user.fullname,
        'form': forms.utils.jsonify(form) if is_json_request() else form,
        'osf_contact_email': settings.OSF_CONTACT_EMAIL,
    }
Example #49
0
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
    }