keycloak 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/initializer_generator.rb +1 -4
- data/lib/keycloak.rb +133 -91
- data/lib/keycloak/exceptions.rb +4 -1
- data/lib/keycloak/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16dc6ab9a33a507e177fc676f907c015291d0f70
|
4
|
+
data.tar.gz: 4c49fd2835f1ac1a39410c50f77b8fca4bdb791c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 813ec13535ea225feb0a42048169c6d62caa51ccc7b54aac269ce8beba2a5b63e2ee065163f0df196ec83aa0f74cfeb8fa363a3a64160a6bf249fbadbae487d0
|
7
|
+
data.tar.gz: 37d21da9cecd8a1f21a013b6afb8a35f99919654ca5957bedf4f86cd2a01ac2fd8d3620a82f7d115ffa5ae77bd9b9c283195981440a69611058a023844105b59
|
@@ -8,10 +8,7 @@ class InitializerGenerator < Rails::Generators::Base
|
|
8
8
|
# If true, then all request exception will explode in application (this is the default value)
|
9
9
|
Keycloak.generate_request_exception = #{generate_request_exception}
|
10
10
|
# controller that manage the user session
|
11
|
-
Keycloak.keycloak_controller = 'session'
|
12
|
-
# internal user for admin tasks
|
13
|
-
Keycloak::Internal.admin_user = ''
|
14
|
-
Keycloak::Internal.admin_password = ''"
|
11
|
+
Keycloak.keycloak_controller = 'session'"
|
15
12
|
end
|
16
13
|
end
|
17
14
|
end
|
data/lib/keycloak.rb
CHANGED
@@ -9,7 +9,7 @@ module Keycloak
|
|
9
9
|
|
10
10
|
class << self
|
11
11
|
attr_accessor :proxy, :generate_request_exception, :keycloak_controller,
|
12
|
-
:
|
12
|
+
:proc_cookie_token, :proc_external_attributes
|
13
13
|
end
|
14
14
|
|
15
15
|
|
@@ -23,26 +23,20 @@ module Keycloak
|
|
23
23
|
module Client
|
24
24
|
|
25
25
|
class << self
|
26
|
-
attr_reader :
|
27
|
-
|
28
|
-
:token, :token_introspection, :decoded_refresh_token,
|
29
|
-
:active, :decoded_id_token, :userinfo
|
26
|
+
attr_reader :realm, :url, :client_id, :auth_server_url,
|
27
|
+
:secret, :configuration, :public_key
|
30
28
|
|
31
|
-
attr_accessor :external_attributes
|
32
29
|
end
|
33
30
|
|
34
31
|
KEYCLOAK_JSON_FILE = 'keycloak.json'
|
35
32
|
|
36
33
|
def self.get_token(user, password)
|
37
34
|
setup_module
|
38
|
-
reset_active
|
39
|
-
|
40
|
-
@user, @password = user, password
|
41
35
|
|
42
36
|
payload = {'client_id' => @client_id,
|
43
37
|
'client_secret' => @secret,
|
44
|
-
'username' =>
|
45
|
-
'password' =>
|
38
|
+
'username' => user,
|
39
|
+
'password' => password,
|
46
40
|
'grant_type' => 'password'
|
47
41
|
}
|
48
42
|
|
@@ -50,24 +44,50 @@ module Keycloak
|
|
50
44
|
end
|
51
45
|
|
52
46
|
def self.get_token_by_code(code, redirect_uri)
|
53
|
-
|
47
|
+
verify_setup
|
48
|
+
|
49
|
+
payload = {'client_id' => @client_id,
|
50
|
+
'client_secret' => @secret,
|
51
|
+
'code' => code,
|
52
|
+
'grant_type' => 'authorization_code',
|
53
|
+
'redirect_uri' => redirect_uri
|
54
|
+
}
|
55
|
+
|
56
|
+
mount_request_token(payload)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.get_token_by_refresh_token(refreshToken = nil)
|
60
|
+
verify_setup
|
61
|
+
|
62
|
+
refreshToken = self.token['refresh_token']
|
63
|
+
|
64
|
+
payload = {'client_id' => @client_id,
|
65
|
+
'client_secret' => @secret,
|
66
|
+
'refresh_token' => refreshToken,
|
67
|
+
'grant_type' => 'refresh_token'
|
68
|
+
}
|
69
|
+
|
70
|
+
mount_request_token(payload)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.get_token_by_client_credentials
|
74
|
+
setup_module
|
54
75
|
|
55
76
|
payload = {'client_id' => @client_id,
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
'redirect_uri' => redirect_uri
|
60
|
-
}
|
77
|
+
'client_secret' => @secret,
|
78
|
+
'grant_type' => 'client_credentials'
|
79
|
+
}
|
61
80
|
|
62
81
|
mount_request_token(payload)
|
63
82
|
end
|
64
83
|
|
65
84
|
def self.get_token_introspection(refresh = false)
|
66
|
-
|
85
|
+
verify_setup
|
86
|
+
|
67
87
|
unless refresh
|
68
|
-
payload = {'token' =>
|
88
|
+
payload = {'token' => self.token["access_token"]}
|
69
89
|
else
|
70
|
-
payload = {'token' =>
|
90
|
+
payload = {'token' => self.token["refresh_token"]}
|
71
91
|
end
|
72
92
|
|
73
93
|
authorization = Base64.strict_encode64("#{@client_id}:#{@secret}")
|
@@ -80,15 +100,11 @@ module Keycloak
|
|
80
100
|
RestClient.post(@configuration['token_introspection_endpoint'], payload, header){|response, request, result|
|
81
101
|
case response.code
|
82
102
|
when 200..399
|
83
|
-
|
84
|
-
|
85
|
-
@token_introspection
|
103
|
+
response.body
|
104
|
+
|
86
105
|
else
|
87
106
|
response.return!
|
88
107
|
end
|
89
|
-
if !@active
|
90
|
-
reset_active
|
91
|
-
end
|
92
108
|
}
|
93
109
|
end
|
94
110
|
|
@@ -96,16 +112,20 @@ module Keycloak
|
|
96
112
|
end
|
97
113
|
|
98
114
|
def self.url_login_redirect(redirect_uri, response_type = 'code')
|
115
|
+
verify_setup
|
116
|
+
|
99
117
|
p = URI.encode_www_form({:response_type => response_type, :client_id => @client_id, :redirect_uri => redirect_uri})
|
100
118
|
"#{@configuration['authorization_endpoint']}?#{p}"
|
101
119
|
end
|
102
120
|
|
103
121
|
def self.logout(redirect_uri = '')
|
104
|
-
|
122
|
+
verify_setup
|
123
|
+
|
124
|
+
if self.token
|
105
125
|
payload = {'client_id' => @client_id,
|
106
|
-
|
107
|
-
|
108
|
-
|
126
|
+
'client_secret' => @secret,
|
127
|
+
'refresh_token' => self.token["refresh_token"]
|
128
|
+
}
|
109
129
|
|
110
130
|
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
111
131
|
|
@@ -119,7 +139,6 @@ module Keycloak
|
|
119
139
|
RestClient.post(final_url, payload, header){|response, request, result|
|
120
140
|
case response.code
|
121
141
|
when 200..399
|
122
|
-
reset_active
|
123
142
|
true
|
124
143
|
else
|
125
144
|
response.return!
|
@@ -134,7 +153,9 @@ module Keycloak
|
|
134
153
|
end
|
135
154
|
|
136
155
|
def self.get_userinfo
|
137
|
-
|
156
|
+
verify_setup
|
157
|
+
|
158
|
+
payload = {'access_token' => self.token["access_token"]}
|
138
159
|
|
139
160
|
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
140
161
|
|
@@ -142,8 +163,7 @@ module Keycloak
|
|
142
163
|
RestClient.post(@configuration['userinfo_endpoint'], payload, header){|response, request, result|
|
143
164
|
case response.code
|
144
165
|
when 200
|
145
|
-
|
146
|
-
@userinfo
|
166
|
+
response.body
|
147
167
|
else
|
148
168
|
response.return!
|
149
169
|
end
|
@@ -154,6 +174,8 @@ module Keycloak
|
|
154
174
|
end
|
155
175
|
|
156
176
|
def self.url_user_account
|
177
|
+
verify_setup
|
178
|
+
|
157
179
|
"#{@url}/realms/#{@realm}/account"
|
158
180
|
end
|
159
181
|
|
@@ -166,7 +188,6 @@ module Keycloak
|
|
166
188
|
@secret = installation["credentials"]["secret"]
|
167
189
|
@public_key = installation["realm-public-key"]
|
168
190
|
@auth_server_url = installation["auth-server-url"]
|
169
|
-
reset_active(false)
|
170
191
|
openid_configuration
|
171
192
|
else
|
172
193
|
raise "#{KEYCLOAK_JSON_FILE} not found."
|
@@ -174,8 +195,10 @@ module Keycloak
|
|
174
195
|
end
|
175
196
|
|
176
197
|
def self.has_role?(userRole)
|
198
|
+
verify_setup
|
199
|
+
|
177
200
|
if user_signed_in?
|
178
|
-
dt =
|
201
|
+
dt = decoded_access_token[0]
|
179
202
|
dt = dt["resource_access"][@client_id]
|
180
203
|
if dt != nil
|
181
204
|
dt["roles"].each do |role|
|
@@ -191,22 +214,50 @@ module Keycloak
|
|
191
214
|
end
|
192
215
|
|
193
216
|
def self.user_signed_in?
|
217
|
+
verify_setup
|
218
|
+
|
194
219
|
begin
|
195
|
-
get_token_introspection['active']
|
196
|
-
rescue
|
197
|
-
|
220
|
+
JSON(get_token_introspection)['active'] === true
|
221
|
+
rescue => e
|
222
|
+
if e.class < Keycloak::KeycloakException
|
223
|
+
raise
|
224
|
+
else
|
225
|
+
false
|
226
|
+
end
|
198
227
|
end
|
199
228
|
end
|
200
229
|
|
201
230
|
def self.get_attribute(attributeName)
|
202
|
-
|
231
|
+
verify_setup
|
232
|
+
|
233
|
+
attr = decoded_access_token[0]
|
203
234
|
attr[attributeName]
|
204
235
|
end
|
205
236
|
|
237
|
+
def self.token
|
238
|
+
unless Keycloak.proc_cookie_token.nil?
|
239
|
+
JSON Keycloak.proc_cookie_token.call
|
240
|
+
else
|
241
|
+
raise Keycloak::ProcCookieTokenNotDefined
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.external_attributes
|
246
|
+
unless Keycloak.proc_external_attributes.nil?
|
247
|
+
Keycloak.proc_external_attributes.call
|
248
|
+
else
|
249
|
+
raise Keycloak::ProcExternalAttributesNotDefined
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
206
253
|
private
|
207
254
|
|
208
255
|
KEYCLOACK_CONTROLLER_DEFAULT = 'session'
|
209
256
|
|
257
|
+
def self.verify_setup
|
258
|
+
get_installation if @configuration.nil?
|
259
|
+
end
|
260
|
+
|
210
261
|
def self.setup_module
|
211
262
|
Keycloak.proxy ||= ''
|
212
263
|
Keycloak.keycloak_controller ||= KEYCLOACK_CONTROLLER_DEFAULT
|
@@ -227,9 +278,9 @@ module Keycloak
|
|
227
278
|
|
228
279
|
def self.openid_configuration
|
229
280
|
RestClient.proxy = Keycloak.proxy unless Keycloak.proxy.empty?
|
230
|
-
|
281
|
+
configUrl = "#{@url}/realms/#{@realm}/.well-known/openid-configuration"
|
231
282
|
_request = -> do
|
232
|
-
RestClient.get
|
283
|
+
RestClient.get configUrl
|
233
284
|
end
|
234
285
|
response = exec_request _request
|
235
286
|
if response.code == 200
|
@@ -239,14 +290,6 @@ module Keycloak
|
|
239
290
|
end
|
240
291
|
end
|
241
292
|
|
242
|
-
def self.reset_active(resetExternalAttributes = true)
|
243
|
-
@active = false
|
244
|
-
@userinfo = nil
|
245
|
-
if resetExternalAttributes
|
246
|
-
@external_attributes = nil
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
293
|
def self.mount_request_token(payload)
|
251
294
|
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
252
295
|
|
@@ -254,15 +297,7 @@ module Keycloak
|
|
254
297
|
RestClient.post(@configuration['token_endpoint'], payload, header){|response, request, result|
|
255
298
|
case response.code
|
256
299
|
when 200
|
257
|
-
|
258
|
-
@token = JSON response.body
|
259
|
-
@decoded_access_token = JWT.decode @token["access_token"], @public_key, false, { :algorithm => 'RS256' }
|
260
|
-
@decoded_refresh_token = JWT.decode @token["refresh_token"], @public_key, false, { :algorithm => 'RS256' }
|
261
|
-
if @token["id_token"]
|
262
|
-
@decoded_id_token = JWT.decode @token["id_token"], @public_key, false, { :algorithm => 'RS256' }
|
263
|
-
end
|
264
|
-
Keycloak::Admin.setup_admin(@auth_server_url, @realm, @token["access_token"])
|
265
|
-
@token
|
300
|
+
response.body
|
266
301
|
else
|
267
302
|
response.return!
|
268
303
|
end
|
@@ -272,19 +307,28 @@ module Keycloak
|
|
272
307
|
exec_request _request
|
273
308
|
end
|
274
309
|
|
310
|
+
def self.decoded_access_token
|
311
|
+
JWT.decode self.token["access_token"], @public_key, false, { :algorithm => 'RS256' }
|
312
|
+
end
|
313
|
+
|
314
|
+
def self.decoded_refresh_token
|
315
|
+
JWT.decode self.token["refresh_token"], @public_key, false, { :algorithm => 'RS256' }
|
316
|
+
end
|
317
|
+
|
318
|
+
def self.decoded_id_token
|
319
|
+
tk = self.token
|
320
|
+
if tk["id_token"]
|
321
|
+
@decoded_id_token = JWT.decode tk["id_token"], @public_key, false, { :algorithm => 'RS256' }
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
275
325
|
end
|
276
326
|
|
277
327
|
# Os recursos desse module (admin) serão utilizadas apenas por usuários que possuem as roles do client realm-management
|
278
328
|
module Admin
|
279
329
|
|
280
330
|
class << self
|
281
|
-
attr_reader :access_token, :auth_server_url, :realm
|
282
|
-
end
|
283
331
|
|
284
|
-
def self.setup_admin(auth_server_url, realm, access_token)
|
285
|
-
@auth_server_url = auth_server_url
|
286
|
-
@access_token = access_token
|
287
|
-
@realm = realm
|
288
332
|
end
|
289
333
|
|
290
334
|
def self.get_users( queryParameters = nil)
|
@@ -407,25 +451,25 @@ module Keycloak
|
|
407
451
|
# Generics methods
|
408
452
|
|
409
453
|
def self.generic_get(service, queryParameters = nil)
|
410
|
-
Keycloak.generic_request(
|
454
|
+
Keycloak.generic_request(Keycloak::Client.token['access_token'], full_url(service), queryParameters, nil, 'GET')
|
411
455
|
end
|
412
456
|
|
413
457
|
def self.generic_post(service, queryParameters, bodyParameter)
|
414
|
-
Keycloak.generic_request(
|
458
|
+
Keycloak.generic_request(Keycloak::Client.token['access_token'], full_url(service), queryParameters, bodyParameter, 'POST')
|
415
459
|
end
|
416
460
|
|
417
461
|
def self.generic_put(service, queryParameters, bodyParameter)
|
418
|
-
Keycloak.generic_request(
|
462
|
+
Keycloak.generic_request(Keycloak::Client.token['access_token'], full_url(service), queryParameters, bodyParameter, 'PUT')
|
419
463
|
end
|
420
464
|
|
421
465
|
def self.generic_delete(service, queryParameters = nil, bodyParameter = nil)
|
422
|
-
Keycloak.generic_request(
|
466
|
+
Keycloak.generic_request(Keycloak::Client.token['access_token'], full_url(service), queryParameters, bodyParameter, 'DELETE')
|
423
467
|
end
|
424
468
|
|
425
469
|
private
|
426
470
|
|
427
471
|
def self.base_url
|
428
|
-
|
472
|
+
Keycloak::Client.auth_server_url + "/admin/realms/#{Keycloak::Client.realm}/"
|
429
473
|
end
|
430
474
|
|
431
475
|
def self.full_url(service)
|
@@ -438,13 +482,13 @@ module Keycloak
|
|
438
482
|
include Keycloak::Admin
|
439
483
|
|
440
484
|
class << self
|
441
|
-
attr_accessor
|
485
|
+
attr_accessor
|
442
486
|
end
|
443
487
|
|
444
488
|
def self.change_password(userID, redirectURI = '')
|
445
489
|
proc = lambda {|token|
|
446
490
|
Keycloak.generic_request(token["access_token"],
|
447
|
-
Keycloak::
|
491
|
+
Keycloak::Admin.full_url("users/#{userID}/execute-actions-email"),
|
448
492
|
{:redirect_uri => redirectURI, :client_id => Keycloak::Client.client_id},
|
449
493
|
['UPDATE_PASSWORD'],
|
450
494
|
'PUT')
|
@@ -460,9 +504,9 @@ module Keycloak
|
|
460
504
|
|
461
505
|
def self.get_logged_user_info
|
462
506
|
proc = lambda {|token|
|
463
|
-
userinfo = Keycloak::Client.get_userinfo
|
507
|
+
userinfo = JSON Keycloak::Client.get_userinfo
|
464
508
|
Keycloak.generic_request(token["access_token"],
|
465
|
-
Keycloak::
|
509
|
+
Keycloak::Admin.full_url("users/#{userinfo['sub']}"),
|
466
510
|
nil, nil, 'GET')
|
467
511
|
}
|
468
512
|
|
@@ -477,7 +521,7 @@ module Keycloak
|
|
477
521
|
search = {:email => userLogin}
|
478
522
|
end
|
479
523
|
users = JSON Keycloak.generic_request(token["access_token"],
|
480
|
-
Keycloak::
|
524
|
+
Keycloak::Admin.full_url("users/"),
|
481
525
|
search, nil, 'GET')
|
482
526
|
users[0]
|
483
527
|
if users.count == 0
|
@@ -529,7 +573,7 @@ module Keycloak
|
|
529
573
|
:enabled => true}
|
530
574
|
|
531
575
|
if !newUser || Keycloak.generic_request(token["access_token"],
|
532
|
-
Keycloak::
|
576
|
+
Keycloak::Admin.full_url("users/"),
|
533
577
|
nil, userRepresentation, 'POST')
|
534
578
|
|
535
579
|
user = get_user_info(userName, true) if newUser
|
@@ -539,18 +583,18 @@ module Keycloak
|
|
539
583
|
:value => password}
|
540
584
|
|
541
585
|
if Keycloak.generic_request(token["access_token"],
|
542
|
-
Keycloak::
|
586
|
+
Keycloak::Admin.full_url("users/#{user['id']}/reset-password"),
|
543
587
|
nil, credentialRepresentation, 'PUT')
|
544
588
|
|
545
589
|
client = JSON Keycloak.generic_request(token["access_token"],
|
546
|
-
Keycloak::
|
590
|
+
Keycloak::Admin.full_url("clients/"),
|
547
591
|
{:clientId => Keycloak::Client.client_id}, nil, 'GET')
|
548
592
|
|
549
593
|
roles = Array.new
|
550
594
|
clientRolesNames.each do |r|
|
551
595
|
if r && !r.empty?
|
552
596
|
role = JSON Keycloak.generic_request(token["access_token"],
|
553
|
-
Keycloak::
|
597
|
+
Keycloak::Admin.full_url("clients/#{client[0]['id']}/roles/#{r}"),
|
554
598
|
nil, nil, 'GET')
|
555
599
|
roles.push(role)
|
556
600
|
end
|
@@ -558,7 +602,7 @@ module Keycloak
|
|
558
602
|
|
559
603
|
if roles.count > 0
|
560
604
|
Keycloak.generic_request(token["access_token"],
|
561
|
-
Keycloak::
|
605
|
+
Keycloak::Admin.full_url("users/#{user['id']}/role-mappings/clients/#{client[0]['id']}"),
|
562
606
|
nil, roles, 'POST')
|
563
607
|
end
|
564
608
|
end
|
@@ -584,11 +628,9 @@ module Keycloak
|
|
584
628
|
Keycloak::Client.get_installation
|
585
629
|
|
586
630
|
payload = {'client_id' => Keycloak::Client.client_id,
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
'grant_type' => 'password'
|
591
|
-
}
|
631
|
+
'client_secret' => Keycloak::Client.secret,
|
632
|
+
'grant_type' => 'client_credentials'
|
633
|
+
}
|
592
634
|
|
593
635
|
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
594
636
|
|
@@ -633,6 +675,7 @@ module Keycloak
|
|
633
675
|
private
|
634
676
|
|
635
677
|
def self.generic_request(accessToken, uri, queryParameters, bodyParameter, method)
|
678
|
+
Keycloak::Client.verify_setup
|
636
679
|
final_url = uri
|
637
680
|
|
638
681
|
header = {'Content-Type' => 'application/x-www-form-urlencoded',
|
@@ -689,20 +732,19 @@ module Keycloak
|
|
689
732
|
end
|
690
733
|
|
691
734
|
def self.rescue_response(response)
|
692
|
-
|
693
|
-
case @last_response.code
|
735
|
+
case response.code
|
694
736
|
when 200..399
|
695
|
-
if
|
737
|
+
if response.body.empty?
|
696
738
|
true
|
697
739
|
else
|
698
|
-
|
740
|
+
response.body
|
699
741
|
end
|
700
742
|
else
|
701
743
|
if Keycloak.explode_exception
|
702
|
-
|
744
|
+
response.return!
|
703
745
|
else
|
704
746
|
begin
|
705
|
-
|
747
|
+
response.return!
|
706
748
|
rescue RestClient::ExceptionWithResponse => err
|
707
749
|
err.response
|
708
750
|
rescue Exception => e
|
data/lib/keycloak/exceptions.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
1
|
module Keycloak
|
2
|
-
class
|
2
|
+
class KeycloakException < StandardError; end
|
3
|
+
class UserLoginNotFound < KeycloakException; end
|
4
|
+
class ProcCookieTokenNotDefined < KeycloakException; end
|
5
|
+
class ProcExternalAttributesNotDefined < KeycloakException; end
|
3
6
|
end
|
data/lib/keycloak/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keycloak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Guilherme Portugues
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|