workos 7.1.2 → 8.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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +49 -0
  3. data/.github/workflows/release-please.yml +2 -2
  4. data/.gitignore +2 -0
  5. data/.last-synced-sha +1 -1
  6. data/.oagen-manifest.json +61 -40
  7. data/.release-please-manifest.json +1 -1
  8. data/.yardopts +6 -0
  9. data/CHANGELOG.md +36 -0
  10. data/Gemfile +6 -0
  11. data/Gemfile.lock +33 -2
  12. data/README.md +19 -0
  13. data/docs/V7_MIGRATION_GUIDE.md +21 -0
  14. data/lib/workos/actions.rb +1 -1
  15. data/lib/workos/api_keys/api_key.rb +1 -1
  16. data/lib/workos/api_keys/api_key_created_data.rb +1 -1
  17. data/lib/workos/api_keys/organization_api_key.rb +43 -0
  18. data/lib/workos/{types/events_order.rb → api_keys/organization_api_key_owner.rb} +1 -3
  19. data/lib/workos/api_keys/organization_api_key_with_value.rb +46 -0
  20. data/lib/workos/{types/audit_logs_order.rb → api_keys/organization_api_key_with_value_owner.rb} +1 -3
  21. data/lib/workos/api_keys.rb +46 -46
  22. data/lib/workos/audit_logs.rb +4 -4
  23. data/lib/workos/authorization/user_organization_membership_base_list_data.rb +5 -2
  24. data/lib/workos/authorization/{role_assignment.rb → user_role_assignment.rb} +5 -2
  25. data/lib/workos/authorization/{role_assignment_resource.rb → user_role_assignment_resource.rb} +1 -1
  26. data/lib/workos/authorization.rb +122 -22
  27. data/lib/workos/base_client.rb +71 -5
  28. data/lib/workos/client.rb +4 -4
  29. data/lib/workos/connect.rb +2 -2
  30. data/lib/workos/directory_sync/directory_user.rb +3 -0
  31. data/lib/workos/directory_sync/directory_user_with_groups.rb +4 -1
  32. data/lib/workos/directory_sync/dsync_user_updated_data.rb +3 -0
  33. data/lib/workos/directory_sync.rb +6 -6
  34. data/lib/workos/encryptors/aes_gcm.rb +19 -5
  35. data/lib/workos/events.rb +2 -2
  36. data/lib/workos/feature_flags.rb +6 -6
  37. data/lib/workos/groups.rb +4 -4
  38. data/lib/workos/multi_factor_auth.rb +2 -2
  39. data/lib/workos/organizations.rb +2 -2
  40. data/lib/workos/session.rb +28 -7
  41. data/lib/workos/session_manager.rb +24 -1
  42. data/lib/workos/sso/profile.rb +3 -0
  43. data/lib/workos/sso.rb +2 -2
  44. data/lib/workos/types/event_context_actor_source.rb +2 -1
  45. data/lib/workos/types/{applications_order.rb → pagination_order.rb} +1 -1
  46. data/lib/workos/types/{vault_byok_key_verification_completed_data_key_provider.rb → vault_byok_key_provider.rb} +1 -1
  47. data/lib/workos/user_management/create_user_api_key.rb +25 -0
  48. data/lib/workos/user_management/organization_membership.rb +5 -2
  49. data/lib/workos/user_management/user_api_key.rb +43 -0
  50. data/lib/workos/user_management/user_api_key_created_data_owner.rb +25 -0
  51. data/lib/workos/{api_keys/api_key_with_value_owner.rb → user_management/user_api_key_owner.rb} +1 -1
  52. data/lib/workos/{types/webhooks_order.rb → user_management/user_api_key_revoked_data_owner.rb} +1 -3
  53. data/lib/workos/{api_keys/api_key_with_value.rb → user_management/user_api_key_with_value.rb} +2 -2
  54. data/lib/workos/{types/groups_order.rb → user_management/user_api_key_with_value_owner.rb} +1 -3
  55. data/lib/workos/user_management/user_organization_membership.rb +5 -2
  56. data/lib/workos/user_management.rb +114 -10
  57. data/lib/workos/user_management_organization_membership_groups.rb +2 -2
  58. data/lib/workos/vault/vault_byok_key_deleted.rb +34 -0
  59. data/lib/workos/vault/vault_byok_key_deleted_data.rb +22 -0
  60. data/lib/workos/version.rb +1 -1
  61. data/lib/workos/webhooks.rb +3 -3
  62. data/rbi/workos/api_key.rbi +2 -2
  63. data/rbi/workos/api_key_created_data.rbi +2 -2
  64. data/rbi/workos/api_key_revoked_data.rbi +2 -2
  65. data/rbi/workos/api_keys.rbi +17 -17
  66. data/rbi/workos/authorization.rbi +27 -1
  67. data/rbi/workos/client.rbi +3 -3
  68. data/rbi/workos/create_user_api_key.rbi +36 -0
  69. data/rbi/workos/directory_user.rbi +6 -0
  70. data/rbi/workos/directory_user_with_groups.rbi +6 -0
  71. data/rbi/workos/dsync_user_updated_data.rbi +6 -0
  72. data/rbi/workos/organization_api_key.rbi +72 -0
  73. data/rbi/workos/{api_key_with_value_owner.rbi → organization_api_key_owner.rbi} +1 -1
  74. data/rbi/workos/organization_api_key_with_value.rbi +78 -0
  75. data/rbi/workos/organization_api_key_with_value_owner.rbi +30 -0
  76. data/rbi/workos/organization_membership.rbi +6 -0
  77. data/rbi/workos/profile.rbi +6 -0
  78. data/rbi/workos/user_api_key.rbi +72 -0
  79. data/rbi/workos/user_api_key_created_data_owner.rbi +36 -0
  80. data/rbi/workos/user_api_key_owner.rbi +36 -0
  81. data/rbi/workos/user_api_key_revoked_data_owner.rbi +36 -0
  82. data/rbi/workos/{api_key_with_value.rbi → user_api_key_with_value.rbi} +3 -3
  83. data/rbi/workos/user_api_key_with_value_owner.rbi +36 -0
  84. data/rbi/workos/user_management.rbi +31 -0
  85. data/rbi/workos/user_organization_membership.rbi +6 -0
  86. data/rbi/workos/user_organization_membership_base_list_data.rbi +6 -0
  87. data/rbi/workos/{role_assignment.rbi → user_role_assignment.rbi} +9 -3
  88. data/rbi/workos/{role_assignment_resource.rbi → user_role_assignment_resource.rbi} +1 -1
  89. data/rbi/workos/vault_byok_key_deleted.rbi +54 -0
  90. data/rbi/workos/vault_byok_key_deleted_data.rbi +30 -0
  91. data/script/docs +16 -0
  92. data/script/docs-serve +12 -0
  93. data/script/llms-txt +37 -0
  94. data/test/workos/test_actions.rb +9 -0
  95. data/test/workos/test_api_keys.rb +17 -17
  96. data/test/workos/test_authorization.rb +16 -0
  97. data/test/workos/test_base_client.rb +44 -0
  98. data/test/workos/test_encryptors_aes_gcm.rb +16 -1
  99. data/test/workos/test_model_round_trip.rb +278 -83
  100. data/test/workos/test_session.rb +43 -4
  101. data/test/workos/test_user_management.rb +25 -1
  102. data/test/workos/test_webhook_verify.rb +11 -0
  103. metadata +39 -33
  104. data/lib/workos/types/authorization_order.rb +0 -9
  105. data/lib/workos/types/connections_order.rb +0 -9
  106. data/lib/workos/types/directories_order.rb +0 -9
  107. data/lib/workos/types/directory_groups_order.rb +0 -9
  108. data/lib/workos/types/directory_users_order.rb +0 -9
  109. data/lib/workos/types/feature_flags_order.rb +0 -9
  110. data/lib/workos/types/organizations_api_keys_order.rb +0 -9
  111. data/lib/workos/types/organizations_feature_flags_order.rb +0 -9
  112. data/lib/workos/types/organizations_order.rb +0 -9
  113. data/lib/workos/types/permissions_order.rb +0 -9
  114. data/lib/workos/types/user_management_invitations_order.rb +0 -9
  115. data/lib/workos/types/user_management_multi_factor_authentication_order.rb +0 -9
  116. data/lib/workos/types/user_management_organization_membership_groups_order.rb +0 -9
  117. data/lib/workos/types/user_management_organization_membership_order.rb +0 -9
  118. data/lib/workos/types/user_management_users_authorized_applications_order.rb +0 -9
  119. data/lib/workos/types/user_management_users_feature_flags_order.rb +0 -9
  120. data/lib/workos/types/user_management_users_order.rb +0 -9
@@ -333,6 +333,13 @@ module WorkOS
333
333
  end
334
334
  def revoke_invitation(id:, request_options:); end
335
335
 
336
+ sig do
337
+ params(
338
+ request_options: T::Hash[Symbol, T.untyped]
339
+ ).returns(WorkOS::JWTTemplateResponse)
340
+ end
341
+ def list_jwt_template(request_options:); end
342
+
336
343
  sig do
337
344
  params(
338
345
  content: String,
@@ -452,5 +459,29 @@ module WorkOS
452
459
  end
453
460
  def delete_user_authorized_application(application_id:, user_id:, request_options:); end
454
461
 
462
+ sig do
463
+ params(
464
+ user_id: String,
465
+ before: T.nilable(String),
466
+ after: T.nilable(String),
467
+ limit: T.nilable(Integer),
468
+ order: T.nilable(String),
469
+ organization_id: T.nilable(String),
470
+ request_options: T::Hash[Symbol, T.untyped]
471
+ ).returns(WorkOS::Types::ListStruct)
472
+ end
473
+ def list_user_api_keys(user_id:, before:, after:, limit:, order:, organization_id:, request_options:); end
474
+
475
+ sig do
476
+ params(
477
+ user_id: String,
478
+ name: String,
479
+ organization_id: String,
480
+ permissions: T.nilable(T::Array[String]),
481
+ request_options: T::Hash[Symbol, T.untyped]
482
+ ).returns(WorkOS::UserApiKeyWithValue)
483
+ end
484
+ def create_user_api_key(user_id:, name:, organization_id:, permissions:, request_options:); end
485
+
455
486
  end
456
487
  end
@@ -75,6 +75,12 @@ module WorkOS
75
75
  sig { params(value: WorkOS::SlimRole).returns(WorkOS::SlimRole) }
76
76
  def role=(value); end
77
77
 
78
+ sig { returns(WorkOS::User) }
79
+ def user; end
80
+
81
+ sig { params(value: WorkOS::User).returns(WorkOS::User) }
82
+ def user=(value); end
83
+
78
84
  sig { returns(T::Hash[Symbol, T.untyped]) }
79
85
  def to_h; end
80
86
 
@@ -69,6 +69,12 @@ module WorkOS
69
69
  sig { params(value: String).returns(String) }
70
70
  def updated_at=(value); end
71
71
 
72
+ sig { returns(WorkOS::User) }
73
+ def user; end
74
+
75
+ sig { params(value: WorkOS::User).returns(WorkOS::User) }
76
+ def user=(value); end
77
+
72
78
  sig { returns(T::Hash[Symbol, T.untyped]) }
73
79
  def to_h; end
74
80
 
@@ -5,7 +5,7 @@
5
5
  # typed: strong
6
6
 
7
7
  module WorkOS
8
- class RoleAssignment
8
+ class UserRoleAssignment
9
9
  sig { params(json: T.any(String, T::Hash[Symbol, T.untyped])).void }
10
10
  def initialize(json); end
11
11
 
@@ -21,16 +21,22 @@ module WorkOS
21
21
  sig { params(value: String).returns(String) }
22
22
  def id=(value); end
23
23
 
24
+ sig { returns(String) }
25
+ def organization_membership_id; end
26
+
27
+ sig { params(value: String).returns(String) }
28
+ def organization_membership_id=(value); end
29
+
24
30
  sig { returns(WorkOS::SlimRole) }
25
31
  def role; end
26
32
 
27
33
  sig { params(value: WorkOS::SlimRole).returns(WorkOS::SlimRole) }
28
34
  def role=(value); end
29
35
 
30
- sig { returns(WorkOS::RoleAssignmentResource) }
36
+ sig { returns(WorkOS::UserRoleAssignmentResource) }
31
37
  def resource; end
32
38
 
33
- sig { params(value: WorkOS::RoleAssignmentResource).returns(WorkOS::RoleAssignmentResource) }
39
+ sig { params(value: WorkOS::UserRoleAssignmentResource).returns(WorkOS::UserRoleAssignmentResource) }
34
40
  def resource=(value); end
35
41
 
36
42
  sig { returns(String) }
@@ -5,7 +5,7 @@
5
5
  # typed: strong
6
6
 
7
7
  module WorkOS
8
- class RoleAssignmentResource
8
+ class UserRoleAssignmentResource
9
9
  sig { params(json: T.any(String, T::Hash[Symbol, T.untyped])).void }
10
10
  def initialize(json); end
11
11
 
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is auto-generated by oagen. Do not edit.
4
+
5
+ # typed: strong
6
+
7
+ module WorkOS
8
+ class VaultByokKeyDeleted
9
+ sig { params(json: T.any(String, T::Hash[Symbol, T.untyped])).void }
10
+ def initialize(json); end
11
+
12
+ sig { returns(String) }
13
+ def id; end
14
+
15
+ sig { params(value: String).returns(String) }
16
+ def id=(value); end
17
+
18
+ sig { returns(String) }
19
+ def event; end
20
+
21
+ sig { params(value: String).returns(String) }
22
+ def event=(value); end
23
+
24
+ sig { returns(WorkOS::VaultByokKeyDeletedData) }
25
+ def data; end
26
+
27
+ sig { params(value: WorkOS::VaultByokKeyDeletedData).returns(WorkOS::VaultByokKeyDeletedData) }
28
+ def data=(value); end
29
+
30
+ sig { returns(String) }
31
+ def created_at; end
32
+
33
+ sig { params(value: String).returns(String) }
34
+ def created_at=(value); end
35
+
36
+ sig { returns(T.nilable(WorkOS::EventContext)) }
37
+ def context; end
38
+
39
+ sig { params(value: T.nilable(WorkOS::EventContext)).returns(T.nilable(WorkOS::EventContext)) }
40
+ def context=(value); end
41
+
42
+ sig { returns(String) }
43
+ def object; end
44
+
45
+ sig { params(value: String).returns(String) }
46
+ def object=(value); end
47
+
48
+ sig { returns(T::Hash[Symbol, T.untyped]) }
49
+ def to_h; end
50
+
51
+ sig { params(args: T.untyped).returns(String) }
52
+ def to_json(*args); end
53
+ end
54
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is auto-generated by oagen. Do not edit.
4
+
5
+ # typed: strong
6
+
7
+ module WorkOS
8
+ class VaultByokKeyDeletedData
9
+ sig { params(json: T.any(String, T::Hash[Symbol, T.untyped])).void }
10
+ def initialize(json); end
11
+
12
+ sig { returns(String) }
13
+ def organization_id; end
14
+
15
+ sig { params(value: String).returns(String) }
16
+ def organization_id=(value); end
17
+
18
+ sig { returns(String) }
19
+ def key_provider; end
20
+
21
+ sig { params(value: String).returns(String) }
22
+ def key_provider=(value); end
23
+
24
+ sig { returns(T::Hash[Symbol, T.untyped]) }
25
+ def to_h; end
26
+
27
+ sig { params(args: T.untyped).returns(String) }
28
+ def to_json(*args); end
29
+ end
30
+ end
data/script/docs ADDED
@@ -0,0 +1,16 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ cd "$(dirname "$0")/.."
4
+ bundle config set --local with 'docs'
5
+ bundle install
6
+ rm -rf docs/_site
7
+ # 1. Generate HTML docs (default YARD format).
8
+ bundle exec yard doc
9
+ # 2. Generate Markdown docs alongside the HTML, using the yard-markdown plugin.
10
+ # We point output at the same docs/_site directory so .md files land next to .html.
11
+ # --no-cache forces a re-parse so the markdown formatter sees fresh registry data.
12
+ # --plugin yard-markdown ensures the plugin is loaded without relying on ~/.yard/config.
13
+ bundle exec yard doc --plugin yard-markdown --format markdown --no-cache --output-dir docs/_site
14
+ # 3. Generate llms.txt (index) from the markdown output, so the published site
15
+ # is consumable by LLM tooling.
16
+ bundle exec ./script/llms-txt
data/script/docs-serve ADDED
@@ -0,0 +1,12 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ cd "$(dirname "$0")/.."
4
+ bundle config set --local with 'docs'
5
+ bundle install --quiet
6
+ if [ "${1:-}" = "--static" ]; then
7
+ if [ ! -d docs/_site ]; then
8
+ ./script/docs
9
+ fi
10
+ exec bundle exec ruby -run -e httpd -- docs/_site -p 4000
11
+ fi
12
+ exec bundle exec yard server --reload --port 4000
data/script/llms-txt ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Generates llms.txt (curated index) from the Markdown docs produced by
5
+ # `yard --format markdown` in docs/_site. See https://llmstxt.org for the format.
6
+
7
+ require "pathname"
8
+
9
+ ROOT = Pathname.new(__dir__).join("..").expand_path
10
+ SITE = ROOT.join("docs/_site")
11
+ require ROOT.join("lib/workos/version").to_s
12
+
13
+ abort "docs/_site is missing — run script/docs first" unless SITE.directory?
14
+
15
+ md_files = Pathname.glob(SITE.join("**/*.md")).sort_by { |p| p.relative_path_from(SITE).to_s }
16
+
17
+ title_for = lambda do |path|
18
+ first_heading = path.each_line.lazy.map(&:strip).find { |l| l.start_with?("# ") }
19
+ first_heading ? first_heading.sub(/^#\s+/, "").sub(/\s*<a id=.*?<\/a>\s*$/, "").strip : path.basename(".md").to_s
20
+ end
21
+
22
+ index_lines = []
23
+ index_lines << "# WorkOS Ruby SDK v#{WorkOS::VERSION}"
24
+ index_lines << ""
25
+ index_lines << "> API client for WorkOS. This file indexes the auto-generated YARD documentation for the `workos` gem so language models can locate per-class references."
26
+ index_lines << ""
27
+ index_lines << "## API Reference"
28
+ index_lines << ""
29
+ md_files.each do |path|
30
+ rel = path.relative_path_from(SITE).to_s
31
+ index_lines << "- [#{title_for.call(path)}](#{rel})"
32
+ end
33
+ index_lines << ""
34
+
35
+ SITE.join("llms.txt").write(index_lines.join("\n"))
36
+
37
+ puts "Wrote #{SITE.join("llms.txt")} (#{md_files.size} entries)"
@@ -49,6 +49,15 @@ class ActionsTest < Minitest::Test
49
49
  end
50
50
  end
51
51
 
52
+ def test_verify_header_raises_on_future_timestamp
53
+ payload = '{"x":1}'
54
+ future_ts = now_ms + 60_000 # 60s ahead, beyond default 30s tolerance
55
+ sig = OpenSSL::HMAC.hexdigest("SHA256", SECRET, "#{future_ts}.#{payload}")
56
+ assert_raises(WorkOS::SignatureVerificationError) do
57
+ @actions.verify_header(payload: payload, sig_header: "t=#{future_ts}, v1=#{sig}", secret: SECRET)
58
+ end
59
+ end
60
+
52
61
  def test_sign_response_authentication_allow
53
62
  resp = @actions.sign_response(action_type: "authentication", verdict: "Allow", secret: SECRET)
54
63
  assert_equal "authentication_action_response", resp["object"]
@@ -11,20 +11,6 @@ class ApiKeysTest < Minitest::Test
11
11
  @client = WorkOS::Client.new(api_key: "sk_test_123")
12
12
  end
13
13
 
14
- def test_create_validation_returns_expected_result
15
- stub_request(:post, %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)})
16
- .to_return(body: "{}", status: 200)
17
- result = @client.api_keys.create_validation(value: "stub")
18
- refute_nil result
19
- end
20
-
21
- def test_delete_api_key_returns_expected_result
22
- stub_request(:delete, %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)})
23
- .to_return(body: "{}", status: 200)
24
- result = @client.api_keys.delete_api_key(id: "stub")
25
- assert_nil result
26
- end
27
-
28
14
  def test_list_organization_api_keys_returns_expected_result
29
15
  stub_request(:get, %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)})
30
16
  .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
@@ -39,12 +25,26 @@ class ApiKeysTest < Minitest::Test
39
25
  refute_nil result
40
26
  end
41
27
 
28
+ def test_create_validation_returns_expected_result
29
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)})
30
+ .to_return(body: "{}", status: 200)
31
+ result = @client.api_keys.create_validation(value: "stub")
32
+ refute_nil result
33
+ end
34
+
35
+ def test_delete_api_key_returns_expected_result
36
+ stub_request(:delete, %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)})
37
+ .to_return(body: "{}", status: 200)
38
+ result = @client.api_keys.delete_api_key(id: "stub")
39
+ assert_nil result
40
+ end
41
+
42
42
  # Parameterized authentication error tests (one per endpoint).
43
43
  [
44
- {name: :create_validation, verb: :post, url: %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)}, args: {value: "stub"}},
45
- {name: :delete_api_key, verb: :delete, url: %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)}, args: {id: "stub"}},
46
44
  {name: :list_organization_api_keys, verb: :get, url: %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)}, args: {organization_id: "stub"}},
47
- {name: :create_organization_api_key, verb: :post, url: %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)}, args: {organization_id: "stub", name: "stub"}}
45
+ {name: :create_organization_api_key, verb: :post, url: %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)}, args: {organization_id: "stub", name: "stub"}},
46
+ {name: :create_validation, verb: :post, url: %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)}, args: {value: "stub"}},
47
+ {name: :delete_api_key, verb: :delete, url: %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)}, args: {id: "stub"}}
48
48
  ].each do |spec|
49
49
  define_method("test_#{spec[:name]}_raises_authentication_error_on_401") do
50
50
  stub_request(spec[:verb], spec[:url])
@@ -192,6 +192,13 @@ class AuthorizationTest < Minitest::Test
192
192
  assert_kind_of WorkOS::Types::ListStruct, result
193
193
  end
194
194
 
195
+ def test_list_role_assignments_for_resource_by_external_id_returns_expected_result
196
+ stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub/role_assignments(\?|\z)})
197
+ .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
198
+ result = @client.authorization.list_role_assignments_for_resource_by_external_id(organization_id: "stub", resource_type_slug: "stub", external_id: "stub")
199
+ assert_kind_of WorkOS::Types::ListStruct, result
200
+ end
201
+
195
202
  def test_list_resources_returns_expected_result
196
203
  stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)})
197
204
  .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
@@ -259,6 +266,13 @@ class AuthorizationTest < Minitest::Test
259
266
  assert_kind_of WorkOS::Types::ListStruct, result
260
267
  end
261
268
 
269
+ def test_list_role_assignments_for_resource_returns_expected_result
270
+ stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/resources/stub/role_assignments(\?|\z)})
271
+ .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
272
+ result = @client.authorization.list_role_assignments_for_resource(resource_id: "stub")
273
+ assert_kind_of WorkOS::Types::ListStruct, result
274
+ end
275
+
262
276
  def test_list_environment_roles_returns_expected_result
263
277
  stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)})
264
278
  .to_return(body: "{}", status: 200)
@@ -358,12 +372,14 @@ class AuthorizationTest < Minitest::Test
358
372
  {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")}},
359
373
  {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"}},
360
374
  {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"}},
375
+ {name: :list_role_assignments_for_resource_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub/role_assignments(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
361
376
  {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
377
  {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")}},
363
378
  {name: :get_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
364
379
  {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")}},
365
380
  {name: :delete_resource, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
366
381
  {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"}},
382
+ {name: :list_role_assignments_for_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub/role_assignments(\?|\z)}, args: {resource_id: "stub"}},
367
383
  {name: :list_environment_roles, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)}},
368
384
  {name: :create_environment_role, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)}, args: {slug: "stub", name: "stub"}},
369
385
  {name: :get_environment_role, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub(\?|\z)}, args: {slug: "stub"}},
@@ -170,4 +170,48 @@ class BaseClientTest < Minitest::Test
170
170
  assert evict.finished
171
171
  refute keep.finished
172
172
  end
173
+
174
+ def test_redact_path_strips_invitation_token_segment
175
+ redacted = @client.send(:redact_path, "/user_management/invitations/by_token/invtoken_secret123")
176
+ assert_equal "/user_management/invitations/by_token/[REDACTED]", redacted
177
+ end
178
+
179
+ def test_redact_path_strips_magic_auth_token_segment
180
+ redacted = @client.send(:redact_path, "/user_management/magic_auth/magic_secret/extra")
181
+ assert_equal "/user_management/magic_auth/[REDACTED]/[REDACTED]", redacted
182
+ end
183
+
184
+ def test_redact_path_preserves_non_token_paths
185
+ assert_equal "/organizations/org_123", @client.send(:redact_path, "/organizations/org_123")
186
+ end
187
+
188
+ def test_redact_path_preserves_query_string
189
+ redacted = @client.send(:redact_path, "/user_management/invitations/by_token/secret?foo=bar")
190
+ assert_equal "/user_management/invitations/by_token/[REDACTED]?foo=bar", redacted
191
+ end
192
+
193
+ def test_redact_path_handles_nil_and_empty
194
+ assert_nil @client.send(:redact_path, nil)
195
+ assert_equal "", @client.send(:redact_path, "")
196
+ end
197
+
198
+ def test_redact_path_scrubs_sensitive_query_params
199
+ redacted = @client.send(:redact_path, "/user_management/sessions/logout?session_id=ses_abc123&return_to=https://app.example.com")
200
+ assert_equal "/user_management/sessions/logout?session_id=[REDACTED]&return_to=https://app.example.com", redacted
201
+ end
202
+
203
+ def test_redact_path_scrubs_authorize_code_query_param
204
+ redacted = @client.send(:redact_path, "/user_management/authorize?client_id=client_1&code=auth_code_secret&state=xyz")
205
+ assert_equal "/user_management/authorize?client_id=client_1&code=[REDACTED]&state=xyz", redacted
206
+ end
207
+
208
+ def test_redact_path_leaves_non_sensitive_query_params_untouched
209
+ redacted = @client.send(:redact_path, "/user_management/users?limit=10&order=desc")
210
+ assert_equal "/user_management/users?limit=10&order=desc", redacted
211
+ end
212
+
213
+ def test_redact_path_scrubs_query_alongside_path_segment_redaction
214
+ redacted = @client.send(:redact_path, "/user_management/magic_auth/magic_secret?token=qs_token")
215
+ assert_equal "/user_management/magic_auth/[REDACTED]?token=[REDACTED]", redacted
216
+ end
173
217
  end
@@ -28,11 +28,26 @@ class EncryptorsAesGcmTest < Minitest::Test
28
28
 
29
29
  def test_unseal_with_wrong_key_raises
30
30
  sealed = @enc.seal({"x" => 1}, PASSWORD)
31
+ # Wrong key is the same length (>= 32 bytes) so the length guard doesn't
32
+ # short-circuit; we want to assert the underlying cipher rejection.
31
33
  assert_raises(OpenSSL::Cipher::CipherError) do
32
- @enc.unseal(sealed, "wrong-password")
34
+ @enc.unseal(sealed, "wrong-cookie-password-32-bytes--")
33
35
  end
34
36
  end
35
37
 
38
+ def test_unseal_rejects_short_key
39
+ sealed = @enc.seal({"x" => 1}, PASSWORD)
40
+ assert_raises(ArgumentError) do
41
+ @enc.unseal(sealed, "too-short")
42
+ end
43
+ end
44
+
45
+ def test_seal_rejects_short_key
46
+ assert_raises(ArgumentError) { @enc.seal({"x" => 1}, "too-short") }
47
+ assert_raises(ArgumentError) { @enc.seal({"x" => 1}, nil) }
48
+ assert_raises(ArgumentError) { @enc.seal({"x" => 1}, "") }
49
+ end
50
+
36
51
  def test_unseal_rejects_short_payload
37
52
  assert_raises(ArgumentError) do
38
53
  @enc.unseal(Base64.strict_encode64("short"), PASSWORD)