Esempio 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)
Esempio n. 2
0
    def test_throttle_period_expired_using_datetime(self):
        timestamp = datetime.datetime.utcnow()
        is_expired = throttle_period_expired(timestamp=(timestamp + datetime.timedelta(seconds=29)),  throttle=30)
        assert_false(is_expired)

        is_expired = throttle_period_expired(timestamp=(timestamp - datetime.timedelta(seconds=31)),  throttle=30)
        assert_true(is_expired)
Esempio n. 3
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)
Esempio n. 4
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)
Esempio n. 5
0
def notify_added_contributor(node, contributor, auth=None, throttle=None):
    throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE

    # Exclude forks and templates because the user forking/templating the project gets added
    # via 'add_contributor' but does not need to get notified.
    # Only email users for projects, or for components where they are not contributors on the parent node.
    if (contributor.is_registered and not node.template_node and not node.is_fork and
            (not node.parent_node or
                (node.parent_node and not node.parent_node.is_contributor(contributor)))):
        contributor_record = contributor.contributor_added_email_records.get(node._id, {})
        if contributor_record:
            timestamp = contributor_record.get('last_sent', None)
            if timestamp:
                if not throttle_period_expired(timestamp, throttle):
                    return
        else:
            contributor.contributor_added_email_records[node._id] = {}

        mails.send_mail(
            contributor.username,
            mails.CONTRIBUTOR_ADDED,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else '',
            all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor)
        )

        contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp()
        contributor.save()
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'):
    throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE

    # Exclude forks and templates because the user forking/templating the project gets added
    # via 'add_contributor' but does not need to get notified.
    # Only email users for projects, or for components where they are not contributors on the parent node.
    if (contributor.is_registered and not node.template_node and not node.is_fork and
            (not node.parent_node or
                (node.parent_node and not node.parent_node.is_contributor(contributor)))):
        email_template = getattr(mails, 'CONTRIBUTOR_ADDED_{}'.format(email_template.upper()))
        contributor_record = contributor.contributor_added_email_records.get(node._id, {})
        if contributor_record:
            timestamp = contributor_record.get('last_sent', None)
            if timestamp:
                if not throttle_period_expired(timestamp, throttle):
                    return
        else:
            contributor.contributor_added_email_records[node._id] = {}

        mails.send_mail(
            contributor.username,
            email_template,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else '',
            all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor)
        )

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

    elif not contributor.is_registered:
        unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
Esempio n. 7
0
def notify_added_contributor(node, contributor, auth=None, throttle=None):
    throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE

    # Exclude forks and templates because the user forking/templating the project gets added
    # via 'add_contributor' but does not need to get notified.
    # Only email users for projects, or for components where they are not contributors on the parent node.
    if (contributor.is_registered and not node.template_node and not node.is_fork and
            (not node.parent_node or
                (node.parent_node and not node.parent_node.is_contributor(contributor)))):
        contributor_record = contributor.contributor_added_email_records.get(node._id, {})
        if contributor_record:
            timestamp = contributor_record.get('last_sent', None)
            if timestamp:
                if not throttle_period_expired(timestamp, throttle):
                    return
        else:
            contributor.contributor_added_email_records[node._id] = {}

        mails.send_mail(
            contributor.username,
            mails.CONTRIBUTOR_ADDED,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else ''
        )

        contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp()
        contributor.save()
Esempio n. 8
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 = datetime.datetime.utcnow()

    user.save()

    return _profile_view(user, is_profile=True)
Esempio 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(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)
Esempio n. 10
0
def before_request():
    # TODO: Fix circular import
    from framework.auth.core import get_user
    from framework.auth import cas
    from website.util import time as util_time

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

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

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

    cookie = request.cookies.get(settings.COOKIE_NAME)
    if cookie:
        try:
            session_id = itsdangerous.Signer(
                settings.SECRET_KEY).unsign(cookie)
            user_session = Session.load(session_id) or Session(_id=session_id)
        except itsdangerous.BadData:
            return
        if not util_time.throttle_period_expired(user_session.date_created,
                                                 settings.OSF_SESSION_TIMEOUT):
            if user_session.data.get(
                    'auth_user_id') and 'api' not in request.url:
                database['user'].update(
                    {'_id': user_session.data.get('auth_user_id')},
                    {'$set': {
                        'date_last_login': datetime.utcnow()
                    }},
                    w=0)
            set_session(user_session)
        else:
            remove_session(user_session)
Esempio n. 11
0
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600):
    """Send an email for claiming a user account. Either sends to the given email
    or the referrer's email, depending on the email address provided.

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

    """
    claimer_email = email.lower().strip()

    unclaimed_record = user.get_unclaimed_record(node._primary_key)
    referrer = User.load(unclaimed_record['referrer_id'])
    claim_url = user.get_claim_url(node._primary_key, external=True)
    # If given email is the same provided by user, just send to that email
    if unclaimed_record.get('email') == claimer_email:
        mail_tpl = mails.INVITE
        to_addr = claimer_email
        unclaimed_record['claimer_email'] = claimer_email
        user.save()
    else:  # Otherwise have the referrer forward the email to the user
        # roll the valid token for each email, thus user cannot change email and approve a different email address
        timestamp = unclaimed_record.get('last_sent')
        if not throttle_period_expired(timestamp, throttle):
            raise HTTPError(400, data=dict(
                message_long='User account can only be claimed with an existing user once every 24 hours'
            ))
        unclaimed_record['last_sent'] = get_timestamp()
        unclaimed_record['token'] = generate_confirm_token()
        unclaimed_record['claimer_email'] = claimer_email
        user.save()
        claim_url = user.get_claim_url(node._primary_key, external=True)
        if notify:
            pending_mail = mails.PENDING_VERIFICATION
            mails.send_mail(
                claimer_email,
                pending_mail,
                user=user,
                referrer=referrer,
                fullname=unclaimed_record['name'],
                node=node
            )
        mail_tpl = mails.FORWARD_INVITE
        to_addr = referrer.username
    mails.send_mail(
        to_addr,
        mail_tpl,
        user=user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        email=claimer_email,
        fullname=unclaimed_record['name']
    )
    return to_addr
Esempio n. 12
0
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600):
    """Send an email for claiming a user account. Either sends to the given email
    or the referrer's email, depending on the email address provided.

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

    """
    claimer_email = email.lower().strip()

    unclaimed_record = user.get_unclaimed_record(node._primary_key)
    referrer = User.load(unclaimed_record['referrer_id'])
    claim_url = user.get_claim_url(node._primary_key, external=True)
    # If given email is the same provided by user, just send to that email
    if unclaimed_record.get('email') == claimer_email:
        mail_tpl = mails.INVITE
        to_addr = claimer_email
        unclaimed_record['claimer_email'] = claimer_email
        user.save()
    else:  # Otherwise have the referrer forward the email to the user
        # roll the valid token for each email, thus user cannot change email and approve a different email address
        timestamp = unclaimed_record.get('last_sent')
        if not throttle_period_expired(timestamp, throttle):
            raise HTTPError(400, data=dict(
                message_long='User account can only be claimed with an existing user once every 24 hours'
            ))
        unclaimed_record['last_sent'] = get_timestamp()
        unclaimed_record['token'] = generate_confirm_token()
        unclaimed_record['claimer_email'] = claimer_email
        user.save()
        claim_url = user.get_claim_url(node._primary_key, external=True)
        if notify:
            pending_mail = mails.PENDING_VERIFICATION
            mails.send_mail(
                claimer_email,
                pending_mail,
                user=user,
                referrer=referrer,
                fullname=unclaimed_record['name'],
                node=node
            )
        mail_tpl = mails.FORWARD_INVITE
        to_addr = referrer.username
    mails.send_mail(
        to_addr,
        mail_tpl,
        user=user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        email=claimer_email,
        fullname=unclaimed_record['name']
    )
    return to_addr
Esempio n. 13
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,
    )
Esempio 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 = 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,
    )
Esempio n. 15
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)):

        preprint_provider = None
        if email_template == 'preprint':
            email_template, preprint_provider = find_preprint_provider(node)
            if not email_template or not preprint_provider:
                return
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')(
                email_template, preprint_provider.name)
        elif node.is_preprint:
            email_template = getattr(
                mails, 'CONTRIBUTOR_ADDED_PREPRINT_NODE_FROM_OSF'.format(
                    email_template.upper()))
        else:
            email_template = getattr(
                mails,
                'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper()))

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

        mails.send_mail(
            contributor.username,
            email_template,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else '',
            all_global_subscriptions_none=
            check_if_all_global_subscriptions_are_none(contributor),
            branded_service=preprint_provider)

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

    elif not contributor.is_registered:
        unreg_contributor_added.send(node,
                                     contributor=contributor,
                                     auth=auth,
                                     email_template=email_template)
Esempio 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'
            else:
                # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email.
                # if the user account is not claimed yet
                if (user_obj.is_invited and
                        user_obj.unclaimed_records and
                        not user_obj.date_last_login and
                        not user_obj.is_claimed and
                        not user_obj.is_registered):
                    status_message = 'You cannot reset password on this account. Please contact OSF Support.'
                    kind = 'error'
                else:
                    # new random verification key (v2)
                    user_obj.verification_key_v2 = generate_verification_key(verification_type='password')
                    user_obj.email_last_sent = datetime.datetime.utcnow()
                    user_obj.save()
                    reset_link = furl.urljoin(
                        settings.DOMAIN,
                        web_url_for(
                            'reset_password_get',
                            uid=user_obj._id,
                            token=user_obj.verification_key_v2['token']
                        )
                    )
                    mails.send_mail(
                        to_addr=email,
                        mail=mails.FORGOT_PASSWORD,
                        reset_link=reset_link
                    )

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

    return {}
Esempio n. 17
0
def before_request():
    from framework.auth import cas
    from website.util import time as util_time

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

    if request.authorization:
        # TODO: Fix circular import
        from framework.auth.core import get_user
        user = get_user(
            email=request.authorization.username,
            password=request.authorization.password
        )
        # Create empty session
        # TODO: Shoudn't need to create a session for Basic Auth
        session = Session()
        set_session(session)

        if user:
            user_addon = user.get_addon('twofactor')
            if user_addon and user_addon.is_confirmed:
                otp = request.headers.get('X-OSF-OTP')
                if otp is None or not user_addon.verify_code(otp):
                    # Must specify two-factor authentication OTP code or invalid two-factor
                    # authentication OTP code.
                    session.data['auth_error_code'] = http.UNAUTHORIZED
                    return

            session.data['auth_user_username'] = user.username
            session.data['auth_user_id'] = user._primary_key
            session.data['auth_user_fullname'] = user.fullname
        else:
            # Invalid key: Not found in database
            session.data['auth_error_code'] = http.UNAUTHORIZED
        return

    cookie = request.cookies.get(settings.COOKIE_NAME)
    if cookie:
        try:
            session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie)
            session = Session.load(session_id) or Session(_id=session_id)
        except itsdangerous.BadData:
            return

        if not util_time.throttle_period_expired(session.date_created, settings.OSF_SESSION_TIMEOUT):
            if session.data.get('auth_user_id') and 'api' not in request.url:
                database['user'].update({'_id': session.data.get('auth_user_id')}, {'$set': {'date_last_login': datetime.utcnow()}}, w=0)
            set_session(session)
        else:
            remove_session(session)
Esempio n. 18
0
def forgot_password_post(auth, **kwargs):
    """
    View for user to submit forgot password form.
    HTTP Method: POST
    """

    # If user is already logged in, redirect to dashboard page.
    if auth.logged_in:
        return redirect(web_url_for('dashboard'))

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

    if form.validate():
        email = form.email.data
        status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to '
                          'reset the OSF password has been sent to {0}. If you do not receive an email and believe '
                          'you should have, please contact OSF Support. ').format(email)
        # check if the user exists
        user_obj = get_user(email=email)
        if user_obj:
            # check forgot_password rate limit
            if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                # new random verification key, allows OSF to check whether the reset_password request is valid,
                # this verification key is used twice, one for GET reset_password and one for POST reset_password
                # and it will be destroyed when POST reset_password succeeds
                user_obj.verification_key = generate_verification_key()
                user_obj.email_last_sent = datetime.datetime.utcnow()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for(
                        'reset_password_get',
                        verification_key=user_obj.verification_key
                    )
                )
                mails.send_mail(
                    to_addr=email,
                    mail=mails.FORGOT_PASSWORD,
                    reset_link=reset_link
                )
                status.push_status_message(status_message, kind='success', trust=False)
            else:
                status.push_status_message('You have recently requested to change your password. Please wait a '
                                           'few minutes before trying again.', kind='error', trust=False)
        else:
            status.push_status_message(status_message, kind='success', trust=False)
    else:
        forms.push_errors_to_status(form.errors)
        # Don't go anywhere

    return {}
Esempio n. 19
0
def forgot_password_post(auth, **kwargs):
    """
    View for user to submit forgot password form.
    HTTP Method: POST
    """

    # If user is already logged in, redirect to dashboard page.
    if auth.logged_in:
        return redirect(web_url_for('dashboard'))

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

    if form.validate():
        email = form.email.data
        status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to '
                          'reset the OSF password has been sent to {0}. If you do not receive an email and believe '
                          'you should have, please contact OSF Support. ').format(email)
        # check if the user exists
        user_obj = get_user(email=email)
        if user_obj:
            # check forgot_password rate limit
            if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                # new random verification key, allows OSF to check whether the reset_password request is valid,
                # this verification key is used twice, one for GET reset_password and one for POST reset_password
                # and it will be destroyed when POST reset_password succeeds
                user_obj.verification_key = generate_verification_key()
                user_obj.email_last_sent = datetime.datetime.utcnow()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for(
                        'reset_password_get',
                        verification_key=user_obj.verification_key
                    )
                )
                mails.send_mail(
                    to_addr=email,
                    mail=mails.FORGOT_PASSWORD,
                    reset_link=reset_link
                )
                status.push_status_message(status_message, kind='success', trust=False)
            else:
                status.push_status_message('You have recently requested to change your password. Please wait a '
                                           'few minutes before trying again.', kind='error', trust=False)
        else:
            status.push_status_message(status_message, kind='success', trust=False)
    else:
        forms.push_errors_to_status(form.errors)
        # Don't go anywhere

    return {}
Esempio n. 20
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.SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=auth.user)
    user.email_last_sent = datetime.datetime.utcnow()
    user.save()
    return {"message": "Sent account deactivation request"}
Esempio n. 21
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'}
Esempio n. 22
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.SUPPORT_EMAIL,
        mail=mails.REQUEST_EXPORT,
        user=auth.user,
    )
    user.email_last_sent = datetime.datetime.utcnow()
    user.save()
    return {'message': 'Sent account export request'}
Esempio n. 23
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.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'}
Esempio n. 24
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)):

        preprint_provider = None
        if email_template == 'preprint':
            email_template, preprint_provider = find_preprint_provider(node)
            if not email_template or not preprint_provider:
                return
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')(email_template, preprint_provider.name)
        else:
            email_template = getattr(mails, 'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper()))

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

        mails.send_mail(
            contributor.username,
            email_template,
            user=contributor,
            node=node,
            referrer_name=auth.user.fullname if auth else '',
            all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor),
            branded_service=preprint_provider
        )

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

    elif not contributor.is_registered:
        unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
Esempio 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}
Esempio n. 26
0
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600):
    unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key)
    # roll the valid token for each email, thus user cannot change email and approve a different email address
    timestamp = unclaimed_record.get('last_sent')
    if not throttle_period_expired(timestamp, throttle):
        raise HTTPError(
            400,
            data=dict(
                message_long=
                'User account can only be claimed with an existing user once every 24 hours'
            ))
    unclaimed_record['token'] = generate_confirm_token()
    unclaimed_record['claimer_email'] = claimer.username
    unreg_user.save()
    referrer = User.load(unclaimed_record['referrer_id'])
    claim_url = web_url_for(
        'claim_user_registered',
        uid=unreg_user._primary_key,
        pid=node._primary_key,
        token=unclaimed_record['token'],
        _external=True,
    )
    # Send mail to referrer, telling them to forward verification link to claimer
    mails.send_mail(
        referrer.username,
        mails.FORWARD_INVITE_REGISTERED,
        user=unreg_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        fullname=unclaimed_record['name'],
    )
    unclaimed_record['last_sent'] = get_timestamp()
    unreg_user.save()
    # Send mail to claimer, telling them to wait for referrer
    mails.send_mail(
        claimer.username,
        mails.PENDING_VERIFICATION_REGISTERED,
        fullname=claimer.fullname,
        referrer=referrer,
        node=node,
    )
Esempio n. 27
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.SUPPORT_EMAIL,
        mail=mails.REQUEST_DEACTIVATION,
        user=auth.user,
    )
    user.email_last_sent = datetime.datetime.utcnow()
    user.save()
    return {'message': 'Sent account deactivation request'}
Esempio n. 28
0
def forgot_password_post():
    """Attempt to send user password reset or return respective error.
    """
    form = ForgotPasswordForm(request.form, prefix='forgot_password')

    if form.validate():
        email = form.email.data
        status_message = (
            'If there is an OSF account associated with {0}, an email with instructions on how to reset '
            'the OSF password has been sent to {0}. If you do not receive an email and believe you '
            'should have, please contact OSF Support. ').format(email)
        user_obj = get_user(email=email)
        if user_obj:
            if throttle_period_expired(user_obj.email_last_sent,
                                       settings.SEND_EMAIL_THROTTLE):
                user_obj.verification_key = security.random_string(20)
                user_obj.email_last_sent = datetime.datetime.utcnow()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for('reset_password',
                                verification_key=user_obj.verification_key))
                mails.send_mail(to_addr=email,
                                mail=mails.FORGOT_PASSWORD,
                                reset_link=reset_link)
                status.push_status_message(status_message,
                                           kind='success',
                                           trust=False)
            else:
                status.push_status_message(
                    'You have recently requested to change your password. Please wait a little '
                    'while before trying again.',
                    kind='error',
                    trust=False)
        else:
            status.push_status_message(status_message,
                                       kind='success',
                                       trust=False)
    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Esempio n. 29
0
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600):
    unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key)
    # roll the valid token for each email, thus user cannot change email and approve a different email address
    timestamp = unclaimed_record.get('last_sent')
    if not throttle_period_expired(timestamp, throttle):
        raise HTTPError(400, data=dict(
            message_long='User account can only be claimed with an existing user once every 24 hours'
        ))
    unclaimed_record['token'] = generate_confirm_token()
    unclaimed_record['claimer_email'] = claimer.username
    unreg_user.save()
    referrer = User.load(unclaimed_record['referrer_id'])
    claim_url = web_url_for(
        'claim_user_registered',
        uid=unreg_user._primary_key,
        pid=node._primary_key,
        token=unclaimed_record['token'],
        _external=True,
    )
    # Send mail to referrer, telling them to forward verification link to claimer
    mails.send_mail(
        referrer.username,
        mails.FORWARD_INVITE_REGISTERED,
        user=unreg_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        fullname=unclaimed_record['name'],
    )
    unclaimed_record['last_sent'] = get_timestamp()
    unreg_user.save()
    # Send mail to claimer, telling them to wait for referrer
    mails.send_mail(
        claimer.username,
        mails.PENDING_VERIFICATION_REGISTERED,
        fullname=claimer.fullname,
        referrer=referrer,
        node=node,
    )
Esempio n. 30
0
def resend_confirmation_post(auth):
    """
    View for user to submit resend confirmation form.
    HTTP Method: POST
    """

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

    form = ResendConfirmationForm(request.form)

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

    # Don't go anywhere
    return {'form': form}
Esempio n. 31
0
def forgot_password_post():
    """Attempt to send user password reset or return respective error.
    """
    form = ForgotPasswordForm(request.form, prefix='forgot_password')

    if form.validate():
        email = form.email.data
        status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to reset '
                          'the OSF password has been sent to {0}. If you do not receive an email and believe you '
                          'should have, please contact OSF Support. ').format(email)
        user_obj = get_user(email=email)
        if user_obj:
            if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE):
                user_obj.verification_key = security.random_string(20)
                user_obj.email_last_sent = datetime.datetime.utcnow()
                user_obj.save()
                reset_link = furl.urljoin(
                    settings.DOMAIN,
                    web_url_for(
                        'reset_password',
                        verification_key=user_obj.verification_key
                    )
                )
                mails.send_mail(
                    to_addr=email,
                    mail=mails.FORGOT_PASSWORD,
                    reset_link=reset_link
                )
                status.push_status_message(status_message, kind='success', trust=False)
            else:
                status.push_status_message('You have recently requested to change your password. Please wait a little '
                                           'while before trying again.',
                                           kind='error',
                                           trust=False)
        else:
            status.push_status_message(status_message, kind='success', trust=False)
    forms.push_errors_to_status(form.errors)
    return auth_login(forgot_password_form=form)
Esempio n. 32
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',
                },
                'erpc': {
                    'system_tag': 'erp_challenge_campaign',
                    'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC,
                    'login_type': 'native',
                },
            }

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

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

            CAMPAIGNS = newest_campaigns
            CAMPAIGNS_LAST_REFRESHED = timezone.now()

    return CAMPAIGNS
Esempio n. 33
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 = User.load(unclaimed_record["referrer_id"])
    claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True)

    # When adding the contributor, the referrer provides both name and email.
    # The given email is the same provided by user, just send to that email.
    if unclaimed_record.get("email") == claimer_email:
        mail_tpl = getattr(mails, "INVITE_{}".format(email_template.upper()))
        to_addr = claimer_email
        unclaimed_record["claimer_email"] = claimer_email
        unclaimed_user.save()
    # When adding the contributor, the referred only provides the name.
    # The account is later claimed by some one who provides the email.
    # Send email to the referrer and ask her/him to forward the email to the user.
    else:
        # check throttle
        timestamp = unclaimed_record.get("last_sent")
        if not throttle_period_expired(timestamp, throttle):
            raise HTTPError(
                http.BAD_REQUEST,
                data=dict(message_long="User account can only be claimed with an existing user once every 24 hours"),
            )
        # roll the valid token for each email, thus user cannot change email and approve a different email address
        verification_key = generate_verification_key(verification_type="claim")
        unclaimed_record["last_sent"] = get_timestamp()
        unclaimed_record["token"] = verification_key["token"]
        unclaimed_record["expires"] = verification_key["expires"]
        unclaimed_record["claimer_email"] = claimer_email
        unclaimed_user.save()

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

    # send an email to the referrer with `claim_url`
    mails.send_mail(
        to_addr,
        mail_tpl,
        user=unclaimed_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        email=claimer_email,
        fullname=unclaimed_record["name"],
    )

    return to_addr
Esempio n. 34
0
def before_request():
    # TODO: Fix circular import
    from framework.auth.core import get_user
    from framework.auth import cas
    from website.util import time as util_time
    Session = apps.get_model('osf.Session')

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

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

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

    cookie = request.cookies.get(settings.COOKIE_NAME)
    if cookie:
        try:
            session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie)
            user_session = Session.load(session_id) or Session(_id=session_id)
        except itsdangerous.BadData:
            return
        if not util_time.throttle_period_expired(user_session.created, settings.OSF_SESSION_TIMEOUT):
            # Update date last login when making non-api requests
            if user_session.data.get('auth_user_id') and 'api' not in request.url:
                OSFUser = apps.get_model('osf.OSFUser')
                (
                    OSFUser.objects
                    .filter(guids___id__isnull=False, guids___id=user_session.data['auth_user_id'])
                    # Throttle updates
                    .filter(Q(date_last_login__isnull=True) | Q(date_last_login__lt=timezone.now() - dt.timedelta(seconds=settings.DATE_LAST_LOGIN_THROTTLE)))
                ).update(date_last_login=timezone.now())
            set_session(user_session)
        else:
            remove_session(user_session)
Esempio n. 35
0
 def test_throttle_period_expired_no_timestamp(self):
     is_expired = throttle_period_expired(timestamp=None, throttle=30)
     assert_true(is_expired)
Esempio n. 36
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 = User.load(unclaimed_record['referrer_id'])
    claim_url = unclaimed_user.get_claim_url(node._primary_key, external=True)

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

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

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

    # Send an email to the claimer (Option 1) or to the referrer (Option 2) with `claim_url`
    mails.send_mail(
        to_addr,
        mail_tpl,
        user=unclaimed_user,
        referrer=referrer,
        node=node,
        claim_url=claim_url,
        email=claimer_email,
        fullname=unclaimed_record['name'],
        branded_service_name=preprint_provider
    )

    return to_addr
Esempio n. 37
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',
                },
                'erpc': {
                    'system_tag': 'erp_challenge_campaign',
                    'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url,
                    'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC,
                    'login_type': 'native',
                },
            }

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

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

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

            CAMPAIGNS = newest_campaigns
            CAMPAIGNS_LAST_REFRESHED = timezone.now()

    return CAMPAIGNS
Esempio n. 38
0
def get_campaigns():

    global CAMPAIGNS
    global CAMPAIGNS_LAST_REFRESHED

    logger = logging.getLogger(__name__)

    if not CAMPAIGNS or throttle_period_expired(CAMPAIGNS_LAST_REFRESHED, CAMPAIGN_REFRESH_THRESHOLD):

        # Native campaigns: PREREG and ERPC
        CAMPAIGNS = {
            'prereg': {
                'system_tag': 'prereg_challenge_campaign',
                'redirect_url': furl.furl(DOMAIN).add(path='prereg/').url,
                'confirmation_email_template': mails.CONFIRM_EMAIL_PREREG,
                'login_type': 'native',
            },
            'erpc': {
                'system_tag': 'erp_challenge_campaign',
                'redirect_url': furl.furl(DOMAIN).add(path='erpc/').url,
                'confirmation_email_template': mails.CONFIRM_EMAIL_ERPC,
                'login_type': 'native',
            },
        }

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

        # Proxy campaigns: Preprints, both OSF and branded ones
        try:
            preprint_providers = PreprintProvider.find(Q('_id', 'ne', None))
            for provider in preprint_providers:
                if provider._id == 'osf':
                    template = 'osf'
                    name = 'OSF'
                    url_path = 'preprints/'
                else:
                    template = 'branded'
                    name = provider.name
                    url_path = 'preprints/{}'.format(provider._id)
                campaign = '{}-preprints'.format(provider._id)
                system_tag = '{}_preprints'.format(provider._id)
                CAMPAIGNS.update({
                    campaign: {
                        'system_tag': system_tag,
                        'redirect_url': furl.furl(DOMAIN).add(path=url_path).url,
                        'confirmation_email_template': mails.CONFIRM_EMAIL_PREPRINTS(template, name),
                        'login_type': 'proxy',
                        'provider': name,
                    }
                })
        except (NoResultsFound or QueryException or ImproperConfigurationError) as e:
            logger.warn('An error has occurred during campaign initialization: {}', e)

        CAMPAIGNS_LAST_REFRESHED = datetime.utcnow()

    return CAMPAIGNS
Esempio n. 39
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
    if unclaimed_record.get('email') == claimer_email:
        # check email template for branded preprints
        if email_template == 'preprint':
            email_template, preprint_provider = find_preprint_provider(node)
            if not email_template or not preprint_provider:
                return
            mail_tpl = getattr(mails,
                               'INVITE_PREPRINT')(email_template,
                                                  preprint_provider.name)
        else:
            mail_tpl = getattr(mails,
                               'INVITE_DEFAULT'.format(email_template.upper()))

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

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

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

    return to_addr
Esempio n. 40
0
 def test_throttle_period_expired_no_timestamp(self):
     is_expired = throttle_period_expired(timestamp=None, throttle=30)
     assert_true(is_expired)