keycloak-admin 0.7.0 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -1
  3. data/CHANGELOG.md +70 -0
  4. data/Dockerfile +1 -1
  5. data/Gemfile.lock +7 -5
  6. data/README.md +149 -3
  7. data/lib/keycloak-admin.rb +13 -0
  8. data/lib/keycloak-admin/client/client.rb +11 -3
  9. data/lib/keycloak-admin/client/client_client.rb +24 -0
  10. data/lib/keycloak-admin/client/client_role_mappings_client.rb +32 -0
  11. data/lib/keycloak-admin/client/group_client.rb +46 -0
  12. data/lib/keycloak-admin/client/realm_client.rb +54 -0
  13. data/lib/keycloak-admin/client/role_client.rb +32 -0
  14. data/lib/keycloak-admin/client/role_mapper_client.rb +20 -0
  15. data/lib/keycloak-admin/client/user_client.rb +44 -2
  16. data/lib/keycloak-admin/representation/camel_json.rb +1 -1
  17. data/lib/keycloak-admin/representation/client_representation.rb +16 -0
  18. data/lib/keycloak-admin/representation/federated_identity_representation.rb +15 -0
  19. data/lib/keycloak-admin/representation/group_representation.rb +15 -0
  20. data/lib/keycloak-admin/representation/realm_representation.rb +14 -0
  21. data/lib/keycloak-admin/representation/representation.rb +5 -1
  22. data/lib/keycloak-admin/representation/role_representation.rb +17 -0
  23. data/lib/keycloak-admin/representation/user_representation.rb +21 -14
  24. data/lib/keycloak-admin/resource/base_role_containing_resource.rb +26 -0
  25. data/lib/keycloak-admin/resource/group_resource.rb +7 -0
  26. data/lib/keycloak-admin/resource/user_resource.rb +7 -0
  27. data/lib/keycloak-admin/version.rb +1 -1
  28. data/spec/client/client_client_spec.rb +53 -0
  29. data/spec/client/client_role_mappings_client_spec.rb +82 -0
  30. data/spec/client/group_client_spec.rb +125 -0
  31. data/spec/client/realm_client_spec.rb +108 -0
  32. data/spec/client/role_client_spec.rb +83 -0
  33. data/spec/client/role_mapper_client_spec.rb +47 -0
  34. data/spec/client/user_client_spec.rb +105 -14
  35. data/spec/representation/user_representation_spec.rb +15 -0
  36. data/spec/resource/group_resource_spec.rb +14 -0
  37. data/spec/resource/user_resource_spec.rb +14 -0
  38. data/spec/spec_helper.rb +7 -0
  39. metadata +25 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 970da832a135573c026406dd6e24bebad0faadde
4
- data.tar.gz: 0116019f2c32443bbd8c9567b8243cb84e75da7a
2
+ SHA256:
3
+ metadata.gz: 4c3087f9b9079163b0648bd1a3ae30d3526fef7f8f5d7afc59f251b9f23e7507
4
+ data.tar.gz: 99eeb84049b48e7410e271f4633d0a79b765d9dd5bb4d185469eebe247c98b19
5
5
  SHA512:
6
- metadata.gz: 65fab8de261bdb5bbef86e05961e4c49858143d6b0eb3e1282ed13b67aa3d4b2e2a7bf6a54788cf02ebaee78eea9c2658644d54e70b45777e6348ac4867c199e
7
- data.tar.gz: b8ab004c2772051d424f277572b42c57ef523d04fc1f43274c97964bd4dc1291b54d24bb70b8785b0fe404c30a23f2bba2636efb244ac6a4eb663bc40e58708f
6
+ metadata.gz: 28d689fe25884746e02f3f1dba166a2c78472f7e897680b39efe2c10e782fe52a1f661265e59ee676f65a4ee8cd3cc3b99e77dcb9fc3e8a44b2a92ca976db038
7
+ data.tar.gz: d065e64a554f1991e1ec4be2c77cfb81d5e811a6db64d452bdafb7e1cf1e0ce4449444446374e9f09472fa07fee6f8a7468311ff44a86bfd7c5e1ff2cd573656
data/.gitignore CHANGED
@@ -5,4 +5,5 @@ test/dummy/db/*.sqlite3
5
5
  test/dummy/db/*.sqlite3-journal
6
6
  test/dummy/log/*.log
7
7
  test/dummy/tmp/
8
- *.gem
8
+ *.gem
9
+ .idea/
@@ -0,0 +1,70 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.7.5] - 2020-03-28
9
+
10
+ Thanks to @RomanHargrave
11
+ * Support for working with federated identity provider (broker) links
12
+
13
+ ## [0.7.4] - 2019-10-17
14
+
15
+ * Support for Rails 6
16
+
17
+ ## [0.7.3] - 2019-07-11
18
+
19
+ Thanks to @cederigo=
20
+ * For a given user, get her list of groups
21
+
22
+ ## [0.7.2] - 2019-06-17
23
+
24
+ Thanks to @vlad-ro:
25
+
26
+ * Get list of client role mappings for a group
27
+ * Save client role mappings for a user/group
28
+ * Save realm-level role mappings for a user/group
29
+
30
+ ## [0.7.1] - 2019-06-11
31
+
32
+ Thanks to @vlad-ro:
33
+
34
+ * List users
35
+ * List clients
36
+ * List groups, create/save a group
37
+ * List roles, save a role
38
+ * List realms, save/update/delete a realm
39
+ * Get list of client role mappings for a user
40
+ * Support passing rest client options for user save and search
41
+ * Support using gem without ActiveSupport
42
+
43
+ ## [0.7.0] - 2019-06-06
44
+
45
+ Thanks to @vlad-ro:
46
+
47
+ * Support passing rest client options
48
+ * More documentation
49
+ * More tests
50
+ * Better handling of timeouts
51
+
52
+ ## [0.6.5] - 2019-05-14
53
+
54
+ * Get user
55
+
56
+ ## [0.6.2] - 2019-05-14
57
+
58
+ * Update users
59
+
60
+ ## [0.6] - 2019-03-06
61
+
62
+ * Save a locale when creating a new user
63
+
64
+ ## [0.5] - 2018-01-26
65
+
66
+ * Client to access Custom REST API configurable-token
67
+
68
+ ## [0.3] - 2018-01-19
69
+
70
+ * Support of impersonation
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:2.3
1
+ FROM ruby:2.6.5
2
2
  RUN mkdir -p /usr/src/app/lib/keycloak-admin
3
3
  WORKDIR /usr/src/app
4
4
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keycloak-admin (0.7.0)
4
+ keycloak-admin (0.7.5)
5
5
  http-cookie (~> 1.0, >= 1.0.3)
6
6
  rest-client (~> 2.0)
7
7
 
@@ -10,15 +10,17 @@ GEM
10
10
  specs:
11
11
  byebug (9.1.0)
12
12
  diff-lcs (1.3)
13
- domain_name (0.5.20180417)
13
+ domain_name (0.5.20190701)
14
14
  unf (>= 0.0.5, < 1.0.0)
15
+ http-accept (1.7.0)
15
16
  http-cookie (1.0.3)
16
17
  domain_name (~> 0.5)
17
- mime-types (3.2.2)
18
+ mime-types (3.3.1)
18
19
  mime-types-data (~> 3.2015)
19
- mime-types-data (3.2019.0331)
20
+ mime-types-data (3.2019.1009)
20
21
  netrc (0.11.0)
21
- rest-client (2.0.2)
22
+ rest-client (2.1.0)
23
+ http-accept (>= 1.7.0, < 2.0)
22
24
  http-cookie (>= 1.0.2, < 2.0)
23
25
  mime-types (>= 1.16, < 4.0)
24
26
  netrc (~> 0.8)
data/README.md CHANGED
@@ -12,7 +12,7 @@ This gem *does not* require Rails.
12
12
  For example, using `bundle`, add this line to your Gemfile.
13
13
 
14
14
  ```ruby
15
- gem "keycloak-admin", "0.7.0"
15
+ gem "keycloak-admin", "0.7.5"
16
16
  ```
17
17
 
18
18
  ## Login
@@ -58,6 +58,7 @@ KeycloakAdmin.configure do |config|
58
58
  config.username = ENV["KEYCLOAK_ADMIN_USER"]
59
59
  config.password = ENV["KEYCLOAK_ADMIN_PASSWORD"]
60
60
  config.logger = Rails.logger
61
+ config.rest_client_options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE }
61
62
  end
62
63
  ```
63
64
  This example is autoloaded in a Rails environment.
@@ -76,6 +77,7 @@ All options have a default value. However, all of them can be changed in your in
76
77
  | `username` | `nil`| String | Optional | Username to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `mummy` |
77
78
  | `password` | `nil`| String | Optional | Clear password to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `bobby` |
78
79
  | `logger` | `Logger.new(STDOUT)`| Logger | Optional | The logger used by `keycloak-admin` | `Rails.logger` | 
80
+ | `rest_client_options` | `{}`| Hash | Optional | Options to pass to `RestClient` | `{ verify_ssl: OpenSSL::SSL::VERIFY_NONE }` | 
79
81
 
80
82
 
81
83
  ## Use Case
@@ -83,11 +85,19 @@ All options have a default value. However, all of them can be changed in your in
83
85
  ### Supported features
84
86
 
85
87
  * Get an access token
86
- * Create/update/get a user
88
+ * Create/update/get/delete a user
89
+ * Get list of users, search for user(s)
87
90
  * Reset credentials
88
- * Delete a user
89
91
  * Impersonate a user
90
92
  * Exchange a configurable token
93
+ * Get list of clients
94
+ * Get list of groups, create/save a group
95
+ * Get list of roles, save a role
96
+ * Get list of realms, save/update/delete a realm
97
+ * Get list of client role mappings for a user/group
98
+ * Save client role mappings for a user/group
99
+ * Save realm-level role mappings for a user/group
100
+ * Link/Unlink users to federated identity provider brokers
91
101
 
92
102
  ### Get an access token
93
103
 
@@ -114,6 +124,14 @@ Returns an array of `KeycloakAdmin::UserRepresentation`.
114
124
  KeycloakAdmin.realm("a_realm").users.search("a_username_or_an_email")
115
125
  ```
116
126
 
127
+ ### List all users in a realm
128
+
129
+ Returns an array of `KeycloakAdmin::UserRepresentation`.
130
+
131
+ ```ruby
132
+ KeycloakAdmin.realm("a_realm").users.list
133
+ ```
134
+
117
135
  ### Save a user
118
136
 
119
137
  Returns the provided `user`, which must be of type `KeycloakAdmin::UserRepresentation`.
@@ -188,6 +206,134 @@ token_lifespan_in_seconds = 20
188
206
  KeycloakAdmin.realm("a_realm").configurable_token.exchange_with(user_access_token, token_lifespan_in_seconds)
189
207
  ```
190
208
 
209
+ ### Get list of realms
210
+
211
+ Returns an array of `KeycloakAdmin::RealmRepresentation`.
212
+
213
+ ```ruby
214
+ KeycloakAdmin.realm("master").list
215
+ ```
216
+
217
+ ### Save a realm
218
+
219
+ Takes `realm` of type `KeycloakAdmin::RealmRepresentation`, or an object implementing `to_json`, such as a `Hash`.
220
+
221
+ ```ruby
222
+ KeycloakAdmin.realm(nil).save(realm)
223
+ ```
224
+
225
+ ### Update a realm
226
+
227
+ If you want to update its entire entity. To update some specific attributes, provide an object implementing `to_json`, such as a `Hash`.
228
+
229
+ ```ruby
230
+ KeycloakAdmin.realm("a_realm").update({
231
+ smtpServer: { host: 'test_host' }
232
+ })
233
+ ```
234
+
235
+ ### Delete a realm
236
+
237
+ ```ruby
238
+ KeycloakAdmin.realm("a_realm").delete
239
+ ```
240
+
241
+ ### Get list of clients in a realm
242
+
243
+ Returns an array of `KeycloakAdmin::ClientRepresentation`.
244
+
245
+ ```ruby
246
+ KeycloakAdmin.realm("a_realm").clients.list
247
+ ```
248
+
249
+ ### Get list of groups in a realm
250
+
251
+ Returns an array of `KeycloakAdmin::GroupRepresentation`.
252
+
253
+ ```ruby
254
+ KeycloakAdmin.realm("a_realm").groups.list
255
+ ```
256
+
257
+ ### Save a group
258
+
259
+ Returns the id of saved `group` provided, which must be of type `KeycloakAdmin::GroupRepresentation`.
260
+
261
+ ```ruby
262
+ KeycloakAdmin.realm("a_realm").groups.save(group)
263
+ ```
264
+
265
+ ### Create and save a group with a name and path
266
+
267
+ Returns the id of created group.
268
+
269
+ ```ruby
270
+ group_name = "test"
271
+ group_path = "/top"
272
+ group_id = KeycloakAdmin.realm("a_realm").groups.create!(group_name, group_path)
273
+ ```
274
+
275
+ ### Get list of roles in a realm
276
+
277
+ Returns an array of `KeycloakAdmin::RoleRepresentation`.
278
+
279
+ ```ruby
280
+ KeycloakAdmin.realm("a_realm").roles.list
281
+ ```
282
+
283
+ ### Save a role
284
+
285
+ Takes `role`, which must be of type `KeycloakAdmin::RoleRepresentation`.
286
+
287
+ ```ruby
288
+ KeycloakAdmin.realm("a_realm").roles.save(role)
289
+ ```
290
+
291
+ ### Get list of client role mappings for a user/group
292
+
293
+ Returns an array of `KeycloakAdmin::RoleRepresentation`.
294
+
295
+ ```ruby
296
+ user_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
297
+ client_id = "1869e876-71b4-4de2-849e-66540db3a098"
298
+ KeycloakAdmin.realm("a_realm").user(user_id).client_role_mappings(client_id).list_available
299
+ ```
300
+ or
301
+ ```ruby
302
+ group_id = "3a63b5c0-ef8a-47fd-86ed-b5fead18d9b8"
303
+ client_id = "1869e876-71b4-4de2-849e-66540db3a098"
304
+ KeycloakAdmin.realm("a_realm").group(group_id).client_role_mappings(client_id).list_available
305
+ ```
306
+
307
+ ### Save list of client role mappings for a user/group
308
+
309
+ Takes `role_list`, which must be an array of type `KeycloakAdmin::RoleRepresentation`.
310
+
311
+ ```ruby
312
+ user_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
313
+ client_id = "1869e876-71b4-4de2-849e-66540db3a098"
314
+ KeycloakAdmin.realm("a_realm").user(user_id).client_role_mappings(client_id).save(role_list)
315
+ ```
316
+ or
317
+ ```ruby
318
+ group_id = "3a63b5c0-ef8a-47fd-86ed-b5fead18d9b8"
319
+ client_id = "1869e876-71b4-4de2-849e-66540db3a098"
320
+ KeycloakAdmin.realm("a_realm").group(group_id).client_role_mappings(client_id).save(role_list)
321
+ ```
322
+
323
+ ### Save list of realm-level role mappings for a user/group
324
+
325
+ Takes `role_list`, which must be an array of type `KeycloakAdmin::RoleRepresentation`.
326
+
327
+ ```ruby
328
+ user_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
329
+ KeycloakAdmin.realm("a_realm").user(user_id).role_mapper.save_realm_level(role_list)
330
+ ```
331
+ or
332
+ ```ruby
333
+ group_id = "3a63b5c0-ef8a-47fd-86ed-b5fead18d9b8"
334
+ KeycloakAdmin.realm("a_realm").group(group_id).role_mapper.save_realm_level(role_list)
335
+ ```
336
+
191
337
  ## How to execute library tests
192
338
 
193
339
  From the `keycloak-admin-api` directory:
@@ -2,17 +2,30 @@ require "logger"
2
2
 
3
3
  require_relative "keycloak-admin/configuration"
4
4
  require_relative "keycloak-admin/client/client"
5
+ require_relative "keycloak-admin/client/client_client"
6
+ require_relative "keycloak-admin/client/client_role_mappings_client"
7
+ require_relative "keycloak-admin/client/group_client"
5
8
  require_relative "keycloak-admin/client/realm_client"
9
+ require_relative "keycloak-admin/client/role_client"
10
+ require_relative "keycloak-admin/client/role_mapper_client"
6
11
  require_relative "keycloak-admin/client/token_client"
7
12
  require_relative "keycloak-admin/client/user_client"
8
13
  require_relative "keycloak-admin/client/configurable_token_client"
9
14
  require_relative "keycloak-admin/representation/camel_json"
10
15
  require_relative "keycloak-admin/representation/representation"
16
+ require_relative "keycloak-admin/representation/client_representation"
17
+ require_relative "keycloak-admin/representation/group_representation"
11
18
  require_relative "keycloak-admin/representation/token_representation"
12
19
  require_relative "keycloak-admin/representation/impersonation_redirection_representation"
13
20
  require_relative "keycloak-admin/representation/impersonation_representation"
14
21
  require_relative "keycloak-admin/representation/credential_representation"
22
+ require_relative "keycloak-admin/representation/realm_representation"
23
+ require_relative "keycloak-admin/representation/role_representation"
24
+ require_relative "keycloak-admin/representation/federated_identity_representation"
15
25
  require_relative "keycloak-admin/representation/user_representation"
26
+ require_relative "keycloak-admin/resource/base_role_containing_resource"
27
+ require_relative "keycloak-admin/resource/group_resource"
28
+ require_relative "keycloak-admin/resource/user_resource"
16
29
 
17
30
  module KeycloakAdmin
18
31
 
@@ -9,13 +9,13 @@ module KeycloakAdmin
9
9
  @configuration.server_url
10
10
  end
11
11
 
12
- def token
13
- @token ||= KeycloakAdmin.realm(@configuration.client_realm_name).token.get
12
+ def current_token
13
+ @current_token ||= KeycloakAdmin.realm(@configuration.client_realm_name).token.get
14
14
  end
15
15
 
16
16
  def headers
17
17
  {
18
- Authorization: "Bearer #{token.access_token}",
18
+ Authorization: "Bearer #{current_token.access_token}",
19
19
  content_type: :json,
20
20
  accept: :json
21
21
  }
@@ -29,6 +29,14 @@ module KeycloakAdmin
29
29
  http_error(e.response)
30
30
  end
31
31
 
32
+ def created_id(response)
33
+ unless response.net_http_res.is_a? Net::HTTPCreated
34
+ raise "Create method returned status #{response.net_http_res.message} (Code: #{response.net_http_res.code}); expected status: Created (201)"
35
+ end
36
+ (_head, _separator, id) = response.headers[:location].rpartition('/')
37
+ id
38
+ end
39
+
32
40
  private
33
41
 
34
42
  def http_error(response)
@@ -0,0 +1,24 @@
1
+ module KeycloakAdmin
2
+ class ClientClient < Client
3
+ def initialize(configuration, realm_client)
4
+ super(configuration)
5
+ raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
6
+ @realm_client = realm_client
7
+ end
8
+
9
+ def list
10
+ response = execute_http do
11
+ RestClient::Resource.new(clients_url, @configuration.rest_client_options).get(headers)
12
+ end
13
+ JSON.parse(response).map { |client_as_hash| ClientRepresentation.from_hash(client_as_hash) }
14
+ end
15
+
16
+ def clients_url(id=nil)
17
+ if id
18
+ "#{@realm_client.realm_admin_url}/clients/#{id}"
19
+ else
20
+ "#{@realm_client.realm_admin_url}/clients"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ module KeycloakAdmin
2
+ class ClientRoleMappingsClient < Client
3
+ def initialize(configuration, user_resource, client_id)
4
+ super(configuration)
5
+ @user_resource = user_resource
6
+ @client_id = client_id
7
+ end
8
+
9
+ def list_available
10
+ response = execute_http do
11
+ RestClient::Resource.new(list_available_url, @configuration.rest_client_options).get(headers)
12
+ end
13
+ JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
14
+ end
15
+
16
+ def save(role_representation_list)
17
+ execute_http do
18
+ RestClient::Resource.new(base_url, @configuration.rest_client_options).post(
19
+ role_representation_list.to_json, headers
20
+ )
21
+ end
22
+ end
23
+
24
+ def list_available_url
25
+ "#{@user_resource.resource_url}/role-mappings/clients/#{@client_id}/available"
26
+ end
27
+
28
+ def base_url
29
+ "#{@user_resource.resource_url}/role-mappings/clients/#{@client_id}"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ module KeycloakAdmin
2
+ class GroupClient < Client
3
+ def initialize(configuration, realm_client)
4
+ super(configuration)
5
+ raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
6
+ @realm_client = realm_client
7
+ end
8
+
9
+ def list
10
+ response = execute_http do
11
+ RestClient::Resource.new(groups_url, @configuration.rest_client_options).get(headers)
12
+ end
13
+ JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
14
+ end
15
+
16
+ def create!(name, path = nil)
17
+ response = save(build(name, path))
18
+ created_id(response)
19
+ end
20
+
21
+ def save(group_representation)
22
+ execute_http do
23
+ RestClient::Resource.new(groups_url, @configuration.rest_client_options).post(
24
+ group_representation.to_json, headers
25
+ )
26
+ end
27
+ end
28
+
29
+ def groups_url(id=nil)
30
+ if id
31
+ "#{@realm_client.realm_admin_url}/groups/#{id}"
32
+ else
33
+ "#{@realm_client.realm_admin_url}/groups"
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def build(name, path)
40
+ group = GroupRepresentation.new
41
+ group.name = name
42
+ group.path = path
43
+ group
44
+ end
45
+ end
46
+ end