keycloak-admin 1.0.24 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6bc5dc390440dedbf6a682c53717e334e4f174b31e43b7990dae87bed2e0bbf
4
- data.tar.gz: b37367aaa9726b48a266b55baf7fcebf772f302c18f215ad0e694d6e60421608
3
+ metadata.gz: 140b6a3bb680503f4bac8336250974b930535ef270f66b56b488ad78e182cd6b
4
+ data.tar.gz: 45c9d2ed7af63d6eaf027eebb4a6a9309d115d1fea88df9d8b4bb5c0d14ec7cc
5
5
  SHA512:
6
- metadata.gz: 869a2108bbffcdc242a438a20b6d8c06fa907354e2d0648cac91920d08ba070b2bf9cc35d710f1a1fcb40691176b84b515372af540bdc7383d4835217e9ce989
7
- data.tar.gz: ed68ed6de159d1d0bf87c298b49fab7fc94cc1d5f563ef25a2b56e922807ddb98b9fba69f189bc7ccc843380595afce42cc202e14b53f07fcc97f7c141145e43
6
+ metadata.gz: da197ebef3a7bc8fc82a95258eb684d007fbb69d48b62f6f19afd510fbbce11b30ac0c98f3b0731ebc56556311330707360a72f0505c9021d7f34d799a045534
7
+ data.tar.gz: e77461558697bbe7c372973673a3e58d9530720da6db2eaa76b79b1616e06ad05148cf843bd3c23a9d15a6fa55e27ac8988fccea776d593fd96e5f1130b6c0e1
data/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0] - 2023-10-03
9
+
10
+ * Search for groups with parameters (thanks to @@tlloydthwaites)
11
+ * Get client by ID, Find client by Client ID, Update Client (thanks to @gee-forr)
12
+
8
13
  ## [1.0.24] - 2023-06-07
9
14
 
10
15
  * Revert the modifications on the feature 'Update a User' introduced in `1.0.22`. This implementation had breaking changes such as not being able to update several attributes (`first_name`, `email`, etc).
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keycloak-admin (1.0.24)
4
+ keycloak-admin (1.1.0)
5
5
  http-cookie (~> 1.0, >= 1.0.3)
6
6
  rest-client (~> 2.0)
7
7
 
@@ -15,9 +15,9 @@ GEM
15
15
  http-accept (1.7.0)
16
16
  http-cookie (1.0.5)
17
17
  domain_name (~> 0.5)
18
- mime-types (3.4.1)
18
+ mime-types (3.5.1)
19
19
  mime-types-data (~> 3.2015)
20
- mime-types-data (3.2023.0218.1)
20
+ mime-types-data (3.2023.0808)
21
21
  netrc (0.11.0)
22
22
  rest-client (2.1.0)
23
23
  http-accept (>= 1.7.0, < 2.0)
@@ -33,10 +33,10 @@ GEM
33
33
  rspec-expectations (3.12.3)
34
34
  diff-lcs (>= 1.2.0, < 2.0)
35
35
  rspec-support (~> 3.12.0)
36
- rspec-mocks (3.12.5)
36
+ rspec-mocks (3.12.6)
37
37
  diff-lcs (>= 1.2.0, < 2.0)
38
38
  rspec-support (~> 3.12.0)
39
- rspec-support (3.12.0)
39
+ rspec-support (3.12.1)
40
40
  unf (0.1.4)
41
41
  unf_ext
42
42
  unf_ext (0.0.8.2)
data/README.md CHANGED
@@ -21,7 +21,7 @@ To login on Keycloak's Admin API, you first need to setup a client.
21
21
 
22
22
  Go to your realm administration page and open `Clients`. Then, click on the `Create` button.
23
23
  On the first screen, enter:
24
- * `Client ID`: _e.g. my-app-admin-client_
24
+ * `Client ID`: _e.g. my-app-admin-client_
25
25
  * `Client Protocol`: select `openid-connect`
26
26
  * `Root URL`: let it blank
27
27
 
@@ -45,7 +45,7 @@ The next screen must be configured depending on how you want to authenticate:
45
45
  * In this gem's configuration (see Section `Configuration`):
46
46
  * Setup `username` and `password` according to your user's configuration
47
47
  * Setup `client_id` with your `Client ID` (_e.g. my-app-admin-client_)
48
- * If your client is `confidential`, copy its Client Secret to `client_secret`
48
+ * If your client is `confidential`, copy its Client Secret to `client_secret`
49
49
 
50
50
  ### Login with `Direct Access Grants` (Service account)
51
51
 
@@ -60,15 +60,15 @@ Using a service account to use the REST Admin API does not require to create a d
60
60
  * After saving this client
61
61
  * open the `Service Account Roles` and add relevant `realm-management.` client's roles. For instance: `view-users` if you want to search for users using this gem.
62
62
  * open the `Credentials` tab and copy the `Client Secret`
63
-
63
+
64
64
  * In this gem's configuration (see Section `Configuration`):
65
65
  * Set `use_service_account` to `true`
66
66
  * Setup `client_id` with your `Client ID` (_e.g. my-app-admin-client_)
67
- * Copy its Client Secret to `client_secret`
67
+ * Copy its Client Secret to `client_secret`
68
68
 
69
69
  ## Configuration
70
70
 
71
- To configure this gem, call `KeycloakAdmin.configure`.
71
+ To configure this gem, call `KeycloakAdmin.configure`.
72
72
  For instance, to configure this gem based on environment variables, write (and load if required) a `keycloak_admin.rb`:
73
73
  ```ruby
74
74
  KeycloakAdmin.configure do |config|
@@ -96,14 +96,14 @@ All options have a default value. However, all of them can be changed in your in
96
96
  | `client_realm_name` | `""`| String | Required | Name of the realm that contains the admin client. | `master` |
97
97
  | `client_id` | `admin-cli`| String | Required | Client that should be used to access admin capabilities. | `api-cli` |
98
98
  | `client_secret` | `nil`| String | Optional | If your client is `confidential`, this parameter must be specified. | `4e3c481c-f823-4a6a-b8a7-bf8c86e3eac3` |
99
- | `use_service_account` | `true` | Boolean | Required | `true` if the connection to the client uses a Service Account. `false` if the connection to the client uses a username/password credential. | `false` |
99
+ | `use_service_account` | `true` | Boolean | Required | `true` if the connection to the client uses a Service Account. `false` if the connection to the client uses a username/password credential. | `false` |
100
100
  | `username` | `nil`| String | Optional | Username to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `mummy` |
101
101
  | `password` | `nil`| String | Optional | Clear password to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `bobby` |
102
102
  | `logger` | `Logger.new(STDOUT)`| Logger | Optional | The logger used by `keycloak-admin` | `Rails.logger` | 
103
103
  | `rest_client_options` | `{}`| Hash | Optional | Options to pass to `RestClient` | `{ verify_ssl: OpenSSL::SSL::VERIFY_NONE }` | 
104
104
 
105
105
 
106
- ## Use Case
106
+ ## Use Cases
107
107
 
108
108
  ### Supported features
109
109
 
@@ -113,8 +113,8 @@ All options have a default value. However, all of them can be changed in your in
113
113
  * Reset credentials
114
114
  * Impersonate a user
115
115
  * Exchange a configurable token
116
- * Get list of clients
117
- * Create clients
116
+ * Get list of clients, or find a client by its id or client_id
117
+ * Create, update, and delete clients
118
118
  * Get list of groups, create/save a group
119
119
  * Get list of roles, save a role
120
120
  * Get list of realms, save/update/delete a realm
@@ -279,10 +279,26 @@ KeycloakAdmin.realm("a_realm").delete
279
279
 
280
280
  ### Get list of clients in a realm
281
281
 
282
- Returns an array of `KeycloakAdmin::ClientRepresentation`.
282
+ Returns an array of `KeycloakAdmin::ClientRepresentation` or a single `KeycloakAdmin::ClientRepresentation`
283
+
284
+ Finding a client by its `client_id` is a somewhat slow operation, as it requires fetching all clients and then filtering. Keycloak's API does not support fetching a client by its `client_id` directly.
283
285
 
284
286
  ```ruby
285
287
  KeycloakAdmin.realm("a_realm").clients.list
288
+ KeycloakAdmin.realm("a_realm").clients.get(id) # id is Keycloak's database id, not the client_id
289
+ KeycloakAdmin.realm("a_realm").clients.find_by_client_id(client_id)
290
+ ```
291
+
292
+ ### Updating a client
293
+
294
+ ```ruby
295
+ my_client = KeycloakAdmin.realm("a_realm").clients.get(id)
296
+
297
+ my_client.name = "My new client name"
298
+ my_client.description = "This is a new description"
299
+ my_client.redirect_uris << "https://www.example.com/auth/callback"
300
+
301
+ KeycloakAdmin.realm("a_realm").clients.update(client) # Returns the updated client
286
302
  ```
287
303
 
288
304
  ### Get list of groups in a realm
@@ -293,6 +309,22 @@ Returns an array of `KeycloakAdmin::GroupRepresentation`.
293
309
  KeycloakAdmin.realm("a_realm").groups.list
294
310
  ```
295
311
 
312
+ ### Search for a group
313
+
314
+ Returns an array of `KeycloakAdmin::GroupRepresentation`.
315
+
316
+ According to [the documentation](https://www.keycloak.org/docs-api/22.0.1/rest-api/index.html#_groups):
317
+ * When providing a `String` parameter, this produces an arbitrary search string
318
+ * When providing a `Hash`, you can specify other fields (_e.g_ q, max, first)
319
+
320
+ ```ruby
321
+ KeycloakAdmin.realm("a_realm").groups.search("MyGroup")
322
+ ```
323
+
324
+ ```ruby
325
+ KeycloakAdmin.realm("a_realm").groups.search({query: "MyGroup", exact: true, max: 1})
326
+ ```
327
+
296
328
  ### Save a group
297
329
 
298
330
  Returns the id of saved `group` provided, which must be of type `KeycloakAdmin::GroupRepresentation`.
@@ -415,4 +447,3 @@ From the `keycloak-admin-api` directory:
415
447
  $ docker build . -t keycloak-admin:test
416
448
  $ docker run -v `pwd`:/usr/src/app/ keycloak-admin:test rspec spec
417
449
  ```
418
-
data/bin/console ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "keycloak-admin"
6
+ require "byebug"
7
+
8
+ require "irb"
9
+ IRB.start
@@ -6,6 +6,13 @@ module KeycloakAdmin
6
6
  @realm_client = realm_client
7
7
  end
8
8
 
9
+ def get(id)
10
+ response = execute_http do
11
+ RestClient::Resource.new(clients_url(id), @configuration.rest_client_options).get(headers)
12
+ end
13
+ ClientRepresentation.from_hash(JSON.parse(response))
14
+ end
15
+
9
16
  def save(client_representation)
10
17
  execute_http do
11
18
  RestClient::Resource.new(clients_url, @configuration.rest_client_options).post(
@@ -21,6 +28,10 @@ module KeycloakAdmin
21
28
  JSON.parse(response).map { |client_as_hash| ClientRepresentation.from_hash(client_as_hash) }
22
29
  end
23
30
 
31
+ def find_by_client_id(client_id)
32
+ list.find { |client| client.client_id == client_id }
33
+ end
34
+
24
35
  def delete(id)
25
36
  execute_http do
26
37
  RestClient::Resource.new(clients_url(id), @configuration.rest_client_options).delete(headers)
@@ -28,6 +39,16 @@ module KeycloakAdmin
28
39
  true
29
40
  end
30
41
 
42
+ def update(client_representation)
43
+ execute_http do
44
+ RestClient::Resource.new(clients_url(client_representation.id), @configuration.rest_client_options).put(
45
+ create_payload(client_representation), headers
46
+ )
47
+ end
48
+
49
+ get(client_representation.id)
50
+ end
51
+
31
52
  def get_service_account_user(client_id)
32
53
  response = execute_http do
33
54
  RestClient::Resource.new(service_account_user_url(client_id), @configuration.rest_client_options).get(headers)
@@ -7,8 +7,20 @@ module KeycloakAdmin
7
7
  end
8
8
 
9
9
  def list
10
+ search(nil)
11
+ end
12
+
13
+ def search(query)
14
+ derived_headers = case query
15
+ when String
16
+ headers.merge({params: { search: query }})
17
+ when Hash
18
+ headers.merge({params: query })
19
+ else
20
+ headers
21
+ end
10
22
  response = execute_http do
11
- RestClient::Resource.new(groups_url, @configuration.rest_client_options).get(headers)
23
+ RestClient::Resource.new(groups_url, @configuration.rest_client_options).get(derived_headers)
12
24
  end
13
25
  JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
14
26
  end
@@ -1,3 +1,3 @@
1
1
  module KeycloakAdmin
2
- VERSION = "1.0.24"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -22,6 +22,49 @@ RSpec.describe KeycloakAdmin::ClientClient do
22
22
  end
23
23
  end
24
24
 
25
+ describe "#get" do
26
+ let(:realm_name) { "valid-realm" }
27
+ let(:id) { "test_client_id" }
28
+ let(:client_name) { "test_client_name" }
29
+
30
+ before(:each) do
31
+ @client_client = KeycloakAdmin.realm(realm_name).clients
32
+
33
+ stub_token_client
34
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '{"id":"test_client_id","name":"test_client_name"}'
35
+ end
36
+
37
+ it "finds a client" do
38
+ client = @client_client.get(id)
39
+ expect(client.name).to eq client_name
40
+ expect(client.id).to eq id
41
+ end
42
+ end
43
+
44
+ describe "#find_by_client_id" do
45
+ let(:realm_name) { "valid-realm" }
46
+ let(:client_id) { "my_client_id" }
47
+ let(:client_name) { "test_client_name" }
48
+
49
+ before(:each) do
50
+ @client_client = KeycloakAdmin.realm(realm_name).clients
51
+
52
+ stub_token_client
53
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '[{"id":"test_client_id","clientId": "my_client_id","name":"test_client_name"},{"id":"test_client_id_2","clientId":"client_id_2","name":"test_client_name_2"}]'
54
+ end
55
+
56
+ it "finds a client it has" do
57
+ client = @client_client.find_by_client_id(client_id)
58
+ expect(client.name).to eq client_name
59
+ expect(client.client_id).to eq client_id
60
+ end
61
+
62
+ it "returns nil if it doesn't have the client" do
63
+ client = @client_client.find_by_client_id("client_id_3")
64
+ expect(client).to be_nil
65
+ end
66
+ end
67
+
25
68
  describe "#list" do
26
69
  let(:realm_name) { "valid-realm" }
27
70
 
@@ -51,6 +94,25 @@ RSpec.describe KeycloakAdmin::ClientClient do
51
94
  end
52
95
  end
53
96
 
97
+ describe "#update" do
98
+ let(:realm_name) { "valid-realm" }
99
+ let(:client) { KeycloakAdmin::ClientRepresentation.from_hash({ "id" => "test_client_id", "clientId" => "my-client", "name" => "old_name" }) }
100
+
101
+ before(:each) do
102
+ @client_client = KeycloakAdmin.realm(realm_name).clients
103
+
104
+ stub_token_client
105
+ allow_any_instance_of(RestClient::Resource).to receive(:put).and_return ''
106
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '{"id":"test_client_id", "clientId": "my-client","name":"new_name"}'
107
+ end
108
+
109
+ it "updates a client" do
110
+ updated_client = @client_client.update(client)
111
+
112
+ expect(updated_client.name).to eq "new_name"
113
+ end
114
+ end
115
+
54
116
  describe "#delete" do
55
117
  let(:realm_name) { "valid-realm" }
56
118
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keycloak-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.24
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorent Lempereur
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-07 00:00:00.000000000 Z
11
+ date: 2023-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-cookie
@@ -87,6 +87,7 @@ files:
87
87
  - Gemfile.lock
88
88
  - MIT-LICENSE
89
89
  - README.md
90
+ - bin/console
90
91
  - keycloak-admin.gemspec
91
92
  - lib/keycloak-admin.rb
92
93
  - lib/keycloak-admin/client/attack_detection_client.rb