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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Vagrantfile
!/cert/*.crt.example
/config/application.yml
/config/aws.yml
/geo_data/*
/keys/*.key.enc
!/keys/*.key.enc.example
/keys/equifax_rsa
Expand Down
3 changes: 3 additions & 0 deletions .reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,13 @@ detectors:
- TwoFactorLoginOptionsPresenter
UncommunicativeMethodName:
exclude:
- Deploy::Activate#download_application_yml_from_s3
- Deploy::Activate#download_geocoding_database_from_s3
- PhoneConfirmationFlow
- render_401
- SessionDecorator#registration_bullet_1
- ServiceProviderSessionDecorator#registration_bullet_1

UncommunicativeModuleName:
exclude:
- X509
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ gem 'identity-hostdata', github: '18F/identity-hostdata', branch: 'master'
gem 'json-jwt'
gem 'local_time'
gem 'lograge'
gem 'maxminddb'
gem 'net-sftp'
gem 'newrelic_rpm'
gem 'pg'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ GEM
systemu (~> 2.6.2)
mail (2.7.1)
mini_mime (>= 0.1.1)
maxminddb (0.1.22)
memory_profiler (0.9.11)
method_source (0.9.2)
mime-types (3.2.2)
Expand Down Expand Up @@ -710,6 +711,7 @@ DEPENDENCIES
lexisnexis!
local_time
lograge
maxminddb
net-sftp
newrelic_rpm
overcommit
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def show
personal_key: flash[:personal_key],
decorated_user: current_user.decorate
)

@login_presenter = LoginPresenter.new(user: current_user)
end

private
Expand Down
59 changes: 59 additions & 0 deletions app/presenters/login_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class LoginPresenter
include ActionView::Helpers::DateHelper

def initialize(user:)
@user = user
end

def current_sign_in_location_and_ip
I18n.t('account.index.sign_in_location_and_ip', location: current_location, ip: current_ip)
end

def last_sign_in_location_and_ip
I18n.t('account.index.sign_in_location_and_ip', location: last_location, ip: last_ip)
end

def current_timestamp
timestamp = user.current_sign_in_at || Time.zone.now
I18n.t(
'account.index.sign_in_timestamp',
timestamp: time_ago_in_words(
timestamp, highest_measures: 2, two_words_connector: two_words_connector
)
)
end

def last_timestamp
timestamp = user.last_sign_in_at || Time.zone.now
I18n.t(
'account.index.sign_in_timestamp',
timestamp: time_ago_in_words(
timestamp, highest_measures: 2, two_words_connector: two_words_connector
)
)
end

private

attr_reader :user

def current_location
IpGeocoder.new(current_ip).location
end

def last_location
IpGeocoder.new(last_ip).location
end

def current_ip
user.current_sign_in_ip
end

def last_ip
user.last_sign_in_ip
end

def two_words_connector
" #{I18n.t('datetime.dotiw.two_words_connector')} "
end
end
42 changes: 42 additions & 0 deletions app/services/ip_geocoder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class IpGeocoder
def initialize(ip)
@ip = ip
end

def location
geocoded_location&.language = I18n.locale

return city_and_state if both_city_and_state_present?
return country if country.present?

I18n.t('account.index.unknown_location')
end

private

attr_reader :ip

def city_and_state
"#{city}, #{state}"
end

def both_city_and_state_present?
city.present? && state.present?
end

def city
geocoded_location&.city
end

def state
geocoded_location&.state_code
end

def country
geocoded_location&.country
end

def geocoded_location
@geocoded_location ||= Geocoder.search(ip).first
end
end
2 changes: 1 addition & 1 deletion app/views/accounts/_account_item.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
.col.col-4.right-align
- if local_assigns.key? :path
= render action, path: path, name: name
- else
- elsif local_assigns.key? :action
= render action
6 changes: 6 additions & 0 deletions app/views/accounts/_current_sign_in.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.p2.clearfix.border-top
.clearfix.mxn1
.sm-col.sm-col-6.px1
.bold = presenter.current_sign_in_location_and_ip
.sm-col.sm-col-6.px1.sm-right-align
= presenter.current_timestamp
6 changes: 6 additions & 0 deletions app/views/accounts/_last_sign_in.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.p2.clearfix.border-top
.clearfix.mxn1
.sm-col.sm-col-6.px1
.bold = presenter.last_sign_in_location_and_ip
.sm-col.sm-col-6.px1.sm-right-align
= presenter.last_timestamp
7 changes: 7 additions & 0 deletions app/views/accounts/show.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ h1.hide = t('titles.account')
- @view_model.recent_events.each do |event|
= render event.event_partial, event: event

.mb3.profile-info-box
.bg-lightest-blue.pb1.pt1.px2.h6.caps.clearfix
= t('headings.account.login_history')
= image_tag asset_url('history.svg'), width: 12, class: 'ml1'
= render 'accounts/current_sign_in', presenter: @login_presenter
= render 'accounts/last_sign_in', presenter: @login_presenter

.mb3.profile-info-box
.bg-lightest-blue.pb1.pt1.px2.h6.caps.clearfix
= t('headings.account.account_management')
Expand Down
5 changes: 5 additions & 0 deletions bin/setup
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Dir.chdir APP_ROOT do
run "test -L certs/saml.crt || cp certs/saml.crt.example certs/saml.crt"
run "test -L certs/saml2018.crt || cp certs/saml2018.crt.example certs/saml2018.crt"

puts "== Copying GeoLite2 City database =="
run "test -L geo_data/GeoLite2-City.mmdb || mkdir geo_data && cd geo_data && " \
"curl http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz | tar xvz"
run "mv geo_data/GeoLite2-City_20181120/GeoLite2-City.mmdb geo_data/GeoLite2-City.mmdb"

if ARGV.shift == "--docker" then
run 'docker-compose build'
run 'docker-compose run --rm web yarn install'
Expand Down
1 change: 1 addition & 0 deletions config/i18n-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ ignore_unused:
- 'devise.mailer.confirmation_instructions.subject'
- 'devise.mailer.reset_password_instructions.subject'
- 'devise.sessions.signed_in'
- 'datetime.dotiw.two_words_connector'
- 'service_providers.*'
- 'two_factor_authentication.invalid_otp'
- 'two_factor_authentication.invalid_personal_key'
Expand Down
6 changes: 6 additions & 0 deletions config/initializers/geocoder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Geocoder.configure(
ip_lookup: :geoip2,
geoip2: {
file: Rails.root.join('geo_data', 'GeoLite2-City.mmdb'),
}
)
3 changes: 3 additions & 0 deletions config/locales/account/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ en:
reactivation:
instructions: Your profile was recently deactivated due to a password reset.
link: Reactivate your profile now.
sign_in_location_and_ip: 'From %{location} (IP address: %{ip})'
sign_in_timestamp: "%{timestamp} ago"
ssn: Social Security Number
unknown_location: unknown location
verification:
instructions: Your account requires a secret code to be verified.
reactivate_button: Enter the code you received via US mail
Expand Down
3 changes: 3 additions & 0 deletions config/locales/account/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ es:
reactivation:
instructions: Su perfil ha sido desactivado debido a un cambio de contraseña.
link: Reactive su perfil ahora.
sign_in_location_and_ip: 'Desde %{location} (Dirección IP: %{ip})'
sign_in_timestamp: Hace %{timestamp}
ssn: Número de Seguro Social
unknown_location: ubicación desconocida
verification:
instructions: Su cuenta requiere que un código secreto sea verificado.
reactivate_button: Ingrese el código que recibió por correo postal.
Expand Down
3 changes: 3 additions & 0 deletions config/locales/account/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ fr:
de mot passe. Vous pouvez utiliser votre clé personnelle pour réactiver
votre profil.
link: Réactivez votre profil maintenant.
sign_in_location_and_ip: "%{location} (Adresse IP: %{ip})"
sign_in_timestamp: Il y a %{timestamp}
ssn: Numéro d'assurance sociale
unknown_location: lieu inconnu
verification:
instructions: Votre compte requiert la vérification d'un code secret.
reactivate_button: Entrez le code que vous avez reçu par la poste
Expand Down
5 changes: 5 additions & 0 deletions config/locales/dotiw/en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
en:
datetime:
dotiw:
two_words_connector: and
5 changes: 5 additions & 0 deletions config/locales/dotiw/es.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
es:
datetime:
dotiw:
two_words_connector: y
5 changes: 5 additions & 0 deletions config/locales/dotiw/fr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fr:
datetime:
dotiw:
two_words_connector: et
1 change: 1 addition & 0 deletions config/locales/headings/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ en:
account_history: Account history
account_management: Account Management
connected_apps: Applications
login_history: Login history
login_info: Your account
profile_info: Profile information
reactivate: Reactivate your account
Expand Down
1 change: 1 addition & 0 deletions config/locales/headings/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ es:
account_history: Historial de cuenta
account_management: Manejo de cuenta
connected_apps: Aplicaciones
login_history: Historial de sesión
login_info: Su cuenta
profile_info: Información de perfil
reactivate: Reactive su cuenta
Expand Down
1 change: 1 addition & 0 deletions config/locales/headings/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ fr:
account_history: Historique du compte
account_management: Gestion de compte
connected_apps: Applications
login_history: Historique de connexion
login_info: Votre compte
profile_info: Information du profil
reactivate: Réactivez votre compte
Expand Down
39 changes: 38 additions & 1 deletion lib/deploy/activate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,49 @@ def initialize(logger: default_logger, s3_client: nil)
end

def run
download_application_yml_from_s3
deep_merge_s3_data_with_example_application_yml
set_proper_file_permissions_for_application_yml

download_geocoding_database_from_s3
set_proper_file_permissions_for_geolocation_db
end

private

def download_application_yml_from_s3
LoginGov::Hostdata.s3(logger: logger, s3_client: s3_client).download_configs(
'/%<env>s/idp/v1/application.yml' => env_yaml_path
)
end

def deep_merge_s3_data_with_example_application_yml
File.open(result_yaml_path, 'w') { |file| file.puts YAML.dump(application_config) }
end

def set_proper_file_permissions_for_application_yml
FileUtils.chmod(0o640, [env_yaml_path, result_yaml_path])
end

private
def download_geocoding_database_from_s3
ec2_region = ec2_data.region

LoginGov::Hostdata::S3.new(
bucket: "login-gov.secrets.#{ec2_data.account_id}-#{ec2_region}",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'll have to make this bucket exists in both the sandbox and prod account

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It was in the sandbox account's bucket. I just uploaded it to the prod account.

env: nil,
region: ec2_region,
logger: logger,
s3_client: s3_client
).download_configs('/common/GeoLite2-City.mmdb' => geolocation_db_path)
end

def ec2_data
@ec2_data ||= LoginGov::Hostdata::EC2.load
end

def set_proper_file_permissions_for_geolocation_db
FileUtils.chmod(0o640, geolocation_db_path)
end

def default_logger
logger = Logger.new(STDOUT)
Expand All @@ -49,5 +82,9 @@ def example_application_yaml_path
def result_yaml_path
File.join(root, 'config/application.yml')
end

def geolocation_db_path
File.join(root, 'geo_data/GeoLite2-City.mmdb')
end
end
end
4 changes: 4 additions & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,8 @@
example.run
Capybara.use_default_driver
end

config.before(:each, type: :feature) do
allow_any_instance_of(Geocoder::Result::Test).to receive(:language=)
end
end
Loading