From 78940f0aa64bb25969020b18d769162310dbcd3b Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Thu, 10 Sep 2020 11:40:47 -0700 Subject: [PATCH 01/75] Notify via email when resetting password from RISC (LG-3120) (#4177) * Combine reset password classes * Remove rake task and script * Update email copy --- app/forms/security_event_form.rb | 2 - app/mailers/user_mailer.rb | 3 +- app/services/marketing_site.rb | 8 ++++ .../reset_password_and_notify_user.rb | 28 ------------ app/services/reset_user_password.rb | 22 +++++++--- .../reset_user_password_and_send_email.rb | 42 ------------------ .../please_reset_password.html.erb | 23 ++++++++++ .../please_reset_password.html.slim | 33 -------------- bin/reset_user_password | 43 ------------------- config/locales/user_mailer/en.yml | 24 ++++++----- config/locales/user_mailer/es.yml | 25 +++++------ config/locales/user_mailer/fr.yml | 27 ++++++------ lib/tasks/reset_user_passwords.rake | 20 --------- spec/mailers/user_mailer_spec.rb | 5 +-- .../reset_password_and_notify_user_spec.rb | 38 ---------------- ...reset_user_password_and_send_email_spec.rb | 43 ------------------- spec/services/reset_user_password_spec.rb | 27 ++++++++++++ 17 files changed, 117 insertions(+), 296 deletions(-) delete mode 100644 app/services/reset_password_and_notify_user.rb delete mode 100644 app/services/reset_user_password_and_send_email.rb create mode 100644 app/views/user_mailer/please_reset_password.html.erb delete mode 100644 app/views/user_mailer/please_reset_password.html.slim delete mode 100755 bin/reset_user_password delete mode 100644 lib/tasks/reset_user_passwords.rake delete mode 100644 spec/services/reset_password_and_notify_user_spec.rb delete mode 100644 spec/services/reset_user_password_and_send_email_spec.rb create mode 100644 spec/services/reset_user_password_spec.rb diff --git a/app/forms/security_event_form.rb b/app/forms/security_event_form.rb index 90627e0315c..cdda0dad373 100644 --- a/app/forms/security_event_form.rb +++ b/app/forms/security_event_form.rb @@ -45,8 +45,6 @@ def submit if event_type == SecurityEvent::AUTHORIZATION_FRAUD_DETECTED ResetUserPassword.new(user: user).call - UserEventCreator.new(current_user: user). - create_out_of_band_user_event(:password_invalidated) end end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 8c500a368f9..a6ca6c39ef2 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -97,8 +97,7 @@ def account_reset_cancel(email_address) mail(to: email_address.email, subject: t('user_mailer.account_reset_cancel.subject')) end - def please_reset_password(email_address, message) - @message = message + def please_reset_password(email_address) mail(to: email_address, subject: t('user_mailer.please_reset_password.subject')) end diff --git a/app/services/marketing_site.rb b/app/services/marketing_site.rb index e5731a60b8d..03f9fd4e517 100644 --- a/app/services/marketing_site.rb +++ b/app/services/marketing_site.rb @@ -34,6 +34,14 @@ def self.help_authentication_app_url URI.join(BASE_URL, locale_segment, 'help/creating-an-account/authentication-application/').to_s end + def self.help_which_authentication_method_url + URI.join( + BASE_URL, + locale_segment, + 'help/authentication-methods/which-authentication-method-should-i-use/', + ).to_s + end + def self.help_hardware_security_key_url URI.join(BASE_URL, locale_segment, 'help/signing-in/what-is-a-hardware-security-key/').to_s end diff --git a/app/services/reset_password_and_notify_user.rb b/app/services/reset_password_and_notify_user.rb deleted file mode 100644 index a81eff22ad7..00000000000 --- a/app/services/reset_password_and_notify_user.rb +++ /dev/null @@ -1,28 +0,0 @@ -class ResetPasswordAndNotifyUser - attr_reader :email_address, :message - - def initialize(email_address, message) - @email_address = email_address - @message = message - end - - def call - return warn("User '#{email_address}' does not exist") if user.blank? - reset_user_password - notify_user - end - - private - - def user - @user ||= User.find_with_email(email_address) - end - - def reset_user_password - user.update!(encrypted_password_digest: '') - end - - def notify_user - UserMailer.please_reset_password(email_address, message).deliver_now - end -end diff --git a/app/services/reset_user_password.rb b/app/services/reset_user_password.rb index 71f27ab00a8..8fdca01d7b4 100644 --- a/app/services/reset_user_password.rb +++ b/app/services/reset_user_password.rb @@ -1,20 +1,30 @@ class ResetUserPassword - def initialize(user:, log_stdout: false) + def initialize(user:) @user = user - @log_stdout = log_stdout end def call - reset_user_password_and_log_event + reset_user_password + log_event + notify_user end private attr_reader :user - def reset_user_password_and_log_event + def reset_user_password user.update!(password: SecureRandom.hex(8)) - return unless @log_stdout - Kernel.puts "Password for user with email #{user.email_addresses.take.email} has been reset" + end + + def log_event + UserEventCreator.new(current_user: user). + create_out_of_band_user_event(:password_invalidated) + end + + def notify_user + user.email_addresses.each do |email_address| + UserMailer.please_reset_password(email_address.email).deliver_now + end end end diff --git a/app/services/reset_user_password_and_send_email.rb b/app/services/reset_user_password_and_send_email.rb deleted file mode 100644 index a33ca7588eb..00000000000 --- a/app/services/reset_user_password_and_send_email.rb +++ /dev/null @@ -1,42 +0,0 @@ -# Arguments: -# * user_emails: String, comma-separated list of emails -# -# Usage: -# ResetUserPasswordAndSendEmail.new(user_emails: 'test1t@test.com,test2@test.com').call - -class ResetUserPasswordAndSendEmail - def initialize(user_emails:) - @user_emails = user_emails - end - - def call - reset_password_and_send_email_to_each_affected_user - end - - private - - attr_reader :user_emails - - def reset_password_and_send_email_to_each_affected_user - affected_emails.each do |email| - user = User.find_with_email(email) - if user - ResetUserPassword.new(user: user, log_stdout: true).call - notify_user_to_reset_password(user) - else - Kernel.puts "user with email #{email} not found" - end - end - end - - def affected_emails - user_emails.split(',') - end - - def notify_user_to_reset_password(user) - user.confirmed_email_addresses.each do |email_address| - UserMailer.please_reset_password(email_address).deliver_now - Kernel.puts "Email sent to user with email #{email_address.email}" - end - end -end diff --git a/app/views/user_mailer/please_reset_password.html.erb b/app/views/user_mailer/please_reset_password.html.erb new file mode 100644 index 00000000000..24c274e886e --- /dev/null +++ b/app/views/user_mailer/please_reset_password.html.erb @@ -0,0 +1,23 @@ +

+ <%= t('user_mailer.please_reset_password.intro') %> +

+ +

+ <%= t('user_mailer.please_reset_password.call_to_action') %> +

+ +
    +
  1. <%= t('user_mailer.please_reset_password.step_1') %>
  2. +
  3. <%= t('user_mailer.please_reset_password.step_2') %>
  4. +
  5. <%= t('user_mailer.please_reset_password.step_3') %>
  6. +
+ +

+ <%= t('user_mailer.please_reset_password.reminder_html') %> +

+ +

+ <%= link_to( + t('user_mailer.please_reset_password.learn_more_link_text'), + MarketingSite.help_which_authentication_method_url) %> +

diff --git a/app/views/user_mailer/please_reset_password.html.slim b/app/views/user_mailer/please_reset_password.html.slim deleted file mode 100644 index 5201aa373de..00000000000 --- a/app/views/user_mailer/please_reset_password.html.slim +++ /dev/null @@ -1,33 +0,0 @@ -p.lead - = t('user_mailer.please_reset_password.intro') - =< @message.presence - -p.lead - = t('user_mailer.please_reset_password.call_to_action') - -table.button.expanded.large.radius - tbody - tr - td - table - tbody - tr - td - center - = link_to t('user_mailer.please_reset_password.link_text'), - forgot_password_url, - target: '_blank', class: 'float-center', align: 'center' - td.expander - -p - = link_to forgot_password_url, forgot_password_url, target: '_blank' - -p.lead - = t('user_mailer.please_reset_password.additional_help_html', - contact_url: link_to(MarketingSite.contact_url, MarketingSite.contact_url)) - -table.spacer - tbody - tr - td.s10 height="10px" - |   diff --git a/bin/reset_user_password b/bin/reset_user_password deleted file mode 100755 index 36db10fe663..00000000000 --- a/bin/reset_user_password +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env ruby - -require 'optparse' - -options = {} -parser = OptionParser.new do |opts| - opts.banner = <<~TXT - Usage: bin/rails r bin/reset_user_password [OPTIONS] - - Takes a comma-separated list of email addresses, and for each user with - a matching email, we reset their password to a secure random value, and - send them an email prompting them to reset their password. - - For example: - - bin/rails r bin/reset_user_password --emails=test1@test.com,foo@bar.com - - Options: - TXT - - opts.on('-h', '--help', 'Display this message') do - STDERR.puts opts - exit - end - - opts.on('-e', '--emails REQUIRED', 'Comma-separated list of emails') do |emails| - options[:emails] = emails - end -end - -args = parser.order!(ARGV) {} - -parser.parse!(args) - -supplied_emails = options[:emails] - -if supplied_emails.nil? - STDERR.puts parser - STDERR.puts 'A comma-separated email list is required' - exit 1 -end - -ResetUserPasswordAndSendEmail.new(user_emails: supplied_emails).call diff --git a/config/locales/user_mailer/en.yml b/config/locales/user_mailer/en.yml index b4a2809eb4b..62b6472a7c2 100644 --- a/config/locales/user_mailer/en.yml +++ b/config/locales/user_mailer/en.yml @@ -150,17 +150,19 @@ en: intro: A new phone number was added to your %{app} profile. subject: New phone number added please_reset_password: - additional_help_html: If you’ve used your login.gov password on other websites, - we encourage you to update these passwords as well. If you have any questions, - please leave a message for our team at %{contact_url} — we will respond within - two business days. - call_to_action: To regain access to your login.gov account, you will need to - reset your password using the link below. - intro: We recently identified a security issue with your password. We have disabled - your password to keep your account safe. This was not a result of a security - problem with login.gov, but was an issue with your password being compromised. - link_text: Reset your password - subject: Please reset your password + call_to_action: 'As a precaution, we’ve disabled your password to keep your + information safe. Please follow the steps below to reset your password and + secure your account:' + intro: We’ve detected unusual activity on your login.gov account. We are concerned + someone other than you may be trying to access your information. + learn_more_link_text: Learn more about your options + reminder_html: As a reminder, login.gov will never ask for your login credentials + by phone or email. You can take additional steps to secure your account + by enabling two-factor authentication. + step_1: Visit the login.gov website and select sign in + step_2: Select “forgot your password?” at the bottom of the page + step_3: Follow the instructions to reset your password + subject: Unusual activity — reset your login.gov password reset_password_instructions: footer: This link expires in %{expires} hours. header: To finish resetting your password, please click the link below or copy diff --git a/config/locales/user_mailer/es.yml b/config/locales/user_mailer/es.yml index 184057a4cf3..5aaeb659bb2 100644 --- a/config/locales/user_mailer/es.yml +++ b/config/locales/user_mailer/es.yml @@ -156,18 +156,19 @@ es: intro: Se agregó un nuevo número de teléfono a su perfil de %{app}. subject: Nuevo número de teléfono añadido please_reset_password: - additional_help_html: Si ha utilizado su contraseña de login.gov en otros sitios, - le pedimos que actualice estas contraseñas también. Si tiene preguntas, por - favor deje un mensaje para nuestro equipo en %{contact_url}, y le responderemos - en dos días hábiles. - call_to_action: Para retomar acceso a su cuenta en login.gov, tendrá que restablecer - su contraseña utilizando el enlace a continuación. - intro: Recientemente identificamos un problema de seguridad con su contraseña. - Hemos deshabilitado su contraseña para mantener su cuenta segura. Esto no - fue el resultado de un problema de seguridad con login.gov, sino un problema - con su contraseña que ha sido comprometida. - link_text: Restablezca su contraseña - subject: Restablece tu contraseña + call_to_action: 'Como medida de precaución, inhabilitamos su contraseña para + mantener su información segura. Siga los pasos a continuación para restablecer + su contraseña y asegure su cuenta:' + intro: Hemos detectado actividad inusual en su cuenta login.gov. Estamos preocupados + alguien que no sea usted puede estar intentando acceder a su información. + learn_more_link_text: Conozca más sobre sus opciones + reminder_html: Le recordamos que login.gov nunca le pedirá sus credenciales + de inicio de sesión. por teléfono o correo electrónico. Puede tomar medidas + adicionales para proteger su cuenta habilitando la autenticación de dos factores. + step_1: Visite el sitio web login.gov y seleccione iniciar sesión + step_2: Seleccione "¿olvidó su contraseña?" al final de la página + step_3: Siga las instrucciones para restablecer su contraseña + subject: Actividad inusual — restablezca su contraseña de login.gov reset_password_instructions: footer: Este enlace expira en %{expires} horas. header: Para terminar de restablecer su contraseña, haga clic en el enlace de diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml index 71c4e33c2b8..5a3fbb957af 100644 --- a/config/locales/user_mailer/fr.yml +++ b/config/locales/user_mailer/fr.yml @@ -166,18 +166,21 @@ fr: intro: Un nouveau numéro de téléphone a été ajouté à votre profil %{app}. subject: Nouveau numéro de téléphone ajouté please_reset_password: - additional_help_html: Si vous avez utilisé votre mot de passe login.gov sur - d'autres sites internet, nous vous encourageons à mettre ces mots de passe - à jour également. Si vous avez des questions, veuillez laisser un message - à notre équipe sur %{contact_url} — nous répondrons sous deux jours ouvrables. - call_to_action: Pour accéder de nouveau à votre compte login.gov, vous allez - devoir réinitialiser votre mot de passe en utilisant le lien ci-dessous. - intro: Nous avons récemment identifié un problème de sécurité concernant votre - mot de passe. Nous avons désactivé ce dernier pour sécuriser votre compte. - Cela n'a pas été causé par un problème de sécurité sur login.gov mais par - la corruption de votre mot de passe. - link_text: Réinitialisez votre mot de passe - subject: Veuillez réinitialiser votre mot de passe + call_to_action: 'Par précaution, nous avons désactivé votre mot de passe pour + conserver votre informations sûres. Veuillez suivre les étapes ci-dessous + pour réinitialiser votre mot de passe et Sécurise ton compte:' + intro: Nous avons détecté une activité inhabituelle sur votre compte login.gov. + Nous sommes concernés quelqu'un d'autre que vous tente peut-être d'accéder + à vos informations. + learn_more_link_text: En savoir plus sur vos options + reminder_html: Pour rappel, login.gov ne vous demandera jamais vos identifiants + de connexion par téléphone ou par e-mail. Vous pouvez prendre des mesures + supplémentaires pour sécuriser votre compte en activant l'authentification + à deux facteurs. + step_1: Visitez le site Web login.gov et sélectionnez Connexion + step_2: Sélectionnez "Vous avez oublié votre mot de passe?" au bas de la page + step_3: Suivez les instructions pour réinitialiser votre mot de passe + subject: Activité inhabituelle — réinitialisez votre mot de passe login.gov reset_password_instructions: footer: Ce lien expire dans %{expires} heures. header: Pour terminer la réinitialisation de votre mot de passe, veuillez cliquer diff --git a/lib/tasks/reset_user_passwords.rake b/lib/tasks/reset_user_passwords.rake deleted file mode 100644 index 0522c2c61f9..00000000000 --- a/lib/tasks/reset_user_passwords.rake +++ /dev/null @@ -1,20 +0,0 @@ -namespace :adhoc do - USAGE_WARNING = <<~EMAIL.freeze - WARNING: Running this task without EMAILS argument is a noop - Usage: rake adhoc:reset_passwords_and_notify_users EMAILS=user1@asdf.com,user2@asdf.com - - To include an additional message: - rake adhoc:reset_passwords_and_notify_users EMAILS=user1@asdf.com,user2@asdf.com MESSAGE='Your password may have been compromised by a keylogger' - EMAIL - - desc 'Reset the passwords for a comma separated list of users' - task reset_passwords_and_notify_users: :environment do - emails_input = ENV['EMAILS'] - next warn(USAGE_WARNING) if emails_input.blank? - - emails = emails_input.split(',') - emails.each do |email| - ResetPasswordAndNotifyUser.new(email, ENV['MESSAGE']).call - end - end -end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 7268a77b68c..0e6b245bf50 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -336,7 +336,7 @@ def expect_email_body_to_have_help_and_contact_links end describe 'please_reset_password' do - let(:mail) { UserMailer.please_reset_password(email_address.email, 'This is a test.') } + let(:mail) { UserMailer.please_reset_password(email_address.email) } it_behaves_like 'a system email' @@ -354,9 +354,6 @@ def expect_email_body_to_have_help_and_contact_links expect(mail.html_part.body). to have_content(strip_tags(t('user_mailer.please_reset_password.call_to_action'))) - - expect(mail.html_part.body). - to have_content('This is a test.') end end diff --git a/spec/services/reset_password_and_notify_user_spec.rb b/spec/services/reset_password_and_notify_user_spec.rb deleted file mode 100644 index cf6adf103ff..00000000000 --- a/spec/services/reset_password_and_notify_user_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'rails_helper' - -describe ResetPasswordAndNotifyUser do - let(:email_address) { 'changemypassword@example.com' } - let(:message) { 'Hello, user.' } - - subject { described_class.new(email_address, message) } - - before do - allow(subject).to receive(:warn) - end - - describe '#call' do - context 'when the user does exist' do - it 'resets the password and notifies the user' do - password = 'compromised password' - user = create(:user, email: email_address, password: password) - - subject.call - - user.reload - mail = ActionMailer::Base.deliveries.last - - expect(mail.to).to eq([email_address]) - expect(mail.html).to include(message) - expect(user.valid_password?(password)).to eq(false) - end - end - - context 'when the user does not exist' do - it 'prints a warning' do - expect(subject).to receive(:warn).with("User '#{email_address}' does not exist") - - subject.call - end - end - end -end diff --git a/spec/services/reset_user_password_and_send_email_spec.rb b/spec/services/reset_user_password_and_send_email_spec.rb deleted file mode 100644 index 74e5c4582ae..00000000000 --- a/spec/services/reset_user_password_and_send_email_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'rails_helper' - -describe ResetUserPasswordAndSendEmail do - context 'when the user exists in the DB' do - it "resets the user's password and sends an email" do - allow(Kernel).to receive(:puts) - - email = 'test@test.com' - user = create(:user, email: email) - old_password = user.encrypted_password_digest - subject = ResetUserPasswordAndSendEmail.new(user_emails: 'test@test.com') - - mailer = instance_double(ActionMailer::MessageDelivery, deliver_now: true) - allow(UserMailer).to receive(:please_reset_password). - with(user.email_addresses.first).and_return(mailer) - - expect(mailer).to receive(:deliver_now) - - subject.call - user.reload - - expect(user.encrypted_password_digest).to_not eq old_password - end - end - - context 'when the user does not exist in the DB' do - it "does not attempt to reset the user's password nor send an email" do - affected_email = 'test@test.com' - not_affected_email = 'not_affected@test.com' - user = create(:user, email: not_affected_email) - old_password = user.encrypted_password_digest - subject = ResetUserPasswordAndSendEmail.new(user_emails: affected_email) - - expect(UserMailer).to_not receive(:please_reset_password) - expect(Kernel).to receive(:puts).with("user with email #{affected_email} not found") - - subject.call - user.reload - - expect(user.encrypted_password_digest).to eq old_password - end - end -end diff --git a/spec/services/reset_user_password_spec.rb b/spec/services/reset_user_password_spec.rb new file mode 100644 index 00000000000..b1dc4d25f57 --- /dev/null +++ b/spec/services/reset_user_password_spec.rb @@ -0,0 +1,27 @@ +require 'rails_helper' + +RSpec.describe ResetUserPassword do + subject(:reset_user_password) { ResetUserPassword.new(user: user) } + let(:user) { create(:user, :with_multiple_emails) } + + describe '#call' do + subject(:call) { reset_user_password.call } + + it 'changes the password' do + expect { call }.to(change { user.reload.encrypted_password_digest }) + end + + it 'creates a password_invalidated user event' do + expect { call }. + to(change { user.events.password_invalidated.size }.from(0).to(1)) + end + + it 'notifies the user via email to each of their email addresses' do + expect { call }. + to(change { ActionMailer::Base.deliveries.count }.by(2)) + + mails = ActionMailer::Base.deliveries.last(2) + expect(mails.map(&:to).flatten).to match_array(user.email_addresses.map(&:email)) + end + end +end From 4b90534e3bb24ae809122a51ed1de6957eaa1e6b Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Thu, 10 Sep 2020 12:08:04 -0700 Subject: [PATCH 02/75] Remove docker branch builds, leave "build-release-container" (#4181) --- .circleci/config.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 87499e403ed..404967a9148 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -184,32 +184,6 @@ jobs: bundle exec rubocop bundle exec slim-lint app/views make check_asset_strings - build-latest-container: - working_directory: ~/identity-idp - docker: - - image: circleci/ruby:2.6 - steps: - - checkout - - setup_remote_docker - - run: | - rev=$(git rev-parse --short HEAD) - docker build -t logindotgov/idp:latest -t logindotgov/idp:"${rev}" -f production.Dockerfile . - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - docker push logindotgov/idp:"${rev}" - docker push logindotgov/idp:latest - build-latest-dev-container: - working_directory: ~/identity-idp - docker: - - image: circleci/ruby:2.6 - steps: - - checkout - - setup_remote_docker - - run: | - rev=$(git rev-parse --short HEAD) - docker build -t logindotgov/dev:latest -t logindotgov/dev:"${rev}" -f development.Dockerfile . - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - docker push logindotgov/dev:"${rev}" - docker push logindotgov/dev:latest build-release-container: working_directory: ~/identity-idp docker: @@ -285,12 +259,6 @@ workflows: jobs: - build - lints - - build-latest-dev-container: - requires: - - build - - build-latest-container: - requires: - - build - build-release-container: requires: - build From 14b41cb7c605de851ecdca63af1f47898b5ee2dd Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 9 Sep 2020 10:38:24 -0500 Subject: [PATCH 03/75] convert idv cac success template to erb --- app/views/idv/cac/success.html.erb | 22 ++++++++++++++++++++++ app/views/idv/cac/success.html.slim | 16 ---------------- 2 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 app/views/idv/cac/success.html.erb delete mode 100644 app/views/idv/cac/success.html.slim diff --git a/app/views/idv/cac/success.html.erb b/app/views/idv/cac/success.html.erb new file mode 100644 index 00000000000..1c9eea1b97c --- /dev/null +++ b/app/views/idv/cac/success.html.erb @@ -0,0 +1,22 @@ +<% title t('cac_proofing.titles.cac_proofing') %> +
+ <%= t('cac_proofing.step', step: 2) %> +
+ +<%= image_tag(asset_url('state-id-confirm@3x.png'), width: 210) %> + +

+ <%= t('cac_proofing.headings.success') %> +

+ +
+ +
+ <%= button_to(t('forms.buttons.continue'), url_for, method: :put, + class: 'btn btn-primary btn-wide sm-col-6 col-12') %> +
+ +<%= form_for('', url: url_for, method: 'PUT', + html: { autocomplete: 'off', role: 'form', class: 'mt2' }) do |f| %> +<% end %> +<%= render 'idv/cac/start_over_or_cancel' %> diff --git a/app/views/idv/cac/success.html.slim b/app/views/idv/cac/success.html.slim deleted file mode 100644 index 8eb83846969..00000000000 --- a/app/views/idv/cac/success.html.slim +++ /dev/null @@ -1,16 +0,0 @@ -- title t('cac_proofing.titles.cac_proofing') - -h5.my1.caps.bold.accent-blue = t('cac_proofing.step', step: 2) - -= image_tag(asset_url('state-id-confirm@3x.png'), width: 210) - -h1.h3.mb2.mt3.my0 = t('cac_proofing.headings.success') - -br -.mt4 = button_to(t('forms.buttons.continue'), url_for, method: :put, - class: 'btn btn-primary btn-wide sm-col-6 col-12') - -= form_for('', url: url_for, method: 'PUT', - html: { autocomplete: 'off', role: 'form', class: 'mt2' }) do |f| - -= render 'idv/cac/start_over_or_cancel' From 997556884a187196ee23b898819e42fd13c47323 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 9 Sep 2020 10:51:30 -0500 Subject: [PATCH 04/75] convert backup code setup confirm delete template to erb --- .../backup_code_setup/confirm_delete.html.erb | 23 +++++++++++++++++++ .../confirm_delete.html.slim | 16 ------------- 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 app/views/users/backup_code_setup/confirm_delete.html.erb delete mode 100644 app/views/users/backup_code_setup/confirm_delete.html.slim diff --git a/app/views/users/backup_code_setup/confirm_delete.html.erb b/app/views/users/backup_code_setup/confirm_delete.html.erb new file mode 100644 index 00000000000..3c1ece2bd98 --- /dev/null +++ b/app/views/users/backup_code_setup/confirm_delete.html.erb @@ -0,0 +1,23 @@ +<% title t('forms.backup_code.confirm_delete') %> + +<%= image_tag(asset_url('alert/warning-lg.svg'), alt: '', width: 54) %> + +

+ <%= t('forms.backup_code.confirm_delete') %> +

+ +
+
+
+ +

+ <%= t('forms.backup_code.caution_delete') %> +

+ +<%= form_tag(backup_code_delete_path, method: :delete, role: 'form') do %> + <%= button_tag t('account.index.backup_code_confirm_delete'), type: 'submit', + class: 'btn btn-primary col-6 mb2 p2 rounded-lg center' %> +<% end %> + +<%= link_to t('links.cancel'), account_path, + class: 'btn p2 rounded-lg border border-blue blue center' %> diff --git a/app/views/users/backup_code_setup/confirm_delete.html.slim b/app/views/users/backup_code_setup/confirm_delete.html.slim deleted file mode 100644 index c27f540db06..00000000000 --- a/app/views/users/backup_code_setup/confirm_delete.html.slim +++ /dev/null @@ -1,16 +0,0 @@ -- title t('forms.backup_code.confirm_delete') - -= image_tag(asset_url('alert/warning-lg.svg'), alt: '', width: 54) - -h1.h3.mb1.mt3.my0 = t('forms.backup_code.confirm_delete') - -.col-2.mb2 - hr class="mt3 mb2 bw4 rounded border-yellow" - -p.mb4 == t('forms.backup_code.caution_delete') - -= form_tag(backup_code_delete_path, method: :delete, role: 'form') do - = button_tag t('account.index.backup_code_confirm_delete'), type: 'submit', - class: 'btn btn-primary col-6 mb2 p2 rounded-lg center' -= link_to t('links.cancel'), account_path, - class: 'btn p2 rounded-lg border border-blue blue center' From 263bab2d40811446dc806358ebdfd4764da788c6 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 9 Sep 2020 11:01:24 -0500 Subject: [PATCH 05/75] convert backup code setup confirm edit template to erb --- .../users/backup_code_setup/edit.html.erb | 23 +++++++++++++++++++ .../users/backup_code_setup/edit.html.slim | 16 ------------- 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 app/views/users/backup_code_setup/edit.html.erb delete mode 100644 app/views/users/backup_code_setup/edit.html.slim diff --git a/app/views/users/backup_code_setup/edit.html.erb b/app/views/users/backup_code_setup/edit.html.erb new file mode 100644 index 00000000000..f1fe61b8f39 --- /dev/null +++ b/app/views/users/backup_code_setup/edit.html.erb @@ -0,0 +1,23 @@ +<% title t('forms.backup_code_regenerate.confirm') %> + +<%= image_tag(asset_url('alert/warning-lg.svg'), alt: '', width: 54) %> + +

+ <%= t('forms.backup_code_regenerate.confirm') %> +

+ +
+
+
+ +

+ <%= t('forms.backup_code_regenerate.caution') %> +

+ +<%= form_tag(backup_code_setup_path, method: :patch, role: 'form') do %> + <%= button_tag t('account.index.backup_code_confirm_regenerate'), type: 'submit', + class: 'btn btn-primary col-6 mb2 p2 rounded-lg center' %> +<% end %> + +<%= link_to t('links.cancel'), account_path, + class: 'btn p2 rounded-lg border border-blue blue center' %> diff --git a/app/views/users/backup_code_setup/edit.html.slim b/app/views/users/backup_code_setup/edit.html.slim deleted file mode 100644 index b9357c59f9c..00000000000 --- a/app/views/users/backup_code_setup/edit.html.slim +++ /dev/null @@ -1,16 +0,0 @@ -- title t('forms.backup_code_regenerate.confirm') - -= image_tag(asset_url('alert/warning-lg.svg'), alt: '', width: 54) - -h1.h3.mb1.mt3.my0 = t('forms.backup_code_regenerate.confirm') - -.col-2.mb2 - hr class="mt3 mb2 bw4 rounded border-yellow" - -p.mb4 == t('forms.backup_code_regenerate.caution') - -= form_tag(backup_code_setup_path, method: :patch, role: 'form') do - = button_tag t('account.index.backup_code_confirm_regenerate'), type: 'submit', - class: 'btn btn-primary col-6 mb2 p2 rounded-lg center' -= link_to t('links.cancel'), account_path, - class: 'btn p2 rounded-lg border border-blue blue center' From b0545ea732308447e704a3112d39f9aaf582f3a5 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 9 Sep 2020 11:05:00 -0500 Subject: [PATCH 06/75] convert piv cac auth setup error template to erb --- .../error.html.erb | 25 +++++++++++++++++++ .../error.html.slim | 16 ------------ 2 files changed, 25 insertions(+), 16 deletions(-) create mode 100644 app/views/users/piv_cac_authentication_setup/error.html.erb delete mode 100644 app/views/users/piv_cac_authentication_setup/error.html.slim diff --git a/app/views/users/piv_cac_authentication_setup/error.html.erb b/app/views/users/piv_cac_authentication_setup/error.html.erb new file mode 100644 index 00000000000..5f0f403288e --- /dev/null +++ b/app/views/users/piv_cac_authentication_setup/error.html.erb @@ -0,0 +1,25 @@ + + +

+ <%= @presenter.heading %> +

+ +

+ <%= @presenter.description %> +

+ +
+

+ <%= link_to t('forms.piv_cac_setup.choose_different_certificate'), + setup_piv_cac_url %> +

+

+ <% if MfaPolicy.new(current_user).two_factor_enabled? %> + <%= link_to t('links.cancel'), account_path %> + <% else %> + <%= link_to t('two_factor_authentication.choose_another_option'), two_factor_options_path %> + <% end %> +

+
diff --git a/app/views/users/piv_cac_authentication_setup/error.html.slim b/app/views/users/piv_cac_authentication_setup/error.html.slim deleted file mode 100644 index e068e6ad427..00000000000 --- a/app/views/users/piv_cac_authentication_setup/error.html.slim +++ /dev/null @@ -1,16 +0,0 @@ -div class='alert alert-error' role='alert' - = @presenter.title - -h1.h3.my0 = @presenter.heading - -p.mt-tiny.mb3 = @presenter.description - -.mt2.pt1.border-top - p - = link_to t('forms.piv_cac_setup.choose_different_certificate'), - setup_piv_cac_url - p - - if MfaPolicy.new(current_user).two_factor_enabled? - = link_to t('links.cancel'), account_path - - else - = link_to t('two_factor_authentication.choose_another_option'), two_factor_options_path From 466b3916487e5a118c97929759659922fbc00126 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Thu, 10 Sep 2020 15:24:00 -0700 Subject: [PATCH 07/75] Add more randomness to Agency factories (#4186) **Why**: We have a unique index on agency name --- spec/factories/agencies.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/factories/agencies.rb b/spec/factories/agencies.rb index c61fd261308..9253cb480f0 100644 --- a/spec/factories/agencies.rb +++ b/spec/factories/agencies.rb @@ -1,13 +1,17 @@ FactoryBot.define do factory :agency do agency_name_templates = [ - 'Department of %{industry}', - 'Bureau of %{industry}', - '%{industry} Administration', - '%{industry} Agency', + 'Department of %{industry} %{tag}', + 'Bureau of %{industry} %{tag}', + '%{industry} Administration %{tag}', + '%{industry} Agency %{tag}', ] id { Agency.last&.id.to_i + 1 } # autoincrementer is messed up for this table - name { format(agency_name_templates.sample, industry: Faker::Company.industry) } + name do + format(agency_name_templates.sample, + industry: Faker::Company.industry, + tag: SecureRandom.hex) + end end end From 752316f75920e5c6c61f14c9cf11e02ebe9b6c11 Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Fri, 11 Sep 2020 16:50:43 +0300 Subject: [PATCH 08/75] fix: Gemfile & Gemfile.lock to reduce vulnerabilities (#4187) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-RUBY-ACTIONVIEW-632514 --- Gemfile | 4 +- Gemfile.lock | 124 +++++++++++++++++++++++++-------------------------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/Gemfile b/Gemfile index ee057cb0dfc..68cb4c6ee7a 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" } ruby '~> 2.6.5' -gem 'rails', '~> 5.2.4', '>= 5.2.4.3' +gem 'rails', '~> 5.2.4', '>= 5.2.4.4' gem 'ahoy_matey', '~> 2.2', '>= 2.2.1' gem 'american_date' @@ -11,7 +11,7 @@ gem 'aws-sdk-kms', '~> 1.4' gem 'aws-sdk-ses', '~> 1.6' gem 'base32-crockford' gem 'device_detector' -gem 'devise', '~> 4.7.1' +gem 'devise', '~> 4.7.2' gem 'dotiw', '>= 4.0.1' gem 'exception_notification', '>= 4.4.0' gem 'faraday' diff --git a/Gemfile.lock b/Gemfile.lock index 265737b1109..33be1bfa23e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,43 +68,43 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (5.2.4.3) - actionpack (= 5.2.4.3) + actioncable (5.2.4.4) + actionpack (= 5.2.4.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.4.3) - actionpack (= 5.2.4.3) - actionview (= 5.2.4.3) - activejob (= 5.2.4.3) + actionmailer (5.2.4.4) + actionpack (= 5.2.4.4) + actionview (= 5.2.4.4) + activejob (= 5.2.4.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.4.3) - actionview (= 5.2.4.3) - activesupport (= 5.2.4.3) + actionpack (5.2.4.4) + actionview (= 5.2.4.4) + activesupport (= 5.2.4.4) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.4.3) - activesupport (= 5.2.4.3) + actionview (5.2.4.4) + activesupport (= 5.2.4.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.2.4.3) - activesupport (= 5.2.4.3) + activejob (5.2.4.4) + activesupport (= 5.2.4.4) globalid (>= 0.3.6) - activemodel (5.2.4.3) - activesupport (= 5.2.4.3) - activerecord (5.2.4.3) - activemodel (= 5.2.4.3) - activesupport (= 5.2.4.3) + activemodel (5.2.4.4) + activesupport (= 5.2.4.4) + activerecord (5.2.4.4) + activemodel (= 5.2.4.4) + activesupport (= 5.2.4.4) arel (>= 9.0) - activestorage (5.2.4.3) - actionpack (= 5.2.4.3) - activerecord (= 5.2.4.3) + activestorage (5.2.4.4) + actionpack (= 5.2.4.4) + activerecord (= 5.2.4.4) marcel (~> 0.3.1) - activesupport (5.2.4.3) + activesupport (5.2.4.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -159,7 +159,7 @@ GEM ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) base32-crockford (0.1.0) - bcrypt (3.1.13) + bcrypt (3.1.16) benchmark-ips (2.8.2) better_errors (2.7.1) coderay (>= 1.0.0) @@ -229,18 +229,18 @@ GEM unicode_plot (>= 0.0.4, < 1.0.0) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - device_detector (1.0.3) + device_detector (1.0.4) devise (4.7.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) - diff-lcs (1.3) + diff-lcs (1.4.4) docile (1.1.5) dotenv (2.7.6) - dotiw (4.0.1) - actionpack (>= 4) + dotiw (5.1.0) + activesupport i18n dumb_delegator (0.8.1) email_spec (2.2.0) @@ -252,15 +252,15 @@ GEM equalizer (0.0.11) errbase (0.2.0) erubi (1.9.0) - exception_notification (4.4.0) + exception_notification (4.4.3) actionmailer (>= 4.0, < 7) activesupport (>= 4.0, < 7) execjs (2.7.0) - factory_bot (5.2.0) - activesupport (>= 4.2.0) - factory_bot_rails (5.2.0) - factory_bot (~> 5.2.0) - railties (>= 4.2.0) + factory_bot (6.1.0) + activesupport (>= 5.0.0) + factory_bot_rails (6.1.0) + factory_bot (~> 6.1.0) + railties (>= 5.0.0) faker (2.7.0) i18n (>= 1.6, < 1.8) faraday (1.0.1) @@ -345,7 +345,7 @@ GEM activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.6.0) + loofah (2.7.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) lumberjack (1.0.13) @@ -365,7 +365,7 @@ GEM mini_histogram (0.1.3) mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.14.1) + minitest (5.14.2) msgpack (1.3.3) multi_xml (0.6.0) multipart-post (2.1.1) @@ -376,7 +376,7 @@ GEM net-ssh (>= 2.6.5) net-ssh (5.2.0) newrelic_rpm (6.12.0.367) - nio4r (2.5.2) + nio4r (2.5.3) nokogiri (1.10.10) mini_portile2 (~> 2.4.0) notiffany (0.1.3) @@ -396,7 +396,7 @@ GEM pg (1.1.4) phonelib (0.6.39) pkcs11 (0.3.2) - premailer (1.11.1) + premailer (1.13.1) addressable css_parser (>= 1.6.0) htmlentities (>= 4.0.0) @@ -415,7 +415,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) psych (3.1.0) - public_suffix (4.0.5) + public_suffix (4.0.6) puma (4.3.5) nio4r (~> 2.0) rack (2.2.3) @@ -436,23 +436,23 @@ GEM rack_session_access (0.2.0) builder (>= 2.0.0) rack (>= 1.0.0) - rails (5.2.4.3) - actioncable (= 5.2.4.3) - actionmailer (= 5.2.4.3) - actionpack (= 5.2.4.3) - actionview (= 5.2.4.3) - activejob (= 5.2.4.3) - activemodel (= 5.2.4.3) - activerecord (= 5.2.4.3) - activestorage (= 5.2.4.3) - activesupport (= 5.2.4.3) + rails (5.2.4.4) + actioncable (= 5.2.4.4) + actionmailer (= 5.2.4.4) + actionpack (= 5.2.4.4) + actionview (= 5.2.4.4) + activejob (= 5.2.4.4) + activemodel (= 5.2.4.4) + activerecord (= 5.2.4.4) + activestorage (= 5.2.4.4) + activesupport (= 5.2.4.4) bundler (>= 1.3.0) - railties (= 5.2.4.3) + railties (= 5.2.4.4) sprockets-rails (>= 2.0.0) - rails-controller-testing (1.0.4) - actionpack (>= 5.0.1.x) - actionview (>= 5.0.1.x) - activesupport (>= 5.0.1.x) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) @@ -466,9 +466,9 @@ GEM rails-i18n (5.1.3) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.2.4.3) - actionpack (= 5.2.4.3) - activesupport (= 5.2.4.3) + railties (5.2.4.4) + actionpack (= 5.2.4.4) + activesupport (= 5.2.4.4) method_source rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) @@ -484,7 +484,7 @@ GEM redis (>= 3.0, < 5.0) recaptcha (5.2.1) json - redis (4.2.1) + redis (4.2.2) redis-session-store (0.11.3) actionpack (>= 3, < 7) redis (>= 3, < 5) @@ -648,8 +648,8 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - warden (1.2.8) - rack (>= 2.0.6) + warden (1.2.9) + rack (>= 2.0.9) webauthn (2.1.0) awrence (~> 1.1) bindata (~> 2.4) @@ -670,7 +670,7 @@ GEM activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) - websocket-driver (0.7.2) + websocket-driver (0.7.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xmldsig (0.6.6) @@ -711,7 +711,7 @@ DEPENDENCIES codeclimate-test-reporter derailed (>= 0.1.0) device_detector - devise (~> 4.7.1) + devise (~> 4.7.2) dotiw (>= 4.0.1) email_spec exception_notification (>= 4.4.0) @@ -758,7 +758,7 @@ DEPENDENCIES rack-test (>= 1.1.0) rack-timeout rack_session_access (>= 0.2.0) - rails (~> 5.2.4, >= 5.2.4.3) + rails (~> 5.2.4, >= 5.2.4.4) rails-controller-testing (>= 1.0.4) rails-erd (>= 1.6.0) raise-if-root From 844ae6fa850d3cc8feb0697ad8e6b3f088c8631f Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Fri, 28 Aug 2020 10:34:45 -0500 Subject: [PATCH 09/75] refactor account page a bit --- app/view_models/account_show.rb | 24 ++++-------------------- app/views/accounts/_header.html.erb | 2 +- app/views/accounts/show.html.erb | 13 +++++++++---- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/app/view_models/account_show.rb b/app/view_models/account_show.rb index f04d2f25403..4cbc0a42bca 100644 --- a/app/view_models/account_show.rb +++ b/app/view_models/account_show.rb @@ -9,24 +9,12 @@ def initialize(decrypted_pii:, personal_key:, decorated_user:, locked_for_sessio @pii = determine_pii end - def header_partial - 'accounts/header' + def show_personal_key_partial? + personal_key.present? end - def personal_key_partial - if personal_key.present? - 'accounts/personal_key' - else - 'shared/null' - end - end - - def password_reset_partial - if decorated_user.password_reset_profile.present? - 'accounts/password_reset' - else - 'shared/null' - end + def show_password_reset_partial? + decorated_user.password_reset_profile.present? end def pending_profile_partial @@ -41,10 +29,6 @@ def pending_profile_partial end end - def badges_partial - 'accounts/badges' - end - def unphishable_badge_partial return 'shared/null' unless MfaPolicy.new(decorated_user.user).unphishable? 'accounts/unphishable_badge' diff --git a/app/views/accounts/_header.html.erb b/app/views/accounts/_header.html.erb index bd037c3db3b..17fc4974fd8 100644 --- a/app/views/accounts/_header.html.erb +++ b/app/views/accounts/_header.html.erb @@ -12,7 +12,7 @@ - <%= render partial: view_model.badges_partial %> + <%= render 'accounts/badges' %>
<%= t('headings.account.login_info') %> diff --git a/app/views/accounts/show.html.erb b/app/views/accounts/show.html.erb index bc0e9876566..89db95800f6 100644 --- a/app/views/accounts/show.html.erb +++ b/app/views/accounts/show.html.erb @@ -4,11 +4,16 @@ <% title t('titles.account') %> -<%= render @view_model.personal_key_partial, view_model: @view_model %> -<%= render @view_model.password_reset_partial, view_model: @view_model %> -<%= render @view_model.pending_profile_partial, view_model: @view_model %> +<% if @view_model.show_personal_key_partial? %> + <%= render 'accounts/personal_key', view_model: @view_model %> +<% end %> -<%= render @view_model.header_partial, view_model: @view_model %> +<% if @view_model.show_password_reset_partial? %> + <%= render 'accounts/password_reset', view_model: @view_model %> +<% end %> + +<%= render @view_model.pending_profile_partial, view_model: @view_model %> +<%= render 'accounts/header', view_model: @view_model %>
From acee84280fe1b8debc68c01358378ebed7ea7116 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Tue, 1 Sep 2020 17:06:52 -0500 Subject: [PATCH 10/75] placeholder --- app/assets/stylesheets/components/_nav.scss | 24 +++- .../components/_profile-section.scss | 22 +++- app/assets/stylesheets/components/_util.scss | 1 - app/controllers/accounts_controller.rb | 32 ++++- .../users/backup_code_setup_controller.rb | 2 +- .../users/totp_setup_controller.rb | 6 +- .../users/webauthn_setup_controller.rb | 4 +- app/decorators/device_decorator.rb | 4 - app/view_models/account_show.rb | 29 ++--- app/view_models/navigation.rb | 39 ++++++ app/views/accounts/_account_item.html.erb | 17 --- app/views/accounts/_auth_apps.html.erb | 38 +++--- app/views/accounts/_backup_codes.html.erb | 43 ++++--- app/views/accounts/_connected_app.html.erb | 2 +- .../_delete_account_item_heading.html.erb | 1 - app/views/accounts/_device_item.html.erb | 2 +- app/views/accounts/_emails.html.erb | 10 +- app/views/accounts/_identity_item.html.erb | 2 +- .../accounts/_manage_personal_key.html.erb | 15 +++ app/views/accounts/_mobile_nav.html.erb | 25 ++++ app/views/accounts/_nav_auth.html.erb | 19 +-- app/views/accounts/_phone.html.erb | 43 ++++--- app/views/accounts/_pii.html.erb | 75 +++++------- app/views/accounts/_piv_cac.html.erb | 38 +++--- app/views/accounts/_side_nav.html.erb | 20 +++ app/views/accounts/_webauthn.html.erb | 45 +++---- .../actions/_regenerate_backup_codes.html.erb | 5 - .../accounts/connected_accounts.html.erb | 15 +++ app/views/accounts/history.html.erb | 25 ++++ app/views/accounts/show.html.erb | 106 +++------------- .../two_factor_authentication.html.erb | 33 +++++ app/views/events/show.html.erb | 4 +- app/views/layouts/account_side_nav.html.erb | 33 +++++ app/views/layouts/base.html.erb | 8 +- .../users/webauthn_setup/delete.html.erb | 2 +- config/locales/account/en.yml | 30 +++-- config/locales/account/es.yml | 30 +++-- config/locales/account/fr.yml | 30 +++-- config/locales/forms/en.yml | 2 +- config/locales/headings/en.yml | 6 +- config/locales/headings/es.yml | 6 +- config/locales/headings/fr.yml | 6 +- config/locales/titles/en.yml | 1 - config/locales/titles/es.yml | 1 - config/locales/titles/fr.yml | 1 - config/routes.rb | 3 + .../features/users/piv_cac_management_spec.rb | 6 +- .../users/regenerate_personal_key_spec.rb | 5 + spec/features/webauthn/management_spec.rb | 18 +-- spec/view_models/account_show_spec.rb | 108 ----------------- .../connected_accounts.html.erb_spec.rb | 31 +++++ spec/views/accounts/history.html.erb_spec.rb | 23 ++++ spec/views/accounts/show.html.erb_spec.rb | 114 +----------------- ...two_factor_authentication.html.erb_spec.rb | 74 ++++++++++++ 54 files changed, 712 insertions(+), 572 deletions(-) create mode 100644 app/view_models/navigation.rb delete mode 100644 app/views/accounts/_account_item.html.erb delete mode 100644 app/views/accounts/_delete_account_item_heading.html.erb create mode 100644 app/views/accounts/_manage_personal_key.html.erb create mode 100644 app/views/accounts/_mobile_nav.html.erb create mode 100644 app/views/accounts/_side_nav.html.erb create mode 100644 app/views/accounts/connected_accounts.html.erb create mode 100644 app/views/accounts/history.html.erb create mode 100644 app/views/accounts/two_factor_authentication.html.erb create mode 100644 app/views/layouts/account_side_nav.html.erb create mode 100644 spec/views/accounts/connected_accounts.html.erb_spec.rb create mode 100644 spec/views/accounts/history.html.erb_spec.rb create mode 100644 spec/views/accounts/two_factor_authentication.html.erb_spec.rb diff --git a/app/assets/stylesheets/components/_nav.scss b/app/assets/stylesheets/components/_nav.scss index 7fdb70533b2..4efa129c406 100644 --- a/app/assets/stylesheets/components/_nav.scss +++ b/app/assets/stylesheets/components/_nav.scss @@ -15,9 +15,29 @@ .nav-nonbranded { height: 72px; } - - .nav-nonbranded { +.nav-nonbranded { a { line-height: 17px; } img { height: 17px; } } } + +.sidenav-mobile { + @include at-media("desktop") { + display: none; + } + + .usa-nav__close { + @include add-background-svg("close-blue-60v-alt"); + @include u-square(6); + background-position: center center; + background-repeat: no-repeat; + } +} + +.sidenav { + display: none; + + @include at-media("desktop") { + display: block; + } +} diff --git a/app/assets/stylesheets/components/_profile-section.scss b/app/assets/stylesheets/components/_profile-section.scss index fb84149b984..0ea02a56894 100644 --- a/app/assets/stylesheets/components/_profile-section.scss +++ b/app/assets/stylesheets/components/_profile-section.scss @@ -1,8 +1,8 @@ .profile-info-box { border: 0; - border-bottom: $border-width solid $border-color; border-radius: 0; margin-bottom: 0; + padding: $space-3; overflow: hidden; .bg-lightest-blue img { @@ -13,6 +13,26 @@ @media #{$breakpoint-sm} { .profile-info-box { + border-radius: $border-radius-md; + margin-bottom: $space-3; + } +} + +.events-info-box { + border: 0; + border: $border-width solid $border-color; + border-radius: 0; + margin-bottom: 0; + overflow: hidden; + + .bg-lightest-blue img { + margin-top: -2px; + vertical-align: middle; + } +} + +@media #{$breakpoint-sm} { + .events-info-box { border: $border-width solid $border-color; border-radius: $border-radius-md; margin-bottom: $space-3; diff --git a/app/assets/stylesheets/components/_util.scss b/app/assets/stylesheets/components/_util.scss index 40e95b730eb..b215d5fb4ff 100644 --- a/app/assets/stylesheets/components/_util.scss +++ b/app/assets/stylesheets/components/_util.scss @@ -69,7 +69,6 @@ // Temporary Classes for Overriding during design system migration - .border-top { border-top: 1px solid $border-color; } diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index e60f20caeee..b7bae6f23b3 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -2,7 +2,7 @@ class AccountsController < ApplicationController include RememberDeviceConcern before_action :confirm_two_factor_authenticated - layout 'card_wide' + layout 'account_side_nav' def show analytics.track_event(Analytics::ACCOUNT_VISIT) @@ -14,4 +14,34 @@ def show locked_for_session: pii_locked_for_session?(current_user), ) end + + def connected_accounts + cacher = Pii::Cacher.new(current_user, user_session) + @view_model = AccountShow.new( + decrypted_pii: cacher.fetch, + personal_key: flash[:personal_key], + decorated_user: current_user.decorate, + locked_for_session: pii_locked_for_session?(current_user), + ) + end + + def history + cacher = Pii::Cacher.new(current_user, user_session) + @view_model = AccountShow.new( + decrypted_pii: cacher.fetch, + personal_key: flash[:personal_key], + decorated_user: current_user.decorate, + locked_for_session: pii_locked_for_session?(current_user), + ) + end + + def two_factor_authentication + cacher = Pii::Cacher.new(current_user, user_session) + @view_model = AccountShow.new( + decrypted_pii: cacher.fetch, + personal_key: flash[:personal_key], + decorated_user: current_user.decorate, + locked_for_session: pii_locked_for_session?(current_user), + ) + end end diff --git a/app/controllers/users/backup_code_setup_controller.rb b/app/controllers/users/backup_code_setup_controller.rb index 5f2da6fef95..65840b4ba61 100644 --- a/app/controllers/users/backup_code_setup_controller.rb +++ b/app/controllers/users/backup_code_setup_controller.rb @@ -45,7 +45,7 @@ def delete current_user.backup_code_configurations.destroy_all flash[:success] = t('notices.backup_codes_deleted') revoke_remember_device(current_user) - redirect_to account_url + redirect_to account_two_factor_authentication_path end private diff --git a/app/controllers/users/totp_setup_controller.rb b/app/controllers/users/totp_setup_controller.rb index fdfb0f004f3..e121fae58a4 100644 --- a/app/controllers/users/totp_setup_controller.rb +++ b/app/controllers/users/totp_setup_controller.rb @@ -34,7 +34,7 @@ def confirm def disable process_successful_disable if MfaPolicy.new(current_user).multiple_factors_enabled? - redirect_to account_url + redirect_to account_two_factor_authentication_path end private @@ -124,7 +124,9 @@ def new_totp_secret end def cap_auth_app_count - redirect_to account_url if Figaro.env.max_auth_apps_per_account.to_i <= current_auth_app_count + if Figaro.env.max_auth_apps_per_account.to_i <= current_auth_app_count + redirect_to account_two_factor_authentication_path + end end def current_auth_app_count diff --git a/app/controllers/users/webauthn_setup_controller.rb b/app/controllers/users/webauthn_setup_controller.rb index 62c7d409c03..ee2abd4fdee 100644 --- a/app/controllers/users/webauthn_setup_controller.rb +++ b/app/controllers/users/webauthn_setup_controller.rb @@ -34,7 +34,7 @@ def delete else handle_failed_delete end - redirect_to account_url + redirect_to account_two_factor_authentication_path end def show_delete @@ -109,7 +109,7 @@ def process_invalid_webauthn(form) render :new else flash[:error] = t('errors.webauthn_setup.general_error') - redirect_to account_url + redirect_to account_two_factor_authentication_path end end diff --git a/app/decorators/device_decorator.rb b/app/decorators/device_decorator.rb index fd9973a1b95..491a4cd39b8 100644 --- a/app/decorators/device_decorator.rb +++ b/app/decorators/device_decorator.rb @@ -1,10 +1,6 @@ DeviceDecorator = Struct.new(:device) do delegate :nice_name, :last_used_at, :id, to: :device - def device_partial - 'accounts/device_item' - end - def last_sign_in_location_and_ip I18n.t('account.index.sign_in_location_and_ip', location: last_location, ip: device.last_ip) end diff --git a/app/view_models/account_show.rb b/app/view_models/account_show.rb index 4cbc0a42bca..dc060426bb9 100644 --- a/app/view_models/account_show.rb +++ b/app/view_models/account_show.rb @@ -55,14 +55,6 @@ def show_pii_partial? decrypted_pii.present? || decorated_user.identity_verified? end - def pii_partial - if show_pii_partial? - 'accounts/pii' - else - 'shared/null' - end - end - def totp_partial if TwoFactorAuthentication::AuthAppPolicy.new(decorated_user.user).enabled? disable_totp_partial @@ -97,16 +89,13 @@ def enable_piv_cac_partial 'accounts/actions/enable_piv_cac' end - def manage_personal_key_partial - yield if decorated_user.password_reset_profile.blank? - end - - def personal_key_action_partial - 'accounts/actions/manage_personal_key' - end - - def personal_key_item_partial - 'accounts/personal_key_item_heading' + def show_manage_personal_key_partial? + if TwoFactorAuthentication::PersonalKeyPolicy.new(decorated_user.user).visible? && + decorated_user.password_reset_profile.blank? + true + else + false + end end def backup_codes_partial @@ -129,10 +118,6 @@ def backup_codes_generated_at decorated_user.user.backup_code_configurations.order(created_at: :asc).first&.created_at end - def recent_event_partial - 'accounts/event_item' - end - def header_personalization return decrypted_pii.first_name if decrypted_pii.present? diff --git a/app/view_models/navigation.rb b/app/view_models/navigation.rb new file mode 100644 index 00000000000..65f64fdc404 --- /dev/null +++ b/app/view_models/navigation.rb @@ -0,0 +1,39 @@ +class Navigation + class << self + include Rails.application.routes.url_helpers + end + NavItem = Struct.new(:title, :href, :children) + + def self.navigation_items(user) + [ + NavItem.new(I18n.t('account.navigation.your_account'), account_path, [ + NavItem.new(I18n.t('account.navigation.add_email'), add_email_path), + NavItem.new(I18n.t('account.navigation.edit_password'), manage_password_path), + NavItem.new(I18n.t('account.navigation.delete_account'), account_delete_path), + ]), + NavItem.new(I18n.t('account.navigation.two_factor_authentication'), + account_two_factor_authentication_path, [ + NavItem.new(I18n.t('account.navigation.add_phone_number'), add_phone_path), + NavItem.new(I18n.t('account.navigation.add_authentication_apps'), authenticator_setup_url), + NavItem.new(I18n.t('account.navigation.add_security_key'), webauthn_setup_path), + NavItem.new(I18n.t('account.navigation.add_federal_id'), setup_piv_cac_path), + NavItem.new(I18n.t('account.navigation.get_backup_codes'), backup_codes_path(user)), + ]), + NavItem.new(I18n.t('account.navigation.connected_accounts'), + account_connected_accounts_path, []), + NavItem.new(I18n.t('account.navigation.history'), account_history_path, [ + NavItem.new(I18n.t('account.navigation.forget_browsers'), + forget_all_browsers_path), + ]), + NavItem.new(I18n.t('account.navigation.customer_support'), MarketingSite.help_url, []), + ] + end + + def self.backup_codes_path(user) + if TwoFactorAuthentication::BackupCodePolicy.new(user).configured? + backup_code_regenerate_path + else + backup_code_create_path + end + end +end diff --git a/app/views/accounts/_account_item.html.erb b/app/views/accounts/_account_item.html.erb deleted file mode 100644 index 9a75c9380e3..00000000000 --- a/app/views/accounts/_account_item.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -
-
-
- <%= name %> -
-
- <%= content if local_assigns.key? :content %> -
-
-
- <% if local_assigns.key? :path %> - <%= render action, path: path, name: name %> - <% elsif local_assigns.key? :action %> - <%= render action %> - <% end %> -
-
diff --git a/app/views/accounts/_auth_apps.html.erb b/app/views/accounts/_auth_apps.html.erb index 24331524bc1..0a186feae4b 100644 --- a/app/views/accounts/_auth_apps.html.erb +++ b/app/views/accounts/_auth_apps.html.erb @@ -1,10 +1,10 @@ -
-
-
- Authentication apps -
+
+
+

+ <%= t('headings.account.authentication_apps') %> +

<% if current_user.auth_app_configurations.count < Figaro.env.max_auth_apps_per_account.to_i %> -
+
@@ -12,19 +12,21 @@ <% end %>
- <% MfaContext.new(current_user).auth_app_configurations.each do |auth_app_configuration| %> -
-
- diff --git a/app/views/accounts/_backup_codes.html.erb b/app/views/accounts/_backup_codes.html.erb index fa01a4283b0..618bc2d5350 100644 --- a/app/views/accounts/_backup_codes.html.erb +++ b/app/views/accounts/_backup_codes.html.erb @@ -1,22 +1,29 @@ -
-
-
<%= t 'forms.backup_code.title' %>
-
- <% if TwoFactorAuthentication::BackupCodePolicy.new(current_user).configured? %> -

- <%= t 'account.index.backup_codes_exist' %> -

-

- <%= - local_time(@view_model.backup_codes_generated_at, t('time.formats.event_timestamp')) - %> -

- <% else %> - <%= t 'account.index.backup_codes_no_exist' %> - <% end %> +
+
+

+ <%= t('forms.backup_code.title') %> +

+
+ <%= render @view_model.backup_codes_partial %>
-
- <%= render @view_model.backup_codes_partial %> +
+ <% if TwoFactorAuthentication::BackupCodePolicy.new(current_user).configured? %> +
+ <%= t 'account.index.backup_codes_exist' %> + <%= + local_time(@view_model.backup_codes_generated_at, t('time.formats.event_timestamp')) + %> +
+
+ <% if MfaPolicy.new(current_user).multiple_factors_enabled? %> + <%= link_to t('forms.buttons.delete'), backup_code_delete_path %> + <% end %> +
+ <% else %> +
+ <%= t 'account.index.backup_codes_no_exist' %> +
+ <% end %>
diff --git a/app/views/accounts/_connected_app.html.erb b/app/views/accounts/_connected_app.html.erb index 4d51ad1f4ef..6d879992008 100644 --- a/app/views/accounts/_connected_app.html.erb +++ b/app/views/accounts/_connected_app.html.erb @@ -1,4 +1,4 @@ -
+
diff --git a/app/views/accounts/_delete_account_item_heading.html.erb b/app/views/accounts/_delete_account_item_heading.html.erb deleted file mode 100644 index 2500a36cfa0..00000000000 --- a/app/views/accounts/_delete_account_item_heading.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= t('account.items.delete_your_account') %> diff --git a/app/views/accounts/_device_item.html.erb b/app/views/accounts/_device_item.html.erb index b1fd7dbbe3c..9e589f0d4ff 100644 --- a/app/views/accounts/_device_item.html.erb +++ b/app/views/accounts/_device_item.html.erb @@ -1,4 +1,4 @@ -
+
diff --git a/app/views/accounts/_emails.html.erb b/app/views/accounts/_emails.html.erb index 54d637c0b35..29939e83ffb 100644 --- a/app/views/accounts/_emails.html.erb +++ b/app/views/accounts/_emails.html.erb @@ -1,8 +1,8 @@ -
-
-
+
+
+

<%= t('account.index.email_addresses') %> -

+
<% if EmailPolicy.new(current_user).can_add_email? %>
<% @view_model.decorated_user.visible_email_addresses.each do |email| %> -