sqsc-keycloak-ruby 1.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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'