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 +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +5 -5
- data/README.md +42 -11
- data/bin/console +9 -0
- data/lib/keycloak-admin/client/client_client.rb +21 -0
- data/lib/keycloak-admin/client/group_client.rb +13 -1
- data/lib/keycloak-admin/version.rb +1 -1
- data/spec/client/client_client_spec.rb +62 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 140b6a3bb680503f4bac8336250974b930535ef270f66b56b488ad78e182cd6b
|
4
|
+
data.tar.gz: 45c9d2ed7af63d6eaf027eebb4a6a9309d115d1fea88df9d8b4bb5c0d14ec7cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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.
|
18
|
+
mime-types (3.5.1)
|
19
19
|
mime-types-data (~> 3.2015)
|
20
|
-
mime-types-data (3.2023.
|
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.
|
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.
|
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
|
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
@@ -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(
|
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
|
@@ -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
|
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-
|
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
|