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.
@@ -11,6 +11,7 @@ module KeycloakAdmin
11
11
  :add_read_token_role_on_create,
12
12
  :authenticate_by_default,
13
13
  :link_only,
14
+ :organization_id,
14
15
  :first_broker_login_flow_alias,
15
16
  :config
16
17
 
@@ -30,6 +31,7 @@ module KeycloakAdmin
30
31
  hash["addReadTokenRoleOnCreate"],
31
32
  hash["authenticateByDefault"],
32
33
  hash["linkOnly"],
34
+ hash["organizationId"],
33
35
  hash["firstBrokerLoginFlowAlias"],
34
36
  hash["config"]
35
37
  )
@@ -47,6 +49,7 @@ module KeycloakAdmin
47
49
  add_read_token_role_on_create,
48
50
  authenticate_by_default,
49
51
  link_only,
52
+ organization_id,
50
53
  first_broker_login_flow_alias,
51
54
  config)
52
55
  @alias = alias_name
@@ -60,6 +63,7 @@ module KeycloakAdmin
60
63
  @add_read_token_role_on_create = add_read_token_role_on_create
61
64
  @authenticate_by_default = authenticate_by_default
62
65
  @link_only = link_only
66
+ @organization_id = organization_id
63
67
  @first_broker_login_flow_alias = first_broker_login_flow_alias
64
68
  @config = config || {}
65
69
  end
@@ -0,0 +1,11 @@
1
+ module KeycloakAdmin
2
+ class MemberRepresentation < UserRepresentation
3
+ attr_accessor :membership_type
4
+
5
+ def self.from_hash(hash)
6
+ member = super(hash)
7
+ member.membership_type = hash["membershipType"]
8
+ member
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module KeycloakAdmin
2
+ class OrganizationDomainRepresentation < Representation
3
+ attr_accessor :name, :verified
4
+
5
+ def initialize(name, verified)
6
+ @name = name
7
+ @verified = verified
8
+ end
9
+
10
+ def self.from_hash(hash)
11
+ new(hash["name"], hash["verified"])
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+
18
+
@@ -0,0 +1,30 @@
1
+ module KeycloakAdmin
2
+ class OrganizationRepresentation < Representation
3
+ attr_accessor :id,
4
+ :name,
5
+ :alias,
6
+ :enabled,
7
+ :description,
8
+ :redirect_url,
9
+ :attributes,
10
+ :domains,
11
+ :members,
12
+ :attributes,
13
+ :identity_providers
14
+
15
+ def self.from_hash(hash)
16
+ role = new
17
+ role.id = hash["id"]
18
+ role.name = hash["name"]
19
+ role.alias = hash["alias"]
20
+ role.enabled = hash["enabled"]
21
+ role.description = hash["description"]
22
+ role.redirect_url = hash["redirectUrl"]
23
+ role.attributes = hash["attributes"] || {}
24
+ role.domains = hash["domains"].nil? ? [] : hash["domains"].map { |domain| KeycloakAdmin::OrganizationDomainRepresentation.from_hash(domain) }
25
+ role.members = hash["members"].nil? ? [] : hash["members"].map { |member| KeycloakAdmin::MemberRepresentation.from_hash(member) }
26
+ role.identity_providers = hash["identityProviders"].nil? ? [] : hash["identityProviders"].map { |provider| KeycloakAdmin::IdentityProviderRepresentation.from_hash(provider) }
27
+ role
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module KeycloakAdmin
2
- VERSION = "1.1.5"
2
+ VERSION = "1.1.7"
3
3
  end
@@ -7,6 +7,7 @@ require_relative "keycloak-admin/client/client_role_client"
7
7
  require_relative "keycloak-admin/client/client_role_mappings_client"
8
8
  require_relative "keycloak-admin/client/group_client"
9
9
  require_relative "keycloak-admin/client/realm_client"
10
+ require_relative "keycloak-admin/client/organization_client"
10
11
  require_relative "keycloak-admin/client/role_client"
11
12
  require_relative "keycloak-admin/client/role_mapper_client"
12
13
  require_relative "keycloak-admin/client/token_client"
@@ -15,6 +16,8 @@ require_relative "keycloak-admin/client/identity_provider_client"
15
16
  require_relative "keycloak-admin/client/configurable_token_client"
16
17
  require_relative "keycloak-admin/client/attack_detection_client"
17
18
  require_relative "keycloak-admin/client/client_authz_scope_client"
19
+ require_relative "keycloak-admin/client/client_scope_client"
20
+ require_relative "keycloak-admin/client/client_scope_protocol_mapper_client"
18
21
  require_relative "keycloak-admin/client/client_authz_resource_client"
19
22
  require_relative "keycloak-admin/client/client_authz_policy_client"
20
23
  require_relative "keycloak-admin/client/client_authz_permission_client"
@@ -27,14 +30,18 @@ require_relative "keycloak-admin/representation/token_representation"
27
30
  require_relative "keycloak-admin/representation/impersonation_redirection_representation"
28
31
  require_relative "keycloak-admin/representation/impersonation_representation"
29
32
  require_relative "keycloak-admin/representation/credential_representation"
33
+ require_relative "keycloak-admin/representation/organization_domain_representation"
34
+ require_relative "keycloak-admin/representation/organization_representation"
30
35
  require_relative "keycloak-admin/representation/realm_representation"
31
36
  require_relative "keycloak-admin/representation/role_representation"
32
37
  require_relative "keycloak-admin/representation/federated_identity_representation"
33
38
  require_relative "keycloak-admin/representation/user_representation"
39
+ require_relative "keycloak-admin/representation/member_representation"
34
40
  require_relative "keycloak-admin/representation/identity_provider_mapper_representation"
35
41
  require_relative "keycloak-admin/representation/identity_provider_representation"
36
42
  require_relative "keycloak-admin/representation/attack_detection_representation"
37
43
  require_relative "keycloak-admin/representation/session_representation"
44
+ require_relative "keycloak-admin/representation/client_scope_representation"
38
45
  require_relative "keycloak-admin/representation/client_authz_scope_representation"
39
46
  require_relative "keycloak-admin/representation/client_authz_resource_representation"
40
47
  require_relative "keycloak-admin/representation/client_authz_policy_representation"
@@ -0,0 +1,220 @@
1
+ RSpec.describe KeycloakAdmin::ClientScopeClient do
2
+ let(:realm_name) { "valid-realm" }
3
+ let(:client_scope_id) { "valid-scope-id" }
4
+
5
+ let(:scope_json) do
6
+ <<~JSON
7
+ {"id":"valid-scope-id","name":"my-scope","description":"A test scope","protocol":"openid-connect","attributes":{"display.on.consent.screen":"true","include.in.token.scope":"true"}}
8
+ JSON
9
+ end
10
+
11
+ let(:scope_with_mappers_json) do
12
+ <<~JSON
13
+ {"id":"valid-scope-id","name":"my-scope","description":"A test scope","protocol":"openid-connect","attributes":{},"protocolMappers":[{"id":"mapper-id","name":"my-claim","protocol":"openid-connect","protocolMapper":"oidc-hardcoded-claim-mapper","config":{"claim.name":"my_claim","claim.value":"bar","access.token.claim":"true"}}]}
14
+ JSON
15
+ end
16
+
17
+ describe "#initialize" do
18
+ context "when realm_name is defined" do
19
+ it "does not raise any error" do
20
+ expect { KeycloakAdmin.realm(realm_name).client_scopes }.to_not raise_error
21
+ end
22
+ end
23
+
24
+ context "when realm_name is not defined" do
25
+ it "raises an argument error" do
26
+ expect { KeycloakAdmin.realm(nil).client_scopes }.to raise_error(ArgumentError)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#list" do
32
+ before(:each) do
33
+ @client = KeycloakAdmin.realm(realm_name).client_scopes
34
+ stub_token_client
35
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return stub_response
36
+ end
37
+
38
+ context "with one scope" do
39
+ let(:stub_response) { "[#{scope_json}]" }
40
+
41
+ it "returns one scope" do
42
+ expect(@client.list.size).to eq 1
43
+ end
44
+
45
+ it "returns the correct scope attributes" do
46
+ expect(@client.list.first).to have_attributes(
47
+ id: "valid-scope-id",
48
+ name: "my-scope",
49
+ description: "A test scope",
50
+ protocol: "openid-connect"
51
+ )
52
+ end
53
+
54
+ it "returns attributes map" do
55
+ expect(@client.list.first.attributes).to include(
56
+ "display.on.consent.screen" => "true",
57
+ "include.in.token.scope" => "true"
58
+ )
59
+ end
60
+ end
61
+
62
+ context "with multiple scopes" do
63
+ let(:second_scope_json) { '{"id":"other-scope-id","name":"other-scope","protocol":"openid-connect"}' }
64
+ let(:stub_response) { "[#{scope_json},#{second_scope_json}]" }
65
+
66
+ it "returns two scopes" do
67
+ expect(@client.list.size).to eq 2
68
+ end
69
+
70
+ it "includes both scope names" do
71
+ expect(@client.list.map(&:name)).to include("my-scope", "other-scope")
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#get" do
77
+ before(:each) do
78
+ @client = KeycloakAdmin.realm(realm_name).client_scopes
79
+ stub_token_client
80
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return stub_response
81
+ end
82
+
83
+ context "without protocol mappers" do
84
+ let(:stub_response) { scope_json }
85
+
86
+ it "returns the correct id" do
87
+ expect(@client.get(client_scope_id).id).to eq "valid-scope-id"
88
+ end
89
+
90
+ it "returns the correct name" do
91
+ expect(@client.get(client_scope_id).name).to eq "my-scope"
92
+ end
93
+
94
+ it "returns the correct description" do
95
+ expect(@client.get(client_scope_id).description).to eq "A test scope"
96
+ end
97
+
98
+ it "returns the correct protocol" do
99
+ expect(@client.get(client_scope_id).protocol).to eq "openid-connect"
100
+ end
101
+
102
+ it "returns an empty protocolMappers list" do
103
+ expect(@client.get(client_scope_id).protocol_mappers).to eq []
104
+ end
105
+ end
106
+
107
+ context "with protocol mappers" do
108
+ let(:stub_response) { scope_with_mappers_json }
109
+
110
+ it "returns protocol mappers" do
111
+ expect(@client.get(client_scope_id).protocol_mappers.size).to eq 1
112
+ end
113
+
114
+ it "returns the correct mapper name" do
115
+ expect(@client.get(client_scope_id).protocol_mappers.first.name).to eq "my-claim"
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#create!" do
121
+ before(:each) do
122
+ @client = KeycloakAdmin.realm(realm_name).client_scopes
123
+ stub_token_client
124
+ allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
125
+ end
126
+
127
+ let(:scope_representation) do
128
+ scope = KeycloakAdmin::ClientScopeRepresentation.new
129
+ scope.name = "my-scope"
130
+ scope.description = "A test scope"
131
+ scope.protocol = "openid-connect"
132
+ scope.attributes = { "display.on.consent.screen" => "true", "include.in.token.scope" => "true" }
133
+ scope
134
+ end
135
+
136
+ it "creates successfully" do
137
+ expect(@client.create!(scope_representation)).to be true
138
+ end
139
+ end
140
+
141
+ describe "#save" do
142
+ before(:each) do
143
+ @client = KeycloakAdmin.realm(realm_name).client_scopes
144
+ stub_token_client
145
+ allow_any_instance_of(RestClient::Resource).to receive(:put).and_return ""
146
+ end
147
+
148
+ let(:scope_representation) { KeycloakAdmin::ClientScopeRepresentation.from_hash(JSON.parse(scope_json)) }
149
+
150
+ it "calls put on the scope url" do
151
+ expect_any_instance_of(RestClient::Resource).to receive(:put).with(anything, anything)
152
+ @client.save(scope_representation)
153
+ end
154
+
155
+ it "returns true" do
156
+ expect(@client.save(scope_representation)).to be true
157
+ end
158
+ end
159
+
160
+ describe "#search" do
161
+ let(:second_scope_json) { '{"id":"other-scope-id","name":"other-scope","protocol":"openid-connect"}' }
162
+
163
+ before(:each) do
164
+ @client = KeycloakAdmin.realm(realm_name).client_scopes
165
+ stub_token_client
166
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return "[#{scope_json},#{second_scope_json}]"
167
+ end
168
+
169
+ context "when the name matches one scope" do
170
+ it "returns only the matching scope" do
171
+ expect(@client.search("my-scope").size).to eq 1
172
+ end
173
+
174
+ it "returns the correct scope" do
175
+ expect(@client.search("my-scope").first).to have_attributes(id: "valid-scope-id", name: "my-scope")
176
+ end
177
+ end
178
+
179
+ context "when the name is a partial match" do
180
+ it "returns all scopes containing the substring" do
181
+ expect(@client.search("scope").size).to eq 2
182
+ end
183
+ end
184
+
185
+ context "when no scope matches" do
186
+ it "returns an empty array" do
187
+ expect(@client.search("unknown")).to eq []
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "#delete" do
193
+ before(:each) do
194
+ @client = KeycloakAdmin.realm(realm_name).client_scopes
195
+ stub_token_client
196
+ allow_any_instance_of(RestClient::Resource).to receive(:delete).and_return ""
197
+ end
198
+
199
+ it "returns true" do
200
+ expect(@client.delete(client_scope_id)).to eq true
201
+ end
202
+ end
203
+
204
+ describe "#client_scopes_url" do
205
+ let(:client) { KeycloakAdmin.realm(realm_name).client_scopes }
206
+ let(:base_url) { "http://auth.service.io/auth/admin/realms/valid-realm/client-scopes" }
207
+
208
+ context "without a client_scope_id" do
209
+ it "returns the base url" do
210
+ expect(client.client_scopes_url).to eq base_url
211
+ end
212
+ end
213
+
214
+ context "with a client_scope_id" do
215
+ it "returns the url with client_scope_id appended" do
216
+ expect(client.client_scopes_url(client_scope_id)).to eq "#{base_url}/valid-scope-id"
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,230 @@
1
+ RSpec.describe KeycloakAdmin::ClientScopeProtocolMapperClient do
2
+ let(:realm_name) { "valid-realm" }
3
+ let(:client_scope_id) { "valid-scope-id" }
4
+ let(:mapper_id) { "valid-mapper-id" }
5
+
6
+ let(:mapper_json) do
7
+ <<~JSON
8
+ {"id":"valid-mapper-id","name":"my-claim","protocol":"openid-connect","protocolMapper":"oidc-hardcoded-claim-mapper","config":{"claim.name":"my_claim","claim.value":"bar","access.token.claim":"true"}}
9
+ JSON
10
+ end
11
+
12
+ let(:audience_mapper_json) do
13
+ <<~JSON
14
+ {"protocol":"openid-connect","protocolMapper":"oidc-audience-mapper","name":"audience-config-rvw-123","config":{"included.client.audience":"","included.custom.audience":"https://api.example.com","id.token.claim":"false","access.token.claim":"true","lightweight.claim":"false","introspection.token.claim":"true"}}
15
+ JSON
16
+ end
17
+
18
+ describe "#initialize" do
19
+ context "when realm_name is defined" do
20
+ it "does not raise any error" do
21
+ expect { KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id) }.to_not raise_error
22
+ end
23
+ end
24
+
25
+ context "when realm_name is not defined" do
26
+ it "raises an argument error" do
27
+ expect { KeycloakAdmin.realm(nil).client_scope_protocol_mappers(client_scope_id) }.to raise_error(ArgumentError)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#list" do
33
+ before(:each) do
34
+ @client = KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id)
35
+ stub_token_client
36
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return stub_response
37
+ end
38
+
39
+ context "with a hardcoded claim mapper" do
40
+ let(:stub_response) { "[#{mapper_json}]" }
41
+
42
+ it "returns one mapper" do
43
+ expect(@client.list.size).to eq 1
44
+ end
45
+
46
+ it "returns the correct mapper attributes" do
47
+ expect(@client.list.first).to have_attributes(id: "valid-mapper-id", name: "my-claim", protocol: "openid-connect", protocolMapper: "oidc-hardcoded-claim-mapper")
48
+ end
49
+ end
50
+
51
+ context "with an audience mapper" do
52
+ let(:stub_response) { "[#{audience_mapper_json}]" }
53
+
54
+ it "returns one mapper" do
55
+ expect(@client.list.size).to eq 1
56
+ end
57
+
58
+ it "returns the correct mapper attributes" do
59
+ expect(@client.list.first).to have_attributes(name: "audience-config-rvw-123", protocol: "openid-connect", protocolMapper: "oidc-audience-mapper")
60
+ end
61
+ end
62
+
63
+ context "with multiple mappers" do
64
+ let(:stub_response) { "[#{mapper_json},#{audience_mapper_json}]" }
65
+
66
+ it "returns two mappers" do
67
+ expect(@client.list.size).to eq 2
68
+ end
69
+
70
+ it "includes both mapper names" do
71
+ expect(@client.list.map(&:name)).to include("my-claim", "audience-config-rvw-123")
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#get" do
77
+ before(:each) do
78
+ @client = KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id)
79
+ stub_token_client
80
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return stub_response
81
+ end
82
+
83
+ context "with a hardcoded claim mapper" do
84
+ let(:stub_response) { mapper_json }
85
+
86
+ it "returns the correct id" do
87
+ expect(@client.get(mapper_id).id).to eq "valid-mapper-id"
88
+ end
89
+
90
+ it "returns the correct name" do
91
+ expect(@client.get(mapper_id).name).to eq "my-claim"
92
+ end
93
+
94
+ it "returns the correct protocol" do
95
+ expect(@client.get(mapper_id).protocol).to eq "openid-connect"
96
+ end
97
+
98
+ it "returns the correct protocolMapper" do
99
+ expect(@client.get(mapper_id).protocolMapper).to eq "oidc-hardcoded-claim-mapper"
100
+ end
101
+ end
102
+
103
+ context "with an audience mapper" do
104
+ let(:stub_response) { audience_mapper_json }
105
+
106
+ it "returns the correct name" do
107
+ expect(@client.get(mapper_id).name).to eq "audience-config-rvw-123"
108
+ end
109
+
110
+ it "returns the correct protocol" do
111
+ expect(@client.get(mapper_id).protocol).to eq "openid-connect"
112
+ end
113
+
114
+ it "returns the correct protocolMapper" do
115
+ expect(@client.get(mapper_id).protocolMapper).to eq "oidc-audience-mapper"
116
+ end
117
+
118
+ it "returns the correct config" do
119
+ expect(@client.get(mapper_id).config).to include(
120
+ "included.custom.audience" => "https://api.example.com",
121
+ "access.token.claim" => "true",
122
+ "introspection.token.claim" => "true",
123
+ "id.token.claim" => "false"
124
+ )
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "#create!" do
130
+ before(:each) do
131
+ @client = KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id)
132
+ stub_token_client
133
+ allow_any_instance_of(RestClient::Resource).to receive(:post).and_return stub_response
134
+ end
135
+
136
+ context "with a hardcoded claim mapper" do
137
+ let(:stub_response) { mapper_json }
138
+ let(:mapper_representation) do
139
+ mapper = KeycloakAdmin::ProtocolMapperRepresentation.new
140
+ mapper.name = "my-claim"
141
+ mapper.protocol = "openid-connect"
142
+ mapper.protocolMapper = "oidc-hardcoded-claim-mapper"
143
+ mapper.config = { "claim.name" => "my_claim", "claim.value" => "bar", "access.token.claim" => "true" }
144
+ mapper
145
+ end
146
+
147
+ it "creates successfully" do
148
+ expect(@client.create!(mapper_representation)).to be true
149
+ end
150
+ end
151
+
152
+ context "with an audience mapper" do
153
+ let(:stub_response) { audience_mapper_json }
154
+ let(:mapper_representation) do
155
+ mapper = KeycloakAdmin::ProtocolMapperRepresentation.new
156
+ mapper.name = "audience-config-rvw-123"
157
+ mapper.protocol = "openid-connect"
158
+ mapper.protocolMapper = "oidc-audience-mapper"
159
+ mapper.config = {
160
+ "included.client.audience" => "",
161
+ "included.custom.audience" => "https://api.example.com",
162
+ "id.token.claim" => "false",
163
+ "access.token.claim" => "true",
164
+ "lightweight.claim" => "false",
165
+ "introspection.token.claim" => "true"
166
+ }
167
+ mapper
168
+ end
169
+
170
+ it "creates successfully" do
171
+ expect(@client.create!(mapper_representation)).to be true
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "#save" do
177
+ before(:each) do
178
+ @client = KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id)
179
+ stub_token_client
180
+ allow_any_instance_of(RestClient::Resource).to receive(:put).and_return ""
181
+ end
182
+
183
+ context "with a hardcoded claim mapper" do
184
+ let(:mapper_representation) { KeycloakAdmin::ProtocolMapperRepresentation.from_hash(JSON.parse(mapper_json)) }
185
+
186
+ it "calls put on the mapper url" do
187
+ expect_any_instance_of(RestClient::Resource).to receive(:put).with(anything, anything)
188
+ @client.save(mapper_representation)
189
+ end
190
+ end
191
+
192
+ context "with an audience mapper" do
193
+ let(:mapper_representation) { KeycloakAdmin::ProtocolMapperRepresentation.from_hash(JSON.parse(audience_mapper_json)) }
194
+
195
+ it "calls put on the mapper url" do
196
+ expect_any_instance_of(RestClient::Resource).to receive(:put).with(anything, anything)
197
+ @client.save(mapper_representation)
198
+ end
199
+ end
200
+ end
201
+
202
+ describe "#delete" do
203
+ before(:each) do
204
+ @client = KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id)
205
+ stub_token_client
206
+ allow_any_instance_of(RestClient::Resource).to receive(:delete).and_return ""
207
+ end
208
+
209
+ it "returns true" do
210
+ expect(@client.delete(mapper_id)).to eq true
211
+ end
212
+ end
213
+
214
+ describe "#protocol_mappers_url" do
215
+ let(:client) { KeycloakAdmin.realm(realm_name).client_scope_protocol_mappers(client_scope_id) }
216
+ let(:base_url) { "http://auth.service.io/auth/admin/realms/valid-realm/client-scopes/valid-scope-id/protocol-mappers/models" }
217
+
218
+ context "without a mapper_id" do
219
+ it "returns the base url" do
220
+ expect(client.protocol_mappers_url).to eq base_url
221
+ end
222
+ end
223
+
224
+ context "with a mapper_id" do
225
+ it "returns the url with mapper_id appended" do
226
+ expect(client.protocol_mappers_url(mapper_id)).to eq "#{base_url}/valid-mapper-id"
227
+ end
228
+ end
229
+ end
230
+ end