activeadmin-oidc 2.0.1 → 2.1.2

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: 1a4d32669000d3de07114d54dc1c5d408899c1c8cf40e86b154e328ab08ed385
4
- data.tar.gz: d1f002c56666e32b794bc1024a37b8bb06e275642c625b198664febd7caf6805
3
+ metadata.gz: 12acbb45f9bb446114545d4634dfeac08880038ad2860e95e61c5a97a26f8fda
4
+ data.tar.gz: 18c6dadbc576e60c0cc6f8ecae04d8cf8c674127f430c12b8ea33f12b7472e47
5
5
  SHA512:
6
- metadata.gz: 79d676e27f836c5d271925d067cb5a072450e237394b07a590ac988b779ff5200a39c79208170ec3ef930837b8ad46dbf0f97e8403b9899fb73c1a6983092ab9
7
- data.tar.gz: 13ac29d12fda8a44ba24aa992ac61d65f63c607894e576d66276cf18f2fa478f26b881b335bf93f767296c65c2c35a9d43b1bf76ddcb48d19078dd75ce94bb8f
6
+ metadata.gz: 0f4a8f41ac39006e4c0882b55fc481f1bee795290655c71e79a56a219425145a7f977f60ec4e72d2e0a317b765688dd41478c48c208f49104f85dd024c5273d9
7
+ data.tar.gz: 0e017cf647f5fbea240074c247bc7baac89c81222d7a94d2a0387364a3d7ef08468d5cef1e183c375291f56fc4d6105b316c78076cc82beee75a04b4ee7c651e
@@ -38,18 +38,19 @@ module ActiveAdmin
38
38
  provider: ActiveAdmin::Oidc::Engine::PROVIDER_NAME.to_s
39
39
  ).call
40
40
 
41
- # Devise checks active_for_authentication? on session
42
- # deserialization but NOT on initial OmniAuth sign-in.
43
- # Guard here so disabled/locked users are rejected immediately.
44
- unless admin_user.active_for_authentication?
45
- message = admin_user.inactive_message
46
- flash[:alert] = I18n.t("devise.failure.#{message}", default: message.to_s)
47
- redirect_to after_omniauth_failure_path_for(resource_name)
48
- return
49
- end
50
-
51
41
  sign_in_and_redirect admin_user, event: :authentication
52
42
  set_flash_message(:notice, :success, kind: 'OIDC') if is_navigational_format?
43
+ rescue ActiveAdmin::Oidc::InactiveError => e
44
+ Rails.logger.warn("[activeadmin-oidc] inactive: #{e.inactive_message_key}")
45
+ # Fall back to the standard `inactive` translation rather
46
+ # than the raw symbol — custom keys like :locked_by_admin
47
+ # would otherwise leak host-internal state into a flash
48
+ # visible to unauthenticated visitors.
49
+ flash[:alert] = I18n.t(
50
+ "devise.failure.#{e.inactive_message_key}",
51
+ default: I18n.t("devise.failure.inactive")
52
+ )
53
+ redirect_to after_omniauth_failure_path_for(resource_name)
53
54
  rescue ActiveAdmin::Oidc::ProvisioningError => e
54
55
  Rails.logger.warn("[activeadmin-oidc] denial: #{e.message}")
55
56
  flash[:alert] = ActiveAdmin::Oidc.config.access_denied_message
@@ -78,14 +79,18 @@ module ActiveAdmin
78
79
  # `:database_authenticatable` is in the mapping's `used_helpers`,
79
80
  # so an OIDC-only model never gets it. The engine mounts
80
81
  # `new_<scope>_session_path` itself, but the helper lives on
81
- # whichever route set the mapping's `router_name` points at
82
- # main_app by default, or `<engine_name>` for hosts that mount
83
- # Devise inside a Rails engine. Replicate Devise's own dispatch
84
- # so the right context is asked.
82
+ # whichever route set Devise's URL helper dispatcher points at:
83
+ # the per-mapping `router_name` (set by
84
+ # `devise_for :scope, router_name: :engine`) when present,
85
+ # otherwise the global `Devise.available_router_name`
86
+ # (set by `Devise.router_name = :engine`), which defaults to
87
+ # `:main_app`. Replicate that dispatcher here so the helper is
88
+ # resolved on the right context (Rails.application proxy or
89
+ # mounted engine proxy).
85
90
  def after_omniauth_failure_path_for(scope)
86
- router_name = ::Devise.mappings[scope].router_name
87
- context = router_name ? send(router_name) : self
88
- context.public_send(:"new_#{scope}_session_path")
91
+ router_name = ::Devise.mappings[scope].router_name ||
92
+ ::Devise.available_router_name
93
+ send(router_name).public_send(:"new_#{scope}_session_path")
89
94
  end
90
95
  end
91
96
  end
@@ -5,7 +5,7 @@
5
5
  </h2>
6
6
 
7
7
  <%= button_to ActiveAdmin::Oidc.config.login_button_label,
8
- "/admin/auth/oidc",
8
+ "#{OmniAuth.config.path_prefix}/oidc",
9
9
  method: :post,
10
10
  class: "activeadmin-oidc-login-button w-full",
11
11
  form_class: 'formtastic',
@@ -15,10 +15,12 @@
15
15
  <div id="login">
16
16
  <h2><%= active_admin_application.site_title(self) %></h2>
17
17
 
18
- <%= button_to ActiveAdmin::Oidc.config.login_button_label,
19
- "/admin/auth/oidc",
20
- method: :post,
21
- class: "activeadmin-oidc-login-button",
22
- data: { turbo: false } %>
18
+ <%= form_tag "#{OmniAuth.config.path_prefix}/oidc",
19
+ method: :post,
20
+ class: "activeadmin-oidc-login-form formtastic",
21
+ data: { turbo: false } do %>
22
+ <%= submit_tag ActiveAdmin::Oidc.config.login_button_label,
23
+ class: "activeadmin-oidc-login-button" %>
24
+ <% end %>
23
25
  </div>
24
26
  <% end %>
@@ -35,11 +35,31 @@ module ActiveAdmin
35
35
  validate_claims!
36
36
 
37
37
  admin_user = find_or_adopt_or_build
38
+
39
+ # Retry path: a concurrent first sign-in inserted between our
40
+ # initial miss-and-build and our failed save. Return the
41
+ # winner's row verbatim — on_login already ran on our (now
42
+ # discarded) in-memory build, and re-firing it would double
43
+ # any host-side side effects (audit log, webhook, email).
44
+ return admin_user if @retried
45
+
38
46
  assign_base_attributes(admin_user)
39
47
 
40
48
  allowed = invoke_on_login(admin_user)
41
49
  raise ProvisioningError, denial_message unless allowed
42
50
 
51
+ # Devise's `active_for_authentication?` guard runs in the
52
+ # controller post-sign-in, but by then we've already saved
53
+ # the record. Hostile attempts where on_login flips an
54
+ # inactivity flag (e.g. enabled=false) would otherwise leave
55
+ # provisional rows in the DB on every try. Refuse before
56
+ # persisting. Raise the dedicated InactiveError so the
57
+ # controller can surface the model's I18n inactive_message
58
+ # instead of the generic denial flash.
59
+ unless admin_user.active_for_authentication?
60
+ raise InactiveError, admin_user.inactive_message
61
+ end
62
+
43
63
  save!(admin_user)
44
64
  admin_user
45
65
  rescue RetryProvisioning
@@ -84,6 +104,19 @@ module ActiveAdmin
84
104
  "Identity #{identity_value.inspect} is already linked to a different account (takeover guard)"
85
105
  end
86
106
 
107
+ # Adoption of a pre-existing row (provider/uid still nil) by
108
+ # an IdP-supplied identity value is a privilege-escalation
109
+ # vector when the IdP allows external / unverified emails:
110
+ # an attacker registers `ceo@example.com` at the IdP and
111
+ # adopts the seeded admin row. Refuse when the IdP explicitly
112
+ # marks the claim as unverified. (Absent claim → unchanged
113
+ # behaviour, since many IdPs don't ship `email_verified` at
114
+ # all.)
115
+ if @claims["email_verified"] == false
116
+ raise ProvisioningError,
117
+ "Identity #{identity_value.inspect} is not verified by the IdP — refusing adoption"
118
+ end
119
+
87
120
  identity_match.provider = @provider
88
121
  identity_match.uid = uid
89
122
  return identity_match
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveAdmin
4
4
  module Oidc
5
- VERSION = "2.0.1"
5
+ VERSION = "2.1.2"
6
6
  end
7
7
  end
@@ -28,6 +28,23 @@ module ActiveAdmin
28
28
  class ProvisioningError < Error; end
29
29
  class RetryProvisioning < Error; end
30
30
 
31
+ # Raised when active_for_authentication? rejects the user. Carries
32
+ # the model's inactive_message symbol so the controller can
33
+ # translate it via I18n (devise.failure.<symbol>) instead of
34
+ # falling back to the generic denial flash.
35
+ class InactiveError < ProvisioningError
36
+ attr_reader :inactive_message_key
37
+
38
+ # Devise's default inactive_message is :inactive. Hosts can
39
+ # override the method and legitimately return nil on some
40
+ # branches; fall back to :inactive so the controller never
41
+ # ends up with an empty flash.
42
+ def initialize(inactive_message_key)
43
+ @inactive_message_key = inactive_message_key.presence || :inactive
44
+ super(@inactive_message_key.to_s)
45
+ end
46
+ end
47
+
31
48
  class << self
32
49
  def config
33
50
  @config ||= Configuration.new
@@ -1,9 +1,11 @@
1
1
  <div id="login">
2
2
  <h2><%%= active_admin_application.site_title(self) %></h2>
3
3
 
4
- <%%= button_to ActiveAdmin::Oidc.config.login_button_label,
5
- "/admin/auth/oidc",
6
- method: :post,
7
- class: "activeadmin-oidc-login-button",
8
- data: { turbo: false } %>
4
+ <%%= form_tag "#{OmniAuth.config.path_prefix}/oidc",
5
+ method: :post,
6
+ class: "activeadmin-oidc-login-form formtastic",
7
+ data: { turbo: false } do %>
8
+ <%%= submit_tag ActiveAdmin::Oidc.config.login_button_label,
9
+ class: "activeadmin-oidc-login-button" %>
10
+ <%% end %>
9
11
  </div>
@@ -4,7 +4,7 @@
4
4
  </h2>
5
5
 
6
6
  <%%= button_to ActiveAdmin::Oidc.config.login_button_label,
7
- "/admin/auth/oidc",
7
+ "#{OmniAuth.config.path_prefix}/oidc",
8
8
  method: :post,
9
9
  class: "activeadmin-oidc-login-button w-full",
10
10
  form_class: 'formtastic',
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin-oidc
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Fedoronchuk
@@ -33,28 +33,28 @@ dependencies:
33
33
  name: devise
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  requirements:
36
- - - ">="
36
+ - - "~>"
37
37
  - !ruby/object:Gem::Version
38
38
  version: '5.0'
39
39
  type: :runtime
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
- - - ">="
43
+ - - "~>"
44
44
  - !ruby/object:Gem::Version
45
45
  version: '5.0'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: omniauth
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
50
- - - ">="
50
+ - - "~>"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '2.1'
53
53
  type: :runtime
54
54
  prerelease: false
55
55
  version_requirements: !ruby/object:Gem::Requirement
56
56
  requirements:
57
- - - ">="
57
+ - - "~>"
58
58
  - !ruby/object:Gem::Version
59
59
  version: '2.1'
60
60
  - !ruby/object:Gem::Dependency
@@ -64,6 +64,9 @@ dependencies:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: '1.0'
67
+ - - "<"
68
+ - !ruby/object:Gem::Version
69
+ version: '3'
67
70
  type: :runtime
68
71
  prerelease: false
69
72
  version_requirements: !ruby/object:Gem::Requirement
@@ -71,6 +74,9 @@ dependencies:
71
74
  - - ">="
72
75
  - !ruby/object:Gem::Version
73
76
  version: '1.0'
77
+ - - "<"
78
+ - !ruby/object:Gem::Version
79
+ version: '3'
74
80
  - !ruby/object:Gem::Dependency
75
81
  name: omniauth_openid_connect
76
82
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +84,9 @@ dependencies:
78
84
  - - ">="
79
85
  - !ruby/object:Gem::Version
80
86
  version: '0.6'
87
+ - - "<"
88
+ - !ruby/object:Gem::Version
89
+ version: '1'
81
90
  type: :runtime
82
91
  prerelease: false
83
92
  version_requirements: !ruby/object:Gem::Requirement
@@ -85,6 +94,9 @@ dependencies:
85
94
  - - ">="
86
95
  - !ruby/object:Gem::Version
87
96
  version: '0.6'
97
+ - - "<"
98
+ - !ruby/object:Gem::Version
99
+ version: '1'
88
100
  - !ruby/object:Gem::Dependency
89
101
  name: rails
90
102
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +104,9 @@ dependencies:
92
104
  - - ">="
93
105
  - !ruby/object:Gem::Version
94
106
  version: '7.2'
107
+ - - "<"
108
+ - !ruby/object:Gem::Version
109
+ version: '9'
95
110
  type: :runtime
96
111
  prerelease: false
97
112
  version_requirements: !ruby/object:Gem::Requirement
@@ -99,60 +114,63 @@ dependencies:
99
114
  - - ">="
100
115
  - !ruby/object:Gem::Version
101
116
  version: '7.2'
117
+ - - "<"
118
+ - !ruby/object:Gem::Version
119
+ version: '9'
102
120
  - !ruby/object:Gem::Dependency
103
121
  name: rspec-rails
104
122
  requirement: !ruby/object:Gem::Requirement
105
123
  requirements:
106
- - - ">="
124
+ - - "~>"
107
125
  - !ruby/object:Gem::Version
108
126
  version: '6.0'
109
127
  type: :development
110
128
  prerelease: false
111
129
  version_requirements: !ruby/object:Gem::Requirement
112
130
  requirements:
113
- - - ">="
131
+ - - "~>"
114
132
  - !ruby/object:Gem::Version
115
133
  version: '6.0'
116
134
  - !ruby/object:Gem::Dependency
117
135
  name: capybara
118
136
  requirement: !ruby/object:Gem::Requirement
119
137
  requirements:
120
- - - ">="
138
+ - - "~>"
121
139
  - !ruby/object:Gem::Version
122
140
  version: '3.40'
123
141
  type: :development
124
142
  prerelease: false
125
143
  version_requirements: !ruby/object:Gem::Requirement
126
144
  requirements:
127
- - - ">="
145
+ - - "~>"
128
146
  - !ruby/object:Gem::Version
129
147
  version: '3.40'
130
148
  - !ruby/object:Gem::Dependency
131
149
  name: webmock
132
150
  requirement: !ruby/object:Gem::Requirement
133
151
  requirements:
134
- - - ">="
152
+ - - "~>"
135
153
  - !ruby/object:Gem::Version
136
154
  version: '3.19'
137
155
  type: :development
138
156
  prerelease: false
139
157
  version_requirements: !ruby/object:Gem::Requirement
140
158
  requirements:
141
- - - ">="
159
+ - - "~>"
142
160
  - !ruby/object:Gem::Version
143
161
  version: '3.19'
144
162
  - !ruby/object:Gem::Dependency
145
163
  name: jwt
146
164
  requirement: !ruby/object:Gem::Requirement
147
165
  requirements:
148
- - - ">="
166
+ - - "~>"
149
167
  - !ruby/object:Gem::Version
150
168
  version: '2.7'
151
169
  type: :development
152
170
  prerelease: false
153
171
  version_requirements: !ruby/object:Gem::Requirement
154
172
  requirements:
155
- - - ">="
173
+ - - "~>"
156
174
  - !ruby/object:Gem::Version
157
175
  version: '2.7'
158
176
  - !ruby/object:Gem::Dependency
@@ -162,6 +180,9 @@ dependencies:
162
180
  - - ">="
163
181
  - !ruby/object:Gem::Version
164
182
  version: '1.7'
183
+ - - "<"
184
+ - !ruby/object:Gem::Version
185
+ version: '3'
165
186
  type: :development
166
187
  prerelease: false
167
188
  version_requirements: !ruby/object:Gem::Requirement
@@ -169,60 +190,63 @@ dependencies:
169
190
  - - ">="
170
191
  - !ruby/object:Gem::Version
171
192
  version: '1.7'
193
+ - - "<"
194
+ - !ruby/object:Gem::Version
195
+ version: '3'
172
196
  - !ruby/object:Gem::Dependency
173
197
  name: rake
174
198
  requirement: !ruby/object:Gem::Requirement
175
199
  requirements:
176
- - - ">="
200
+ - - "~>"
177
201
  - !ruby/object:Gem::Version
178
202
  version: '13.0'
179
203
  type: :development
180
204
  prerelease: false
181
205
  version_requirements: !ruby/object:Gem::Requirement
182
206
  requirements:
183
- - - ">="
207
+ - - "~>"
184
208
  - !ruby/object:Gem::Version
185
209
  version: '13.0'
186
210
  - !ruby/object:Gem::Dependency
187
211
  name: rubocop
188
212
  requirement: !ruby/object:Gem::Requirement
189
213
  requirements:
190
- - - ">="
214
+ - - "~>"
191
215
  - !ruby/object:Gem::Version
192
216
  version: '1.60'
193
217
  type: :development
194
218
  prerelease: false
195
219
  version_requirements: !ruby/object:Gem::Requirement
196
220
  requirements:
197
- - - ">="
221
+ - - "~>"
198
222
  - !ruby/object:Gem::Version
199
223
  version: '1.60'
200
224
  - !ruby/object:Gem::Dependency
201
225
  name: rubocop-rails
202
226
  requirement: !ruby/object:Gem::Requirement
203
227
  requirements:
204
- - - ">="
228
+ - - "~>"
205
229
  - !ruby/object:Gem::Version
206
230
  version: '2.20'
207
231
  type: :development
208
232
  prerelease: false
209
233
  version_requirements: !ruby/object:Gem::Requirement
210
234
  requirements:
211
- - - ">="
235
+ - - "~>"
212
236
  - !ruby/object:Gem::Version
213
237
  version: '2.20'
214
238
  - !ruby/object:Gem::Dependency
215
239
  name: rubocop-rspec
216
240
  requirement: !ruby/object:Gem::Requirement
217
241
  requirements:
218
- - - ">="
242
+ - - "~>"
219
243
  - !ruby/object:Gem::Version
220
244
  version: '2.25'
221
245
  type: :development
222
246
  prerelease: false
223
247
  version_requirements: !ruby/object:Gem::Requirement
224
248
  requirements:
225
- - - ">="
249
+ - - "~>"
226
250
  - !ruby/object:Gem::Version
227
251
  version: '2.25'
228
252
  description: |