workos 7.1.1 → 7.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -42,7 +42,7 @@ class AuditLogsTest < Minitest::Test
42
42
  def test_create_schema_returns_expected_result
43
43
  stub_request(:post, %r{\Ahttps://api\.workos\.com/audit_logs/actions/stub/schemas(\?|\z)})
44
44
  .to_return(body: "{}", status: 200)
45
- result = @client.audit_logs.create_schema(action_name: "stub", targets: [])
45
+ result = @client.audit_logs.create_schema(action_name: "stub", targets: [{}])
46
46
  refute_nil result
47
47
  end
48
48
 
@@ -73,7 +73,7 @@ class AuditLogsTest < Minitest::Test
73
73
  {name: :update_organization_audit_logs_retention, verb: :put, url: %r{\Ahttps://api\.workos\.com/organizations/stub/audit_logs_retention(\?|\z)}, args: {id: "stub", retention_period_in_days: 1}},
74
74
  {name: :list_actions, verb: :get, url: %r{\Ahttps://api\.workos\.com/audit_logs/actions(\?|\z)}},
75
75
  {name: :list_action_schemas, verb: :get, url: %r{\Ahttps://api\.workos\.com/audit_logs/actions/stub/schemas(\?|\z)}, args: {action_name: "stub"}},
76
- {name: :create_schema, verb: :post, url: %r{\Ahttps://api\.workos\.com/audit_logs/actions/stub/schemas(\?|\z)}, args: {action_name: "stub", targets: []}},
76
+ {name: :create_schema, verb: :post, url: %r{\Ahttps://api\.workos\.com/audit_logs/actions/stub/schemas(\?|\z)}, args: {action_name: "stub", targets: [{}]}},
77
77
  {name: :create_event, verb: :post, url: %r{\Ahttps://api\.workos\.com/audit_logs/events(\?|\z)}, args: {organization_id: "stub", event: {}}},
78
78
  {name: :create_export, verb: :post, url: %r{\Ahttps://api\.workos\.com/audit_logs/exports(\?|\z)}, args: {organization_id: "stub", range_start: "stub", range_end: "stub"}},
79
79
  {name: :get_export, verb: :get, url: %r{\Ahttps://api\.workos\.com/audit_logs/exports/stub(\?|\z)}, args: {audit_log_export_id: "stub"}}
@@ -13,15 +13,31 @@ class AuthorizationTest < Minitest::Test
13
13
 
14
14
  def test_check_returns_expected_result
15
15
  stub_request(:post, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/check(\?|\z)})
16
+ .with(body: hash_including("permission_slug" => "stub", "resource_id" => "stub"))
16
17
  .to_return(body: "{}", status: 200)
17
- result = @client.authorization.check(organization_membership_id: "stub", permission_slug: "stub", resource_target: {type: "by_id"})
18
+ result = @client.authorization.check(organization_membership_id: "stub", permission_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetById.new(resource_id: "stub"))
19
+ refute_nil result
20
+ end
21
+
22
+ def test_check_with_resource_target_by_external_id_returns_expected_result
23
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/check(\?|\z)})
24
+ .with(body: hash_including("permission_slug" => "stub", "resource_external_id" => "stub", "resource_type_slug" => "stub"))
25
+ .to_return(body: "{}", status: 200)
26
+ result = @client.authorization.check(organization_membership_id: "stub", permission_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetByExternalId.new(resource_external_id: "stub", resource_type_slug: "stub"))
18
27
  refute_nil result
19
28
  end
20
29
 
21
30
  def test_list_resources_for_membership_returns_expected_result
22
31
  stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/resources(\?|\z)})
23
32
  .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
24
- result = @client.authorization.list_resources_for_membership(organization_membership_id: "stub", permission_slug: "stub", parent_resource: {type: "by_id"})
33
+ result = @client.authorization.list_resources_for_membership(organization_membership_id: "stub", permission_slug: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub"))
34
+ assert_kind_of WorkOS::Types::ListStruct, result
35
+ end
36
+
37
+ def test_list_resources_for_membership_with_parent_resource_by_external_id_returns_expected_result
38
+ stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/resources(\?|\z)})
39
+ .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
40
+ result = @client.authorization.list_resources_for_membership(organization_membership_id: "stub", permission_slug: "stub", parent_resource: WorkOS::Authorization::ParentResourceByExternalId.new(parent_resource_type_slug: "stub", parent_resource_external_id: "stub"))
25
41
  assert_kind_of WorkOS::Types::ListStruct, result
26
42
  end
27
43
 
@@ -48,15 +64,31 @@ class AuthorizationTest < Minitest::Test
48
64
 
49
65
  def test_assign_role_returns_expected_result
50
66
  stub_request(:post, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)})
67
+ .with(body: hash_including("role_slug" => "stub", "resource_id" => "stub"))
51
68
  .to_return(body: "{}", status: 200)
52
- result = @client.authorization.assign_role(organization_membership_id: "stub", role_slug: "stub", resource_target: {type: "by_id"})
69
+ result = @client.authorization.assign_role(organization_membership_id: "stub", role_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetById.new(resource_id: "stub"))
70
+ refute_nil result
71
+ end
72
+
73
+ def test_assign_role_with_resource_target_by_external_id_returns_expected_result
74
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)})
75
+ .with(body: hash_including("role_slug" => "stub", "resource_external_id" => "stub", "resource_type_slug" => "stub"))
76
+ .to_return(body: "{}", status: 200)
77
+ result = @client.authorization.assign_role(organization_membership_id: "stub", role_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetByExternalId.new(resource_external_id: "stub", resource_type_slug: "stub"))
53
78
  refute_nil result
54
79
  end
55
80
 
56
81
  def test_remove_role_returns_expected_result
57
82
  stub_request(:delete, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)})
58
83
  .to_return(body: "{}", status: 200)
59
- result = @client.authorization.remove_role(organization_membership_id: "stub", role_slug: "stub", resource_target: {type: "by_id"})
84
+ result = @client.authorization.remove_role(organization_membership_id: "stub", role_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetById.new(resource_id: "stub"))
85
+ assert_nil result
86
+ end
87
+
88
+ def test_remove_role_with_resource_target_by_external_id_returns_expected_result
89
+ stub_request(:delete, %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)})
90
+ .to_return(body: "{}", status: 200)
91
+ result = @client.authorization.remove_role(organization_membership_id: "stub", role_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetByExternalId.new(resource_external_id: "stub", resource_type_slug: "stub"))
60
92
  assert_nil result
61
93
  end
62
94
 
@@ -112,7 +144,7 @@ class AuthorizationTest < Minitest::Test
112
144
  def test_set_organization_role_permissions_returns_expected_result
113
145
  stub_request(:put, %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub/permissions(\?|\z)})
114
146
  .to_return(body: "{}", status: 200)
115
- result = @client.authorization.set_organization_role_permissions(organization_id: "stub", slug: "stub", permissions: [])
147
+ result = @client.authorization.set_organization_role_permissions(organization_id: "stub", slug: "stub", permissions: ["stub"])
116
148
  refute_nil result
117
149
  end
118
150
 
@@ -132,8 +164,17 @@ class AuthorizationTest < Minitest::Test
132
164
 
133
165
  def test_update_resource_by_external_id_returns_expected_result
134
166
  stub_request(:patch, %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)})
167
+ .with(body: hash_including("parent_resource_id" => "stub"))
135
168
  .to_return(body: "{}", status: 200)
136
- result = @client.authorization.update_resource_by_external_id(organization_id: "stub", resource_type_slug: "stub", external_id: "stub")
169
+ result = @client.authorization.update_resource_by_external_id(organization_id: "stub", resource_type_slug: "stub", external_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub"))
170
+ refute_nil result
171
+ end
172
+
173
+ def test_update_resource_by_external_id_with_parent_resource_by_external_id_returns_expected_result
174
+ stub_request(:patch, %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)})
175
+ .with(body: hash_including("parent_resource_external_id" => "stub", "parent_resource_type_slug" => "stub"))
176
+ .to_return(body: "{}", status: 200)
177
+ result = @client.authorization.update_resource_by_external_id(organization_id: "stub", resource_type_slug: "stub", external_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceByExternalId.new(parent_resource_external_id: "stub", parent_resource_type_slug: "stub"))
137
178
  refute_nil result
138
179
  end
139
180
 
@@ -154,14 +195,30 @@ class AuthorizationTest < Minitest::Test
154
195
  def test_list_resources_returns_expected_result
155
196
  stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)})
156
197
  .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
157
- result = @client.authorization.list_resources
198
+ result = @client.authorization.list_resources(parent: WorkOS::Authorization::ParentById.new(parent_resource_id: "stub"))
199
+ assert_kind_of WorkOS::Types::ListStruct, result
200
+ end
201
+
202
+ def test_list_resources_with_parent_by_external_id_returns_expected_result
203
+ stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)})
204
+ .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
205
+ result = @client.authorization.list_resources(parent: WorkOS::Authorization::ParentByExternalId.new(parent_resource_type_slug: "stub", parent_external_id: "stub"))
158
206
  assert_kind_of WorkOS::Types::ListStruct, result
159
207
  end
160
208
 
161
209
  def test_create_resource_returns_expected_result
162
210
  stub_request(:post, %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)})
211
+ .with(body: hash_including("external_id" => "stub", "name" => "stub", "resource_type_slug" => "stub", "organization_id" => "stub", "parent_resource_id" => "stub"))
163
212
  .to_return(body: "{}", status: 200)
164
- result = @client.authorization.create_resource(external_id: "stub", name: "stub", resource_type_slug: "stub", organization_id: "stub")
213
+ result = @client.authorization.create_resource(external_id: "stub", name: "stub", resource_type_slug: "stub", organization_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub"))
214
+ refute_nil result
215
+ end
216
+
217
+ def test_create_resource_with_parent_resource_by_external_id_returns_expected_result
218
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)})
219
+ .with(body: hash_including("external_id" => "stub", "name" => "stub", "resource_type_slug" => "stub", "organization_id" => "stub", "parent_resource_external_id" => "stub", "parent_resource_type_slug" => "stub"))
220
+ .to_return(body: "{}", status: 200)
221
+ result = @client.authorization.create_resource(external_id: "stub", name: "stub", resource_type_slug: "stub", organization_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceByExternalId.new(parent_resource_external_id: "stub", parent_resource_type_slug: "stub"))
165
222
  refute_nil result
166
223
  end
167
224
 
@@ -174,8 +231,17 @@ class AuthorizationTest < Minitest::Test
174
231
 
175
232
  def test_update_resource_returns_expected_result
176
233
  stub_request(:patch, %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)})
234
+ .with(body: hash_including("parent_resource_id" => "stub"))
235
+ .to_return(body: "{}", status: 200)
236
+ result = @client.authorization.update_resource(resource_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub"))
237
+ refute_nil result
238
+ end
239
+
240
+ def test_update_resource_with_parent_resource_by_external_id_returns_expected_result
241
+ stub_request(:patch, %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)})
242
+ .with(body: hash_including("parent_resource_external_id" => "stub", "parent_resource_type_slug" => "stub"))
177
243
  .to_return(body: "{}", status: 200)
178
- result = @client.authorization.update_resource(resource_id: "stub")
244
+ result = @client.authorization.update_resource(resource_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceByExternalId.new(parent_resource_external_id: "stub", parent_resource_type_slug: "stub"))
179
245
  refute_nil result
180
246
  end
181
247
 
@@ -231,7 +297,7 @@ class AuthorizationTest < Minitest::Test
231
297
  def test_set_environment_role_permissions_returns_expected_result
232
298
  stub_request(:put, %r{\Ahttps://api\.workos\.com/authorization/roles/stub/permissions(\?|\z)})
233
299
  .to_return(body: "{}", status: 200)
234
- result = @client.authorization.set_environment_role_permissions(slug: "stub", permissions: [])
300
+ result = @client.authorization.set_environment_role_permissions(slug: "stub", permissions: ["stub"])
235
301
  refute_nil result
236
302
  end
237
303
 
@@ -272,13 +338,13 @@ class AuthorizationTest < Minitest::Test
272
338
 
273
339
  # Parameterized authentication error tests (one per endpoint).
274
340
  [
275
- {name: :check, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/check(\?|\z)}, args: {organization_membership_id: "stub", permission_slug: "stub", resource_target: {type: "by_id"}}},
276
- {name: :list_resources_for_membership, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/resources(\?|\z)}, args: {organization_membership_id: "stub", permission_slug: "stub", parent_resource: {type: "by_id"}}},
341
+ {name: :check, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/check(\?|\z)}, args: {organization_membership_id: "stub", permission_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetById.new(resource_id: "stub")}},
342
+ {name: :list_resources_for_membership, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/resources(\?|\z)}, args: {organization_membership_id: "stub", permission_slug: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
277
343
  {name: :list_effective_permissions, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/resources/stub/permissions(\?|\z)}, args: {organization_membership_id: "stub", resource_id: "stub"}},
278
344
  {name: :list_effective_permissions_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/resources/stub/stub/permissions(\?|\z)}, args: {organization_membership_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
279
345
  {name: :list_role_assignments, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)}, args: {organization_membership_id: "stub"}},
280
- {name: :assign_role, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)}, args: {organization_membership_id: "stub", role_slug: "stub", resource_target: {type: "by_id"}}},
281
- {name: :remove_role, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)}, args: {organization_membership_id: "stub", role_slug: "stub", resource_target: {type: "by_id"}}},
346
+ {name: :assign_role, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)}, args: {organization_membership_id: "stub", role_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetById.new(resource_id: "stub")}},
347
+ {name: :remove_role, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments(\?|\z)}, args: {organization_membership_id: "stub", role_slug: "stub", resource_target: WorkOS::Authorization::ResourceTargetById.new(resource_id: "stub")}},
282
348
  {name: :remove_role_assignment, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organization_memberships/stub/role_assignments/stub(\?|\z)}, args: {organization_membership_id: "stub", role_assignment_id: "stub"}},
283
349
  {name: :list_organization_roles, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles(\?|\z)}, args: {organization_id: "stub"}},
284
350
  {name: :create_organization_role, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles(\?|\z)}, args: {organization_id: "stub", name: "stub"}},
@@ -286,16 +352,16 @@ class AuthorizationTest < Minitest::Test
286
352
  {name: :update_organization_role, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub(\?|\z)}, args: {organization_id: "stub", slug: "stub"}},
287
353
  {name: :delete_organization_role, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub(\?|\z)}, args: {organization_id: "stub", slug: "stub"}},
288
354
  {name: :add_organization_role_permission, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub/permissions(\?|\z)}, args: {organization_id: "stub", slug: "stub", body_slug: "stub"}},
289
- {name: :set_organization_role_permissions, verb: :put, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub/permissions(\?|\z)}, args: {organization_id: "stub", slug: "stub", permissions: []}},
355
+ {name: :set_organization_role_permissions, verb: :put, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub/permissions(\?|\z)}, args: {organization_id: "stub", slug: "stub", permissions: ["stub"]}},
290
356
  {name: :remove_organization_role_permission, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/roles/stub/permissions/stub(\?|\z)}, args: {organization_id: "stub", slug: "stub", permission_slug: "stub"}},
291
357
  {name: :get_resource_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
292
- {name: :update_resource_by_external_id, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
358
+ {name: :update_resource_by_external_id, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
293
359
  {name: :delete_resource_by_external_id, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
294
360
  {name: :list_memberships_for_resource_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub/organization_memberships(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub", permission_slug: "stub"}},
295
- {name: :list_resources, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)}},
296
- {name: :create_resource, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)}, args: {external_id: "stub", name: "stub", resource_type_slug: "stub", organization_id: "stub"}},
361
+ {name: :list_resources, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)}, args: {parent: WorkOS::Authorization::ParentById.new(parent_resource_id: "stub")}},
362
+ {name: :create_resource, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)}, args: {external_id: "stub", name: "stub", resource_type_slug: "stub", organization_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
297
363
  {name: :get_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
298
- {name: :update_resource, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
364
+ {name: :update_resource, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
299
365
  {name: :delete_resource, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
300
366
  {name: :list_memberships_for_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub/organization_memberships(\?|\z)}, args: {resource_id: "stub", permission_slug: "stub"}},
301
367
  {name: :list_environment_roles, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)}},
@@ -303,7 +369,7 @@ class AuthorizationTest < Minitest::Test
303
369
  {name: :get_environment_role, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub(\?|\z)}, args: {slug: "stub"}},
304
370
  {name: :update_environment_role, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub(\?|\z)}, args: {slug: "stub"}},
305
371
  {name: :add_environment_role_permission, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub/permissions(\?|\z)}, args: {slug: "stub", body_slug: "stub"}},
306
- {name: :set_environment_role_permissions, verb: :put, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub/permissions(\?|\z)}, args: {slug: "stub", permissions: []}},
372
+ {name: :set_environment_role_permissions, verb: :put, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub/permissions(\?|\z)}, args: {slug: "stub", permissions: ["stub"]}},
307
373
  {name: :list_permissions, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/permissions(\?|\z)}},
308
374
  {name: :create_permission, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/permissions(\?|\z)}, args: {slug: "stub", name: "stub"}},
309
375
  {name: :get_permission, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/permissions/stub(\?|\z)}, args: {slug: "stub"}},
@@ -82,10 +82,36 @@ class BaseClientTest < Minitest::Test
82
82
  end
83
83
  end
84
84
 
85
+ # A pooled connection that either returns a canned response or raises when
86
+ # driven, so execute_request can be exercised without real TCP+TLS.
87
+ class StubConnection < FakeConnection
88
+ attr_accessor :read_timeout, :open_timeout
89
+
90
+ def initialize(response: nil, error: nil, **kwargs)
91
+ super(**kwargs)
92
+ @response = response
93
+ @error = error
94
+ end
95
+
96
+ def request(_request)
97
+ raise @error if @error
98
+
99
+ @response
100
+ end
101
+ end
102
+
85
103
  def setup
86
104
  @client = WorkOS::BaseClient.new(api_key: "sk_test_123", max_retries: 1)
87
105
  end
88
106
 
107
+ def teardown
108
+ super
109
+ # Close any open connections and clear the fiber-local cache to avoid
110
+ # leaking pooled connections between tests.
111
+ @client.shutdown
112
+ Fiber[:workos_connections] = nil
113
+ end
114
+
89
115
  def test_request_dispatches_known_methods
90
116
  client = RecordingClient.new(api_key: "sk_test_123")
91
117
 
@@ -170,4 +196,141 @@ class BaseClientTest < Minitest::Test
170
196
  assert evict.finished
171
197
  refute keep.finished
172
198
  end
199
+
200
+ # An exception execute_request's rescue clause doesn't list still leaves the
201
+ # socket mid-stream, so the connection must not survive in the pool for the
202
+ # next request on this thread to pick up.
203
+ def test_unlisted_request_error_evicts_the_pooled_connection
204
+ conn = StubConnection.new(error: Net::HTTPBadResponse.new("wrong version"))
205
+ cache = @client.send(:thread_connections)
206
+ cache["https:api.workos.com:443:30"] = conn
207
+
208
+ assert_raises(Net::HTTPBadResponse) do
209
+ @client.execute_request(request: Net::HTTP::Get.new("/things"))
210
+ end
211
+
212
+ refute cache.key?("https:api.workos.com:443:30"),
213
+ "a connection whose request did not complete must not stay pooled"
214
+ assert conn.finished
215
+ end
216
+
217
+ def test_completed_request_keeps_the_connection_pooled
218
+ conn = StubConnection.new(response: Net::HTTPOK.new("1.1", "200", "OK"))
219
+ cache = @client.send(:thread_connections)
220
+ cache["https:api.workos.com:443:30"] = conn
221
+
222
+ @client.execute_request(request: Net::HTTP::Get.new("/things"))
223
+
224
+ assert cache.key?("https:api.workos.com:443:30")
225
+ refute conn.finished
226
+ end
227
+
228
+ # Raised asynchronously into the worker thread to stand in for a caller-side
229
+ # abort: Timeout.timeout, Thread#kill, a signal handler unwinding the stack.
230
+ # It descends from Exception rather than StandardError so that nothing in
231
+ # execute_request can catch it — the `ensure` is the only cleanup that runs.
232
+ class AbortSignal < Exception; end # standard:disable Lint/InheritException
233
+
234
+ # End-to-end regression test over a real keep-alive socket, for the failure
235
+ # the eviction actually prevents: request one is abandoned mid-flight, the
236
+ # server then writes response one onto that socket, and request two on the
237
+ # same thread reads those stale bytes as if they were its own response.
238
+ #
239
+ # Before the fix, request two sees marker "one" (or a mangled response) off
240
+ # the pooled socket. After it, the abandoned socket is closed and the server
241
+ # accepts a fresh connection for request two.
242
+ def test_aborted_request_does_not_leak_its_response_to_the_next_request
243
+ WebMock.disable!
244
+ server = TCPServer.new("127.0.0.1", 0)
245
+ port = server.addr[1]
246
+ client = WorkOS::BaseClient.new(
247
+ api_key: "sk_test_123",
248
+ base_url: "http://127.0.0.1:#{port}",
249
+ timeout: 5,
250
+ max_retries: 0
251
+ )
252
+
253
+ request_one_read = Queue.new
254
+ release_response_one = Queue.new
255
+ response_one_written = Queue.new
256
+ aborted = Queue.new
257
+ connections = Queue.new
258
+
259
+ server_thread = Thread.new do
260
+ # Connection one: read the request, then hold the response back until
261
+ # the client has been aborted and has unwound.
262
+ first = server.accept
263
+ connections << first
264
+ read_http_request(first)
265
+ request_one_read << true
266
+ release_response_one.pop
267
+ begin
268
+ write_http_response(first, {marker: "one"})
269
+ rescue Errno::EPIPE, Errno::ECONNRESET, IOError
270
+ # Expected once the fix closes the abandoned socket.
271
+ end
272
+ response_one_written << true
273
+
274
+ # Connection two: a fresh accept, which only completes because the
275
+ # client did not reuse the socket above.
276
+ second = server.accept
277
+ connections << second
278
+ read_http_request(second)
279
+ write_http_response(second, {marker: "two"})
280
+ end
281
+
282
+ worker = Thread.new do
283
+ begin
284
+ client.execute_request(request: Net::HTTP::Get.new("/one"))
285
+ rescue AbortSignal
286
+ # The caller unwinds but the thread survives and goes on to serve more
287
+ # work, the way a Puma or Solid Queue worker does. execute_request's
288
+ # `ensure` has already run by the time this body executes.
289
+ aborted << true
290
+ response_one_written.pop
291
+ end
292
+ response = client.execute_request(request: Net::HTTP::Get.new("/two"))
293
+ JSON.parse(response.body)["marker"]
294
+ end
295
+
296
+ marker = Timeout.timeout(15) do
297
+ request_one_read.pop
298
+ worker.raise(AbortSignal)
299
+ aborted.pop
300
+ release_response_one << true
301
+ worker.value
302
+ end
303
+
304
+ assert_equal "two", marker,
305
+ "request two read the abandoned socket's response instead of its own"
306
+ ensure
307
+ server_thread&.kill
308
+ worker&.kill
309
+ until connections.nil? || connections.empty?
310
+ socket = connections.pop
311
+ socket.close unless socket.closed?
312
+ end
313
+ server&.close
314
+ WebMock.enable!
315
+ end
316
+
317
+ def read_http_request(socket)
318
+ socket.gets # request line
319
+ loop do
320
+ line = socket.gets
321
+ break if line.nil? || line == "\r\n"
322
+ end
323
+ end
324
+
325
+ def write_http_response(socket, body)
326
+ payload = JSON.generate(body)
327
+ socket.write(
328
+ "HTTP/1.1 200 OK\r\n" \
329
+ "Content-Type: application/json\r\n" \
330
+ "Content-Length: #{payload.bytesize}\r\n" \
331
+ "Connection: keep-alive\r\n" \
332
+ "\r\n#{payload}"
333
+ )
334
+ socket.flush
335
+ end
173
336
  end
@@ -3,6 +3,9 @@
3
3
  # @oagen-ignore-file
4
4
  require "test_helper"
5
5
  require "base64"
6
+ require "json"
7
+ require "openssl"
8
+ require "securerandom"
6
9
 
7
10
  class EncryptorsAesGcmTest < Minitest::Test
8
11
  PASSWORD = "test-cookie-password-at-least-32"
@@ -51,4 +54,22 @@ class EncryptorsAesGcmTest < Minitest::Test
51
54
  sealed2 = @enc.seal(data, PASSWORD)
52
55
  refute_equal sealed1, sealed2
53
56
  end
57
+
58
+ def test_unseal_reads_legacy_v6_payload
59
+ data = {"access_token" => "tok_abc", "refresh_token" => "ref_xyz"}
60
+ sealed = legacy_v6_seal(data, PASSWORD)
61
+ assert_equal data, @enc.unseal(sealed, PASSWORD)
62
+ end
63
+
64
+ private
65
+
66
+ def legacy_v6_seal(data, key)
67
+ cipher = OpenSSL::Cipher.new("aes-256-gcm").encrypt
68
+ iv = SecureRandom.random_bytes(12)
69
+ cipher.key = key
70
+ cipher.iv = iv
71
+ ciphertext = cipher.update(JSON.generate(data)) + cipher.final
72
+
73
+ Base64.encode64(iv + ciphertext + cipher.auth_tag)
74
+ end
54
75
  end
@@ -6,6 +6,7 @@ require "json"
6
6
  require "openssl"
7
7
  require "jwt"
8
8
  require "base64"
9
+ require "securerandom"
9
10
 
10
11
  class SessionTest < Minitest::Test
11
12
  PASSWORD = "very-long-cookie-password-secret"
@@ -84,6 +85,22 @@ class SessionTest < Minitest::Test
84
85
  assert_equal "u_1", result.user["id"]
85
86
  end
86
87
 
88
+ def test_authenticate_reads_legacy_v6_sealed_session
89
+ rsa, pub = signing_key_pair
90
+ access_token = make_jwt({"sid" => "session_v6", "org_id" => "org_legacy", "exp" => Time.now.to_i + 60}, rsa)
91
+ sealed = legacy_v6_seal({"access_token" => access_token, "user" => {"id" => "u_legacy"}}, PASSWORD)
92
+
93
+ stub_request(:get, "https://api.workos.com/sso/jwks/client_001")
94
+ .to_return(status: 200, body: jwks_payload(pub).to_json)
95
+
96
+ result = @sm.authenticate(seal_data: sealed, cookie_password: PASSWORD)
97
+ assert_kind_of WorkOS::SessionManager::AuthSuccess, result
98
+ assert result.authenticated
99
+ assert_equal "session_v6", result.session_id
100
+ assert_equal "org_legacy", result.organization_id
101
+ assert_equal "u_legacy", result.user["id"]
102
+ end
103
+
87
104
  def test_authenticate_merges_custom_claims_from_block
88
105
  rsa, pub = signing_key_pair
89
106
  access_token = make_jwt(
@@ -244,6 +261,45 @@ class SessionTest < Minitest::Test
244
261
  assert_equal "rt_new", unsealed["refresh_token"]
245
262
  end
246
263
 
264
+ def test_refresh_reads_legacy_v6_sealed_session
265
+ rsa, pub = signing_key_pair
266
+ old_access = make_jwt({"sid" => "session_old_v6", "exp" => Time.now.to_i - 60}, rsa)
267
+ sealed = legacy_v6_seal(
268
+ {"access_token" => old_access, "refresh_token" => "rt_old_v6", "user" => {"id" => "u_v6"}},
269
+ PASSWORD
270
+ )
271
+
272
+ new_access = make_jwt({"sid" => "session_new_v6", "org_id" => "org_v6", "role" => "member", "exp" => Time.now.to_i + 300}, rsa)
273
+ api_response = {
274
+ "access_token" => new_access,
275
+ "refresh_token" => "rt_new_v6",
276
+ "user" => {"id" => "u_v6", "email" => "legacy@example.com"},
277
+ "impersonator" => nil
278
+ }
279
+
280
+ stub_request(:post, "https://api.workos.com/user_management/authenticate")
281
+ .with(body: hash_including("grant_type" => "refresh_token", "refresh_token" => "rt_old_v6"))
282
+ .to_return(status: 200, body: api_response.to_json)
283
+ stub_request(:get, "https://api.workos.com/sso/jwks/client_001")
284
+ .to_return(status: 200, body: jwks_payload(pub).to_json)
285
+
286
+ session = @sm.load(seal_data: sealed, cookie_password: PASSWORD)
287
+ result = session.refresh
288
+
289
+ assert_kind_of WorkOS::SessionManager::RefreshSuccess, result
290
+ assert result.authenticated
291
+ assert_equal "session_new_v6", result.session_id
292
+ assert_equal "org_v6", result.organization_id
293
+ assert_equal "member", result.role
294
+ assert_equal "u_v6", result.user["id"]
295
+
296
+ refute_empty result.sealed_session
297
+ unsealed = @sm.unseal_data(result.sealed_session, PASSWORD)
298
+ assert_equal new_access, unsealed["access_token"]
299
+ assert_equal "rt_new_v6", unsealed["refresh_token"]
300
+ assert_equal "u_v6", unsealed["user"]["id"]
301
+ end
302
+
247
303
  def test_refresh_updates_internal_seal_data_for_subsequent_authenticate
248
304
  rsa, pub = signing_key_pair
249
305
  old_access = make_jwt({"sid" => "session_old", "exp" => Time.now.to_i - 60}, rsa)
@@ -383,4 +439,16 @@ class SessionTest < Minitest::Test
383
439
  assert_kind_of WorkOS::SessionManager::AuthSuccess, result
384
440
  assert_equal "s_custom", result.session_id
385
441
  end
442
+
443
+ private
444
+
445
+ def legacy_v6_seal(data, key)
446
+ cipher = OpenSSL::Cipher.new("aes-256-gcm").encrypt
447
+ iv = SecureRandom.random_bytes(12)
448
+ cipher.key = key
449
+ cipher.iv = iv
450
+ ciphertext = cipher.update(JSON.generate(data)) + cipher.final
451
+
452
+ Base64.encode64(iv + ciphertext + cipher.auth_tag)
453
+ end
386
454
  end
@@ -203,8 +203,17 @@ class UserManagementTest < Minitest::Test
203
203
 
204
204
  def test_create_user_returns_expected_result
205
205
  stub_request(:post, %r{\Ahttps://api\.workos\.com/user_management/users(\?|\z)})
206
+ .with(body: hash_including("email" => "stub", "password" => "stub"))
206
207
  .to_return(body: "{}", status: 200)
207
- result = @client.user_management.create_user(email: "stub")
208
+ result = @client.user_management.create_user(email: "stub", password: WorkOS::UserManagement::PasswordPlaintext.new(password: "stub"))
209
+ refute_nil result
210
+ end
211
+
212
+ def test_create_user_with_password_hashed_returns_expected_result
213
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/user_management/users(\?|\z)})
214
+ .with(body: hash_including("email" => "stub", "password_hash" => "stub", "password_hash_type" => "stub"))
215
+ .to_return(body: "{}", status: 200)
216
+ result = @client.user_management.create_user(email: "stub", password: WorkOS::UserManagement::PasswordHashed.new(password_hash: "stub", password_hash_type: "stub"))
208
217
  refute_nil result
209
218
  end
210
219
 
@@ -224,8 +233,17 @@ class UserManagementTest < Minitest::Test
224
233
 
225
234
  def test_update_user_returns_expected_result
226
235
  stub_request(:put, %r{\Ahttps://api\.workos\.com/user_management/users/stub(\?|\z)})
236
+ .with(body: hash_including("password" => "stub"))
237
+ .to_return(body: "{}", status: 200)
238
+ result = @client.user_management.update_user(id: "stub", password: WorkOS::UserManagement::PasswordPlaintext.new(password: "stub"))
239
+ refute_nil result
240
+ end
241
+
242
+ def test_update_user_with_password_hashed_returns_expected_result
243
+ stub_request(:put, %r{\Ahttps://api\.workos\.com/user_management/users/stub(\?|\z)})
244
+ .with(body: hash_including("password_hash" => "stub", "password_hash_type" => "stub"))
227
245
  .to_return(body: "{}", status: 200)
228
- result = @client.user_management.update_user(id: "stub")
246
+ result = @client.user_management.update_user(id: "stub", password: WorkOS::UserManagement::PasswordHashed.new(password_hash: "stub", password_hash_type: "stub"))
229
247
  refute_nil result
230
248
  end
231
249
 
@@ -357,8 +375,17 @@ class UserManagementTest < Minitest::Test
357
375
 
358
376
  def test_create_organization_membership_returns_expected_result
359
377
  stub_request(:post, %r{\Ahttps://api\.workos\.com/user_management/organization_memberships(\?|\z)})
378
+ .with(body: hash_including("user_id" => "stub", "organization_id" => "stub", "role_slug" => "stub"))
360
379
  .to_return(body: "{}", status: 200)
361
- result = @client.user_management.create_organization_membership(user_id: "stub", organization_id: "stub")
380
+ result = @client.user_management.create_organization_membership(user_id: "stub", organization_id: "stub", role: WorkOS::UserManagement::RoleSingle.new(role_slug: "stub"))
381
+ refute_nil result
382
+ end
383
+
384
+ def test_create_organization_membership_with_role_multiple_returns_expected_result
385
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/user_management/organization_memberships(\?|\z)})
386
+ .with(body: hash_including("user_id" => "stub", "organization_id" => "stub", "role_slugs" => ["stub"]))
387
+ .to_return(body: "{}", status: 200)
388
+ result = @client.user_management.create_organization_membership(user_id: "stub", organization_id: "stub", role: WorkOS::UserManagement::RoleMultiple.new(role_slugs: ["stub"]))
362
389
  refute_nil result
363
390
  end
364
391
 
@@ -371,8 +398,17 @@ class UserManagementTest < Minitest::Test
371
398
 
372
399
  def test_update_organization_membership_returns_expected_result
373
400
  stub_request(:put, %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub(\?|\z)})
401
+ .with(body: hash_including("role_slug" => "stub"))
402
+ .to_return(body: "{}", status: 200)
403
+ result = @client.user_management.update_organization_membership(id: "stub", role: WorkOS::UserManagement::RoleSingle.new(role_slug: "stub"))
404
+ refute_nil result
405
+ end
406
+
407
+ def test_update_organization_membership_with_role_multiple_returns_expected_result
408
+ stub_request(:put, %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub(\?|\z)})
409
+ .with(body: hash_including("role_slugs" => ["stub"]))
374
410
  .to_return(body: "{}", status: 200)
375
- result = @client.user_management.update_organization_membership(id: "stub")
411
+ result = @client.user_management.update_organization_membership(id: "stub", role: WorkOS::UserManagement::RoleMultiple.new(role_slugs: ["stub"]))
376
412
  refute_nil result
377
413
  end
378
414
 
@@ -430,10 +466,10 @@ class UserManagementTest < Minitest::Test
430
466
  {name: :confirm_password_reset, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/password_reset/confirm(\?|\z)}, args: {token: "stub", new_password: "stub"}},
431
467
  {name: :get_password_reset, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/password_reset/stub(\?|\z)}, args: {id: "stub"}},
432
468
  {name: :list_users, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/users(\?|\z)}},
433
- {name: :create_user, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/users(\?|\z)}, args: {email: "stub"}},
469
+ {name: :create_user, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/users(\?|\z)}, args: {email: "stub", password: WorkOS::UserManagement::PasswordPlaintext.new(password: "stub")}},
434
470
  {name: :get_user_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/users/external_id/stub(\?|\z)}, args: {external_id: "stub"}},
435
471
  {name: :get_user, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/users/stub(\?|\z)}, args: {id: "stub"}},
436
- {name: :update_user, verb: :put, url: %r{\Ahttps://api\.workos\.com/user_management/users/stub(\?|\z)}, args: {id: "stub"}},
472
+ {name: :update_user, verb: :put, url: %r{\Ahttps://api\.workos\.com/user_management/users/stub(\?|\z)}, args: {id: "stub", password: WorkOS::UserManagement::PasswordPlaintext.new(password: "stub")}},
437
473
  {name: :delete_user, verb: :delete, url: %r{\Ahttps://api\.workos\.com/user_management/users/stub(\?|\z)}, args: {id: "stub"}},
438
474
  {name: :confirm_email_change, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/users/stub/email_change/confirm(\?|\z)}, args: {id: "stub", code: "stub"}},
439
475
  {name: :send_email_change, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/users/stub/email_change/send(\?|\z)}, args: {id: "stub", new_email: "stub"}},
@@ -452,9 +488,9 @@ class UserManagementTest < Minitest::Test
452
488
  {name: :create_magic_auth, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/magic_auth(\?|\z)}, args: {email: "stub"}},
453
489
  {name: :get_magic_auth, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/magic_auth/stub(\?|\z)}, args: {id: "stub"}},
454
490
  {name: :list_organization_memberships, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships(\?|\z)}},
455
- {name: :create_organization_membership, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships(\?|\z)}, args: {user_id: "stub", organization_id: "stub"}},
491
+ {name: :create_organization_membership, verb: :post, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships(\?|\z)}, args: {user_id: "stub", organization_id: "stub", role: WorkOS::UserManagement::RoleSingle.new(role_slug: "stub")}},
456
492
  {name: :get_organization_membership, verb: :get, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub(\?|\z)}, args: {id: "stub"}},
457
- {name: :update_organization_membership, verb: :put, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub(\?|\z)}, args: {id: "stub"}},
493
+ {name: :update_organization_membership, verb: :put, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub(\?|\z)}, args: {id: "stub", role: WorkOS::UserManagement::RoleSingle.new(role_slug: "stub")}},
458
494
  {name: :delete_organization_membership, verb: :delete, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub(\?|\z)}, args: {id: "stub"}},
459
495
  {name: :deactivate_organization_membership, verb: :put, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub/deactivate(\?|\z)}, args: {id: "stub"}},
460
496
  {name: :reactivate_organization_membership, verb: :put, url: %r{\Ahttps://api\.workos\.com/user_management/organization_memberships/stub/reactivate(\?|\z)}, args: {id: "stub"}},