keycloak-admin 1.1.5 → 1.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc70b33efe8f4293f7eb0f60eeb30f9468d7866a08e20180b6a3b66c35c96ea8
4
- data.tar.gz: f9cfd0bec183db13053ecd034c4c0c180a8564c116fdb6ace4271a086622f18b
3
+ metadata.gz: 0e12885f5810b15fdc63cc227cb48c1e5a730b6328d3021e4de319d1859fb996
4
+ data.tar.gz: 5b691cf6498c9754c8778ed043501c94620ef0ca3a7649ae1e2d8ebf310047c8
5
5
  SHA512:
6
- metadata.gz: a3a4bf1e49ea91c845458186565f68636244e515a7ad78d12e69c50ad190de94fd38050a3222ed16eb33f0b5a13d5e1aa65e496ebb48fb732a5248ff6c227953
7
- data.tar.gz: b60ee39f400e84c57f1627042abbb07b0ca820471d3fe53448c09a2ef8b4cc1acb193ea4f11d63d4b60941f0ee9496ecb6a5fc89bc6007fa1c1eca69a5b37925
6
+ metadata.gz: 552d1b3896305cbe8799ca2627631f049fe8a526b3450fec6c4a360dd9ac2139185b91e642973acf2ee9c1af2bdf49073e5cf66c2eafbc3d5a72c8749331cb53
7
+ data.tar.gz: 2fbd6f1e6034c0654ac56b856d67fc975fab74a6642071e0bb6f5e8fcdd0b2989042ee2a6aef735e767ca6a9ba53d471870e58b97d1cd71df68b62036677eea4
@@ -68,7 +68,7 @@ jobs:
68
68
  "redirectUris":["http://localhost:8180/demo"]
69
69
  }'
70
70
 
71
- - uses: actions/checkout@v4
71
+ - uses: actions/checkout@v6
72
72
  - name: Set up Ruby
73
73
  uses: ruby/setup-ruby@v1
74
74
  with:
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ 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.7] - 2026-03-27
9
+
10
+ * [Feature] Client scopes - supported operations: `create!`, `get`, `delete`, `list`, and `search`.
11
+ * [Feature] Client scopes protocol mappers - supported operations: `create!`, `get`, `delete`, `list`, and `search`.
12
+
13
+ ## [1.1.6] - 2026-01-05
14
+
15
+ * [Feature] Support for Organizations (Multi-tenancy):
16
+ * **Organization Management**:
17
+ * Supported operations: `create!`, `update`, `get`, `delete`, `list`, and `count`.
18
+ * Supported searching and filtering via `exact`, `query`, and `search` parameters.
19
+ * **Member Management**:
20
+ * Added ability to list organization members with pagination and filtering (`members`).
21
+ * Added `members_count` to retrieve the total number of members.
22
+ * Added `get_member`, `add_member` (by user ID), and `delete_member`.
23
+ * Added helper to find all organizations associated with a specific user: `associated_with_member`.
24
+ * **Invitations**:
25
+ * Added `invite_user`: Invites a new user via email/name.
26
+ * Added `invite_existing_user`: Invites an existing Keycloak user to the organization by ID.
27
+ * **Identity Provider (IdP) Linking**:
28
+ * Added methods to manage IdPs linked to an organization: `identity_providers`, `get_identity_provider`, `add_identity_provider`, and `delete_identity_provider`.
29
+
8
30
  ## [1.1.5] - 2026-01-05
9
31
 
10
32
  * [Feature] Added the ability to list credentials for a given user.
data/Gemfile.lock CHANGED
@@ -1,25 +1,29 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keycloak-admin (1.1.5)
4
+ keycloak-admin (1.1.7)
5
5
  http-cookie (~> 1.0, >= 1.0.3)
6
6
  rest-client (~> 2.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- byebug (12.0.0)
11
+ byebug (13.0.0)
12
+ reline (>= 0.6.0)
12
13
  diff-lcs (1.6.2)
13
14
  domain_name (0.6.20240107)
14
15
  http-accept (1.7.0)
15
16
  http-cookie (1.1.0)
16
17
  domain_name (~> 0.5)
18
+ io-console (0.8.2)
17
19
  logger (1.7.0)
18
20
  mime-types (3.7.0)
19
21
  logger
20
22
  mime-types-data (~> 3.2025, >= 3.2025.0507)
21
- mime-types-data (3.2025.0924)
23
+ mime-types-data (3.2026.0317)
22
24
  netrc (0.11.0)
25
+ reline (0.6.3)
26
+ io-console (~> 0.5)
23
27
  rest-client (2.1.0)
24
28
  http-accept (>= 1.7.0, < 2.0)
25
29
  http-cookie (>= 1.0.2, < 2.0)
@@ -34,16 +38,16 @@ GEM
34
38
  rspec-expectations (3.13.5)
35
39
  diff-lcs (>= 1.2.0, < 2.0)
36
40
  rspec-support (~> 3.13.0)
37
- rspec-mocks (3.13.7)
41
+ rspec-mocks (3.13.8)
38
42
  diff-lcs (>= 1.2.0, < 2.0)
39
43
  rspec-support (~> 3.13.0)
40
- rspec-support (3.13.6)
44
+ rspec-support (3.13.7)
41
45
 
42
46
  PLATFORMS
43
47
  ruby
44
48
 
45
49
  DEPENDENCIES
46
- byebug (= 12.0.0)
50
+ byebug (= 13.0.0)
47
51
  keycloak-admin!
48
52
  rspec (= 3.13.2)
49
53
 
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.1.5"
15
+ gem "keycloak-admin", "1.1.6"
16
16
  ```
17
17
 
18
18
  ## Login
@@ -135,6 +135,13 @@ All options have a default value. However, all of them can be changed in your in
135
135
  * Execute actions emails
136
136
  * Send forgot passsword mail
137
137
  * Client Authorization, create, update, get, delete Resource, Scope, Policy, Permission, Policy Enforcer
138
+ * Get list of client scopes, create/save/get/delete/search a client scope
139
+ * Get list of protocol mappers for a client scope, create/save/get/delete a protocol mapper
140
+ * Get list of organizations, create/update/get/delete an organization
141
+ * Get list of members of an organization, add/remove members
142
+ * Invite new or existing users to an organization
143
+ * List, add, and remove Identity Providers for an organization
144
+ * Get list of organizations associated with a specific user
138
145
 
139
146
  ### Get an access token
140
147
 
@@ -252,6 +259,12 @@ user_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
252
259
  KeycloakAdmin.realm("a_realm").users.get_redirect_impersonation(user_id)
253
260
  ```
254
261
 
262
+ ### List all the organizations of a realm
263
+
264
+ ```ruby
265
+ KeycloakAdmin.realm("a_realm").organizations.list
266
+ ```
267
+
255
268
  ### Exchange a configurable token
256
269
 
257
270
  *Requires your Keycloak server to have deployed the Custom REST API `configurable-token`* (https://github.com/looorent/keycloak-configurable-token-api)
@@ -746,6 +759,120 @@ KeycloakAdmin.realm("realm_a").authz_permissions(client.id, 'scope').delete(scop
746
759
  KeycloakAdmin.realm("realm_a").authz_permissions(client.id, 'resource').delete(resource_permission.id)
747
760
  ```
748
761
 
762
+ ### Manage Client Scopes
763
+
764
+ ### List all client scopes in a realm
765
+
766
+ Returns an array of `KeycloakAdmin::ClientScopeRepresentation`.
767
+
768
+ ```ruby
769
+ KeycloakAdmin.realm("a_realm").client_scopes.list
770
+ ```
771
+
772
+ ### Get a client scope by its id
773
+
774
+ Returns an instance of `KeycloakAdmin::ClientScopeRepresentation`.
775
+
776
+ ```ruby
777
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
778
+ KeycloakAdmin.realm("a_realm").client_scopes.get(client_scope_id)
779
+ ```
780
+
781
+ ### Search for client scopes by name
782
+
783
+ Returns an array of `KeycloakAdmin::ClientScopeRepresentation` whose names contain the given substring.
784
+
785
+ ```ruby
786
+ KeycloakAdmin.realm("a_realm").client_scopes.search("my-scope")
787
+ ```
788
+
789
+ ### Create a client scope
790
+
791
+ Takes `scope_representation` of type `KeycloakAdmin::ClientScopeRepresentation`. Returns `true` on success.
792
+
793
+ ```ruby
794
+ scope = KeycloakAdmin::ClientScopeRepresentation.new
795
+ scope.name = "my-scope"
796
+ scope.description = "My custom scope"
797
+ scope.protocol = "openid-connect"
798
+ scope.attributes = { "display.on.consent.screen" => "true", "include.in.token.scope" => "true" }
799
+ KeycloakAdmin.realm("a_realm").client_scopes.create!(scope)
800
+ ```
801
+
802
+ ### Save (update) a client scope
803
+
804
+ Takes `scope_representation` of type `KeycloakAdmin::ClientScopeRepresentation` (must include its `id`). Returns `true` on success.
805
+
806
+ ```ruby
807
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
808
+ scope = KeycloakAdmin.realm("a_realm").client_scopes.get(client_scope_id)
809
+ scope.description = "Updated description"
810
+ KeycloakAdmin.realm("a_realm").client_scopes.save(scope)
811
+ ```
812
+
813
+ ### Delete a client scope
814
+
815
+ ```ruby
816
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
817
+ KeycloakAdmin.realm("a_realm").client_scopes.delete(client_scope_id)
818
+ ```
819
+
820
+ ### Manage Protocol Mappers for a Client Scope
821
+
822
+ Protocol mappers allow you to transform tokens and assertions. The following operations are available on the protocol mappers of a given client scope.
823
+
824
+ ### List protocol mappers for a client scope
825
+
826
+ Returns an array of `KeycloakAdmin::ProtocolMapperRepresentation`.
827
+
828
+ ```ruby
829
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
830
+ KeycloakAdmin.realm("a_realm").client_scope_protocol_mappers(client_scope_id).list
831
+ ```
832
+
833
+ ### Get a protocol mapper by its id
834
+
835
+ Returns an instance of `KeycloakAdmin::ProtocolMapperRepresentation`.
836
+
837
+ ```ruby
838
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
839
+ mapper_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
840
+ KeycloakAdmin.realm("a_realm").client_scope_protocol_mappers(client_scope_id).get(mapper_id)
841
+ ```
842
+
843
+ ### Create a protocol mapper for a client scope
844
+
845
+ Takes `mapper_representation` of type `KeycloakAdmin::ProtocolMapperRepresentation`. Returns `true` on success.
846
+
847
+ ```ruby
848
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
849
+ mapper = KeycloakAdmin::ProtocolMapperRepresentation.new
850
+ mapper.name = "my-mapper"
851
+ mapper.protocol = "openid-connect"
852
+ mapper.protocolMapper = "oidc-usermodel-attribute-mapper"
853
+ mapper.config = { "user.attribute" => "locale", "claim.name" => "locale", "jsonType.label" => "String", "id.token.claim" => "true", "access.token.claim" => "true", "userinfo.token.claim" => "true" }
854
+ KeycloakAdmin.realm("a_realm").client_scope_protocol_mappers(client_scope_id).create!(mapper)
855
+ ```
856
+
857
+ ### Save (update) a protocol mapper for a client scope
858
+
859
+ Takes `mapper_representation` of type `KeycloakAdmin::ProtocolMapperRepresentation` (must include its `id`). Returns `true` on success.
860
+
861
+ ```ruby
862
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
863
+ mapper = KeycloakAdmin.realm("a_realm").client_scope_protocol_mappers(client_scope_id).get(mapper_id)
864
+ mapper.config["claim.name"] = "updated_claim"
865
+ KeycloakAdmin.realm("a_realm").client_scope_protocol_mappers(client_scope_id).save(mapper)
866
+ ```
867
+
868
+ ### Delete a protocol mapper from a client scope
869
+
870
+ ```ruby
871
+ client_scope_id = "7686af34-204c-4515-8122-78d19febbf6e"
872
+ mapper_id = "95985b21-d884-4bbd-b852-cb8cd365afc2"
873
+ KeycloakAdmin.realm("a_realm").client_scope_protocol_mappers(client_scope_id).delete(mapper_id)
874
+ ```
875
+
749
876
  ## How to execute library tests
750
877
 
751
878
  From the `keycloak-admin-api` directory:
@@ -20,5 +20,5 @@ Gem::Specification.new do |spec|
20
20
  spec.add_dependency "http-cookie", "~> 1.0", ">= 1.0.3"
21
21
  spec.add_dependency "rest-client", "~> 2.0"
22
22
  spec.add_development_dependency "rspec", "3.13.2"
23
- spec.add_development_dependency "byebug", "12.0.0"
23
+ spec.add_development_dependency "byebug", "13.0.0"
24
24
  end
@@ -0,0 +1,65 @@
1
+ module KeycloakAdmin
2
+ class ClientScopeClient < Client
3
+ def initialize(configuration, realm_client)
4
+ super(configuration)
5
+
6
+ raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
7
+
8
+ @realm_client = realm_client
9
+ end
10
+
11
+ def list
12
+ response = execute_http do
13
+ RestClient::Resource.new(client_scopes_url, @configuration.rest_client_options).get(headers)
14
+ end
15
+
16
+ JSON.parse(response).map { |h| ClientScopeRepresentation.from_hash(h) }
17
+ end
18
+
19
+ def get(client_scope_id)
20
+ response = execute_http do
21
+ RestClient::Resource.new(client_scopes_url(client_scope_id), @configuration.rest_client_options).get(headers)
22
+ end
23
+
24
+ ClientScopeRepresentation.from_hash(JSON.parse(response))
25
+ end
26
+
27
+ def create!(client_scope_representation)
28
+ execute_http do
29
+ RestClient::Resource.new(client_scopes_url, @configuration.rest_client_options).post(
30
+ create_payload(client_scope_representation), headers
31
+ )
32
+ end
33
+
34
+ true
35
+ end
36
+
37
+ def save(client_scope_representation)
38
+ execute_http do
39
+ RestClient::Resource.new(client_scopes_url(client_scope_representation.id), @configuration.rest_client_options).put(
40
+ create_payload(client_scope_representation), headers
41
+ )
42
+ end
43
+
44
+ true
45
+ end
46
+
47
+ def search(name)
48
+ list.select { |scope| scope&.name&.include?(name) }
49
+ end
50
+
51
+ def delete(client_scope_id)
52
+ execute_http do
53
+ RestClient::Resource.new(client_scopes_url(client_scope_id), @configuration.rest_client_options).delete(headers)
54
+ end
55
+
56
+ true
57
+ end
58
+
59
+ def client_scopes_url(client_scope_id = nil)
60
+ base = "#{@realm_client.realm_admin_url}/client-scopes"
61
+
62
+ client_scope_id ? "#{base}/#{client_scope_id}" : base
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,62 @@
1
+ module KeycloakAdmin
2
+ class ClientScopeProtocolMapperClient < Client
3
+ def initialize(configuration, realm_client, client_scope_id)
4
+ super(configuration)
5
+
6
+ raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
7
+
8
+ @realm_client = realm_client
9
+ @client_scope_id = client_scope_id
10
+ end
11
+
12
+ def list
13
+ response = execute_http do
14
+ RestClient::Resource.new(protocol_mappers_url, @configuration.rest_client_options).get(headers)
15
+ end
16
+
17
+ JSON.parse(response).map { |h| ProtocolMapperRepresentation.from_hash(h) }
18
+ end
19
+
20
+ def get(mapper_id)
21
+ response = execute_http do
22
+ RestClient::Resource.new(protocol_mappers_url(mapper_id), @configuration.rest_client_options).get(headers)
23
+ end
24
+
25
+ ProtocolMapperRepresentation.from_hash(JSON.parse(response))
26
+ end
27
+
28
+ def create!(mapper_representation)
29
+ execute_http do
30
+ RestClient::Resource.new(protocol_mappers_url, @configuration.rest_client_options).post(
31
+ create_payload(mapper_representation), headers
32
+ )
33
+ end
34
+
35
+ true
36
+ end
37
+
38
+ def save(mapper_representation)
39
+ execute_http do
40
+ RestClient::Resource.new(protocol_mappers_url(mapper_representation.id), @configuration.rest_client_options).put(
41
+ create_payload(mapper_representation), headers
42
+ )
43
+ end
44
+
45
+ true
46
+ end
47
+
48
+ def delete(mapper_id)
49
+ execute_http do
50
+ RestClient::Resource.new(protocol_mappers_url(mapper_id), @configuration.rest_client_options).delete(headers)
51
+ end
52
+
53
+ true
54
+ end
55
+
56
+ def protocol_mappers_url(mapper_id = nil)
57
+ base = "#{@realm_client.realm_admin_url}/client-scopes/#{@client_scope_id}/protocol-mappers/models"
58
+
59
+ mapper_id ? "#{base}/#{mapper_id}" : base
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,245 @@
1
+ module KeycloakAdmin
2
+ class OrganizationClient < Client
3
+ def initialize(configuration, realm_client)
4
+ super(configuration)
5
+ raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
6
+ @realm_client = realm_client
7
+ end
8
+
9
+ # This endpoint does not return members
10
+ def list(brief_representation=true, exact=nil, first=nil, max=nil, query=nil, search=nil)
11
+ response = execute_http do
12
+ RestClient::Resource.new(organizations_url_with_parameters(brief_representation, exact, first, max, query, search), @configuration.rest_client_options).get(headers)
13
+ end
14
+ JSON.parse(response).map { |organization_as_hash| OrganizationRepresentation.from_hash(organization_as_hash) }
15
+ end
16
+
17
+ def count(exact=nil, query=nil, search=nil)
18
+ response = execute_http do
19
+ RestClient::Resource.new(count_url(exact, query, search), @configuration.rest_client_options).get(headers)
20
+ end
21
+ response.to_i
22
+ end
23
+
24
+ def delete(organization_id)
25
+ execute_http do
26
+ RestClient::Resource.new(organization_url(organization_id), @configuration.rest_client_options).delete(headers)
27
+ end
28
+ true
29
+ end
30
+
31
+ def update(organization_representation)
32
+ execute_http do
33
+ RestClient::Resource.new(organization_url(organization_representation.id), @configuration.rest_client_options).put(
34
+ create_payload(organization_representation), headers
35
+ )
36
+ end
37
+
38
+ get(organization_representation.id)
39
+ end
40
+
41
+ def create!(name, alias_name, enabled, description, redirect_url=nil, domains=[], attributes={})
42
+ save(build(name, alias_name, enabled, description, redirect_url, domains, attributes))
43
+ end
44
+
45
+ # This operation does not associate members and identity providers
46
+ def save(organization_representation)
47
+ execute_http do
48
+ RestClient::Resource.new(organizations_url, @configuration.rest_client_options).post(
49
+ create_payload(organization_representation), headers
50
+ )
51
+ end
52
+ true
53
+ end
54
+
55
+ def get(organization_id)
56
+ response = execute_http do
57
+ RestClient::Resource.new(organization_url(organization_id), @configuration.rest_client_options).get(headers)
58
+ end
59
+ OrganizationRepresentation.from_hash(JSON.parse(response))
60
+ end
61
+
62
+ def identity_providers(organization_id)
63
+ response = execute_http do
64
+ RestClient::Resource.new(identity_providers_url(organization_id), @configuration.rest_client_options).get(headers)
65
+ end
66
+ JSON.parse(response).map { |idp_as_hash| IdentityProviderRepresentation.from_hash(idp_as_hash) }
67
+ end
68
+
69
+ def get_identity_provider(organization_id, identity_provider_alias)
70
+ raise ArgumentError.new("identity_provider_alias must be defined") if identity_provider_alias.nil?
71
+ response = execute_http do
72
+ RestClient::Resource.new("#{identity_providers_url(organization_id)}/#{identity_provider_alias}", @configuration.rest_client_options).get(headers)
73
+ end
74
+ IdentityProviderRepresentation.from_hash(JSON.parse(response))
75
+ end
76
+
77
+ def add_identity_provider(organization_id, identity_provider_alias)
78
+ raise ArgumentError.new("identity_provider_alias must be defined") if identity_provider_alias.nil?
79
+ execute_http do
80
+ RestClient::Resource.new(identity_providers_url(organization_id), @configuration.rest_client_options).post(identity_provider_alias, headers)
81
+ end
82
+ true
83
+ end
84
+
85
+ def delete_identity_provider(organization_id, identity_provider_alias)
86
+ execute_http do
87
+ RestClient::Resource.new(identity_provider_url(organization_id, identity_provider_alias), @configuration.rest_client_options).delete(headers)
88
+ end
89
+ true
90
+ end
91
+
92
+ def members_count(organization_id)
93
+ response = execute_http do
94
+ RestClient::Resource.new(members_count_url(organization_id), @configuration.rest_client_options).get(headers)
95
+ end
96
+ response.to_i
97
+ end
98
+
99
+ def members(organization_id, exact=nil, first=nil, max=nil, membership_type=nil, search=nil)
100
+ response = execute_http do
101
+ RestClient::Resource.new(members_url_with_query_parameters(organization_id, exact, first, max, membership_type, search), @configuration.rest_client_options).get(headers)
102
+ end
103
+ JSON.parse(response).map { |member_as_hash| MemberRepresentation.from_hash(member_as_hash) }
104
+ end
105
+
106
+ def invite_existing_user(organization_id, user_id)
107
+ raise ArgumentError.new("user_id must be defined") if user_id.nil?
108
+ execute_http do
109
+ RestClient::Resource.new(invite_existing_user_url(organization_id), @configuration.rest_client_options).post({id: user_id}, headers.merge(content_type: "application/x-www-form-urlencoded"))
110
+ end
111
+ true
112
+ end
113
+
114
+ def invite_user(organization_id, email, first_name, last_name)
115
+ execute_http do
116
+ RestClient::Resource.new(invite_user_url(organization_id), @configuration.rest_client_options).post({
117
+ email: email,
118
+ firstName: first_name,
119
+ lastName: last_name
120
+ }, headers.merge(content_type: "application/x-www-form-urlencoded"))
121
+ end
122
+ true
123
+ end
124
+
125
+ def add_member(organization_id, user_id)
126
+ raise ArgumentError.new("user_id must be defined") if user_id.nil?
127
+ execute_http do
128
+ RestClient::Resource.new(members_url(organization_id), @configuration.rest_client_options).post(user_id, headers)
129
+ end
130
+ true
131
+ end
132
+
133
+ def delete_member(organization_id, member_id)
134
+ execute_http do
135
+ RestClient::Resource.new(member_url(organization_id, member_id), @configuration.rest_client_options).delete(headers)
136
+ end
137
+ true
138
+ end
139
+
140
+ def get_member(organization_id, member_id)
141
+ response = execute_http do
142
+ RestClient::Resource.new(member_url(organization_id, member_id), @configuration.rest_client_options).get(headers)
143
+ end
144
+ MemberRepresentation.from_hash(JSON.parse(response))
145
+ end
146
+
147
+ def associated_with_member(member_id, brief_representation=true)
148
+ response = execute_http do
149
+ RestClient::Resource.new(associated_with_member_url(member_id, brief_representation), @configuration.rest_client_options).get(headers)
150
+ end
151
+ JSON.parse(response).map { |organization_as_hash| OrganizationRepresentation.from_hash(organization_as_hash) }
152
+ end
153
+
154
+ def organizations_url
155
+ "#{@realm_client.realm_admin_url}/organizations"
156
+ end
157
+
158
+ def organization_url(organization_id)
159
+ raise ArgumentError.new("organization_id must be defined") if organization_id.nil?
160
+ "#{organizations_url}/#{organization_id}"
161
+ end
162
+
163
+ def identity_providers_url(organization_id)
164
+ "#{organization_url(organization_id)}/identity-providers"
165
+ end
166
+
167
+ def identity_provider_url(organization_id, identity_provider_alias)
168
+ raise ArgumentError.new("identity_provider_alias must be defined") if identity_provider_alias.nil?
169
+ "#{identity_providers_url(organization_id)}/#{identity_provider_alias}"
170
+ end
171
+
172
+ def count_url(exact, query, search)
173
+ query_parameters = {exact: exact, q: query, search: search}.compact.to_a.map { |e| "#{e[0]}=#{e[1]}" }.join("&")
174
+ "#{organizations_url}/count?#{query_parameters}"
175
+ end
176
+
177
+ def organizations_url_with_parameters(brief_representation, exact, first, max, query, search)
178
+ query_parameters = {
179
+ briefRepresentation: brief_representation,
180
+ exact: exact,
181
+ first: first,
182
+ max: max,
183
+ q: query,
184
+ search: search
185
+ }.compact.to_a.map { |e| "#{e[0]}=#{e[1]}" }.join("&")
186
+ "#{organizations_url}?#{query_parameters}"
187
+ end
188
+
189
+ def associated_with_member_url(member_id, brief_representation=true)
190
+ "#{organizations_url}/members/#{member_id}/organizations?briefRepresentation=#{brief_representation}"
191
+ end
192
+
193
+ def members_count_url(organization_id)
194
+ "#{organization_url(organization_id)}/members/count"
195
+ end
196
+
197
+ def member_url(organization_id, member_id)
198
+ raise ArgumentError.new("member_id must be defined") if member_id.nil?
199
+ "#{organization_url(organization_id)}/members/#{member_id}"
200
+ end
201
+
202
+ def invite_existing_user_url(organization_id)
203
+ "#{organization_url(organization_id)}/members/invite-existing-user"
204
+ end
205
+
206
+ def invite_user_url(organization_id)
207
+ "#{organization_url(organization_id)}/members/invite-user"
208
+ end
209
+
210
+ def members_url(organization_id)
211
+ "#{organization_url(organization_id)}/members"
212
+ end
213
+
214
+ def members_url_with_query_parameters(organization_id, exact, first, max, membership_type, search)
215
+ query_parameters = {
216
+ exact: exact,
217
+ first: first,
218
+ max: max,
219
+ membershipType: membership_type,
220
+ search: search
221
+ }.compact.to_a.map { |e| "#{e[0]}=#{e[1]}" }.join("&")
222
+ "#{organization_url(organization_id)}/members?#{query_parameters}"
223
+ end
224
+
225
+ def build(name, alias_name, enabled, description, redirect_url=nil, domains=[], attributes={})
226
+ unless domains.is_a?(Array)
227
+ raise ArgumentError.new("domains must be an Array, got #{new_domains.class}")
228
+ end
229
+
230
+ unless domains.all? { |domain| domain.is_a?(KeycloakAdmin::OrganizationDomainRepresentation) }
231
+ raise ArgumentError.new("All items in domains must be of type OrganizationDomainRepresentation")
232
+ end
233
+
234
+ organization = OrganizationRepresentation.new
235
+ organization.name = name
236
+ organization.alias = alias_name
237
+ organization.enabled = enabled
238
+ organization.description = description
239
+ organization.redirect_url = redirect_url
240
+ organization.domains = domains
241
+ organization.attributes = attributes
242
+ organization
243
+ end
244
+ end
245
+ end
@@ -95,10 +95,22 @@ module KeycloakAdmin
95
95
  IdentityProviderClient.new(@configuration, self)
96
96
  end
97
97
 
98
+ def organizations
99
+ OrganizationClient.new(@configuration, self)
100
+ end
101
+
98
102
  def user(user_id)
99
103
  UserResource.new(@configuration, self, user_id)
100
104
  end
101
105
 
106
+ def client_scopes
107
+ ClientScopeClient.new(@configuration, self)
108
+ end
109
+
110
+ def client_scope_protocol_mappers(client_scope_id)
111
+ ClientScopeProtocolMapperClient.new(@configuration, self, client_scope_id)
112
+ end
113
+
102
114
  def authz_scopes(client_id, resource_id = nil)
103
115
  ClientAuthzScopeClient.new(@configuration, self, client_id, resource_id)
104
116
  end
@@ -0,0 +1,21 @@
1
+ module KeycloakAdmin
2
+ class ClientScopeRepresentation < Representation
3
+ attr_accessor :id,
4
+ :name,
5
+ :description,
6
+ :protocol,
7
+ :attributes,
8
+ :protocol_mappers
9
+
10
+ def self.from_hash(hash)
11
+ rep = new
12
+ rep.id = hash["id"]
13
+ rep.name = hash["name"]
14
+ rep.description = hash["description"]
15
+ rep.protocol = hash["protocol"]
16
+ rep.attributes = hash["attributes"]
17
+ rep.protocol_mappers = (hash["protocolMappers"] || []).map { |m| ProtocolMapperRepresentation.from_hash(m) }
18
+ rep
19
+ end
20
+ end
21
+ end