keycloak-admin 1.1.1 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/Dockerfile +24 -0
- data/.github/workflows/ci.yml +83 -0
- data/CHANGELOG.md +12 -2
- data/Gemfile.lock +8 -8
- data/README.md +277 -4
- data/lib/keycloak-admin/client/client_authz_permission_client.rb +81 -0
- data/lib/keycloak-admin/client/client_authz_policy_client.rb +76 -0
- data/lib/keycloak-admin/client/client_authz_resource_client.rb +93 -0
- data/lib/keycloak-admin/client/client_authz_scope_client.rb +71 -0
- data/lib/keycloak-admin/client/group_client.rb +41 -13
- data/lib/keycloak-admin/client/realm_client.rb +16 -0
- data/lib/keycloak-admin/client/role_client.rb +12 -10
- data/lib/keycloak-admin/client/user_client.rb +1 -0
- data/lib/keycloak-admin/representation/client_authz_permission_representation.rb +34 -0
- data/lib/keycloak-admin/representation/client_authz_policy_config_representation.rb +15 -0
- data/lib/keycloak-admin/representation/client_authz_policy_representation.rb +27 -0
- data/lib/keycloak-admin/representation/client_authz_resource_representation.rb +26 -0
- data/lib/keycloak-admin/representation/client_authz_scope_representation.rb +17 -0
- data/lib/keycloak-admin/representation/group_representation.rb +9 -5
- data/lib/keycloak-admin/version.rb +1 -1
- data/lib/keycloak-admin.rb +9 -0
- data/spec/client/client_authz_permission_client_spec.rb +170 -0
- data/spec/client/client_authz_policy_client_spec.rb +170 -0
- data/spec/client/client_authz_resource_client_spec.rb +150 -0
- data/spec/client/client_authz_scope_client_spec.rb +134 -0
- data/spec/client/client_client_spec.rb +2 -2
- data/spec/client/client_role_mappings_client_spec.rb +2 -2
- data/spec/client/group_client_spec.rb +137 -15
- data/spec/client/identity_provider_client_spec.rb +1 -1
- data/spec/client/realm_client_spec.rb +4 -4
- data/spec/client/role_client_spec.rb +12 -16
- data/spec/client/role_mapper_client_spec.rb +1 -1
- data/spec/client/token_client_spec.rb +1 -1
- data/spec/client/user_client_spec.rb +5 -5
- data/spec/configuration_spec.rb +1 -1
- data/spec/integration/client_authorization_spec.rb +95 -0
- data/spec/representation/client_authz_permission_representation_spec.rb +52 -0
- data/spec/representation/client_authz_policy_representation_spec.rb +47 -0
- data/spec/representation/client_authz_resource_representation_spec.rb +33 -0
- data/spec/representation/client_authz_scope_representation_spec.rb +19 -0
- data/spec/representation/group_representation_spec.rb +7 -0
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be47369f8365b8b32ff4ca3b09ebad45f5037212eca3022524b10ba17b0f64d6
|
4
|
+
data.tar.gz: 36a340f437ecb97ce5c415e752d5e1159ebbc5911d454f643985de541c6deaf3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bd85e4709a0044168b7c3b3e9e2d89b1fab559070d4a318d65283db2046716439c4d1d0a534ba3cc9b1cbc89771626945877a76723bd03cc5f0e6dbebe6b266
|
7
|
+
data.tar.gz: e4b2286bac6d7e11192a1f3b6a569170849275bd3fa91aa104cdf521139a691396c8681a4152b5611e6a28e6c1457f60329b39ebca44cc4eb328f9ade9f7f605
|
@@ -0,0 +1,24 @@
|
|
1
|
+
### Dockerfile for: tillawy/keycloak-github-actions
|
2
|
+
##
|
3
|
+
## To build & push
|
4
|
+
# docker buildx build . --platform linux/amd64 -t tillawy/keycloak-github-actions:25.0.1
|
5
|
+
# docker push tillawy/keycloak-github-actions:25.0.1
|
6
|
+
#
|
7
|
+
## To Run Locally
|
8
|
+
# docker run \
|
9
|
+
# --rm \
|
10
|
+
# -p 8080:8080 \
|
11
|
+
# -e KEYCLOAK_ADMIN="admin" \
|
12
|
+
# -e KEYCLOAK_ADMIN_PASSWORD="admin" \
|
13
|
+
# -e KC_HOSTNAME="http://localhost:8080" \
|
14
|
+
# -e KC_HOSTNAME_ADMIN="http://localhost:8080" \
|
15
|
+
# -e KC_HTTP_ENABLED="true" \
|
16
|
+
# -e KC_DB="dev-file" \
|
17
|
+
# tillawy/keycloak-github-actions:25.0.1
|
18
|
+
|
19
|
+
FROM quay.io/keycloak/keycloak:25.0.1
|
20
|
+
|
21
|
+
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
|
22
|
+
|
23
|
+
CMD ["start-dev", "--hostname=http://localhost:8080" , "--hostname-admin=http://localhost:8080" , "--http-enabled=true", "--verbose"]
|
24
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: Ruby
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ "main" ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ "main" ]
|
15
|
+
|
16
|
+
permissions:
|
17
|
+
contents: read
|
18
|
+
|
19
|
+
jobs:
|
20
|
+
test:
|
21
|
+
runs-on: ubuntu-latest
|
22
|
+
services:
|
23
|
+
keycloak:
|
24
|
+
image: tillawy/keycloak-github-actions:25.0.1
|
25
|
+
ports:
|
26
|
+
- 8080:8080
|
27
|
+
options: '--health-cmd "exec 3<>/dev/tcp/localhost/8080" --health-interval 5s --health-timeout 5s --health-retries 10 --health-start-period 100s'
|
28
|
+
env:
|
29
|
+
KEYCLOAK_ADMIN: "admin"
|
30
|
+
KEYCLOAK_ADMIN_PASSWORD: "admin"
|
31
|
+
KC_HOSTNAME: "http://localhost:8080"
|
32
|
+
KC_HOSTNAME_ADMIN: "http://localhost:8080"
|
33
|
+
KC_HTTP_ENABLED: "true"
|
34
|
+
KC_DB: "dev-file"
|
35
|
+
|
36
|
+
strategy:
|
37
|
+
matrix:
|
38
|
+
ruby-version: ['3.2']
|
39
|
+
|
40
|
+
steps:
|
41
|
+
- name: create realm
|
42
|
+
run: |
|
43
|
+
TOKEN=$(curl --silent --location --request POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
|
44
|
+
--header 'Content-Type: application/x-www-form-urlencoded' \
|
45
|
+
--data-urlencode 'grant_type=password' \
|
46
|
+
--data-urlencode 'username=admin' \
|
47
|
+
--data-urlencode 'password=admin' \
|
48
|
+
--data-urlencode 'client_id=admin-cli' | jq -r '.access_token')
|
49
|
+
|
50
|
+
curl --silent --show-error -L -X POST "http://localhost:8080/admin/realms" \
|
51
|
+
--header "Content-Type: application/json" \
|
52
|
+
--header "Authorization: Bearer ${TOKEN}" \
|
53
|
+
--data '{"realm":"dummy","enabled":true}'
|
54
|
+
|
55
|
+
curl --silent --show-error --request POST 'http://localhost:8080/admin/realms/dummy/clients' \
|
56
|
+
--header 'Authorization: Bearer '$TOKEN \
|
57
|
+
--header 'Content-Type: application/json' \
|
58
|
+
--data-raw '{
|
59
|
+
"clientId":"dummy-client",
|
60
|
+
"enabled":true,
|
61
|
+
"consentRequired": false,
|
62
|
+
"attributes":{},
|
63
|
+
"serviceAccountsEnabled": true,
|
64
|
+
"protocol":"openid-connect",
|
65
|
+
"publicClient":false,
|
66
|
+
"authorizationServicesEnabled": true,
|
67
|
+
"clientAuthenticatorType":"client-secret",
|
68
|
+
"redirectUris":["http://localhost:8180/demo"]
|
69
|
+
}'
|
70
|
+
|
71
|
+
- uses: actions/checkout@v4
|
72
|
+
- name: Set up Ruby
|
73
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
74
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
75
|
+
# uses: ruby/setup-ruby@v1
|
76
|
+
uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
|
77
|
+
with:
|
78
|
+
ruby-version: ${{ matrix.ruby-version }}
|
79
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
80
|
+
- name: Run tests
|
81
|
+
run: bundle exec rspec
|
82
|
+
env:
|
83
|
+
GITHUB_ACTIONS: true
|
data/CHANGELOG.md
CHANGED
@@ -5,10 +5,20 @@ 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.3] - 2024-07-12
|
9
|
+
|
10
|
+
* Client Authorization management support (thanks to @tillawy)
|
11
|
+
* GitHub-actions setup to execute `rspec` (thanks to @tillawy)
|
12
|
+
|
13
|
+
## [1.1.2] - 2024-05-22
|
14
|
+
|
15
|
+
* Add group endpoints (get, children, delete), support for group attributes (thanks to @mkrawc)
|
16
|
+
* GroupClient#save method now can update an existing group (thanks to @mkrawc)
|
17
|
+
* RoleClient#save method now can update an existing role (thanks to @mkrawc)
|
18
|
+
|
8
19
|
## [1.1.1] - 2024-01-21
|
9
20
|
|
10
|
-
* Add/List realm-role/s to a group, Allow role-names with spaces, List groups assigned to role (thanks to @LiquidMagical
|
11
|
-
)
|
21
|
+
* Add/List realm-role/s to a group, Allow role-names with spaces, List groups assigned to role (thanks to @LiquidMagical)
|
12
22
|
|
13
23
|
## [1.1.0] - 2023-10-03
|
14
24
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
keycloak-admin (1.1.
|
4
|
+
keycloak-admin (1.1.3)
|
5
5
|
http-cookie (~> 1.0, >= 1.0.3)
|
6
6
|
rest-client (~> 2.0)
|
7
7
|
|
@@ -9,14 +9,14 @@ GEM
|
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
11
|
byebug (11.1.3)
|
12
|
-
diff-lcs (1.5.
|
12
|
+
diff-lcs (1.5.1)
|
13
13
|
domain_name (0.6.20240107)
|
14
14
|
http-accept (1.7.0)
|
15
|
-
http-cookie (1.0.
|
15
|
+
http-cookie (1.0.6)
|
16
16
|
domain_name (~> 0.5)
|
17
17
|
mime-types (3.5.2)
|
18
18
|
mime-types-data (~> 3.2015)
|
19
|
-
mime-types-data (3.
|
19
|
+
mime-types-data (3.2024.0702)
|
20
20
|
netrc (0.11.0)
|
21
21
|
rest-client (2.1.0)
|
22
22
|
http-accept (>= 1.7.0, < 2.0)
|
@@ -27,15 +27,15 @@ GEM
|
|
27
27
|
rspec-core (~> 3.12.0)
|
28
28
|
rspec-expectations (~> 3.12.0)
|
29
29
|
rspec-mocks (~> 3.12.0)
|
30
|
-
rspec-core (3.12.
|
30
|
+
rspec-core (3.12.3)
|
31
31
|
rspec-support (~> 3.12.0)
|
32
|
-
rspec-expectations (3.12.
|
32
|
+
rspec-expectations (3.12.4)
|
33
33
|
diff-lcs (>= 1.2.0, < 2.0)
|
34
34
|
rspec-support (~> 3.12.0)
|
35
|
-
rspec-mocks (3.12.
|
35
|
+
rspec-mocks (3.12.7)
|
36
36
|
diff-lcs (>= 1.2.0, < 2.0)
|
37
37
|
rspec-support (~> 3.12.0)
|
38
|
-
rspec-support (3.12.
|
38
|
+
rspec-support (3.12.2)
|
39
39
|
|
40
40
|
PLATFORMS
|
41
41
|
ruby
|
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", "1.
|
15
|
+
gem "keycloak-admin", "1.1.3"
|
16
16
|
```
|
17
17
|
|
18
18
|
## Login
|
@@ -80,7 +80,9 @@ KeycloakAdmin.configure do |config|
|
|
80
80
|
config.username = ENV["KEYCLOAK_ADMIN_USER"]
|
81
81
|
config.password = ENV["KEYCLOAK_ADMIN_PASSWORD"]
|
82
82
|
config.logger = Rails.logger
|
83
|
-
|
83
|
+
|
84
|
+
# You configure RestClient to your liking – see https://github.com/rest-client/rest-client/blob/master/lib/restclient/request.rb for available options.
|
85
|
+
config.rest_client_options = { timeout: 5 }
|
84
86
|
end
|
85
87
|
```
|
86
88
|
This example is autoloaded in a Rails environment.
|
@@ -100,7 +102,7 @@ All options have a default value. However, all of them can be changed in your in
|
|
100
102
|
| `username` | `nil`| String | Optional | Username to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `mummy` |
|
101
103
|
| `password` | `nil`| String | Optional | Clear password to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `bobby` |
|
102
104
|
| `logger` | `Logger.new(STDOUT)`| Logger | Optional | The logger used by `keycloak-admin` | `Rails.logger` |
|
103
|
-
| `rest_client_options` | `{}`| Hash | Optional | Options to pass to `RestClient` | `{
|
105
|
+
| `rest_client_options` | `{}`| Hash | Optional | Options to pass to `RestClient` | `{ timeout: 5 }` |
|
104
106
|
|
105
107
|
|
106
108
|
## Use Cases
|
@@ -131,6 +133,7 @@ All options have a default value. However, all of them can be changed in your in
|
|
131
133
|
* Link/Unlink users to federated identity provider brokers
|
132
134
|
* Execute actions emails
|
133
135
|
* Send forgot passsword mail
|
136
|
+
* Client Authorization, create, update, get, delete Resource, Scope, Policy, Permission, Policy Enforcer
|
134
137
|
|
135
138
|
### Get an access token
|
136
139
|
|
@@ -187,10 +190,15 @@ If you want to update its entire entity. To update some specific attributes, pro
|
|
187
190
|
|
188
191
|
```ruby
|
189
192
|
KeycloakAdmin.realm("a_realm").users.update("05c135c6-5ad8-4e17-b1fa-635fc089fd71", {
|
190
|
-
email: "hello@gmail.com"
|
193
|
+
email: "hello@gmail.com",
|
194
|
+
username: "hello",
|
195
|
+
first_name: "Jean",
|
196
|
+
last_name: "Dupond"
|
191
197
|
})
|
192
198
|
```
|
193
199
|
|
200
|
+
Attention point: Since Keycloak 24.0.4, when updating a user, all the writable profile attributes must be passed, otherwise they will be removed. (https://www.keycloak.org/docs/24.0.4/upgrading/)
|
201
|
+
|
194
202
|
### Delete a user
|
195
203
|
|
196
204
|
```ruby
|
@@ -465,6 +473,271 @@ Returns an array of `KeycloakAdmin::IdentityProviderRepresentation`.
|
|
465
473
|
KeycloakAdmin.realm("a_realm").identity_providers.list
|
466
474
|
```
|
467
475
|
|
476
|
+
### Manage [Client Authorization Resources & Scopes](https://www.keycloak.org/docs/latest/authorization_services/index.html#_resource_overview)
|
477
|
+
|
478
|
+
In order to use authorization, you need to enable the client's `authorization_services_enabled` attribute.
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
client_id = "dummy-client"
|
482
|
+
client = KeycloakAdmin.realm("realm_a").clients.find_by_client_id(client_id)
|
483
|
+
client.authorization_services_enabled = true
|
484
|
+
KeycloakAdmin.realm("a_realm").clients.update(client)
|
485
|
+
```
|
486
|
+
|
487
|
+
### Create a scope
|
488
|
+
|
489
|
+
Returns added `KeycloakAdmin::ClientAuthzScopeRepresentation`
|
490
|
+
|
491
|
+
```ruby
|
492
|
+
KeycloakAdmin.realm("a_realm").authz_scopes(client_id).create!("POST_1", "POST 1 scope description", "http://icon.url")
|
493
|
+
````
|
494
|
+
|
495
|
+
### Search for scope
|
496
|
+
|
497
|
+
Returns array of `KeycloakAdmin::ClientAuthzScopeRepresentation`
|
498
|
+
|
499
|
+
```ruby
|
500
|
+
KeycloakAdmin.realm("a_realm").authz_scopes(client.id).search("POST")
|
501
|
+
```
|
502
|
+
|
503
|
+
### Get one scope by its id
|
504
|
+
|
505
|
+
Returns `KeycloakAdmin::ClientAuthzScopeRepresentation`
|
506
|
+
|
507
|
+
```ruby
|
508
|
+
KeycloakAdmin.realm("a_realm").authz_scopes(client.id).get(scope_id)
|
509
|
+
```
|
510
|
+
|
511
|
+
### Delete one scope
|
512
|
+
|
513
|
+
```ruby
|
514
|
+
KeycloakAdmin.realm("a_realm").authz_scopes(client.id).delete(scope.id)
|
515
|
+
```
|
516
|
+
|
517
|
+
### Create a client authorization resource
|
518
|
+
|
519
|
+
note: for scopes, use {name: scope.name} to reference the scope object
|
520
|
+
|
521
|
+
Returns added `KeycloakAdmin::ClientAuthzResourceRepresentation`
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
KeycloakAdmin.realm("realm_id")
|
525
|
+
.authz_resources(client.id)
|
526
|
+
.create!(
|
527
|
+
"Dummy Resource",
|
528
|
+
"type",
|
529
|
+
["/resource_1/*", "/resource_1/"],
|
530
|
+
true,
|
531
|
+
"display_name",
|
532
|
+
[ {name: scope_1.name} ],
|
533
|
+
{"attribute": ["value_1", "value_2"]}
|
534
|
+
)
|
535
|
+
```
|
536
|
+
|
537
|
+
### Update a client authorization resource
|
538
|
+
|
539
|
+
Returns updated `KeycloakAdmin::ClientAuthzResourceRepresentation`
|
540
|
+
|
541
|
+
note: for scopes, use {name: scope.name} to reference the scope object
|
542
|
+
|
543
|
+
```ruby
|
544
|
+
KeycloakAdmin.realm("realm_a")
|
545
|
+
.authz_resources(client.id)
|
546
|
+
.update(resource.id,
|
547
|
+
{
|
548
|
+
"name": "Dummy Resource",
|
549
|
+
"type": "type",
|
550
|
+
"owner_managed_access": true,
|
551
|
+
"display_name": "display_name",
|
552
|
+
"attributes": {"a":["b","c"]},
|
553
|
+
"uris": [ "/resource_1/*" , "/resource_1/" ],
|
554
|
+
"scopes":[
|
555
|
+
{name: scope_1.name},
|
556
|
+
{name: scope_2.name}
|
557
|
+
],
|
558
|
+
"icon_uri": "https://icon.url"
|
559
|
+
})
|
560
|
+
```
|
561
|
+
|
562
|
+
### Find client authorization resources by (name, type, uri, owner, scope)
|
563
|
+
|
564
|
+
Returns array of `KeycloakAdmin::ClientAuthzResourceRepresentation`
|
565
|
+
|
566
|
+
```ruby
|
567
|
+
KeycloakAdmin.realm("realm_a").authz_resources(client.id).find_by("Dummy Resource", "", "", "", "")
|
568
|
+
```
|
569
|
+
or
|
570
|
+
```ruby
|
571
|
+
KeycloakAdmin.realm("realm_a").authz_resources(client.id).find_by("", "type", "", "", "")
|
572
|
+
```
|
573
|
+
|
574
|
+
### Get client authorization resource by its id
|
575
|
+
|
576
|
+
Returns `KeycloakAdmin::ClientAuthzResourceRepresentation`
|
577
|
+
|
578
|
+
```ruby
|
579
|
+
KeycloakAdmin.realm("realm_a").authz_resources(client.id).get(resource.id)
|
580
|
+
```
|
581
|
+
|
582
|
+
### delete a client authorization resource
|
583
|
+
|
584
|
+
```ruby
|
585
|
+
KeycloakAdmin.realm("realm_a").authz_resources(client.id).delete(resource.id)
|
586
|
+
```
|
587
|
+
|
588
|
+
### Create a client authorization policy
|
589
|
+
|
590
|
+
Note: for the moment only `role` policies are supported.
|
591
|
+
|
592
|
+
Returns added `KeycloakAdmin::ClientAuthzPolicyRepresentation`
|
593
|
+
|
594
|
+
```ruby
|
595
|
+
KeycloakAdmin.realm("realm_a")
|
596
|
+
.authz_policies(client.id, 'role')
|
597
|
+
.create!("Policy 1",
|
598
|
+
"description",
|
599
|
+
"role",
|
600
|
+
"POSITIVE",
|
601
|
+
"UNANIMOUS",
|
602
|
+
true,
|
603
|
+
[{id: realm_role.id, required: true}]
|
604
|
+
)
|
605
|
+
```
|
606
|
+
|
607
|
+
### Find client authorization policies by (name, type)
|
608
|
+
|
609
|
+
Returns array of `KeycloakAdmin::ClientAuthzPolicyRepresentation`
|
610
|
+
|
611
|
+
```ruby
|
612
|
+
KeycloakAdmin.realm("realm_a").authz_policies(client.id, 'role').find_by("Policy 1", "role")
|
613
|
+
```
|
614
|
+
|
615
|
+
### Get client authorization policy by its id
|
616
|
+
|
617
|
+
Returns `KeycloakAdmin::ClientAuthzPolicyRepresentation`
|
618
|
+
|
619
|
+
```ruby
|
620
|
+
KeycloakAdmin.realm("realm_a").authz_policies(client.id, 'role').get(policy.id)
|
621
|
+
```
|
622
|
+
|
623
|
+
### Delete a client authorization policy
|
624
|
+
|
625
|
+
```ruby
|
626
|
+
KeycloakAdmin.realm("realm_a").authz_policies(client.id, 'role').delete(policy.id)
|
627
|
+
```
|
628
|
+
|
629
|
+
### Create a client authorization permission (Resource type)
|
630
|
+
|
631
|
+
Returns added `KeycloakAdmin::ClientAuthzPermissionRepresentation`
|
632
|
+
|
633
|
+
```ruby
|
634
|
+
KeycloakAdmin.realm("realm_a")
|
635
|
+
.authz_permissions(client.id, :resource)
|
636
|
+
.create!("Dummy Resource Permission",
|
637
|
+
"resource description",
|
638
|
+
"UNANIMOUS",
|
639
|
+
"POSITIVE",
|
640
|
+
[resource.id],
|
641
|
+
[policy.id],
|
642
|
+
nil,
|
643
|
+
""
|
644
|
+
)
|
645
|
+
```
|
646
|
+
|
647
|
+
### Create a client authorization permission (Scope type)
|
648
|
+
|
649
|
+
Returns added `KeycloakAdmin::ClientAuthzPermissionRepresentation`
|
650
|
+
|
651
|
+
```ruby
|
652
|
+
KeycloakAdmin.realm("realm_a")
|
653
|
+
.authz_permissions(client.id, :scope)
|
654
|
+
.create!("Dummy Scope Permission",
|
655
|
+
"scope description",
|
656
|
+
"UNANIMOUS",
|
657
|
+
"POSITIVE",
|
658
|
+
[resource.id],
|
659
|
+
[policy.id],
|
660
|
+
[scope_1.id, scope_2.id],
|
661
|
+
""
|
662
|
+
)
|
663
|
+
```
|
664
|
+
|
665
|
+
### List a resource authorization permissions (all: scope or resource)
|
666
|
+
|
667
|
+
Return array of `KeycloakAdmin::ClientAuthzPermissionRepresentation`
|
668
|
+
|
669
|
+
```ruby
|
670
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "", resource.id).list
|
671
|
+
```
|
672
|
+
|
673
|
+
### List a resource authorization permissions (by type: resource)
|
674
|
+
|
675
|
+
Return array of `KeycloakAdmin::ClientAuthzPermissionRepresentation`
|
676
|
+
|
677
|
+
```ruby
|
678
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, 'resource').list
|
679
|
+
```
|
680
|
+
### List a resource authorization permissions (by type: scope)
|
681
|
+
|
682
|
+
Return array of `KeycloakAdmin::ClientAuthzPermissionRepresentation`
|
683
|
+
|
684
|
+
```ruby
|
685
|
+
authz_permissions(client.id, 'scope').list.size
|
686
|
+
```
|
687
|
+
|
688
|
+
### Find client authorization permissions by (name, type, scope)
|
689
|
+
|
690
|
+
Return array of `KeycloakAdmin::ClientAuthzPermissionRepresentation`
|
691
|
+
|
692
|
+
```ruby
|
693
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "resource").find_by(resource_permission.name, nil)
|
694
|
+
```
|
695
|
+
or
|
696
|
+
```ruby
|
697
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "resource").find_by(resource_permission.name, nil)
|
698
|
+
```
|
699
|
+
or
|
700
|
+
```ruby
|
701
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "resource").find_by(resource_permission.name, resource.id)
|
702
|
+
|
703
|
+
```
|
704
|
+
or
|
705
|
+
```ruby
|
706
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "scope").find_by(scope_permission.name, resource.id)
|
707
|
+
```
|
708
|
+
or
|
709
|
+
```ruby
|
710
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "scope").find_by(scope_permission.name, resource.id, "POST_1")
|
711
|
+
```
|
712
|
+
or
|
713
|
+
```ruby
|
714
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "resource").find_by(nil, resource.id)
|
715
|
+
```
|
716
|
+
or
|
717
|
+
```ruby
|
718
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "scope").find_by(nil, resource.id)
|
719
|
+
```
|
720
|
+
or
|
721
|
+
```ruby
|
722
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "scope").find_by(nil, resource.id, "POST_1")
|
723
|
+
```
|
724
|
+
or
|
725
|
+
```ruby
|
726
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, "scope").find_by(scope_permission.name, nil)
|
727
|
+
```
|
728
|
+
|
729
|
+
### Delete a client authorization permission, scope type
|
730
|
+
|
731
|
+
```ruby
|
732
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, 'scope').delete(scope.id)
|
733
|
+
```
|
734
|
+
|
735
|
+
### Delete a client authorization permission, resource type
|
736
|
+
|
737
|
+
```ruby
|
738
|
+
KeycloakAdmin.realm("realm_a").authz_permissions(client.id, 'resource').delete(resource_permission.id)
|
739
|
+
```
|
740
|
+
|
468
741
|
## How to execute library tests
|
469
742
|
|
470
743
|
From the `keycloak-admin-api` directory:
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class ClientAuthzPermissionClient < Client
|
3
|
+
def initialize(configuration, realm_client, client_id, type, resource_id = nil)
|
4
|
+
super(configuration)
|
5
|
+
raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
|
6
|
+
raise ArgumentError.new("bad permission type") if !resource_id && !%i[resource scope].include?(type.to_sym)
|
7
|
+
|
8
|
+
@realm_client = realm_client
|
9
|
+
@client_id = client_id
|
10
|
+
@type = type
|
11
|
+
@resource_id = resource_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete(permission_id)
|
15
|
+
execute_http do
|
16
|
+
RestClient::Resource.new(authz_permission_url(@client_id, nil, nil, permission_id), @configuration.rest_client_options).delete(headers)
|
17
|
+
end
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_by(name, resource, scope = nil)
|
22
|
+
response = execute_http do
|
23
|
+
url = "#{authz_permission_url(@client_id)}?name=#{name}&resource=#{resource}&type=#{@type}&scope=#{scope}&deep=true&first=0&max=100"
|
24
|
+
RestClient::Resource.new(url, @configuration.rest_client_options).get(headers)
|
25
|
+
end
|
26
|
+
JSON.parse(response).map { |role_as_hash| ClientAuthzPermissionRepresentation.from_hash(role_as_hash) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def create!(name, description, decision_strategy,logic = "POSITIVE", resources = [], policies = [], scopes = [], resource_type = nil)
|
30
|
+
response = save(build(name, description, decision_strategy, logic, resources, policies, scopes, resource_type))
|
31
|
+
ClientAuthzPermissionRepresentation.from_hash(JSON.parse(response))
|
32
|
+
end
|
33
|
+
|
34
|
+
def save(permission_representation)
|
35
|
+
execute_http do
|
36
|
+
RestClient::Resource.new(authz_permission_url(@client_id, nil, permission_representation.type), @configuration.rest_client_options).post(
|
37
|
+
create_payload(permission_representation), headers
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def list
|
43
|
+
response = execute_http do
|
44
|
+
RestClient::Resource.new(authz_permission_url(@client_id, @resource_id), @configuration.rest_client_options).get(headers)
|
45
|
+
end
|
46
|
+
JSON.parse(response).map { |role_as_hash| ClientAuthzPermissionRepresentation.from_hash(role_as_hash) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def get(permission_id)
|
50
|
+
response = execute_http do
|
51
|
+
RestClient::Resource.new(authz_permission_url(@client_id, nil, @type, permission_id), @configuration.rest_client_options).get(headers)
|
52
|
+
end
|
53
|
+
ClientAuthzPermissionRepresentation.from_hash(JSON.parse(response))
|
54
|
+
end
|
55
|
+
|
56
|
+
def authz_permission_url(client_id, resource_id = nil, type = nil, id = nil)
|
57
|
+
if resource_id
|
58
|
+
"#{@realm_client.realm_admin_url}/clients/#{client_id}/authz/resource-server/resource/#{resource_id}/permissions"
|
59
|
+
elsif id
|
60
|
+
"#{@realm_client.realm_admin_url}/clients/#{client_id}/authz/resource-server/permission/#{type}/#{id}"
|
61
|
+
else
|
62
|
+
"#{@realm_client.realm_admin_url}/clients/#{client_id}/authz/resource-server/permission/#{type}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def build(name, description, decision_strategy, logic, resources, policies, scopes, resource_type)
|
67
|
+
policy = ClientAuthzPermissionRepresentation.new
|
68
|
+
policy.name = name
|
69
|
+
policy.description = description
|
70
|
+
policy.type = @type
|
71
|
+
policy.decision_strategy = decision_strategy
|
72
|
+
policy.resource_type = resource_type
|
73
|
+
policy.resources = resources
|
74
|
+
policy.policies = policies
|
75
|
+
policy.scopes = scopes
|
76
|
+
policy.logic = logic
|
77
|
+
policy
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class ClientAuthzPolicyClient < Client
|
3
|
+
def initialize(configuration, realm_client, client_id, type)
|
4
|
+
super(configuration)
|
5
|
+
raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
|
6
|
+
raise ArgumentError.new("type must be defined") unless type
|
7
|
+
raise ArgumentError.new("only 'role' policies supported") unless type.to_sym == :role
|
8
|
+
|
9
|
+
@realm_client = realm_client
|
10
|
+
@client_id = client_id
|
11
|
+
@type = type
|
12
|
+
end
|
13
|
+
|
14
|
+
def create!(name, description, type, logic, decision_strategy, fetch_roles, roles)
|
15
|
+
response = save(build(name, description, type, logic, decision_strategy, fetch_roles, roles))
|
16
|
+
ClientAuthzPolicyRepresentation.from_hash(JSON.parse(response))
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(policy_representation)
|
20
|
+
execute_http do
|
21
|
+
RestClient::Resource.new(authz_policy_url(@client_id, @type), @configuration.rest_client_options).post(
|
22
|
+
create_payload(policy_representation), headers
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(policy_id)
|
28
|
+
response = execute_http do
|
29
|
+
RestClient::Resource.new(authz_policy_url(@client_id, @type, policy_id), @configuration.rest_client_options).get(headers)
|
30
|
+
end
|
31
|
+
ClientAuthzPolicyRepresentation.from_hash(JSON.parse(response))
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_by(name, type)
|
35
|
+
response = execute_http do
|
36
|
+
url = "#{authz_policy_url(@client_id, @type)}?permission=false&name=#{name}&type=#{type}&first=0&max=11"
|
37
|
+
RestClient::Resource.new(url, @configuration.rest_client_options).get(headers)
|
38
|
+
end
|
39
|
+
JSON.parse(response).map { |role_as_hash| ClientAuthzPolicyRepresentation.from_hash(role_as_hash) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(policy_id)
|
43
|
+
execute_http do
|
44
|
+
RestClient::Resource.new(authz_policy_url(@client_id, @type, policy_id), @configuration.rest_client_options).delete(headers)
|
45
|
+
end
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def list
|
50
|
+
response = execute_http do
|
51
|
+
RestClient::Resource.new(authz_policy_url(@client_id, @type), @configuration.rest_client_options).get(headers)
|
52
|
+
end
|
53
|
+
JSON.parse(response).map { |role_as_hash| ClientAuthzPolicyRepresentation.from_hash(role_as_hash) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def authz_policy_url(client_id, type, id = nil)
|
57
|
+
if id
|
58
|
+
"#{@realm_client.realm_admin_url}/clients/#{client_id}/authz/resource-server/policy/#{type}/#{id}"
|
59
|
+
else
|
60
|
+
"#{@realm_client.realm_admin_url}/clients/#{client_id}/authz/resource-server/policy/#{type}?permission=false"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def build(name, description, type, logic, decision_strategy, fetch_roles, roles=[])
|
65
|
+
policy = ClientAuthzPolicyRepresentation.new
|
66
|
+
policy.name = name
|
67
|
+
policy.description = description
|
68
|
+
policy.type = type
|
69
|
+
policy.logic = logic
|
70
|
+
policy.decision_strategy = decision_strategy
|
71
|
+
policy.fetch_roles = fetch_roles
|
72
|
+
policy.roles = roles
|
73
|
+
policy
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|