authtrail 0.2.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f88e2c20a95601d9da18766155a4eb0300ac193d7be16c56b8f293e42237163b
4
- data.tar.gz: a74f7435a461fce5c2f2c12d98cc3329aeb6f45c6e54eac6192cd5d04dd1a857
3
+ metadata.gz: 22c5cb73374854a16a8581aad0618cf66033a7346784b16213ed58bb74d56b6b
4
+ data.tar.gz: 6666d786ba53acf7169c39e02eefcfdc7ae4211f26482bb47f142ca31d22885c
5
5
  SHA512:
6
- metadata.gz: 3a572225e8e080da90c400293ebccbb6a7808f642f2469b79e0777ed37470ccec13ebd31a6f6b57d4fc6b89d037f25c1ae1980298b68b04bdc258ff71037e578
7
- data.tar.gz: 6558512fa9aa0b95932a95165c79533672f91c612116666cec6afe2743fcb7cd8357e9b230dadcab6ce503d8d8e622566c0d188d9b92cb016c37de5b46e3a09e
6
+ metadata.gz: 6c429f790573ad4814b6c92eeff96cfc03b9b283046c5e1c23c425346674071792607c2f1c2f762ab588acaeb70badab8dafccaea38cc92c0c6944f63b7079fa
7
+ data.tar.gz: 9bfd5a70268d39c89f9792b3f3b6435935b55c11f201d690991053067e456b214463f912adce3dc879cb39f515322139a9cb690d7ca89ee98debb1640150d32d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 0.4.1 (2021-08-14)
2
+
3
+ - Improved error message when `geocoder` gem not installed
4
+
5
+ ## 0.4.0 (2021-08-13)
6
+
7
+ - Disabled geocoding by default
8
+ - Made the `geocoder` gem an optional dependency
9
+ - Added `country_code` to geocoding
10
+
11
+ ## 0.3.1 (2021-03-03)
12
+
13
+ - Added `--lockbox` option to install generator
14
+
15
+ ## 0.3.0 (2021-03-01)
16
+
17
+ - Disabled geocoding by default for new installations
18
+ - Raise an exception instead of logging when auditing fails
19
+ - Removed support for Rails < 5.2 and Ruby < 2.6
20
+
1
21
  ## 0.2.2 (2020-11-21)
2
22
 
3
23
  - Added `transform_method` option
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017-2020 Andrew Kane
1
+ Copyright (c) 2017-2021 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Track Devise login activity
4
4
 
5
+ **AuthTrail 0.4.0 was recently released** - see [how to upgrade](#upgrading)
6
+
5
7
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
6
8
 
7
9
  [![Build Status](https://github.com/ankane/authtrail/workflows/build/badge.svg?branch=master)](https://github.com/ankane/authtrail/actions)
@@ -14,13 +16,22 @@ Add this line to your application’s Gemfile:
14
16
  gem 'authtrail'
15
17
  ```
16
18
 
17
- And run:
19
+ To encrypt email and IP addresses, install [Lockbox](https://github.com/ankane/lockbox) and [Blind Index](https://github.com/ankane/blind_index) and run:
20
+
21
+ ```sh
22
+ rails generate authtrail:install --lockbox
23
+ rails db:migrate
24
+ ```
25
+
26
+ If you prefer not to encrypt data, run:
18
27
 
19
28
  ```sh
20
29
  rails generate authtrail:install
21
30
  rails db:migrate
22
31
  ```
23
32
 
33
+ To enable geocoding, see the [Geocoding section](#geocoding).
34
+
24
35
  ## How It Works
25
36
 
26
37
  A `LoginActivity` record is created every time a user tries to login. You can then use this information to detect suspicious behavior. Data includes:
@@ -47,7 +58,7 @@ AuthTrail.exclude_method = lambda do |data|
47
58
  end
48
59
  ```
49
60
 
50
- Add or modify data (also add new fields to the `login_activities` table)
61
+ Add or modify data - also add new fields to the `login_activities` table if needed
51
62
 
52
63
  ```ruby
53
64
  AuthTrail.transform_method = lambda do |data, request|
@@ -95,50 +106,83 @@ The `LoginActivity` model uses a [polymorphic association](https://guides.rubyon
95
106
 
96
107
  ## Geocoding
97
108
 
98
- IP geocoding is performed in a background job so it doesn’t slow down web requests. You can disable it entirely with:
109
+ AuthTrail uses [Geocoder](https://github.com/alexreisner/geocoder) for geocoding. We recommend configuring [local geocoding](#local-geocoding) or [load balancer geocoding](#load-balancer-geocoding) so IP addresses are not sent to a 3rd party service. If you do use a 3rd party service and adhere to GDPR, be sure to add it to your subprocessor list.
110
+
111
+ To enable geocoding, add this line to your application’s Gemfile:
99
112
 
100
113
  ```ruby
101
- AuthTrail.geocode = false
114
+ gem 'geocoder'
102
115
  ```
103
116
 
104
- Set job queue for geocoding
117
+ And update `config/initializers/authtrail.rb`:
105
118
 
106
119
  ```ruby
107
- AuthTrail.job_queue = :low_priority
120
+ AuthTrail.geocode = true
108
121
  ```
109
122
 
110
- ### Geocoding Performance
123
+ Geocoding is performed in a background job so it doesn’t slow down web requests. Set the job queue with:
111
124
 
112
- To avoid calls to a remote API, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geoip2/geolite2/) and configure Geocoder to use it.
125
+ ```ruby
126
+ AuthTrail.job_queue = :low_priority
127
+ ```
113
128
 
114
- Add this line to your application’s Gemfile:
129
+ ### Local Geocoding
130
+
131
+ For privacy and performance, we recommend geocoding locally. Add this line to your application’s Gemfile:
115
132
 
116
133
  ```ruby
117
134
  gem 'maxminddb'
118
135
  ```
119
136
 
120
- And create an initializer at `config/initializers/geocoder.rb` with:
137
+ For city-level geocoding, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geoip2/geolite2/) and create `config/initializers/geocoder.rb` with:
121
138
 
122
139
  ```ruby
123
140
  Geocoder.configure(
124
141
  ip_lookup: :geoip2,
125
142
  geoip2: {
126
- file: Rails.root.join("lib", "GeoLite2-City.mmdb")
143
+ file: "path/to/GeoLite2-City.mmdb"
144
+ }
145
+ )
146
+ ```
147
+
148
+ For country-level geocoding, install the `geoip-database` package. It’s preinstalled on Heroku. For Ubuntu, use:
149
+
150
+ ```sh
151
+ sudo apt-get install geoip-database
152
+ ```
153
+
154
+ And create `config/initializers/geocoder.rb` with:
155
+
156
+ ```ruby
157
+ Geocoder.configure(
158
+ ip_lookup: :maxmind_local,
159
+ maxmind_local: {
160
+ file: "/usr/share/GeoIP/GeoIP.dat",
161
+ package: :country
127
162
  }
128
163
  )
129
164
  ```
130
165
 
131
- ## Data Protection
166
+ ### Load Balancer Geocoding
167
+
168
+ Some load balancers can add geocoding information to request headers.
132
169
 
133
- Protect the privacy of your users by encrypting fields that contain personal data, such as `identity` and `ip`. [Lockbox](https://github.com/ankane/lockbox) is great for this. Use [Blind Index](https://github.com/ankane/blind_index) so you can still query the fields.
170
+ - [nginx](https://nginx.org/en/docs/http/ngx_http_geoip_module.html)
171
+ - [Google Cloud](https://cloud.google.com/load-balancing/docs/custom-headers)
172
+ - [Cloudflare](https://support.cloudflare.com/hc/en-us/articles/200168236-Configuring-Cloudflare-IP-Geolocation)
134
173
 
135
174
  ```ruby
136
- class LoginActivity < ApplicationRecord
137
- encrypts :identity, :ip
138
- blind_index :identity, :ip
175
+ AuthTrail.geocode = false
176
+
177
+ AuthTrail.transform_method = lambda do |data, request|
178
+ data[:country] = request.headers["<country-header>"]
179
+ data[:region] = request.headers["<region-header>"]
180
+ data[:city] = request.headers["<city-header>"]
139
181
  end
140
182
  ```
141
183
 
184
+ Check out [this example](https://github.com/ankane/authtrail/issues/40)
185
+
142
186
  ## Other Notes
143
187
 
144
188
  We recommend using this in addition to Devise’s `Lockable` module and [Rack::Attack](https://github.com/kickstarter/rack-attack).
@@ -147,6 +191,18 @@ Check out [Hardening Devise](https://ankane.org/hardening-devise) and [Secure Ra
147
191
 
148
192
  ## Upgrading
149
193
 
194
+ ### 0.4.0
195
+
196
+ There are two notable changes to geocoding:
197
+
198
+ 1. Geocoding is now disabled by default (this was already the case for new installations with 0.3.0+). Check out the instructions for [how to enable it](#geocoding) (you may need to create `config/initializers/authtrail.rb`).
199
+
200
+ 2. The `geocoder` gem is now an optional dependency. To use geocoding, add it to your Gemfile:
201
+
202
+ ```ruby
203
+ gem 'geocoder'
204
+ ```
205
+
150
206
  ### 0.2.0
151
207
 
152
208
  To store latitude and longitude, create a migration with:
@@ -8,6 +8,8 @@ module AuthTrail
8
8
  result =
9
9
  begin
10
10
  Geocoder.search(login_activity.ip).first
11
+ rescue NameError
12
+ raise "Add the geocoder gem to your Gemfile to use geocoding"
11
13
  rescue => e
12
14
  Rails.logger.info "Geocode failed: #{e.message}"
13
15
  nil
@@ -18,6 +20,7 @@ module AuthTrail
18
20
  city: result.try(:city),
19
21
  region: result.try(:state),
20
22
  country: result.try(:country),
23
+ country_code: result.try(:country_code),
21
24
  latitude: result.try(:latitude),
22
25
  longitude: result.try(:longitude)
23
26
  }
@@ -2,34 +2,29 @@ module AuthTrail
2
2
  module Manager
3
3
  class << self
4
4
  def after_set_user(user, auth, opts)
5
- # do not raise an exception for tracking
6
- AuthTrail.safely do
7
- request = ActionDispatch::Request.new(auth.env)
5
+ request = ActionDispatch::Request.new(auth.env)
8
6
 
9
- AuthTrail.track(
10
- strategy: detect_strategy(auth),
11
- scope: opts[:scope].to_s,
12
- identity: AuthTrail.identity_method.call(request, opts, user),
13
- success: true,
14
- request: request,
15
- user: user
16
- )
17
- end
7
+ AuthTrail.track(
8
+ strategy: detect_strategy(auth),
9
+ scope: opts[:scope].to_s,
10
+ identity: AuthTrail.identity_method.call(request, opts, user),
11
+ success: true,
12
+ request: request,
13
+ user: user
14
+ )
18
15
  end
19
16
 
20
17
  def before_failure(env, opts)
21
- AuthTrail.safely do
22
- request = ActionDispatch::Request.new(env)
18
+ request = ActionDispatch::Request.new(env)
23
19
 
24
- AuthTrail.track(
25
- strategy: detect_strategy(env["warden"]),
26
- scope: opts[:scope].to_s,
27
- identity: AuthTrail.identity_method.call(request, opts, nil),
28
- success: false,
29
- request: request,
30
- failure_reason: opts[:message].to_s
31
- )
32
- end
20
+ AuthTrail.track(
21
+ strategy: detect_strategy(env["warden"]),
22
+ scope: opts[:scope].to_s,
23
+ identity: AuthTrail.identity_method.call(request, opts, nil),
24
+ success: false,
25
+ request: request,
26
+ failure_reason: opts[:message].to_s
27
+ )
33
28
  end
34
29
 
35
30
  private
@@ -1,3 +1,3 @@
1
1
  module AuthTrail
2
- VERSION = "0.2.2"
2
+ VERSION = "0.4.1"
3
3
  end
data/lib/authtrail.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # dependencies
2
- require "geocoder"
3
2
  require "warden"
4
3
 
5
4
  # modules
@@ -11,7 +10,7 @@ module AuthTrail
11
10
  class << self
12
11
  attr_accessor :exclude_method, :geocode, :track_method, :identity_method, :job_queue, :transform_method
13
12
  end
14
- self.geocode = true
13
+ self.geocode = false
15
14
  self.identity_method = lambda do |request, opts, user|
16
15
  if user
17
16
  user.try(:email)
@@ -6,17 +6,47 @@ module Authtrail
6
6
  include ActiveRecord::Generators::Migration
7
7
  source_root File.join(__dir__, "templates")
8
8
 
9
+ class_option :lockbox, type: :boolean
10
+
9
11
  def copy_migration
10
12
  migration_template "login_activities_migration.rb", "db/migrate/create_login_activities.rb", migration_version: migration_version
11
13
  end
12
14
 
15
+ def copy_templates
16
+ template "initializer.rb", "config/initializers/authtrail.rb"
17
+ end
18
+
13
19
  def generate_model
14
- template "login_activity_model.rb", "app/models/login_activity.rb"
20
+ if lockbox?
21
+ template "model_lockbox.rb", "app/models/login_activity.rb"
22
+ else
23
+ template "model.rb", "app/models/login_activity.rb"
24
+ end
15
25
  end
16
26
 
17
27
  def migration_version
18
28
  "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
19
29
  end
30
+
31
+ def identity_column
32
+ if lockbox?
33
+ "t.text :identity_ciphertext\n t.string :identity_bidx, index: true"
34
+ else
35
+ "t.string :identity, index: true"
36
+ end
37
+ end
38
+
39
+ def ip_column
40
+ if lockbox?
41
+ "t.text :ip_ciphertext\n t.string :ip_bidx, index: true"
42
+ else
43
+ "t.string :ip, index: true"
44
+ end
45
+ end
46
+
47
+ def lockbox?
48
+ options[:lockbox]
49
+ end
20
50
  end
21
51
  end
22
52
  end
@@ -0,0 +1,14 @@
1
+ # set to true for geocoding (and add the geocoder gem to your Gemfile)
2
+ # we recommend configuring local geocoding as well
3
+ # see https://github.com/ankane/authtrail#geocoding
4
+ AuthTrail.geocode = false
5
+
6
+ # add or modify data
7
+ # AuthTrail.transform_method = lambda do |data, request|
8
+ # data[:request_id] = request.request_id
9
+ # end
10
+
11
+ # exclude certain attempts from tracking
12
+ # AuthTrail.exclude_method = lambda do |data|
13
+ # data[:identity] == "capybara@example.org"
14
+ # end
@@ -3,12 +3,12 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
3
3
  create_table :login_activities do |t|
4
4
  t.string :scope
5
5
  t.string :strategy
6
- t.string :identity
6
+ <%= identity_column %>
7
7
  t.boolean :success
8
8
  t.string :failure_reason
9
9
  t.references :user, polymorphic: true
10
10
  t.string :context
11
- t.string :ip
11
+ <%= ip_column %>
12
12
  t.text :user_agent
13
13
  t.text :referrer
14
14
  t.string :city
@@ -18,8 +18,5 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
18
18
  t.float :longitude
19
19
  t.datetime :created_at
20
20
  end
21
-
22
- add_index :login_activities, :identity
23
- add_index :login_activities, :ip
24
21
  end
25
22
  end
@@ -0,0 +1,14 @@
1
+ class LoginActivity < ApplicationRecord
2
+ belongs_to :user, polymorphic: true, optional: true
3
+
4
+ encrypts :identity, :ip
5
+ blind_index :identity, :ip
6
+
7
+ before_save :reduce_precision
8
+
9
+ # reduce precision to city level to protect IP
10
+ def reduce_precision
11
+ self.latitude = latitude&.round(1) if try(:latitude_changed?)
12
+ self.longitude = longitude&.round(1) if try(:longitude_changed?)
13
+ end
14
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authtrail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-22 00:00:00.000000000 Z
11
+ date: 2021-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5'
33
+ version: '5.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5'
40
+ version: '5.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: warden
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,120 +52,8 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: geocoder
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: bundler
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: minitest
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '5'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '5'
111
- - !ruby/object:Gem::Dependency
112
- name: combustion
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: rails
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: sqlite3
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: devise
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
55
  description:
168
- email: andrew@chartkick.com
56
+ email: andrew@ankane.org
169
57
  executables: []
170
58
  extensions: []
171
59
  extra_rdoc_files: []
@@ -179,8 +67,10 @@ files:
179
67
  - lib/auth_trail/version.rb
180
68
  - lib/authtrail.rb
181
69
  - lib/generators/authtrail/install_generator.rb
70
+ - lib/generators/authtrail/templates/initializer.rb.tt
182
71
  - lib/generators/authtrail/templates/login_activities_migration.rb.tt
183
- - lib/generators/authtrail/templates/login_activity_model.rb.tt
72
+ - lib/generators/authtrail/templates/model.rb.tt
73
+ - lib/generators/authtrail/templates/model_lockbox.rb.tt
184
74
  homepage: https://github.com/ankane/authtrail
185
75
  licenses:
186
76
  - MIT
@@ -193,14 +83,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
83
  requirements:
194
84
  - - ">="
195
85
  - !ruby/object:Gem::Version
196
- version: '2.4'
86
+ version: '2.6'
197
87
  required_rubygems_version: !ruby/object:Gem::Requirement
198
88
  requirements:
199
89
  - - ">="
200
90
  - !ruby/object:Gem::Version
201
91
  version: '0'
202
92
  requirements: []
203
- rubygems_version: 3.1.4
93
+ rubygems_version: 3.2.22
204
94
  signing_key:
205
95
  specification_version: 4
206
96
  summary: Track Devise login activity