Example #1
0
def node_register_template_page(auth, node, metaschema_id, **kwargs):
    if node.is_registration and bool(node.registered_schema):
        try:
            meta_schema = MetaSchema.find_one(
                Q('_id', 'eq', metaschema_id)
            )
        except NoResultsFound:
            # backwards compatability for old urls, lookup by name
            try:
                meta_schema = MetaSchema.find(
                    Q('name', 'eq', _id_to_name(metaschema_id))
                ).sort('-schema_version')[0]
            except IndexError:
                raise HTTPError(http.NOT_FOUND, data={
                    'message_short': 'Invalid schema name',
                    'message_long': 'No registration schema with that name could be found.'
                })
        if meta_schema not in node.registered_schema:
            raise HTTPError(http.BAD_REQUEST, data={
                'message_short': 'Invalid schema',
                'message_long': 'This registration has no registration supplment with that name.'
            })

        ret = _view_project(node, auth, primary=True)
        ret['node']['registered_schema'] = serialize_meta_schema(meta_schema)
        return ret
    else:
        status.push_status_message(
            'You have been redirected to the project\'s registrations page. From here you can initiate a new Draft Registration to complete the registration process',
            trust=False
        )
        return redirect(node.web_url_for('node_registrations', view=kwargs.get('template')))
Example #2
0
def project_generate_private_link_post(auth, node, **kwargs):
    """ creata a new private link object and add it to the node and its selected children"""

    node_ids = request.json.get('node_ids', [])
    name = request.json.get('name', '')
    anonymous = request.json.get('anonymous', False)

    if node._id not in node_ids:
        node_ids.insert(0, node._id)

    nodes = [Node.load(node_id) for node_id in node_ids]

    has_public_node = any(node.is_public for node in nodes)

    new_link = new_private_link(
        name=name, user=auth.user, nodes=nodes, anonymous=anonymous
    )

    if anonymous and has_public_node:
        status.push_status_message(
            'Anonymized view-only links <b>DO NOT</b> '
            'anonymize contributors of public project or component.'
        )

    return new_link
Example #3
0
def confirm_email_get(**kwargs):
    """View for email confirmation links.
    Authenticates and redirects to user settings page if confirmation is
    successful, otherwise shows an "Expired Link" error.

    methods: GET
    """
    user = User.load(kwargs['uid'])
    token = kwargs['token']
    if user:
        if user.confirm_email(token):  # Confirm and register the user
            user.date_last_login = datetime.datetime.utcnow()
            user.save()

            # Go to settings page
            status.push_status_message(language.WELCOME_MESSAGE, 'success')
            response = redirect('/settings/')

            return framework.auth.authenticate(user, response=response)
    # Return data for the error template
    return {
        'code': http.BAD_REQUEST,
        'message_short': 'Link Expired',
        'message_long': language.LINK_EXPIRED
    }, http.BAD_REQUEST
Example #4
0
def embargo_termination_handler(action, registration, registered_from):
    status.push_status_message({
        'approve': 'Your approval to make this embargo public has been accepted.',
        'reject': 'Your disapproval has been accepted and this embargo will not be made public.',
    }[action], kind='success', trust=False)
    # Allow decorated view function to return response
    return None
Example #5
0
def node_registration_embargo_disapprove(auth, node, token, **kwargs):
    """Handles disapproval of registration embargoes
    :param auth: User wanting to disapprove the embargo
    :return: Redirect to registration or
    :raises: HTTPError if invalid token or user is not admin
    """

    if not node.pending_embargo:
        raise HTTPError(
            http.BAD_REQUEST,
            data={"message_short": "Invalid Token", "message_long": "This registration is not pending an embargo."},
        )
    # Note(hryabcki): node.registered_from not accessible after disapproval
    if node.embargo.for_existing_registration:
        redirect_url = node.web_url_for("view_project")
    else:
        redirect_url = node.registered_from.web_url_for("view_project")
    try:
        node.embargo.disapprove_embargo(auth.user, token)
        node.embargo.save()
    except InvalidEmbargoDisapprovalToken as e:
        raise HTTPError(http.BAD_REQUEST, data={"message_short": e.message_short, "message_long": e.message_long})
    except PermissionsError as e:
        raise HTTPError(http.FORBIDDEN, data={"message_short": "Unauthorized access", "message_long": e.message})

    status.push_status_message("Your disapproval has been accepted and the embargo has been cancelled.", "success")
    return redirect(redirect_url)
Example #6
0
def node_registration_embargo_approve(auth, node, token, **kwargs):
    """Handles approval of registration embargoes
    :param auth: User wanting to approve the embargo
    :param kwargs:
    :return: Redirect to registration or
    :raises: HTTPError if invalid token or user is not admin
    """

    if not node.pending_embargo:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': 'Invalid Token',
            'message_long': 'This registration is not pending an embargo.'
        })

    try:
        node.embargo.approve_embargo(auth.user, token)
        node.embargo.save()
    except InvalidEmbargoApprovalToken as e:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': e.message_short,
            'message_long': e.message_long
        })
    except PermissionsError as e:
        raise HTTPError(http.FORBIDDEN, data={
            'message_short': 'Unauthorized access',
            'message_long': e.message
        })

    status.push_status_message('Your approval has been accepted.', kind='success', trust=False)
    return redirect(node.web_url_for('view_project'))
Example #7
0
def node_register_template_page(auth, node, metaschema_id, **kwargs):
    if node.is_registration and bool(node.registered_schema):
        try:
            meta_schema = RegistrationSchema.objects.get(_id=metaschema_id)
        except RegistrationSchema.DoesNotExist:
            # backwards compatability for old urls, lookup by name
            meta_schema = RegistrationSchema.objects.filter(name=_id_to_name(metaschema_id)).order_by('-schema_version').first()
            if not meta_schema:
                raise HTTPError(http.NOT_FOUND, data={
                    'message_short': 'Invalid schema name',
                    'message_long': 'No registration schema with that name could be found.'
                })
        if not node.registered_schema.filter(id=meta_schema.id).exists():
            raise HTTPError(http.BAD_REQUEST, data={
                'message_short': 'Invalid schema',
                'message_long': 'This registration has no registration supplment with that name.'
            })

        ret = _view_project(node, auth, primary=True)
        my_meta = serialize_meta_schema(meta_schema)
        if has_anonymous_link(node, auth):
            for indx, schema_page in enumerate(my_meta['schema']['pages']):
                for idx, schema_question in enumerate(schema_page['questions']):
                    if schema_question['title'] in settings.ANONYMIZED_TITLES:
                        del my_meta['schema']['pages'][indx]['questions'][idx]
        ret['node']['registered_schema'] = serialize_meta_schema(meta_schema)
        return ret
    else:
        status.push_status_message(
            'You have been redirected to the project\'s registrations page. From here you can initiate a new Draft Registration to complete the registration process',
            trust=False,
            id='redirected_to_registrations',
        )
        return redirect(node.web_url_for('node_registrations', view=kwargs.get('template'), _guid=True))
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 project_new_node(auth, node, **kwargs):
    form = NewNodeForm(request.form)
    user = auth.user
    if form.validate():
        try:
            new_component = new_node(
                title=strip_html(form.title.data),
                user=user,
                category=form.category.data,
                parent=node,
            )
        except ValidationValueError as e:
            raise HTTPError(
                http.BAD_REQUEST,
                data=dict(message_long=e.message)
            )

        message = (
            'Your component was created successfully. You can keep working on the component page below, '
            'or return to the <u><a href="{url}">project page</a></u>.'
        ).format(url=node.url)
        status.push_status_message(message, kind='info', trust=True)

        return {
            'status': 'success',
        }, 201, None, new_component.url
    else:
        # TODO: This function doesn't seem to exist anymore?
        status.push_errors_to_status(form.errors)
    raise HTTPError(http.BAD_REQUEST, redirect_url=node.url)
Example #10
0
def node_registration_retraction_disapprove(auth, node, token, **kwargs):
    """Handles approval of registration retractions
    :param auth: User wanting to approve retraction
    :param kwargs:
    :return: Redirect to registration or
    :raises: HTTPError if invalid token or user is not admin
    """

    if not node.pending_retraction:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': 'Invalid Token',
            'message_long': 'This registration is not pending a retraction.'
        })

    try:
        node.retraction.disapprove_retraction(auth.user, token)
        node.retraction.save()
    except InvalidRetractionDisapprovalToken as e:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': e.message_short,
            'message_long': e.message_long
        })
    except PermissionsError as e:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': 'Unauthorized access',
            'message_long': e.message
        })

    status.push_status_message('Your disapproval has been accepted and the retraction has been cancelled.', kind='success', trust=False)
    return redirect(node.web_url_for('view_project'))
Example #11
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 #12
0
def retraction_handler(action, registration, registered_from):
    status.push_status_message({
        'approve': 'Your withdrawal approval has been accepted.',
        'reject': 'Your disapproval has been accepted and the withdrawal has been cancelled.'
    }[action], kind='success', trust=False)
    # Allow decorated view function to return response
    return None
Example #13
0
def component_remove(auth, node, **kwargs):
    """Remove component, and recursively remove its children. If node has a
    parent, add log and redirect to parent; else redirect to user dashboard.

    """
    try:
        node.remove_node(auth)
    except NodeStateError as e:
        raise HTTPError(
            http.BAD_REQUEST,
            data={
                'message_short': 'Error',
                'message_long': 'Could not delete component: ' + e.message
            },
        )
    node.save()

    message = '{} deleted'.format(
        node.project_or_component.capitalize()
    )
    status.push_status_message(message)
    parent = node.parent_node
    if parent and parent.can_view(auth):
        redirect_url = node.node__parent[0].url
    else:
        redirect_url = '/dashboard/'

    return {
        'url': redirect_url,
    }
Example #14
0
def project_removecontributor(auth, node, **kwargs):

    contributor = User.load(request.json['id'])
    if contributor is None:
        raise HTTPError(http.BAD_REQUEST)

    # Forbidden unless user is removing herself
    if not node.has_permission(auth.user, 'admin'):
        if auth.user != contributor:
            raise HTTPError(http.FORBIDDEN)

    outcome = node.remove_contributor(
        contributor=contributor, auth=auth,
    )

    if outcome:
        if auth.user == contributor:
            status.push_status_message('Removed self from project', 'info')
            return {'redirectUrl': web_url_for('dashboard')}
        status.push_status_message('Contributor removed', 'info')
        return {}

    raise HTTPError(
        http.BAD_REQUEST,
        data={
            'message_long': (
                '{0} must have at least one contributor with admin '
                'rights'.format(
                    node.project_or_component.capitalize()
                )
            )
        }
    )
Example #15
0
def submit_draft_for_review(auth, node, draft, *args, **kwargs):
    """Submit for approvals and/or notifications

    :return: serialized registration
    :rtype: dict
    :raises: HTTPError if embargo end date is invalid
    """
    data = request.get_json()
    meta = {}
    registration_choice = data.get('registrationChoice', 'immediate')
    validate_registration_choice(registration_choice)
    if registration_choice == 'embargo':
        # Initiate embargo
        end_date_string = data['embargoEndDate']
        validate_embargo_end_date(end_date_string, node)
        meta['embargo_end_date'] = end_date_string
    meta['registration_choice'] = registration_choice
    draft.submit_for_review(
        initiated_by=auth.user,
        meta=meta,
        save=True
    )

    push_status_message(language.AFTER_SUBMIT_FOR_REVIEW,
                        kind='info',
                        trust=False)
    return {
        'status': 'initiated',
        'urls': {
            'registrations': node.web_url_for('node_registrations')
        }
    }, http.ACCEPTED
Example #16
0
def auth_email_logout(token, user):
    """
    When a user is adding an email or merging an account, add the email to the user and log them out.
    """

    redirect_url = cas.get_logout_url(service_url=cas.get_login_url(service_url=web_url_for('index', _absolute=True)))
    try:
        unconfirmed_email = user.get_unconfirmed_email_for_token(token)
    except InvalidTokenError:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': 'Bad token',
            'message_long': 'The provided token is invalid.'
        })
    except ExpiredTokenError:
        status.push_status_message('The private link you used is expired.')
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': 'Expired link',
            'message_long': 'The private link you used is expired.'
        })
    try:
        user_merge = User.find_one(Q('emails', 'eq', unconfirmed_email))
    except NoResultsFound:
        user_merge = False
    if user_merge:
        remove_sessions_for_user(user_merge)
    user.email_verifications[token]['confirmed'] = True
    user.save()
    remove_sessions_for_user(user)
    resp = redirect(redirect_url)
    resp.delete_cookie(settings.COOKIE_NAME, domain=settings.OSF_COOKIE_DOMAIN)
    return resp
Example #17
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 #18
0
def user_account_password(auth, **kwargs):
    user = auth.user
    old_password = request.form.get('old_password', None)
    new_password = request.form.get('new_password', None)
    confirm_password = request.form.get('confirm_password', None)

    # It has been more than 1 hour since last invalid attempt to change password. Reset the counter for invalid attempts.
    if throttle_period_expired(user.change_password_last_attempt, settings.TIME_RESET_CHANGE_PASSWORD_ATTEMPTS):
        user.reset_old_password_invalid_attempts()

    # There have been more than 3 failed attempts and throttle hasn't expired.
    if user.old_password_invalid_attempts >= settings.INCORRECT_PASSWORD_ATTEMPTS_ALLOWED and not throttle_period_expired(user.change_password_last_attempt, settings.CHANGE_PASSWORD_THROTTLE):
        push_status_message(
            message='Too many failed attempts. Please wait a while before attempting to change your password.',
            kind='warning',
            trust=False
        )
        return redirect(web_url_for('user_account'))

    try:
        user.change_password(old_password, new_password, confirm_password)
    except ChangePasswordError as error:
        for m in error.messages:
            push_status_message(m, kind='warning', trust=False)
    else:
        # We have to logout the user first so all CAS sessions are invalid
        user.save()
        osf_logout()
        return redirect(cas.get_logout_url(cas.get_login_url(
            web_url_for('user_account', _absolute=True) + '?password_reset=True',
            username=user.username,
            verification_key=user.verification_key,
        )))
    user.save()
    return redirect(web_url_for('user_account'))
Example #19
0
def component_remove(**kwargs):
    """Remove component, and recursively remove its children. If node has a
    parent, add log and redirect to parent; else redirect to user dashboard.

    """
    node_to_use = kwargs['node'] or kwargs['project']
    auth = kwargs['auth']

    try:
        node_to_use.remove_node(auth)
    except NodeStateError as e:
        raise HTTPError(
            http.BAD_REQUEST,
            data={
                'message_long': 'Could not delete component: ' + e.message
            },
        )
    node_to_use.save()

    message = '{} deleted'.format(
        node_to_use.project_or_component.capitalize()
    )
    status.push_status_message(message)
    if node_to_use.node__parent:
        redirect_url = node_to_use.node__parent[0].url
    else:
        redirect_url = '/dashboard/'

    return {
        'url': redirect_url,
    }
Example #20
0
def node_registration_retraction_approve(auth, node, token, **kwargs):
    """Handles disapproval of registration retractions
    :param auth: User wanting to disapprove retraction
    :return: Redirect to registration or
    :raises: HTTPError if invalid token or user is not admin
    """

    if not node.pending_retraction:
        raise HTTPError(
            http.BAD_REQUEST,
            data={"message_short": "Invalid Token", "message_long": "This registration is not pending a retraction."},
        )

    try:
        node.retraction.approve_retraction(auth.user, token)
        node.retraction.save()
        if node.is_retracted:
            node.update_search()
    except InvalidRetractionApprovalToken as e:
        raise HTTPError(http.BAD_REQUEST, data={"message_short": e.message_short, "message_long": e.message_long})
    except PermissionsError as e:
        raise HTTPError(http.BAD_REQUEST, data={"message_short": "Unauthorized access", "message_long": e.message})

    status.push_status_message("Your approval has been accepted.", "success")
    return redirect(node.web_url_for("view_project"))
Example #21
0
 def test_push_status_message_unexpected_error(self, mock_sesh):
     status_message = 'This is a message'
     exception_message = 'this is some very unexpected problem'
     mock_get = mock.Mock(side_effect=RuntimeError(exception_message))
     mock_data = mock.Mock()
     mock_data.attach_mock(mock_get, 'get')
     mock_sesh.attach_mock(mock_data, 'data')
     try:
         push_status_message(status_message, kind='error')
         assert_true(
             False,
             'push_status_message() should have generated a RuntimeError exception.'
         )
     except ValidationError as e:
         assert_true(
             False,
             'push_status_message() should have re-raised the RuntimeError not gotten ValidationError.'
         )
     except RuntimeError as e:
         assert_equal(getattr(e, 'message', None),
                      exception_message,
                      'push_status_message() should have re-raised the '
                      'original RuntimeError with the original message.')
     except BaseException:
         assert_true(
             False, 'Unexpected Exception from push_status_message when called '
             'from the v2 API with type "error"')
Example #22
0
def project_generate_private_link_post(auth, node, **kwargs):
    """ creata a new private link object and add it to the node and its selected children"""

    node_ids = request.json.get('node_ids', [])
    name = request.json.get('name', '')

    anonymous = request.json.get('anonymous', False)

    if node._id not in node_ids:
        node_ids.insert(0, node._id)

    nodes = [Node.load(node_id) for node_id in node_ids]

    has_public_node = any(node.is_public for node in nodes)

    try:
        new_link = new_private_link(
            name=name, user=auth.user, nodes=nodes, anonymous=anonymous
        )
    except ValidationValueError as e:
        raise HTTPError(
            http.BAD_REQUEST,
            data=dict(message_long=e.message)
        )

    if anonymous and has_public_node:
        status.push_status_message(
            'Anonymized view-only links <b>DO NOT</b> '
            'anonymize contributors of public projects or components.',
            trust=True
        )

    return new_link
Example #23
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 #24
0
 def on_add(self):
     push_status_message('Please <u><a href="#TfaVerify">activate your'
                         ' device</a></u> before continuing.', 'info')
     super(TwoFactorUserSettings, self).on_add()
     self.totp_secret = _generate_seed()
     self.totp_drift = 0
     self.is_confirmed = False
Example #25
0
def confirm_email_get(token, auth=None, **kwargs):
    """View for email confirmation links.
    Authenticates and redirects to user settings page if confirmation is
    successful, otherwise shows an "Expired Link" error.

    methods: GET
    """
    user = User.load(kwargs['uid'])
    is_merge = 'confirm_merge' in request.args
    is_initial_confirmation = not user.date_confirmed

    if user is None:
        raise HTTPError(http.NOT_FOUND)

    if auth and auth.user and (auth.user._id == user._id or auth.user._id == user.merged_by._id):
        if not is_merge:
            # determine if the user registered through a campaign
            campaign = campaigns.campaign_for_user(user)
            if campaign:
                return redirect(
                    campaigns.campaign_url_for(campaign)
                )
            status.push_status_message(language.WELCOME_MESSAGE, 'default', jumbotron=True)
            # Go to dashboard
            return redirect(web_url_for('dashboard'))

        status.push_status_message(language.MERGE_COMPLETE, 'success')
        return redirect(web_url_for('user_account'))

    try:
        user.confirm_email(token, merge=is_merge)
    except exceptions.EmailConfirmTokenError as e:
        raise HTTPError(http.BAD_REQUEST, data={
            'message_short': e.message_short,
            'message_long': e.message_long
        })

    if is_initial_confirmation:
        user.date_last_login = datetime.datetime.utcnow()
        user.save()

        # Send out our welcome message
        mails.send_mail(
            to_addr=user.username,
            mail=mails.WELCOME,
            mimetype='html',
            user=user
        )

    # Redirect to CAS and authenticate the user with a verification key.
    user.verification_key = security.random_string(20)
    user.save()

    return redirect(cas.get_login_url(
        request.url,
        auto=True,
        username=user.username,
        verification_key=user.verification_key
    ))
Example #26
0
def auth_register(auth):
    """
    View for OSF register. Land on the register page, redirect or go to `auth_logout`
    depending on `data` returned by `login_and_register_handler`.

    `/register` only takes a valid campaign, a valid next, the logout flag or no query parameter
    `login_and_register_handler()` handles the following cases:
        if campaign and logged in, go to campaign landing page (or valid next_url if presents)
        if campaign and logged out, go to campaign register page (with next_url if presents)
        if next_url and logged in, go to next url
        if next_url and logged out, go to cas login page with current request url as service parameter
        if next_url and logout flag, log user out first and then go to the next_url
        if none, go to `/dashboard` which is decorated by `@must_be_logged_in`

    :param auth: the auth context
    :return: land, redirect or `auth_logout`
    :raise: http.BAD_REQUEST
    """

    context = {}
    # a target campaign in `auth.campaigns`
    campaign = request.args.get('campaign')
    # the service url for CAS login or redirect url for OSF
    next_url = request.args.get('next')
    # used only for `claim_user_registered`
    logout = request.args.get('logout')

    # logout must have next_url
    if logout and not next_url:
        raise HTTPError(http.BAD_REQUEST)

    data = login_and_register_handler(auth, login=False, campaign=campaign, next_url=next_url, logout=logout)

    # land on register page
    if data['status_code'] == http.OK:
        if data['must_login_warning']:
            status.push_status_message(language.MUST_LOGIN, trust=False)
        destination = cas.get_login_url(data['next_url'])
        # "Already have and account?" link
        context['non_institution_login_url'] = destination
        # "Sign In" button in navigation bar, overwrite the default value set in routes.py
        context['login_url'] = destination
        # "Login through your institution" link
        context['institution_login_url'] = cas.get_login_url(data['next_url'], campaign='institution')
        context['preprint_campaigns'] = {k._id + '-preprints': {
            'id': k._id,
            'name': k.name,
            'logo_path': k.get_asset_url('square_color_no_transparent')
        } for k in PreprintProvider.objects.all() if k._id != 'osf'}
        context['campaign'] = data['campaign']
        return context, http.OK
    # redirect to url
    elif data['status_code'] == http.FOUND:
        return redirect(data['next_url'])
    # go to other views
    elif data['status_code'] == 'auth_logout':
        return auth_logout(redirect_url=data['next_url'])

    raise HTTPError(http.BAD_REQUEST)
Example #27
0
 def test_push_status_message_no_response(self):
     status_message = 'This is a message'
     statuses = ['info', 'warning', 'warn', 'success', 'danger', 'default']
     for status in statuses:
         try:
             push_status_message(status_message, kind=status)
         except:
             assert_true(False, 'Exception from push_status_message via API v2 with type "{}".'.format(status))
Example #28
0
 def test_push_status_message_no_response(self):
     status_message = "This is a message"
     statuses = ["info", "warning", "warn", "success", "danger", "default"]
     for status in statuses:
         try:
             push_status_message(status_message, kind=status)
         except:
             assert_true(False, 'Exception from push_status_message via API v2 with type "{}".'.format(status))
Example #29
0
def node_fork_page(auth, node, **kwargs):
    try:
        fork = node.fork_node(auth)
    except PermissionsError:
        raise HTTPError(http.FORBIDDEN, redirect_url=node.url)
    message = "{} has been successfully forked.".format(node.project_or_component.capitalize())
    status.push_status_message(message, kind="success", trust=False)
    return fork.url
Example #30
0
def registration_approval_handler(action, registration, registered_from):
    # TODO: Unnecessary and duplicated dictionary.
    status.push_status_message({
        'approve': 'Your registration approval has been accepted.',
        'reject': 'Your disapproval has been accepted and the registration has been cancelled.',
    }[action], kind='success', trust=False)
    # Allow decorated view function to return response
    return None
Example #31
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'
            # 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_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 project_remove_contributor(auth, **kwargs):
    """Remove a contributor from a list of nodes.

    :param Auth auth: Consolidated authorization
    :raises: HTTPError(400) if contributors to be removed are not in list
        or if no admin users would remain after changes were applied

    """
    contributor_id = request.get_json()['contributorID']
    node_ids = request.get_json()['nodeIDs']
    contributor = User.load(contributor_id)
    if contributor is None:
        raise HTTPError(http.BAD_REQUEST, data={'message_long': 'Contributor not found.'})
    redirect_url = {}
    parent_id = node_ids[0]
    for node_id in node_ids:
        # Update permissions and order
        node = Node.load(node_id)

        # Forbidden unless user is removing herself
        if not node.has_permission(auth.user, 'admin'):
            if auth.user != contributor:
                raise HTTPError(http.FORBIDDEN)

        if len(node.visible_contributor_ids) == 1 \
                and node.visible_contributor_ids[0] == contributor._id:
            raise HTTPError(http.FORBIDDEN, data={
                'message_long': 'Must have at least one bibliographic contributor'
            })

        nodes_removed = node.remove_contributor(contributor, auth=auth)
        # remove_contributor returns false if there is not one admin or visible contributor left after the move.
        if not nodes_removed:
            raise HTTPError(http.BAD_REQUEST, data={
                'message_long': 'Could not remove contributor.'})

        # On parent node, if user has removed herself from project, alert; redirect to
        # node summary if node is public, else to user's dashboard page
        if not node.is_contributor(auth.user) and node_id == parent_id:
            status.push_status_message(
                'You have removed yourself as a contributor from this project',
                kind='success',
                trust=False
            )
            if node.is_public:
                redirect_url = {'redirectUrl': node.url}
            else:
                redirect_url = {'redirectUrl': web_url_for('dashboard')}
    return redirect_url
Example #33
0
def claim_user_form(**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 get_current_user():
        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 #34
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 #35
0
def user_account(auth, **kwargs):
    user = auth.user
    user_addons = addon_utils.get_addons_by_config_type('user', user)
    if 'password_reset' in request.args:
        push_status_message('Password updated successfully.', kind='success', trust=False)

    return {
        'user_id': user._id,
        'addons': user_addons,
        'addons_js': collect_user_config_js([addon for addon in settings.ADDONS_AVAILABLE if 'user' in addon.configs]),
        'addons_css': [],
        'requested_deactivation': user.requested_deactivation,
        'external_identity': user.external_identity,
        'storage_flag_is_active': storage_i18n_flag_active(),
    }
Example #36
0
def user_account_password(auth, **kwargs):
    user = auth.user
    old_password = request.form.get('old_password', None)
    new_password = request.form.get('new_password', None)
    confirm_password = request.form.get('confirm_password', None)

    try:
        user.change_password(old_password, new_password, confirm_password)
        user.save()
    except ChangePasswordError as error:
        push_status_message('<br />'.join(error.messages) + '.', kind='warning')
    else:
        push_status_message('Password updated successfully.', kind='info')

    return redirect(web_url_for('user_account'))
Example #37
0
def check_can_access(node, user, key=None, api_node=None):
    """View helper that returns whether a given user can access a node.
    If ``user`` is None, returns False.

    :rtype: boolean
    :raises: HTTPError (403) if user cannot access the node
    """
    if user is None:
        return False
    if not node.can_view(Auth(user=user)) and api_node != node:
        if key in node.private_link_keys_deleted:
            status.push_status_message(
                "The view-only links you used are expired.")
        raise HTTPError(http.FORBIDDEN)
    return True
Example #38
0
def node_register_page(auth, node, **kwargs):
    """Display the registration metadata for a registration.

    :return: serialized Node
    """

    if node.is_registration:
        return serialize_node(node, auth)
    else:
        status.push_status_message(
            'You have been redirected to the project\'s registrations page. From here you can initiate a new Draft Registration to complete the registration process',
            trust=False,
            id='redirected_to_registrations',
        )
        return redirect(node.web_url_for('node_registrations', view='draft', _guid=True))
Example #39
0
def project_new_node(auth, node, **kwargs):
    form = NewNodeForm(request.form)
    user = auth.user
    if form.validate():
        try:
            new_component = new_node(
                title=strip_html(form.title.data),
                user=user,
                category=form.category.data,
                parent=node,
            )
        except ValidationError as e:
            raise HTTPError(
                http.BAD_REQUEST,
                data=dict(message_long=e.message)
            )
        redirect_url = node.url
        message = (
            'Your component was created successfully. You can keep working on the project page below, '
            'or go to the new <u><a href={component_url}>component</a></u>.'
        ).format(component_url=new_component.url)
        if form.inherit_contributors.data and node.has_permission(user, WRITE):
            for contributor in node.contributors:
                perm = CREATOR_PERMISSIONS if contributor._id == user._id else node.get_permissions(contributor)
                if contributor._id == user._id and not contributor.is_registered:
                    new_component.add_unregistered_contributor(
                        fullname=contributor.fullname, email=contributor.email,
                        permissions=perm, auth=auth, existing_user=contributor
                    )
                else:
                    new_component.add_contributor(contributor, permissions=perm, auth=auth)

            new_component.save()
            redirect_url = new_component.url + 'contributors/'
            message = (
                'Your component was created successfully. You can edit the contributor permissions below, '
                'work on your <u><a href={component_url}>component</a></u> or return to the <u> '
                '<a href="{project_url}">project page</a></u>.'
            ).format(component_url=new_component.url, project_url=node.url)
        status.push_status_message(message, kind='info', trust=True)

        return {
            'status': 'success',
        }, 201, None, redirect_url
    else:
        # TODO: This function doesn't seem to exist anymore?
        status.push_errors_to_status(form.errors)
    raise HTTPError(http.BAD_REQUEST, redirect_url=node.url)
Example #40
0
def replace_unclaimed_user_with_registered(user):
    """Listens for the user_registered signal. If unreg_user is stored in the
    session, then the current user is trying to claim themselves as a contributor.
    Replaces the old, unregistered contributor with the newly registered
    account.

    """
    unreg_user_info = session.data.get('unreg_user')
    if unreg_user_info:
        unreg_user = User.load(unreg_user_info['uid'])
        pid = unreg_user_info['pid']
        node = Node.load(pid)
        node.replace_contributor(old=unreg_user, new=user)
        node.save()
        status.push_status_message(
            'Successfully claimed contributor.', kind='success', trust=False)
Example #41
0
def check_can_access(node, user, key=None, api_node=None):
    """View helper that returns whether a given user can access a node.
    If ``user`` is None, returns False.

    :rtype: boolean
    :raises: HTTPError (403) if user cannot access the node
    """
    if user is None:
        return False
    if not node.can_view(Auth(user=user)) and api_node != node:
        if key in node.private_link_keys_deleted:
            status.push_status_message('The view-only links you used are expired.', trust=False)
        raise HTTPError(http.FORBIDDEN, data={'message_long': ('User has restricted access to this page. '
            'If this should not have occurred and the issue persists, please report it to '
            '<a href="mailto:[email protected]">[email protected]</a>.')})
    return True
Example #42
0
def node_register_template_page(auth, node, metaschema_id, **kwargs):
    if node.is_registration and bool(node.registered_schema):
        try:
            meta_schema = RegistrationSchema.objects.get(_id=metaschema_id)
        except RegistrationSchema.DoesNotExist:
            # backwards compatability for old urls, lookup by name
            meta_schema = RegistrationSchema.objects.filter(name=_id_to_name(
                metaschema_id)).order_by('-schema_version').first()
            if not meta_schema:
                raise HTTPError(
                    http.NOT_FOUND,
                    data={
                        'message_short':
                        'Invalid schema name',
                        'message_long':
                        'No registration schema with that name could be found.'
                    })
        if not node.registered_schema.filter(id=meta_schema.id).exists():
            raise HTTPError(
                http.BAD_REQUEST,
                data={
                    'message_short':
                    'Invalid schema',
                    'message_long':
                    'This registration has no registration supplment with that name.'
                })

        ret = _view_project(node, auth, primary=True)
        my_meta = serialize_meta_schema(meta_schema)
        if has_anonymous_link(node, auth):
            for indx, schema_page in enumerate(my_meta['schema']['pages']):
                for idx, schema_question in enumerate(
                        schema_page['questions']):
                    if schema_question['title'] in settings.ANONYMIZED_TITLES:
                        del my_meta['schema']['pages'][indx]['questions'][idx]
        ret['node']['registered_schema'] = serialize_meta_schema(meta_schema)
        return ret
    else:
        status.push_status_message(
            'You have been redirected to the project\'s registrations page. From here you can initiate a new Draft Registration to complete the registration process',
            trust=False,
            id='redirected_to_registrations',
        )
        return redirect(
            node.web_url_for('node_registrations',
                             view=kwargs.get('template'),
                             _guid=True))
Example #43
0
def register_draft_registration(auth, node, draft, *args, **kwargs):
    """Initiate a registration from a draft registration

    :return: success message; url to registrations page
    :rtype: dict
    """
    json_data = request.get_json()
    if 'data' not in json_data:
        raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Payload must include "data".'))
    data = json_data['data']
    if 'attributes' not in data:
        raise HTTPError(http.BAD_REQUEST, data=dict(message_long='Payload must include "data/attributes".'))
    attributes = data['attributes']
    registration_choice = attributes['registration_choice']
    validate_registration_choice(registration_choice)

    # Don't allow resubmission unless submission was rejected
    if draft.approval and draft.approval.state != Sanction.REJECTED:
        raise HTTPError(http.CONFLICT, data=dict(message_long='Cannot resubmit previously submitted draft.'))

    register = draft.register(auth)
    draft.save()

    if registration_choice == 'embargo':
        # Initiate embargo
        embargo_end_date = parse_date(attributes['lift_embargo'], ignoretz=True).replace(tzinfo=pytz.utc)
        try:
            register.embargo_registration(auth.user, embargo_end_date)
        except ValidationError as err:
            raise HTTPError(http.BAD_REQUEST, data=dict(message_long=err.message))
    else:
        try:
            register.require_approval(auth.user)
        except NodeStateError as err:
            raise HTTPError(http.BAD_REQUEST, data=dict(message_long=str(err)))

    register.save()
    push_status_message(language.AFTER_REGISTER_ARCHIVING,
                        kind='info',
                        trust=False,
                        id='registration_archiving')
    return {
        'status': 'initiated',
        'urls': {
            'registrations': node.web_url_for('node_registrations', _guid=True)
        }
    }, http.ACCEPTED
Example #44
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 #45
0
def submit_draft_for_review(auth, node, draft, *args, **kwargs):
    """Submit for approvals and/or notifications

    :return: serialized registration
    :rtype: dict
    :raises: HTTPError if embargo end date is invalid
    """
    data = request.get_json()
    meta = {}
    registration_choice = data.get('registrationChoice', 'immediate')
    validate_registration_choice(registration_choice)
    if registration_choice == 'embargo':
        # Initiate embargo
        end_date_string = data['embargoEndDate']
        validate_embargo_end_date(end_date_string, node)
        meta['embargo_end_date'] = end_date_string
    meta['registration_choice'] = registration_choice

    # Don't allow resubmission unless submission was rejected
    if draft.approval and draft.approval.state != Sanction.REJECTED:
        raise HTTPError(http.CONFLICT, data=dict(message_long='Cannot resubmit previously submitted draft.'))

    draft.submit_for_review(
        initiated_by=auth.user,
        meta=meta,
        save=True
    )

    if prereg_utils.get_prereg_schema() == draft.registration_schema:

        node.add_log(
            action=NodeLog.PREREG_REGISTRATION_INITIATED,
            params={'node': node._primary_key},
            auth=auth,
            save=False
        )
        node.save()

    push_status_message(language.AFTER_SUBMIT_FOR_REVIEW,
                        kind='info',
                        trust=False)
    return {
        'status': 'initiated',
        'urls': {
            'registrations': node.web_url_for('node_registrations')
        }
    }, http.ACCEPTED
Example #46
0
def register_draft_registration(auth, node, draft, *args, **kwargs):
    """Initiate a registration from a draft registration

    :return: success message; url to registrations page
    :rtype: dict
    """
    data = request.get_json()
    registration_choice = data.get('registrationChoice', 'immediate')
    validate_registration_choice(registration_choice)

    # Don't allow resubmission unless submission was rejected
    if draft.approval and draft.approval.state != Sanction.REJECTED:
        raise HTTPError(
            http.CONFLICT,
            data=dict(
                message_long='Cannot resubmit previously submitted draft.'))

    register = draft.register(auth)
    draft.save()

    if registration_choice == 'embargo':
        # Initiate embargo
        embargo_end_date = parse_date(data['embargoEndDate'],
                                      ignoretz=True).replace(tzinfo=pytz.utc)
        try:
            register.embargo_registration(auth.user, embargo_end_date)
        except ValidationError as err:
            raise HTTPError(http.BAD_REQUEST,
                            data=dict(message_long=err.message))
    else:
        try:
            register.require_approval(auth.user)
        except NodeStateError as err:
            raise HTTPError(http.BAD_REQUEST,
                            data=dict(message_long=err.message))

    register.save()
    push_status_message(language.AFTER_REGISTER_ARCHIVING,
                        kind='info',
                        trust=False)
    return {
        'status': 'initiated',
        'urls': {
            'registrations': node.web_url_for('node_registrations')
        }
    }, http.ACCEPTED
Example #47
0
def figshare_oauth_callback(auth, **kwargs):

    user = auth.user

    nid = kwargs.get('nid') or kwargs.get('pid')
    node = models.Node.load(nid) if nid else None

    # Fail if node provided and user not contributor
    if node and not node.is_contributor(user):
        raise HTTPError(http.FORBIDDEN)

    if user is None:
        raise HTTPError(http.NOT_FOUND)
    if kwargs.get('nid') and not node:
        raise HTTPError(http.NOT_FOUND)

    figshare_user = user.get_addon('figshare')

    verifier = request.args.get('oauth_verifier')

    access_token, access_token_secret = oauth_get_token(
        figshare_user.oauth_request_token,
        figshare_user.oauth_request_token_secret, verifier)
    # Handle request cancellations from FigShare's API
    if not access_token or not access_token_secret:
        push_status_message('figshare authorization request cancelled.')
        if node:
            return redirect(node.web_url_for('node_setting'))
        return redirect(web_url_for('user_addons'))

    figshare_user.oauth_request_token = None
    figshare_user.oauth_request_token_secret = None
    figshare_user.oauth_access_token = access_token
    figshare_user.oauth_access_token_secret = access_token_secret
    figshare_user.save()

    if node:
        figshare_node = node.get_addon('figshare')

        figshare_node.user_settings = figshare_user
        figshare_node.save()

    if node:
        return redirect(os.path.join(node.url, 'settings'))

    return redirect(web_url_for('user_addons'))
Example #48
0
def node_register_template_page(auth, node, metaschema_id, **kwargs):
    if node.is_registration and bool(node.registered_schema):
        try:
            meta_schema = MetaSchema.find_one(Q('_id', 'eq', metaschema_id))
        except NoResultsFound:
            # backwards compatability for old urls, lookup by name
            try:
                meta_schema = MetaSchema.find(
                    Q('name', 'eq',
                      _id_to_name(metaschema_id))).sort('-schema_version')[0]
            except IndexError:
                raise HTTPError(
                    http.NOT_FOUND,
                    data={
                        'message_short':
                        'Invalid schema name',
                        'message_long':
                        'No registration schema with that name could be found.'
                    })
        if meta_schema not in node.registered_schema:
            raise HTTPError(
                http.BAD_REQUEST,
                data={
                    'message_short':
                    'Invalid schema',
                    'message_long':
                    'This registration has no registration supplment with that name.'
                })

        ret = _view_project(node, auth, primary=True)
        my_meta = serialize_meta_schema(meta_schema)
        if has_anonymous_link(node, auth):
            for indx, schema_page in enumerate(my_meta['schema']['pages']):
                for idx, schema_question in enumerate(
                        schema_page['questions']):
                    if schema_question['title'] in settings.ANONYMIZED_TITLES:
                        del my_meta['schema']['pages'][indx]['questions'][idx]
        ret['node']['registered_schema'] = serialize_meta_schema(meta_schema)
        return ret
    else:
        status.push_status_message(
            'You have been redirected to the project\'s registrations page. From here you can initiate a new Draft Registration to complete the registration process',
            trust=False)
        return redirect(
            node.web_url_for('node_registrations',
                             view=kwargs.get('template')))
Example #49
0
def node_register_template_page_post(auth, node, **kwargs):
    data = request.json

    if settings.DISK_SAVING_MODE:
        raise HTTPError(http.METHOD_NOT_ALLOWED, redirect_url=node.url)

    # Sanitize payload data
    clean_data = process_payload(data)

    template = kwargs['template']
    # TODO: Using json.dumps because node_to_use.registered_meta's values are
    # expected to be strings (not dicts). Eventually migrate all these to be
    # dicts, as this is unnecessary
    schema = MetaSchema.find(Q('name', 'eq',
                               template)).sort('-schema_version')[0]

    # Create the registration
    register = node.register_node(
        schema,
        auth,
        template,
        json.dumps(clean_data),
    )
    try:
        if data.get('registrationChoice', 'immediate') == 'embargo':
            # Initiate embargo
            embargo_end_date = parse_date(data['embargoEndDate'],
                                          ignoretz=True)
            register.embargo_registration(auth.user, embargo_end_date)
        else:
            register.require_approval(auth.user)
        register.save()
    except ValidationValueError as err:
        raise HTTPError(http.BAD_REQUEST, data=dict(message_long=err.message))

    push_status_message(language.AFTER_REGISTER_ARCHIVING,
                        kind='info',
                        trust=False)

    return {
        'status': 'initiated',
        'urls': {
            'registrations': node.web_url_for('node_registrations')
        }
    }, http.CREATED
Example #50
0
    def post(self, request, *args, **kwargs):
        node = self.get_object()

        node.is_public = False
        node.keenio_read_key = ''

        # After set permissions callback
        for addon in node.get_addons():
            message = addon.after_set_privacy(node, 'private')
            if message:
                status.push_status_message(message, kind='info', trust=False)

        if node.get_identifier_value('doi'):
            node.request_identifier_update(category='doi')

        node.save()

        return redirect(self.get_success_url())
Example #51
0
def two_factor(**kwargs):
    """View for handling two factor code authentication

    methods: GET, POST
    """
    if request.method != 'POST':
        return {}

    two_factor_code = request.form['twoFactorCode']
    try:  # verify two factor for current user
        response = verify_two_factor(
            session.data['two_factor_auth']['auth_user_id'], two_factor_code)
        return response
    except exceptions.TwoFactorValidationError:
        status.push_status_message(language.TWO_FACTOR_FAILED)
        # Get next URL from GET / POST data
        next_url = request.args.get('next', request.form.get('next_url', ''))
        return {'next_url': next_url}, http.UNAUTHORIZED
Example #52
0
def confirm_email_get(**kwargs):
    """View for email confirmation links.
    Authenticates and redirects to user settings page if confirmation is
    successful, otherwise shows an "Expired Link" error.

    methods: GET
    """
    user = User.load(kwargs['uid'])
    is_initial_confirmation = not user.date_confirmed
    token = kwargs['token']

    if user is None:
        raise HTTPError(http.NOT_FOUND)

    try:
        user.confirm_email(token)
    except exceptions.EmailConfirmTokenError as e:
        raise HTTPError(http.BAD_REQUEST,
                        data={
                            'message_short': e.message_short,
                            'message_long': e.message_long
                        })
    except exceptions.DuplicateEmailError as e:
        raise HTTPError(http.BAD_REQUEST,
                        data={
                            'message_short':
                            'Email Confirmation Failed',
                            'message_long':
                            'This email address has already been confirmed by '
                            'another user.'
                        })

    if is_initial_confirmation:
        user.date_last_login = datetime.datetime.utcnow()
        user.save()

        # Go to settings page
        status.push_status_message(language.WELCOME_MESSAGE, 'success')
        response = redirect('/settings/')
    else:
        status.push_status_message(language.CONFIRMED_EMAIL, 'success')
        response = redirect(web_url_for('user_account'))

    return framework.auth.authenticate(user, response=response)
Example #53
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 #54
0
def check_can_access(node, user, key=None, api_node=None, include_groups=True):
    """View helper that returns whether a given user can access a node.
    If ``user`` is None, returns False.

    :rtype: boolean
    :raises: HTTPError (403) if user cannot access the node
    """
    if user is None:
        return False
    if request.args.get('action', '') == 'download':
        if check_can_download_preprint_file(user, node):
            return True

    if (not node.can_view(Auth(user=user)) and api_node != node) or (not include_groups and not node.is_contributor(user)):
        if node.is_deleted:
            raise HTTPError(http.GONE, data={'message_long': 'The node for this file has been deleted.'})

        if getattr(node, 'private_link_keys_deleted', False) and key in node.private_link_keys_deleted:
            status.push_status_message('The view-only links you used are expired.', trust=False)

        if getattr(node, 'access_requests_enabled', False):
            access_request = node.requests.filter(creator=user).exclude(machine_state='accepted')
            data = {
                'node': {
                    'id': node._id,
                    'url': node.url
                },
                'user': {
                    'access_request_state': access_request.get().machine_state if access_request else None
                }
            }
            raise TemplateHTTPError(
                http.FORBIDDEN,
                template='request_access.mako',
                data=data
            )

        raise HTTPError(
            http.FORBIDDEN,
            data={'message_long': ('User has restricted access to this page. If this should not '
                                   'have occurred and the issue persists, ' + language.SUPPORT_LINK)}
        )
    return True
Example #55
0
def project_removecontributor(auth, node, **kwargs):

    contributor = User.load(request.json['id'])
    if contributor is None:
        raise HTTPError(http.BAD_REQUEST)

    # Forbidden unless user is removing herself
    if not node.has_permission(auth.user, 'admin'):
        if auth.user != contributor:
            raise HTTPError(http.FORBIDDEN)

    if len(node.visible_contributor_ids) == 1 \
            and node.visible_contributor_ids[0] == contributor._id:
        raise HTTPError(http.FORBIDDEN,
                        data={
                            'message_long':
                            'Must have at least one bibliographic contributor'
                        })

    outcome = node.remove_contributor(
        contributor=contributor,
        auth=auth,
    )

    if outcome:
        if auth.user == contributor:
            status.push_status_message('Removed self from project',
                                       kind='success',
                                       trust=False)
            return {'redirectUrl': web_url_for('dashboard')}
        status.push_status_message('Contributor removed',
                                   kind='success',
                                   trust=False)
        return {}

    raise HTTPError(http.BAD_REQUEST,
                    data={
                        'message_long':
                        ('{0} must have at least one contributor with admin '
                         'rights'.format(
                             node.project_or_component.capitalize()))
                    })
Example #56
0
 def test_push_status_message_unexpected_error(self, mock_sesh):
     status_message = 'This is a message'
     exception_message = 'this is some very unexpected problem'
     mock_get = mock.Mock(side_effect=RuntimeError(exception_message))
     mock_data = mock.Mock()
     mock_data.attach_mock(mock_get, 'get')
     mock_sesh.attach_mock(mock_data, 'data')
     try:
         push_status_message(status_message, kind='error')
         assert_true(False, 'push_status_message() should have generated a RuntimeError exception.')
     except ValidationError as e:
         assert_true(False, 'push_status_message() should have re-raised the RuntimeError not gotten ValidationError.')
     except RuntimeError as e:
         assert_equal(getattr(e, 'message', None),
                      exception_message,
                      'push_status_message() should have re-raised the '
                      'original RuntimeError with the original message.')
     except:
         assert_true(False, 'Unexpected Exception from push_status_message when called '
                            'from the v2 API with type "error"')
Example #57
0
def node_registration_embargo_disapprove(auth, node, token, **kwargs):
    """Handles disapproval of registration embargoes
    :param auth: User wanting to disapprove the embargo
    :return: Redirect to registration or
    :raises: HTTPError if invalid token or user is not admin
    """

    if not node.pending_embargo:
        raise HTTPError(http.BAD_REQUEST,
                        data={
                            'message_short':
                            'Invalid Token',
                            'message_long':
                            'This registration is not pending an embargo.'
                        })
    # Note(hryabcki): node.registered_from not accessible after disapproval
    if node.embargo.for_existing_registration:
        redirect_url = node.web_url_for('view_project')
    else:
        redirect_url = node.registered_from.web_url_for('view_project')
    try:
        node.embargo.disapprove_embargo(auth.user, token)
        node.embargo.save()
    except InvalidEmbargoDisapprovalToken as e:
        raise HTTPError(http.BAD_REQUEST,
                        data={
                            'message_short': e.message_short,
                            'message_long': e.message_long
                        })
    except PermissionsError as e:
        raise HTTPError(http.FORBIDDEN,
                        data={
                            'message_short': 'Unauthorized access',
                            'message_long': e.message
                        })

    status.push_status_message(
        'Your disapproval has been accepted and the embargo has been cancelled.',
        kind='success',
        trust=False)
    return redirect(redirect_url)
Example #58
0
def project_new_node(auth, node, **kwargs):
    form = NewNodeForm(request.form)
    user = auth.user
    if form.validate():
        try:
            new_component = new_node(
                title=strip_html(form.title.data),
                user=user,
                category=form.category.data,
                parent=node,
            )
        except ValidationValueError as e:
            raise HTTPError(http.BAD_REQUEST,
                            data=dict(message_long=e.message))
        redirect_url = new_component.url
        message = (
            'Your component was created successfully. You can keep working on the component page below, '
            'or return to the <u><a href="{url}">project page</a></u>.'
        ).format(url=node.url)
        if form.inherit_contributors.data and node.has_permission(user, ADMIN):
            for contributor in node.contributors:
                new_component.add_contributor(
                    contributor,
                    permissions=node.get_permissions(contributor),
                    auth=auth)
            new_component.save()
            redirect_url = redirect_url + 'contributors/'
            message = (
                'Your component was created successfully. You can edit the contributor permissions below, '
                'work on your <u><a href=' + new_component.url +
                '>component</a></u> or return to the <u><a href="{url}">project page</a></u>.'
            ).format(url=node.url)
        status.push_status_message(message, kind='info', trust=True)

        return {
            'status': 'success',
        }, 201, None, redirect_url
    else:
        # TODO: This function doesn't seem to exist anymore?
        status.push_errors_to_status(form.errors)
    raise HTTPError(http.BAD_REQUEST, redirect_url=node.url)
Example #59
0
def check_can_access(node, user, key=None, api_node=None):
    """View helper that returns whether a given user can access a node.
    If ``user`` is None, returns False.

    :rtype: boolean
    :raises: HTTPError (403) if user cannot access the node
    """
    if user is None:
        return False
    if not node.can_view(Auth(user=user)) and api_node != node:
        if key in node.private_link_keys_deleted:
            status.push_status_message(
                'The view-only links you used are expired.', trust=False)

        if node.access_requests_enabled:
            access_request = node.requests.filter(creator=user).exclude(
                machine_state='accepted')
            data = {
                'node': {
                    'id': node._id,
                    'url': node.url
                },
                'user': {
                    'access_request_state':
                    access_request.get().machine_state
                    if access_request else None
                }
            }
            raise TemplateHTTPError(http.FORBIDDEN,
                                    template='request_access.mako',
                                    data=data)

        raise HTTPError(
            http.FORBIDDEN,
            data={
                'message_long':
                ('User has restricted access to this page. If this should not '
                 'have occurred and the issue persists, ' +
                 language.SUPPORT_LINK)
            })
    return True
Example #60
0
def user_account_password(auth, **kwargs):
    user = auth.user
    old_password = request.form.get('old_password', None)
    new_password = request.form.get('new_password', None)
    confirm_password = request.form.get('confirm_password', None)

    # It has been more than 1 hour since last invalid attempt to change password. Reset the counter for invalid attempts.
    if throttle_period_expired(user.change_password_last_attempt,
                               settings.TIME_RESET_CHANGE_PASSWORD_ATTEMPTS):
        user.reset_old_password_invalid_attempts()

    # There have been more than 3 failed attempts and throttle hasn't expired.
    if user.old_password_invalid_attempts >= settings.INCORRECT_PASSWORD_ATTEMPTS_ALLOWED and not throttle_period_expired(
            user.change_password_last_attempt,
            settings.CHANGE_PASSWORD_THROTTLE):
        push_status_message(
            message=
            'Too many failed attempts. Please wait a while before attempting to change your password.',
            kind='warning',
            trust=False)
        return redirect(web_url_for('user_account'))

    try:
        user.change_password(old_password, new_password, confirm_password)
    except ChangePasswordError as error:
        for m in error.messages:
            push_status_message(m, kind='warning', trust=False)
    else:
        # We have to logout the user first so all CAS sessions are invalid
        user.save()
        osf_logout()
        return redirect(
            cas.get_logout_url(
                cas.get_login_url(
                    web_url_for('user_account', _absolute=True) +
                    '?password_reset=True',
                    username=user.username,
                    verification_key=user.verification_key,
                )))
    user.save()
    return redirect(web_url_for('user_account'))