keycloak-ruby 1.0

Sign up to get free protection for your applications and to get access to all the features.
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