sqsc-keycloak-ruby 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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__)
@@ -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
@@ -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,10 @@
1
+ module Keycloak
2
+ module Generators
3
+ class ConfigGenerator < Rails::Generators::Base
4
+ source_root(File.expand_path(File.dirname(__FILE__)))
5
+ def copy_initializer
6
+ copy_file '../../keycloak.rb', 'config/initializers/keycloak.rb'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -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,3 @@
1
+ module Keycloak
2
+ VERSION = '1.0.0.pre'.freeze
3
+ 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'