Skip to content

Commit

Permalink
Merge pull request #2643 from 18F/stages/rc-2018-11-08
Browse files Browse the repository at this point in the history
Deploy RC 70 to int
  • Loading branch information
jgsmith-usds authored Nov 5, 2018
2 parents 2bfd7ae + 042f375 commit b98cac7
Show file tree
Hide file tree
Showing 151 changed files with 2,254 additions and 410 deletions.
3 changes: 3 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ plugins:
channel: rubocop-0-58
scss-lint:
enabled: true
checks:
HexLength:
enabled: false

exclude_patterns:
- 'db/schema.rb'
Expand Down
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Metrics/ClassLength:
- spec/**/*
- app/controllers/application_controller.rb
- app/controllers/openid_connect/authorization_controller.rb
- app/controllers/saml_idp_controller.rb
- app/controllers/users/confirmations_controller.rb
- app/controllers/users/sessions_controller.rb
- app/controllers/users/two_factor_authentication_controller.rb
Expand Down
8 changes: 4 additions & 4 deletions app/assets/stylesheets/variables/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ $red: #e21c3d !default;
$fuchsia: #f012be !default;
$purple: #b10dc9 !default;
$maroon: #85144b !default;
$white: #fff !default;
$white: #ffffff !default;
$silver: #d9dadb !default;
$gray: #5b616a !default;
$gray-light: #ddd !default;
$gray-light: #dddddd !default;
$gray-lighter: #fafafa !default;
$black: #111 !default;
$black: #111111 !default;
$pink: #eb4d67 !default;
$red: #f00 !default;
$red: #ff0000 !default;
$red-lightest: #fff7f8 !default;
4 changes: 2 additions & 2 deletions app/controllers/account_recovery_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ class AccountRecoverySetupController < ApplicationController
before_action :confirm_two_factor_authenticated

def index
return redirect_to account_url unless piv_cac_enabled_but_not_phone_enabled?
return redirect_to account_url unless piv_cac_enabled_but_not_multiple_mfa_enabled?
@two_factor_options_form = TwoFactorOptionsForm.new(current_user)
@presenter = account_recovery_options_presenter
end

private

def account_recovery_options_presenter
AccountRecoveryOptionsPresenter.new
AccountRecoveryOptionsPresenter.new(current_user, current_sp)
end
end
3 changes: 2 additions & 1 deletion app/controllers/account_reset/request_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def show
def create
analytics.track_event(Analytics::ACCOUNT_RESET, analytics_attributes)
AccountReset::CreateRequest.new(current_user).call
flash[:email] = current_user.email_address.email
flash[:email] = current_user.email_addresses.first.email
redirect_to account_reset_confirm_request_url
end

Expand All @@ -40,6 +40,7 @@ def analytics_attributes
sms_phone: TwoFactorAuthentication::PhonePolicy.new(current_user).configured?,
totp: TwoFactorAuthentication::AuthAppPolicy.new(current_user).configured?,
piv_cac: TwoFactorAuthentication::PivCacPolicy.new(current_user).configured?,
email_addresses: current_user.email_addresses.count,
}
end
end
Expand Down
34 changes: 26 additions & 8 deletions app/controllers/analytics_controller.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
class AnalyticsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :confirm_two_factor_authenticated

def create
unless analytics_saved?
session[:platform_authenticator] = true
analytics.track_event(Analytics::PLATFORM_AUTHENTICATOR, results.to_h)
results.each do |event, result|
next if result.nil?

analytics.track_event(event, result.to_h)
end
head :ok
end

private

def results
FormResponse.new(success: true, errors: {},
extra: { platform_authenticator: params[:available] })
{
Analytics::FRONTEND_BROWSER_CAPABILITIES => platform_authenticator_result,
}
end

def platform_authenticator_result
return unless current_user
return if platform_authenticator_results_saved? || !platform_authenticator_params_valid?

session[:platform_authenticator_analytics_saved] = true
platform_authenticator_available = params[:available] ||
params.dig(:platform_authenticator, :available)
extra = { platform_authenticator: (platform_authenticator_available == 'true') }
FormResponse.new(success: true, errors: {}, extra: extra)
end

def platform_authenticator_params_valid?
result = params[:available] || params.dig(:platform_authenticator, :available)
%w[true false].include?(result)
end

def analytics_saved?
session[:platform_authenticator]
def platform_authenticator_results_saved?
session[:platform_authenticator_analytics_saved] == true ||
session[:platform_authenticator] == true
end
end
4 changes: 2 additions & 2 deletions app/controllers/concerns/account_recoverable.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module AccountRecoverable
# :reek:FeatureEnvy
def piv_cac_enabled_but_not_phone_enabled?
def piv_cac_enabled_but_not_multiple_mfa_enabled?
# we need to change this so it's about having multiple mfa methods defined rather than
# piv/cac + phone. Leaving as-is for now.
TwoFactorAuthentication::PivCacPolicy.new(current_user).enabled? &&
!TwoFactorAuthentication::PhonePolicy.new(current_user).enabled?
!MfaPolicy.new(current_user).multiple_factors_enabled?
end
end
28 changes: 25 additions & 3 deletions app/controllers/concerns/remember_device_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ def save_remember_device_preference
def check_remember_device_preference
return unless authentication_context?
return if remember_device_cookie.nil?
return unless remember_device_cookie.valid_for_user?(current_user)
handle_valid_otp
return unless remember_device_cookie.valid_for_user?(
user: current_user,
expiration_interval: decorated_session.mfa_expiration_interval
)

handle_valid_remember_device_cookie
end

def remember_device_cookie
Expand All @@ -24,9 +28,27 @@ def remember_device_cookie
)
end

def remember_device_expired_for_sp?
return false unless user_session[:mfa_device_remembered]
return true if remember_device_cookie.nil?

!remember_device_cookie.valid_for_user?(
user: current_user,
expiration_interval: decorated_session.mfa_expiration_interval
)
end

private

def handle_valid_remember_device_cookie
user_session[:mfa_device_remembered] = true
mark_user_session_authenticated
bypass_sign_in current_user
redirect_to after_otp_verification_confirmation_url
reset_otp_session_data
end

def remember_device_cookie_expiration
Figaro.env.remember_device_expiration_days.to_i.days.from_now
Figaro.env.remember_device_expiration_hours_aal_1.to_i.hours.from_now
end
end
11 changes: 8 additions & 3 deletions app/controllers/concerns/two_factor_authenticatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ def current_password_required?

def check_already_authenticated
return unless initial_authentication_context?
return unless user_fully_authenticated?
return if remember_device_expired_for_sp?

redirect_to after_otp_verification_confirmation_url if user_fully_authenticated?
redirect_to after_otp_verification_confirmation_url
end

def reset_attempt_count_if_user_no_longer_locked_out
Expand All @@ -76,6 +78,7 @@ def handle_valid_otp
handle_valid_otp_for_confirmation_context
end
save_remember_device_preference
user_session.delete(:mfa_device_remembered)

redirect_to after_otp_verification_confirmation_url
reset_otp_session_data
Expand Down Expand Up @@ -145,7 +148,9 @@ def old_phone

def phone_changed
create_user_event(:phone_changed)
UserMailer.phone_changed(current_user).deliver_later
current_user.confirmed_email_addresses.each do |email_address|
UserMailer.phone_changed(email_address).deliver_later
end
end

def phone_confirmed
Expand Down Expand Up @@ -234,7 +239,7 @@ def account_reset_token
def authenticator_view_data
{
two_factor_authentication_method: two_factor_authentication_method,
user_email: current_user.email_address.email,
user_email: current_user.email_addresses.first.email,
remember_device_available: false,
}.merge(generic_data)
end
Expand Down
4 changes: 3 additions & 1 deletion app/controllers/concerns/unconfirmed_user_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def validate_token

def process_valid_confirmation_token
@confirmation_token = params[:confirmation_token]
@forbidden_passwords = ForbiddenPasswords.new(@user.email_address.email).call
@forbidden_passwords = @user.email_addresses.flat_map do |email_address|
ForbiddenPasswords.new(email_address.email).call
end
flash.now[:success] = t('devise.confirmations.confirmed_but_must_set_password')
session[:user_confirmation_token] = @confirmation_token
end
Expand Down
10 changes: 8 additions & 2 deletions app/controllers/openid_connect/authorization_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module OpenidConnect
class AuthorizationController < ApplicationController
include AccountRecoverable
include FullyAuthenticatable
include RememberDeviceConcern
include VerifyProfileConcern
include VerifySPAttributesConcern

Expand All @@ -10,18 +11,23 @@ class AuthorizationController < ApplicationController
before_action :force_login_if_prompt_param_is_login_and_request_is_external, only: [:index]
before_action :store_request, only: [:index]
before_action :apply_secure_headers_override, only: [:index]
before_action :confirm_user_is_authenticated_with_fresh_mfa, only: :index

def index
return confirm_two_factor_authenticated(request_id) unless user_fully_authenticated?
link_identity_to_service_provider
return redirect_to account_recovery_setup_url if piv_cac_enabled_but_not_phone_enabled?
return redirect_to account_recovery_setup_url if piv_cac_enabled_but_not_multiple_mfa_enabled?
return redirect_to_account_or_verify_profile_url if profile_or_identity_needs_verification?
return redirect_to(sign_up_completed_url) if needs_sp_attribute_verification?
handle_successful_handoff
end

private

def confirm_user_is_authenticated_with_fresh_mfa
return confirm_two_factor_authenticated(request_id) unless user_fully_authenticated?
redirect_to user_two_factor_authentication_url if remember_device_expired_for_sp?
end

def link_identity_to_service_provider
@authorize_form.link_identity_to_service_provider(current_user, session.id)
end
Expand Down
10 changes: 8 additions & 2 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ class SamlIdpController < ApplicationController
include SamlIdpLogoutConcern
include AccountRecoverable
include FullyAuthenticatable
include RememberDeviceConcern
include VerifyProfileConcern
include VerifySPAttributesConcern

skip_before_action :verify_authenticity_token
before_action :validate_saml_logout_request, only: :logout
before_action :confirm_user_is_authenticated_with_fresh_mfa, only: :auth

def auth
return confirm_two_factor_authenticated(request_id) unless user_fully_authenticated?
link_identity_from_session_data
capture_analytics
return redirect_to account_recovery_setup_url if piv_cac_enabled_but_not_phone_enabled?
return redirect_to account_recovery_setup_url if piv_cac_enabled_but_not_multiple_mfa_enabled?
return redirect_to_account_or_verify_profile_url if profile_or_identity_needs_verification?
return redirect_to(sign_up_completed_url) if needs_sp_attribute_verification?
handle_successful_handoff
Expand All @@ -40,6 +41,11 @@ def logout

private

def confirm_user_is_authenticated_with_fresh_mfa
return confirm_two_factor_authenticated(request_id) unless user_fully_authenticated?
redirect_to user_two_factor_authentication_url if remember_device_expired_for_sp?
end

def validate_saml_logout_request(raw_saml_request = params[:SAMLRequest])
request_valid = saml_request_valid?(raw_saml_request)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def presenter_for_two_factor_authentication_method

def handle_result(result)
if result.success?
create_user_event(:personal_key_used)
generate_new_personal_key
handle_valid_otp
else
Expand Down Expand Up @@ -69,6 +70,7 @@ def handle_valid_otp
handle_valid_otp_for_authentication_context
redirect_to manage_personal_key_url
reset_otp_session_data
user_session.delete(:mfa_device_remembered)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def handle_valid_piv_cac
handle_valid_otp_for_authentication_context
redirect_to next_step
reset_otp_session_data
user_session.delete(:mfa_device_remembered)
end

def next_step
Expand Down Expand Up @@ -63,7 +64,7 @@ def render_show_after_invalid
def piv_cac_view_data
{
two_factor_authentication_method: two_factor_authentication_method,
user_email: current_user.email_address.email,
user_email: current_user.email_addresses.first.email,
remember_device_available: false,
}.merge(generic_data)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def handle_valid_webauthn
handle_valid_otp_for_authentication_context
redirect_to after_otp_verification_confirmation_url
reset_otp_session_data
user_session.delete(:mfa_device_remembered)
end

def handle_invalid_webauthn
Expand Down
8 changes: 6 additions & 2 deletions app/controllers/users/passwords_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ class PasswordsController < ReauthnRequiredController

def edit
@update_user_password_form = UpdateUserPasswordForm.new(current_user)
@forbidden_passwords = ForbiddenPasswords.new(current_user.email_address.email).call
@forbidden_passwords = current_user.email_addresses.flat_map do |email_address|
ForbiddenPasswords.new(email_address.email).call
end
end

def update
Expand Down Expand Up @@ -41,7 +43,9 @@ def handle_invalid_password
# need to provide our custom forbidden passwords data that zxcvbn needs,
# otherwise the JS will throw an exception and the password strength
# meter will not appear.
@forbidden_passwords = ForbiddenPasswords.new(current_user.email).call
@forbidden_passwords = current_user.email_addresses.flat_map do |email_address|
ForbiddenPasswords.new(email_address.email).call
end
render :edit
end
end
Expand Down
14 changes: 8 additions & 6 deletions app/controllers/users/reset_passwords_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def edit

if result.success?
@reset_password_form = ResetPasswordForm.new(build_user)
@forbidden_passwords = forbidden_passwords(token_user.email_address.email)
@forbidden_passwords = forbidden_passwords(token_user.email_addresses)
else
handle_invalid_or_expired_token(result)
end
Expand All @@ -36,7 +36,6 @@ def edit
# PUT /resource/password
def update
self.resource = user_matching_token(user_params[:reset_password_token])

@reset_password_form = ResetPasswordForm.new(resource)

result = @reset_password_form.submit(user_params)
Expand All @@ -52,8 +51,10 @@ def update

protected

def forbidden_passwords(email_address)
ForbiddenPasswords.new(email_address).call
def forbidden_passwords(email_addresses)
email_addresses.flat_map do |email_address|
ForbiddenPasswords.new(email_address.email).call
end
end

def email_params
Expand Down Expand Up @@ -112,8 +113,9 @@ def handle_successful_password_reset
end

def handle_unsuccessful_password_reset(result)
if result.errors[:reset_password_token].present?
flash[:error] = t('devise.passwords.token_expired')
reset_password_token_errors = result.errors[:reset_password_token]
if reset_password_token_errors.present?
flash[:error] = t("devise.passwords.#{reset_password_token_errors.first}")
redirect_to new_user_password_url
return
end
Expand Down
Loading

0 comments on commit b98cac7

Please sign in to comment.