workos 5.11.0 → 5.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -2
- data/.rubocop.yml +5 -6
- data/.rubocop_todo.yml +94 -0
- data/.ruby-version +1 -1
- data/Gemfile.lock +20 -15
- data/lib/workos/cache.rb +94 -0
- data/lib/workos/organization.rb +11 -1
- data/lib/workos/organizations.rb +5 -2
- data/lib/workos/session.rb +3 -3
- data/lib/workos/version.rb +1 -1
- data/lib/workos.rb +1 -0
- data/spec/lib/workos/cache_spec.rb +94 -0
- data/spec/lib/workos/organizations_spec.rb +15 -0
- data/spec/lib/workos/session_spec.rb +46 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update_with_stripe_customer_id.yml +78 -0
- data/workos.gemspec +2 -2
- metadata +14 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6852c01440a63d103cd39a4c0aee2eeb5d1215d79a3e8eb2a3053b5aa3f3376e
|
4
|
+
data.tar.gz: 71c5c53d1f8fc5219903cfcfc23cbeb657da1db2c64980fa9d8914bd6ae92b1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb001da89bc0cf2866dcfd4d4c5207ddc7c960538ab413c45fcd5d5f80e4c97e205cc0e3fb51bdaaf8af984e9e30ad5c317c30e453c8e6579c1914350dfe3f2c
|
7
|
+
data.tar.gz: 5fe92a1ad40c3bced46b0597430b02ea36b20c82f109614df332695aa7d9f613a025e0f30d3afcaab978c5b3de6882a3a67886e55343de513fb508950664a5d3
|
data/.github/workflows/ci.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
Enabled: false
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
4
3
|
Layout/DotPosition:
|
5
4
|
EnforcedStyle: trailing
|
6
5
|
Layout/EmptyLineAfterMagicComment:
|
@@ -8,11 +7,11 @@ Layout/EmptyLineAfterMagicComment:
|
|
8
7
|
Layout/EmptyLines:
|
9
8
|
Enabled: false
|
10
9
|
Layout/LineLength:
|
11
|
-
|
10
|
+
AllowedPatterns:
|
12
11
|
- 'VCR\.use_cassette'
|
13
12
|
- '(\A|\s)/.*?/'
|
14
13
|
Metrics/BlockLength:
|
15
|
-
|
14
|
+
AllowedMethods: ['describe', 'context', 'before', 'it']
|
16
15
|
Metrics/MethodLength:
|
17
16
|
Max: 30
|
18
17
|
Metrics/ModuleLength:
|
@@ -28,4 +27,4 @@ Style/TrailingCommaInArguments:
|
|
28
27
|
Style/TrailingCommaInHashLiteral:
|
29
28
|
EnforcedStyleForMultiline: 'consistent_comma'
|
30
29
|
AllCops:
|
31
|
-
TargetRubyVersion:
|
30
|
+
TargetRubyVersion: 3.1
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2025-01-22 21:38:20 UTC using RuboCop version 1.71.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
# This cop supports safe autocorrection (--autocorrect).
|
11
|
+
# Configuration parameters: EnforcedStyleAlignWith.
|
12
|
+
# SupportedStylesAlignWith: either, start_of_block, start_of_line
|
13
|
+
Layout/BlockAlignment:
|
14
|
+
Exclude:
|
15
|
+
- 'spec/lib/workos/session_spec.rb'
|
16
|
+
|
17
|
+
# Offense count: 2
|
18
|
+
# This cop supports safe autocorrection (--autocorrect).
|
19
|
+
Layout/EmptyLinesAroundMethodBody:
|
20
|
+
Exclude:
|
21
|
+
- 'lib/workos/mfa.rb'
|
22
|
+
- 'lib/workos/user_management.rb'
|
23
|
+
|
24
|
+
# Offense count: 1
|
25
|
+
# This cop supports safe autocorrection (--autocorrect).
|
26
|
+
Layout/SpaceAroundMethodCallOperator:
|
27
|
+
Exclude:
|
28
|
+
- 'spec/lib/workos/directory_sync_spec.rb'
|
29
|
+
|
30
|
+
# Offense count: 1
|
31
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
32
|
+
Lint/DuplicateRequire:
|
33
|
+
Exclude:
|
34
|
+
- 'lib/workos/session.rb'
|
35
|
+
|
36
|
+
# Offense count: 3
|
37
|
+
# Configuration parameters: AllowedParentClasses.
|
38
|
+
Lint/MissingSuper:
|
39
|
+
Exclude:
|
40
|
+
- 'lib/workos/directory_group.rb'
|
41
|
+
- 'lib/workos/directory_user.rb'
|
42
|
+
- 'lib/workos/errors.rb'
|
43
|
+
|
44
|
+
# Offense count: 5
|
45
|
+
# Configuration parameters: CountComments, CountAsOne.
|
46
|
+
Metrics/ClassLength:
|
47
|
+
Max: 624
|
48
|
+
|
49
|
+
# Offense count: 8
|
50
|
+
# This cop supports safe autocorrection (--autocorrect).
|
51
|
+
# Configuration parameters: EnforcedStyle.
|
52
|
+
# SupportedStyles: separated, grouped
|
53
|
+
Style/AccessorGrouping:
|
54
|
+
Exclude:
|
55
|
+
- 'lib/workos/errors.rb'
|
56
|
+
|
57
|
+
# Offense count: 34
|
58
|
+
# This cop supports safe autocorrection (--autocorrect).
|
59
|
+
# Configuration parameters: EnforcedStyle.
|
60
|
+
# SupportedStyles: braces, no_braces
|
61
|
+
Style/HashAsLastArrayItem:
|
62
|
+
Exclude:
|
63
|
+
- 'spec/lib/workos/directory_sync_spec.rb'
|
64
|
+
- 'spec/lib/workos/event_spec.rb'
|
65
|
+
- 'spec/lib/workos/organizations_spec.rb'
|
66
|
+
- 'spec/lib/workos/sso_spec.rb'
|
67
|
+
- 'spec/lib/workos/user_management_spec.rb'
|
68
|
+
|
69
|
+
# Offense count: 2
|
70
|
+
# This cop supports safe autocorrection (--autocorrect).
|
71
|
+
Style/KeywordParametersOrder:
|
72
|
+
Exclude:
|
73
|
+
- 'lib/workos/organizations.rb'
|
74
|
+
|
75
|
+
# Offense count: 3
|
76
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
77
|
+
# Configuration parameters: SafeForConstants.
|
78
|
+
Style/RedundantFetchBlock:
|
79
|
+
Exclude:
|
80
|
+
- 'spec/lib/workos/cache_spec.rb'
|
81
|
+
|
82
|
+
# Offense count: 1
|
83
|
+
# This cop supports safe autocorrection (--autocorrect).
|
84
|
+
# Configuration parameters: AllowMultipleReturnValues.
|
85
|
+
Style/RedundantReturn:
|
86
|
+
Exclude:
|
87
|
+
- 'lib/workos/directory_user.rb'
|
88
|
+
|
89
|
+
# Offense count: 2
|
90
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
91
|
+
Style/SlicingWithRange:
|
92
|
+
Exclude:
|
93
|
+
- 'lib/workos/deprecated_hash_wrapper.rb'
|
94
|
+
- 'lib/workos/session.rb'
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.1.4
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
workos (5.
|
4
|
+
workos (5.12.0)
|
5
5
|
encryptor (~> 3.0)
|
6
6
|
jwt (~> 2.8)
|
7
7
|
|
@@ -19,16 +19,18 @@ GEM
|
|
19
19
|
diff-lcs (1.5.1)
|
20
20
|
encryptor (3.0.0)
|
21
21
|
hashdiff (1.1.0)
|
22
|
+
json (2.9.1)
|
22
23
|
jwt (2.10.1)
|
23
24
|
base64
|
24
|
-
|
25
|
-
|
25
|
+
language_server-protocol (3.17.0.3)
|
26
|
+
parallel (1.26.3)
|
27
|
+
parser (3.3.7.0)
|
26
28
|
ast (~> 2.4.1)
|
27
29
|
racc
|
28
30
|
public_suffix (5.0.4)
|
29
|
-
racc (1.
|
31
|
+
racc (1.8.1)
|
30
32
|
rainbow (3.1.1)
|
31
|
-
regexp_parser (2.
|
33
|
+
regexp_parser (2.10.0)
|
32
34
|
rexml (3.2.6)
|
33
35
|
rspec (3.9.0)
|
34
36
|
rspec-core (~> 3.9.0)
|
@@ -43,19 +45,22 @@ GEM
|
|
43
45
|
diff-lcs (>= 1.2.0, < 2.0)
|
44
46
|
rspec-support (~> 3.9.0)
|
45
47
|
rspec-support (3.9.4)
|
46
|
-
rubocop (
|
48
|
+
rubocop (1.71.0)
|
49
|
+
json (~> 2.3)
|
50
|
+
language_server-protocol (>= 3.17.0)
|
47
51
|
parallel (~> 1.10)
|
48
|
-
parser (>=
|
52
|
+
parser (>= 3.3.0.2)
|
49
53
|
rainbow (>= 2.2.2, < 4.0)
|
50
|
-
regexp_parser (>=
|
51
|
-
|
52
|
-
rubocop-ast (>= 0.6.0)
|
54
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
55
|
+
rubocop-ast (>= 1.36.2, < 2.0)
|
53
56
|
ruby-progressbar (~> 1.7)
|
54
|
-
unicode-display_width (>=
|
55
|
-
rubocop-ast (1.
|
56
|
-
parser (>= 3.3.0
|
57
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
58
|
+
rubocop-ast (1.37.0)
|
59
|
+
parser (>= 3.3.1.0)
|
57
60
|
ruby-progressbar (1.13.0)
|
58
|
-
unicode-display_width (1.
|
61
|
+
unicode-display_width (3.1.4)
|
62
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
63
|
+
unicode-emoji (4.0.4)
|
59
64
|
vcr (5.0.0)
|
60
65
|
webmock (3.23.0)
|
61
66
|
addressable (>= 2.8.0)
|
@@ -68,7 +73,7 @@ PLATFORMS
|
|
68
73
|
DEPENDENCIES
|
69
74
|
bundler (>= 2.0.1)
|
70
75
|
rspec (~> 3.9.0)
|
71
|
-
rubocop (~>
|
76
|
+
rubocop (~> 1.71)
|
72
77
|
vcr (~> 5.0.0)
|
73
78
|
webmock
|
74
79
|
workos!
|
data/lib/workos/cache.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WorkOS
|
4
|
+
# The Cache module provides a simple in-memory cache for storing values
|
5
|
+
# This module is not meant to be instantiated in a user space, and is used internally by the SDK
|
6
|
+
module Cache
|
7
|
+
# The Entry class represents a cache entry with a value and an expiration time
|
8
|
+
class Entry
|
9
|
+
attr_reader :value, :expires_at
|
10
|
+
|
11
|
+
# Initializes a new cache entry
|
12
|
+
# @param value [Object] The value to store in the cache
|
13
|
+
# @param expires_in_seconds [Integer, nil] The expiration time for the value in seconds, or nil for no expiration
|
14
|
+
def initialize(value, expires_in_seconds)
|
15
|
+
@value = value
|
16
|
+
@expires_at = expires_in_seconds ? Time.now + expires_in_seconds : nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# Checks if the entry has expired
|
20
|
+
# @return [Boolean] True if the entry has expired, false otherwise
|
21
|
+
def expired?
|
22
|
+
return false if expires_at.nil?
|
23
|
+
|
24
|
+
Time.now > @expires_at
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Fetches a value from the cache, or calls the block to fetch the value if it is not present
|
30
|
+
# @param key [String] The key to fetch the value for
|
31
|
+
# @param expires_in [Integer] The expiration time for the value in seconds
|
32
|
+
# @param force [Boolean] If true, the value will be fetched from the block even if it is present in the cache
|
33
|
+
# @param block [Proc] The block to call to fetch the value if it is not present in the cache
|
34
|
+
# @return [Object] The value fetched from the cache or the block
|
35
|
+
def fetch(key, expires_in: nil, force: false, &block)
|
36
|
+
entry = store[key]
|
37
|
+
|
38
|
+
if force || entry.nil? || entry.expired?
|
39
|
+
value = block.call
|
40
|
+
store[key] = Entry.new(value, expires_in)
|
41
|
+
return value
|
42
|
+
end
|
43
|
+
|
44
|
+
entry.value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Reads a value from the cache
|
48
|
+
# @param key [String] The key to read the value for
|
49
|
+
# @return [Object] The value read from the cache, or nil if the value is not present or has expired
|
50
|
+
def read(key)
|
51
|
+
entry = store[key]
|
52
|
+
return nil if entry.nil? || entry.expired?
|
53
|
+
|
54
|
+
entry.value
|
55
|
+
end
|
56
|
+
|
57
|
+
# Writes a value to the cache
|
58
|
+
# @param key [String] The key to write the value for
|
59
|
+
# @param value [Object] The value to write to the cache
|
60
|
+
# @param expires_in [Integer] The expiration time for the value in seconds
|
61
|
+
# @return [Object] The value written to the cache
|
62
|
+
def write(key, value, expires_in: nil)
|
63
|
+
store[key] = Entry.new(value, expires_in)
|
64
|
+
value
|
65
|
+
end
|
66
|
+
|
67
|
+
# Deletes a value from the cache
|
68
|
+
# @param key [String] The key to delete the value for
|
69
|
+
def delete(key)
|
70
|
+
store.delete(key)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Clears all values from the cache
|
74
|
+
def clear
|
75
|
+
store.clear
|
76
|
+
end
|
77
|
+
|
78
|
+
# Checks if a value exists in the cache
|
79
|
+
# @param key [String] The key to check for
|
80
|
+
# @return [Boolean] True if the value exists and has not expired, false otherwise
|
81
|
+
def exist?(key)
|
82
|
+
entry = store[key]
|
83
|
+
!(entry.nil? || entry.expired?)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# The in-memory store for the cache
|
89
|
+
def store
|
90
|
+
@store ||= {}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/workos/organization.rb
CHANGED
@@ -7,7 +7,15 @@ module WorkOS
|
|
7
7
|
class Organization
|
8
8
|
include HashProvider
|
9
9
|
|
10
|
-
attr_accessor
|
10
|
+
attr_accessor(
|
11
|
+
:id,
|
12
|
+
:domains,
|
13
|
+
:stripe_customer_id,
|
14
|
+
:name,
|
15
|
+
:allow_profiles_outside_organization,
|
16
|
+
:created_at,
|
17
|
+
:updated_at,
|
18
|
+
)
|
11
19
|
|
12
20
|
def initialize(json)
|
13
21
|
hash = JSON.parse(json, symbolize_names: true)
|
@@ -16,6 +24,7 @@ module WorkOS
|
|
16
24
|
@name = hash[:name]
|
17
25
|
@allow_profiles_outside_organization = hash[:allow_profiles_outside_organization]
|
18
26
|
@domains = hash[:domains]
|
27
|
+
@stripe_customer_id = hash[:stripe_customer_id]
|
19
28
|
@created_at = hash[:created_at]
|
20
29
|
@updated_at = hash[:updated_at]
|
21
30
|
end
|
@@ -26,6 +35,7 @@ module WorkOS
|
|
26
35
|
name: name,
|
27
36
|
allow_profiles_outside_organization: allow_profiles_outside_organization,
|
28
37
|
domains: domains,
|
38
|
+
stripe_customer_id: stripe_customer_id,
|
29
39
|
created_at: created_at,
|
30
40
|
updated_at: updated_at,
|
31
41
|
}
|
data/lib/workos/organizations.rb
CHANGED
@@ -71,7 +71,7 @@ module WorkOS
|
|
71
71
|
|
72
72
|
# Create an organization
|
73
73
|
#
|
74
|
-
# @param [Array<Hash>] domain_data List of domain hashes describing an
|
74
|
+
# @param [Array<Hash>] domain_data List of domain hashes describing an organization domain.
|
75
75
|
# @option domain_data [String] domain The domain that belongs to the organization.
|
76
76
|
# @option domain_data [String] state The state of the domain. "verified" or "pending"
|
77
77
|
# @param [String] name A unique, descriptive name for the organization
|
@@ -118,9 +118,10 @@ module WorkOS
|
|
118
118
|
# Update an organization
|
119
119
|
#
|
120
120
|
# @param [String] organization Organization unique identifier
|
121
|
-
# @param [Array<Hash>] domain_data List of domain hashes describing an
|
121
|
+
# @param [Array<Hash>] domain_data List of domain hashes describing an organization domain.
|
122
122
|
# @option domain_data [String] domain The domain that belongs to the organization.
|
123
123
|
# @option domain_data [String] state The state of the domain. "verified" or "pending"
|
124
|
+
# @param [String] stripe_customer_id The Stripe customer ID associated with this organization.
|
124
125
|
# @param [String] name A unique, descriptive name for the organization
|
125
126
|
# @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
|
126
127
|
# within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
|
@@ -129,6 +130,7 @@ module WorkOS
|
|
129
130
|
# Deprecated: Use domain_data instead.
|
130
131
|
def update_organization(
|
131
132
|
organization:,
|
133
|
+
stripe_customer_id: nil,
|
132
134
|
domain_data: nil,
|
133
135
|
domains: nil,
|
134
136
|
name: nil,
|
@@ -136,6 +138,7 @@ module WorkOS
|
|
136
138
|
)
|
137
139
|
body = { name: name }
|
138
140
|
body[:domain_data] = domain_data if domain_data
|
141
|
+
body[:stripe_customer_id] = stripe_customer_id if stripe_customer_id
|
139
142
|
|
140
143
|
if domains
|
141
144
|
warn_deprecation '`domains` is deprecated. Use `domain_data` instead.'
|
data/lib/workos/session.rb
CHANGED
@@ -11,7 +11,6 @@ require 'uri'
|
|
11
11
|
module WorkOS
|
12
12
|
# The Session class provides helper methods for working with WorkOS sessions
|
13
13
|
# This class is not meant to be instantiated in a user space, and is instantiated internally but exposed.
|
14
|
-
# rubocop:disable Metrics/ClassLength
|
15
14
|
class Session
|
16
15
|
attr_accessor :jwks, :jwks_algorithms, :user_management, :cookie_password, :session_data, :client_id
|
17
16
|
|
@@ -23,7 +22,9 @@ module WorkOS
|
|
23
22
|
@session_data = session_data
|
24
23
|
@client_id = client_id
|
25
24
|
|
26
|
-
@jwks =
|
25
|
+
@jwks = Cache.fetch("jwks_#{client_id}", expires_in: 5 * 60) do
|
26
|
+
create_remote_jwk_set(URI(@user_management.get_jwks_url(client_id)))
|
27
|
+
end
|
27
28
|
@jwks_algorithms = @jwks.map { |key| key[:alg] }.compact.uniq
|
28
29
|
end
|
29
30
|
|
@@ -178,5 +179,4 @@ module WorkOS
|
|
178
179
|
end
|
179
180
|
# rubocop:enable Naming/PredicateName
|
180
181
|
end
|
181
|
-
# rubocop:enable Metrics/ClassLength
|
182
182
|
end
|
data/lib/workos/version.rb
CHANGED
data/lib/workos.rb
CHANGED
@@ -45,6 +45,7 @@ module WorkOS
|
|
45
45
|
autoload :AuthenticationFactorAndChallenge, 'workos/authentication_factor_and_challenge'
|
46
46
|
autoload :AuthenticationResponse, 'workos/authentication_response'
|
47
47
|
autoload :AuditLogs, 'workos/audit_logs'
|
48
|
+
autoload :Cache, 'workos/cache'
|
48
49
|
autoload :Challenge, 'workos/challenge'
|
49
50
|
autoload :Client, 'workos/client'
|
50
51
|
autoload :Connection, 'workos/connection'
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe WorkOS::Cache do
|
4
|
+
before { described_class.clear }
|
5
|
+
|
6
|
+
describe '.write and .read' do
|
7
|
+
it 'stores and retrieves data' do
|
8
|
+
described_class.write('key', 'value')
|
9
|
+
expect(described_class.read('key')).to eq('value')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'returns nil if key does not exist' do
|
13
|
+
expect(described_class.read('missing')).to be_nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.fetch' do
|
18
|
+
it 'returns cached value when present and not expired' do
|
19
|
+
described_class.write('key', 'value')
|
20
|
+
fetch_value = described_class.fetch('key') { 'new_value' }
|
21
|
+
expect(fetch_value).to eq('value')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'executes block and caches value when not present' do
|
25
|
+
fetch_value = described_class.fetch('key') { 'new_value' }
|
26
|
+
expect(fetch_value).to eq('new_value')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'executes block and caches value when force is true' do
|
30
|
+
described_class.write('key', 'value')
|
31
|
+
fetch_value = described_class.fetch('key', force: true) { 'new_value' }
|
32
|
+
expect(fetch_value).to eq('new_value')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'expiration' do
|
37
|
+
it 'expires values after specified time' do
|
38
|
+
described_class.write('key', 'value', expires_in: 0.1)
|
39
|
+
expect(described_class.read('key')).to eq('value')
|
40
|
+
sleep 0.2
|
41
|
+
expect(described_class.read('key')).to be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'executes block and caches new value when expired' do
|
45
|
+
described_class.write('key', 'old_value', expires_in: 0.1)
|
46
|
+
sleep 0.2
|
47
|
+
fetch_value = described_class.fetch('key') { 'new_value' }
|
48
|
+
expect(fetch_value).to eq('new_value')
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'does not expire values when expires_in is nil' do
|
52
|
+
described_class.write('key', 'value', expires_in: nil)
|
53
|
+
sleep 0.2
|
54
|
+
expect(described_class.read('key')).to eq('value')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '.exist?' do
|
59
|
+
it 'returns true if key exists' do
|
60
|
+
described_class.write('key', 'value')
|
61
|
+
expect(described_class.exist?('key')).to be true
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns false if expired' do
|
65
|
+
described_class.write('key', 'value', expires_in: 0.1)
|
66
|
+
sleep 0.2
|
67
|
+
expect(described_class.exist?('key')).to be false
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns false if key does not exist' do
|
71
|
+
expect(described_class.exist?('missing')).to be false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.delete' do
|
76
|
+
it 'deletes key' do
|
77
|
+
described_class.write('key', 'value')
|
78
|
+
described_class.delete('key')
|
79
|
+
expect(described_class.read('key')).to be_nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.clear' do
|
84
|
+
it 'removes all keys from the cache' do
|
85
|
+
described_class.write('key1', 'value1')
|
86
|
+
described_class.write('key2', 'value2')
|
87
|
+
|
88
|
+
described_class.clear
|
89
|
+
|
90
|
+
expect(described_class.read('key1')).to be_nil
|
91
|
+
expect(described_class.read('key2')).to be_nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -295,6 +295,21 @@ describe WorkOS::Organizations do
|
|
295
295
|
end
|
296
296
|
end
|
297
297
|
end
|
298
|
+
context 'with a stripe_customer_id' do
|
299
|
+
it 'updates the organization' do
|
300
|
+
VCR.use_cassette 'organization/update_with_stripe_customer_id' do
|
301
|
+
organization = described_class.update_organization(
|
302
|
+
organization: 'org_01JJ5H14CAA2SQ5G9HNN6TBZ05',
|
303
|
+
name: 'Test Organization',
|
304
|
+
stripe_customer_id: 'cus_123',
|
305
|
+
)
|
306
|
+
|
307
|
+
expect(organization.id).to eq('org_01JJ5H14CAA2SQ5G9HNN6TBZ05')
|
308
|
+
expect(organization.name).to eq('Test Organization')
|
309
|
+
expect(organization.stripe_customer_id).to eq('cus_123')
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
298
313
|
end
|
299
314
|
|
300
315
|
describe '.delete_organization' do
|
@@ -19,6 +19,52 @@ describe WorkOS::Session do
|
|
19
19
|
allow(user_management).to receive(:get_jwks_url).with(client_id).and_return(jwks_url)
|
20
20
|
end
|
21
21
|
|
22
|
+
describe 'JWKS caching' do
|
23
|
+
before do
|
24
|
+
WorkOS::Cache.clear
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'caches and returns JWKS' do
|
28
|
+
expect(Net::HTTP).to receive(:get).once
|
29
|
+
session1 = WorkOS::Session.new(
|
30
|
+
user_management: user_management,
|
31
|
+
client_id: client_id,
|
32
|
+
session_data: session_data,
|
33
|
+
cookie_password: cookie_password,
|
34
|
+
)
|
35
|
+
|
36
|
+
session2 = WorkOS::Session.new(
|
37
|
+
user_management: user_management,
|
38
|
+
client_id: client_id,
|
39
|
+
session_data: session_data,
|
40
|
+
cookie_password: cookie_password,
|
41
|
+
)
|
42
|
+
|
43
|
+
expect(session1.jwks.map(&:export)).to eq(session2.jwks.map(&:export))
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'fetches JWKS from remote when cache is expired' do
|
47
|
+
expect(Net::HTTP).to receive(:get).twice
|
48
|
+
session1 = WorkOS::Session.new(
|
49
|
+
user_management: user_management,
|
50
|
+
client_id: client_id,
|
51
|
+
session_data: session_data,
|
52
|
+
cookie_password: cookie_password,
|
53
|
+
)
|
54
|
+
|
55
|
+
allow(Time).to receive(:now).and_return(Time.now + 301)
|
56
|
+
|
57
|
+
session2 = WorkOS::Session.new(
|
58
|
+
user_management: user_management,
|
59
|
+
client_id: client_id,
|
60
|
+
session_data: session_data,
|
61
|
+
cookie_password: cookie_password,
|
62
|
+
)
|
63
|
+
|
64
|
+
expect(session1.jwks.map(&:export)).to eq(session2.jwks.map(&:export))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
22
68
|
it 'raises an error if cookie_password is nil or empty' do
|
23
69
|
expect do
|
24
70
|
WorkOS::Session.new(
|
@@ -0,0 +1,78 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: put
|
5
|
+
uri: https://api.workos.com/organizations/org_01JJ5H14CAA2SQ5G9HNN6TBZ05
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"name":"Test Organization","stripe_customer_id":"cus_123"}'
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
User-Agent:
|
17
|
+
- WorkOS; ruby/3.1.4; arm64-darwin24; v5.11.1
|
18
|
+
Authorization:
|
19
|
+
- Bearer <API_KEY>
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Wed, 22 Jan 2025 16:50:26 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/json; charset=utf-8
|
29
|
+
Transfer-Encoding:
|
30
|
+
- chunked
|
31
|
+
Connection:
|
32
|
+
- keep-alive
|
33
|
+
Cf-Ray:
|
34
|
+
- 90610b03f84db9f1-SEA
|
35
|
+
Cf-Cache-Status:
|
36
|
+
- DYNAMIC
|
37
|
+
Etag:
|
38
|
+
- W/"1cc-uqKwJf4L85rLspCK9lJ1ceRlbSA"
|
39
|
+
Strict-Transport-Security:
|
40
|
+
- max-age=15552000; includeSubDomains
|
41
|
+
Vary:
|
42
|
+
- Origin, Accept-Encoding
|
43
|
+
Access-Control-Allow-Credentials:
|
44
|
+
- 'true'
|
45
|
+
Content-Security-Policy:
|
46
|
+
- 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self''
|
47
|
+
https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src
|
48
|
+
''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests'
|
49
|
+
Expect-Ct:
|
50
|
+
- max-age=0
|
51
|
+
Referrer-Policy:
|
52
|
+
- no-referrer
|
53
|
+
X-Content-Type-Options:
|
54
|
+
- nosniff
|
55
|
+
X-Dns-Prefetch-Control:
|
56
|
+
- 'off'
|
57
|
+
X-Download-Options:
|
58
|
+
- noopen
|
59
|
+
X-Frame-Options:
|
60
|
+
- SAMEORIGIN
|
61
|
+
X-Permitted-Cross-Domain-Policies:
|
62
|
+
- none
|
63
|
+
X-Request-Id:
|
64
|
+
- 7b62039f-40f4-4c2c-9ef9-7b1f63615481
|
65
|
+
X-Xss-Protection:
|
66
|
+
- '0'
|
67
|
+
Set-Cookie:
|
68
|
+
- _cfuvid=TPT.bwe9giSuDXv9bDFC8leHolKCFKy2kdsa0G89jGI-1737564626875-0.0.1.1-604800000;
|
69
|
+
path=/; domain=.workos.com; HttpOnly; Secure; SameSite=None
|
70
|
+
Server:
|
71
|
+
- cloudflare
|
72
|
+
body:
|
73
|
+
encoding: ASCII-8BIT
|
74
|
+
string: '{"object":"organization","id":"org_01JJ5H14CAA2SQ5G9HNN6TBZ05","name":"Test
|
75
|
+
Organization","allow_profiles_outside_organization":false,"created_at":"2025-01-21T22:51:46.441Z","updated_at":"2025-01-22T16:50:26.789Z","domains":[{"verification_strategy":"manual","state":"verified","object":"organization_domain","id":"org_domain_01JJ5H14CKRGAE9HVA1Y9YABSX","organization_id":"org_01JJ5H14CAA2SQ5G9HNN6TBZ05","domain":"example.me"}],"stripe_customer_id":"cus_123"}'
|
76
|
+
http_version:
|
77
|
+
recorded_at: Wed, 22 Jan 2025 16:50:26 GMT
|
78
|
+
recorded_with: VCR 5.0.0
|
data/workos.gemspec
CHANGED
@@ -26,9 +26,9 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.add_development_dependency 'bundler', '>= 2.0.1'
|
28
28
|
spec.add_development_dependency 'rspec', '~> 3.9.0'
|
29
|
-
spec.add_development_dependency 'rubocop', '~>
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 1.71'
|
30
30
|
spec.add_development_dependency 'vcr', '~> 5.0.0'
|
31
31
|
spec.add_development_dependency 'webmock'
|
32
32
|
|
33
|
-
spec.required_ruby_version = '>=
|
33
|
+
spec.required_ruby_version = '>= 3.1'
|
34
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WorkOS
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: encryptor
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '1.71'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '1.71'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: vcr
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- ".gitignore"
|
124
124
|
- ".rspec"
|
125
125
|
- ".rubocop.yml"
|
126
|
+
- ".rubocop_todo.yml"
|
126
127
|
- ".ruby-version"
|
127
128
|
- Gemfile
|
128
129
|
- Gemfile.lock
|
@@ -136,6 +137,7 @@ files:
|
|
136
137
|
- lib/workos/audit_logs.rb
|
137
138
|
- lib/workos/authentication_factor_and_challenge.rb
|
138
139
|
- lib/workos/authentication_response.rb
|
140
|
+
- lib/workos/cache.rb
|
139
141
|
- lib/workos/challenge.rb
|
140
142
|
- lib/workos/client.rb
|
141
143
|
- lib/workos/configuration.rb
|
@@ -184,6 +186,7 @@ files:
|
|
184
186
|
- lib/workos/webhooks.rb
|
185
187
|
- lib/workos/widgets.rb
|
186
188
|
- spec/lib/workos/audit_logs_spec.rb
|
189
|
+
- spec/lib/workos/cache_spec.rb
|
187
190
|
- spec/lib/workos/client.rb
|
188
191
|
- spec/lib/workos/configuration_spec.rb
|
189
192
|
- spec/lib/workos/directory_sync_spec.rb
|
@@ -270,6 +273,7 @@ files:
|
|
270
273
|
- spec/support/fixtures/vcr_cassettes/organization/list.yml
|
271
274
|
- spec/support/fixtures/vcr_cassettes/organization/list_organization_roles.yml
|
272
275
|
- spec/support/fixtures/vcr_cassettes/organization/update.yml
|
276
|
+
- spec/support/fixtures/vcr_cassettes/organization/update_with_stripe_customer_id.yml
|
273
277
|
- spec/support/fixtures/vcr_cassettes/organization/update_without_name.yml
|
274
278
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml
|
275
279
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml
|
@@ -382,7 +386,7 @@ licenses:
|
|
382
386
|
- MIT
|
383
387
|
metadata:
|
384
388
|
documentation_uri: https://docs.workos.com/sdk/ruby
|
385
|
-
post_install_message:
|
389
|
+
post_install_message:
|
386
390
|
rdoc_options: []
|
387
391
|
require_paths:
|
388
392
|
- lib
|
@@ -390,7 +394,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
390
394
|
requirements:
|
391
395
|
- - ">="
|
392
396
|
- !ruby/object:Gem::Version
|
393
|
-
version: '
|
397
|
+
version: '3.1'
|
394
398
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
395
399
|
requirements:
|
396
400
|
- - ">="
|
@@ -398,11 +402,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
398
402
|
version: '0'
|
399
403
|
requirements: []
|
400
404
|
rubygems_version: 3.4.19
|
401
|
-
signing_key:
|
405
|
+
signing_key:
|
402
406
|
specification_version: 4
|
403
407
|
summary: API client for WorkOS
|
404
408
|
test_files:
|
405
409
|
- spec/lib/workos/audit_logs_spec.rb
|
410
|
+
- spec/lib/workos/cache_spec.rb
|
406
411
|
- spec/lib/workos/client.rb
|
407
412
|
- spec/lib/workos/configuration_spec.rb
|
408
413
|
- spec/lib/workos/directory_sync_spec.rb
|
@@ -489,6 +494,7 @@ test_files:
|
|
489
494
|
- spec/support/fixtures/vcr_cassettes/organization/list.yml
|
490
495
|
- spec/support/fixtures/vcr_cassettes/organization/list_organization_roles.yml
|
491
496
|
- spec/support/fixtures/vcr_cassettes/organization/update.yml
|
497
|
+
- spec/support/fixtures/vcr_cassettes/organization/update_with_stripe_customer_id.yml
|
492
498
|
- spec/support/fixtures/vcr_cassettes/organization/update_without_name.yml
|
493
499
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml
|
494
500
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml
|