castle_devise 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c13e653cf7d00df4cdb2436431a031be5c4120d5e667ff2c5feb00effd374011
4
+ data.tar.gz: 8c4c4fafd623cda48e9f9c125c81cda8071ae81e7ec62cad74bc0476bd89c4ea
5
+ SHA512:
6
+ metadata.gz: 3066ab56d17f1d3097f4aecaa115162e0e9263016cd4dce602a0491d5071ad5fcb933871d6830147303ca859cd60e5c503cdd30dc23112e0b94c0337e4cabee2
7
+ data.tar.gz: b8c18958cc92790e26e4e222c8c7f34f4db8be72c1c55d54225ca23dd427bcad7082c1efe7208655dbe43c3d31840fbc4875797e53d7b074eef88e21d1a7d118
@@ -0,0 +1,18 @@
1
+ name: Lint
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ standardrb:
7
+ name: runner / standardrb
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Check out code
11
+ uses: actions/checkout@v1
12
+ - name: standardrb
13
+ uses: SennaLabs/action-standardrb@v0.0.3
14
+ with:
15
+ github_token: ${{ secrets.github_token }}
16
+ reporter: github-pr-review # Default is github-pr-check
17
+ rubocop_version: 1.1.1 # note: this actually refers to standardb version, not Rubocop
18
+ rubocop_flags: --format progress
@@ -0,0 +1,25 @@
1
+ name: Specs
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ environment: tests
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - name: Set up Ruby
12
+ uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: 2.6.5
15
+ - name: Run specs
16
+ run: |
17
+ gem install bundler -v 2.2.19
18
+ bundle install
19
+ bundle exec rake
20
+ env:
21
+ CASTLE_API_SECRET: ${{ secrets.CASTLE_API_SECRET }}
22
+ - name: Simplecov Report
23
+ uses: aki77/simplecov-report-action@v1
24
+ with:
25
+ token: ${{ secrets.GITHUB_TOKEN }}
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ # IntelliJ
14
+ .idea/
15
+
16
+ vendor/
17
+
18
+ spec/dummy_app/log/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-06-11
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in castle-devise.gemspec
6
+ gemspec
7
+
8
+ gem "activerecord"
9
+ gem "railties", "~> 5.2"
10
+ gem "rake"
11
+ gem "rspec"
12
+ gem "rspec-rails"
13
+ gem "simplecov"
14
+ gem "standard"
15
+ gem "sqlite3"
16
+ gem "vcr"
17
+ gem "webmock"
data/Gemfile.lock ADDED
@@ -0,0 +1,169 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ castle_devise (0.1.0)
5
+ activesupport (>= 5.0)
6
+ castle-rb (>= 7.0, < 8.0)
7
+ devise (>= 4.3.0, < 5.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionpack (5.2.6)
13
+ actionview (= 5.2.6)
14
+ activesupport (= 5.2.6)
15
+ rack (~> 2.0, >= 2.0.8)
16
+ rack-test (>= 0.6.3)
17
+ rails-dom-testing (~> 2.0)
18
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
19
+ actionview (5.2.6)
20
+ activesupport (= 5.2.6)
21
+ builder (~> 3.1)
22
+ erubi (~> 1.4)
23
+ rails-dom-testing (~> 2.0)
24
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
25
+ activemodel (5.2.6)
26
+ activesupport (= 5.2.6)
27
+ activerecord (5.2.6)
28
+ activemodel (= 5.2.6)
29
+ activesupport (= 5.2.6)
30
+ arel (>= 9.0)
31
+ activesupport (5.2.6)
32
+ concurrent-ruby (~> 1.0, >= 1.0.2)
33
+ i18n (>= 0.7, < 2)
34
+ minitest (~> 5.1)
35
+ tzinfo (~> 1.1)
36
+ addressable (2.7.0)
37
+ public_suffix (>= 2.0.2, < 5.0)
38
+ arel (9.0.0)
39
+ ast (2.4.2)
40
+ bcrypt (3.1.16)
41
+ builder (3.2.4)
42
+ castle-rb (7.1.1)
43
+ concurrent-ruby (1.1.9)
44
+ crack (0.4.5)
45
+ rexml
46
+ crass (1.0.6)
47
+ devise (4.8.0)
48
+ bcrypt (~> 3.0)
49
+ orm_adapter (~> 0.1)
50
+ railties (>= 4.1.0)
51
+ responders
52
+ warden (~> 1.2.3)
53
+ diff-lcs (1.4.4)
54
+ docile (1.4.0)
55
+ erubi (1.10.0)
56
+ hashdiff (1.0.1)
57
+ i18n (1.8.10)
58
+ concurrent-ruby (~> 1.0)
59
+ loofah (2.10.0)
60
+ crass (~> 1.0.2)
61
+ nokogiri (>= 1.5.9)
62
+ method_source (1.0.0)
63
+ minitest (5.14.4)
64
+ nokogiri (1.11.7-x86_64-darwin)
65
+ racc (~> 1.4)
66
+ orm_adapter (0.5.0)
67
+ parallel (1.20.1)
68
+ parser (3.0.1.1)
69
+ ast (~> 2.4.1)
70
+ public_suffix (4.0.6)
71
+ racc (1.5.2)
72
+ rack (2.2.3)
73
+ rack-test (1.1.0)
74
+ rack (>= 1.0, < 3)
75
+ rails-dom-testing (2.0.3)
76
+ activesupport (>= 4.2.0)
77
+ nokogiri (>= 1.6)
78
+ rails-html-sanitizer (1.3.0)
79
+ loofah (~> 2.3)
80
+ railties (5.2.6)
81
+ actionpack (= 5.2.6)
82
+ activesupport (= 5.2.6)
83
+ method_source
84
+ rake (>= 0.8.7)
85
+ thor (>= 0.19.0, < 2.0)
86
+ rainbow (3.0.0)
87
+ rake (13.0.3)
88
+ regexp_parser (2.1.1)
89
+ responders (3.0.1)
90
+ actionpack (>= 5.0)
91
+ railties (>= 5.0)
92
+ rexml (3.2.5)
93
+ rspec (3.10.0)
94
+ rspec-core (~> 3.10.0)
95
+ rspec-expectations (~> 3.10.0)
96
+ rspec-mocks (~> 3.10.0)
97
+ rspec-core (3.10.1)
98
+ rspec-support (~> 3.10.0)
99
+ rspec-expectations (3.10.1)
100
+ diff-lcs (>= 1.2.0, < 2.0)
101
+ rspec-support (~> 3.10.0)
102
+ rspec-mocks (3.10.2)
103
+ diff-lcs (>= 1.2.0, < 2.0)
104
+ rspec-support (~> 3.10.0)
105
+ rspec-rails (5.0.1)
106
+ actionpack (>= 5.2)
107
+ activesupport (>= 5.2)
108
+ railties (>= 5.2)
109
+ rspec-core (~> 3.10)
110
+ rspec-expectations (~> 3.10)
111
+ rspec-mocks (~> 3.10)
112
+ rspec-support (~> 3.10)
113
+ rspec-support (3.10.2)
114
+ rubocop (1.14.0)
115
+ parallel (~> 1.10)
116
+ parser (>= 3.0.0.0)
117
+ rainbow (>= 2.2.2, < 4.0)
118
+ regexp_parser (>= 1.8, < 3.0)
119
+ rexml
120
+ rubocop-ast (>= 1.5.0, < 2.0)
121
+ ruby-progressbar (~> 1.7)
122
+ unicode-display_width (>= 1.4.0, < 3.0)
123
+ rubocop-ast (1.7.0)
124
+ parser (>= 3.0.1.1)
125
+ rubocop-performance (1.11.2)
126
+ rubocop (>= 1.7.0, < 2.0)
127
+ rubocop-ast (>= 0.4.0)
128
+ ruby-progressbar (1.11.0)
129
+ simplecov (0.21.2)
130
+ docile (~> 1.1)
131
+ simplecov-html (~> 0.11)
132
+ simplecov_json_formatter (~> 0.1)
133
+ simplecov-html (0.12.3)
134
+ simplecov_json_formatter (0.1.3)
135
+ sqlite3 (1.4.2)
136
+ standard (1.1.1)
137
+ rubocop (= 1.14.0)
138
+ rubocop-performance (= 1.11.2)
139
+ thor (1.1.0)
140
+ thread_safe (0.3.6)
141
+ tzinfo (1.2.9)
142
+ thread_safe (~> 0.1)
143
+ unicode-display_width (2.0.0)
144
+ vcr (6.0.0)
145
+ warden (1.2.9)
146
+ rack (>= 2.0.9)
147
+ webmock (3.13.0)
148
+ addressable (>= 2.3.6)
149
+ crack (>= 0.3.2)
150
+ hashdiff (>= 0.4.0, < 2.0.0)
151
+
152
+ PLATFORMS
153
+ x86_64-darwin-18
154
+
155
+ DEPENDENCIES
156
+ activerecord
157
+ castle_devise!
158
+ railties (~> 5.2)
159
+ rake
160
+ rspec
161
+ rspec-rails
162
+ simplecov
163
+ sqlite3
164
+ standard
165
+ vcr
166
+ webmock
167
+
168
+ BUNDLED WITH
169
+ 2.2.15
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Castle
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,197 @@
1
+ **Disclaimer:** CastleDevise is currently in beta. There might be some upcoming breaking changes to the gem before we stabilize the API.
2
+
3
+ ---
4
+
5
+ # CastleDevice
6
+
7
+ CastleDevise is a [Devise](https://github.com/heartcombo/devise) plugin that integrates [Castle](https://castle.io).
8
+
9
+ It currently provides the following features:
10
+ - preventing bots from registration attacks using Castle's [Filter API](https://docs.castle.io/v1/reference/api-reference/#filter)
11
+ - preventing ATO attacks using Castle's [Risk API](https://docs.castle.io/v1/reference/api-reference/#risk)
12
+
13
+ If you want to learn about all capabilities of Castle, please take a look at [our documentation](https://docs.castle.io/).
14
+
15
+ ## Installation
16
+
17
+ Include `castle_devise` in your Gemfile:
18
+
19
+ ```ruby
20
+ gem 'castle_devise'
21
+ ```
22
+
23
+ Create `config/initializers/castle_devise.rb` and fill in your API secret and APP_ID from the [Castle Dashboard](https://dashboard.castle.io/settings/general)
24
+
25
+ ```ruby
26
+ CastleDevise.configure do |config|
27
+ config.api_secret = ENV.fetch('CASTLE_API_SECRET')
28
+ config.app_id = ENV.fetch('CASTLE_APP_ID')
29
+
30
+ # When monitoring mode is enabled, CastleDevise sends
31
+ # requests to Castle but it doesn't act on the "deny" verdicts.
32
+ #
33
+ # This is useful when you want to check out how Castle scores
34
+ # your traffic without blocking any of your users.
35
+ #
36
+ # Once you are ready to use Castle as your security provider,
37
+ # you can set monitoring_mode to false.
38
+ config.monitoring_mode = true
39
+ end
40
+ ```
41
+
42
+ Add `:castle_protectable` Devise module to your User model:
43
+
44
+ ```ruby
45
+ class User < ApplicationRecord
46
+ devise :database_authenticatable, :registerable,
47
+ :recoverable, :rememberable, :validatable,
48
+ :castle_protectable # <--- add this
49
+ end
50
+ ```
51
+
52
+ Add an additional translation to your `config/locales/devise.en.yml`:
53
+
54
+ ```yml
55
+ en:
56
+ devise:
57
+ registrations:
58
+ blocked_by_castle: "Account cannot be created at this moment. Please try again later."
59
+ ```
60
+
61
+ (See [devise.en.yml in our specs](spec/dummy_app/config/locales/devise.en.yml#L40))
62
+
63
+ #### Further steps if you're not using Webpacker
64
+
65
+ Include Castle's c.js script in the head section of your layout:
66
+
67
+ ```ruby
68
+ <%= castle_javascript_tag %>
69
+ ```
70
+
71
+ Add the following tag to the the `<form>` tag in both `devise/registrations/new.html.erb` and `devise/sessions/new.html.erb` (if you haven't generated them yet, run `rails generate devise:views`):
72
+
73
+ ```ruby
74
+ <%= form_for @user do |f| %>
75
+
76
+ <%= castle_request_token %>
77
+
78
+ <% end %>
79
+ ```
80
+
81
+ You're set! Now verify that everything works by logging in to your application as any user. You should be able to see that User on the [Castle Users Page](https://dashboard.castle.io/users)
82
+
83
+
84
+ #### Further steps if you're using Webpacker
85
+
86
+ Add `castle.js` to your package.json file.
87
+
88
+ TODO: fill this in.
89
+
90
+
91
+ ## How-Tos
92
+
93
+ ### Customize the login flow
94
+
95
+ #### Do something after Castle denies a login
96
+
97
+ We aim to provide sensible defaults, which means that when Castle denies a login, your application
98
+ will behave as if the User has not been authenticated. You might still want to log such an event,
99
+ and you can do this in a Warden hook:
100
+
101
+ ```ruby
102
+ Warden::Manager.before_failure do |env, opts|
103
+ # The raw Castle response if a request to Castle has been made
104
+ castle_response = env["castle_devise.risk_response"]
105
+ # CastleDevise::Context, if a request to Castle has been made
106
+ castle_context = env["castle_devise.risk_context"]
107
+
108
+ if castle_response&.dig(:policy, :action) == "deny"
109
+ # auth failed because Castle denied
110
+ end
111
+ end
112
+
113
+ ```
114
+
115
+ #### Implement your own challenge flow or do something after an "allow" action
116
+
117
+ In your `SessionsController`:
118
+
119
+ ```ruby
120
+ class SessionsController < Devise::SessionsController
121
+ def create
122
+ super do |resource|
123
+ if castle_challenge?
124
+ # At this point a User is already authenticated, you might want so sign out:
125
+ sign_out(resource)
126
+ # .... write your own MFA flow
127
+ # You can call #castle_risk_response to access Castle response
128
+ # see https://docs.castle.io/v1/reference/api-reference/#risk for details
129
+
130
+ # Fetch the Device token to use it for user feedback
131
+ # https://docs.castle.io/v1/tutorials/advanced-features/end-user-feedback
132
+ device_token = castle_risk_response.dig(:device, :token)
133
+
134
+ # You might want to fetch our risk signals as well
135
+ # https://docs.castle.io/v1/reference/signals/
136
+ event_signals = castle_risk_response[:signals].keys
137
+ return
138
+ end
139
+
140
+ # do any other action you'd like to perform after a user has been signed in below
141
+ end
142
+ end
143
+ end
144
+ ```
145
+
146
+ Please note that some Devise extensions might completely override `Devise::SessionsController#create`.
147
+ In this case, you have to handle everything manually - `castle_challenge?` should be called after
148
+ a call to `warden.authenticate!` has been successful.
149
+
150
+ #### Do not sent login/registration events
151
+
152
+ You can configure CastleDevise not to send login or registration events for a given Devise model:
153
+
154
+ ```ruby
155
+ class User < ApplicationRecord
156
+ devise :database_authenticatable, :registerable,
157
+ :castle_protectable,
158
+ castle_hooks: {
159
+ # set it to false to prevent CastleDevise
160
+ # from sending filter($login)
161
+ before_registration: true,
162
+ # set it to false to prevent CastleDevise from
163
+ # sending risk($login) and log($login, $failed)
164
+ after_login: true
165
+ }
166
+ end
167
+ ```
168
+
169
+ #### Intercept request/response
170
+
171
+ You can register before- and after- request hooks in CastleDevise.
172
+
173
+ ```ruby
174
+ CastleDevise.configure do |config|
175
+ # Add custom properties to the request but only when sending
176
+ # requests to the Risk endpoint
177
+ # action - Castle API endpoint (eg. :risk, :filter, :log)
178
+ # context - CastleDevise::Context
179
+ # payload - Hash (payload passed to the Castle SDK)
180
+ config.before_request do |action, context, payload|
181
+ if action == :risk
182
+ payload[:properties] = {
183
+ from_eu: context.resource.ip.from_eu?
184
+ }
185
+ end
186
+ end
187
+
188
+ config.before_request do |action, context, payload|
189
+ # you can register multiple before_request hooks
190
+ end
191
+
192
+ # Intercept the response - enrich your logs with Castle signals
193
+ config.after_request do |action, context, payload, response|
194
+ Logging.add_tags(response[:signals].keys)
195
+ end
196
+ end
197
+ ```