예제 #1
0
def two_factor_email(token):
    if current_user.is_authenticated:
        return redirect_when_logged_in(current_user.id)

    # checks url is valid, and hasn't timed out
    try:
        token_data = json.loads(
            check_token(token, current_app.config['SECRET_KEY'],
                        current_app.config['DANGEROUS_SALT'],
                        current_app.config['EMAIL_2FA_EXPIRY_SECONDS']))
    except SignatureExpired as exc:
        # lets decode again, without the expiry, to get the user id out
        orig_data = json.loads(
            check_token(token, current_app.config['SECRET_KEY'],
                        current_app.config['DANGEROUS_SALT'], None))
        session['user_details'] = {'id': orig_data['user_id']}
        flash(
            "The link in the email we sent you has expired. We’ve sent you a new one."
        )
        return redirect(url_for('.resend_email_link'))

    user_id = token_data['user_id']
    # checks if code was already used
    logged_in, msg = user_api_client.check_verify_code(
        user_id, token_data['secret_code'], "email")

    if not logged_in:
        flash("This link has already been used")
        session['user_details'] = {'id': user_id}
        return redirect(url_for('.resend_email_link'))
    return log_in_user(user_id)
def test_should_throw_exception_when_token_is_tampered_with():
    import uuid
    token = generate_token(str(uuid.uuid4()), 'secret-key', 'dangerous-salt')
    try:
        check_token(token + 'qerqwer', 'secret-key', 'dangerous-salt', 30)
        fail()
    except BadSignature:
        pass
예제 #3
0
def test_should_throw_exception_when_token_is_tampered_with():
    import uuid
    token = generate_token(str(uuid.uuid4()), 'secret-key', 'dangerous-salt')
    try:
        check_token(token + 'qerqwer', 'secret-key', 'dangerous-salt', 30)
        fail()
    except BadSignature:
        pass
예제 #4
0
def verify_email(token):
    try:
        token_data = check_token(
            token,
            current_app.config["SECRET_KEY"],
            current_app.config["DANGEROUS_SALT"],
            current_app.config["EMAIL_EXPIRY_SECONDS"],
        )
    except SignatureExpired:
        flash(
            _("The security code in the email we sent you has expired. We’ve sent you a new one."
              ))
        return redirect(url_for("main.resend_email_verification"))

    # token contains json blob of format: {'user_id': '...', 'secret_code': '...'} (secret_code is unused)
    token_data = json.loads(token_data)
    user = User.from_id(token_data["user_id"])
    if not user:
        abort(404)

    if user.is_active:
        flash(_("That verification link has expired."))
        return redirect(url_for("main.sign_in"))

    session["user_details"] = {"email": user.email_address, "id": user.id}
    user.send_verify_code()
    return redirect(url_for("main.verify"))
예제 #5
0
def validate_invitation_token(invitation_type, token):

    max_age_seconds = 60 * 60 * 24 * current_app.config[
        "INVITATION_EXPIRATION_DAYS"]

    try:
        invited_user_id = check_token(
            token,
            current_app.config["SECRET_KEY"],
            current_app.config["DANGEROUS_SALT"],
            max_age_seconds,
        )
    except SignatureExpired:
        errors = {"invitation": "invitation expired"}
        raise InvalidRequest(errors, status_code=400)
    except BadData:
        errors = {"invitation": "bad invitation link"}
        raise InvalidRequest(errors, status_code=400)

    if invitation_type == "service":
        invited_user = get_invited_user_by_id(invited_user_id)
        return jsonify(data=invited_user_schema.dump(invited_user).data), 200
    elif invitation_type == "organisation":
        invited_user = dao_get_invited_organisation_user(invited_user_id)
        return jsonify(data=invited_user.serialize()), 200
    else:
        raise InvalidRequest(
            "Unrecognised invitation type: {}".format(invitation_type))
예제 #6
0
def new_password(token):
    from notifications_utils.url_safe_token import check_token
    try:
        token_data = check_token(token, current_app.config['SECRET_KEY'],
                                 current_app.config['DANGEROUS_SALT'],
                                 current_app.config['TOKEN_MAX_AGE_SECONDS'])
    except SignatureExpired:
        flash(
            'The link in the email we sent you has expired. Enter your email address to resend.'
        )
        return redirect(url_for('.forgot_password'))

    email_address = json.loads(token_data)['email']
    user = user_api_client.get_user_by_email(email_address)
    if user.password_changed_at and datetime.strptime(user.password_changed_at, '%Y-%m-%d %H:%M:%S.%f') > \
            datetime.strptime(json.loads(token_data)['created_at'], '%Y-%m-%d %H:%M:%S.%f'):
        flash('The link in the email has already been used')
        return redirect(url_for('main.index'))

    form = NewPasswordForm()

    if form.validate_on_submit():
        user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
        session['user_details'] = {
            'id': user.id,
            'email': user.email_address,
            'password': form.new_password.data
        }
        return redirect(url_for('main.two_factor'))
    else:
        return render_template('views/new-password.html',
                               token=token,
                               form=form,
                               user=user)
예제 #7
0
def new_password(token):
    from notifications_utils.url_safe_token import check_token

    try:
        token_data = check_token(
            token,
            current_app.config["SECRET_KEY"],
            current_app.config["DANGEROUS_SALT"],
            current_app.config["TOKEN_MAX_AGE_SECONDS"],
        )
    except SignatureExpired:
        flash("The link in the email we sent you has expired. Enter your email address to resend.")
        return redirect(url_for(".forgot_password"))

    email_address = json.loads(token_data)["email"]
    user = user_api_client.get_user_by_email(email_address)
    if user.password_changed_at and datetime.strptime(
        user.password_changed_at, "%Y-%m-%d %H:%M:%S.%f"
    ) > datetime.strptime(json.loads(token_data)["created_at"], "%Y-%m-%d %H:%M:%S.%f"):
        flash("The link in the email has already been used")
        return redirect(url_for("main.index"))

    form = NewPasswordForm()

    if form.validate_on_submit():
        user_api_client.send_verify_code(user.id, "sms", user.mobile_number)
        session["user_details"] = {"id": user.id, "email": user.email_address, "password": form.new_password.data}
        return redirect(url_for("main.two_factor"))
    else:
        return render_template("views/new-password.html", token=token, form=form, user=user)
예제 #8
0
def validate_invitation_token(invitation_type, token):

    max_age_seconds = 60 * 60 * 24 * current_app.config[
        'INVITATION_EXPIRATION_DAYS']

    try:
        invited_user_id = check_token(token, current_app.config['SECRET_KEY'],
                                      current_app.config['DANGEROUS_SALT'],
                                      max_age_seconds)
    except SignatureExpired:
        errors = {
            'invitation':
            'Your invitation to GOV.UK Notify has expired. '
            'Please ask the person that invited you to send you another one'
        }
        raise InvalidRequest(errors, status_code=400)
    except BadData:
        errors = {
            'invitation':
            'Something’s wrong with this link. Make sure you’ve copied the whole thing.'
        }
        raise InvalidRequest(errors, status_code=400)

    if invitation_type == 'service':
        invited_user = get_invited_user_by_id(invited_user_id)
        return jsonify(data=invited_user_schema.dump(invited_user).data), 200
    elif invitation_type == 'organisation':
        invited_user = dao_get_invited_organisation_user(invited_user_id)
        return jsonify(data=invited_user.serialize()), 200
    else:
        raise InvalidRequest(
            "Unrecognised invitation type: {}".format(invitation_type))
예제 #9
0
def verify_email(token):
    try:
        token_data = check_token(
            token,
            current_app.config['SECRET_KEY'],
            current_app.config['DANGEROUS_SALT'],
            current_app.config['EMAIL_EXPIRY_SECONDS']
        )
    except SignatureExpired:
        flash("The link in the email we sent you has expired. We've sent you a new one.")
        return redirect(url_for('main.resend_email_verification'))

    # token contains json blob of format: {'user_id': '...', 'secret_code': '...'} (secret_code is unused)
    token_data = json.loads(token_data)
    user = user_api_client.get_user(token_data['user_id'])
    if not user:
        abort(404)

    if user.is_active:
        flash("That verification link has expired.")
        return redirect(url_for('main.sign_in'))

    session['user_details'] = {"email": user.email_address, "id": user.id}
    user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
    return redirect(url_for('main.verify'))
예제 #10
0
def verify_email(token):
    try:
        token_data = check_token(token,
                                 current_app.config['SECRET_KEY'],
                                 current_app.config['DANGEROUS_SALT'],
                                 current_app.config['EMAIL_EXPIRY_SECONDS'])

        token_data = json.loads(token_data)
        verified = user_api_client.check_verify_code(token_data['user_id'], token_data['secret_code'], 'email')
        user = user_api_client.get_user(token_data['user_id'])
        if not user:
            abort(404)

        if user.is_active:
            flash("That verification link has expired.")
            return redirect(url_for('main.sign_in'))

        session['user_details'] = {"email": user.email_address, "id": user.id}
        if verified[0]:
            user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
            return redirect('verify')
        else:
            if verified[1] == 'Code has expired':
                flash("The link in the email we sent you has expired. We've sent you a new one.")
                return redirect(url_for('main.resend_email_verification'))
            else:
                message = "There was a problem verifying your account. Error message: '{}'".format(verified[1])
                flash(message)
                return redirect(url_for('main.index'))

    except SignatureExpired:
        flash('The link in the email we sent you has expired')
        return redirect(url_for('main.resend_email_verification'))
def new_password(token):
    try:
        token_data = check_token(token, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT'],
                                 current_app.config['EMAIL_EXPIRY_SECONDS'])
    except SignatureExpired:
        flash('The link in the email we sent you has expired. Enter your email address to resend.')
        return redirect(url_for('.forgot_password'))

    email_address = json.loads(token_data)['email']
    user = user_api_client.get_user_by_email(email_address)
    if user.password_changed_at and datetime.strptime(user.password_changed_at, '%Y-%m-%d %H:%M:%S.%f') > \
            datetime.strptime(json.loads(token_data)['created_at'], '%Y-%m-%d %H:%M:%S.%f'):
        flash('The link in the email has already been used')
        return redirect(url_for('main.index'))

    form = NewPasswordForm()

    if form.validate_on_submit():
        user_api_client.reset_failed_login_count(user.id)
        session['user_details'] = {
            'id': user.id,
            'email': user.email_address,
            'password': form.new_password.data}
        if user.auth_type == 'email_auth':
            # they've just clicked an email link, so have done an email auth journey anyway. Just log them in.
            return log_in_user(user.id)
        else:
            # send user a 2fa sms code
            user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
            return redirect(url_for('main.two_factor'))
    else:
        return render_template('views/new-password.html', token=token, form=form, user=user)
예제 #12
0
def two_factor_email(token):
    redirect_url = request.args.get('next')
    if current_user.is_authenticated:
        return redirect_when_logged_in(
            platform_admin=current_user.platform_admin)

    # checks url is valid, and hasn't timed out
    try:
        token_data = json.loads(
            check_token(token, current_app.config['SECRET_KEY'],
                        current_app.config['DANGEROUS_SALT'],
                        current_app.config['EMAIL_2FA_EXPIRY_SECONDS']))
    except SignatureExpired:
        return render_template('views/email-link-invalid.html',
                               redirect_url=redirect_url)

    user_id = token_data['user_id']
    # checks if code was already used
    logged_in, msg = user_api_client.check_verify_code(
        user_id, token_data['secret_code'], "email")

    if not logged_in:
        return render_template('views/email-link-invalid.html',
                               redirect_url=redirect_url)
    return log_in_user(user_id)
def test_return_none_when_token_is_expired():
    max_age = -1000
    payload = 'some_payload'
    token = generate_token(payload, 'secret-key', 'dangerous-salt')
    try:
        assert check_token(token, 'secret-key', 'dangerous-salt', max_age) is None
        fail('Expected a SignatureExpired exception')
    except SignatureExpired:
        pass
예제 #14
0
def user_profile_email_confirm(token):
    token_data = check_token(token, current_app.config['SECRET_KEY'],
                             current_app.config['DANGEROUS_SALT'],
                             current_app.config['EMAIL_EXPIRY_SECONDS'])
    token_data = json.loads(token_data)
    user = User.from_id(token_data['user_id'])
    user.update(email_address=token_data['email'])
    session.pop(NEW_EMAIL, None)

    return redirect(url_for('.user_profile'))
예제 #15
0
def test_return_none_when_token_is_expired():
    max_age = -1000
    payload = 'some_payload'
    token = generate_token(payload, 'secret-key', 'dangerous-salt')
    token = urllib.parse.unquote(token)
    try:
        assert check_token(token, 'secret-key', 'dangerous-salt', max_age) is None
        fail('Expected a SignatureExpired exception')
    except SignatureExpired:
        pass
예제 #16
0
def user_profile_email_confirm(token):
    token_data = check_token(token, current_app.config['SECRET_KEY'],
                             current_app.config['DANGEROUS_SALT'],
                             current_app.config['EMAIL_EXPIRY_SECONDS'])
    token_data = json.loads(token_data)
    user_id = token_data['user_id']
    new_email = token_data['email']
    user_api_client.update_user_attribute(user_id, email_address=new_email)
    session.pop(NEW_EMAIL, None)

    return redirect(url_for('.user_profile'))
예제 #17
0
def user_profile_email_confirm(token):
    token_data = check_token(token,
                             current_app.config['SECRET_KEY'],
                             current_app.config['DANGEROUS_SALT'],
                             current_app.config['EMAIL_EXPIRY_SECONDS'])
    token_data = json.loads(token_data)
    user_id = token_data['user_id']
    new_email = token_data['email']
    user_api_client.update_user_attribute(user_id,
                                          email_address=new_email)
    session.pop(NEW_EMAIL, None)

    return redirect(url_for('.user_profile'))
예제 #18
0
def user_profile_email_confirm(token):
    token_data = check_token(
        token,
        current_app.config["SECRET_KEY"],
        current_app.config["DANGEROUS_SALT"],
        current_app.config["EMAIL_EXPIRY_SECONDS"],
    )
    token_data = json.loads(token_data)
    user = User.from_id(token_data["user_id"])
    user.update(email_address=token_data["email"])
    session.pop(NEW_EMAIL, None)

    return redirect(url_for(".user_profile"))
예제 #19
0
def get_invited_user_by_token(token):

    max_age_seconds = 60 * 60 * 24 * current_app.config['INVITATION_EXPIRATION_DAYS']

    try:
        invited_user_id = check_token(token,
                                      current_app.config['SECRET_KEY'],
                                      current_app.config['DANGEROUS_SALT'],
                                      max_age_seconds)
    except SignatureExpired:
        errors = {'invitation':
                  ['Your invitation to GOV.UK Notify has expired. '
                   'Please ask the person that invited you to send you another one']}
        raise InvalidRequest(errors, status_code=400)

    invited_user = get_invited_user_by_id(invited_user_id)

    return jsonify(data=invited_user_schema.dump(invited_user).data), 200
예제 #20
0
def new_password(token):
    try:
        token_data = check_token(
            token,
            current_app.config["SECRET_KEY"],
            current_app.config["DANGEROUS_SALT"],
            current_app.config["EMAIL_EXPIRY_SECONDS"],
        )
    except SignatureExpired:
        flash(_("The security code in the email we sent you has expired. Enter your email address to re-send."))
        return redirect(url_for(".forgot_password"))

    email_address = json.loads(token_data)["email"]
    user = User.from_email_address(email_address)
    if user.password_changed_at and datetime.strptime(user.password_changed_at, "%Y-%m-%d %H:%M:%S.%f") > datetime.strptime(
        json.loads(token_data)["created_at"], "%Y-%m-%d %H:%M:%S.%f"
    ):
        flash(_("The security code in the email has already been used"))
        return redirect(url_for("main.index"))

    form = NewPasswordForm()

    if form.validate_on_submit():
        user.reset_failed_login_count()
        session["user_details"] = {
            "id": user.id,
            "email": user.email_address,
            "password": form.new_password.data,
        }
        if user.auth_type == "email_auth":
            # they've just clicked an email link, so have done an email auth journey anyway. Just log them in.
            return log_in_user(user.id)
        else:
            # send user a 2fa sms code
            user.send_verify_code()
            return redirect(url_for("main.two_factor_sms_sent"))
    else:
        return render_template("views/new-password.html", token=token, form=form, user=user)
예제 #21
0
def verify_email(token):
    try:
        token_data = check_token(token, current_app.config['SECRET_KEY'],
                                 current_app.config['DANGEROUS_SALT'],
                                 current_app.config['EMAIL_EXPIRY_SECONDS'])

        token_data = json.loads(token_data)
        verified = user_api_client.check_verify_code(token_data['user_id'],
                                                     token_data['secret_code'],
                                                     'email')
        user = user_api_client.get_user(token_data['user_id'])
        if not user:
            abort(404)

        if user.is_active:
            flash("That verification link has expired.")
            return redirect(url_for('main.sign_in'))

        session['user_details'] = {"email": user.email_address, "id": user.id}
        if verified[0]:
            user_api_client.send_verify_code(user.id, 'sms',
                                             user.mobile_number)
            return redirect('verify')
        else:
            if verified[1] == 'Code has expired':
                flash(
                    "The link in the email we sent you has expired. We've sent you a new one."
                )
                return redirect(url_for('main.resend_email_verification'))
            else:
                message = "There was a problem verifying your account. Error message: '{}'".format(
                    verified[1])
                flash(message)
                return redirect(url_for('main.index'))

    except SignatureExpired:
        flash('The link in the email we sent you has expired')
        return redirect(url_for('main.resend_email_verification'))
예제 #22
0
def reverify_email_token(token):
    try:
        token_data = check_token(token, current_app.config['SECRET_KEY'],
                                 current_app.config['DANGEROUS_SALT'],
                                 current_app.config['EMAIL_EXPIRY_SECONDS'])
    except SignatureExpired:
        flash(
            'The link in the email we sent you has expired. We\'ve sent you a new one.'
        )
        return redirect(url_for('main.resend_email_reverification'))

    token_data = json.loads(token_data)
    user = user_api_client.get_user(token_data['user_id'])

    if not user:
        abort(404)

    session['user_details'] = {
        'email': user.email_address,
        'id': user.id,
        'set_last_verified_at': True,
    }

    return log_in_user(user.id)
def test_should_return_payload_from_signed_token():
    payload = '*****@*****.**'
    token = generate_token(payload, 'secret-key', 'dangerous-salt')
    assert payload == check_token(token, 'secret-key', 'dangerous-salt', 30)
예제 #24
0
def accept_invite(token):
    try:
        check_token(token, current_app.config['SECRET_KEY'],
                    current_app.config['DANGEROUS_SALT'],
                    current_app.config['INVITATION_EXPIRY_SECONDS'])
    except SignatureExpired:
        errors = [
            'Your invitation to GOV.UK Notify has expired. '
            'Please ask the person that invited you to send you another one'
        ]
        return render_template("error/400.html", message=errors), 400

    invited_user = invite_api_client.check_token(token)

    if not current_user.is_anonymous and current_user.email_address.lower(
    ) != invited_user.email_address.lower():
        message = Markup("""
            You’re signed in as {}.
            This invite is for another email address.
            <a href={}>Sign out</a> and click the link again to accept this invite.
            """.format(current_user.email_address,
                       url_for("main.sign_out", _external=True)))

        flash(message=message)

        abort(403)

    if invited_user.status == 'cancelled':
        from_user = user_api_client.get_user(invited_user.from_user)
        service = service_api_client.get_service(invited_user.service)['data']
        return render_template('views/cancelled-invitation.html',
                               from_user=from_user.name,
                               service_name=service['name'])

    if invited_user.status == 'accepted':
        session.pop('invited_user', None)
        return redirect(
            url_for('main.service_dashboard', service_id=invited_user.service))

    session['invited_user'] = invited_user.serialize()

    existing_user = user_api_client.get_user_by_email_or_none(
        invited_user.email_address)
    service_users = user_api_client.get_users_for_service(invited_user.service)

    if existing_user:
        invite_api_client.accept_invite(invited_user.service, invited_user.id)
        if existing_user in service_users:
            return redirect(
                url_for('main.service_dashboard',
                        service_id=invited_user.service))
        else:
            service = service_api_client.get_service(
                invited_user.service)['data']
            # if the service you're being added to can modify auth type, then check if this is relevant
            if 'email_auth' in service['permissions'] and (
                    # they have a phone number, we want them to start using it. if they dont have a mobile we just
                    # ignore that option of the invite
                (existing_user.mobile_number
                 and invited_user.auth_type == 'sms_auth') or
                    # we want them to start sending emails. it's always valid, so lets always update
                    invited_user.auth_type == 'email_auth'):
                user_api_client.update_user_attribute(
                    existing_user.id, auth_type=invited_user.auth_type)
            user_api_client.add_user_to_service(invited_user.service,
                                                existing_user.id,
                                                invited_user.permissions)
            return redirect(
                url_for('main.service_dashboard',
                        service_id=invited_user.service))
    else:
        return redirect(url_for('main.register_from_invite'))
예제 #25
0
def test_should_return_payload_from_signed_token():
    payload = '*****@*****.**'
    token = generate_token(payload, 'secret-key', 'dangerous-salt')
    token = urllib.parse.unquote(token)
    assert payload == check_token(token, 'secret-key', 'dangerous-salt', 30)