Exemplo n.º 1
0
    def test_throttle_period_expired_using_timestamp_in_seconds(self):
        timestamp = int(time.time())
        is_expired = throttle_period_expired(timestamp=(timestamp + 29), throttle=30)
        assert_false(is_expired)

        is_expired = throttle_period_expired(timestamp=(timestamp - 31), throttle=30)
        assert_true(is_expired)
Exemplo n.º 2
0
    def test_throttle_period_expired_using_datetime(self):
        timestamp = timezone.now()
        is_expired = throttle_period_expired(timestamp=(timestamp + datetime.timedelta(seconds=29)),  throttle=30)
        assert_false(is_expired)

        is_expired = throttle_period_expired(timestamp=(timestamp - datetime.timedelta(seconds=31)),  throttle=30)
        assert_true(is_expired)
Exemplo n.º 3
0
def user_account_password(auth, **kwargs):
    user = auth.user
    old_password = request.form.get('old_password', None)
    new_password = request.form.get('new_password', None)
    confirm_password = request.form.get('confirm_password', None)

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

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

    try:
        user.change_password(old_password, new_password, confirm_password)
    except ChangePasswordError as error:
        for m in error.messages:
            push_status_message(m, kind='warning', trust=False)
    else:
        # We have to logout the user first so all CAS sessions are invalid
        user.save()
        osf_logout()
        return redirect(cas.get_logout_url(cas.get_login_url(
            web_url_for('user_account', _absolute=True) + '?password_reset=True',
            username=user.username,
            verification_key=user.verification_key,
        )))
    user.save()
    return redirect(web_url_for('user_account'))
Exemplo n.º 4
0
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.get_user()
        existing_password = request.data['existing_password']
        new_password = request.data['new_password']

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

        # There have been more than 3 failed attempts and throttle hasn't expired.
        if user.old_password_invalid_attempts >= settings.INCORRECT_PASSWORD_ATTEMPTS_ALLOWED and not throttle_period_expired(
            user.change_password_last_attempt, settings.CHANGE_PASSWORD_THROTTLE,
        ):
            time_since_throttle = (timezone.now() - user.change_password_last_attempt.replace(tzinfo=pytz.utc)).total_seconds()
            wait_time = settings.CHANGE_PASSWORD_THROTTLE - time_since_throttle
            raise Throttled(wait=wait_time)

        try:
            # double new password for confirmation because validation is done on the front-end.
            user.change_password(existing_password, new_password, new_password)
        except ChangePasswordError as error:
            # A response object must be returned instead of raising an exception to avoid rolling back the transaction
            # and losing the incrementation of failed password attempts
            user.save()
            return JsonResponse(
                {'errors': [{'detail': message} for message in error.messages]},
                status=400,
                content_type='application/vnd.api+json; application/json',
            )

        user.save()
        remove_sessions_for_user(user)
        return Response(status=status.HTTP_204_NO_CONTENT)
Exemplo n.º 5
0
def resend_confirmation(auth):
    user = auth.user
    data = request.get_json()

    validate_user(data, user)
    if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
        raise HTTPError(httplib.BAD_REQUEST,
                        data={'message_long': 'Too many requests. Please wait a while before sending another confirmation email.'})

    try:
        primary = data['email']['primary']
        confirmed = data['email']['confirmed']
        address = data['email']['address'].strip().lower()
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    if primary or confirmed:
        raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'Cannnot resend confirmation for confirmed emails'})

    user.add_unconfirmed_email(address)

    # TODO: This setting is now named incorrectly.
    if settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
        send_confirm_email(user, email=address)
        user.email_last_sent = timezone.now()

    user.save()

    return _profile_view(user, is_profile=True)
Exemplo n.º 6
0
def user_account_password(auth, **kwargs):
    user = auth.user
    old_password = request.form.get('old_password', None)
    new_password = request.form.get('new_password', None)
    confirm_password = request.form.get('confirm_password', None)

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

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

    try:
        user.change_password(old_password, new_password, confirm_password)
    except ChangePasswordError as error:
        for m in error.messages:
            push_status_message(m, kind='warning', trust=False)
    else:
        # We have to logout the user first so all CAS sessions are invalid
        user.save()
        osf_logout()
        return redirect(cas.get_logout_url(cas.get_login_url(
            web_url_for('user_account', _absolute=True) + '?password_reset=True',
            username=user.username,
            verification_key=user.verification_key,
        )))
    user.save()
    return redirect(web_url_for('user_account'))
Exemplo n.º 7
0
def notify_added_node_group_member(group, node, user, permission, auth, throttle=None):
    throttle = throttle or settings.GROUP_CONNECTED_EMAIL_THROTTLE

    node_group_record = user.group_connected_email_records.get(group._id, {})
    if node_group_record:
        timestamp = node_group_record.get('last_sent', None)
        if timestamp:
            if not throttle_period_expired(timestamp, throttle):
                return
    else:
        user.group_connected_email_records[group._id] = {}

    if (not auth or auth.user != user) and user.is_registered:
        email_template = mails.GROUP_ADDED_TO_NODE
        mails.send_mail(
            to_addr=user.username,
            mail=email_template,
            mimetype='html',
            user=user,
            node=node,
            all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(user),
            group_name=group.name,
            permission=permission,
            referrer_name=auth.user.fullname if auth else '',
            osf_contact_email=settings.OSF_CONTACT_EMAIL,
        )

        user.group_connected_email_records[group._id]['last_sent'] = get_timestamp()
        user.save()
Exemplo n.º 8
0
def notify_added_group_member(group, user, permission, auth=None, throttle=None, email_template='default', *args, **kwargs):
    if email_template == 'false':
        return

    throttle = throttle or settings.GROUP_MEMBER_ADDED_EMAIL_THROTTLE

    member_record = user.member_added_email_records.get(group._id, {})
    if member_record:
        timestamp = member_record.get('last_sent', None)
        if timestamp:
            if not throttle_period_expired(timestamp, throttle):
                return
    else:
        user.member_added_email_records[group._id] = {}

    if user.is_registered:
        email_template = mails.GROUP_MEMBER_ADDED
        mails.send_mail(
            to_addr=user.username,
            mail=email_template,
            mimetype='html',
            user=user,
            group_name=group.name,
            permission=permission,
            referrer_name=auth.user.fullname if auth else '',
            osf_contact_email=settings.OSF_CONTACT_EMAIL,
        )
        user.member_added_email_records[group._id]['last_sent'] = get_timestamp()
        user.save()

    else:
        unreg_member_added.send(group, user=user, permission=permission, auth=auth, throttle=throttle, email_template=email_template)
Exemplo n.º 9
0
def resend_confirmation(auth):
    user = auth.user
    data = request.get_json()

    validate_user(data, user)
    if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST,
                        data={'message_long': 'Too many requests. Please wait a while before sending another confirmation email.'})

    try:
        primary = data['email']['primary']
        confirmed = data['email']['confirmed']
        address = data['email']['address'].strip().lower()
    except KeyError:
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST)

    if primary or confirmed:
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Cannnot resend confirmation for confirmed emails'})

    user.add_unconfirmed_email(address)

    # TODO: This setting is now named incorrectly.
    if settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
        send_confirm_email(user, email=address)
        user.email_last_sent = timezone.now()

    user.save()

    return _profile_view(user, is_profile=True)
Exemplo n.º 10
0
def user_account_password(auth, **kwargs):
    user = auth.user
    old_password = request.form.get('old_password', None)
    new_password = request.form.get('new_password', None)
    confirm_password = request.form.get('confirm_password', None)

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

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

    try:
        user.change_password(old_password, new_password, confirm_password)
        if user.verification_key_v2:
            user.verification_key_v2['expires'] = timezone.now()
        user.save()
    except ChangePasswordError as error:
        for m in error.messages:
            push_status_message(m, kind='warning', trust=False)
    else:
        push_status_message('Password updated successfully.', kind='success', trust=False)

    user.save()

    return redirect(web_url_for('user_account'))
Exemplo n.º 11
0
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.get_user()
        existing_password = request.data['existing_password']
        new_password = request.data['new_password']

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

        # There have been more than 3 failed attempts and throttle hasn't expired.
        if user.old_password_invalid_attempts >= settings.INCORRECT_PASSWORD_ATTEMPTS_ALLOWED and not throttle_period_expired(
            user.change_password_last_attempt, settings.CHANGE_PASSWORD_THROTTLE,
        ):
            time_since_throttle = (timezone.now() - user.change_password_last_attempt.replace(tzinfo=pytz.utc)).total_seconds()
            wait_time = settings.CHANGE_PASSWORD_THROTTLE - time_since_throttle
            raise Throttled(wait=wait_time)

        try:
            # double new password for confirmation because validation is done on the front-end.
            user.change_password(existing_password, new_password, new_password)
        except ChangePasswordError as error:
            # A response object must be returned instead of raising an exception to avoid rolling back the transaction
            # and losing the incrementation of failed password attempts
            user.save()
            return JsonResponse(
                {'errors': [{'detail': message} for message in error.messages]},
                status=400,
                content_type='application/vnd.api+json; application/json',
            )

        user.save()
        remove_sessions_for_user(user)
        return Response(status=status.HTTP_204_NO_CONTENT)
Exemplo n.º 12
0
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'):
    if email_template == 'false':
        return

    throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE

    # Email users for projects, or for components where they are not contributors on the parent node.
    if contributor.is_registered and \
            (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor))):

        mimetype = 'html'
        preprint_provider = None
        logo = None
        if email_template == 'preprint':
            email_template, preprint_provider = find_preprint_provider(node)
            if not email_template or not preprint_provider:
                return
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')(email_template, preprint_provider)
            if preprint_provider._id == 'osf':
                logo = settings.OSF_PREPRINTS_LOGO
            else:
                logo = preprint_provider._id
        elif email_template == 'access_request':
            mimetype = 'html'
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_ACCESS_REQUEST'.format(email_template.upper()))
        elif node.is_preprint:
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT_NODE_FROM_OSF'.format(email_template.upper()))
            logo = settings.OSF_PREPRINTS_LOGO
        else:
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper()))

        contributor_record = contributor.contributor_added_email_records.get(node._id, {})
        if contributor_record:
            timestamp = contributor_record.get('last_sent', None)
            if timestamp:
                if not throttle_period_expired(timestamp, throttle):
                    return
        else:
            contributor.contributor_added_email_records[node._id] = {}

        mails.send_mail(
            contributor.username,
            email_template,
            mimetype=mimetype,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else '',
            all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor),
            branded_service=preprint_provider,
            can_change_preferences=False,
            logo=logo if logo else settings.OSF_LOGO,
            osf_contact_email=settings.OSF_CONTACT_EMAIL
        )

        contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp()
        contributor.save()

    elif not contributor.is_registered:
        unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
Exemplo n.º 13
0
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'):
    if email_template == 'false':
        return

    throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE

    # Email users for projects, or for components where they are not contributors on the parent node.
    if contributor.is_registered and \
            (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor))):

        mimetype = 'html'
        preprint_provider = None
        logo = None
        if email_template == 'preprint':
            email_template, preprint_provider = find_preprint_provider(node)
            if not email_template or not preprint_provider:
                return
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')(email_template, preprint_provider)
            if preprint_provider._id == 'osf':
                logo = settings.OSF_PREPRINTS_LOGO
            else:
                logo = preprint_provider._id
        elif email_template == 'access_request':
            mimetype = 'html'
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_ACCESS_REQUEST'.format(email_template.upper()))
        elif node.is_preprint:
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT_NODE_FROM_OSF'.format(email_template.upper()))
            logo = settings.OSF_PREPRINTS_LOGO
        else:
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper()))

        contributor_record = contributor.contributor_added_email_records.get(node._id, {})
        if contributor_record:
            timestamp = contributor_record.get('last_sent', None)
            if timestamp:
                if not throttle_period_expired(timestamp, throttle):
                    return
        else:
            contributor.contributor_added_email_records[node._id] = {}

        mails.send_mail(
            contributor.username,
            email_template,
            mimetype=mimetype,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else '',
            all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor),
            branded_service=preprint_provider,
            can_change_preferences=False,
            logo=logo if logo else settings.OSF_LOGO,
            osf_contact_email=settings.OSF_CONTACT_EMAIL
        )

        contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp()
        contributor.save()

    elif not contributor.is_registered:
        unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
Exemplo n.º 14
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'],
        _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,
    )
Exemplo n.º 15
0
def check_email_throttle(node, contributor, throttle=None):
    throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE
    contributor_record = contributor.contributor_added_email_records.get(node._id, {})
    if contributor_record:
        timestamp = contributor_record.get('last_sent', None)
        if timestamp:
            if not throttle_period_expired(timestamp, throttle):
                return True
    else:
        contributor.contributor_added_email_records[node._id] = {}
Exemplo n.º 16
0
def forgot_password_post():
    """
    View for user to submit forgot password form.
    HTTP Method: POST
    :return {}
    """

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

    if not form.validate():
        # Don't go anywhere
        forms.push_errors_to_status(form.errors)
    else:
        email = form.email.data
        status_message = (
            'If there is an OSF account associated with {0}, an email with instructions on how to '
            'reset the OSF password has been sent to {0}. If you do not receive an email and believe '
            'you should have, please contact OSF Support. ').format(email)
        kind = 'success'
        # check if the user exists
        user_obj = get_user(email=email)
        if user_obj:
            # rate limit forgot_password_post
            if not throttle_period_expired(user_obj.email_last_sent,
                                           settings.SEND_EMAIL_THROTTLE):
                status_message = 'You have recently requested to change your password. Please wait a few minutes ' \
                                 'before trying again.'
                kind = 'error'
            # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email.
            elif user_obj.is_active:
                # new random verification key (v2)
                user_obj.verification_key_v2 = generate_verification_key(
                    verification_type='password')
                user_obj.email_last_sent = timezone.now()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for('reset_password_get',
                                uid=user_obj._id,
                                token=user_obj.verification_key_v2['token']))
                mails.send_mail(
                    to_addr=email,
                    mail=mails.FORGOT_PASSWORD,
                    reset_link=reset_link,
                    can_change_preferences=False,
                )

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

    return {}
Exemplo n.º 17
0
    def get_object(self):
        email_id = self.kwargs['email_id']
        user = self.get_user()
        email = None

        # check to see if it's a confirmed email with hashed id
        decoded_id = hashids.decode(email_id)
        if decoded_id:
            try:
                email = user.emails.get(id=decoded_id[0])
            except Email.DoesNotExist:
                email = None
            else:
                primary = email.address == user.username
                address = email.address
                confirmed = True
                verified = True
                is_merge = False

        # check to see if it's an unconfirmed email with a token
        elif user.unconfirmed_emails:
            try:
                email = user.email_verifications[email_id]
                address = email['email']
                confirmed = email['confirmed']
                verified = False
                primary = False
                is_merge = Email.objects.filter(address=address).exists()
            except KeyError:
                email = None

        if not email:
            raise NotFound

        # check for resend confirmation email query parameter in a GET request
        if self.request.method == 'GET' and is_truthy(
                self.request.query_params.get('resend_confirmation')):
            if not confirmed and settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
                if throttle_period_expired(user.email_last_sent,
                                           settings.SEND_EMAIL_THROTTLE):
                    send_confirm_email(user, email=address, renew=True)
                    user.email_last_sent = timezone.now()
                    user.save()

        return UserEmail(email_id=email_id,
                         address=address,
                         confirmed=confirmed,
                         verified=verified,
                         primary=primary,
                         is_merge=is_merge)
Exemplo n.º 18
0
def forgot_password_post():
    """
    View for user to submit forgot password form.
    HTTP Method: POST
    :return {}
    """

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

    if not form.validate():
        # Don't go anywhere
        forms.push_errors_to_status(form.errors)
    else:
        email = form.email.data
        status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to '
                          'reset the OSF password has been sent to {0}. If you do not receive an email and believe '
                          'you should have, please contact OSF Support. ').format(email)
        kind = 'success'
        # check if the user exists
        user_obj = get_user(email=email)
        if user_obj:
            # rate limit forgot_password_post
            if not throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                status_message = 'You have recently requested to change your password. Please wait a few minutes ' \
                                 'before trying again.'
                kind = 'error'
            # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email.
            elif user_obj.is_active:
                # new random verification key (v2)
                user_obj.verification_key_v2 = generate_verification_key(verification_type='password')
                user_obj.email_last_sent = timezone.now()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for(
                        'reset_password_get',
                        uid=user_obj._id,
                        token=user_obj.verification_key_v2['token']
                    )
                )
                mails.send_mail(
                    to_addr=email,
                    mail=mails.FORGOT_PASSWORD,
                    reset_link=reset_link,
                    can_change_preferences=False,
                )

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

    return {}
Exemplo n.º 19
0
def request_export(auth):
    user = auth.user
    if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
        raise HTTPError(httplib.BAD_REQUEST,
                        data={'message_long': 'Too many requests. Please wait a while before sending another account export request.',
                              'error_type': 'throttle_error'})

    mails.send_mail(
        to_addr=settings.OSF_SUPPORT_EMAIL,
        mail=mails.REQUEST_EXPORT,
        user=auth.user,
    )
    user.email_last_sent = timezone.now()
    user.save()
    return {'message': 'Sent account export request'}
Exemplo n.º 20
0
def request_export(auth):
    user = auth.user
    if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
        raise HTTPError(http_status.HTTP_400_BAD_REQUEST,
                        data={'message_long': 'Too many requests. Please wait a while before sending another account export request.',
                              'error_type': 'throttle_error'})

    mails.send_mail(
        to_addr=settings.OSF_SUPPORT_EMAIL,
        mail=mails.REQUEST_EXPORT,
        user=auth.user,
        can_change_preferences=False,
    )
    user.email_last_sent = timezone.now()
    user.save()
    return {'message': 'Sent account export request'}
Exemplo n.º 21
0
    def get_object(self):
        email_id = self.kwargs['email_id']
        user = self.get_user()
        email = None

        # check to see if it's a confirmed email with hashed id
        decoded_id = hashids.decode(email_id)
        if decoded_id:
            try:
                email = user.emails.get(id=decoded_id[0])
            except Email.DoesNotExist:
                email = None
            else:
                primary = email.address == user.username
                address = email.address
                confirmed = True
                verified = True
                is_merge = False

        # check to see if it's an unconfirmed email with a token
        elif user.unconfirmed_emails:
            try:
                email = user.email_verifications[email_id]
                address = email['email']
                confirmed = email['confirmed']
                verified = False
                primary = False
                is_merge = Email.objects.filter(address=address).exists()
            except KeyError:
                email = None

        if not email:
            raise NotFound

        # check for resend confirmation email query parameter in a GET request
        if self.request.method == 'GET' and is_truthy(self.request.query_params.get('resend_confirmation')):
            if not confirmed and settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
                if throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                    send_confirm_email(user, email=address, renew=True)
                    user.email_last_sent = timezone.now()
                    user.save()

        return UserEmail(email_id=email_id, address=address, confirmed=confirmed, verified=verified, primary=primary, is_merge=is_merge)
Exemplo n.º 22
0
def request_deactivation(auth):
    user = auth.user
    if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
        raise HTTPError(http.BAD_REQUEST,
                        data={
                            'message_long': 'Too many requests. Please wait a while before sending another account deactivation request.',
                            'error_type': 'throttle_error'
                        })

    mails.send_mail(
        to_addr=settings.OSF_SUPPORT_EMAIL,
        mail=mails.REQUEST_DEACTIVATION,
        user=auth.user,
        can_change_preferences=False,
    )
    user.email_last_sent = timezone.now()
    user.requested_deactivation = True
    user.save()
    return {'message': 'Sent account deactivation request'}
Exemplo n.º 23
0
def resend_confirmation_post(auth):
    """
    View for user to submit resend confirmation form.
    HTTP Method: POST
    """

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

    form = ResendConfirmationForm(request.form)

    if form.validate():
        clean_email = form.email.data
        user = get_user(email=clean_email)
        status_message = (
            'If there is an OSF account associated with this unconfirmed email address {0}, '
            'a confirmation email has been resent to it. If you do not receive an email and believe '
            'you should have, please contact OSF Support.').format(clean_email)
        kind = 'success'
        if user:
            if throttle_period_expired(user.email_last_sent,
                                       settings.SEND_EMAIL_THROTTLE):
                try:
                    send_confirm_email(user, clean_email, renew=True)
                except KeyError:
                    # already confirmed, redirect to dashboard
                    status_message = 'This email {0} has already been confirmed.'.format(
                        clean_email)
                    kind = 'warning'
                user.email_last_sent = timezone.now()
                user.save()
            else:
                status_message = (
                    'You have recently requested to resend your confirmation email. '
                    'Please wait a few minutes before trying again.')
                kind = 'error'
        status.push_status_message(status_message, kind=kind, trust=False)
    else:
        forms.push_errors_to_status(form.errors)

    # Don't go anywhere
    return {'form': form}
Exemplo n.º 24
0
def request_deactivation(auth):
    user = auth.user
    if not throttle_period_expired(user.email_last_sent,
                                   settings.SEND_EMAIL_THROTTLE):
        raise HTTPError(
            http.BAD_REQUEST,
            data={
                'message_long':
                'Too many requests. Please wait a while before sending another account deactivation request.',
                'error_type': 'throttle_error'
            })

    mails.send_mail(
        to_addr=settings.OSF_SUPPORT_EMAIL,
        mail=mails.REQUEST_DEACTIVATION,
        user=auth.user,
    )
    user.email_last_sent = timezone.now()
    user.requested_deactivation = True
    user.save()
    return {'message': 'Sent account deactivation request'}
Exemplo n.º 25
0
def resend_confirmation_post(auth):
    """
    View for user to submit resend confirmation form.
    HTTP Method: POST
    """

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

    form = ResendConfirmationForm(request.form)

    if form.validate():
        clean_email = form.email.data
        user = get_user(email=clean_email)
        status_message = ('If there is an OSF account associated with this unconfirmed email address {0}, '
                          'a confirmation email has been resent to it. If you do not receive an email and believe '
                          'you should have, please contact OSF Support.').format(clean_email)
        kind = 'success'
        if user:
            if throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                try:
                    send_confirm_email(user, clean_email, renew=True)
                except KeyError:
                    # already confirmed, redirect to dashboard
                    status_message = 'This email {0} has already been confirmed.'.format(clean_email)
                    kind = 'warning'
                user.email_last_sent = timezone.now()
                user.save()
            else:
                status_message = ('You have recently requested to resend your confirmation email. '
                                 'Please wait a few minutes before trying again.')
                kind = 'error'
        status.push_status_message(status_message, kind=kind, trust=False)
    else:
        forms.push_errors_to_status(form.errors)

    # Don't go anywhere
    return {'form': form}
Exemplo n.º 26
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
Exemplo n.º 27
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_status.HTTP_400_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_status.HTTP_400_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
Exemplo n.º 28
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_status.HTTP_400_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_status.HTTP_400_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,
    )
Exemplo n.º 29
0
def update_user(auth):
    """Update the logged-in user's profile."""

    # trust the decorator to handle auth
    user = auth.user
    data = request.get_json()

    validate_user(data, user)

    # TODO: Expand this to support other user attributes

    ##########
    # Emails #
    ##########

    if 'emails' in data:

        emails_list = [x['address'].strip().lower() for x in data['emails']]

        if user.username.strip().lower() not in emails_list:
            raise HTTPError(httplib.FORBIDDEN)

        available_emails = [
            each.strip().lower()
            for each in list(user.emails.values_list('address', flat=True)) +
            user.unconfirmed_emails
        ]
        # removals
        removed_emails = [
            each.strip().lower() for each in available_emails
            if each not in emails_list
        ]

        if user.username.strip().lower() in removed_emails:
            raise HTTPError(httplib.FORBIDDEN)

        for address in removed_emails:
            if user.emails.filter(address=address):
                try:
                    user.remove_email(address)
                except PermissionsError as e:
                    raise HTTPError(httplib.FORBIDDEN, str(e))
            user.remove_unconfirmed_email(address)

        # additions
        added_emails = [
            each['address'].strip().lower() for each in data['emails']
            if each['address'].strip().lower() not in available_emails
        ]

        for address in added_emails:
            try:
                user.add_unconfirmed_email(address)
            except (ValidationError, ValueError):
                raise HTTPError(http.BAD_REQUEST,
                                data=dict(message_long='Invalid Email'))
            except BlacklistedEmailError:
                sentry.log_message('User attempted to add a blacklisted email',
                                   extra_data={
                                       'user_id': user.id,
                                       'address': address,
                                   })
                raise HTTPError(
                    http.BAD_REQUEST,
                    data=dict(message_long=language.BLACKLISTED_EMAIL))

            # TODO: This setting is now named incorrectly.
            if settings.CONFIRM_REGISTRATIONS_BY_EMAIL:
                if not throttle_period_expired(user.email_last_sent,
                                               settings.SEND_EMAIL_THROTTLE):
                    raise HTTPError(
                        httplib.BAD_REQUEST,
                        data={
                            'message_long':
                            'Too many requests. Please wait a while before adding an email to your account.'
                        })
                send_confirm_email(user, email=address)

        ############
        # Username #
        ############

        # get the first email that is set to primary and has an address
        primary_email = next((
            each for each in data['emails']
            # email is primary
            if each.get('primary') and each.get('confirmed')
            # an address is specified (can't trust those sneaky users!)
            and each.get('address')))

        if primary_email:
            primary_email_address = primary_email['address'].strip().lower()
            if primary_email_address not in [
                    each.strip().lower()
                    for each in user.emails.values_list('address', flat=True)
            ]:
                raise HTTPError(httplib.FORBIDDEN)
            username = primary_email_address

        # make sure the new username has already been confirmed
        if username and username != user.username and user.emails.filter(
                address=username).exists():

            mails.send_mail(user.username,
                            mails.PRIMARY_EMAIL_CHANGED,
                            user=user,
                            new_address=username,
                            can_change_preferences=False,
                            osf_contact_email=settings.OSF_CONTACT_EMAIL)

            # Remove old primary email from subscribed mailing lists
            for list_name, subscription in user.mailchimp_mailing_lists.items(
            ):
                if subscription:
                    mailchimp_utils.unsubscribe_mailchimp_async(
                        list_name, user._id, username=user.username)
            user.username = username

    ###################
    # Timezone/Locale #
    ###################

    if 'locale' in data:
        if data['locale']:
            locale = data['locale'].replace('-', '_')
            user.locale = locale
    # TODO: Refactor to something like:
    #   user.timezone = data.get('timezone', user.timezone)
    if 'timezone' in data:
        if data['timezone']:
            user.timezone = data['timezone']

    user.save()

    # Update subscribed mailing lists with new primary email
    # TODO: move to user.save()
    for list_name, subscription in user.mailchimp_mailing_lists.items():
        if subscription:
            mailchimp_utils.subscribe_mailchimp(list_name, user._id)

    return _profile_view(user, is_profile=True)
Exemplo n.º 30
0
def before_request():
    # TODO: Fix circular import
    from framework.auth.core import get_user
    from framework.auth import cas
    from framework.utils import throttle_period_expired
    Session = apps.get_model('osf.Session')

    # Central Authentication Server Ticket Validation and Authentication
    ticket = request.args.get('ticket')
    if ticket:
        service_url = furl.furl(request.url)
        service_url.args.pop('ticket')
        # Attempt to authenticate wih CAS, and return a proper redirect response
        return cas.make_response_from_ticket(ticket=ticket, service_url=service_url.url)

    if request.authorization:
        user = get_user(
            email=request.authorization.username,
            password=request.authorization.password
        )
        # Create an empty session
        # TODO: Shoudn't need to create a session for Basic Auth
        user_session = Session()
        set_session(user_session)

        if user:
            user_addon = user.get_addon('twofactor')
            if user_addon and user_addon.is_confirmed:
                otp = request.headers.get('X-OSF-OTP')
                if otp is None or not user_addon.verify_code(otp):
                    # Must specify two-factor authentication OTP code or invalid two-factor authentication OTP code.
                    user_session.data['auth_error_code'] = http.UNAUTHORIZED
                    return
            user_session.data['auth_user_username'] = user.username
            user_session.data['auth_user_fullname'] = user.fullname
            if user_session.data.get('auth_user_id', None) != user._primary_key:
                user_session.data['auth_user_id'] = user._primary_key
                user_session.save()
        else:
            # Invalid key: Not found in database
            user_session.data['auth_error_code'] = http.UNAUTHORIZED
        return

    cookie = request.cookies.get(settings.COOKIE_NAME)
    if cookie:
        try:
            session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie)
            user_session = Session.load(session_id) or Session(_id=session_id)
        except itsdangerous.BadData:
            return
        if not throttle_period_expired(user_session.created, settings.OSF_SESSION_TIMEOUT):
            # Update date last login when making non-api requests
            if user_session.data.get('auth_user_id') and 'api' not in request.url:
                OSFUser = apps.get_model('osf.OSFUser')
                (
                    OSFUser.objects
                    .filter(guids___id__isnull=False, guids___id=user_session.data['auth_user_id'])
                    # Throttle updates
                    .filter(Q(date_last_login__isnull=True) | Q(date_last_login__lt=timezone.now() - dt.timedelta(seconds=settings.DATE_LAST_LOGIN_THROTTLE)))
                ).update(date_last_login=timezone.now())
            set_session(user_session)
        else:
            remove_session(user_session)
def get_campaigns():

    global CAMPAIGNS
    global CAMPAIGNS_LAST_REFRESHED

    if not CAMPAIGNS or (not mutex.locked() and throttle_period_expired(
            CAMPAIGNS_LAST_REFRESHED, CAMPAIGN_REFRESH_THRESHOLD)):
        with mutex:
            # Native campaigns: PREREG and ERPC
            newest_campaigns = {
                'prereg': {
                    'system_tag': CampaignSourceTags.Prereg.value,
                    'redirect_url': furl.furl(DOMAIN).add(path='prereg/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_PREREG,
                    'login_type': 'native',
                    'logo': settings.OSF_PREREG_LOGO
                },
                'erpc': {
                    'system_tag': CampaignSourceTags.ErpChallenge.value,
                    'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC,
                    'login_type': 'native',
                },
            }

            # Institution Login
            newest_campaigns.update({
                'institution': {
                    'system_tag': 'institution_campaign',
                    'redirect_url': '',
                    'login_type': 'institution',
                },
            })

            # Proxy campaigns: Preprints, both OSF and branded ones
            preprint_providers = PreprintProvider.objects.all()
            for provider in preprint_providers:
                if provider._id == 'osf':
                    template = 'osf'
                    name = 'OSF'
                    url_path = 'preprints/'
                    external_url = None
                else:
                    template = 'branded'
                    name = provider.name
                    url_path = 'preprints/{}'.format(provider._id)
                    external_url = provider.domain
                campaign = '{}-preprints'.format(provider._id)
                system_tag = provider_source_tag(provider._id, 'preprint')
                newest_campaigns.update({
                    campaign: {
                        'system_tag':
                        system_tag,
                        'redirect_url':
                        furl.furl(DOMAIN).add(path=url_path).url,
                        'external_url':
                        external_url,
                        'confirmation_email_template':
                        mails.CONFIRM_EMAIL_PREPRINTS(template, name),
                        'login_type':
                        'proxy',
                        'provider':
                        name,
                        'logo':
                        provider._id
                        if name != 'OSF' else settings.OSF_PREPRINTS_LOGO,
                    }
                })

            # Proxy campaigns: Registries, OSF only
            # TODO: refactor for futher branded registries when there is a model for registries providers
            newest_campaigns.update({
                'osf-registries': {
                    'system_tag': provider_source_tag('osf', 'registry'),
                    'redirect_url':
                    furl.furl(DOMAIN).add(path='registries/').url,
                    'confirmation_email_template':
                    mails.CONFIRM_EMAIL_REGISTRIES_OSF,
                    'login_type': 'proxy',
                    'provider': 'osf',
                    'logo': settings.OSF_REGISTRIES_LOGO
                }
            })

            newest_campaigns.update({
                'osf-registered-reports': {
                    'system_tag':
                    CampaignSourceTags.OsfRegisteredReports.value,
                    'redirect_url': furl.furl(DOMAIN).add(path='rr/').url,
                    'confirmation_email_template':
                    mails.CONFIRM_EMAIL_REGISTRIES_OSF,
                    'login_type': 'proxy',
                    'provider': 'osf',
                    'logo': settings.OSF_REGISTRIES_LOGO
                }
            })

            CAMPAIGNS = newest_campaigns
            CAMPAIGNS_LAST_REFRESHED = timezone.now()

    return CAMPAIGNS
Exemplo n.º 32
0
def _forgot_password_post(mail_template, reset_route, institutional=False):
    """
    View for user to submit forgot password form (standard or institutional).  Validates submitted
    form and sends reset-password link via email if valid.  If user has submitted another password
    reset request recently, declines to create a new one and asks the user to not submit again for
    awhile.

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

    HTTP Method: POST
    :return {}
    """

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

    if not form.validate():
        # Don't go anywhere
        forms.push_errors_to_status(form.errors)
    else:
        email = form.email.data
        status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to '
                          'reset the OSF password has been sent to {0}. If you do not receive an email and believe '
                          'you should have, please contact OSF Support. ').format(email)
        kind = 'success'
        # check if the user exists
        user_obj = get_user(email=email)
        if user_obj:
            # rate limit forgot_password_post
            if not throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                status_message = 'You have recently requested to change your password. Please wait a few minutes ' \
                                 'before trying again.'
                kind = 'error'
            # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email.
            elif user_obj.is_active:
                # new random verification key (v2)
                user_obj.verification_key_v2 = generate_verification_key(verification_type='password')
                user_obj.email_last_sent = timezone.now()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for(
                        reset_route,
                        uid=user_obj._id,
                        token=user_obj.verification_key_v2['token']
                    )
                )
                mails.send_mail(
                    to_addr=email,
                    mail=mail_template,
                    reset_link=reset_link,
                    can_change_preferences=False,
                )

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

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

    return {}
Exemplo n.º 33
0
 def test_throttle_period_expired_no_timestamp(self):
     is_expired = throttle_period_expired(timestamp=None, throttle=30)
     assert_true(is_expired)
Exemplo n.º 34
0
 def test_throttle_period_expired_no_timestamp(self):
     is_expired = throttle_period_expired(timestamp=None, throttle=30)
     assert_true(is_expired)
Exemplo n.º 35
0
def get_campaigns():

    global CAMPAIGNS
    global CAMPAIGNS_LAST_REFRESHED

    if not CAMPAIGNS or (not mutex.locked() and throttle_period_expired(CAMPAIGNS_LAST_REFRESHED, CAMPAIGN_REFRESH_THRESHOLD)):
        with mutex:
            # Native campaigns: PREREG and ERPC
            newest_campaigns = {
                'prereg': {
                    'system_tag': 'prereg_challenge_campaign',
                    'redirect_url': furl.furl(DOMAIN).add(path='prereg/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_PREREG,
                    'login_type': 'native',
                    'logo': settings.OSF_PREREG_LOGO
                },
                'erpc': {
                    'system_tag': 'erp_challenge_campaign',
                    'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC,
                    'login_type': 'native',
                },
            }

            # Institution Login
            newest_campaigns.update({
                'institution': {
                    'system_tag': 'institution_campaign',
                    'redirect_url': '',
                    'login_type': 'institution',
                },
            })

            # Proxy campaigns: Preprints, both OSF and branded ones
            preprint_providers = PreprintProvider.objects.all()
            for provider in preprint_providers:
                if provider._id == 'osf':
                    template = 'osf'
                    name = 'OSF'
                    url_path = 'preprints/'
                    external_url = None
                else:
                    template = 'branded'
                    name = provider.name
                    url_path = 'preprints/{}'.format(provider._id)
                    external_url = provider.domain
                campaign = '{}-preprints'.format(provider._id)
                system_tag = '{}_preprints'.format(provider._id)
                newest_campaigns.update({
                    campaign: {
                        'system_tag': system_tag,
                        'redirect_url': furl.furl(DOMAIN).add(path=url_path).url,
                        'external_url': external_url,
                        'confirmation_email_template': mails.CONFIRM_EMAIL_PREPRINTS(template, name),
                        'login_type': 'proxy',
                        'provider': name,
                        'logo': provider._id if name != 'OSF' else settings.OSF_PREPRINTS_LOGO,
                    }
                })

            # Proxy campaigns: Registries, OSF only
            # TODO: refactor for futher branded registries when there is a model for registries providers
            newest_campaigns.update({
                'osf-registries': {
                    'system_tag': 'osf_registries',
                    'redirect_url': furl.furl(DOMAIN).add(path='registries/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_REGISTRIES_OSF,
                    'login_type': 'proxy',
                    'provider': 'osf',
                    'logo': settings.OSF_REGISTRIES_LOGO
                }
            })

            newest_campaigns.update({
                'osf-registered-reports': {
                    'system_tag': 'osf_registered_reports',
                    'redirect_url': furl.furl(DOMAIN).add(path='rr/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_REGISTRIES_OSF,
                    'login_type': 'proxy',
                    'provider': 'osf',
                    'logo': settings.OSF_REGISTRIES_LOGO
                }
            })

            CAMPAIGNS = newest_campaigns
            CAMPAIGNS_LAST_REFRESHED = timezone.now()

    return CAMPAIGNS
Exemplo n.º 36
0
def before_request():
    # TODO: Fix circular import
    from framework.auth.core import get_user
    from framework.auth import cas
    from framework.utils import throttle_period_expired
    Session = apps.get_model('osf.Session')

    # Central Authentication Server Ticket Validation and Authentication
    ticket = request.args.get('ticket')
    if ticket:
        service_url = furl.furl(request.url)
        service_url.args.pop('ticket')
        # Attempt to authenticate wih CAS, and return a proper redirect response
        return cas.make_response_from_ticket(ticket=ticket,
                                             service_url=service_url.url)

    if request.authorization:
        user = get_user(email=request.authorization.username,
                        password=request.authorization.password)
        # Create an empty session
        # TODO: Shoudn't need to create a session for Basic Auth
        user_session = Session()
        set_session(user_session)

        if user:
            user_addon = user.get_addon('twofactor')
            if user_addon and user_addon.is_confirmed:
                otp = request.headers.get('X-OSF-OTP')
                if otp is None or not user_addon.verify_code(otp):
                    # Must specify two-factor authentication OTP code or invalid two-factor authentication OTP code.
                    user_session.data[
                        'auth_error_code'] = http_status.HTTP_401_UNAUTHORIZED
                    return
            user_session.data['auth_user_username'] = user.username
            user_session.data['auth_user_fullname'] = user.fullname
            if user_session.data.get('auth_user_id',
                                     None) != user._primary_key:
                user_session.data['auth_user_id'] = user._primary_key
                user_session.save()
        else:
            # Invalid key: Not found in database
            user_session.data[
                'auth_error_code'] = http_status.HTTP_401_UNAUTHORIZED
        return

    cookie = request.cookies.get(settings.COOKIE_NAME)
    if cookie:
        try:
            session_id = itsdangerous.Signer(
                settings.SECRET_KEY).unsign(cookie)
            user_session = Session.load(session_id) or Session(_id=session_id)
        except itsdangerous.BadData:
            return
        if not throttle_period_expired(user_session.created,
                                       settings.OSF_SESSION_TIMEOUT):
            # Update date last login when making non-api requests
            if user_session.data.get(
                    'auth_user_id') and 'api' not in request.url:
                OSFUser = apps.get_model('osf.OSFUser')
                (OSFUser.objects.filter(
                    guids___id__isnull=False,
                    guids___id=user_session.data['auth_user_id'])
                 # Throttle updates
                 .filter(
                     Q(date_last_login__isnull=True)
                     | Q(date_last_login__lt=timezone.now() - dt.timedelta(
                         seconds=settings.DATE_LAST_LOGIN_THROTTLE)))).update(
                             date_last_login=timezone.now())
            set_session(user_session)
        else:
            remove_session(user_session)