keycloak-admin 2.0.2 → 2.0.3

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/MIT-LICENSE +18 -17
  4. data/README.md +66 -5
  5. data/keycloak-admin.gemspec +1 -0
  6. data/lib/keycloak-admin/client/attack_detection_client.rb +2 -1
  7. data/lib/keycloak-admin/client/client.rb +17 -4
  8. data/lib/keycloak-admin/client/client_authz_permission_client.rb +4 -3
  9. data/lib/keycloak-admin/client/client_authz_policy_client.rb +3 -2
  10. data/lib/keycloak-admin/client/client_authz_resource_client.rb +14 -10
  11. data/lib/keycloak-admin/client/client_authz_scope_client.rb +4 -3
  12. data/lib/keycloak-admin/client/client_client.rb +6 -2
  13. data/lib/keycloak-admin/client/client_role_client.rb +2 -1
  14. data/lib/keycloak-admin/client/client_role_mappings_client.rb +3 -2
  15. data/lib/keycloak-admin/client/client_scope_client.rb +2 -1
  16. data/lib/keycloak-admin/client/client_scope_protocol_mapper_client.rb +3 -2
  17. data/lib/keycloak-admin/client/configurable_token_client.rb +2 -1
  18. data/lib/keycloak-admin/client/group_client.rb +13 -4
  19. data/lib/keycloak-admin/client/identity_provider_client.rb +2 -1
  20. data/lib/keycloak-admin/client/organization_client.rb +6 -5
  21. data/lib/keycloak-admin/client/realm_client.rb +3 -2
  22. data/lib/keycloak-admin/client/resource.rb +8 -6
  23. data/lib/keycloak-admin/client/response.rb +1 -0
  24. data/lib/keycloak-admin/client/role_client.rb +3 -6
  25. data/lib/keycloak-admin/client/role_mapper_client.rb +2 -1
  26. data/lib/keycloak-admin/client/token_client.rb +1 -0
  27. data/lib/keycloak-admin/client/user_client.rb +63 -44
  28. data/lib/keycloak-admin/configuration.rb +37 -2
  29. data/lib/keycloak-admin/error.rb +1 -0
  30. data/lib/keycloak-admin/path_encoding.rb +24 -0
  31. data/lib/keycloak-admin/representation/attack_detection_representation.rb +1 -0
  32. data/lib/keycloak-admin/representation/camel_json.rb +7 -3
  33. data/lib/keycloak-admin/representation/client_authz_policy_config_representation.rb +1 -0
  34. data/lib/keycloak-admin/representation/client_authz_policy_representation.rb +1 -0
  35. data/lib/keycloak-admin/representation/client_authz_resource_representation.rb +1 -0
  36. data/lib/keycloak-admin/representation/client_authz_scope_representation.rb +1 -0
  37. data/lib/keycloak-admin/representation/client_representation.rb +4 -5
  38. data/lib/keycloak-admin/representation/client_scope_representation.rb +1 -0
  39. data/lib/keycloak-admin/representation/credential_representation.rb +1 -0
  40. data/lib/keycloak-admin/representation/federated_identity_representation.rb +1 -0
  41. data/lib/keycloak-admin/representation/group_representation.rb +1 -0
  42. data/lib/keycloak-admin/representation/identity_provider_mapper_representation.rb +1 -0
  43. data/lib/keycloak-admin/representation/identity_provider_representation.rb +1 -0
  44. data/lib/keycloak-admin/representation/impersonation_redirection_representation.rb +1 -0
  45. data/lib/keycloak-admin/representation/impersonation_representation.rb +1 -0
  46. data/lib/keycloak-admin/representation/member_representation.rb +1 -0
  47. data/lib/keycloak-admin/representation/organization_domain_representation.rb +1 -0
  48. data/lib/keycloak-admin/representation/organization_representation.rb +1 -1
  49. data/lib/keycloak-admin/representation/protocol_mapper_representation.rb +1 -0
  50. data/lib/keycloak-admin/representation/realm_representation.rb +1 -0
  51. data/lib/keycloak-admin/representation/representation.rb +7 -5
  52. data/lib/keycloak-admin/representation/role_representation.rb +2 -1
  53. data/lib/keycloak-admin/representation/session_representation.rb +2 -1
  54. data/lib/keycloak-admin/representation/token_representation.rb +1 -0
  55. data/lib/keycloak-admin/representation/user_representation.rb +1 -0
  56. data/lib/keycloak-admin/resource/base_role_containing_resource.rb +4 -1
  57. data/lib/keycloak-admin/resource/group_resource.rb +1 -0
  58. data/lib/keycloak-admin/resource/user_resource.rb +1 -0
  59. data/lib/keycloak-admin/version.rb +2 -1
  60. data/lib/keycloak-admin.rb +3 -0
  61. metadata +16 -1
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "faraday"
2
3
  require "uri"
3
4
 
@@ -18,13 +19,11 @@ module KeycloakAdmin
18
19
  new(url, options, logger).send(:request, method, payload, headers)
19
20
  end
20
21
 
21
- def self.put(url, payload, headers = {}, logger = nil)
22
- new(url, {}, logger).put(payload, headers)
23
- end
24
-
25
22
  def initialize(url, options = {}, logger = nil)
23
+ options = (options || {}).dup
24
+ @adapter = options.delete(:adapter)
26
25
  @url = url
27
- @options = options || {}
26
+ @options = options
28
27
  @logger = logger
29
28
  end
30
29
 
@@ -75,7 +74,10 @@ module KeycloakAdmin
75
74
  # headers by default, which would print the bearer token on every call; headers: false
76
75
  # keeps this to method/url/status only. Bodies are already off by Faraday's own default.
77
76
  f.response :logger, @logger, headers: false if @logger
78
- f.adapter Faraday.default_adapter
77
+ # Faraday's default net_http adapter wraps every request in http.start, which reopens
78
+ # the connection each time; config.faraday_adapter lets a caller swap in a pooling one.
79
+ # Splatted so an adapter needing arguments can be given as [:name, options].
80
+ f.adapter(*Array(@adapter || Faraday.default_adapter))
79
81
  end
80
82
  end
81
83
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  # Wraps a Faraday::Response the way RestClient::Response used to: as the body string
3
4
  # itself (RestClient::Response < String), so existing call sites that do JSON.parse(response)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class RoleClient < Client
3
4
  def initialize(configuration, realm_client)
@@ -15,8 +16,6 @@ module KeycloakAdmin
15
16
 
16
17
  # Returns the role representation for the specified role name
17
18
  def get(name)
18
- # allows special characters in the name like space
19
- name = URI.encode_uri_component(name)
20
19
  response = execute_http do
21
20
  resource(role_name_url(name)).get(headers)
22
21
  end
@@ -25,8 +24,6 @@ module KeycloakAdmin
25
24
 
26
25
  # Lists all groups that have the specified role name assigned
27
26
  def list_groups(name)
28
- # allows special characters in the name like space
29
- name = URI.encode_uri_component(name)
30
27
  response = execute_http do
31
28
  resource("#{role_name_url(name)}/groups").get(headers)
32
29
  end
@@ -49,11 +46,11 @@ module KeycloakAdmin
49
46
  end
50
47
 
51
48
  def role_id_url(id)
52
- "#{@realm_client.realm_admin_url}/roles-by-id/#{id}"
49
+ "#{@realm_client.realm_admin_url}/roles-by-id/#{encode_segment(id)}"
53
50
  end
54
51
 
55
52
  def role_name_url(name)
56
- "#{@realm_client.realm_admin_url}/roles/#{name}"
53
+ "#{@realm_client.realm_admin_url}/roles/#{encode_segment(name)}"
57
54
  end
58
55
  end
59
56
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class RoleMapperClient < Client
3
4
  def initialize(configuration, user_resource)
@@ -23,7 +24,7 @@ module KeycloakAdmin
23
24
  def remove_realm_level(role_representation_list)
24
25
  execute_http do
25
26
  Resource.execute(
26
- @configuration.faraday_options.merge(
27
+ connection_options.merge(
27
28
  method: :delete,
28
29
  url: realm_level_url,
29
30
  payload: create_payload(role_representation_list),
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class TokenClient < Client
3
4
  def initialize(configuration, realm_client)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class UserClient < Client
3
4
  def initialize(configuration, realm_client)
@@ -6,55 +7,61 @@ module KeycloakAdmin
6
7
  @realm_client = realm_client
7
8
  end
8
9
 
10
+ # Reads the id of the new user off the Location header rather than searching for it
11
+ # afterwards: Keycloak's `search` parameter matches a substring of the username, email,
12
+ # first name or last name, so an existing user merely containing this email was returned
13
+ # instead of the one just created.
9
14
  def create!(username, email, password, email_verified, locale, attributes={})
10
- user = save(build(username, email, password, email_verified, locale, attributes))
11
- search(user.email)&.first
15
+ response = post_user(build(username, email, password, email_verified, locale, attributes))
16
+ get(created_id(response))
12
17
  end
13
18
 
14
19
  def save(user_representation)
15
- execute_http do
16
- resource(users_url).post(
17
- create_payload(user_representation), headers
18
- )
19
- end
20
+ post_user(user_representation)
20
21
  user_representation
21
22
  end
22
23
 
23
24
  # pay attention that, since Keycloak 24.0.4, partial updates of attributes are not authorized anymore
24
25
  def update(user_id, user_representation_body)
25
26
  raise ArgumentError.new("user_id must be defined") if user_id.nil?
26
- Resource.execute(
27
- @configuration.faraday_options.merge(
28
- method: :put,
29
- url: users_url(user_id),
30
- payload: create_payload(user_representation_body),
31
- headers: headers,
32
- logger: @configuration.logger
27
+ execute_http do
28
+ Resource.execute(
29
+ connection_options.merge(
30
+ method: :put,
31
+ url: users_url(user_id),
32
+ payload: create_payload(user_representation_body),
33
+ headers: headers,
34
+ logger: @configuration.logger
35
+ )
33
36
  )
34
- )
37
+ end
35
38
  end
36
39
 
37
40
  def add_group(user_id, group_id)
38
- Resource.execute(
39
- @configuration.faraday_options.merge(
40
- method: :put,
41
- url: "#{users_url(user_id)}/groups/#{group_id}",
42
- payload: create_payload({}),
43
- headers: headers,
44
- logger: @configuration.logger
41
+ execute_http do
42
+ Resource.execute(
43
+ connection_options.merge(
44
+ method: :put,
45
+ url: "#{users_url(user_id)}/groups/#{encode_segment(group_id)}",
46
+ payload: create_payload({}),
47
+ headers: headers,
48
+ logger: @configuration.logger
49
+ )
45
50
  )
46
- )
51
+ end
47
52
  end
48
53
 
49
54
  def remove_group(user_id, group_id)
50
- Resource.execute(
51
- @configuration.faraday_options.merge(
52
- method: :delete,
53
- url: "#{users_url(user_id)}/groups/#{group_id}",
54
- headers: headers,
55
- logger: @configuration.logger
55
+ execute_http do
56
+ Resource.execute(
57
+ connection_options.merge(
58
+ method: :delete,
59
+ url: "#{users_url(user_id)}/groups/#{encode_segment(group_id)}",
60
+ headers: headers,
61
+ logger: @configuration.logger
62
+ )
56
63
  )
57
- )
64
+ end
58
65
  end
59
66
 
60
67
  def add_client_roles_on_user(user_id, client_id, role_representations)
@@ -94,8 +101,12 @@ module KeycloakAdmin
94
101
  JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
95
102
  end
96
103
 
97
- def list
98
- search(nil)
104
+ # Keycloak answers this endpoint with at most 100 users unless `max` says otherwise, so a
105
+ # bare `list` silently truncates a larger realm. Passing neither bound sends no query
106
+ # parameter at all, exactly as before.
107
+ def list(first: nil, max: nil)
108
+ pagination = {first: first, max: max}.compact
109
+ search(pagination.empty? ? nil : pagination)
99
110
  end
100
111
 
101
112
  def delete(user_id)
@@ -112,13 +123,13 @@ module KeycloakAdmin
112
123
  JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
113
124
  end
114
125
 
115
- def update_password(user_id, new_password)
126
+ def update_password(user_id, new_password, temporary: false)
116
127
  execute_http do
117
128
  Resource.execute(
118
- @configuration.faraday_options.merge(
129
+ connection_options.merge(
119
130
  method: :put,
120
131
  url: reset_password_url(user_id),
121
- payload: { type: "password", value: new_password, temporary: false }.to_json,
132
+ payload: { type: "password", value: new_password, temporary: temporary }.to_json,
122
133
  headers: headers,
123
134
  logger: @configuration.logger
124
135
  )
@@ -148,16 +159,16 @@ module KeycloakAdmin
148
159
 
149
160
  url = execute_actions_email_url(user_id)
150
161
  url = "#{url}?#{build_query(query)}" unless query.empty?
151
- Resource.put(url, create_payload(actions), headers, @configuration.logger)
162
+ resource(url).put(create_payload(actions), headers)
152
163
  end
153
164
  user_id
154
165
  end
155
166
 
156
167
  def impersonate(user_id)
157
- impersonation = get_redirect_impersonation(user_id)
158
168
  response = execute_http do
169
+ impersonation = get_redirect_impersonation(user_id)
159
170
  Resource.execute(
160
- @configuration.faraday_options.merge(
171
+ connection_options.merge(
161
172
  method: :post,
162
173
  url: impersonation.impersonation_url,
163
174
  payload: impersonation.body.to_json,
@@ -183,7 +194,7 @@ module KeycloakAdmin
183
194
 
184
195
  execute_http do
185
196
  Resource.execute(
186
- @configuration.faraday_options.merge(
197
+ connection_options.merge(
187
198
  method: :post,
188
199
  url: logout_url(user_id),
189
200
  headers: headers,
@@ -206,7 +217,7 @@ module KeycloakAdmin
206
217
 
207
218
  execute_http do
208
219
  Resource.execute(
209
- @configuration.faraday_options.merge(
220
+ connection_options.merge(
210
221
  method: :post,
211
222
  url: federated_identity_url(user_id, idp_id),
212
223
  payload: fed_id_rep.to_json,
@@ -225,14 +236,14 @@ module KeycloakAdmin
225
236
 
226
237
  def users_url(id=nil)
227
238
  if id
228
- "#{@realm_client.realm_admin_url}/users/#{id}"
239
+ "#{@realm_client.realm_admin_url}/users/#{encode_segment(id)}"
229
240
  else
230
241
  "#{@realm_client.realm_admin_url}/users"
231
242
  end
232
243
  end
233
244
 
234
245
  def user_client_role_mappings_url(user_id, client_id)
235
- "#{users_url(user_id)}/role-mappings/clients/#{client_id}"
246
+ "#{users_url(user_id)}/role-mappings/clients/#{encode_segment(client_id)}"
236
247
  end
237
248
 
238
249
  def reset_password_url(user_id)
@@ -263,7 +274,7 @@ module KeycloakAdmin
263
274
  def federated_identity_url(user_id, identity_provider)
264
275
  raise ArgumentError.new("user_id must be defined") if user_id.nil?
265
276
  raise ArgumentError.new("identity_provider must be defined") if identity_provider.nil?
266
- "#{users_url(user_id)}/federated-identity/#{identity_provider}"
277
+ "#{users_url(user_id)}/federated-identity/#{encode_segment(identity_provider)}"
267
278
  end
268
279
 
269
280
  def logout_url(user_id)
@@ -274,13 +285,21 @@ module KeycloakAdmin
274
285
 
275
286
  private
276
287
 
288
+ def post_user(user_representation)
289
+ execute_http do
290
+ resource(users_url).post(
291
+ create_payload(user_representation), headers
292
+ )
293
+ end
294
+ end
295
+
277
296
  def build(username, email, password, email_verified, locale, attributes={})
278
297
  user = UserRepresentation.new
279
298
  user.email = email
280
299
  user.username = username
281
300
  user.email_verified = email_verified
282
301
  user.enabled = true
283
- user.attributes = attributes || {}
302
+ user.attributes = (attributes || {}).dup
284
303
  user.attributes[:locale] = locale if locale
285
304
  user.add_credential(CredentialRepresentation.from_password(password)) if !password.nil?
286
305
  user
@@ -1,13 +1,48 @@
1
+ # frozen_string_literal: true
1
2
  require "base64"
2
3
 
3
4
  module KeycloakAdmin
4
5
  class Configuration
5
- attr_accessor :server_url, :server_domain, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger, :faraday_options
6
+ attr_accessor :server_url, :server_domain, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger, :faraday_options, :faraday_adapter
6
7
 
7
8
  # Treat a cached token as expired this many seconds before its real expiry, so it
8
9
  # cannot go stale between the check below and the request that relies on it.
9
10
  TOKEN_EXPIRY_SAFETY_MARGIN_SECONDS = 10
10
11
 
12
+ FILTERED_ATTRIBUTES = %i[@client_secret @password @cached_token].freeze
13
+ FILTERED_PLACEHOLDER = "[FILTERED]".freeze
14
+
15
+ def initialize
16
+ @token_mutex = Mutex.new
17
+ end
18
+
19
+ def inspect
20
+ rendered = instance_variables.map do |name|
21
+ value = instance_variable_get(name)
22
+ shown = if FILTERED_ATTRIBUTES.include?(name) && !value.nil?
23
+ FILTERED_PLACEHOLDER
24
+ else
25
+ value.inspect
26
+ end
27
+ "#{name}=#{shown}"
28
+ end
29
+ "#<#{self.class.name} #{rendered.join(", ")}>"
30
+ end
31
+
32
+ # Returns the cached token, fetching one through the block if there is none.
33
+ # The block runs at most once across threads.
34
+ def fetch_token_once
35
+ token = cached_token
36
+ if token.nil?
37
+ @token_mutex.synchronize do
38
+ token = cached_token
39
+ token.nil? ? cache_token(yield) : token
40
+ end
41
+ else
42
+ token
43
+ end
44
+ end
45
+
11
46
  # Returns the cached TokenRepresentation, or nil if there is none or it is (about to be) expired.
12
47
  def cached_token
13
48
  return nil if @token_expires_at.nil? || Time.now >= @token_expires_at
@@ -22,8 +57,8 @@ module KeycloakAdmin
22
57
  end
23
58
 
24
59
  def clear_cached_token!
25
- @cached_token = nil
26
60
  @token_expires_at = nil
61
+ @cached_token = nil
27
62
  end
28
63
 
29
64
  def body_for_token_retrieval
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class Error < StandardError; end
3
4
 
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require "uri"
3
+
4
+ module KeycloakAdmin
5
+ # Percent-encoding for the identifiers interpolated into a URL path.
6
+ #
7
+ # Included by Client and BaseRoleContainingResource, which have no common ancestor but
8
+ # both build URLs out of caller-supplied values.
9
+ module PathEncoding
10
+ private
11
+
12
+ # Encodes '/' as %2F too, so an identifier can never open a path segment of its own.
13
+ # Values that are already URL-safe - the UUIDs Keycloak hands out - come out unchanged,
14
+ # so this is a no-op for the usual case.
15
+ #
16
+ # Without it, any identifier holding a character that is illegal in a URI (a space, most
17
+ # obviously) made Ruby's URI parser raise URI::InvalidURIError before the request was
18
+ # ever sent. That is reachable with values users choose themselves, identity provider
19
+ # aliases being the clearest case.
20
+ def encode_segment(value)
21
+ URI.encode_uri_component(value.to_s)
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class AttackDetectionRepresentation < Representation
3
4
  attr_accessor :num_failures,
@@ -1,11 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  module CamelJson
3
4
 
4
5
  def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
5
- if first_letter_in_uppercase
6
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
6
+ word = lower_case_and_underscored_word.to_s
7
+ if word.empty?
8
+ word
9
+ elsif first_letter_in_uppercase
10
+ word.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
7
11
  else
8
- lower_case_and_underscored_word[0] + camelize(lower_case_and_underscored_word)[1..-1]
12
+ word[0] + camelize(word)[1..-1]
9
13
  end
10
14
  end
11
15
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ClientAuthzPolicyConfigRepresentation < Representation
3
4
  attr_accessor :roles,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ClientAuthzPolicyRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
 
3
4
  class ClientAuthzResourceRepresentation < Representation
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ClientAuthzScopeRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ClientRepresentation < Representation
3
4
  attr_accessor :id,
@@ -26,7 +27,6 @@ module KeycloakAdmin
26
27
  :authentication_flow_binding_overrides,
27
28
  :full_scope_allowed,
28
29
  :node_re_registration_timeout,
29
- :attributes,
30
30
  :protocol_mappers,
31
31
  :default_client_scopes,
32
32
  :optional_client_scopes,
@@ -41,9 +41,9 @@ module KeycloakAdmin
41
41
  client.client_authenticator_type = hash["clientAuthenticatorType"]
42
42
  client.always_display_in_console = hash["alwaysDisplayInConsole"] || false
43
43
  client.surrogate_auth_required = hash["surrogateAuthRequired"] || false
44
- client.redirect_uris = hash["redirectUris"] || false
45
- client.web_origins = hash["webOrigins"] || false
46
- client.not_before = hash["notBefore"] || false
44
+ client.redirect_uris = hash["redirectUris"] || []
45
+ client.web_origins = hash["webOrigins"] || []
46
+ client.not_before = hash["notBefore"] || 0
47
47
  client.bearer_only = hash["bearerOnly"] || false
48
48
  client.consent_required = hash["consentRequired"] || false
49
49
  client.standard_flow_enabled = hash["standardFlowEnabled"] || false
@@ -60,7 +60,6 @@ module KeycloakAdmin
60
60
  client.authentication_flow_binding_overrides = hash["authenticationFlowBindingOverrides"] || {}
61
61
  client.full_scope_allowed = hash["fullScopeAllowed"] || false
62
62
  client.node_re_registration_timeout = hash["nodeReRegistrationTimeout"] || -1
63
- client.attributes = hash["attributes"]
64
63
  client.protocol_mappers = (hash["protocolMappers"] || []).map { |protocol_mapper_hash| ProtocolMapperRepresentation.from_hash(protocol_mapper_hash) }
65
64
  client.default_client_scopes = hash["defaultClientScopes"] || []
66
65
  client.optional_client_scopes = hash["optionalClientScopes"] || []
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ClientScopeRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class CredentialRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class FederatedIdentityRepresentation < Representation
3
4
  attr_accessor :identity_provider,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class GroupRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class IdentityProviderMapperRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class IdentityProviderRepresentation < Representation
3
4
  attr_accessor :alias,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ImpersonationRedirectionRepresentation < Representation
3
4
  attr_reader :headers, :impersonation_url, :http_method, :body
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "http-cookie"
2
3
 
3
4
  module KeycloakAdmin
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class MemberRepresentation < UserRepresentation
3
4
  attr_accessor :membership_type
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class OrganizationDomainRepresentation < Representation
3
4
  attr_accessor :name, :verified
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class OrganizationRepresentation < Representation
3
4
  attr_accessor :id,
@@ -9,7 +10,6 @@ module KeycloakAdmin
9
10
  :attributes,
10
11
  :domains,
11
12
  :members,
12
- :attributes,
13
13
  :identity_providers
14
14
 
15
15
  def self.from_hash(hash)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class ProtocolMapperRepresentation < Representation
3
4
  attr_accessor :config,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class RealmRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "json"
2
3
  require_relative "camel_json"
3
4
 
@@ -5,14 +6,15 @@ class Representation
5
6
  include ::KeycloakAdmin::CamelJson
6
7
 
7
8
  def as_json(options=nil)
8
- Hash[instance_variables.map { |ivar| [ivar.to_s[1..-1], instance_variable_get(ivar)] }]
9
+ instance_variables.each_with_object({}) do |ivar, hash|
10
+ val = instance_variable_get(ivar)
11
+ hash[ivar.to_s[1..-1]] = val unless val.nil?
12
+ end
9
13
  end
10
14
 
11
15
  def to_json(options=nil)
12
- snaked_hash = as_json(options)
13
- snaked_hash.keys.reduce({}) do |camelized_hash, key|
14
- camelized_hash[camelize(key, false)] = snaked_hash[key]
15
- camelized_hash
16
+ as_json(options).each_with_object({}) do |(key, val), hash|
17
+ hash[camelize(key, false)] = val
16
18
  end.to_json(options)
17
19
  end
18
20
 
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class RoleRepresentation < Representation
3
4
  attr_accessor :id,
4
5
  :name,
5
6
  :composite,
6
7
  :client_role,
7
- :container_id,
8
+ :container_id
8
9
 
9
10
  def self.from_hash(hash)
10
11
  role = new
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class SessionRepresentation < Representation
3
4
  attr_accessor :id,
@@ -6,7 +7,7 @@ module KeycloakAdmin
6
7
  :ip_address,
7
8
  :start,
8
9
  :last_access,
9
- :remember_me,
10
+ :remember_me
10
11
 
11
12
  def self.from_hash(hash)
12
13
  rep = new
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module KeycloakAdmin
3
4
  class TokenRepresentation < Representation
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class UserRepresentation < Representation
3
4
  attr_accessor :id,
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module KeycloakAdmin
2
3
  class BaseRoleContainingResource
4
+ include PathEncoding
5
+
3
6
  attr_reader :resource_id
4
7
 
5
8
  def initialize(configuration, realm_client, resource_id)
@@ -14,7 +17,7 @@ module KeycloakAdmin
14
17
  end
15
18
 
16
19
  def resource_url
17
- "#{@realm_client.realm_admin_url}/#{resources_name}/#{@resource_id}"
20
+ "#{@realm_client.realm_admin_url}/#{resources_name}/#{encode_segment(@resource_id)}"
18
21
  end
19
22
 
20
23
  def client_role_mappings(client_id)