Пример #1
0
def redirect_to_twitter(twitter_handle):
    """Redirect GET requests for /@TwitterHandle/ to respective the OSF user
    account if it associated with an active account

    :param uid: uid for requested User
    :return: Redirect to User's Twitter account page
    """
    try:
        user = User.find_one(Q('social.twitter', 'iexact', twitter_handle))
    except NoResultsFound:
        raise HTTPError(http.NOT_FOUND, data={
            'message_short': 'User Not Found',
            'message_long': 'There is no active user associated with the Twitter handle: {0}.'.format(twitter_handle)
        })
    except MultipleResultsFound:
        users = User.find(Q('social.twitter', 'iexact', twitter_handle))
        message_long = 'There are multiple OSF accounts associated with the ' \
                       'Twitter handle: <strong>{0}</strong>. <br /> Please ' \
                       'select from the accounts below. <br /><ul>'.format(markupsafe.escape(twitter_handle))
        for user in users:
            message_long += '<li><a href="{0}">{1}</a></li>'.format(user.url, markupsafe.escape(user.fullname))
        message_long += '</ul>'
        raise HTTPError(http.MULTIPLE_CHOICES, data={
            'message_short': 'Multiple Users Found',
            'message_long': message_long
        })

    return redirect(user.url)
Пример #2
0
def deserialize_contributors(node, user_dicts, auth, validate=False):
    """View helper that returns a list of User objects from a list of
    serialized users (dicts). The users in the list may be registered or
    unregistered users.

    e.g. ``[{'id': 'abc123', 'registered': True, 'fullname': ..},
            {'id': None, 'registered': False, 'fullname'...},
            {'id': '123ab', 'registered': False, 'fullname': ...}]

    If a dict represents an unregistered user without an ID, creates a new
    unregistered User record.

    :param Node node: The node to add contributors to
    :param list(dict) user_dicts: List of serialized users in the format above.
    :param Auth auth:
    :param bool validate: Whether to validate and sanitize fields (if necessary)
    """

    # Add the registered contributors
    contribs = []
    for contrib_dict in user_dicts:
        fullname = contrib_dict['fullname']
        visible = contrib_dict['visible']
        email = contrib_dict.get('email')

        if validate is True:
            # Validate and sanitize inputs as needed. Email will raise error if invalid.
            # TODO Edge case bug: validation and saving are performed in same loop, so all in list
            # up to the invalid entry will be saved. (communicate to the user what needs to be retried)
            fullname = sanitize.strip_html(fullname)
            if not fullname:
                raise ValidationError('Full name field cannot be empty')
            if email:
                validate_email(email)  # Will raise a ValidationError if email invalid

        if contrib_dict['id']:
            contributor = OSFUser.load(contrib_dict['id'])
        else:
            try:
                contributor = OSFUser.create_unregistered(
                    fullname=fullname,
                    email=email)
                contributor.save()
            except ValidationError:
                ## FIXME: This suppresses an exception if ID not found & new validation fails; get_user will return None
                contributor = get_user(email=email)

        # Add unclaimed record if necessary
        if not contributor.is_registered:
            contributor.add_unclaimed_record(node, referrer=auth.user,
                given_name=fullname,
                email=email)
            contributor.save()

        contribs.append({
            'user': contributor,
            'visible': visible,
            'permissions': expand_permissions(contrib_dict.get('permission'))
        })
    return contribs
Пример #3
0
    def test_improperly_scoped_token_can_not_create_or_email(self, mock_auth, mock_mail):
        token = ApiOAuth2PersonalToken(
            owner=self.user,
            name='Unauthorized Token',
            scopes='osf.full_write'
        )

        mock_cas_resp = CasResponse(
            authenticated=True,
            user=self.user._id,
            attributes={
                'accessToken': token.token_id,
                'accessTokenScope': [s for s in token.scopes.split(' ')]
            }
        )
        mock_auth.return_value = self.user, mock_cas_resp

        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0)
        res = self.app.post_json_api(
            '{}?send_email=true'.format(self.base_url),
            self.data,
            headers={'Authorization': 'Bearer {}'.format(token.token_id)},
            expect_errors=True
        )

        assert_equal(res.status_code, 403)
        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0)
        assert_equal(mock_mail.call_count, 0)
Пример #4
0
    def test_admin_scoped_token_can_create_and_send_email(self, mock_auth, mock_mail):
        token = ApiOAuth2PersonalToken(
            owner=self.user,
            name='Admin Token',
            scopes='osf.admin'
        )

        mock_cas_resp = CasResponse(
            authenticated=True,
            user=self.user._id,
            attributes={
                'accessToken': token.token_id,
                'accessTokenScope': [s for s in token.scopes.split(' ')]
            }
        )
        mock_auth.return_value = self.user, mock_cas_resp

        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0)
        res = self.app.post_json_api(
            '{}?send_email=true'.format(self.base_url),
            self.data,
            headers={'Authorization': 'Bearer {}'.format(token.token_id)}
        )

        assert_equal(res.status_code, 201)
        assert_equal(res.json['data']['attributes']['username'], self.unconfirmed_email)
        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 1)
        assert_equal(mock_mail.call_count, 1)
Пример #5
0
    def test_properly_scoped_token_can_create_without_username_but_not_send_email(self, mock_auth, mock_mail):
        token = ApiOAuth2PersonalToken(
            owner=self.user,
            name='Authorized Token',
            scopes='osf.users.create'
        )

        mock_cas_resp = CasResponse(
            authenticated=True,
            user=self.user._id,
            attributes={
                'accessToken': token.token_id,
                'accessTokenScope': [s for s in token.scopes.split(' ')]
            }
        )
        mock_auth.return_value = self.user, mock_cas_resp

        self.data['data']['attributes'] = {'full_name': 'No Email'}

        assert_equal(User.find(Q('fullname', 'eq', 'No Email')).count(), 0)
        res = self.app.post_json_api(
            '{}?send_email=true'.format(self.base_url),
            self.data,
            headers={'Authorization': 'Bearer {}'.format(token.token_id)}
        )

        assert_equal(res.status_code, 201)
        username = res.json['data']['attributes']['username']
        try:
            no_failure = UUID(username)
        except ValueError:
            raise AssertionError('Username is not a valid UUID')
        assert_equal(User.find(Q('fullname', 'eq', 'No Email')).count(), 1)
        assert_equal(mock_mail.call_count, 0)
Пример #6
0
    def test_logged_out_user_cannot_create_other_user_or_send_mail(self, mock_mail):
        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0)
        res = self.app.post_json_api(
            '{}?send_email=true'.format(self.base_url),
            self.data,
            expect_errors=True
        )

        assert_equal(res.status_code, 401)
        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0)
        assert_equal(mock_mail.call_count, 0)
Пример #7
0
def find_inactive_users_with_no_inactivity_email_sent_or_queued():
    inactive_users = User.find(
        (Q('date_last_login', 'lt', timezone.now() - settings.NO_LOGIN_WAIT_TIME) & Q('tags__name', 'ne', 'osf4m')) |
        (Q('date_last_login', 'lt', timezone.now() - settings.NO_LOGIN_OSF4M_WAIT_TIME) & Q('tags__name', 'eq', 'osf4m'))
    )
    inactive_emails = QueuedMail.find(Q('email_type', 'eq', NO_LOGIN_TYPE))

    #This is done to prevent User query returns comparison to User, as equality fails
    #on datetime fields due to pymongo rounding. Instead here _id is compared.
    users_sent_id = [email.user._id for email in inactive_emails]
    inactive_ids = [user._id for user in inactive_users if user.is_active]
    users_to_send = [User.load(id) for id in (set(inactive_ids) - set(users_sent_id))]
    return users_to_send
Пример #8
0
    def test_cookied_requests_can_create_and_email(self, mock_mail):
        session = Session(data={'auth_user_id': self.user._id})
        session.save()
        cookie = itsdangerous.Signer(settings.SECRET_KEY).sign(session._id)
        self.app.set_cookie(settings.COOKIE_NAME, str(cookie))

        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0)
        res = self.app.post_json_api(
            '{}?send_email=true'.format(self.base_url),
            self.data
        )
        assert_equal(res.status_code, 201)
        assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 1)
        assert_equal(mock_mail.call_count, 1)
Пример #9
0
    def check_spam(self):
        request, user_id = get_request_and_user_id()
        user = OSFUser.load(user_id)
        if not isinstance(request, DummyRequest):
            request_headers = {
                k: v
                for k, v in get_headers_from_request(request).items()
                if isinstance(v, basestring)
            }

        node = self.wiki_page.node

        if not settings.SPAM_CHECK_ENABLED:
            return False
        if settings.SPAM_CHECK_PUBLIC_ONLY and not node.is_public:
            return False
        if 'ham_confirmed' in user.system_tags:
            return False

        content = self._get_spam_content(node)
        if not content:
            return
        is_spam = node.do_check_spam(
            user.fullname,
            user.username,
            content,
            request_headers
        )

        logger.info("Node ({}) '{}' smells like {} (tip: {})".format(
            node._id, node.title.encode('utf-8'), 'SPAM' if is_spam else 'HAM', node.spam_pro_tip
        ))
        if is_spam:
            node._check_spam_user(user)
        return is_spam
Пример #10
0
def _get_current_user():
    from osf.models import OSFUser
    current_user_id = get_current_user_id()
    if current_user_id:
        return OSFUser.load(current_user_id)
    else:
        return None
Пример #11
0
    def authenticate(self, request):
        """
        Check whether the request provides a valid OAuth2 bearer token.
        The `user` in `cas_auth_response` is the unique GUID of the user. Please do not use
        the primary key `id` or the email `username`.

        :param request: the request
        :return: the user who owns the bear token and the cas repsonse
        """

        client = cas.get_client()
        try:
            auth_header_field = request.META['HTTP_AUTHORIZATION']
            auth_token = cas.parse_auth_header(auth_header_field)
        except (cas.CasTokenError, KeyError):
            return None

        try:
            cas_auth_response = client.profile(auth_token)
        except cas.CasHTTPError:
            raise exceptions.NotAuthenticated(_('User provided an invalid OAuth2 access token'))

        if cas_auth_response.authenticated is False:
            raise exceptions.NotAuthenticated(_('CAS server failed to authenticate this token'))

        user = OSFUser.load(cas_auth_response.user)
        if not user:
            raise exceptions.AuthenticationFailed(_('Could not find the user associated with this token'))

        check_user(user)
        return user, cas_auth_response
Пример #12
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__address', '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
Пример #13
0
def send_users_email(send_type):
    """Find pending Emails and amalgamates them into a single Email.

    :param send_type
    :return:
    """
    grouped_emails = get_users_emails(send_type)
    for group in grouped_emails:
        user = OSFUser.load(group['user_id'])
        if not user:
            log_exception()
            continue
        info = group['info']
        notification_ids = [message['_id'] for message in info]
        sorted_messages = group_by_node(info)
        if sorted_messages:
            if not user.is_disabled:
                mails.send_mail(
                    to_addr=user.username,
                    mimetype='html',
                    mail=mails.DIGEST,
                    name=user.fullname,
                    message=sorted_messages,
                )
            remove_notifications(email_notification_ids=notification_ids)
Пример #14
0
def subscribe_mailchimp(list_name, user_id):
    user = User.load(user_id)
    m = get_mailchimp_api()
    list_id = get_list_id_from_name(list_name=list_name)

    if user.mailchimp_mailing_lists is None:
        user.mailchimp_mailing_lists = {}

    try:
        m.lists.subscribe(
            id=list_id,
            email={'email': user.username},
            merge_vars={
                'fname': user.given_name,
                'lname': user.family_name,
            },
            double_optin=False,
            update_existing=True,
        )

    except (mailchimp.ValidationError, mailchimp.ListInvalidBounceMemberError) as error:
        sentry.log_exception()
        sentry.log_message(error.message)
        user.mailchimp_mailing_lists[list_name] = False
    else:
        user.mailchimp_mailing_lists[list_name] = True
    finally:
        user.save()
Пример #15
0
def _send_global_and_node_emails(send_type):
    """
    Called by `send_users_email`. Send all global and node-related notification emails.
    """
    grouped_emails = get_users_emails(send_type)
    for group in grouped_emails:
        user = OSFUser.load(group['user_id'])
        if not user:
            log_exception()
            continue
        info = group['info']
        notification_ids = [message['_id'] for message in info]
        sorted_messages = group_by_node(info)
        if sorted_messages:
            if not user.is_disabled:
                # If there's only one node in digest we can show it's preferences link in the template.
                notification_nodes = sorted_messages['children'].keys()
                node = AbstractNode.load(notification_nodes[0]) if len(
                    notification_nodes) == 1 else None
                mails.send_mail(
                    to_addr=user.username,
                    mimetype='html',
                    can_change_node_preferences=bool(node),
                    node=node,
                    mail=mails.DIGEST,
                    name=user.fullname,
                    message=sorted_messages,
                )
            remove_notifications(email_notification_ids=notification_ids)
Пример #16
0
def process_project_search_results(results, **kwargs):
    """
    :param results: list of projects from the modular ODM search
    :return: we return the entire search result, which is a list of
    dictionaries. This includes the list of contributors.
    """
    user = kwargs['auth'].user

    ret = []

    for project in results:
        authors = get_node_contributors_abbrev(project=project, auth=kwargs['auth'])
        authors_html = ''
        for author in authors['contributors']:
            a = OSFUser.load(author['user_id'])
            authors_html += '<a href="%s">%s</a>' % (a.url, a.fullname)
            authors_html += author['separator'] + ' '
        authors_html += ' ' + authors['others_count']

        ret.append({
            'id': project._id,
            'label': project.title,
            'value': project.title,
            'category': 'My Projects' if user in project.contributors else 'Public Projects',
            'authors': authors_html,
        })

    return ret
Пример #17
0
    def setUpClass(cls):
        super(TestVarnish, cls).setUpClass()
        username = uuid.uuid4()
        cls.user = OSFUser.create_confirmed(
            username='******'.format(str(username)),
            password='******',
            fullname='Mocha Test User')
        cls.user.save()
        cls.authorization = HTTPBasicAuth(cls.user.username, 'password')

        small = 5
        large = 10

        components = [[[range(small, random.randint(small, large))
                        for x in range(small, random.randint(small, large))]
                       for y in range(small, random.randint(small, large))]
                      for z in range(small, random.randint(small, large))]

        number_of_projects = random.randint(1, 11)
        number_of_tags = random.randint(1, 11)
        number_of_users = random.randint(1, 11)

        for i in range(number_of_projects):
            name = ''
            create_fake_project(cls.user, number_of_users,
                                random.choice(['public', 'private']),
                                components, name, number_of_tags, None, False)
Пример #18
0
def _get_current_user():
    from osf.models import OSFUser
    current_user_id = get_current_user_id()
    if current_user_id:
        return OSFUser.load(current_user_id, select_for_update=check_select_for_update(request))
    else:
        return None
Пример #19
0
    def get_contributors(self, obj):

        contributor_info = []

        if is_anonymized(self.context['request']):
            return contributor_info

        contributor_ids = obj.get('contributors', None)
        params_node = obj.get('node', None)

        if contributor_ids:
            for contrib_id in contributor_ids:
                user = OSFUser.load(contrib_id)
                unregistered_name = None
                if user.unclaimed_records.get(params_node):
                    unregistered_name = user.unclaimed_records[params_node].get('name', None)

                contributor_info.append({
                    'id': contrib_id,
                    'full_name': user.fullname,
                    'given_name': user.given_name,
                    'middle_names': user.middle_names,
                    'family_name': user.family_name,
                    'unregistered_name': unregistered_name,
                    'active': user.is_active
                })
        return contributor_info
Пример #20
0
def reset_password_get(auth, uid=None, token=None):
    """
    View for user to land on the reset password page.
    HTTp Method: GET

    :param auth: the authentication state
    :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
    """

    # if users are logged in, log them out and redirect back to this page
    if auth.logged_in:
        return auth_logout(redirect_url=request.url)

    # 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)

    # refresh the verification key (v2)
    user_obj.verification_key_v2 = generate_verification_key(verification_type='password')
    user_obj.save()

    return {
        'uid': user_obj._id,
        'token': user_obj.verification_key_v2['token'],
    }
Пример #21
0
    def save(self, *args, **kwargs):
        first_save = not bool(self.pk)
        saved_fields = self.get_dirty_fields() or []
        old_subjects = kwargs.pop('old_subjects', [])
        if saved_fields:
            request, user_id = get_request_and_user_id()
            request_headers = {}
            if not isinstance(request, DummyRequest):
                request_headers = {
                    k: v
                    for k, v in get_headers_from_request(request).items()
                    if isinstance(v, basestring)
                }
            user = OSFUser.load(user_id)
            if user:
                self.check_spam(user, saved_fields, request_headers)

        if not first_save and ('ever_public' in saved_fields and saved_fields['ever_public']):
            raise ValidationError('Cannot set "ever_public" to False')

        ret = super(Preprint, self).save(*args, **kwargs)

        if first_save:
            self._set_default_region()
            self.update_group_permissions()
            self._add_creator_as_contributor()

        if (not first_save and 'is_published' in saved_fields) or self.is_published:
            update_or_enqueue_on_preprint_updated(preprint_id=self._id, old_subjects=old_subjects, saved_fields=saved_fields)
        return ret
Пример #22
0
def serialize_comment(comment):
    reports = [
        serialize_report(user, report)
        for user, report in comment.reports.iteritems()
    ]
    author_abs_url = furl(OSF_DOMAIN)
    author_abs_url.path.add(comment.user.url)

    return {
        'id': comment._id,
        'author': OSFUser.load(comment.user._id),
        'author_id': comment.user._id,
        'author_path': author_abs_url.url,
        'date_created': comment.created,
        'date_modified': comment.modified,
        'content': comment.content,
        'has_children': bool(getattr(comment, 'commented', [])),
        'modified': comment.edited,
        'is_deleted': comment.is_deleted,
        'spam_status': comment.spam_status,
        'reports': reports,
        'node': comment.node,
        'category': reports[0]['category'],
        'osf_support_email': OSF_SUPPORT_EMAIL,
    }
Пример #23
0
def get_user_from_cas_resp(cas_resp):
    """
    Given a CAS service validation response, attempt to retrieve user information and next action.
    The `user` in `cas_resp` is the unique GUID of the user. Please do not use the primary key `id`
    or the email `username`. This holds except for the first step of ORCiD login.

    :param cas_resp: the cas service validation response
    :return: the user, the external_credential, and the next action
    """
    from osf.models import OSFUser
    if cas_resp.user:
        user = OSFUser.load(cas_resp.user)
        # cas returns a valid OSF user id
        if user:
            return user, None, 'authenticate'
        # cas does not return a valid OSF user id
        else:
            external_credential = validate_external_credential(cas_resp.user)
            # invalid cas response
            if not external_credential:
                return None, None, None
            # cas returns a valid external credential
            user = get_user(external_id_provider=external_credential['provider'],
                            external_id=external_credential['id'])
            # existing user found
            if user:
                return user, external_credential, 'authenticate'
            # user first time login through external identity provider
            else:
                return None, external_credential, 'external_first_login'
Пример #24
0
def get_public_components(uid=None, user=None):
    user = user or User.load(uid)

    rel_child_ids = (
        NodeRelation.objects.filter(
            child__is_public=True,
            child__type='osf.node',  # nodes only (not collections or registration)
            child___contributors=user,  # user is a contributor
            is_node_link=False  # exclude childs by node linkage
        )
        .exclude(parent__type='osf.collection')
        .exclude(child__is_deleted=True)
        .values_list('child_id', flat=True)
    )

    nodes = (Node.objects.filter(id__in=rel_child_ids)
    .include('contributor__user__guids', 'guids', '_parents__parent__guids')
    # Defer some fields that we don't use for rendering node lists
    .defer('child_node_subscriptions', 'date_created', 'deleted_date', 'description',
           'file_guid_to_share_uuids')
        .order_by('-date_modified'))

    return [
        serialize_node_summary(node=node, auth=Auth(user), show_path=True)
        for node in nodes
    ]
Пример #25
0
def sync_data_from_mailchimp(**kwargs):
    """Endpoint that the mailchimp webhook sends its data to"""
    key = request.args.get('key')

    if key == settings.MAILCHIMP_WEBHOOK_SECRET_KEY:
        r = request
        action = r.values['type']
        list_name = mailchimp_utils.get_list_name_from_id(list_id=r.values['data[list_id]'])
        username = r.values['data[email]']

        try:
            user = User.find_one(Q('username', 'eq', username))
        except NoResultsFound:
            sentry.log_exception()
            sentry.log_message('A user with this username does not exist.')
            raise HTTPError(404, data=dict(message_short='User not found',
                                        message_long='A user with this username does not exist'))
        if action == 'unsubscribe':
            user.mailchimp_mailing_lists[list_name] = False
            user.save()

        elif action == 'subscribe':
            user.mailchimp_mailing_lists[list_name] = True
            user.save()

    else:
        # TODO: get tests to pass with sentry logging
        # sentry.log_exception()
        # sentry.log_message("Unauthorized request to the OSF.")
        raise HTTPError(http.UNAUTHORIZED)
Пример #26
0
def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 3600):
    """
    A registered user claiming the unclaimed user account as an contributor to a project.
    Send an email for claiming the account to the referrer and notify the claimer.

    :param claimer: the claimer
    :param unclaimed_user: the user account to claim
    :param node: the project node where the user account is claimed
    :param throttle: the time period in seconds before another claim for the account can be made
    :return:
    :raise: http.BAD_REQUEST
    """

    unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key)

    # check throttle
    timestamp = unclaimed_record.get('last_sent')
    if not throttle_period_expired(timestamp, throttle):
        raise HTTPError(http.BAD_REQUEST, data=dict(
            message_long='User account can only be claimed with an existing user once every 24 hours'
        ))

    # roll the valid token for each email, thus user cannot change email and approve a different email address
    verification_key = generate_verification_key(verification_type='claim')
    unclaimed_record['token'] = verification_key['token']
    unclaimed_record['expires'] = verification_key['expires']
    unclaimed_record['claimer_email'] = claimer.username
    unclaimed_user.save()

    referrer = User.load(unclaimed_record['referrer_id'])
    claim_url = web_url_for(
        'claim_user_registered',
        uid=unclaimed_user._primary_key,
        pid=node._primary_key,
        token=unclaimed_record['token'],
        _external=True,
    )

    # Send mail to referrer, telling them to forward verification link to claimer
    mails.send_mail(
        referrer.username,
        mails.FORWARD_INVITE_REGISTERED,
        user=unclaimed_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        fullname=unclaimed_record['name'],
    )
    unclaimed_record['last_sent'] = get_timestamp()
    unclaimed_user.save()

    # Send mail to claimer, telling them to wait for referrer
    mails.send_mail(
        claimer.username,
        mails.PENDING_VERIFICATION_REGISTERED,
        fullname=claimer.fullname,
        referrer=referrer,
        node=node,
    )
Пример #27
0
def osfstorage_create_child(file_node, payload, node_addon, **kwargs):
    parent = file_node  # Just for clarity
    name = payload.get('name')
    user = OSFUser.load(payload.get('user'))
    is_folder = payload.get('kind') == 'folder'

    if not (name or user) or '/' in name:
        raise HTTPError(httplib.BAD_REQUEST)

    try:
        # Create a save point so that we can rollback and unlock
        # the parent record
        with transaction.atomic():
            if is_folder:
                created, file_node = True, parent.append_folder(name)
            else:
                created, file_node = True, parent.append_file(name)
    except (ValidationError, IntegrityError):
        created, file_node = False, parent.find_child_by_name(name, kind=int(not is_folder))

    if not created and is_folder:
        raise HTTPError(httplib.CONFLICT, data={
            'message': 'Cannot create folder "{name}" because a file or folder already exists at path "{path}"'.format(
                name=file_node.name,
                path=file_node.materialized_path,
            )
        })

    if not is_folder:
        try:
            if file_node.checkout is None or file_node.checkout._id == user._id:
                version = file_node.create_version(
                    user,
                    dict(payload['settings'], **dict(
                        payload['worker'], **{
                            'object': payload['metadata']['name'],
                            'service': payload['metadata']['provider'],
                        })
                    ),
                    dict(payload['metadata'], **payload['hashes'])
                )
                version_id = version._id
                archive_exists = version.archive is not None
            else:
                raise HTTPError(httplib.FORBIDDEN, data={
                    'message_long': 'File cannot be updated due to checkout status.'
                })
        except KeyError:
            raise HTTPError(httplib.BAD_REQUEST)
    else:
        version_id = None
        archive_exists = False

    return {
        'status': 'success',
        'archive': not archive_exists,  # Should waterbutler also archive this file
        'data': file_node.serialize(),
        'version': version_id,
    }, httplib.CREATED if created else httplib.OK
Пример #28
0
def _validate_reports(value, *args, **kwargs):
    from osf.models import OSFUser
    for key, val in value.iteritems():
        if not OSFUser.load(key):
            raise ValidationValueError('Keys must be user IDs')
        if not isinstance(val, dict):
            raise ValidationTypeError('Values must be dictionaries')
        if ('category' not in val or 'text' not in val or 'date' not in val or 'retracted' not in val):
            raise ValidationValueError(
                ('Values must include `date`, `category`, ',
                 '`text`, `retracted` keys')
            )
Пример #29
0
 def has_object_permission(self, request, view, obj):
     assert_resource_type(obj, self.acceptable_models)
     auth = get_user_auth(request)
     context = request.parser_context['kwargs']
     node = self.load_resource(context, view)
     user = OSFUser.load(context['user_id'])
     if request.method in permissions.SAFE_METHODS:
         return node.is_public or node.can_view(auth)
     elif request.method == 'DELETE':
         return node.has_permission(auth.user, osf_permissions.ADMIN) or auth.user == user
     else:
         return node.has_permission(auth.user, osf_permissions.ADMIN)
Пример #30
0
 def has_object_permission(self, request, view, obj):
     assert isinstance(obj, (AbstractNode, OSFUser, Contributor)), 'obj must be User, Contributor, or Node, got {}'.format(obj)
     auth = get_user_auth(request)
     context = request.parser_context['kwargs']
     node = AbstractNode.load(context[view.node_lookup_url_kwarg])
     user = OSFUser.load(context['user_id'])
     if request.method in permissions.SAFE_METHODS:
         return node.is_public or node.can_view(auth)
     elif request.method == 'DELETE':
         return node.has_permission(auth.user, osf_permissions.ADMIN) or auth.user == user
     else:
         return node.has_permission(auth.user, osf_permissions.ADMIN)
Пример #31
0
 def get_queryset(self):
     return OSFUser.find(Q('_id', 'eq', self.context['request'].user._id))
Пример #32
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_status.HTTP_400_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_status.HTTP_400_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)
            osf4m_source_tag, created = Tag.all_tags.get_or_create(
                name=CampaignSourceTags.Osf4m.value, system=True)
            osf4m_claimed_tag, created = Tag.all_tags.get_or_create(
                name=CampaignClaimedTags.Osf4m.value, system=True)
            if user_obj.all_tags.filter(id=osf4m_source_tag.id,
                                        system=True).exists():
                user_obj.add_system_tag(osf4m_claimed_tag)
            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'],
    }
Пример #33
0
def get_targets():
    logger.info('Acquiring targets...')
    targets = [u for u in OSFUser.find() if Collection.find(Q('is_bookmark_collection', 'eq', True) & Q('is_deleted', 'eq', False) & Q('creator', 'eq', u)).count() > 1]
    logger.info('Found {} target users.'.format(len(targets)))
    return targets
Пример #34
0
def find_by_email(email):
    try:
        return OSFUser.find_one(Q('username', 'iexact', email))
    except ModularOdmException:
        return None
Пример #35
0
def _get_current_user():
    # avoid cirdep
    from osf.models import OSFUser
    uid = session._get_current_object() and session.data.get('auth_user_id')
    return OSFUser.load(uid)
Пример #36
0
def get_auth(auth, **kwargs):
    cas_resp = None
    if not auth.user:
        # Central Authentication Server OAuth Bearer Token
        authorization = request.headers.get('Authorization')
        if authorization and authorization.startswith('Bearer '):
            client = cas.get_client()
            try:
                access_token = cas.parse_auth_header(authorization)
                cas_resp = client.profile(access_token)
            except cas.CasError as err:
                sentry.log_exception()
                # NOTE: We assume that the request is an AJAX request
                return json_renderer(err)
            if cas_resp.authenticated:
                auth.user = OSFUser.load(cas_resp.user)

    try:
        data = jwt.decode(jwe.decrypt(
            request.args.get('payload', '').encode('utf-8'),
            WATERBUTLER_JWE_KEY),
                          settings.WATERBUTLER_JWT_SECRET,
                          options={'require_exp': True},
                          algorithm=settings.WATERBUTLER_JWT_ALGORITHM)['data']
    except (jwt.InvalidTokenError, KeyError) as err:
        sentry.log_message(str(err))
        raise HTTPError(httplib.FORBIDDEN)

    if not auth.user:
        auth.user = OSFUser.from_cookie(data.get('cookie', ''))

    try:
        action = data['action']
        node_id = data['nid']
        provider_name = data['provider']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    node = AbstractNode.load(node_id)
    if not node:
        raise HTTPError(httplib.NOT_FOUND)

    check_access(node, auth, action, cas_resp)

    provider_settings = node.get_addon(provider_name)
    if not provider_settings:
        raise HTTPError(httplib.BAD_REQUEST)

    try:
        credentials = provider_settings.serialize_waterbutler_credentials()
        waterbutler_settings = provider_settings.serialize_waterbutler_settings(
        )
    except exceptions.AddonError:
        log_exception()
        raise HTTPError(httplib.BAD_REQUEST)

    return {
        'payload':
        jwe.encrypt(
            jwt.encode(
                {
                    'exp':
                    timezone.now() + datetime.timedelta(
                        seconds=settings.WATERBUTLER_JWT_EXPIRATION),
                    'data': {
                        'auth':
                        make_auth(
                            auth.user
                        ),  # A waterbutler auth dict not an Auth object
                        'credentials':
                        credentials,
                        'settings':
                        waterbutler_settings,
                        'callback_url':
                        node.api_url_for(('create_waterbutler_log'
                                          if not node.is_registration else
                                          'registration_callbacks'),
                                         _absolute=True,
                                         _internal=True),
                    }
                },
                settings.WATERBUTLER_JWT_SECRET,
                algorithm=settings.WATERBUTLER_JWT_ALGORITHM),
            WATERBUTLER_JWE_KEY)
    }
Пример #37
0
def send_confirm_email(user,
                       email,
                       renew=False,
                       external_id_provider=None,
                       external_id=None,
                       destination=None):
    """
    Sends `user` a confirmation to the given `email`.


    :param user: the user
    :param email: the email
    :param renew: refresh the token
    :param external_id_provider: user's external id provider
    :param external_id: user's external id
    :param destination: the destination page to redirect after confirmation
    :return:
    :raises: KeyError if user does not have a confirmation token for the given email.
    """

    confirmation_url = user.get_confirmation_url(
        email,
        external=True,
        force=True,
        renew=renew,
        external_id_provider=external_id_provider,
        destination=destination)

    try:
        merge_target = OSFUser.find_one(Q('emails__address', 'eq', email))
    except NoResultsFound:
        merge_target = None

    campaign = campaigns.campaign_for_user(user)
    branded_preprints_provider = None

    # Choose the appropriate email template to use and add existing_user flag if a merge or adding an email.
    if external_id_provider and external_id:
        # First time login through external identity provider, link or create an OSF account confirmation
        if user.external_identity[external_id_provider][
                external_id] == 'CREATE':
            mail_template = mails.EXTERNAL_LOGIN_CONFIRM_EMAIL_CREATE
        elif user.external_identity[external_id_provider][
                external_id] == 'LINK':
            mail_template = mails.EXTERNAL_LOGIN_CONFIRM_EMAIL_LINK
    elif merge_target:
        # Merge account confirmation
        mail_template = mails.CONFIRM_MERGE
        confirmation_url = '{}?logout=1'.format(confirmation_url)
    elif user.is_active:
        # Add email confirmation
        mail_template = mails.CONFIRM_EMAIL
        confirmation_url = '{}?logout=1'.format(confirmation_url)
    elif campaign:
        # Account creation confirmation: from campaign
        mail_template = campaigns.email_template_for_campaign(campaign)
        if campaigns.is_proxy_login(
                campaign
        ) and campaigns.get_service_provider(campaign) != 'OSF':
            branded_preprints_provider = campaigns.get_service_provider(
                campaign)
    else:
        # Account creation confirmation: from OSF
        mail_template = mails.INITIAL_CONFIRM_EMAIL

    mails.send_mail(email,
                    mail_template,
                    'plain',
                    user=user,
                    confirmation_url=confirmation_url,
                    email=email,
                    merge_target=merge_target,
                    external_id_provider=external_id_provider,
                    branded_preprints_provider=branded_preprints_provider)
Пример #38
0
def reverse_user(user_id):
    if isinstance(user_id, int):
        user = OSFUser.objects.get(id=user_id)
    else:
        user = OSFUser.load(user_id)
    return reverse('users:user', kwargs={'guid': user._id})
Пример #39
0
def deserialize_contributors(node, user_dicts, auth, validate=False):
    """View helper that returns a list of User objects from a list of
    serialized users (dicts). The users in the list may be registered or
    unregistered users.

    e.g. ``[{'id': 'abc123', 'registered': True, 'fullname': ..},
            {'id': None, 'registered': False, 'fullname'...},
            {'id': '123ab', 'registered': False, 'fullname': ...}]

    If a dict represents an unregistered user without an ID, creates a new
    unregistered User record.

    :param Node node: The node to add contributors to
    :param list(dict) user_dicts: List of serialized users in the format above.
    :param Auth auth:
    :param bool validate: Whether to validate and sanitize fields (if necessary)
    """

    # Add the registered contributors
    contribs = []
    for contrib_dict in user_dicts:
        fullname = contrib_dict['fullname']
        visible = contrib_dict['visible']
        email = contrib_dict.get('email')

        if validate is True:
            # Validate and sanitize inputs as needed. Email will raise error if invalid.
            # TODO Edge case bug: validation and saving are performed in same loop, so all in list
            # up to the invalid entry will be saved. (communicate to the user what needs to be retried)
            fullname = sanitize.strip_html(fullname)
            if not fullname:
                raise ValidationError('Full name field cannot be empty')
            if email:
                validate_email(
                    email)  # Will raise a ValidationError if email invalid

        if contrib_dict['id']:
            contributor = OSFUser.load(contrib_dict['id'])
        else:
            try:
                contributor = OSFUser.create_unregistered(fullname=fullname,
                                                          email=email)
                contributor.save()
            except ValidationError:
                ## FIXME: This suppresses an exception if ID not found & new validation fails; get_user will return None
                contributor = get_user(email=email)

        # Add unclaimed record if necessary
        if not contributor.is_registered:
            contributor.add_unclaimed_record(node=node,
                                             referrer=auth.user,
                                             given_name=fullname,
                                             email=email)
            contributor.save()

        contribs.append({
            'user':
            contributor,
            'visible':
            visible,
            'permissions':
            expand_permissions(contrib_dict.get('permission'))
        })
    return contribs
Пример #40
0
def get_target_user(auth, uid=None):
    target = OSFUser.load(uid) if uid else auth.user
    if target is None:
        raise HTTPError(http.NOT_FOUND)
    return target
Пример #41
0
def get_auth(auth, **kwargs):
    cas_resp = None
    if not auth.user:
        # Central Authentication Server OAuth Bearer Token
        authorization = request.headers.get('Authorization')
        if authorization and authorization.startswith('Bearer '):
            client = cas.get_client()
            try:
                access_token = cas.parse_auth_header(authorization)
                cas_resp = client.profile(access_token)
            except cas.CasError as err:
                sentry.log_exception()
                # NOTE: We assume that the request is an AJAX request
                return json_renderer(err)
            if cas_resp.authenticated:
                auth.user = OSFUser.load(cas_resp.user)

    try:
        data = jwt.decode(
            jwe.decrypt(request.args.get('payload', '').encode('utf-8'), WATERBUTLER_JWE_KEY),
            settings.WATERBUTLER_JWT_SECRET,
            options={'require_exp': True},
            algorithm=settings.WATERBUTLER_JWT_ALGORITHM
        )['data']
    except (jwt.InvalidTokenError, KeyError) as err:
        sentry.log_message(str(err))
        raise HTTPError(httplib.FORBIDDEN)

    if not auth.user:
        auth.user = OSFUser.from_cookie(data.get('cookie', ''))

    try:
        action = data['action']
        node_id = data['nid']
        provider_name = data['provider']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    node = AbstractNode.load(node_id)
    if not node:
        raise HTTPError(httplib.NOT_FOUND)

    check_access(node, auth, action, cas_resp)

    provider_settings = node.get_addon(provider_name)
    if not provider_settings:
        raise HTTPError(httplib.BAD_REQUEST)

    try:
        path = data.get('path')
        version = data.get('version')
        credentials = None
        waterbutler_settings = None
        fileversion = None
        if provider_name == 'osfstorage':
            if path and version:
                # check to see if this is a file or a folder
                filenode = OsfStorageFileNode.load(path.strip('/'))
                if filenode and filenode.is_file:
                    try:
                        fileversion = FileVersion.objects.filter(
                            basefilenode___id=path.strip('/'),
                            identifier=version
                        ).select_related('region').get()
                    except FileVersion.DoesNotExist:
                        raise HTTPError(httplib.BAD_REQUEST)
            # path and no version, use most recent version
            elif path:
                filenode = OsfStorageFileNode.load(path.strip('/'))
                if filenode and filenode.is_file:
                    fileversion = FileVersion.objects.filter(
                        basefilenode=filenode
                    ).select_related('region').order_by('-created').first()
            if fileversion:
                region = fileversion.region
                credentials = region.waterbutler_credentials
                waterbutler_settings = fileversion.serialize_waterbutler_settings(
                    node_id=provider_settings.owner._id,
                    root_id=provider_settings.root_node._id,
                )
        # If they haven't been set by version region, use the NodeSettings region
        if not (credentials and waterbutler_settings):
            credentials = provider_settings.serialize_waterbutler_credentials()
            waterbutler_settings = provider_settings.serialize_waterbutler_settings()
    except exceptions.AddonError:
        log_exception()
        raise HTTPError(httplib.BAD_REQUEST)

    return {'payload': jwe.encrypt(jwt.encode({
        'exp': timezone.now() + datetime.timedelta(seconds=settings.WATERBUTLER_JWT_EXPIRATION),
        'data': {
            'auth': make_auth(auth.user),  # A waterbutler auth dict not an Auth object
            'credentials': credentials,
            'settings': waterbutler_settings,
            'callback_url': node.api_url_for(
                ('create_waterbutler_log' if not node.is_registration else 'registration_callbacks'),
                _absolute=True,
                _internal=True
            ),
        }
    }, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM), WATERBUTLER_JWE_KEY)}
Пример #42
0
 def test_loading_by_short_guid(self):
     obj = UserFactory()
     assert OSFUser.load(obj._id) == obj
Пример #43
0
 def tearDown(self, app):
     super(TestUsersCreate, self).tearDown()
     app.reset()  # clears cookies
     OSFUser.remove()
Пример #44
0
def search_contributor(query,
                       page=0,
                       size=10,
                       exclude=None,
                       current_user=None):
    """Search for contributors to add to a project using elastic search. Request must
    include JSON data with a "query" field.

    :param query: The substring of the username to search for
    :param page: For pagination, the page number to use for results
    :param size: For pagination, the number of results per page
    :param exclude: A list of User objects to exclude from the search
    :param current_user: A User object of the current user

    :return: List of dictionaries, each containing the ID, full name,
        most recent employment and education, gravatar URL of an OSF user

    """
    start = (page * size)
    items = re.split(r'[\s-]+', query)
    exclude = exclude or []
    normalized_items = []
    for item in items:
        try:
            normalized_item = six.u(item)
        except TypeError:
            normalized_item = item
        normalized_item = unicodedata.normalize('NFKD',
                                                normalized_item).encode(
                                                    'ascii', 'ignore')
        normalized_items.append(normalized_item)
    items = normalized_items

    query = '  AND '.join('{}*~'.format(re.escape(item)) for item in items) + \
            ''.join(' NOT id:"{}"'.format(excluded._id) for excluded in exclude)

    results = search(build_query(query, start=start, size=size),
                     index=INDEX,
                     doc_type='user')
    docs = results['results']
    pages = math.ceil(results['counts'].get('user', 0) / size)
    validate_page_num(page, pages)

    users = []
    for doc in docs:
        # TODO: use utils.serialize_user
        user = User.load(doc['id'])

        if current_user and current_user._id == user._id:
            n_projects_in_common = -1
        elif current_user:
            n_projects_in_common = current_user.n_projects_in_common(user)
        else:
            n_projects_in_common = 0

        if user is None:
            logger.error('Could not load user {0}'.format(doc['id']))
            continue
        if user.is_active:  # exclude merged, unregistered, etc.
            current_employment = None
            education = None

            if user.jobs:
                current_employment = user.jobs[0]['institution']

            if user.schools:
                education = user.schools[0]['institution']

            users.append({
                'fullname':
                doc['user'],
                'id':
                doc['id'],
                'employment':
                current_employment,
                'education':
                education,
                'n_projects_in_common':
                n_projects_in_common,
                'gravatar_url':
                gravatar(user,
                         use_ssl=True,
                         size=settings.PROFILE_IMAGE_MEDIUM),
                'profile_url':
                user.profile_url,
                'registered':
                user.is_registered,
                'active':
                user.is_active
            })

    return {
        'users': users,
        'total': results['counts']['total'],
        'pages': pages,
        'page': page,
    }
Пример #45
0
def create_waterbutler_log(payload, **kwargs):
    with transaction.atomic():
        try:
            auth = payload['auth']
            # Don't log download actions
            if payload['action'] in ('download_file', 'download_zip'):
                return {'status': 'success'}
            action = LOG_ACTION_MAP[payload['action']]
        except KeyError:
            raise HTTPError(httplib.BAD_REQUEST)

        user = OSFUser.load(auth['id'])
        if user is None:
            raise HTTPError(httplib.BAD_REQUEST)

        auth = Auth(user=user)
        node = kwargs['node'] or kwargs['project']

        if action in (NodeLog.FILE_MOVED, NodeLog.FILE_COPIED):

            for bundle in ('source', 'destination'):
                for key in ('provider', 'materialized', 'name', 'nid'):
                    if key not in payload[bundle]:
                        raise HTTPError(httplib.BAD_REQUEST)

            dest = payload['destination']
            src = payload['source']

            if src is not None and dest is not None:
                dest_path = dest['materialized']
                src_path = src['materialized']
                if dest_path.endswith('/') and src_path.endswith('/'):
                    dest_path = os.path.dirname(dest_path)
                    src_path = os.path.dirname(src_path)
                if (os.path.split(dest_path)[0] == os.path.split(src_path)[0]
                        and dest['provider'] == src['provider']
                        and dest['nid'] == src['nid']
                        and dest['name'] != src['name']):
                    action = LOG_ACTION_MAP['rename']

            destination_node = node  # For clarity
            source_node = AbstractNode.load(payload['source']['nid'])

            source = source_node.get_addon(payload['source']['provider'])
            destination = node.get_addon(payload['destination']['provider'])

            payload['source'].update({
                'materialized':
                payload['source']['materialized'].lstrip('/'),
                'addon':
                source.config.full_name,
                'url':
                source_node.web_url_for(
                    'addon_view_or_download_file',
                    path=payload['source']['path'].lstrip('/'),
                    provider=payload['source']['provider']),
                'node': {
                    '_id': source_node._id,
                    'url': source_node.url,
                    'title': source_node.title,
                }
            })

            payload['destination'].update({
                'materialized':
                payload['destination']['materialized'].lstrip('/'),
                'addon':
                destination.config.full_name,
                'url':
                destination_node.web_url_for(
                    'addon_view_or_download_file',
                    path=payload['destination']['path'].lstrip('/'),
                    provider=payload['destination']['provider']),
                'node': {
                    '_id': destination_node._id,
                    'url': destination_node.url,
                    'title': destination_node.title,
                }
            })

            payload.update({
                'node': destination_node._id,
                'project': destination_node.parent_id,
            })

            if not payload.get('errors'):
                destination_node.add_log(action=action,
                                         auth=auth,
                                         params=payload)

            if payload.get('email') is True or payload.get('errors'):
                mails.send_mail(
                    user.username,
                    mails.FILE_OPERATION_FAILED
                    if payload.get('errors') else mails.FILE_OPERATION_SUCCESS,
                    action=payload['action'],
                    source_node=source_node,
                    destination_node=destination_node,
                    source_path=payload['source']['materialized'],
                    destination_path=payload['source']['materialized'],
                    source_addon=payload['source']['addon'],
                    destination_addon=payload['destination']['addon'],
                    osf_support_email=settings.OSF_SUPPORT_EMAIL)

            if payload.get('errors'):
                # Action failed but our function succeeded
                # Bail out to avoid file_signals
                return {'status': 'success'}

        else:
            try:
                metadata = payload['metadata']
                node_addon = node.get_addon(payload['provider'])
            except KeyError:
                raise HTTPError(httplib.BAD_REQUEST)

            if node_addon is None:
                raise HTTPError(httplib.BAD_REQUEST)

            metadata['path'] = metadata['path'].lstrip('/')

            node_addon.create_waterbutler_log(auth, action, metadata)

    with transaction.atomic():
        file_signals.file_updated.send(node=node,
                                       user=user,
                                       event_type=action,
                                       payload=payload)

    return {'status': 'success'}
Пример #46
0
def external_login_confirm_email_get(auth, uid, token):
    """
    View for email confirmation links when user first login through external identity provider.
    HTTP Method: GET

    When users click the confirm link, they are expected not to be logged in. If not, they will be logged out first and
    redirected back to this view. After OSF verifies the link and performs all actions, they will be automatically
    logged in through CAS and redirected back to this view again being authenticated.

    :param auth: the auth context
    :param uid: the user's primary key
    :param token: the verification token
    """

    user = OSFUser.load(uid)
    if not user:
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST)

    destination = request.args.get('destination')
    if not destination:
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST)

    # if user is already logged in
    if auth and auth.user:
        # if it is a wrong user
        if auth.user._id != user._id:
            return auth_logout(redirect_url=request.url)
        # if it is the expected user
        new = request.args.get('new', None)
        if destination in campaigns.get_campaigns():
            # external domain takes priority
            campaign_url = campaigns.external_campaign_url_for(destination)
            if not campaign_url:
                campaign_url = campaigns.campaign_url_for(destination)
            return redirect(campaign_url)
        if new:
            status.push_status_message(language.WELCOME_MESSAGE, kind='default', jumbotron=True, trust=True, id='welcome_message')
        return redirect(web_url_for('dashboard'))

    # token is invalid
    if token not in user.email_verifications:
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST)
    verification = user.email_verifications[token]
    email = verification['email']
    provider = verification['external_identity'].keys()[0]
    provider_id = verification['external_identity'][provider].keys()[0]
    # wrong provider
    if provider not in user.external_identity:
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST)
    external_status = user.external_identity[provider][provider_id]

    try:
        ensure_external_identity_uniqueness(provider, provider_id, user)
    except ValidationError as e:
        raise HTTPError(http_status.HTTP_403_FORBIDDEN, e.message)

    if not user.is_registered:
        user.register(email)

    if not user.emails.filter(address=email.lower()):
        user.emails.create(address=email.lower())

    user.date_last_logged_in = timezone.now()
    user.external_identity[provider][provider_id] = 'VERIFIED'
    user.social[provider.lower()] = provider_id
    del user.email_verifications[token]
    user.verification_key = generate_verification_key()
    user.save()

    service_url = request.url

    if external_status == 'CREATE':
        mails.send_mail(
            to_addr=user.username,
            mail=mails.WELCOME,
            mimetype='html',
            user=user,
            domain=settings.DOMAIN,
            osf_support_email=settings.OSF_SUPPORT_EMAIL,
            storage_flag_is_active=storage_i18n_flag_active(),
        )
        service_url += '&{}'.format(urlencode({'new': 'true'}))
    elif external_status == 'LINK':
        mails.send_mail(
            user=user,
            to_addr=user.username,
            mail=mails.EXTERNAL_LOGIN_LINK_SUCCESS,
            external_id_provider=provider,
            can_change_preferences=False,
        )

    # redirect to CAS and authenticate the user with the verification key
    return redirect(cas.get_login_url(
        service_url,
        username=user.username,
        verification_key=user.verification_key
    ))
Пример #47
0
 def test_get_user_by_id(self):
     user = UserFactory()
     assert_equal(OSFUser.load(user._id), user)
Пример #48
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_status.HTTP_401_UNAUTHORIZED)

    external_id_provider = session.data['auth_user_external_id_provider']
    external_id = session.data['auth_user_external_id']
    fullname = session.data.get('auth_user_fullname') or form.name.data
    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_status.HTTP_403_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)
            if not user.accepted_terms_of_service and form.accepted_terms_of_service.data:
                user.accepted_terms_of_service = timezone.now()
            # 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'
            accepted_terms_of_service = timezone.now() if form.accepted_terms_of_service.data else None
            user = OSFUser.create_unconfirmed(
                username=clean_email,
                password=None,
                fullname=fullname,
                external_identity=external_identity,
                campaign=None,
                accepted_terms_of_service=accepted_terms_of_service
            )
            # 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,
        'auth_user_fullname': fullname
    }
Пример #49
0
def prepare_for_registration_bulk_creation(payload_hash,
                                           initiator_id,
                                           provider_id,
                                           parsing_output,
                                           dry_run=False):

    # Check initiator
    initiator = OSFUser.load(initiator_id)
    if not initiator:
        message = f'Bulk upload preparation failure: initiator [id={initiator_id}] not found'
        return handle_internal_error(initiator=None,
                                     provider=None,
                                     message=message,
                                     dry_run=dry_run)

    # Check provider
    try:
        provider = RegistrationProvider.objects.get(_id=provider_id)
    except RegistrationProvider.DoesNotExist:
        message = f'Bulk upload preparation failure: registration provider [_id={provider_id}] not found'
        return handle_internal_error(initiator=initiator,
                                     provider=None,
                                     message=message,
                                     dry_run=dry_run)
    except RegistrationProvider.MultipleObjectsReturned:
        message = f'Bulk upload preparation failure: multiple registration providers returned for [_id={provider_id}]'
        return handle_internal_error(initiator=initiator,
                                     provider=None,
                                     message=message,
                                     dry_run=dry_run)

    # Check parsing output
    if not parsing_output:
        message = 'Bulk upload preparation failure: missing parser output as task input'
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=message,
                                     dry_run=dry_run)

    # Check schema
    schema_id = parsing_output.get('schema_id', None)
    try:
        schema = RegistrationSchema.objects.get(_id=schema_id)
    except RegistrationSchema.DoesNotExist:
        message = f'Bulk upload preparation failure: registration schema [_id={schema_id}] not found'
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=message,
                                     dry_run=dry_run)
    except RegistrationSchema.MultipleObjectsReturned:
        message = f'Bulk upload preparation failure: multiple registration schemas [_id={schema_id}] returned'
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=message,
                                     dry_run=dry_run)

    # Create the bulk upload job
    upload = RegistrationBulkUploadJob.create(payload_hash, initiator,
                                              provider, schema)
    logger.info(
        f'Creating a registration bulk upload job with [hash={upload.payload_hash}] ...'
    )
    if not dry_run:
        try:
            upload.save()
        except ValidationError:
            sentry.log_exception()
            message = 'Bulk upload preparation failure: failed to create the job'
            return handle_internal_error(initiator=initiator,
                                         provider=provider,
                                         message=message,
                                         dry_run=dry_run)
        upload.reload()
        logger.info(
            f'Bulk upload job created: [pk={upload.id}, hash={upload.payload_hash}]'
        )
    else:
        logger.info('Dry run: insertion did not happen')

    # Create registration rows for the bulk upload job
    registration_rows = parsing_output.get('registrations', [])
    if not registration_rows:
        message = 'Bulk upload preparation failure: missing registration rows'
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=message,
                                     dry_run=dry_run)
    initial_row_count = len(registration_rows)
    logger.info(
        f'Preparing [{initial_row_count}] registration rows for bulk creation ...'
    )

    bulk_upload_rows = set()
    draft_error_list = []
    try:
        for registration_row in registration_rows:
            bulk_upload_row = RegistrationBulkUploadRow.create(
                upload,
                registration_row.get('csv_raw', ''),
                registration_row.get('csv_parsed'),
            )
            metadata = bulk_upload_row.csv_parsed.get('metadata', {}) or {}
            row_external_id = metadata.get('External ID', 'N/A')
            row_title = metadata.get('Title', 'N/A')
            # Check duplicates with the database
            if RegistrationBulkUploadRow.objects.filter(
                    row_hash=bulk_upload_row.row_hash).exists():
                error = 'Duplicate rows - existing row found in the system'
                exception = RegistrationBulkCreationRowError(upload.id,
                                                             'N/A',
                                                             row_title,
                                                             row_external_id,
                                                             error=error)
                logger.error(exception.long_message)
                sentry.log_message(exception.long_message)
                draft_error_list.append(exception.short_message)
            else:
                # Don't `return` or `continue` so that duplicates within the rows can be detected
                pass
            # Check duplicates within the CSV
            if bulk_upload_row in bulk_upload_rows:
                error = 'Duplicate rows - CSV contains duplicate rows'
                exception = RegistrationBulkCreationRowError(upload.id,
                                                             'N/A',
                                                             row_title,
                                                             row_external_id,
                                                             error=error)
                logger.error(exception.long_message)
                sentry.log_message(exception.long_message)
                draft_error_list.append(exception.short_message)
            else:
                bulk_upload_rows.add(bulk_upload_row)
    except Exception as e:
        upload.delete()
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=repr(e),
                                     dry_run=dry_run)

    # Cancel the preparation task if duplicates are found in the CSV and/or in DB
    if draft_error_list:
        upload.delete()
        mails.send_mail(
            to_addr=initiator.username,
            mail=mails.REGISTRATION_BULK_UPLOAD_FAILURE_DUPLICATES,
            fullname=initiator.fullname,
            count=initial_row_count,
            draft_errors=draft_error_list,
            osf_support_email=settings.OSF_SUPPORT_EMAIL,
        )
        return

    if dry_run:
        logger.info(
            'Dry run: complete. Bulk creation did not run and emails are not sent.'
        )
        return

    try:
        logger.info(
            f'Bulk creating [{len(bulk_upload_rows)}] registration rows ...')
        created_objects = RegistrationBulkUploadRow.objects.bulk_create(
            bulk_upload_rows)
    except (ValueError, IntegrityError):
        upload.delete()
        sentry.log_exception()
        message = 'Bulk upload preparation failure: failed to create the rows.'
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=message,
                                     dry_run=dry_run)
    logger.info(f'[{len(created_objects)}] rows successfully prepared.')

    upload.state = JobState.INITIALIZED
    try:
        upload.save()
    except ValidationError:
        upload.delete()
        sentry.log_exception()
        message = 'Bulk upload preparation failure: job state update failed'
        return handle_internal_error(initiator=initiator,
                                     provider=provider,
                                     message=message,
                                     dry_run=dry_run)
    logger.info(
        f'Bulk upload preparation finished: [upload={upload.id}, state={upload.state.name}, '
        f'provider={upload.provider._id}, schema={upload.schema._id}, initiator={upload.initiator._id}]',
    )
Пример #50
0
def osfstorage_create_child(file_node, payload, **kwargs):
    parent = file_node  # Just for clarity
    name = payload.get('name')
    user = OSFUser.load(payload.get('user'))
    is_folder = payload.get('kind') == 'folder'

    if getattr(file_node.target, 'is_registration', False) and not getattr(file_node.target, 'archiving', False):
        raise HTTPError(
            httplib.BAD_REQUEST,
            data={
                'message_short': 'Registered Nodes are immutable',
                'message_long': "The operation you're trying to do cannot be applied to registered Nodes, which are immutable",
            }
        )

    if not (name or user) or '/' in name:
        raise HTTPError(httplib.BAD_REQUEST)

    if getattr(file_node.target, 'is_quickfiles', False) and is_folder:
        raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'})

    try:
        # Create a save point so that we can rollback and unlock
        # the parent record
        with transaction.atomic():
            if is_folder:
                created, file_node = True, parent.append_folder(name)
            else:
                created, file_node = True, parent.append_file(name)
    except (ValidationError, IntegrityError):
        created, file_node = False, parent.find_child_by_name(name, kind=int(not is_folder))

    if not created and is_folder:
        raise HTTPError(httplib.CONFLICT, data={
            'message_long': 'Cannot create folder "{name}" because a file or folder already exists at path "{path}"'.format(
                name=file_node.name,
                path=file_node.materialized_path,
            )
        })

    if not is_folder:
        try:
            if file_node.checkout is None or file_node.checkout._id == user._id:
                version = file_node.create_version(
                    user,
                    dict(payload['settings'], **dict(
                        payload['worker'], **{
                            'object': payload['metadata']['name'],
                            'service': payload['metadata']['provider'],
                        })
                    ),
                    dict(payload['metadata'], **payload['hashes'])
                )
                version_id = version._id
                archive_exists = version.archive is not None
            else:
                raise HTTPError(httplib.FORBIDDEN, data={
                    'message_long': 'File cannot be updated due to checkout status.'
                })
        except KeyError:
            raise HTTPError(httplib.BAD_REQUEST)
    else:
        version_id = None
        archive_exists = False

    return {
        'status': 'success',
        'archive': not archive_exists,  # Should waterbutler also archive this file
        'data': file_node.serialize(),
        'version': version_id,
    }, httplib.CREATED if created else httplib.OK
Пример #51
0
 def tearDown(self):
     super(CitationsNodeTestCase, self).tearDown()
     OSFUser.remove()
     OSFUser.remove()
Пример #52
0
def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 3600):
    """
    A registered user claiming the unclaimed user account as an contributor to a project.
    Send an email for claiming the account to the referrer and notify the claimer.

    :param claimer: the claimer
    :param unclaimed_user: the user account to claim
    :param node: the project node where the user account is claimed
    :param throttle: the time period in seconds before another claim for the account can be made
    :return:
    :raise: http.BAD_REQUEST
    """

    unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key)

    # check throttle
    timestamp = unclaimed_record.get('last_sent')
    if not throttle_period_expired(timestamp, throttle):
        raise HTTPError(http.BAD_REQUEST, data=dict(
            message_long='User account can only be claimed with an existing user once every 24 hours'
        ))

    # roll the valid token for each email, thus user cannot change email and approve a different email address
    verification_key = generate_verification_key(verification_type='claim')
    unclaimed_record['token'] = verification_key['token']
    unclaimed_record['expires'] = verification_key['expires']
    unclaimed_record['claimer_email'] = claimer.username
    unclaimed_user.save()

    referrer = OSFUser.load(unclaimed_record['referrer_id'])
    claim_url = web_url_for(
        'claim_user_registered',
        uid=unclaimed_user._primary_key,
        pid=node._primary_key,
        token=unclaimed_record['token'],
        _absolute=True,
    )

    # Send mail to referrer, telling them to forward verification link to claimer
    mails.send_mail(
        referrer.username,
        mails.FORWARD_INVITE_REGISTERED,
        user=unclaimed_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        fullname=unclaimed_record['name'],
        can_change_preferences=False,
        osf_contact_email=settings.OSF_CONTACT_EMAIL,
    )
    unclaimed_record['last_sent'] = get_timestamp()
    unclaimed_user.save()

    # Send mail to claimer, telling them to wait for referrer
    mails.send_mail(
        claimer.username,
        mails.PENDING_VERIFICATION_REGISTERED,
        fullname=claimer.fullname,
        referrer=referrer,
        node=node,
        can_change_preferences=False,
        osf_contact_email=settings.OSF_CONTACT_EMAIL,
    )
Пример #53
0
 def tearDown(self, *args, **kwargs):
     super(SessionUtilsTestCase, self).tearDown(*args, **kwargs)
     OSFUser.remove()
     Session.remove()
Пример #54
0
def send_claim_email(email, unclaimed_user, node, notify=True, throttle=24 * 3600, email_template='default'):
    """
    Unregistered user claiming a user account as an contributor to a project. Send an email for claiming the account.
    Either sends to the given email or the referrer's email, depending on the email address provided.

    :param str email: The address given in the claim user form
    :param User unclaimed_user: The User record to claim.
    :param Node node: The node where the user claimed their account.
    :param bool notify: If True and an email is sent to the referrer, an email
        will also be sent to the invited user about their pending verification.
    :param int throttle: Time period (in seconds) after the referrer is
        emailed during which the referrer will not be emailed again.
    :param str email_template: the email template to use
    :return
    :raise http.BAD_REQUEST

    """

    claimer_email = email.lower().strip()
    unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key)
    referrer = OSFUser.load(unclaimed_record['referrer_id'])
    claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True)

    # Option 1:
    #   When adding the contributor, the referrer provides both name and email.
    #   The given email is the same provided by user, just send to that email.
    preprint_provider = None
    logo = None
    if unclaimed_record.get('email') == claimer_email:
        # check email template for branded preprints
        if email_template == 'preprint':
            email_template, preprint_provider = find_preprint_provider(node)
            if not email_template or not preprint_provider:
                return
            mail_tpl = getattr(mails, 'INVITE_PREPRINT')(email_template, preprint_provider)
            if preprint_provider._id == 'osf':
                logo = settings.OSF_PREPRINTS_LOGO
            else:
                logo = preprint_provider._id
        else:
            mail_tpl = getattr(mails, 'INVITE_DEFAULT'.format(email_template.upper()))

        to_addr = claimer_email
        unclaimed_record['claimer_email'] = claimer_email
        unclaimed_user.save()
    # Option 2:
    # TODO: [new improvement ticket] this option is disabled from preprint but still available on the project page
    #   When adding the contributor, the referred only provides the name.
    #   The account is later claimed by some one who provides the email.
    #   Send email to the referrer and ask her/him to forward the email to the user.
    else:
        # check throttle
        timestamp = unclaimed_record.get('last_sent')
        if not throttle_period_expired(timestamp, throttle):
            raise HTTPError(http.BAD_REQUEST, data=dict(
                message_long='User account can only be claimed with an existing user once every 24 hours'
            ))
        # roll the valid token for each email, thus user cannot change email and approve a different email address
        verification_key = generate_verification_key(verification_type='claim')
        unclaimed_record['last_sent'] = get_timestamp()
        unclaimed_record['token'] = verification_key['token']
        unclaimed_record['expires'] = verification_key['expires']
        unclaimed_record['claimer_email'] = claimer_email
        unclaimed_user.save()

        claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True)
        # send an email to the invited user without `claim_url`
        if notify:
            pending_mail = mails.PENDING_VERIFICATION
            mails.send_mail(
                claimer_email,
                pending_mail,
                user=unclaimed_user,
                referrer=referrer,
                fullname=unclaimed_record['name'],
                node=node,
                can_change_preferences=False,
                osf_contact_email=settings.OSF_CONTACT_EMAIL,
            )
        mail_tpl = mails.FORWARD_INVITE
        to_addr = referrer.username

    # Send an email to the claimer (Option 1) or to the referrer (Option 2) with `claim_url`
    mails.send_mail(
        to_addr,
        mail_tpl,
        user=unclaimed_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        email=claimer_email,
        fullname=unclaimed_record['name'],
        branded_service=preprint_provider,
        can_change_preferences=False,
        logo=logo if logo else settings.OSF_LOGO,
        osf_contact_email=settings.OSF_CONTACT_EMAIL,
    )

    return to_addr
Пример #55
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 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.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.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()
        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
    }
Пример #56
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,
    }
Пример #57
0
 def get_queryset(self):
     # TODO: sort
     query = self.get_query_from_request()
     return OSFUser.find(query)
Пример #58
0
def get_auth(auth, **kwargs):
    cas_resp = None
    if not auth.user:
        # Central Authentication Server OAuth Bearer Token
        authorization = request.headers.get('Authorization')
        if authorization and authorization.startswith('Bearer '):
            client = cas.get_client()
            try:
                access_token = cas.parse_auth_header(authorization)
                cas_resp = client.profile(access_token)
            except cas.CasError as err:
                sentry.log_exception()
                # NOTE: We assume that the request is an AJAX request
                return json_renderer(err)
            if cas_resp.authenticated:
                auth.user = OSFUser.load(cas_resp.user)

    try:
        data = jwt.decode(jwe.decrypt(
            request.args.get('payload', '').encode('utf-8'),
            WATERBUTLER_JWE_KEY),
                          settings.WATERBUTLER_JWT_SECRET,
                          options={'require_exp': True},
                          algorithm=settings.WATERBUTLER_JWT_ALGORITHM)['data']
    except (jwt.InvalidTokenError, KeyError) as err:
        sentry.log_message(str(err))
        raise HTTPError(httplib.FORBIDDEN)

    if not auth.user:
        auth.user = OSFUser.from_cookie(data.get('cookie', ''))

    try:
        action = data['action']
        node_id = data['nid']
        provider_name = data['provider']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    node = AbstractNode.load(node_id) or Preprint.load(node_id)
    if not node:
        raise HTTPError(httplib.NOT_FOUND)

    check_access(node, auth, action, cas_resp)
    provider_settings = None
    if hasattr(node, 'get_addon'):
        provider_settings = node.get_addon(provider_name)
        if not provider_settings:
            raise HTTPError(httplib.BAD_REQUEST)

    path = data.get('path')
    credentials = None
    waterbutler_settings = None
    fileversion = None
    if provider_name == 'osfstorage':
        if path:
            file_id = path.strip('/')
            # check to see if this is a file or a folder
            filenode = OsfStorageFileNode.load(path.strip('/'))
            if filenode and filenode.is_file:
                # default to most recent version if none is provided in the response
                version = int(data['version']) if data.get(
                    'version') else filenode.versions.count()
                try:
                    fileversion = FileVersion.objects.filter(
                        basefilenode___id=file_id,
                        identifier=version).select_related('region').get()
                except FileVersion.DoesNotExist:
                    raise HTTPError(httplib.BAD_REQUEST)
                if auth.user:
                    # mark fileversion as seen
                    FileVersionUserMetadata.objects.get_or_create(
                        user=auth.user, file_version=fileversion)
                if not node.is_contributor(auth.user):
                    from_mfr = download_is_from_mfr(request, payload=data)
                    # version index is 0 based
                    version_index = version - 1
                    if action == 'render':
                        update_analytics(node, file_id, version_index, 'view')
                    elif action == 'download' and not from_mfr:
                        update_analytics(node, file_id, version_index,
                                         'download')
                    if waffle.switch_is_active(features.ELASTICSEARCH_METRICS):
                        if isinstance(node, Preprint):
                            metric_class = get_metric_class_for_action(
                                action, from_mfr=from_mfr)
                            if metric_class:
                                try:
                                    metric_class.record_for_preprint(
                                        preprint=node,
                                        user=auth.user,
                                        version=fileversion.identifier
                                        if fileversion else None,
                                        path=path)
                                except es_exceptions.ConnectionError:
                                    log_exception()
        if fileversion and provider_settings:
            region = fileversion.region
            credentials = region.waterbutler_credentials
            waterbutler_settings = fileversion.serialize_waterbutler_settings(
                node_id=provider_settings.owner._id,
                root_id=provider_settings.root_node._id,
            )
    # If they haven't been set by version region, use the NodeSettings or Preprint directly
    if not (credentials and waterbutler_settings):
        credentials = node.serialize_waterbutler_credentials(provider_name)
        waterbutler_settings = node.serialize_waterbutler_settings(
            provider_name)

    return {
        'payload':
        jwe.encrypt(
            jwt.encode(
                {
                    'exp':
                    timezone.now() + datetime.timedelta(
                        seconds=settings.WATERBUTLER_JWT_EXPIRATION),
                    'data': {
                        'auth':
                        make_auth(
                            auth.user
                        ),  # A waterbutler auth dict not an Auth object
                        'credentials':
                        credentials,
                        'settings':
                        waterbutler_settings,
                        'callback_url':
                        node.api_url_for(
                            ('create_waterbutler_log'
                             if not getattr(node, 'is_registration', False)
                             else 'registration_callbacks'),
                            _absolute=True,
                            _internal=True)
                    }
                },
                settings.WATERBUTLER_JWT_SECRET,
                algorithm=settings.WATERBUTLER_JWT_ALGORITHM),
            WATERBUTLER_JWE_KEY)
    }
Пример #59
0
def profile_view_id_json(uid, auth):
    user = OSFUser.load(uid)
    is_profile = auth and auth.user == user
    # Do NOT embed nodes, they aren't necessary
    return _profile_view(user, is_profile, embed_nodes=False)
Пример #60
0
def profile_view_id(uid, auth):
    user = OSFUser.load(uid)
    is_profile = auth and auth.user == user
    # Embed node data, so profile node lists can be rendered
    return _profile_view(user, is_profile, include_node_counts=True)