keycloak-admin 1.2.0 → 2.0.1

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -2
  3. data/.github/workflows/release.yml +112 -0
  4. data/CHANGELOG.md +14 -0
  5. data/Gemfile.lock +19 -20
  6. data/README.md +25 -5
  7. data/Rakefile +6 -0
  8. data/keycloak-admin.gemspec +5 -3
  9. data/lib/keycloak-admin/client/attack_detection_client.rb +3 -3
  10. data/lib/keycloak-admin/client/client.rb +18 -7
  11. data/lib/keycloak-admin/client/client_authz_permission_client.rb +7 -7
  12. data/lib/keycloak-admin/client/client_authz_policy_client.rb +5 -5
  13. data/lib/keycloak-admin/client/client_authz_resource_client.rb +6 -6
  14. data/lib/keycloak-admin/client/client_authz_scope_client.rb +5 -5
  15. data/lib/keycloak-admin/client/client_client.rb +6 -6
  16. data/lib/keycloak-admin/client/client_role_client.rb +1 -1
  17. data/lib/keycloak-admin/client/client_role_mappings_client.rb +2 -2
  18. data/lib/keycloak-admin/client/client_scope_client.rb +5 -5
  19. data/lib/keycloak-admin/client/client_scope_protocol_mapper_client.rb +5 -5
  20. data/lib/keycloak-admin/client/configurable_token_client.rb +4 -3
  21. data/lib/keycloak-admin/client/group_client.rb +15 -14
  22. data/lib/keycloak-admin/client/identity_provider_client.rb +4 -4
  23. data/lib/keycloak-admin/client/organization_client.rb +18 -18
  24. data/lib/keycloak-admin/client/realm_client.rb +4 -4
  25. data/lib/keycloak-admin/client/resource.rb +98 -0
  26. data/lib/keycloak-admin/client/response.rb +36 -0
  27. data/lib/keycloak-admin/client/role_client.rb +6 -6
  28. data/lib/keycloak-admin/client/role_mapper_client.rb +9 -8
  29. data/lib/keycloak-admin/client/token_client.rb +1 -3
  30. data/lib/keycloak-admin/client/user_client.rb +38 -31
  31. data/lib/keycloak-admin/configuration.rb +23 -1
  32. data/lib/keycloak-admin/version.rb +1 -1
  33. data/lib/keycloak-admin.rb +3 -1
  34. data/spec/client/attack_detection_client_spec.rb +3 -3
  35. data/spec/client/client_authz_permission_client_spec.rb +5 -5
  36. data/spec/client/client_authz_policy_client_spec.rb +5 -5
  37. data/spec/client/client_authz_resource_client_spec.rb +7 -7
  38. data/spec/client/client_authz_scope_client_spec.rb +5 -5
  39. data/spec/client/client_client_spec.rb +14 -14
  40. data/spec/client/client_role_mappings_client_spec.rb +11 -10
  41. data/spec/client/client_scope_client_spec.rb +7 -7
  42. data/spec/client/client_scope_protocol_mapper_client_spec.rb +7 -7
  43. data/spec/client/client_spec.rb +80 -7
  44. data/spec/client/group_client_spec.rb +47 -47
  45. data/spec/client/identity_provider_client_spec.rb +5 -5
  46. data/spec/client/organization_client_spec.rb +31 -31
  47. data/spec/client/realm_client_spec.rb +20 -20
  48. data/spec/client/resource_spec.rb +223 -0
  49. data/spec/client/response_spec.rb +68 -0
  50. data/spec/client/role_client_spec.rb +10 -10
  51. data/spec/client/role_mapper_client_spec.rb +10 -10
  52. data/spec/client/token_client_spec.rb +5 -5
  53. data/spec/client/user_client_spec.rb +29 -29
  54. data/spec/configuration_spec.rb +39 -2
  55. data/spec/gemspec_spec.rb +25 -0
  56. data/spec/integration/client_authorization_spec.rb +4 -4
  57. data/spec/representation/client_authz_policy_representation_spec.rb +1 -1
  58. data/spec/spec_helper.rb +8 -9
  59. metadata +42 -7
@@ -42,7 +42,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
42
42
  }
43
43
  ]
44
44
  payload
45
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
45
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
46
46
  end
47
47
 
48
48
  it "lists organizations" do
@@ -53,11 +53,11 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
53
53
  end
54
54
 
55
55
  it "passes rest client options" do
56
- rest_client_options = {timeout: 10}
57
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
56
+ faraday_options = {timeout: 10}
57
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
58
58
 
59
- expect(RestClient::Resource).to receive(:new).with(
60
- "http://auth.service.io/auth/admin/realms/valid-realm/organizations?briefRepresentation=true", rest_client_options).and_call_original
59
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
60
+ "http://auth.service.io/auth/admin/realms/valid-realm/organizations?briefRepresentation=true", faraday_options, anything).and_call_original
61
61
 
62
62
  organizations = @organization_client.list
63
63
  expect(organizations.length).to eq 1
@@ -74,7 +74,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
74
74
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
75
75
  stub_token_client
76
76
  json_payload = "2"
77
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
77
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
78
78
  end
79
79
 
80
80
  it "count organizations" do
@@ -163,7 +163,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
163
163
  }
164
164
  ]
165
165
  payload
166
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
166
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
167
167
  end
168
168
 
169
169
  it "list organizations of members organizations" do
@@ -180,7 +180,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
180
180
  before(:each) do
181
181
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
182
182
  stub_token_client
183
- allow_any_instance_of(RestClient::Resource).to receive(:delete).and_return ""
183
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:delete).and_return ""
184
184
  end
185
185
 
186
186
  it "deletes an organization" do
@@ -189,11 +189,11 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
189
189
  end
190
190
 
191
191
  it "raises a delete error" do
192
- rest_client_options = {timeout: 10}
193
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
192
+ faraday_options = {timeout: 10}
193
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
194
194
 
195
- expect(RestClient::Resource).to receive(:new).with(
196
- "http://auth.service.io/auth/admin/realms/valid-realm/organizations/2904e1a1-e5f4-4143-8725-003e54cc8b58", rest_client_options).and_raise("error")
195
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
196
+ "http://auth.service.io/auth/admin/realms/valid-realm/organizations/2904e1a1-e5f4-4143-8725-003e54cc8b58", faraday_options, anything).and_raise("error")
197
197
 
198
198
  expect { @organization_client.delete("2904e1a1-e5f4-4143-8725-003e54cc8b58") }.to raise_error("error")
199
199
  end
@@ -230,7 +230,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
230
230
  }
231
231
  ]
232
232
  payload
233
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
233
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
234
234
  end
235
235
 
236
236
  it "get identity providers" do
@@ -248,7 +248,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
248
248
  before(:each) do
249
249
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
250
250
  stub_token_client
251
- allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
251
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:post).and_return ""
252
252
  end
253
253
 
254
254
  it "adds one identity provider" do
@@ -285,7 +285,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
285
285
  }
286
286
  payload
287
287
  stub_token_client
288
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
288
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
289
289
  end
290
290
 
291
291
  it "get identity provider" do
@@ -320,7 +320,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
320
320
  ]
321
321
  }
322
322
  payload
323
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
323
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
324
324
  end
325
325
 
326
326
  it "get organization" do
@@ -332,11 +332,11 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
332
332
  end
333
333
 
334
334
  it "passes rest client options" do
335
- rest_client_options = {timeout: 10}
336
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
335
+ faraday_options = {timeout: 10}
336
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
337
337
 
338
- expect(RestClient::Resource).to receive(:new).with(
339
- "http://auth.service.io/auth/admin/realms/valid-realm/organizations/8f6e474e-e688-4bec-99ba-5dc862594f4b", rest_client_options).and_call_original
338
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
339
+ "http://auth.service.io/auth/admin/realms/valid-realm/organizations/8f6e474e-e688-4bec-99ba-5dc862594f4b", faraday_options, anything).and_call_original
340
340
 
341
341
  organization = @organization_client.get("8f6e474e-e688-4bec-99ba-5dc862594f4b")
342
342
  expect(organization).to be
@@ -351,7 +351,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
351
351
  before(:each) do
352
352
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
353
353
  stub_token_client
354
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return "2"
354
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return "2"
355
355
  end
356
356
 
357
357
  it "get count of members" do
@@ -399,7 +399,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
399
399
  ]
400
400
  payload
401
401
  stub_token_client
402
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
402
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
403
403
  end
404
404
 
405
405
  it "get members" do
@@ -420,7 +420,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
420
420
  before(:each) do
421
421
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
422
422
  stub_token_client
423
- allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
423
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:post).and_return ""
424
424
  end
425
425
 
426
426
  it "invites an existing user" do
@@ -434,7 +434,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
434
434
  before(:each) do
435
435
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
436
436
  stub_token_client
437
- allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
437
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:post).and_return ""
438
438
  end
439
439
 
440
440
  it "invites an existing user" do
@@ -448,7 +448,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
448
448
  before(:each) do
449
449
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
450
450
  stub_token_client
451
- allow_any_instance_of(RestClient::Resource).to receive(:delete).and_return ""
451
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:delete).and_return ""
452
452
  end
453
453
 
454
454
  it "deletes a member" do
@@ -478,7 +478,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
478
478
  }
479
479
  payload
480
480
  stub_token_client
481
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
481
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
482
482
  end
483
483
 
484
484
  it "gets a member" do
@@ -496,7 +496,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
496
496
  before(:each) do
497
497
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
498
498
  stub_token_client
499
- allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
499
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:post).and_return ""
500
500
  end
501
501
 
502
502
  it "creates a member from an existing user" do
@@ -511,7 +511,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
511
511
  before(:each) do
512
512
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
513
513
  stub_token_client
514
- allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
514
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:post).and_return ""
515
515
  end
516
516
 
517
517
  it "creates a member from an existing user" do
@@ -553,8 +553,8 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
553
553
  before(:each) do
554
554
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
555
555
  stub_token_client
556
- allow_any_instance_of(RestClient::Resource).to receive(:put).and_return ""
557
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return json_payload
556
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:put).and_return ""
557
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return json_payload
558
558
  end
559
559
 
560
560
  it "updates an organization" do
@@ -571,7 +571,7 @@ RSpec.describe KeycloakAdmin::OrganizationClient do
571
571
  before(:each) do
572
572
  @organization_client = KeycloakAdmin.realm(realm_name).organizations
573
573
  stub_token_client
574
- allow_any_instance_of(RestClient::Resource).to receive(:post).and_return ""
574
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:post).and_return ""
575
575
  end
576
576
 
577
577
  it "creates an organization" do
@@ -50,7 +50,7 @@ RSpec.describe KeycloakAdmin::RealmClient do
50
50
  @realm_client = KeycloakAdmin.realm('master')
51
51
 
52
52
  stub_token_client
53
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '[{"id":"test_realm","realm":"test_realm"}]'
53
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return '[{"id":"test_realm","realm":"test_realm"}]'
54
54
  end
55
55
 
56
56
  it "lists realms" do
@@ -60,11 +60,11 @@ RSpec.describe KeycloakAdmin::RealmClient do
60
60
  end
61
61
 
62
62
  it "passes rest client options" do
63
- rest_client_options = {timeout: 10}
64
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
63
+ faraday_options = {timeout: 10}
64
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
65
65
 
66
- expect(RestClient::Resource).to receive(:new).with(
67
- "http://auth.service.io/auth/admin/realms", rest_client_options).and_call_original
66
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
67
+ "http://auth.service.io/auth/admin/realms", faraday_options, anything).and_call_original
68
68
 
69
69
  realms = @realm_client.list
70
70
  expect(realms.length).to eq 1
@@ -79,7 +79,7 @@ RSpec.describe KeycloakAdmin::RealmClient do
79
79
  @realm_client = KeycloakAdmin.realm(realm_name)
80
80
 
81
81
  stub_token_client
82
- allow_any_instance_of(RestClient::Resource).to receive(:delete)
82
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:delete)
83
83
  end
84
84
 
85
85
  it "delete realm" do
@@ -87,11 +87,11 @@ RSpec.describe KeycloakAdmin::RealmClient do
87
87
  end
88
88
 
89
89
  it "passes rest client options" do
90
- rest_client_options = {timeout: 10}
91
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
90
+ faraday_options = {timeout: 10}
91
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
92
92
 
93
- expect(RestClient::Resource).to receive(:new).with(
94
- "http://auth.service.io/auth/admin/realms/valid-realm", rest_client_options).and_call_original
93
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
94
+ "http://auth.service.io/auth/admin/realms/valid-realm", faraday_options, anything).and_call_original
95
95
 
96
96
  expect(@realm_client.delete).to be_truthy
97
97
  end
@@ -109,7 +109,7 @@ RSpec.describe KeycloakAdmin::RealmClient do
109
109
 
110
110
  stub_token_client
111
111
 
112
- expect_any_instance_of(RestClient::Resource).to receive(:post).with(realm.to_json, anything)
112
+ expect_any_instance_of(KeycloakAdmin::Resource).to receive(:post).with(realm.to_json, anything)
113
113
  end
114
114
 
115
115
  it "saves a realm" do
@@ -117,11 +117,11 @@ RSpec.describe KeycloakAdmin::RealmClient do
117
117
  end
118
118
 
119
119
  it "passes rest client options" do
120
- rest_client_options = {timeout: 10}
121
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
120
+ faraday_options = {timeout: 10}
121
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
122
122
 
123
- expect(RestClient::Resource).to receive(:new).with(
124
- "http://auth.service.io/auth/admin/realms", rest_client_options).and_call_original
123
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
124
+ "http://auth.service.io/auth/admin/realms", faraday_options, anything).and_call_original
125
125
 
126
126
  @realm_client.save(realm)
127
127
  end
@@ -135,7 +135,7 @@ RSpec.describe KeycloakAdmin::RealmClient do
135
135
  @realm_client = KeycloakAdmin.realm(realm_name)
136
136
 
137
137
  stub_token_client
138
- expect_any_instance_of(RestClient::Resource).to receive(:put).with(realm_json.to_json, anything)
138
+ expect_any_instance_of(KeycloakAdmin::Resource).to receive(:put).with(realm_json.to_json, anything)
139
139
  end
140
140
 
141
141
  it "updates a realm" do
@@ -143,11 +143,11 @@ RSpec.describe KeycloakAdmin::RealmClient do
143
143
  end
144
144
 
145
145
  it "passes rest client options" do
146
- rest_client_options = {timeout: 10}
147
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
146
+ faraday_options = {timeout: 10}
147
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
148
148
 
149
- expect(RestClient::Resource).to receive(:new).with(
150
- "http://auth.service.io/auth/admin/realms/valid-realm", rest_client_options).and_call_original
149
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
150
+ "http://auth.service.io/auth/admin/realms/valid-realm", faraday_options, anything).and_call_original
151
151
 
152
152
  @realm_client.update(realm_json)
153
153
  end
@@ -0,0 +1,223 @@
1
+ RSpec.describe KeycloakAdmin::Resource do
2
+ # A real, mutable stand-in for the Faraday::Request builder yielded by
3
+ # connection.get/post/put/delete { |req| ... }.
4
+ FakeRequest = Struct.new(:params, :headers) do
5
+ attr_accessor :body
6
+ end
7
+
8
+ let(:fake_request) { FakeRequest.new({}, {}) }
9
+ let(:fake_response) { double(status: 200, body: "ok", headers: {}, reason_phrase: "OK") }
10
+
11
+ def stub_connection(resource, verb)
12
+ connection = double
13
+ allow(connection).to receive(verb) do |&block|
14
+ block.call(fake_request) if block
15
+ fake_response
16
+ end
17
+ allow(resource).to receive(:connection).and_return(connection)
18
+ connection
19
+ end
20
+
21
+ describe "#connection (the real Faraday connection, not stubbed)" do
22
+ def middleware_classes(resource)
23
+ resource.send(:connection).builder.handlers
24
+ end
25
+
26
+ it "does not register the logger middleware when no logger is configured" do
27
+ resource = KeycloakAdmin::Resource.new("http://example.com")
28
+ expect(middleware_classes(resource)).to_not include(Faraday::Response::Logger)
29
+ end
30
+
31
+ it "registers the logger middleware when a logger is configured" do
32
+ resource = KeycloakAdmin::Resource.new("http://example.com", {}, Logger.new(IO::NULL))
33
+ expect(middleware_classes(resource)).to include(Faraday::Response::Logger)
34
+ end
35
+
36
+ it "orders :raise_error before :logger, so a response is logged even when it raises" do
37
+ resource = KeycloakAdmin::Resource.new("http://example.com", {}, Logger.new(IO::NULL))
38
+ handlers = middleware_classes(resource)
39
+ expect(handlers.index(Faraday::Response::RaiseError)).to be < handlers.index(Faraday::Response::Logger)
40
+ end
41
+
42
+ it "logs method/url/status but never headers, since Faraday logs headers by default and one of them is the bearer token" do
43
+ log_output = StringIO.new
44
+ logger = Logger.new(log_output)
45
+ stubs = Faraday::Adapter::Test::Stubs.new
46
+ stubs.get(//) { [200, {}, "ok"] }
47
+
48
+ resource = KeycloakAdmin::Resource.new("http://example.com/x", {}, logger)
49
+ allow(resource).to receive(:connection) do
50
+ Faraday.new(url: "http://example.com/x") do |f|
51
+ f.response :raise_error
52
+ f.response :logger, logger, headers: false
53
+ f.adapter :test, stubs
54
+ end
55
+ end
56
+
57
+ resource.get(Authorization: "Bearer super-secret-token")
58
+
59
+ expect(log_output.string).to include("request: GET")
60
+ expect(log_output.string).to include("response: Status 200")
61
+ expect(log_output.string).to_not include("super-secret-token")
62
+ expect(log_output.string).to_not include("Authorization")
63
+ end
64
+ end
65
+
66
+ describe "#get" do
67
+ it "humanizes symbol header keys into HTTP header names" do
68
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
69
+ stub_connection(resource, :get)
70
+
71
+ resource.get(Authorization: "Bearer token", content_type: :json, accept: :json)
72
+
73
+ expect(fake_request.headers).to eq(
74
+ "Authorization" => "Bearer token",
75
+ "Content-Type" => "application/json",
76
+ "Accept" => "application/json"
77
+ )
78
+ end
79
+
80
+ it "extracts :params into the query string instead of sending it as a header" do
81
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
82
+ stub_connection(resource, :get)
83
+
84
+ resource.get(Authorization: "Bearer token", params: { search: "jean" })
85
+
86
+ expect(fake_request.params).to eq(search: "jean")
87
+ expect(fake_request.headers).to eq("Authorization" => "Bearer token")
88
+ end
89
+
90
+ it "passes a string content_type through unchanged" do
91
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
92
+ stub_connection(resource, :get)
93
+
94
+ resource.get(content_type: "application/x-www-form-urlencoded")
95
+
96
+ expect(fake_request.headers["Content-Type"]).to eq "application/x-www-form-urlencoded"
97
+ end
98
+
99
+ it "does not set a body" do
100
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
101
+ stub_connection(resource, :get)
102
+
103
+ resource.get({})
104
+
105
+ expect(fake_request.body).to be_nil
106
+ end
107
+
108
+ it "wraps the Faraday response in a KeycloakAdmin::Response" do
109
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
110
+ stub_connection(resource, :get)
111
+
112
+ expect(resource.get({})).to be_a KeycloakAdmin::Response
113
+ end
114
+ end
115
+
116
+ describe "#post" do
117
+ it "sends a pre-serialized string payload as the body unchanged" do
118
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
119
+ stub_connection(resource, :post)
120
+
121
+ resource.post('{"username":"jean"}', content_type: :json)
122
+
123
+ expect(fake_request.body).to eq '{"username":"jean"}'
124
+ expect(fake_request.headers["Content-Type"]).to eq "application/json"
125
+ end
126
+
127
+ it "form-encodes a Hash payload" do
128
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
129
+ stub_connection(resource, :post)
130
+
131
+ resource.post({ email: "a@b.com", firstName: "Jean Yves" }, {})
132
+
133
+ expect(fake_request.body).to eq "email=a%40b.com&firstName=Jean+Yves"
134
+ end
135
+
136
+ it "stamps Content-Type: application/x-www-form-urlencoded for a Hash payload when none was set" do
137
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
138
+ stub_connection(resource, :post)
139
+
140
+ resource.post({ id: "42" }, {})
141
+
142
+ expect(fake_request.headers["Content-Type"]).to eq "application/x-www-form-urlencoded"
143
+ end
144
+
145
+ it "does not override an explicit Content-Type for a Hash payload" do
146
+ resource = KeycloakAdmin::Resource.new("http://example.com/users")
147
+ stub_connection(resource, :post)
148
+
149
+ resource.post({ id: "42" }, content_type: "application/vnd.custom+form")
150
+
151
+ expect(fake_request.headers["Content-Type"]).to eq "application/vnd.custom+form"
152
+ end
153
+ end
154
+
155
+ describe "#delete" do
156
+ it "sends no body and only the given headers" do
157
+ resource = KeycloakAdmin::Resource.new("http://example.com/users/1")
158
+ stub_connection(resource, :delete)
159
+
160
+ resource.delete(Authorization: "Bearer token")
161
+
162
+ expect(fake_request.body).to be_nil
163
+ expect(fake_request.headers).to eq("Authorization" => "Bearer token")
164
+ end
165
+ end
166
+
167
+ describe ".execute (RestClient::Request.execute equivalent)" do
168
+ it "dispatches an arbitrary method with a payload, including DELETE with a body" do
169
+ resource_instance = KeycloakAdmin::Resource.new("http://example.com/mappings")
170
+ allow(KeycloakAdmin::Resource).to receive(:new).and_return(resource_instance)
171
+ stub_connection(resource_instance, :delete)
172
+
173
+ KeycloakAdmin::Resource.execute(
174
+ method: :delete,
175
+ url: "http://example.com/mappings",
176
+ payload: '[{"id":"1"}]',
177
+ headers: { content_type: :json }
178
+ )
179
+
180
+ expect(fake_request.body).to eq '[{"id":"1"}]'
181
+ expect(fake_request.headers["Content-Type"]).to eq "application/json"
182
+ end
183
+
184
+ it "forwards non-method/url/payload/headers keys as connection options" do
185
+ resource_instance = KeycloakAdmin::Resource.new("http://example.com/x")
186
+ expected_options = { timeout: 5 }
187
+ stub_connection(resource_instance, :get)
188
+ expect(KeycloakAdmin::Resource).to receive(:new).with("http://example.com/x", expected_options, nil).and_return(resource_instance)
189
+
190
+ KeycloakAdmin::Resource.execute(method: :get, url: "http://example.com/x", headers: {}, timeout: 5)
191
+ end
192
+
193
+ it "extracts :logger and passes it positionally instead of leaving it in connection options" do
194
+ resource_instance = KeycloakAdmin::Resource.new("http://example.com/x")
195
+ logger = Logger.new(IO::NULL)
196
+ stub_connection(resource_instance, :get)
197
+ expect(KeycloakAdmin::Resource).to receive(:new).with("http://example.com/x", {}, logger).and_return(resource_instance)
198
+
199
+ KeycloakAdmin::Resource.execute(method: :get, url: "http://example.com/x", headers: {}, logger: logger)
200
+ end
201
+ end
202
+
203
+ describe ".put (RestClient.put shorthand)" do
204
+ it "posts without requiring any connection options" do
205
+ resource_instance = KeycloakAdmin::Resource.new("http://example.com/x")
206
+ allow(KeycloakAdmin::Resource).to receive(:new).and_return(resource_instance)
207
+ stub_connection(resource_instance, :put)
208
+
209
+ KeycloakAdmin::Resource.put("http://example.com/x", "body", {})
210
+
211
+ expect(fake_request.body).to eq "body"
212
+ end
213
+
214
+ it "forwards the given logger to the underlying Resource" do
215
+ resource_instance = KeycloakAdmin::Resource.new("http://example.com/x")
216
+ logger = Logger.new(IO::NULL)
217
+ stub_connection(resource_instance, :put)
218
+ expect(KeycloakAdmin::Resource).to receive(:new).with("http://example.com/x", {}, logger).and_return(resource_instance)
219
+
220
+ KeycloakAdmin::Resource.put("http://example.com/x", "body", {}, logger)
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,68 @@
1
+ RSpec.describe KeycloakAdmin::Response do
2
+ def fake_faraday_response(status: 200, body: "", headers: {}, reason_phrase: "OK")
3
+ double(status: status, body: body, headers: headers, reason_phrase: reason_phrase)
4
+ end
5
+
6
+ describe "body-as-string behavior (RestClient::Response was a String subclass)" do
7
+ it "behaves like the raw body string" do
8
+ response = KeycloakAdmin::Response.new(fake_faraday_response(body: '{"a":1}'))
9
+ expect(response).to eq '{"a":1}'
10
+ expect(JSON.parse(response)).to eq({ "a" => 1 })
11
+ end
12
+
13
+ it "supports String methods relied on by callers, e.g. #to_i" do
14
+ response = KeycloakAdmin::Response.new(fake_faraday_response(body: "42"))
15
+ expect(response.to_i).to eq 42
16
+ end
17
+
18
+ it "exposes #body returning the same content" do
19
+ response = KeycloakAdmin::Response.new(fake_faraday_response(body: "hello"))
20
+ expect(response.body).to eq "hello"
21
+ end
22
+ end
23
+
24
+ describe "#status / #code / #reason_phrase" do
25
+ it "exposes the HTTP status" do
26
+ response = KeycloakAdmin::Response.new(fake_faraday_response(status: 201))
27
+ expect(response.status).to eq 201
28
+ expect(response.code).to eq 201
29
+ end
30
+
31
+ it "exposes the reason phrase" do
32
+ response = KeycloakAdmin::Response.new(fake_faraday_response(reason_phrase: "Created"))
33
+ expect(response.reason_phrase).to eq "Created"
34
+ end
35
+ end
36
+
37
+ describe "#headers" do
38
+ it "symbolizes header names the way RestClient did (downcased, dash to underscore)" do
39
+ response = KeycloakAdmin::Response.new(fake_faraday_response(headers: { "Location" => "/users/42", "Content-Type" => "application/json" }))
40
+ expect(response.headers[:location]).to eq "/users/42"
41
+ expect(response.headers[:content_type]).to eq "application/json"
42
+ end
43
+
44
+ it "splits a single Set-Cookie header into a one-element array" do
45
+ response = KeycloakAdmin::Response.new(fake_faraday_response(headers: { "Set-Cookie" => "a=1; Path=/" }))
46
+ expect(response.headers[:set_cookie]).to eq ["a=1; Path=/"]
47
+ end
48
+
49
+ it "splits multiple Set-Cookie headers joined by Faraday's net_http adapter" do
50
+ joined = "KEYCLOAK_SESSION=abc; Path=/, KEYCLOAK_IDENTITY=def; Path=/, AUTH_SESSION_ID=ghi; Path=/"
51
+ response = KeycloakAdmin::Response.new(fake_faraday_response(headers: { "Set-Cookie" => joined }))
52
+ expect(response.headers[:set_cookie]).to eq [
53
+ "KEYCLOAK_SESSION=abc; Path=/",
54
+ "KEYCLOAK_IDENTITY=def; Path=/",
55
+ "AUTH_SESSION_ID=ghi; Path=/"
56
+ ]
57
+ end
58
+
59
+ it "does not split on a comma embedded in a cookie's own Expires attribute" do
60
+ joined = "a=1; Expires=Wed, 09 Jun 2027 10:18:14 GMT; Path=/, b=2; Path=/"
61
+ response = KeycloakAdmin::Response.new(fake_faraday_response(headers: { "Set-Cookie" => joined }))
62
+ expect(response.headers[:set_cookie]).to eq [
63
+ "a=1; Expires=Wed, 09 Jun 2027 10:18:14 GMT; Path=/",
64
+ "b=2; Path=/"
65
+ ]
66
+ end
67
+ end
68
+ end
@@ -25,7 +25,7 @@ RSpec.describe KeycloakAdmin::RoleClient do
25
25
  @role_client = KeycloakAdmin.realm(realm_name).roles
26
26
 
27
27
  stub_token_client
28
- allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '[{"id":"test_role_id","name":"test_role_name"}]'
28
+ allow_any_instance_of(KeycloakAdmin::Resource).to receive(:get).and_return '[{"id":"test_role_id","name":"test_role_name"}]'
29
29
  end
30
30
 
31
31
  it "lists roles" do
@@ -35,11 +35,11 @@ RSpec.describe KeycloakAdmin::RoleClient do
35
35
  end
36
36
 
37
37
  it "passes rest client options" do
38
- rest_client_options = {timeout: 10}
39
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
38
+ faraday_options = {timeout: 10}
39
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
40
40
 
41
- expect(RestClient::Resource).to receive(:new).with(
42
- "http://auth.service.io/auth/admin/realms/valid-realm/roles", rest_client_options).and_call_original
41
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
42
+ "http://auth.service.io/auth/admin/realms/valid-realm/roles", faraday_options, anything).and_call_original
43
43
 
44
44
  roles = @role_client.list
45
45
  expect(roles.length).to eq 1
@@ -59,7 +59,7 @@ RSpec.describe KeycloakAdmin::RoleClient do
59
59
  @role_client = KeycloakAdmin.realm(realm_name).roles
60
60
 
61
61
  stub_token_client
62
- expect_any_instance_of(RestClient::Resource).to receive(:post).with(role.to_json, anything)
62
+ expect_any_instance_of(KeycloakAdmin::Resource).to receive(:post).with(role.to_json, anything)
63
63
  end
64
64
 
65
65
  it "saves a role" do
@@ -67,11 +67,11 @@ RSpec.describe KeycloakAdmin::RoleClient do
67
67
  end
68
68
 
69
69
  it "passes rest client options" do
70
- rest_client_options = {timeout: 10}
71
- allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
70
+ faraday_options = {timeout: 10}
71
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:faraday_options).and_return faraday_options
72
72
 
73
- expect(RestClient::Resource).to receive(:new).with(
74
- "http://auth.service.io/auth/admin/realms/valid-realm/roles", rest_client_options).and_call_original
73
+ expect(KeycloakAdmin::Resource).to receive(:new).with(
74
+ "http://auth.service.io/auth/admin/realms/valid-realm/roles", faraday_options, anything).and_call_original
75
75
 
76
76
  @role_client.save(role)
77
77
  end