keycloak-admin 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/CHANGELOG.md +48 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +109 -2
  6. data/lib/keycloak-admin.rb +9 -0
  7. data/lib/keycloak-admin/client/client.rb +11 -3
  8. data/lib/keycloak-admin/client/client_client.rb +24 -0
  9. data/lib/keycloak-admin/client/client_role_mappings_client.rb +20 -0
  10. data/lib/keycloak-admin/client/group_client.rb +46 -0
  11. data/lib/keycloak-admin/client/realm_client.rb +50 -0
  12. data/lib/keycloak-admin/client/role_client.rb +32 -0
  13. data/lib/keycloak-admin/client/user_client.rb +9 -2
  14. data/lib/keycloak-admin/representation/camel_json.rb +1 -1
  15. data/lib/keycloak-admin/representation/client_representation.rb +16 -0
  16. data/lib/keycloak-admin/representation/group_representation.rb +15 -0
  17. data/lib/keycloak-admin/representation/realm_representation.rb +14 -0
  18. data/lib/keycloak-admin/representation/representation.rb +4 -0
  19. data/lib/keycloak-admin/representation/role_representation.rb +17 -0
  20. data/lib/keycloak-admin/representation/user_representation.rb +13 -13
  21. data/lib/keycloak-admin/resource/user_resource.rb +18 -0
  22. data/lib/keycloak-admin/version.rb +1 -1
  23. data/spec/client/client_client_spec.rb +53 -0
  24. data/spec/client/client_role_mappings_client_spec.rb +48 -0
  25. data/spec/client/group_client_spec.rb +125 -0
  26. data/spec/client/realm_client_spec.rb +108 -0
  27. data/spec/client/role_client_spec.rb +83 -0
  28. data/spec/client/user_client_spec.rb +105 -14
  29. data/spec/representation/user_representation_spec.rb +15 -0
  30. data/spec/resource/user_resource_spec.rb +14 -0
  31. data/spec/spec_helper.rb +7 -0
  32. metadata +18 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 970da832a135573c026406dd6e24bebad0faadde
4
- data.tar.gz: 0116019f2c32443bbd8c9567b8243cb84e75da7a
3
+ metadata.gz: 39322dd0c0035b30a67644fa401bbe55c835dbf1
4
+ data.tar.gz: 268b16b6d6e20aac7a44ba993a76114f8d86e1b8
5
5
  SHA512:
6
- metadata.gz: 65fab8de261bdb5bbef86e05961e4c49858143d6b0eb3e1282ed13b67aa3d4b2e2a7bf6a54788cf02ebaee78eea9c2658644d54e70b45777e6348ac4867c199e
7
- data.tar.gz: b8ab004c2772051d424f277572b42c57ef523d04fc1f43274c97964bd4dc1291b54d24bb70b8785b0fe404c30a23f2bba2636efb244ac6a4eb663bc40e58708f
6
+ metadata.gz: 8f57fe6316a4378bfc8e3c90e6732b5977fc2f14a7871f0b69fb2790bcf60ec047a4a3841e8075eabfdd83d3961d3f1c4bdc38ac2a13a630e8a236e098d877bd
7
+ data.tar.gz: bb2384c3c9c16d498baade9a2ef02766396ca370724114da3b0136976bca61f6a9a6912d106ef5420f3bbdba65f06c88daa476088e77f28c2eaa73546857e1f6
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,48 @@
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.1] - Next version
9
+
10
+ Thanks to @vlad-ro:
11
+
12
+ * List users
13
+ * List clients
14
+ * List groups, create/save a group
15
+ * List roles, save a role
16
+ * List realms, save/update/delete a realm
17
+ * Get list of client role mappings for a user
18
+ * Support passing rest client options for user save and search
19
+ * Support using gem without ActiveSupport
20
+
21
+ ## [0.7.0] - 2019-06-11
22
+
23
+ Thanks to @vlad-ro:
24
+
25
+ * Support passing rest client options
26
+ * More documentation
27
+ * More tests
28
+ * Better handling of timeouts
29
+
30
+ ## [0.6.5] - 2019-05-14
31
+
32
+ * Get user
33
+
34
+ ## [0.6.2] - 2019-05-14
35
+
36
+ * Update users
37
+
38
+ ## [0.6] - 2019-03-06
39
+
40
+ * Save a locale when creating a new user
41
+
42
+ ## [0.5] - 2018-01-26
43
+
44
+ * Client to access Custom REST API configurable-token
45
+
46
+ ## [0.3] - 2018-01-19
47
+
48
+ * Support of impersonation
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keycloak-admin (0.7.0)
4
+ keycloak-admin (0.7.1)
5
5
  http-cookie (~> 1.0, >= 1.0.3)
6
6
  rest-client (~> 2.0)
7
7
 
data/README.md CHANGED
@@ -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,16 @@ 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
91
98
 
92
99
  ### Get an access token
93
100
 
@@ -114,6 +121,14 @@ Returns an array of `KeycloakAdmin::UserRepresentation`.
114
121
  KeycloakAdmin.realm("a_realm").users.search("a_username_or_an_email")
115
122
  ```
116
123
 
124
+ ### List all users in a realm
125
+
126
+ Returns an array of `KeycloakAdmin::UserRepresentation`.
127
+
128
+ ```ruby
129
+ KeycloakAdmin.realm("a_realm").users.list
130
+ ```
131
+
117
132
  ### Save a user
118
133
 
119
134
  Returns the provided `user`, which must be of type `KeycloakAdmin::UserRepresentation`.
@@ -188,6 +203,98 @@ token_lifespan_in_seconds = 20
188
203
  KeycloakAdmin.realm("a_realm").configurable_token.exchange_with(user_access_token, token_lifespan_in_seconds)
189
204
  ```
190
205
 
206
+ ### Get list of realms
207
+
208
+ Returns an array of `KeycloakAdmin::RealmRepresentation`.
209
+
210
+ ```ruby
211
+ KeycloakAdmin.realm("master").list
212
+ ```
213
+
214
+ ### Save a realm
215
+
216
+ Takes `realm` of type `KeycloakAdmin::RealmRepresentation`, or an object implementing `to_json`, such as a `Hash`.
217
+
218
+ ```ruby
219
+ KeycloakAdmin.realm(nil).save(realm)
220
+ ```
221
+
222
+ ### Update a realm
223
+
224
+ If you want to update its entire entity. To update some specific attributes, provide an object implementing `to_json`, such as a `Hash`.
225
+
226
+ ```ruby
227
+ KeycloakAdmin.realm("a_realm").update({
228
+ smtpServer: { host: 'test_host' }
229
+ })
230
+ ```
231
+
232
+ ### Delete a realm
233
+
234
+ ```ruby
235
+ KeycloakAdmin.realm("a_realm").delete
236
+ ```
237
+
238
+ ### Get list of clients in a realm
239
+
240
+ Returns an array of `KeycloakAdmin::ClientRepresentation`.
241
+
242
+ ```ruby
243
+ KeycloakAdmin.realm("a_realm").clients.list
244
+ ```
245
+
246
+ ### Get list of groups in a realm
247
+
248
+ Returns an array of `KeycloakAdmin::GroupRepresentation`.
249
+
250
+ ```ruby
251
+ KeycloakAdmin.realm("a_realm").groups.list
252
+ ```
253
+
254
+ ### Save a group
255
+
256
+ Returns the id of saved `group` provided, which must be of type `KeycloakAdmin::GroupRepresentation`.
257
+
258
+ ```ruby
259
+ KeycloakAdmin.realm("a_realm").groups.save(group)
260
+ ```
261
+
262
+ ### Create and save a group with a name and path
263
+
264
+ Returns the id of created group.
265
+
266
+ ```ruby
267
+ group_name = "test"
268
+ group_path = "/top"
269
+ group_id = KeycloakAdmin.realm("a_realm").groups.create!(group_name, group_path)
270
+ ```
271
+
272
+ ### Get list of roles in a realm
273
+
274
+ Returns an array of `KeycloakAdmin::RoleRepresentation`.
275
+
276
+ ```ruby
277
+ KeycloakAdmin.realm("a_realm").roles.list
278
+ ```
279
+
280
+ ### Save a role
281
+
282
+ Takes `role`, which must be of type `KeycloakAdmin::RoleRepresentation`.
283
+
284
+ ```ruby
285
+ KeycloakAdmin.realm("a_realm").roles.save(role)
286
+ ```
287
+
288
+ ### Get list of client role mappings for a user
289
+
290
+ Returns an array of `KeycloakAdmin::RoleRepresentation`.
291
+
292
+ ```ruby
293
+ user_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
294
+ client_id = "1869e876-71b4-4de2-849e-66540db3a098"
295
+ KeycloakAdmin.realm("a_realm").user(user_id).client_role_mappings(client_id).list_available
296
+ ```
297
+
191
298
  ## How to execute library tests
192
299
 
193
300
  From the `keycloak-admin-api` directory:
@@ -2,17 +2,26 @@ 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"
6
10
  require_relative "keycloak-admin/client/token_client"
7
11
  require_relative "keycloak-admin/client/user_client"
8
12
  require_relative "keycloak-admin/client/configurable_token_client"
9
13
  require_relative "keycloak-admin/representation/camel_json"
10
14
  require_relative "keycloak-admin/representation/representation"
15
+ require_relative "keycloak-admin/representation/client_representation"
16
+ require_relative "keycloak-admin/representation/group_representation"
11
17
  require_relative "keycloak-admin/representation/token_representation"
12
18
  require_relative "keycloak-admin/representation/impersonation_redirection_representation"
13
19
  require_relative "keycloak-admin/representation/impersonation_representation"
14
20
  require_relative "keycloak-admin/representation/credential_representation"
21
+ require_relative "keycloak-admin/representation/realm_representation"
22
+ require_relative "keycloak-admin/representation/role_representation"
15
23
  require_relative "keycloak-admin/representation/user_representation"
24
+ require_relative "keycloak-admin/resource/user_resource"
16
25
 
17
26
  module KeycloakAdmin
18
27
 
@@ -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,20 @@
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 list_available_url
17
+ "#{@user_resource.resource_url}/role-mappings/clients/#{@client_id}/available"
18
+ end
19
+ end
20
+ 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
@@ -5,6 +5,36 @@ module KeycloakAdmin
5
5
  @realm_name = realm_name
6
6
  end
7
7
 
8
+ def list
9
+ response = execute_http do
10
+ RestClient::Resource.new(realm_list_url, @configuration.rest_client_options).get(headers)
11
+ end
12
+ JSON.parse(response).map { |realm_as_hash| RealmRepresentation.from_hash(realm_as_hash) }
13
+ end
14
+
15
+ def delete
16
+ execute_http do
17
+ RestClient::Resource.new(realm_admin_url, @configuration.rest_client_options).delete(headers)
18
+ end
19
+ true
20
+ end
21
+
22
+ def save(realm_representation)
23
+ execute_http do
24
+ RestClient::Resource.new(realm_list_url, @configuration.rest_client_options).post(
25
+ realm_representation.to_json, headers
26
+ )
27
+ end
28
+ end
29
+
30
+ def update(realm_representation_body)
31
+ execute_http do
32
+ RestClient::Resource.new(realm_admin_url, @configuration.rest_client_options).put(
33
+ realm_representation_body.to_json, headers
34
+ )
35
+ end
36
+ end
37
+
8
38
  def realm_url
9
39
  if @realm_name
10
40
  "#{server_url}/realms/#{@realm_name}"
@@ -21,6 +51,10 @@ module KeycloakAdmin
21
51
  end
22
52
  end
23
53
 
54
+ def realm_list_url
55
+ "#{server_url}/admin/realms"
56
+ end
57
+
24
58
  def token
25
59
  TokenClient.new(@configuration, self)
26
60
  end
@@ -29,10 +63,26 @@ module KeycloakAdmin
29
63
  ConfigurableTokenClient.new(@configuration, self)
30
64
  end
31
65
 
66
+ def clients
67
+ ClientClient.new(@configuration, self)
68
+ end
69
+
70
+ def groups
71
+ GroupClient.new(@configuration, self)
72
+ end
73
+
74
+ def roles
75
+ RoleClient.new(@configuration, self)
76
+ end
77
+
32
78
  def users
33
79
  UserClient.new(@configuration, self)
34
80
  end
35
81
 
82
+ def user(user_id)
83
+ UserResource.new(@configuration, self, user_id)
84
+ end
85
+
36
86
  def name_defined?
37
87
  !@realm_name.nil?
38
88
  end