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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Dockerfile +24 -0
  3. data/.github/workflows/ci.yml +83 -0
  4. data/CHANGELOG.md +12 -2
  5. data/Gemfile.lock +8 -8
  6. data/README.md +277 -4
  7. data/lib/keycloak-admin/client/client_authz_permission_client.rb +81 -0
  8. data/lib/keycloak-admin/client/client_authz_policy_client.rb +76 -0
  9. data/lib/keycloak-admin/client/client_authz_resource_client.rb +93 -0
  10. data/lib/keycloak-admin/client/client_authz_scope_client.rb +71 -0
  11. data/lib/keycloak-admin/client/group_client.rb +41 -13
  12. data/lib/keycloak-admin/client/realm_client.rb +16 -0
  13. data/lib/keycloak-admin/client/role_client.rb +12 -10
  14. data/lib/keycloak-admin/client/user_client.rb +1 -0
  15. data/lib/keycloak-admin/representation/client_authz_permission_representation.rb +34 -0
  16. data/lib/keycloak-admin/representation/client_authz_policy_config_representation.rb +15 -0
  17. data/lib/keycloak-admin/representation/client_authz_policy_representation.rb +27 -0
  18. data/lib/keycloak-admin/representation/client_authz_resource_representation.rb +26 -0
  19. data/lib/keycloak-admin/representation/client_authz_scope_representation.rb +17 -0
  20. data/lib/keycloak-admin/representation/group_representation.rb +9 -5
  21. data/lib/keycloak-admin/version.rb +1 -1
  22. data/lib/keycloak-admin.rb +9 -0
  23. data/spec/client/client_authz_permission_client_spec.rb +170 -0
  24. data/spec/client/client_authz_policy_client_spec.rb +170 -0
  25. data/spec/client/client_authz_resource_client_spec.rb +150 -0
  26. data/spec/client/client_authz_scope_client_spec.rb +134 -0
  27. data/spec/client/client_client_spec.rb +2 -2
  28. data/spec/client/client_role_mappings_client_spec.rb +2 -2
  29. data/spec/client/group_client_spec.rb +137 -15
  30. data/spec/client/identity_provider_client_spec.rb +1 -1
  31. data/spec/client/realm_client_spec.rb +4 -4
  32. data/spec/client/role_client_spec.rb +12 -16
  33. data/spec/client/role_mapper_client_spec.rb +1 -1
  34. data/spec/client/token_client_spec.rb +1 -1
  35. data/spec/client/user_client_spec.rb +5 -5
  36. data/spec/configuration_spec.rb +1 -1
  37. data/spec/integration/client_authorization_spec.rb +95 -0
  38. data/spec/representation/client_authz_permission_representation_spec.rb +52 -0
  39. data/spec/representation/client_authz_policy_representation_spec.rb +47 -0
  40. data/spec/representation/client_authz_resource_representation_spec.rb +33 -0
  41. data/spec/representation/client_authz_scope_representation_spec.rb +19 -0
  42. data/spec/representation/group_representation_spec.rb +7 -0
  43. metadata +23 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf6847f9dc60316780255644c15320d06a998331df2283e30a007c4bea951ba1
4
- data.tar.gz: 4af207ec29032148c58ff23194f4ade85aec556718bb4896c00ec29fe95000a9
3
+ metadata.gz: be47369f8365b8b32ff4ca3b09ebad45f5037212eca3022524b10ba17b0f64d6
4
+ data.tar.gz: 36a340f437ecb97ce5c415e752d5e1159ebbc5911d454f643985de541c6deaf3
5
5
  SHA512:
6
- metadata.gz: 6e03b3d8ae4f5eac52399fefcbea427d34a5afc5e08475365a8166faa5031eebb7b87cff23393e16e91e2df0f50263023b29262addff9550f79ef7bb5c739637
7
- data.tar.gz: 1c91e21ee8d74ba5ca05f773d20acdc11e3a6c8fd8ab3e8098cb75c7d44ed1c7763e3954b094012a5fe9000c1ff4cb048355e7a3a1f134b8911fc349b03919d9
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.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.0)
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.5)
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.2023.1205)
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.2)
30
+ rspec-core (3.12.3)
31
31
  rspec-support (~> 3.12.0)
32
- rspec-expectations (3.12.3)
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.6)
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.1)
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.0.24"
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
- config.rest_client_options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE }
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` | `{ verify_ssl: OpenSSL::SSL::VERIFY_NONE }` | 
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