sqsc-keycloak-ruby 1.0.0.pre
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 +7 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/.vscode/launch.json +65 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +57 -0
- data/LICENSE.txt +21 -0
- data/README.md +535 -0
- data/README.pt-BR.md +532 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/keycloak-ruby.gemspec +39 -0
- data/lib/generators/keycloak.rb +12 -0
- data/lib/generators/keycloak/config/config_generator.rb +10 -0
- data/lib/keycloak/exceptions.rb +7 -0
- data/lib/keycloak/version.rb +3 -0
- data/lib/sqsc-keycloak-ruby.rb +972 -0
- metadata +148 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "keycloak"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "keycloak/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sqsc-keycloak-ruby"
|
8
|
+
spec.version = Keycloak::VERSION
|
9
|
+
spec.authors = ["Guilherme Portugues", "Jérôme Doucet"]
|
10
|
+
spec.email = ["engineering@squarescale.com", "jerdct@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Add authentication to applications and secure services with Keycloak}
|
13
|
+
spec.description = %q{Fork of https://github.com/imagov/keycloak for Squarescale needs}
|
14
|
+
spec.homepage = "https://github.com/squarescale/keycloak-ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
# if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata["allowed_push_host"] = "https://github.com/imagov/keycloak.git"
|
21
|
+
# else
|
22
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
# "public gem pushes."
|
24
|
+
# end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
34
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
35
|
+
spec.add_development_dependency "rspec", "~> 3.9"
|
36
|
+
spec.add_runtime_dependency "rest-client"
|
37
|
+
spec.add_runtime_dependency "jwt"
|
38
|
+
spec.add_runtime_dependency "json"
|
39
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Set proxy to connect in keycloak server
|
2
|
+
Keycloak.proxy = ''
|
3
|
+
# If true, then all request exception will explode in application (this is the default value)
|
4
|
+
Keycloak.generate_request_exception = true
|
5
|
+
# controller that manage the user session
|
6
|
+
Keycloak.keycloak_controller = 'session'
|
7
|
+
# relm name (only if the installation file is not present)
|
8
|
+
Keycloak.realm = ''
|
9
|
+
# relm url (only if the installation file is not present)
|
10
|
+
Keycloak.auth_server_url = ''
|
11
|
+
# The introspect of the token will be executed every time the Keycloak::Client.has_role? method is invoked, if this setting is set to true.
|
12
|
+
Keycloak.validate_token_when_call_has_role = false
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module Keycloak
|
2
|
+
class KeycloakException < StandardError; end
|
3
|
+
class UserLoginNotFound < KeycloakException; end
|
4
|
+
class ProcCookieTokenNotDefined < KeycloakException; end
|
5
|
+
class ProcExternalAttributesNotDefined < KeycloakException; end
|
6
|
+
class InstallationFileNotFound < KeycloakException; end
|
7
|
+
end
|
@@ -0,0 +1,972 @@
|
|
1
|
+
require 'keycloak/version'
|
2
|
+
require 'rest-client'
|
3
|
+
require 'json'
|
4
|
+
require 'jwt'
|
5
|
+
require 'base64'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
def isempty?(value)
|
9
|
+
value.respond_to?(:empty?) ? !!value.empty? : !value
|
10
|
+
end
|
11
|
+
|
12
|
+
module Keycloak
|
13
|
+
OLD_KEYCLOAK_JSON_FILE = 'keycloak.json'.freeze
|
14
|
+
KEYCLOAK_JSON_FILE = 'config/keycloak.json'.freeze
|
15
|
+
|
16
|
+
class << self
|
17
|
+
attr_accessor :proxy, :generate_request_exception, :keycloak_controller,
|
18
|
+
:proc_cookie_token, :proc_external_attributes,
|
19
|
+
:realm, :auth_server_url, :validate_token_when_call_has_role
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.explode_exception
|
23
|
+
Keycloak.generate_request_exception = true if Keycloak.generate_request_exception.nil?
|
24
|
+
Keycloak.generate_request_exception
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.installation_file
|
28
|
+
@@installation_file ||= if File.exist?(KEYCLOAK_JSON_FILE)
|
29
|
+
KEYCLOAK_JSON_FILE
|
30
|
+
else
|
31
|
+
OLD_KEYCLOAK_JSON_FILE
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.installation_file=(file = nil)
|
36
|
+
raise InstallationFileNotFound unless file.instance_of?(String) && File.exist?(file)
|
37
|
+
@@installation_file = file || KEYCLOAK_JSON_FILE
|
38
|
+
end
|
39
|
+
|
40
|
+
module Client
|
41
|
+
class << self
|
42
|
+
attr_accessor :realm, :auth_server_url
|
43
|
+
attr_reader :client_id, :secret, :configuration, :public_key
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get_token(user, password, client_id = '', secret = '')
|
47
|
+
setup_module
|
48
|
+
|
49
|
+
client_id = @client_id if isempty?(client_id)
|
50
|
+
secret = @secret if isempty?(secret)
|
51
|
+
|
52
|
+
payload = { 'client_id' => client_id,
|
53
|
+
'client_secret' => secret,
|
54
|
+
'username' => user,
|
55
|
+
'password' => password,
|
56
|
+
'grant_type' => 'password' }
|
57
|
+
|
58
|
+
mount_request_token(payload)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.get_token_by_code(code, redirect_uri, client_id = '', secret = '')
|
62
|
+
verify_setup
|
63
|
+
|
64
|
+
client_id = @client_id if isempty?(client_id)
|
65
|
+
secret = @secret if isempty?(secret)
|
66
|
+
|
67
|
+
payload = { 'client_id' => client_id,
|
68
|
+
'client_secret' => secret,
|
69
|
+
'code' => code,
|
70
|
+
'grant_type' => 'authorization_code',
|
71
|
+
'redirect_uri' => redirect_uri }
|
72
|
+
|
73
|
+
mount_request_token(payload)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.get_token_by_exchange(issuer, issuer_token, client_id = '', secret = '', token_endpoint = '')
|
77
|
+
setup_module
|
78
|
+
|
79
|
+
client_id = @client_id if isempty?(client_id)
|
80
|
+
secret = @secret if isempty?(secret)
|
81
|
+
token_endpoint = @configuration['token_endpoint'] if isempty?(token_endpoint)
|
82
|
+
|
83
|
+
payload = { 'client_id' => client_id, 'client_secret' => secret, 'audience' => client_id, 'grant_type' => 'urn:ietf:params:oauth:grant-type:token-exchange', 'subject_token_type' => 'urn:ietf:params:oauth:token-type:access_token', 'subject_issuer' => issuer, 'subject_token' => issuer_token }
|
84
|
+
header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
85
|
+
_request = -> do
|
86
|
+
RestClient.post(token_endpoint, payload, header){|response, request, result|
|
87
|
+
# case response.code
|
88
|
+
# when 200
|
89
|
+
# response.body
|
90
|
+
# else
|
91
|
+
# response.return!
|
92
|
+
# end
|
93
|
+
response.body
|
94
|
+
}
|
95
|
+
end
|
96
|
+
exec_request _request
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.get_userinfo_issuer(access_token = '', userinfo_endpoint = '')
|
100
|
+
verify_setup
|
101
|
+
|
102
|
+
userinfo_endpoint = @configuration['userinfo_endpoint'] if isempty?(userinfo_endpoint)
|
103
|
+
|
104
|
+
access_token = self.token["access_token"] if access_token.empty?
|
105
|
+
payload = { 'access_token' => access_token }
|
106
|
+
header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
107
|
+
_request = -> do
|
108
|
+
RestClient.post(userinfo_endpoint, payload, header){ |response, request, result|
|
109
|
+
response.body
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
exec_request _request
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.get_token_by_refresh_token(refresh_token = '', client_id = '', secret = '')
|
117
|
+
verify_setup
|
118
|
+
|
119
|
+
client_id = @client_id if isempty?(client_id)
|
120
|
+
secret = @secret if isempty?(secret)
|
121
|
+
refresh_token = token['refresh_token'] if refresh_token.empty?
|
122
|
+
|
123
|
+
payload = { 'client_id' => client_id,
|
124
|
+
'client_secret' => secret,
|
125
|
+
'refresh_token' => refresh_token,
|
126
|
+
'grant_type' => 'refresh_token' }
|
127
|
+
|
128
|
+
mount_request_token(payload)
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.get_token_by_client_credentials(client_id = '', secret = '')
|
132
|
+
setup_module
|
133
|
+
|
134
|
+
client_id = @client_id if isempty?(client_id)
|
135
|
+
secret = @secret if isempty?(secret)
|
136
|
+
|
137
|
+
payload = { 'client_id' => client_id,
|
138
|
+
'client_secret' => secret,
|
139
|
+
'grant_type' => 'client_credentials' }
|
140
|
+
|
141
|
+
mount_request_token(payload)
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.get_token_introspection(token = '', client_id = '', secret = '', token_introspection_endpoint = '')
|
145
|
+
verify_setup
|
146
|
+
|
147
|
+
client_id = @client_id if isempty?(client_id)
|
148
|
+
secret = @secret if isempty?(secret)
|
149
|
+
token = self.token['access_token'] if isempty?(token)
|
150
|
+
token_introspection_endpoint = @configuration['token_introspection_endpoint'] if isempty?(token_introspection_endpoint)
|
151
|
+
|
152
|
+
payload = { 'token' => token }
|
153
|
+
|
154
|
+
authorization = Base64.strict_encode64("#{client_id}:#{secret}")
|
155
|
+
authorization = "Basic #{authorization}"
|
156
|
+
|
157
|
+
header = { 'Content-Type' => 'application/x-www-form-urlencoded',
|
158
|
+
'authorization' => authorization }
|
159
|
+
|
160
|
+
_request = -> do
|
161
|
+
RestClient.post(token_introspection_endpoint, payload, header){|response, request, result|
|
162
|
+
case response.code
|
163
|
+
when 200..399
|
164
|
+
response.body
|
165
|
+
else
|
166
|
+
response.return!
|
167
|
+
end
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
exec_request _request
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.url_login_redirect(redirect_uri, response_type = 'code', client_id = '', authorization_endpoint = '')
|
175
|
+
verify_setup
|
176
|
+
|
177
|
+
client_id = @client_id if isempty?(client_id)
|
178
|
+
authorization_endpoint = @configuration['authorization_endpoint'] if isempty?(authorization_endpoint)
|
179
|
+
|
180
|
+
p = URI.encode_www_form(response_type: response_type, client_id: client_id, redirect_uri: redirect_uri)
|
181
|
+
"#{authorization_endpoint}?#{p}"
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.logout(redirect_uri = '', refresh_token = '', client_id = '', secret = '', end_session_endpoint = '')
|
185
|
+
verify_setup
|
186
|
+
|
187
|
+
if self.token || !refresh_token.empty?
|
188
|
+
|
189
|
+
refresh_token = self.token['refresh_token'] if refresh_token.empty?
|
190
|
+
client_id = @client_id if isempty?(client_id)
|
191
|
+
secret = @secret if isempty?(secret)
|
192
|
+
end_session_endpoint = @configuration['end_session_endpoint'] if isempty?(end_session_endpoint)
|
193
|
+
|
194
|
+
payload = { 'client_id' => client_id,
|
195
|
+
'client_secret' => secret,
|
196
|
+
'refresh_token' => refresh_token }
|
197
|
+
|
198
|
+
header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
199
|
+
|
200
|
+
final_url = if redirect_uri.empty?
|
201
|
+
end_session_endpoint
|
202
|
+
else
|
203
|
+
"#{end_session_endpoint}?#{URI.encode_www_form(redirect_uri: redirect_uri)}"
|
204
|
+
end
|
205
|
+
|
206
|
+
_request = -> do
|
207
|
+
RestClient.post(final_url, payload, header){ |response, request, result|
|
208
|
+
case response.code
|
209
|
+
when 200..399
|
210
|
+
true
|
211
|
+
else
|
212
|
+
response.return!
|
213
|
+
end
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
exec_request _request
|
218
|
+
else
|
219
|
+
true
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.get_userinfo(access_token = '', userinfo_endpoint = '')
|
224
|
+
verify_setup
|
225
|
+
|
226
|
+
access_token = self.token['access_token'] if access_token.empty?
|
227
|
+
userinfo_endpoint = @configuration['userinfo_endpoint'] if isempty?(userinfo_endpoint)
|
228
|
+
|
229
|
+
payload = { 'access_token' => access_token }
|
230
|
+
|
231
|
+
header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
232
|
+
|
233
|
+
_request = -> do
|
234
|
+
RestClient.post(userinfo_endpoint, payload, header){ |response, request, result|
|
235
|
+
case response.code
|
236
|
+
when 200
|
237
|
+
response.body
|
238
|
+
else
|
239
|
+
response.return!
|
240
|
+
end
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
exec_request _request
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.url_user_account
|
248
|
+
verify_setup
|
249
|
+
|
250
|
+
"#{@auth_server_url}/realms/#{@realm}/account"
|
251
|
+
end
|
252
|
+
|
253
|
+
def self.has_role?(user_role, access_token = '', client_id = '', secret = '', token_introspection_endpoint = '')
|
254
|
+
verify_setup
|
255
|
+
|
256
|
+
client_id = @client_id if isempty?(client_id)
|
257
|
+
secret = @secret if isempty?(secret)
|
258
|
+
token_introspection_endpoint = @configuration['token_introspection_endpoint'] if isempty?(token_introspection_endpoint)
|
259
|
+
|
260
|
+
if !Keycloak.validate_token_when_call_has_role || user_signed_in?(access_token, client_id, secret, token_introspection_endpoint)
|
261
|
+
dt = decoded_access_token(access_token)[0]
|
262
|
+
dt = dt['resource_access'][client_id]
|
263
|
+
unless dt.nil?
|
264
|
+
dt['roles'].each do |role|
|
265
|
+
return true if role.to_s == user_role.to_s
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
false
|
270
|
+
end
|
271
|
+
|
272
|
+
def self.user_signed_in?(access_token = '', client_id = '', secret = '', token_introspection_endpoint = '')
|
273
|
+
verify_setup
|
274
|
+
|
275
|
+
client_id = @client_id if isempty?(client_id)
|
276
|
+
secret = @secret if isempty?(secret)
|
277
|
+
token_introspection_endpoint = @configuration['token_introspection_endpoint'] if isempty?(token_introspection_endpoint)
|
278
|
+
|
279
|
+
begin
|
280
|
+
JSON(get_token_introspection(access_token, client_id, secret, token_introspection_endpoint))['active'] === true
|
281
|
+
rescue => e
|
282
|
+
if e.class < Keycloak::KeycloakException
|
283
|
+
raise
|
284
|
+
else
|
285
|
+
false
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.get_attribute(attributeName, access_token = '')
|
291
|
+
verify_setup
|
292
|
+
|
293
|
+
attr = decoded_access_token(access_token)[0]
|
294
|
+
attr[attributeName]
|
295
|
+
end
|
296
|
+
|
297
|
+
def self.token
|
298
|
+
if !Keycloak.proc_cookie_token.nil?
|
299
|
+
JSON Keycloak.proc_cookie_token.call
|
300
|
+
else
|
301
|
+
raise Keycloak::ProcCookieTokenNotDefined
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def self.external_attributes
|
306
|
+
if !Keycloak.proc_external_attributes.nil?
|
307
|
+
Keycloak.proc_external_attributes.call
|
308
|
+
else
|
309
|
+
raise Keycloak::ProcExternalAttributesNotDefined
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.decoded_access_token(access_token = '')
|
314
|
+
access_token = self.token["access_token"] if access_token.empty?
|
315
|
+
JWT.decode access_token, @public_key, false, { :algorithm => 'RS256' }
|
316
|
+
end
|
317
|
+
|
318
|
+
def self.decoded_refresh_token(refresh_token = '')
|
319
|
+
refresh_token = self.token["access_token"] if refresh_token.empty?
|
320
|
+
JWT.decode refresh_token, @public_key, false, { :algorithm => 'RS256' }
|
321
|
+
end
|
322
|
+
|
323
|
+
private
|
324
|
+
|
325
|
+
KEYCLOACK_CONTROLLER_DEFAULT = 'session'.freeze
|
326
|
+
|
327
|
+
def self.get_installation
|
328
|
+
if File.exists?(Keycloak.installation_file)
|
329
|
+
installation = JSON File.read(Keycloak.installation_file)
|
330
|
+
@realm = installation["realm"]
|
331
|
+
@client_id = installation["resource"]
|
332
|
+
@secret = installation["credentials"]["secret"]
|
333
|
+
@public_key = installation["realm-public-key"]
|
334
|
+
@auth_server_url = installation["auth-server-url"]
|
335
|
+
else
|
336
|
+
raise "#{Keycloak.installation_file} and relm settings not found." if isempty?(Keycloak.realm) || isempty?(Keycloak.auth_server_url)
|
337
|
+
|
338
|
+
@realm = Keycloak.realm
|
339
|
+
@auth_server_url = Keycloak.auth_server_url
|
340
|
+
end
|
341
|
+
openid_configuration
|
342
|
+
end
|
343
|
+
|
344
|
+
def self.verify_setup
|
345
|
+
get_installation if @configuration.nil?
|
346
|
+
end
|
347
|
+
|
348
|
+
def self.setup_module
|
349
|
+
Keycloak.proxy ||= ''
|
350
|
+
Keycloak.keycloak_controller ||= KEYCLOACK_CONTROLLER_DEFAULT
|
351
|
+
Keycloak.validate_token_when_call_has_role ||= false
|
352
|
+
get_installation
|
353
|
+
end
|
354
|
+
|
355
|
+
def self.exec_request(proc_request)
|
356
|
+
if Keycloak.explode_exception
|
357
|
+
proc_request.call
|
358
|
+
else
|
359
|
+
begin
|
360
|
+
proc_request.call
|
361
|
+
rescue RestClient::ExceptionWithResponse => err
|
362
|
+
err.response
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def self.openid_configuration
|
368
|
+
RestClient.proxy = Keycloak.proxy unless isempty?(Keycloak.proxy)
|
369
|
+
config_url = "#{@auth_server_url}/realms/#{@realm}/.well-known/openid-configuration"
|
370
|
+
_request = -> do
|
371
|
+
RestClient.get config_url
|
372
|
+
end
|
373
|
+
response = exec_request _request
|
374
|
+
if response.code == 200
|
375
|
+
@configuration = JSON response.body
|
376
|
+
else
|
377
|
+
response.return!
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def self.mount_request_token(payload)
|
382
|
+
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
383
|
+
|
384
|
+
_request = -> do
|
385
|
+
RestClient.post(@configuration['token_endpoint'], payload, header){|response, request, result|
|
386
|
+
case response.code
|
387
|
+
when 200
|
388
|
+
response.body
|
389
|
+
else
|
390
|
+
response.return!
|
391
|
+
end
|
392
|
+
}
|
393
|
+
end
|
394
|
+
|
395
|
+
exec_request _request
|
396
|
+
end
|
397
|
+
|
398
|
+
def self.decoded_id_token(idToken = '')
|
399
|
+
tk = self.token
|
400
|
+
idToken = tk["id_token"] if idToken.empty?
|
401
|
+
if idToken
|
402
|
+
@decoded_id_token = JWT.decode idToken, @public_key, false, { :algorithm => 'RS256' }
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
end
|
407
|
+
|
408
|
+
# Os recursos desse module (admin) serão utilizadas apenas por usuários que possuem as roles do client realm-management
|
409
|
+
module Admin
|
410
|
+
class << self
|
411
|
+
end
|
412
|
+
|
413
|
+
def self.get_users(query_parameters = nil, access_token = nil)
|
414
|
+
generic_get("users/", query_parameters, access_token)
|
415
|
+
end
|
416
|
+
|
417
|
+
def self.create_user(user_representation, access_token = nil)
|
418
|
+
generic_post("users/", nil, user_representation, access_token)
|
419
|
+
end
|
420
|
+
|
421
|
+
def self.count_users(access_token = nil)
|
422
|
+
generic_get("users/count/", nil, access_token)
|
423
|
+
end
|
424
|
+
|
425
|
+
def self.get_user(id, access_token = nil)
|
426
|
+
generic_get("users/#{id}", nil, access_token)
|
427
|
+
end
|
428
|
+
|
429
|
+
def self.update_user(id, user_representation, access_token = nil)
|
430
|
+
generic_put("users/#{id}", nil, user_representation, access_token)
|
431
|
+
end
|
432
|
+
|
433
|
+
def self.delete_user(id, access_token = nil)
|
434
|
+
generic_delete("users/#{id}", nil, nil, access_token)
|
435
|
+
end
|
436
|
+
|
437
|
+
def self.revoke_consent_user(id, client_id = nil, access_token = nil)
|
438
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
439
|
+
generic_delete("users/#{id}/consents/#{client_id}", nil, nil, access_token)
|
440
|
+
end
|
441
|
+
|
442
|
+
def self.update_account_email(id, actions, redirect_uri = '', client_id = nil, access_token = nil)
|
443
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
444
|
+
generic_put("users/#{id}/execute-actions-email", { redirect_uri: redirect_uri, client_id: client_id }, actions, access_token)
|
445
|
+
end
|
446
|
+
|
447
|
+
def self.get_role_mappings(id, access_token = nil)
|
448
|
+
generic_get("users/#{id}/role-mappings", nil, access_token)
|
449
|
+
end
|
450
|
+
|
451
|
+
def self.get_groups(query_parameters = nil, access_token = nil)
|
452
|
+
generic_get("groups/", query_parameters, access_token)
|
453
|
+
end
|
454
|
+
|
455
|
+
def self.get_clients(query_parameters = nil, access_token = nil)
|
456
|
+
generic_get("clients/", query_parameters, access_token)
|
457
|
+
end
|
458
|
+
|
459
|
+
def self.get_all_roles_client(id, access_token = nil)
|
460
|
+
generic_get("clients/#{id}/roles", nil, access_token)
|
461
|
+
end
|
462
|
+
|
463
|
+
def self.get_roles_client_by_name(id, role_name, access_token = nil)
|
464
|
+
generic_get("clients/#{id}/roles/#{role_name}", nil, access_token)
|
465
|
+
end
|
466
|
+
|
467
|
+
def self.add_client_level_roles_to_user(id, client, role_representation, access_token = nil)
|
468
|
+
generic_post("users/#{id}/role-mappings/clients/#{client}", nil, role_representation, access_token)
|
469
|
+
end
|
470
|
+
|
471
|
+
def self.delete_client_level_roles_from_user(id, client, role_representation, access_token = nil)
|
472
|
+
generic_delete("users/#{id}/role-mappings/clients/#{client}", nil, role_representation, access_token)
|
473
|
+
end
|
474
|
+
|
475
|
+
def self.get_client_level_role_for_user_and_app(id, client, access_token = nil)
|
476
|
+
generic_get("users/#{id}/role-mappings/clients/#{client}", nil, access_token)
|
477
|
+
end
|
478
|
+
|
479
|
+
def self.update_effective_user_roles(id, client_id, roles_names, access_token = nil)
|
480
|
+
client = JSON get_clients({ clientId: client_id }, access_token)
|
481
|
+
|
482
|
+
user_roles = JSON get_client_level_role_for_user_and_app(id, client[0]['id'], access_token)
|
483
|
+
|
484
|
+
roles = Array.new
|
485
|
+
# Include new role
|
486
|
+
roles_names.each do |r|
|
487
|
+
if r && !r.empty?
|
488
|
+
found = false
|
489
|
+
user_roles.each do |ur|
|
490
|
+
found = ur['name'] == r
|
491
|
+
break if found
|
492
|
+
found = false
|
493
|
+
end
|
494
|
+
if !found
|
495
|
+
role = JSON get_roles_client_by_name(client[0]['id'], r, access_token)
|
496
|
+
roles.push(role)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
garbage_roles = Array.new
|
502
|
+
# Exclude old role
|
503
|
+
user_roles.each do |ur|
|
504
|
+
found = false
|
505
|
+
roles_names.each do |r|
|
506
|
+
if r && !r.empty?
|
507
|
+
found = ur['name'] == r
|
508
|
+
break if found
|
509
|
+
found = false
|
510
|
+
end
|
511
|
+
end
|
512
|
+
if !found
|
513
|
+
garbage_roles.push(ur)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
if garbage_roles.count > 0
|
518
|
+
delete_client_level_roles_from_user(id, client[0]['id'], garbage_roles, access_token)
|
519
|
+
end
|
520
|
+
|
521
|
+
if roles.count > 0
|
522
|
+
add_client_level_roles_to_user(id, client[0]['id'], roles, access_token)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def self.reset_password(id, credential_representation, access_token = nil)
|
527
|
+
generic_put("users/#{id}/reset-password", nil, credential_representation, access_token)
|
528
|
+
end
|
529
|
+
|
530
|
+
def self.get_effective_client_level_role_composite_user(id, client, access_token = nil)
|
531
|
+
generic_get("users/#{id}/role-mappings/clients/#{client}/composite", nil, access_token)
|
532
|
+
end
|
533
|
+
|
534
|
+
# Generics methods
|
535
|
+
|
536
|
+
def self.generic_get(service, query_parameters = nil, access_token = nil)
|
537
|
+
Keycloak.generic_request(effective_access_token(access_token), full_url(service), query_parameters, nil, 'GET')
|
538
|
+
end
|
539
|
+
|
540
|
+
def self.generic_post(service, query_parameters, body_parameter, access_token = nil)
|
541
|
+
Keycloak.generic_request(effective_access_token(access_token), full_url(service), query_parameters, body_parameter, 'POST')
|
542
|
+
end
|
543
|
+
|
544
|
+
def self.generic_put(service, query_parameters, body_parameter, access_token = nil)
|
545
|
+
Keycloak.generic_request(effective_access_token(access_token), full_url(service), query_parameters, body_parameter, 'PUT')
|
546
|
+
end
|
547
|
+
|
548
|
+
def self.generic_delete(service, query_parameters = nil, body_parameter = nil, access_token = nil)
|
549
|
+
Keycloak.generic_request(effective_access_token(access_token), full_url(service), query_parameters, body_parameter, 'DELETE')
|
550
|
+
end
|
551
|
+
|
552
|
+
private
|
553
|
+
|
554
|
+
def self.effective_access_token(access_token)
|
555
|
+
if isempty?(access_token)
|
556
|
+
Keycloak::Client.token['access_token']
|
557
|
+
else
|
558
|
+
access_token
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
def self.base_url
|
563
|
+
Keycloak::Client.auth_server_url + "/admin/realms/#{Keycloak::Client.realm}/"
|
564
|
+
end
|
565
|
+
|
566
|
+
def self.full_url(service)
|
567
|
+
base_url + service
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
module Internal
|
572
|
+
include Keycloak::Admin
|
573
|
+
|
574
|
+
class << self
|
575
|
+
end
|
576
|
+
|
577
|
+
def self.get_users(query_parameters = nil, client_id = '', secret = '')
|
578
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
579
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
580
|
+
|
581
|
+
proc = lambda {|token|
|
582
|
+
Keycloak::Admin.get_users(query_parameters, token['access_token'])
|
583
|
+
}
|
584
|
+
|
585
|
+
default_call(proc, client_id, secret)
|
586
|
+
end
|
587
|
+
|
588
|
+
def self.get_groups(query_parameters = nil, client_id = '', secret = '')
|
589
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
590
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
591
|
+
|
592
|
+
proc = lambda { |token|
|
593
|
+
Keycloak::Admin.get_groups(query_parameters, token['access_token'])
|
594
|
+
}
|
595
|
+
|
596
|
+
default_call(proc, client_id, secret)
|
597
|
+
end
|
598
|
+
|
599
|
+
def self.change_password(user_id, redirect_uri = '', client_id = '', secret = '')
|
600
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
601
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
602
|
+
|
603
|
+
proc = lambda {|token|
|
604
|
+
Keycloak.generic_request(token['access_token'],
|
605
|
+
Keycloak::Admin.full_url("users/#{user_id}/execute-actions-email"),
|
606
|
+
{ redirect_uri: redirect_uri, client_id: client_id },
|
607
|
+
['UPDATE_PASSWORD'],
|
608
|
+
'PUT')
|
609
|
+
}
|
610
|
+
|
611
|
+
default_call(proc, client_id, secret)
|
612
|
+
end
|
613
|
+
|
614
|
+
def self.forgot_password(user_login, redirect_uri = '', client_id = '', secret = '')
|
615
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
616
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
617
|
+
|
618
|
+
user = get_user_info(user_login, true, client_id, secret)
|
619
|
+
change_password(user['id'], redirect_uri, client_id, secret)
|
620
|
+
end
|
621
|
+
|
622
|
+
def self.get_logged_user_info(client_id = '', secret = '')
|
623
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
624
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
625
|
+
|
626
|
+
proc = lambda {|token|
|
627
|
+
userinfo = JSON Keycloak::Client.get_userinfo
|
628
|
+
Keycloak.generic_request(token['access_token'],
|
629
|
+
Keycloak::Admin.full_url("users/#{userinfo['sub']}"),
|
630
|
+
nil, nil, 'GET')
|
631
|
+
}
|
632
|
+
|
633
|
+
default_call(proc, client_id, secret)
|
634
|
+
end
|
635
|
+
|
636
|
+
def self.get_user_info(user_login, whole_word = false, client_id = '', secret = '')
|
637
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
638
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
639
|
+
|
640
|
+
proc = lambda { |token|
|
641
|
+
if user_login.index('@').nil?
|
642
|
+
search = {:username => user_login}
|
643
|
+
else
|
644
|
+
search = {:email => user_login}
|
645
|
+
end
|
646
|
+
users = JSON Keycloak.generic_request(token["access_token"],
|
647
|
+
Keycloak::Admin.full_url("users/"),
|
648
|
+
search, nil, 'GET')
|
649
|
+
users[0]
|
650
|
+
if users.count.zero?
|
651
|
+
raise Keycloak::UserLoginNotFound
|
652
|
+
else
|
653
|
+
efective_index = -1
|
654
|
+
users.each_with_index do |user, i|
|
655
|
+
if whole_word
|
656
|
+
efective_index = i if user_login == user['username'] || user_login == user['email']
|
657
|
+
else
|
658
|
+
efective_index = 0
|
659
|
+
end
|
660
|
+
break if efective_index >= 0
|
661
|
+
end
|
662
|
+
|
663
|
+
if efective_index >= 0
|
664
|
+
if whole_word
|
665
|
+
users[efective_index]
|
666
|
+
else
|
667
|
+
users
|
668
|
+
end
|
669
|
+
else
|
670
|
+
raise Keycloak::UserLoginNotFound
|
671
|
+
end
|
672
|
+
end
|
673
|
+
}
|
674
|
+
|
675
|
+
default_call(proc, client_id, secret)
|
676
|
+
end
|
677
|
+
|
678
|
+
def self.exists_name_or_email(value, user_id = '', client_id = '', secret = '')
|
679
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
680
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
681
|
+
|
682
|
+
begin
|
683
|
+
usuario = Keycloak::Internal.get_user_info(value, true, client_id, secret)
|
684
|
+
if user_id.empty? || user_id != usuario['id']
|
685
|
+
!isempty?(usuario)
|
686
|
+
else
|
687
|
+
false
|
688
|
+
end
|
689
|
+
rescue StandardError
|
690
|
+
false
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
def self.logged_federation_user?(client_id = '', secret = '')
|
695
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
696
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
697
|
+
info = get_logged_user_info(client_id, secret)
|
698
|
+
info['federationLink'] != nil
|
699
|
+
end
|
700
|
+
|
701
|
+
def self.create_simple_user(username, password, email, first_name, last_name, realm_roles_names, client_roles_names, proc = nil, client_id = '', secret = '')
|
702
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
703
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
704
|
+
|
705
|
+
begin
|
706
|
+
username.downcase!
|
707
|
+
user = get_user_info(username, true, client_id, secret)
|
708
|
+
new_user = false
|
709
|
+
rescue Keycloak::UserLoginNotFound
|
710
|
+
new_user = true
|
711
|
+
rescue
|
712
|
+
raise
|
713
|
+
end
|
714
|
+
|
715
|
+
proc_default = lambda { |token|
|
716
|
+
user_representation = { username: username,
|
717
|
+
email: email,
|
718
|
+
firstName: first_name,
|
719
|
+
lastName: last_name,
|
720
|
+
enabled: true }
|
721
|
+
|
722
|
+
if !new_user || Keycloak.generic_request(token["access_token"],
|
723
|
+
Keycloak::Admin.full_url("users/"),
|
724
|
+
nil, user_representation, 'POST')
|
725
|
+
|
726
|
+
user = get_user_info(username, true, client_id, secret) if new_user
|
727
|
+
|
728
|
+
credential_representation = { type: "password",
|
729
|
+
temporary: false,
|
730
|
+
value: password }
|
731
|
+
|
732
|
+
if user['federationLink'] != nil || Keycloak.generic_request(token["access_token"],
|
733
|
+
Keycloak::Admin.full_url("users/#{user['id']}/reset-password"),
|
734
|
+
nil, credential_representation, 'PUT')
|
735
|
+
|
736
|
+
client = JSON Keycloak.generic_request(token["access_token"],
|
737
|
+
Keycloak::Admin.full_url("clients/"),
|
738
|
+
{ clientId: client_id }, nil, 'GET')
|
739
|
+
|
740
|
+
if client_roles_names.count > 0
|
741
|
+
roles = []
|
742
|
+
client_roles_names.each do |r|
|
743
|
+
unless isempty?(r)
|
744
|
+
role = JSON Keycloak.generic_request(token["access_token"],
|
745
|
+
Keycloak::Admin.full_url("clients/#{client[0]['id']}/roles/#{r}"),
|
746
|
+
nil, nil, 'GET')
|
747
|
+
roles.push(role)
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
if roles.count > 0
|
752
|
+
Keycloak.generic_request(token["access_token"],
|
753
|
+
Keycloak::Admin.full_url("users/#{user['id']}/role-mappings/clients/#{client[0]['id']}"),
|
754
|
+
nil, roles, 'POST')
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
if realm_roles_names.count > 0
|
759
|
+
roles = []
|
760
|
+
realm_roles_names.each do |r|
|
761
|
+
unless isempty?(r)
|
762
|
+
role = JSON Keycloak.generic_request(token["access_token"],
|
763
|
+
Keycloak::Admin.full_url("roles/#{r}"),
|
764
|
+
nil, nil, 'GET')
|
765
|
+
roles.push(role)
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
if roles.count > 0
|
770
|
+
Keycloak.generic_request(token["access_token"],
|
771
|
+
Keycloak::Admin.full_url("users/#{user['id']}/role-mappings/realm"),
|
772
|
+
nil, roles, 'POST')
|
773
|
+
end
|
774
|
+
else
|
775
|
+
true
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
}
|
780
|
+
|
781
|
+
if default_call(proc_default, client_id, secret)
|
782
|
+
proc.call user unless proc.nil?
|
783
|
+
end
|
784
|
+
end
|
785
|
+
|
786
|
+
def self.create_starter_user(username, password, email, client_roles_names, proc = nil, client_id = '', secret = '')
|
787
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
788
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
789
|
+
Keycloak::Internal.create_simple_user(username, password, email, '', '', [], client_roles_names, proc, client_id, secret)
|
790
|
+
end
|
791
|
+
|
792
|
+
def self.get_client_roles(client_id = '', secret = '')
|
793
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
794
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
795
|
+
|
796
|
+
proc = lambda {|token|
|
797
|
+
client = JSON Keycloak::Admin.get_clients({ clientId: client_id }, token['access_token'])
|
798
|
+
|
799
|
+
Keycloak.generic_request(token['access_token'],
|
800
|
+
Keycloak::Admin.full_url("clients/#{client[0]['id']}/roles"),
|
801
|
+
nil, nil, 'GET')
|
802
|
+
}
|
803
|
+
|
804
|
+
default_call(proc, client_id, secret)
|
805
|
+
end
|
806
|
+
|
807
|
+
def self.get_client_user_roles(user_id, client_id = '', secret = '')
|
808
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
809
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
810
|
+
|
811
|
+
proc = lambda {|token|
|
812
|
+
client = JSON Keycloak::Admin.get_clients({ clientId: client_id }, token["access_token"])
|
813
|
+
Keycloak::Admin.get_effective_client_level_role_composite_user(user_id, client[0]['id'], token["access_token"])
|
814
|
+
}
|
815
|
+
|
816
|
+
default_call(proc, client_id, secret)
|
817
|
+
end
|
818
|
+
|
819
|
+
def self.has_role?(user_id, user_role, client_id = '', secret = '')
|
820
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
821
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
822
|
+
|
823
|
+
roles = JSON get_client_user_roles(user_id, client_id, secret)
|
824
|
+
if !roles.nil?
|
825
|
+
roles.each do |role|
|
826
|
+
return true if role['name'].to_s == user_role.to_s
|
827
|
+
end
|
828
|
+
false
|
829
|
+
else
|
830
|
+
false
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
protected
|
835
|
+
|
836
|
+
def self.default_call(proc, client_id = '', secret = '')
|
837
|
+
begin
|
838
|
+
tk = nil
|
839
|
+
resp = nil
|
840
|
+
|
841
|
+
Keycloak::Client.get_installation
|
842
|
+
|
843
|
+
client_id = Keycloak::Client.client_id if isempty?(client_id)
|
844
|
+
secret = Keycloak::Client.secret if isempty?(secret)
|
845
|
+
|
846
|
+
payload = { 'client_id' => client_id,
|
847
|
+
'client_secret' => secret,
|
848
|
+
'grant_type' => 'client_credentials' }
|
849
|
+
|
850
|
+
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
851
|
+
|
852
|
+
_request = -> do
|
853
|
+
RestClient.post(Keycloak::Client.configuration['token_endpoint'], payload, header){|response, request, result|
|
854
|
+
case response.code
|
855
|
+
when 200..399
|
856
|
+
tk = JSON response.body
|
857
|
+
resp = proc.call(tk)
|
858
|
+
else
|
859
|
+
response.return!
|
860
|
+
end
|
861
|
+
}
|
862
|
+
end
|
863
|
+
|
864
|
+
Keycloak::Client.exec_request _request
|
865
|
+
ensure
|
866
|
+
if tk
|
867
|
+
payload = { 'client_id' => client_id,
|
868
|
+
'client_secret' => secret,
|
869
|
+
'refresh_token' => tk["refresh_token"] }
|
870
|
+
|
871
|
+
header = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
872
|
+
_request = -> do
|
873
|
+
RestClient.post(Keycloak::Client.configuration['end_session_endpoint'], payload, header){|response, request, result|
|
874
|
+
case response.code
|
875
|
+
when 200..399
|
876
|
+
resp if resp.nil?
|
877
|
+
else
|
878
|
+
response.return!
|
879
|
+
end
|
880
|
+
}
|
881
|
+
end
|
882
|
+
Keycloak::Client.exec_request _request
|
883
|
+
end
|
884
|
+
end
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
private
|
889
|
+
|
890
|
+
def self.generic_request(access_token, uri, query_parameters, body_parameter, method)
|
891
|
+
Keycloak::Client.verify_setup
|
892
|
+
final_url = uri
|
893
|
+
|
894
|
+
header = {'Content-Type' => 'application/x-www-form-urlencoded',
|
895
|
+
'Authorization' => "Bearer #{access_token}"}
|
896
|
+
|
897
|
+
if query_parameters
|
898
|
+
parameters = URI.encode_www_form(query_parameters)
|
899
|
+
final_url = final_url << '?' << parameters
|
900
|
+
end
|
901
|
+
|
902
|
+
case method.upcase
|
903
|
+
when 'GET'
|
904
|
+
_request = -> do
|
905
|
+
RestClient.get(final_url, header){|response, request, result|
|
906
|
+
rescue_response(response)
|
907
|
+
}
|
908
|
+
end
|
909
|
+
when 'POST', 'PUT'
|
910
|
+
header["Content-Type"] = 'application/json'
|
911
|
+
parameters = JSON.generate body_parameter
|
912
|
+
_request = -> do
|
913
|
+
case method.upcase
|
914
|
+
when 'POST'
|
915
|
+
RestClient.post(final_url, parameters, header){|response, request, result|
|
916
|
+
rescue_response(response)
|
917
|
+
}
|
918
|
+
else
|
919
|
+
RestClient.put(final_url, parameters, header){|response, request, result|
|
920
|
+
rescue_response(response)
|
921
|
+
}
|
922
|
+
end
|
923
|
+
end
|
924
|
+
when 'DELETE'
|
925
|
+
_request = -> do
|
926
|
+
if body_parameter
|
927
|
+
header["Content-Type"] = 'application/json'
|
928
|
+
parameters = JSON.generate body_parameter
|
929
|
+
RestClient::Request.execute(method: :delete, url: final_url,
|
930
|
+
payload: parameters, headers: header) { |response, request, result|
|
931
|
+
rescue_response(response)
|
932
|
+
}
|
933
|
+
else
|
934
|
+
RestClient.delete(final_url, header) { |response, request, result|
|
935
|
+
rescue_response(response)
|
936
|
+
}
|
937
|
+
end
|
938
|
+
end
|
939
|
+
else
|
940
|
+
raise
|
941
|
+
end
|
942
|
+
|
943
|
+
_request.call
|
944
|
+
end
|
945
|
+
|
946
|
+
def self.rescue_response(response)
|
947
|
+
case response.code
|
948
|
+
when 200..399
|
949
|
+
if response.body.empty?
|
950
|
+
true
|
951
|
+
else
|
952
|
+
response.body
|
953
|
+
end
|
954
|
+
when 400..499
|
955
|
+
response.return!
|
956
|
+
else
|
957
|
+
if Keycloak.explode_exception
|
958
|
+
response.return!
|
959
|
+
else
|
960
|
+
begin
|
961
|
+
response.return!
|
962
|
+
rescue RestClient::ExceptionWithResponse => err
|
963
|
+
err.response
|
964
|
+
rescue StandardError => e
|
965
|
+
e.message
|
966
|
+
end
|
967
|
+
end
|
968
|
+
end
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
require 'keycloak/exceptions'
|