keycloak-admin 1.1.1 → 1.1.3
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/.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
|