Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Normalize OSF Verification Key [#OSF-6560] #5964

Merged

Conversation

cslzchen
Copy link
Contributor

@cslzchen cslzchen commented Jul 12, 2016

Update

  • Fix merge conflicts
  • Do not reveal user emails in email links
  • Normalize verification key
    • forgot and reset password
    • claim contributor-ship
    • confirm registration or email
    • update tests
  • Temporary Fix for OSF-6673
  • Several minor improvements
  • The following will be moved to a new feature ticket: https://openscience.atlassian.net/browse/OSF-6998.
    • Users can resend confirmation email for contributor claim
    • Bug fixing for OSF-6673

Purpose

Issue

When user forgets and resets password, verification key never gets deleted unless overwritten. In addition, validation only checks the key. Neither the user to whom it belongs nor whether it has expired is checked. Furthermore, verification key are used twice instead of "one-time" for several tasks.

Fix

Add User.verification_key_v2 which contains both the token string and an expiration time. Validation now checks username, token and expiration time. Renew the key once used.

Further Fix

There are two other functionality "User Claim Contributor-ship" and "Send/Resend Registration Confirmation Email" that have similar issue. Although they have their own way of verification, both are fixed with the same idea.

Changes

Normalized Verification Key

### defaults.py
# Expiration time for verification key
EXPIRATION_TIME_DICT = {
    'password': 30,         # 30 minutes for forgot and reset password
    'confirm': 24 * 60,     # 24 hours in minutes for confirm account and email
    'claim': 30 * 24 * 60    # 30 days in minutes for claim contributor-ship
}
### core.py
# generate verification key
def generate_verification_key(verification_type=None):
    """
    Generate a one-time verification key with an optional expiration time.
    The type of the verification key determines the expiration time defined in `website.settings.EXPIRATION_TIME_DICT`.

    :param verification_type: None, verify, confirm or claim
    :return: a string or a dictionary
    """
    token = security.random_string(30)
    # v1 with only the token
    if not verification_type:
        return token
    # v2 with a token and the expiration time
    expires = dt.datetime.utcnow() + dt.timedelta(minutes=settings.EXPIRATION_TIME_DICT[verification_type])
    return {
        'token': token,
        'expires': expires,
    }
### core.py/User
    verification_key = fields.StringField()

    verification_key_v2 = fields.DictionaryField(default=dict)
    # Format: {
    #   'token': <the verification token>
    #   'expires': <the verification expiration time>
    # }

    unclaimed_records = fields.DictionaryField(required=False)
    # Format: {
    #   <project_id>: {
    #       'name': <name that referrer provided>,
    #       'referrer_id': <user ID of referrer>,
    #       'token': <the verification token>,
    #       'expires': <the verification expiration time>,
    #       'email': <email the referrer provided or None>,
    #       'claimer_email': <email the claimer entered or None>,
    #       'last_sent': <timestamp of last email sent to referrer or None>
    #   }
    #   ...
    # }

    email_verifications = fields.DictionaryField(default=dict)
    # Format: {
    #   <token> : {
    #       'email': <email address>,
    #       'expiration': <datetime>,
    #       'confirmed': whether user is confirmed or not,
    #       'external_identity': user's external identity,
    #   }
    # }

Other

  • Resend confirmation will refresh the token.
  • Update get_or_create_user() with verification key.
  • Remove duplicated code get_claim_token() and get_confirm_token().
  • Remove "lingering" code fragments due to previous merge conflicts.
  • Update related routes, makos, factories and tests

Side effects

  • Password reset link becomes one time only. No longer valid once clicked.

Ticket

https://openscience.atlassian.net/browse/OSF-6560
https://openscience.atlassian.net/browse/SEC-33

@cslzchen cslzchen changed the title [WIP] [Feature] Check expiration time and username for verification key [OSF-#6560] [WIP] [Feature] Check expiration time and username for verification key [#OSF-6560] Jul 13, 2016
-   use get_verfiication_key() for token generation
-   remove redundant get_confirm_token() and unused get_claim_token()
-   add 'expires' to each record
-   check token equality and expires when verifying claim
    - fix test_modles.TestUnregisteredUser
    - revert previous changes
@cslzchen cslzchen changed the title [WIP] [Feature] Check expiration time and username for verification key [#OSF-6560] [Feature] Check expiration time and username for OSF verification key [#OSF-6560] Jul 21, 2016
# <token> : {
# 'email': <email address>,
# 'expiration': <datetime>,
# 'confirmed': whether user is confirmed or not
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For CR: just updated the comments, no code change

…o feature/verification-key.

    - remove should-have-gone `reset_password()` form auth views
    - remove should-have-gone code segment which is part of `test_can_reset_password_if_form_success()` from web tests
    - update resetpassowrd.mako to include `${username}` in form action


@collect_auth
def reset_password(auth, **kwargs):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[To CR] Deprecated Code

@cslzchen
Copy link
Contributor Author

cslzchen commented Sep 2, 2016

As discussed with @mfraezz , the resend claim confirmation will be handled in another improvement/feature ticket: https://openscience.atlassian.net/browse/OSF-6998, which takes care of https://openscience.atlassian.net/browse/OSF-6673 along the way.

@cslzchen cslzchen changed the title [Feature] Normalize OSF Verification Key [#OSF-6560, #OSF-6673] [Feature] Normalize OSF Verification Key [#OSF-6560] Sep 2, 2016
"""

# If user is already logged in, log user out
# TODO: discuss this with @MattF
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Contributor Author

@cslzchen cslzchen Sep 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore this # TODO, it was supposed to be added here https://github.com/CenterForOpenScience/osf.io/pull/5964/files#diff-9a64b4982d0bd80f7a498c65ddce2023R138.
As discussed on Friday, the decision is to log users out if they are already logged in instead of redirecting them to dashboard for both reset_password_get and forgot_password_get.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed, then?

'message_short': 'Invalid url.',
'message_long': 'The verification key in the URL is invalid or has expired.'
'message_short': 'Invalid Request.',
'message_long': 'The requested URL is invalid, has expired, or was already used',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update: a better message from product @saradbowman. The message is more informative without leaking the exact failure.

@mfraezz
Copy link
Member

mfraezz commented Sep 7, 2016

LGTM 🌳

Dropping this RTM

@brianjgeiger
Copy link
Collaborator

@cslzchen There are some merge conflicts that need resolving.

    - use verification key v2
    - institution: no verificatiton key is generated
    - meetings: verification key v2 generated for resetting password
    - replace `str verificiation_type` with `bool reset_password`
    - update usage and test
@@ -108,21 +108,24 @@ def register_unconfirmed(username, password, fullname, campaign=None):
return user


def get_or_create_user(fullname, address, is_spam=False):
"""Get or create user by email address.
def get_or_create_user(fullname, address, reset_password=True, is_spam=False):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add reset_password:

  1. for new user from institution login, no need to ask user to reset password after creation, thus no verification key
  2. for conference add_poster_by_email, create user and send user confirmation email to reset password, generate reset password verification key

EXPIRATION_TIME_DICT = {
'password': 30, # 30 minutes for forgot and reset password
'confirm': 24 * 60, # 24 hours in minutes for confirm account and email
'claim': 30 * 24 * 60 # 30 days in minutes for claim contributor-ship
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set 30 days instead of 7 days claim token, change back to 7 days after resend claim confirmation is implemented: OSF-6998.

@@ -556,17 +553,25 @@ def unconfirmed_email_add(auth=None):
}, 200


def send_confirm_email(user, email, external_id_provider=None, external_id=None):
def send_confirm_email(user, email, renew=False, external_id_provider=None, external_id=None):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when user asks to resend confirmation email, refresh the token with renew = True

"""

# If user is already logged in, log user out
# if users are logged in, log them out and redirect back to this page
if auth.logged_in:
Copy link
Contributor Author

@cslzchen cslzchen Sep 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"log user out and redirect back" is more secure and less confusing than just "go to dashboard"
same for forgot_password_get

# clear verification key (v2)
user_obj.verification_key_v2 = {}
# new verification key (v1) for CAS
user_obj.verification_key = generate_verification_key(verification_type=None)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cas login with username and verification key still uses v1 given it is instant verification

@brianjgeiger brianjgeiger merged commit 53419a5 into CenterForOpenScience:develop Sep 28, 2016
@cslzchen cslzchen deleted the feature/verification-key branch February 17, 2017 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants