keycloak-ruby 1.0

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