Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ push_notifications_enabled: 'false'
pwned_passwords_file_path: 'pwned_passwords/pwned_passwords.txt'
rack_mini_profiler: 'false'
rack_timeout_service_timeout_seconds: '15'
rails_mailer_previews_enabled: 'false'
reauthn_window: '120'
recovery_code_length: '4'
redis_throttle_url: redis://localhost:6379/1
Expand Down Expand Up @@ -258,6 +259,7 @@ development:
otp_delivery_blocklist_findtime: '5'
password_pepper: f22d4b2cafac9066fe2f4416f5b7a32c
piv_cac_verify_token_secret: ee7f20f44cdc2ba0c6830f70470d1d1d059e1279cdb58134db92b35947b1528ef5525ece5910cf4f2321ab989a618feea12ef95711dbc62b9601e8520a34ee12
rails_mailer_previews_enabled: 'true'
recurring_jobs_disabled_names: "[]"
s3_report_bucket_prefix: ''
s3_report_public_bucket_prefix: ''
Expand Down
1 change: 1 addition & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
config.action_mailer.asset_host = IdentityConfig.store.mailer_domain_name
config.action_mailer.raise_delivery_errors = false
config.action_mailer.smtp_settings = { address: ENV['SMTP_HOST'] || 'localhost', port: 1025 }
config.action_mailer.show_previews = IdentityConfig.store.rails_mailer_previews_enabled

routes.default_url_options[:protocol] = 'https' if ENV['HTTPS'] == 'on'

Expand Down
5 changes: 5 additions & 0 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
:ses
end

if IdentityConfig.store.rails_mailer_previews_enabled
config.action_mailer.show_previews = true
config.action_mailer.preview_path = Rails.root.join('spec/mailers/previews')
end

routes.default_url_options[:protocol] = :https

# turn off IP spoofing protection since the network configuration in the production environment
Expand Down
8 changes: 6 additions & 2 deletions config/initializers/secure_headers.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
SecureHeaders::Configuration.default do |config| # rubocop:disable Metrics/BlockLength
config.hsts = "max-age=#{365.days.to_i}; includeSubDomains; preload"
config.x_frame_options = Rails.env.development? ? 'ALLOWALL' : 'DENY'
config.x_frame_options = 'DENY'
config.x_content_type_options = 'nosniff'
config.x_xss_protection = '1; mode=block'
config.x_download_options = 'noopen'
Expand All @@ -12,7 +12,6 @@
default_csp_config = {
default_src: ["'self'"],
child_src: ["'self'"], # CSP 2.0 only; replaces frame_src
# frame_ancestors: %w('self'), # CSP 2.0 only; overriden by x_frame_options in some browsers
form_action: ["'self'"],
block_all_mixed_content: true, # CSP 2.0 only;
connect_src: connect_src.flatten,
Expand Down Expand Up @@ -40,6 +39,11 @@
base_uri: ["'self'"],
}

if IdentityConfig.store.rails_mailer_previews_enabled
# CSP 2.0 only; overriden by x_frame_options in some browsers
default_csp_config[:frame_ancestors] = %w['self']
end

config.csp = if !Rails.env.production?
default_csp_config.merge(
script_src: ["'self'", "'unsafe-eval'", "'unsafe-inline'"],
Expand Down
1 change: 1 addition & 0 deletions lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ def self.build_store(config_map)
config.add(:pwned_passwords_file_path, type: :string)
config.add(:rack_mini_profiler, type: :boolean)
config.add(:rack_timeout_service_timeout_seconds, type: :integer)
config.add(:rails_mailer_previews_enabled, type: :boolean)
config.add(:reauthn_window, type: :integer)
config.add(:recovery_code_length, type: :integer)
config.add(:recurring_jobs_disabled_names, type: :json)
Expand Down
85 changes: 57 additions & 28 deletions spec/mailers/previews/user_mailer_preview.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class UserMailerPreview < ActionMailer::Preview
def email_confirmation_instructions
UserMailer.email_confirmation_instructions(
User.first,
'foo@bar.gov',
user,
email_address,
SecureRandom.hex,
request_id: SecureRandom.uuid,
instructions: I18n.t(
Expand All @@ -14,8 +14,8 @@ def email_confirmation_instructions

def unconfirmed_email_instructions
UserMailer.unconfirmed_email_instructions(
User.first,
'foo@bar.gov',
user,
email_address,
SecureRandom.hex,
request_id: SecureRandom.uuid,
instructions: I18n.t(
Expand All @@ -26,98 +26,98 @@ def unconfirmed_email_instructions
end

def signup_with_your_email
UserMailer.signup_with_your_email(User.first, 'foo@bar.gov')
UserMailer.signup_with_your_email(user, email_address)
end

def reset_password_instructions
UserMailer.reset_password_instructions(User.first, 'foo@bar.gov', token: SecureRandom.hex)
UserMailer.reset_password_instructions(user, email_address, token: SecureRandom.hex)
end

def password_changed
UserMailer.password_changed(User.first, EmailAddress.first, disavowal_token: SecureRandom.hex)
UserMailer.password_changed(user, email_address_record, disavowal_token: SecureRandom.hex)
end

def phone_added
UserMailer.phone_added(User.first, EmailAddress.first, disavowal_token: SecureRandom.hex)
UserMailer.phone_added(user, email_address_record, disavowal_token: SecureRandom.hex)
end

def account_does_not_exist
UserMailer.account_does_not_exist('foo@bar.gov', SecureRandom.uuid)
UserMailer.account_does_not_exist(email_address, SecureRandom.uuid)
end

def personal_key_sign_in
UserMailer.personal_key_sign_in(User.first, 'foo@bar.gov', disavowal_token: SecureRandom.hex)
UserMailer.personal_key_sign_in(user, email_address, disavowal_token: SecureRandom.hex)
end

def new_device_sign_in
UserMailer.new_device_sign_in(
user: User.first,
email_address: EmailAddress.first,
user: user,
email_address: email_address_record,
date: 'February 25, 2019 15:02',
location: 'Washington, DC',
disavowal_token: SecureRandom.hex,
)
end

def personal_key_regenerated
UserMailer.personal_key_regenerated(User.first, 'foo@bar.gov')
UserMailer.personal_key_regenerated(user, email_address)
end

def account_reset_request
UserMailer.account_reset_request(
User.first, EmailAddress.first, User.first.build_account_reset_request
user, email_address_record, user.build_account_reset_request
)
end

def account_reset_granted
UserMailer.account_reset_granted(
User.first, EmailAddress.first, User.first.build_account_reset_request
user, email_address_record, user.build_account_reset_request
)
end

def account_reset_complete
UserMailer.account_reset_complete(User.first, EmailAddress.first)
UserMailer.account_reset_complete(user, email_address_record)
end

def account_reset_cancel
UserMailer.account_reset_cancel(User.first, EmailAddress.first)
UserMailer.account_reset_cancel(user, email_address_record)
end

def please_reset_password
UserMailer.please_reset_password(User.first, 'foo@bar.gov')
UserMailer.please_reset_password(user, email_address)
end

def doc_auth_desktop_link_to_sp
UserMailer.doc_auth_desktop_link_to_sp(User.first, 'foo@bar.gov', 'Example App', '/')
UserMailer.doc_auth_desktop_link_to_sp(user, email_address, 'Example App', '/')
end

def letter_reminder
UserMailer.letter_reminder(User.first, 'foo@bar.gov')
UserMailer.letter_reminder(user, email_address)
end

def add_email
UserMailer.add_email(User.first, 'foo@bar.gov', SecureRandom.hex)
UserMailer.add_email(user, email_address, SecureRandom.hex)
end

def email_added
UserMailer.email_added(User.first, 'foo@bar.gov')
UserMailer.email_added(user, email_address)
end

def email_deleted
UserMailer.email_deleted(User.first, 'foo@bar.gov')
UserMailer.email_deleted(user, email_address)
end

def add_email_associated_with_another_account
UserMailer.add_email_associated_with_another_account('foo@bar.gov')
UserMailer.add_email_associated_with_another_account(email_address)
end

def sps_over_quota_limit
UserMailer.sps_over_quota_limit('foo@bar.gov')
UserMailer.sps_over_quota_limit(email_address)
end

def deleted_user_accounts_report
UserMailer.deleted_user_accounts_report(
email: 'foo@bar.gov',
email: email_address,
name: 'my name',
issuers: %w[issuer1 issuer2],
data: 'data',
Expand All @@ -126,11 +126,40 @@ def deleted_user_accounts_report

def account_verified
UserMailer.account_verified(
User.first,
EmailAddress.first,
user,
email_address_record,
date_time: DateTime.now,
sp_name: 'Example App',
disavowal_token: SecureRandom.hex,
)
end

private

def user
unsaveable(User.new(email_addresses: [email_address_record]))
end

def email_address
'email@example.com'
end

def email_address_record
unsaveable(EmailAddress.new(email: email_address))
end

# Remove #save and #save! to make sure we can't write these made-up records
def unsaveable(record)
class << record
def save
raise "don't save me!"
end

def save!
raise "don't save me!"
end
end

record
end
end
16 changes: 14 additions & 2 deletions spec/mailers/previews/user_mailer_preview_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
RSpec.describe UserMailerPreview do
UserMailerPreview.instance_methods(false).each do |mailer_method|
describe "##{mailer_method}" do
before { create(:user) }

it 'generates a preview without blowing up' do
expect { UserMailerPreview.new.public_send(mailer_method) }.to_not raise_error
end
Expand All @@ -17,4 +15,18 @@
preview_methods = UserMailerPreview.instance_methods(false)
expect(mailer_methods - preview_methods).to be_empty
end

it 'uses user and email records that cannot be saved' do
expect(User.count).to eq(0)
user = UserMailerPreview.new.send(:user)
expect { user.save }.to raise_error
expect { user.save! }.to raise_error
expect(User.count).to eq(0)

expect(EmailAddress.count).to eq(0)
email_address_record = UserMailerPreview.new.send(:email_address_record)
expect { email_address_record.save }.to raise_error
expect { email_address_record.save! }.to raise_error
expect(EmailAddress.count).to eq(0)
end
end