Skip to content

[LG-424] disable phone by deleting#2578

Merged
jgsmith-usds merged 17 commits intomasterfrom
jgs/lg-424-disable-phone-by-deletion
Oct 22, 2018
Merged

[LG-424] disable phone by deleting#2578
jgsmith-usds merged 17 commits intomasterfrom
jgs/lg-424-disable-phone-by-deletion

Conversation

@jgsmith-usds
Copy link
Contributor

Why:
We want users able to remove a phone from their account
if their threat model changes.

How:
We let users remove a configured phone if they have at
least one other MFA configured that isn't personal key.

Hi! Before submitting your PR for review, and/or before merging it, please
go through the checklists below. These represent the more critical elements
of our code quality guidelines. The rest of the list can be found in
CONTRIBUTING.md

Controllers

  • When adding a new controller that requires the user to be fully
    authenticated, make sure to add before_action :confirm_two_factor_authenticated
    as the first callback.

Database

  • Unsafe migrations are implemented over several PRs and over several
    deploys to avoid production errors. The strong_migrations gem
    will warn you about unsafe migrations and has great step-by-step instructions
    for various scenarios.

  • Indexes were added if necessary. This article provides a good overview
    of indexes in Rails.

  • Verified that the changes don't affect other apps (such as the dashboard)

  • When relevant, a rake task is created to populate the necessary DB columns
    in the various environments right before deploying, taking into account the users
    who might not have interacted with this column yet (such as users who have not
    set a password yet)

  • Migrations against existing tables have been tested against a copy of the
    production database. See LG-228 Make migrations safer and more resilient #2127 for an example when a migration caused deployment
    issues. In that case, all the migration did was add a new column and an index to
    the Users table, which might seem innocuous.

Encryption

  • The changes are compatible with data that was encrypted with the old code.

Routes

  • GET requests are not vulnerable to CSRF attacks (i.e. they don't change
    state or result in destructive behavior).

Session

  • When adding user data to the session, use the user_session helper
    instead of the session helper so the data does not persist beyond the user's
    session.

Testing

  • Tests added for this feature/bug
  • Prefer feature/integration specs over controller specs
  • When adding code that reads data, write tests for nil values, empty strings,
    and invalid inputs.

@jgsmith-usds
Copy link
Contributor Author

screen shot 2018-10-05 at 1 17 02 pm

The option only shows up if the phone isn't the only MFA configured.

**Why**:
We want users able to remove a phone from their account
if their threat model changes.

**How**:
We let users remove a configured phone if they have at
least one other MFA configured that isn't personal key.
@jgsmith-usds jgsmith-usds force-pushed the jgs/lg-424-disable-phone-by-deletion branch from 21ad7e6 to 6b2ec85 Compare October 10, 2018 13:39

def presenters_for_options_that_are_not_phone(presenters)
presenters.select { |presenter| !phone_presenter_class?(presenter.class) }.uniq(&:class)
presenters.reject { |presenter| phone_presenter_class?(presenter.class) }.uniq(&:class)
Copy link
Contributor

Choose a reason for hiding this comment

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

nice

Copy link
Contributor

Choose a reason for hiding this comment

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

I found an even better way to get the final array of presenters with less code and without sacrificing readability. I was gonna submit it as a separate PR, but you're free to include it in here if you like it:

def all_sms_and_voice_options_plus_one_of_each_remaining_option(configurations)
  presenters = configurations.flat_map(&:selection_presenters)
  phone_presenters, other_presenters = presenters.partition do |presenter| 
    phone_presenter_class?(presenter.class)
  end
  phone_presenters + other_presenters.uniq(&:class)
end

This would allow us to remove these methods:

def presenters_for_options_that_are_not_phone(presenters)
  presenters.select { |presenter| !phone_presenter_class?(presenter.class) }.uniq(&:class)
end

def presenters_for_phone_options(presenters)
  presenters.select { |presenter| phone_presenter_class?(presenter.class) }
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The change was an autofix from Rubocop (make lintfix). I'm going to do a follow-on PR to capture the translation fixes you mentioned elsewhere. I can revisit this in that PR as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, I'll make the fixes in this PR. I thought I had merged this one, but obviously I didn't.

jmhooper
jmhooper previously approved these changes Oct 10, 2018
Copy link
Contributor

@jmhooper jmhooper left a comment

Choose a reason for hiding this comment

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

Tested this out and it seems to work 👍

title: Entrez votre clé personnelle
phone:
buttons:
delete: Supprimer el teléfono
Copy link
Contributor

Choose a reason for hiding this comment

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

Supprimer le téléphone

Copy link
Contributor

Choose a reason for hiding this comment

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

Or maybe Supprimer le numéro de téléphone is better

phone:
delete:
success: Votre téléphone a été supprimé.
failure: Impossible de supprimer votre téléphone.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we want numéro de before each occurrence of téléphone.

end

def delete
analytics.track_event(Analytics::PHONE_DELETION_REQUESTED)
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you mean to put this line after assigning the result so that errors can be captured in analytics?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah. I forgot to go back and update this. Thanks for pointing it out.

end

def submit
success = configuration_absent? ||
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think about making these validations for consistency with other forms? Also, should we localize the error messages?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that, but I was hoping to get it in for this week's deploy. Obviously, I missed that, so I can go back and make validators for them. I'll make them so they're reusable since we might want to do a similar treatment for some other MFA methods.


def extra_analytics_attributes
{
user_id: user.uuid,
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't necessary. It's tracked automatically for authenticated users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh. Okay. I had seen this elsewhere, so cargo-culted it.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's only needed when the user is not yet authenticated, but we can find out who they are based on a token, for example, when confirming email, resetting password, etc.

return true if configuration.user_id == user.id
errors.add(:configuration, :owner, message: "cannot delete someone else's phone")
false
end
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the attack vector here? The user and configuration that are passed in to the form are based on the current user. In what scenario would the configuration not be owned by the user?

@monfresh
Copy link
Contributor

I wanted to document this before I forget, and because I probably won't have time to verify this today. When I was testing this last week, I think I came across this bug:

  1. If you already have a phone in your account, delete it
  2. Add a new phone
  3. When prompted for the OTP, click on the link that says something about "enter a different number"

Expected Result: You see the same phone setup page as the one when you clicked "Add a new phone"
Actual Result: You see the phone setup page that appears when you are creating your account for the first time

@jgsmith-usds
Copy link
Contributor Author

Good call. That was something at the back of my mind, but hadn't really done much to chase down situations.

@jgsmith-usds
Copy link
Contributor Author

Looks like that behavior is the current behavior if someone created an account without a phone number and later added it. Deleting the phone number makes the account the same as if it had been created without a phone at all. I'm digging in to see if I can make the experience better.

@jgsmith-usds
Copy link
Contributor Author

I think I have the behavior tweaked to be less confusing.

jmhooper
jmhooper previously approved these changes Oct 22, 2018
@jgsmith-usds jgsmith-usds force-pushed the jgs/lg-424-disable-phone-by-deletion branch from 2fe26da to 614fb54 Compare October 22, 2018 18:35
@jgsmith-usds jgsmith-usds merged commit f36c580 into master Oct 22, 2018
@jgsmith-usds jgsmith-usds deleted the jgs/lg-424-disable-phone-by-deletion branch October 22, 2018 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants