workos 1.5.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.ruby-version +1 -1
  4. data/.semaphore/semaphore.yml +2 -2
  5. data/Gemfile.lock +3 -3
  6. data/lib/workos/connection.rb +1 -1
  7. data/lib/workos/directory.rb +2 -2
  8. data/lib/workos/directory_sync.rb +29 -0
  9. data/lib/workos/errors.rb +4 -0
  10. data/lib/workos/organization.rb +4 -1
  11. data/lib/workos/organizations.rb +18 -4
  12. data/lib/workos/profile.rb +7 -2
  13. data/lib/workos/sso.rb +38 -15
  14. data/lib/workos/types/connection_struct.rb +1 -1
  15. data/lib/workos/types/directory_struct.rb +2 -2
  16. data/lib/workos/types/organization_struct.rb +1 -0
  17. data/lib/workos/types/profile_struct.rb +1 -0
  18. data/lib/workos/types/webhook_struct.rb +14 -0
  19. data/lib/workos/types.rb +1 -0
  20. data/lib/workos/version.rb +1 -1
  21. data/lib/workos/webhook.rb +47 -0
  22. data/lib/workos/webhooks.rb +168 -0
  23. data/lib/workos.rb +3 -0
  24. data/spec/lib/workos/directory_sync_spec.rb +43 -10
  25. data/spec/lib/workos/sso_spec.rb +142 -2
  26. data/spec/lib/workos/webhooks_spec.rb +190 -0
  27. data/spec/support/fixtures/vcr_cassettes/{organization/update_invalid.yml → directory_sync/get_directory_with_invalid_id.yml} +35 -25
  28. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_valid_id.yml +84 -0
  29. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_after.yml +34 -22
  30. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_before.yml +36 -22
  31. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_domain.yml +30 -19
  32. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_limit.yml +31 -20
  33. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_no_options.yml +39 -21
  34. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_search.yml +32 -20
  35. data/spec/support/fixtures/vcr_cassettes/organization/create.yml +1 -1
  36. data/spec/support/fixtures/vcr_cassettes/organization/get.yml +1 -1
  37. data/spec/support/fixtures/vcr_cassettes/organization/list.yml +4 -4
  38. data/spec/support/fixtures/vcr_cassettes/organization/update.yml +1 -1
  39. data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +1 -1
  40. data/spec/support/profile.txt +1 -1
  41. data/spec/support/webhook_payload.txt +1 -0
  42. metadata +14 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5aecf9fa40a85a4623e12f1ba6804c87fde006931bed9a443327f6851d714df1
4
- data.tar.gz: 63d0daa095bf705643906247371d36a15f7ec9aac2b9d5b04293fb7e135a1d57
3
+ metadata.gz: 0500b789496692e53bc6d47d80a21a0b8b802325e1e0f876fe0b94a775aa4f05
4
+ data.tar.gz: a7e8e350e7fb1336496e9a09ee7543a120e1b6ed62cc60dffbe1b4ccd2dd4712
5
5
  SHA512:
6
- metadata.gz: f6c47e8d64139d4e3452dca04e6466b177b8c029b97779e58b93407d5e08f9c886076a0101a9b272376fe33e9ee16c180464e25b3be42a6089b921108187eeb1
7
- data.tar.gz: 577c4b464439eed2216d1011d637d619cd0af9c69f985f5d56f56ce58f6c72deb71f0d2dc66efc1014daa7d973cd2c1d3e95a2f729627658d4235ce2da683184
6
+ metadata.gz: 549c9210c2d765b2d6f264e62f285f7b96c9225e53724798216eb08167be5451825a729f36273fbda7fb713b9a1db49025b3cc6a308a98b969ca0a3631c8ffd9
7
+ data.tar.gz: 4a3fdf8d50681db812a0de37c56d93b03c6bb194a892280cfc9d287612ba7494a1bd777f92b28538b12d978b0a1dc354ca7c82e12fd1707c440643c7215685cb
data/.rubocop.yml CHANGED
@@ -9,7 +9,7 @@ Layout/LineLength:
9
9
  - 'VCR\.use_cassette'
10
10
  - '(\A|\s)/.*?/'
11
11
  Metrics/BlockLength:
12
- ExcludedMethods: ['describe', 'context']
12
+ ExcludedMethods: ['describe', 'context', 'before']
13
13
  Metrics/MethodLength:
14
14
  Max: 15
15
15
  Metrics/ModuleLength:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.0.1
1
+ 3.0.2
@@ -66,10 +66,10 @@ blocks:
66
66
  - sem-version ruby 2.7.3
67
67
  - bundle install
68
68
  - bundle exec rspec
69
- - name: Ruby 3.0.1
69
+ - name: Ruby 3.0.2
70
70
  commands:
71
71
  - checkout
72
- - sem-version ruby 3.0.1
72
+ - sem-version ruby 3.0.2
73
73
  - bundle install
74
74
  - bundle exec rspec
75
75
  promotions:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- workos (1.5.1)
4
+ workos (2.1.0)
5
5
  sorbet-runtime (~> 0.5)
6
6
 
7
7
  GEM
@@ -60,7 +60,7 @@ GEM
60
60
  simplecov_json_formatter (0.1.2)
61
61
  sorbet (0.5.6388)
62
62
  sorbet-static (= 0.5.6388)
63
- sorbet-runtime (0.5.9094)
63
+ sorbet-runtime (0.5.9300)
64
64
  sorbet-static (0.5.6388-universal-darwin-14)
65
65
  sorbet-static (0.5.6388-universal-darwin-15)
66
66
  sorbet-static (0.5.6388-universal-darwin-16)
@@ -93,4 +93,4 @@ DEPENDENCIES
93
93
  yard
94
94
 
95
95
  BUNDLED WITH
96
- 2.2.16
96
+ 2.2.33
@@ -21,7 +21,7 @@ module WorkOS
21
21
  @name = T.let(raw.name, String)
22
22
  @connection_type = T.let(raw.connection_type, String)
23
23
  @domains = T.let(raw.domains, Array)
24
- @organization_id = T.let(raw.organization_id, String)
24
+ @organization_id = raw.organization_id
25
25
  @state = T.let(raw.state, String)
26
26
  @status = T.let(raw.status, String)
27
27
  @created_at = T.let(raw.created_at, String)
@@ -17,10 +17,10 @@ module WorkOS
17
17
 
18
18
  @id = T.let(raw.id, String)
19
19
  @name = T.let(raw.name, String)
20
- @domain = T.let(raw.domain, String)
20
+ @domain = raw.domain
21
21
  @type = T.let(raw.type, String)
22
22
  @state = T.let(raw.state, String)
23
- @organization_id = T.let(raw.organization_id, String)
23
+ @organization_id = raw.organization_id
24
24
  @created_at = T.let(raw.created_at, String)
25
25
  @updated_at = T.let(raw.updated_at, String)
26
26
  end
@@ -53,6 +53,35 @@ module WorkOS
53
53
  )
54
54
  end
55
55
 
56
+ # Retrieve directory.
57
+ #
58
+ # @param [String] id Directory unique identifier
59
+ #
60
+ # @example
61
+ # WorkOS::SSO.get_directory(id: 'directory_01FK17DWRHH7APAFXT5B52PV0W')
62
+ # => #<WorkOS::Directory:0x00007fb6e4193d20
63
+ # @id="directory_01FK17DWRHH7APAFXT5B52PV0W",
64
+ # @name="Foo Corp",
65
+ # @domain="foo-corp.com",
66
+ # @type="okta scim v2.0",
67
+ # @state="linked",
68
+ # @organization_id="org_01F6Q6TFP7RD2PF6J03ANNWDKV",
69
+ # @created_at="2021-10-27T15:55:47.856Z",
70
+ # @updated_at="2021-10-27T16:03:43.990Z"
71
+ #
72
+ # @return [WorkOS::Directory]
73
+ sig { params(id: String).returns(WorkOS::Directory) }
74
+ def get_directory(id:)
75
+ request = get_request(
76
+ auth: true,
77
+ path: "/directories/#{id}",
78
+ )
79
+
80
+ response = execute_request(request: request)
81
+
82
+ WorkOS::Directory.new(response.body)
83
+ end
84
+
56
85
  # Retrieve directory groups.
57
86
  #
58
87
  # @param [Hash] options An options hash
data/lib/workos/errors.rb CHANGED
@@ -55,4 +55,8 @@ module WorkOS
55
55
  # InvalidRequestError is raised when a request is initiated with invalid
56
56
  # parameters.
57
57
  class InvalidRequestError < WorkOSError; end
58
+
59
+ # SignatureVerificationError is raised when the signature verification for a
60
+ # webhook fails
61
+ class SignatureVerificationError < WorkOSError; end
58
62
  end
@@ -8,7 +8,7 @@ module WorkOS
8
8
  class Organization
9
9
  extend T::Sig
10
10
 
11
- attr_accessor :id, :domains, :name, :created_at, :updated_at
11
+ attr_accessor :id, :domains, :name, :allow_profiles_outside_organization, :created_at, :updated_at
12
12
 
13
13
  sig { params(json: String).void }
14
14
  def initialize(json)
@@ -16,6 +16,7 @@ module WorkOS
16
16
 
17
17
  @id = T.let(raw.id, String)
18
18
  @name = T.let(raw.name, String)
19
+ @allow_profiles_outside_organization = T.let(raw.allow_profiles_outside_organization, T::Boolean)
19
20
  @domains = T.let(raw.domains, Array)
20
21
  @created_at = T.let(raw.created_at, String)
21
22
  @updated_at = T.let(raw.updated_at, String)
@@ -25,6 +26,7 @@ module WorkOS
25
26
  {
26
27
  id: id,
27
28
  name: name,
29
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
28
30
  domains: domains,
29
31
  created_at: created_at,
30
32
  updated_at: updated_at,
@@ -44,6 +46,7 @@ module WorkOS
44
46
  WorkOS::Types::OrganizationStruct.new(
45
47
  id: hash[:id],
46
48
  name: hash[:name],
49
+ allow_profiles_outside_organization: hash[:allow_profiles_outside_organization],
47
50
  domains: hash[:domains],
48
51
  created_at: hash[:created_at],
49
52
  updated_at: hash[:updated_at],
@@ -80,16 +80,23 @@ module WorkOS
80
80
  # @param [Array<String>] domains List of domains that belong to the
81
81
  # organization
82
82
  # @param [String] name A unique, descriptive name for the organization
83
+ # @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
84
+ # within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
83
85
  sig do
84
86
  params(
85
87
  domains: T::Array[String],
86
88
  name: String,
89
+ allow_profiles_outside_organization: T.nilable(T::Boolean),
87
90
  ).returns(WorkOS::Organization)
88
91
  end
89
- def create_organization(domains:, name:)
92
+ def create_organization(domains:, name:, allow_profiles_outside_organization: nil)
90
93
  request = post_request(
91
94
  auth: true,
92
- body: { domains: domains, name: name },
95
+ body: {
96
+ domains: domains,
97
+ name: name,
98
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
99
+ },
93
100
  path: '/organizations',
94
101
  )
95
102
 
@@ -105,17 +112,24 @@ module WorkOS
105
112
  # @param [Array<String>] domains List of domains that belong to the
106
113
  # organization
107
114
  # @param [String] name A unique, descriptive name for the organization
115
+ # @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
116
+ # within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
108
117
  sig do
109
118
  params(
110
119
  organization: String,
111
120
  domains: T::Array[String],
112
121
  name: String,
122
+ allow_profiles_outside_organization: T.nilable(T::Boolean),
113
123
  ).returns(WorkOS::Organization)
114
124
  end
115
- def update_organization(organization:, domains:, name:)
125
+ def update_organization(organization:, domains:, name:, allow_profiles_outside_organization: nil)
116
126
  request = put_request(
117
127
  auth: true,
118
- body: { domains: domains, name: name },
128
+ body: {
129
+ domains: domains,
130
+ name: name,
131
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
132
+ },
119
133
  path: "/organizations/#{organization}",
120
134
  )
121
135
 
@@ -11,9 +11,10 @@ module WorkOS
11
11
  extend T::Sig
12
12
 
13
13
  sig { returns(String) }
14
- attr_accessor :id, :email, :first_name, :last_name, :connection_id,
15
- :connection_type, :idp_id, :raw_attributes
14
+ attr_accessor :id, :email, :first_name, :last_name, :organization_id,
15
+ :connection_id, :connection_type, :idp_id, :raw_attributes
16
16
 
17
+ # rubocop:disable Metrics/AbcSize
17
18
  sig { params(profile_json: String).void }
18
19
  def initialize(profile_json)
19
20
  raw = parse_json(profile_json)
@@ -22,11 +23,13 @@ module WorkOS
22
23
  @email = T.let(raw.email, String)
23
24
  @first_name = raw.first_name
24
25
  @last_name = raw.last_name
26
+ @organization_id = raw.organization_id
25
27
  @connection_id = T.let(raw.connection_id, String)
26
28
  @connection_type = T.let(raw.connection_type, String)
27
29
  @idp_id = raw.idp_id
28
30
  @raw_attributes = raw.raw_attributes
29
31
  end
32
+ # rubocop:enable Metrics/AbcSize
30
33
 
31
34
  sig { returns(String) }
32
35
  def full_name
@@ -39,6 +42,7 @@ module WorkOS
39
42
  email: email,
40
43
  first_name: first_name,
41
44
  last_name: last_name,
45
+ organization_id: organization_id,
42
46
  connection_id: connection_id,
43
47
  connection_type: connection_type,
44
48
  idp_id: idp_id,
@@ -57,6 +61,7 @@ module WorkOS
57
61
  email: hash[:email],
58
62
  first_name: hash[:first_name],
59
63
  last_name: hash[:last_name],
64
+ organization_id: hash[:organization_id],
60
65
  connection_id: hash[:connection_id],
61
66
  connection_type: hash[:connection_type],
62
67
  idp_id: hash[:idp_id],
data/lib/workos/sso.rb CHANGED
@@ -21,23 +21,27 @@ module WorkOS
21
21
  # Generate an Oauth2 authorization URL where your users will
22
22
  # authenticate using the configured SSO Identity Provider.
23
23
  #
24
+ # @param [String] redirect_uri The URI where users are directed
25
+ # after completing the authentication step. Must match a
26
+ # configured redirect URI on your WorkOS dashboard.
27
+ # @param [String] client_id The WorkOS client ID for the environment
28
+ # where you've configured your SSO connection.
24
29
  # @param [String] domain The domain for the relevant SSO Connection
25
- # configured on your WorkOS dashboard. One of provider or domain is
26
- # required
30
+ # configured on your WorkOS dashboard. One of provider, domain,
31
+ # connection, or organization is required.
32
+ # The domain is deprecated.
27
33
  # @param [String] provider A provider name for an Identity Provider
28
- # configured on your WorkOS dashboard. Only 'Google' is supported.
34
+ # configured on your WorkOS dashboard. Only 'GoogleOAuth' and
35
+ # 'MicrosoftOAuth' are supported.
29
36
  # @param [String] connection The ID for a Connection configured on
30
37
  # WorkOS.
31
- # @param [String] client_id The WorkOS client ID for the environment
32
- # where you've configured your SSO connection.
33
- # @param [String] redirect_uri The URI where users are directed
34
- # after completing the authentication step. Must match a
35
- # configured redirect URI on your WorkOS dashboard.
36
- # @param [String] state An aribtrary state object
38
+ # @param [String] organization The ID for an Organization configured
39
+ # on WorkOS.
40
+ # @param [String] state An arbitrary state object
37
41
  # that is preserved and available to the client in the response.
38
42
  # @example
39
43
  # WorkOS::SSO.authorization_url(
40
- # domain: 'acme.com',
44
+ # connection: 'conn_123',
41
45
  # client_id: 'project_01DG5TGK363GRVXP3ZS40WNGEZ',
42
46
  # redirect_uri: 'https://workos.com/callback',
43
47
  # state: {
@@ -45,19 +49,23 @@ module WorkOS
45
49
  # }.to_s
46
50
  # )
47
51
  #
48
- # => "https://api.workos.com/sso/authorize?domain=acme.com" \
52
+ # => "https://api.workos.com/sso/authorize?connection=conn_123" \
49
53
  # "&client_id=project_01DG5TGK363GRVXP3ZS40WNGEZ" \
50
54
  # "&redirect_uri=https%3A%2F%2Fworkos.com%2Fcallback&" \
51
55
  # "response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdocs%22%7D"
52
56
  #
53
57
  # @return [String]
58
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
54
59
  sig do
55
60
  params(
56
61
  redirect_uri: String,
57
62
  client_id: T.nilable(String),
58
63
  domain: T.nilable(String),
64
+ domain_hint: T.nilable(String),
65
+ login_hint: T.nilable(String),
59
66
  provider: T.nilable(String),
60
67
  connection: T.nilable(String),
68
+ organization: T.nilable(String),
61
69
  state: T.nilable(String),
62
70
  ).returns(String)
63
71
  end
@@ -65,14 +73,23 @@ module WorkOS
65
73
  redirect_uri:,
66
74
  client_id: nil,
67
75
  domain: nil,
76
+ domain_hint: nil,
77
+ login_hint: nil,
68
78
  provider: nil,
69
79
  connection: nil,
80
+ organization: nil,
70
81
  state: ''
71
82
  )
83
+ if domain
84
+ warn '[DEPRECATION] `domain` is deprecated.
85
+ Please use `organization` instead.'
86
+ end
87
+
72
88
  validate_authorization_url_arguments(
73
89
  provider: provider,
74
90
  domain: domain,
75
91
  connection: connection,
92
+ organization: organization,
76
93
  )
77
94
 
78
95
  query = URI.encode_www_form({
@@ -81,12 +98,16 @@ module WorkOS
81
98
  response_type: 'code',
82
99
  state: state,
83
100
  domain: domain,
101
+ domain_hint: domain_hint,
102
+ login_hint: login_hint,
84
103
  provider: provider,
85
104
  connection: connection,
105
+ organization: organization,
86
106
  }.compact)
87
107
 
88
108
  "https://#{WorkOS::API_HOSTNAME}/sso/authorize?#{query}"
89
109
  end
110
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
90
111
 
91
112
  sig do
92
113
  params(
@@ -229,16 +250,18 @@ module WorkOS
229
250
  domain: T.nilable(String),
230
251
  provider: T.nilable(String),
231
252
  connection: T.nilable(String),
253
+ organization: T.nilable(String),
232
254
  ).void
233
255
  end
234
256
  def validate_authorization_url_arguments(
235
257
  domain:,
236
258
  provider:,
237
- connection:
259
+ connection:,
260
+ organization:
238
261
  )
239
- if [domain, provider, connection].all?(&:nil?)
240
- raise ArgumentError, 'Either connection, domain, or ' \
241
- 'provider is required.'
262
+ if [domain, provider, connection, organization].all?(&:nil?)
263
+ raise ArgumentError, 'Either connection, domain, ' \
264
+ 'provider, or organization is required.'
242
265
  end
243
266
 
244
267
  return unless provider && !PROVIDERS.include?(provider)
@@ -10,7 +10,7 @@ module WorkOS
10
10
  const :name, String
11
11
  const :connection_type, String
12
12
  const :domains, T::Array[T.untyped]
13
- const :organization_id, String
13
+ const :organization_id, T.nilable(String)
14
14
  const :state, String
15
15
  const :status, String
16
16
  const :created_at, String
@@ -8,10 +8,10 @@ module WorkOS
8
8
  class DirectoryStruct < T::Struct
9
9
  const :id, String
10
10
  const :name, String
11
- const :domain, String
11
+ const :domain, T.nilable(String)
12
12
  const :type, String
13
13
  const :state, String
14
- const :organization_id, String
14
+ const :organization_id, T.nilable(String)
15
15
  const :created_at, String
16
16
  const :updated_at, String
17
17
  end
@@ -8,6 +8,7 @@ module WorkOS
8
8
  class OrganizationStruct < T::Struct
9
9
  const :id, String
10
10
  const :name, String
11
+ const :allow_profiles_outside_organization, T::Boolean
11
12
  const :domains, T::Array[T.untyped]
12
13
  const :created_at, String
13
14
  const :updated_at, String
@@ -10,6 +10,7 @@ module WorkOS
10
10
  const :email, String
11
11
  const :first_name, T.nilable(String)
12
12
  const :last_name, T.nilable(String)
13
+ const :organization_id, T.nilable(String)
13
14
  const :connection_id, String
14
15
  const :connection_type, String
15
16
  const :idp_id, T.nilable(String)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module WorkOS
5
+ module Types
6
+ # This WebhookStruct acts as a typed interface
7
+ # for the Webhook class
8
+ class WebhookStruct < T::Struct
9
+ const :id, String
10
+ const :event, String
11
+ const :data, T::Hash[Symbol, Object]
12
+ end
13
+ end
14
+ end
data/lib/workos/types.rb CHANGED
@@ -15,5 +15,6 @@ module WorkOS
15
15
  require_relative 'types/profile_struct'
16
16
  require_relative 'types/provider_enum'
17
17
  require_relative 'types/directory_user_struct'
18
+ require_relative 'types/webhook_struct'
18
19
  end
19
20
  end
@@ -2,5 +2,5 @@
2
2
  # typed: strong
3
3
 
4
4
  module WorkOS
5
- VERSION = '1.5.1'
5
+ VERSION = '2.1.0'
6
6
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module WorkOS
5
+ # The Webhook class provides a lightweight wrapper around
6
+ # a WorkOS Webhook resource. This class is not meant to be instantiated
7
+ # in user space, and is instantiated internally but exposed.
8
+ class Webhook
9
+ extend T::Sig
10
+
11
+ attr_accessor :id, :event, :data
12
+
13
+ sig { params(json: String).void }
14
+ def initialize(json)
15
+ raw = parse_json(json)
16
+
17
+ @id = T.let(raw.id, String)
18
+ @event = T.let(raw.event, String)
19
+ @data = raw.data
20
+ end
21
+
22
+ def to_json(*)
23
+ {
24
+ id: id,
25
+ event: event,
26
+ data: data,
27
+ }
28
+ end
29
+
30
+ private
31
+
32
+ sig do
33
+ params(
34
+ json_string: String,
35
+ ).returns(WorkOS::Types::WebhookStruct)
36
+ end
37
+ def parse_json(json_string)
38
+ hash = JSON.parse(json_string, symbolize_names: true)
39
+
40
+ WorkOS::Types::WebhookStruct.new(
41
+ id: hash[:id],
42
+ event: hash[:event],
43
+ data: hash[:data],
44
+ )
45
+ end
46
+ end
47
+ end