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 +4 -4
- data/app/controllers/active_admin/oidc/devise/omniauth_callbacks_controller.rb +22 -17
- data/app/views/active_admin/devise/sessions/new.html.erb +8 -6
- data/lib/activeadmin/oidc/user_provisioner.rb +33 -0
- data/lib/activeadmin/oidc/version.rb +1 -1
- data/lib/activeadmin-oidc.rb +17 -0
- data/lib/generators/active_admin/oidc/install/templates/sessions_new.html.erb +7 -5
- data/lib/generators/active_admin/oidc/install/templates/sessions_new_v4.html.erb +1 -1
- metadata +45 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 12acbb45f9bb446114545d4634dfeac08880038ad2860e95e61c5a97a26f8fda
|
|
4
|
+
data.tar.gz: 18c6dadbc576e60c0cc6f8ecae04d8cf8c674127f430c12b8ea33f12b7472e47
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
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
|
-
|
|
88
|
-
|
|
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
|
-
"/
|
|
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
|
-
<%=
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
data/lib/activeadmin-oidc.rb
CHANGED
|
@@ -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
|
-
<%%=
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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>
|
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.
|
|
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: |
|