keycloak-admin 1.0.24 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|