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

feat(idp-migrations): one-time account validation keys #28732

Merged
merged 12 commits into from
Sep 28, 2021
Merged

Conversation

maxiuyuan
Copy link
Contributor

Create one-time keys to validate ownership of an email address, when a user with an existing profile but no password supplies SSO credentials with a matching address. This one-time key is then stored in Redis and an email will be sent containing the key to the user for them to verify their account.

RyanSkonnord and others added 3 commits September 15, 2021 16:00
When a user supplies unrecognized SSO credentials associated with an
email address for an existing user profile, offer them a chance to link
their identity to that profile instead of automatically provisioning a
new profile.

Introduce a feature flag. The feature is incomplete and will hit
unimplemented code if the flag is set.
@maxiuyuan maxiuyuan requested a review from a team as a code owner September 21, 2021 16:44
raise NotImplementedError # TODO
redis_key = "verificationKeyStorage"
TTL = 60 * 10
cluster = redis.clusters.get("default").get_local_client_for_key(redis_key)
Copy link
Member

Choose a reason for hiding this comment

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

Is this using rb or rc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sorry, could u clarify? im not too familiar with what those mean

{% block main %}
<h3>Confirm Email</h3>
<p>Please confirm your email ({{ confirm_email }}) by submitting the following code on the verification page: ({{ verification_key }}). This code will expire in 10 minutes.</p>
<!-- below is if we want to add a clickable button to verify -->
Copy link
Member

Choose a reason for hiding this comment

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

we'll definitely want a clickable button

Copy link
Member

Choose a reason for hiding this comment

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

(will be done in a follow up PR)

Copy link
Contributor

@RyanSkonnord RyanSkonnord left a comment

Choose a reason for hiding this comment

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

Small batch of implementation nits.

verification_value = {"user_id": user.id, "email": email, "member_id": member_id}

cluster.hmset(verification_key, verification_value)
cluster.expire(verification_key, TTL)
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional style advice: I'm always a fan of using objects over primitives where it adds meaning. In my opinion, it would improve the declaration to change it from TTL = 60 * 10 to TTL = timedelta(minutes=10). Then, you would just have to unpack it back to a primitive here, by

cluster.expire(verification_key, TTL.total_seconds())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Redis doesn't seem to like TTL.total_seconds() since it will return 600.0, so i used TTL.seconds instead

Copy link
Contributor

Choose a reason for hiding this comment

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

TTL.seconds works in this case but I think int(TTL.total_seconds()) is better style. The seconds attribute is just the seconds part of the timedelta being divided into days, seconds, and microseconds. (Observe that timedelta(days=1).seconds is 0.) Not much risk of a bug unless the TTL is adjusted to more than 24 hours, but better safe than sorry. 😉

redis_key = "verificationKeyStorage"
TTL = 60 * 10
cluster = redis.clusters.get("default").get_local_client_for_key(redis_key)
TTL = timedelta(minutes=10)
Copy link
Contributor

Choose a reason for hiding this comment

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

TTL can also go next to _REDIS_KEY (as _TTL).

Base automatically changed from idp-automatic-migration-base to master September 23, 2021 18:12
type="user.confirm_email",
context=context,
)
msg.send_async([email])


def create_verification_key(user: User, org: Organization, email: str) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

i'm wondering if we could rename this method to be more descriptive about sending an email to the user: create_verification_key -> send_one_time_account_confirm_link?


verification_code = get_random_string(32, string.ascii_letters + string.digits)
verification_key = f"auth:one-time-key:{verification_code}"
verification_value = {"user_id": user.id, "email": email, "member_id": member_id}
Copy link
Member

Choose a reason for hiding this comment

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

to be extra careful, could we also store in the verification value?:
identity["id"] (have to pass this into method)
and the organization_id?

Copy link
Contributor

Choose a reason for hiding this comment

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

Per discussion in meeting, 👍 for identity["id"] but org ID can be retrieved from member_id.

}
msg = MessageBuilder(
subject="{}Confirm Email".format(options.get("mail.subject-prefix")),
template="sentry/emails/idp_verification_email.txt",
Copy link
Member

Choose a reason for hiding this comment

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

should we standardize all of these names to "confirm_account"?

@@ -1,9 +1,40 @@
import uuid
import string
Copy link
Member

Choose a reason for hiding this comment

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

we could probably generalize the name of this file too, idpmigration.py -> confirm_account.py

Copy link
Member

@JoshFerge JoshFerge Sep 27, 2021

Choose a reason for hiding this comment

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

(can be done in a follow up PR if you'd like as to avoid merge hell)

Copy link
Member

@JoshFerge JoshFerge left a comment

Choose a reason for hiding this comment

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

a bunch of naming /comment nits, feel free to address in follow up PRs.

@maxiuyuan maxiuyuan merged commit 18f76cb into master Sep 28, 2021
@maxiuyuan maxiuyuan deleted the idp_verification branch September 28, 2021 17:55
@github-actions github-actions bot locked and limited conversation to collaborators Oct 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants