statelydb 0.13.0 → 0.15.0
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.
- checksums.yaml +4 -4
- data/lib/api/auth/get_auth_token_pb.rb +18 -0
- data/lib/api/auth/service_pb.rb +18 -0
- data/lib/api/auth/service_services_pb.rb +29 -0
- data/lib/common/auth/{auth0_token_provider.rb → auth_token_provider.rb} +60 -44
- data/lib/common/auth/interceptor.rb +1 -1
- data/lib/common/auth/token_fetcher.rb +144 -0
- data/lib/statelydb.rb +2 -2
- data/sig/statelydb.rbi +151 -8
- data/sig/statelydb.rbs +125 -4
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1d7563d7f40d84bdc1ef3dacff41707ed1d915b638223d33b488ca4c7de531f
|
4
|
+
data.tar.gz: dd682b92fbc100505e15820c5415acadc9defc6bc2f2d0dad5b5797deddbf24b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0f27aa770fdf0c7b3386ac37862226cb4b5215766d265e47c82323b2157966e6ce0d87b5e4081d8d9dcd549f45cd0f6527203b0025b0e6c62ce0780c81dae74
|
7
|
+
data.tar.gz: de86db0a2a0227e6bec231d11c378bffff42fa7911ccc0874bd3ed97fae7f73d0ac29ce55b31817f75bfa82c0b63daa79dabd28276a66e17e2748770450f6243
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: auth/get_auth_token.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
|
8
|
+
descriptor_data = "\n\x19\x61uth/get_auth_token.proto\x12\x0cstately.auth\"B\n\x13GetAuthTokenRequest\x12\x1f\n\naccess_key\x18\x01 \x01(\tH\x00R\taccessKeyB\n\n\x08identity\"W\n\x14GetAuthTokenResponse\x12\x1d\n\nauth_token\x18\x01 \x01(\tR\tauthToken\x12 \n\x0c\x65xpires_in_s\x18\x02 \x01(\x04R\nexpiresInSBv\n\x10\x63om.stately.authB\x11GetAuthTokenProtoP\x01\xa2\x02\x03SAX\xaa\x02\x0cStately.Auth\xca\x02\x0cStately\\Auth\xe2\x02\x18Stately\\Auth\\GPBMetadata\xea\x02\rStately::Authb\x06proto3"
|
9
|
+
|
10
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
12
|
+
|
13
|
+
module Stately
|
14
|
+
module Auth
|
15
|
+
GetAuthTokenRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.auth.GetAuthTokenRequest").msgclass
|
16
|
+
GetAuthTokenResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.auth.GetAuthTokenResponse").msgclass
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: auth/service.proto
|
4
|
+
|
5
|
+
require 'google/protobuf'
|
6
|
+
|
7
|
+
require 'api/auth/get_auth_token_pb'
|
8
|
+
|
9
|
+
|
10
|
+
descriptor_data = "\n\x12\x61uth/service.proto\x12\x0cstately.auth\x1a\x19\x61uth/get_auth_token.proto2i\n\x0b\x41uthService\x12Z\n\x0cGetAuthToken\x12!.stately.auth.GetAuthTokenRequest\x1a\".stately.auth.GetAuthTokenResponse\"\x03\x90\x02\x01\x42q\n\x10\x63om.stately.authB\x0cServiceProtoP\x01\xa2\x02\x03SAX\xaa\x02\x0cStately.Auth\xca\x02\x0cStately\\Auth\xe2\x02\x18Stately\\Auth\\GPBMetadata\xea\x02\rStately::Authb\x06proto3"
|
11
|
+
|
12
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
14
|
+
|
15
|
+
module Stately
|
16
|
+
module Auth
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# Source: auth/service.proto for package 'Stately.Auth'
|
3
|
+
|
4
|
+
require 'grpc'
|
5
|
+
require 'api/auth/service_pb'
|
6
|
+
|
7
|
+
module Stately
|
8
|
+
module Auth
|
9
|
+
module AuthService
|
10
|
+
# AuthService is the service for vending access tokens used to connect to
|
11
|
+
# StatelyDB. This API is meant to be used from SDKs. Access Keys are created
|
12
|
+
# and managed from the stately.dbmanagement.UserService.
|
13
|
+
class Service
|
14
|
+
|
15
|
+
include ::GRPC::GenericService
|
16
|
+
|
17
|
+
self.marshal_class_method = :encode
|
18
|
+
self.unmarshal_class_method = :decode
|
19
|
+
self.service_name = 'stately.auth.AuthService'
|
20
|
+
|
21
|
+
# GetAuthToken returns a short-lived access token from some proof of
|
22
|
+
# identity. This operation will fail if the identity cannot be verified.
|
23
|
+
rpc :GetAuthToken, ::Stately::Auth::GetAuthTokenRequest, ::Stately::Auth::GetAuthTokenResponse
|
24
|
+
end
|
25
|
+
|
26
|
+
Stub = Service.rpc_stub_class
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -6,8 +6,10 @@ require "async/http/internet"
|
|
6
6
|
require "async/semaphore"
|
7
7
|
require "json"
|
8
8
|
require "logger"
|
9
|
-
require "
|
9
|
+
require "grpc"
|
10
10
|
require_relative "token_provider"
|
11
|
+
require_relative "token_fetcher"
|
12
|
+
require_relative "../../error"
|
11
13
|
|
12
14
|
LOGGER = Logger.new($stdout)
|
13
15
|
LOGGER.level = Logger::WARN
|
@@ -22,20 +24,25 @@ module StatelyDB
|
|
22
24
|
# which vends tokens from auth0 with the given client_id and client_secret.
|
23
25
|
# It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
|
24
26
|
# no credentials are explicitly passed and will throw an error if none are found.
|
25
|
-
class
|
27
|
+
class AuthTokenProvider < TokenProvider
|
26
28
|
# @param [String] origin The origin of the OAuth server
|
27
29
|
# @param [String] audience The OAuth Audience for the token
|
28
30
|
# @param [String] client_secret The StatelyDB client secret credential
|
29
31
|
# @param [String] client_id The StatelyDB client ID credential
|
32
|
+
# @param [String] access_key The StatelyDB access key credential
|
33
|
+
# @param [Float] base_retry_backoff_secs The base retry backoff in seconds
|
30
34
|
def initialize(
|
31
35
|
origin: "https://oauth.stately.cloud",
|
32
36
|
audience: "api.stately.cloud",
|
33
|
-
client_secret: ENV.fetch("STATELY_CLIENT_SECRET"),
|
34
|
-
client_id: ENV.fetch("STATELY_CLIENT_ID")
|
37
|
+
client_secret: ENV.fetch("STATELY_CLIENT_SECRET", nil),
|
38
|
+
client_id: ENV.fetch("STATELY_CLIENT_ID", nil),
|
39
|
+
access_key: ENV.fetch("STATELY_ACCESS_KEY", nil),
|
40
|
+
base_retry_backoff_secs: 1
|
35
41
|
)
|
36
42
|
super()
|
37
43
|
@actor = Async::Actor.new(Actor.new(origin: origin, audience: audience,
|
38
|
-
client_secret: client_secret, client_id: client_id
|
44
|
+
client_secret: client_secret, client_id: client_id, access_key: access_key,
|
45
|
+
base_retry_backoff_secs: base_retry_backoff_secs))
|
39
46
|
# this initialization cannot happen in the constructor because it is async and must run on the event loop
|
40
47
|
# which is not available in the constructor
|
41
48
|
@actor.init
|
@@ -60,33 +67,50 @@ module StatelyDB
|
|
60
67
|
# @param [String] audience The OAuth Audience for the token
|
61
68
|
# @param [String] client_secret The StatelyDB client secret credential
|
62
69
|
# @param [String] client_id The StatelyDB client ID credential
|
70
|
+
# @param [String] access_key The StatelyDB access key credential
|
71
|
+
# @param [Float] base_retry_backoff_secs The base retry backoff in seconds
|
63
72
|
def initialize(
|
64
73
|
origin:,
|
65
74
|
audience:,
|
66
75
|
client_secret:,
|
67
|
-
client_id
|
76
|
+
client_id:,
|
77
|
+
access_key:,
|
78
|
+
base_retry_backoff_secs:
|
68
79
|
)
|
69
80
|
super()
|
70
|
-
@client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(origin))
|
71
|
-
@client_id = client_id
|
72
|
-
@client_secret = client_secret
|
73
|
-
@audience = audience
|
74
81
|
|
75
|
-
@
|
76
|
-
|
82
|
+
@token_fetcher = nil
|
83
|
+
if !access_key.nil?
|
84
|
+
@token_fetcher = StatelyDB::Common::Auth::StatelyAccessTokenFetcher.new(
|
85
|
+
origin: origin, access_key: access_key, base_retry_backoff_secs: base_retry_backoff_secs
|
86
|
+
)
|
87
|
+
elsif !client_secret.nil? && !client_id.nil?
|
88
|
+
@token_fetcher = StatelyDB::Common::Auth::Auth0TokenFetcher.new(origin: origin, audience: audience,
|
89
|
+
client_secret: client_secret, client_id: client_id)
|
90
|
+
else
|
91
|
+
raise StatelyDB::Error.new("unable to find client credentials in STATELY_ACCESS_KEY or STATELY_CLIENT_ID and " \
|
92
|
+
"STATELY_CLIENT_SECRET environment variables. Either pass your credentials in " \
|
93
|
+
"explicitly or set these environment variables",
|
94
|
+
code: GRPC::Core::StatusCodes::UNAUTHENTICATED,
|
95
|
+
stately_code: "Unauthenticated")
|
96
|
+
end
|
97
|
+
|
98
|
+
@token_state = nil
|
77
99
|
@pending_refresh = nil
|
78
100
|
end
|
79
101
|
|
80
102
|
# Initialize the actor. This runs on the actor thread which means
|
81
103
|
# we can dispatch async operations here.
|
82
104
|
def init
|
105
|
+
# disable the async lib logger. We do our own error handling and propagation
|
106
|
+
Console.logger.disable(Async::Task)
|
83
107
|
refresh_token
|
84
108
|
end
|
85
109
|
|
86
110
|
# Close the token provider and kill any background operations
|
87
111
|
def close
|
88
112
|
@scheduled&.stop
|
89
|
-
@
|
113
|
+
@token_fetcher&.close
|
90
114
|
end
|
91
115
|
|
92
116
|
# Get the current access token
|
@@ -94,8 +118,7 @@ module StatelyDB
|
|
94
118
|
# @return [String] The current access token
|
95
119
|
def get_token(force: false)
|
96
120
|
if force
|
97
|
-
@
|
98
|
-
@expires_at_unix_secs = nil
|
121
|
+
@token_state = nil
|
99
122
|
else
|
100
123
|
token, ok = valid_access_token
|
101
124
|
return token if ok
|
@@ -107,11 +130,10 @@ module StatelyDB
|
|
107
130
|
# Get the current access token and whether it is valid
|
108
131
|
# @return [Array] The current access token and whether it is valid
|
109
132
|
def valid_access_token
|
110
|
-
return "", false if @
|
111
|
-
return "", false if @expires_at_unix_secs.
|
112
|
-
return "", false if @expires_at_unix_secs < Time.now.to_i
|
133
|
+
return "", false if @token_state.nil?
|
134
|
+
return "", false if @token_state.expires_at_unix_secs < Time.now.to_i
|
113
135
|
|
114
|
-
[@
|
136
|
+
[@token_state.token, true]
|
115
137
|
end
|
116
138
|
|
117
139
|
# Refresh the access token
|
@@ -151,19 +173,16 @@ module StatelyDB
|
|
151
173
|
# @return [String] The new access token
|
152
174
|
def refresh_token_impl
|
153
175
|
Sync do
|
154
|
-
|
155
|
-
|
156
|
-
new_access_token = resp_data["access_token"]
|
157
|
-
new_expires_in_secs = resp_data["expires_in"]
|
176
|
+
token_result = @token_fetcher.fetch
|
177
|
+
new_expires_in_secs = token_result.expires_in_secs
|
158
178
|
new_expires_at_unix_secs = Time.now.to_i + new_expires_in_secs
|
159
|
-
if @expires_at_unix_secs.nil? || new_expires_at_unix_secs > @expires_at_unix_secs
|
160
179
|
|
161
|
-
|
162
|
-
|
180
|
+
# only update the token state if the new expiry is later than the current one
|
181
|
+
if @token_state.nil? || new_expires_at_unix_secs > @token_state.expires_at_unix_secs
|
182
|
+
@token_state = TokenState.new(token: token_result.token, expires_at_unix_secs: new_expires_at_unix_secs)
|
163
183
|
else
|
164
|
-
|
165
|
-
|
166
|
-
new_expires_in_secs = @expires_at_unix_secs - Time.now.to_i
|
184
|
+
# otherwise use the existing expiry time for scheduling the refresh
|
185
|
+
new_expires_in_secs = @token_state.expires_at_unix_secs - Time.now.to_i
|
167
186
|
end
|
168
187
|
|
169
188
|
# Schedule a refresh of the token ahead of the expiry time
|
@@ -182,24 +201,21 @@ module StatelyDB
|
|
182
201
|
@scheduled = nil
|
183
202
|
end
|
184
203
|
|
185
|
-
|
204
|
+
@token_state.token
|
186
205
|
end
|
187
206
|
end
|
207
|
+
end
|
188
208
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
JSON.parse(response.read)
|
200
|
-
ensure
|
201
|
-
response&.close
|
202
|
-
end
|
209
|
+
# Persistent state for the token provider
|
210
|
+
class TokenState
|
211
|
+
attr_reader :token, :expires_at_unix_secs
|
212
|
+
|
213
|
+
# Create a new TokenState
|
214
|
+
# @param [String] token The access token
|
215
|
+
# @param [Integer] expires_at_unix_secs The unix timestamp when the token expires
|
216
|
+
def initialize(token:, expires_at_unix_secs:)
|
217
|
+
@token = token
|
218
|
+
@expires_at_unix_secs = expires_at_unix_secs
|
203
219
|
end
|
204
220
|
end
|
205
221
|
end
|
@@ -11,7 +11,7 @@ module StatelyDB
|
|
11
11
|
class Interceptor < GRPC::ClientInterceptor
|
12
12
|
# @param [TokenProvider] token_provider The token provider to use for authentication
|
13
13
|
def initialize(
|
14
|
-
token_provider:
|
14
|
+
token_provider: AuthTokenProvider.new
|
15
15
|
)
|
16
16
|
super()
|
17
17
|
@token_provider = token_provider
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../net/conn"
|
4
|
+
require_relative "../error_interceptor"
|
5
|
+
require_relative "../../api/auth/service_services_pb"
|
6
|
+
require "grpc"
|
7
|
+
|
8
|
+
module StatelyDB
|
9
|
+
module Common
|
10
|
+
# A module for Stately Cloud auth code
|
11
|
+
module Auth
|
12
|
+
# Result from a token fetch operation
|
13
|
+
class TokenResult
|
14
|
+
attr_reader :token, :expires_in_secs
|
15
|
+
|
16
|
+
# Create a new TokenResult
|
17
|
+
# @param [String] token The access token
|
18
|
+
# @param [Integer] expires_in_secs The number of seconds until the token expires
|
19
|
+
def initialize(token:, expires_in_secs:)
|
20
|
+
@token = token
|
21
|
+
@expires_in_secs = expires_in_secs
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# TokenFetcher is an abstract base class that should be extended
|
26
|
+
# for individual token fetcher implementations
|
27
|
+
class TokenFetcher
|
28
|
+
# Get the current access token
|
29
|
+
# @return [TokenResult] The fetched TokenResult
|
30
|
+
def fetch
|
31
|
+
raise "Not Implemented"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Close the token provider and kill any background operations
|
35
|
+
def close
|
36
|
+
raise "Not Implemented"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Auth0TokenFetcher is a TokenFetcher that fetches tokens from an Auth0 server
|
41
|
+
class Auth0TokenFetcher < TokenFetcher
|
42
|
+
# @param [String] origin The origin of the OAuth server
|
43
|
+
# @param [String] audience The OAuth Audience for the token
|
44
|
+
# @param [String] client_secret The StatelyDB client secret credential
|
45
|
+
# @param [String] client_id The StatelyDB client ID credential
|
46
|
+
def initialize(origin:, audience:, client_secret:, client_id:)
|
47
|
+
super()
|
48
|
+
@client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(origin))
|
49
|
+
@audience = audience
|
50
|
+
@client_secret = client_secret
|
51
|
+
@client_id = client_id
|
52
|
+
end
|
53
|
+
|
54
|
+
# Fetch a new token from auth0
|
55
|
+
# @return [TokenResult] The fetched TokenResult
|
56
|
+
def fetch
|
57
|
+
headers = [["content-type", "application/json"]]
|
58
|
+
body = JSON.dump({ "client_id" => @client_id, client_secret: @client_secret, audience: @audience,
|
59
|
+
grant_type: DEFAULT_GRANT_TYPE })
|
60
|
+
Sync do
|
61
|
+
response = @client.post("/oauth/token", headers, body)
|
62
|
+
raise "Auth request failed" if response.status != 200
|
63
|
+
|
64
|
+
resp_data = JSON.parse(response.read)
|
65
|
+
TokenResult.new(token: resp_data["access_token"], expires_in_secs: resp_data["expires_in"])
|
66
|
+
ensure
|
67
|
+
response&.close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def close
|
72
|
+
@client&.close
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
|
77
|
+
class StatelyAccessTokenFetcher < TokenFetcher
|
78
|
+
NON_RETRYABLE_ERRORS = [
|
79
|
+
GRPC::Core::StatusCodes::UNAUTHENTICATED,
|
80
|
+
GRPC::Core::StatusCodes::PERMISSION_DENIED,
|
81
|
+
GRPC::Core::StatusCodes::NOT_FOUND,
|
82
|
+
GRPC::Core::StatusCodes::UNIMPLEMENTED,
|
83
|
+
GRPC::Core::StatusCodes::INVALID_ARGUMENT
|
84
|
+
].freeze
|
85
|
+
RETRY_ATTEMPTS = 10
|
86
|
+
|
87
|
+
# @param [String] origin The origin of the OAuth server
|
88
|
+
# @param [String] access_key The StatelyDB access key credential
|
89
|
+
# @param [Float] base_retry_backoff_secs The base backoff time in seconds
|
90
|
+
def initialize(origin:, access_key:, base_retry_backoff_secs:)
|
91
|
+
super()
|
92
|
+
@access_key = access_key
|
93
|
+
@base_retry_backoff_secs = base_retry_backoff_secs
|
94
|
+
@channel = Common::Net.new_channel(endpoint: origin)
|
95
|
+
error_interceptor = Common::ErrorInterceptor.new
|
96
|
+
@stub = Stately::Auth::AuthService::Stub.new(nil, nil, channel_override: @channel,
|
97
|
+
interceptors: [error_interceptor])
|
98
|
+
end
|
99
|
+
|
100
|
+
# Fetch a new token from the StatelyDB API
|
101
|
+
# @return [TokenResult] The fetched TokenResult
|
102
|
+
def fetch
|
103
|
+
RETRY_ATTEMPTS.times do |i|
|
104
|
+
resp = @stub.get_auth_token(Stately::Auth::GetAuthTokenRequest.new(access_key: @access_key))
|
105
|
+
return TokenResult.new(token: resp.auth_token, expires_in_secs: resp.expires_in_s)
|
106
|
+
rescue StatelyDB::Error => e
|
107
|
+
# raise if it's the final attempt or if the error is not retryable
|
108
|
+
raise e unless self.class.retryable_error?(e) && i < RETRY_ATTEMPTS - 1
|
109
|
+
|
110
|
+
# exponential backoff
|
111
|
+
sleep(backoff(i, @base_retry_backoff_secs))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def close
|
116
|
+
@channel&.close
|
117
|
+
end
|
118
|
+
|
119
|
+
# Check if an error is retryable
|
120
|
+
# @param [StatelyDB::Error] err The error to check
|
121
|
+
# @return [Boolean] True if the error is retryable
|
122
|
+
def self.retryable_error?(err)
|
123
|
+
!NON_RETRYABLE_ERRORS.include?(err.code)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# backoff returns a duration to wait before retrying a request. `attempt` is
|
131
|
+
# the current attempt number, starting from 0 (e.g. the first attempt is 0,
|
132
|
+
# then 1, then 2...).
|
133
|
+
#
|
134
|
+
# @param [Integer] attempt The current attempt number
|
135
|
+
# @param [Float] base_backoff The base backoff time in seconds
|
136
|
+
# @return [Float] The duration in seconds to wait before retrying
|
137
|
+
def backoff(attempt, base_backoff)
|
138
|
+
# Double the base backoff time per attempt, starting with 1
|
139
|
+
exp = 2**attempt
|
140
|
+
# Add a full jitter to the backoff time, from no wait to 100% of the exponential backoff.
|
141
|
+
# See https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
|
142
|
+
jitter = rand
|
143
|
+
(exp * jitter * base_backoff)
|
144
|
+
end
|
data/lib/statelydb.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "api/db/service_services_pb"
|
4
|
-
require "common/auth/
|
4
|
+
require "common/auth/auth_token_provider"
|
5
5
|
require "common/auth/interceptor"
|
6
6
|
require "common/net/conn"
|
7
7
|
require "common/error_interceptor"
|
@@ -30,7 +30,7 @@ module StatelyDB
|
|
30
30
|
# @param region [String] the region to connect to.
|
31
31
|
def initialize(store_id:,
|
32
32
|
schema:,
|
33
|
-
token_provider: Common::Auth::
|
33
|
+
token_provider: Common::Auth::AuthTokenProvider.new,
|
34
34
|
endpoint: nil,
|
35
35
|
region: nil)
|
36
36
|
if store_id.nil?
|
data/sig/statelydb.rbi
CHANGED
@@ -213,7 +213,7 @@ module StatelyDB
|
|
213
213
|
region: T.nilable(String)
|
214
214
|
).void
|
215
215
|
end
|
216
|
-
def initialize(store_id:, schema:, token_provider: Common::Auth::
|
216
|
+
def initialize(store_id:, schema:, token_provider: Common::Auth::AuthTokenProvider.new, endpoint: nil, region: nil); end
|
217
217
|
|
218
218
|
# _@return_ — nil
|
219
219
|
sig { void }
|
@@ -479,7 +479,7 @@ module StatelyDB
|
|
479
479
|
class Interceptor < GRPC::ClientInterceptor
|
480
480
|
# _@param_ `token_provider` — The token provider to use for authentication
|
481
481
|
sig { params(token_provider: TokenProvider).void }
|
482
|
-
def initialize(token_provider:
|
482
|
+
def initialize(token_provider: AuthTokenProvider.new); end
|
483
483
|
|
484
484
|
# gRPC client unary interceptor
|
485
485
|
#
|
@@ -572,6 +572,105 @@ module StatelyDB
|
|
572
572
|
def add_jwt_to_grpc_request(metadata:); end
|
573
573
|
end
|
574
574
|
|
575
|
+
# Result from a token fetch operation
|
576
|
+
class TokenResult
|
577
|
+
# Create a new TokenResult
|
578
|
+
#
|
579
|
+
# _@param_ `token` — The access token
|
580
|
+
#
|
581
|
+
# _@param_ `expires_in_secs` — The number of seconds until the token expires
|
582
|
+
sig { params(token: String, expires_in_secs: Integer).void }
|
583
|
+
def initialize(token:, expires_in_secs:); end
|
584
|
+
|
585
|
+
# Returns the value of attribute token.
|
586
|
+
sig { returns(T.untyped) }
|
587
|
+
attr_reader :token
|
588
|
+
|
589
|
+
# Returns the value of attribute expires_in_secs.
|
590
|
+
sig { returns(T.untyped) }
|
591
|
+
attr_reader :expires_in_secs
|
592
|
+
end
|
593
|
+
|
594
|
+
# TokenFetcher is an abstract base class that should be extended
|
595
|
+
# for individual token fetcher implementations
|
596
|
+
class TokenFetcher
|
597
|
+
# Get the current access token
|
598
|
+
#
|
599
|
+
# _@return_ — The fetched TokenResult
|
600
|
+
sig { returns(TokenResult) }
|
601
|
+
def fetch; end
|
602
|
+
|
603
|
+
# Close the token provider and kill any background operations
|
604
|
+
sig { returns(T.untyped) }
|
605
|
+
def close; end
|
606
|
+
end
|
607
|
+
|
608
|
+
# Auth0TokenFetcher is a TokenFetcher that fetches tokens from an Auth0 server
|
609
|
+
class Auth0TokenFetcher < StatelyDB::Common::Auth::TokenFetcher
|
610
|
+
# _@param_ `origin` — The origin of the OAuth server
|
611
|
+
#
|
612
|
+
# _@param_ `audience` — The OAuth Audience for the token
|
613
|
+
#
|
614
|
+
# _@param_ `client_secret` — The StatelyDB client secret credential
|
615
|
+
#
|
616
|
+
# _@param_ `client_id` — The StatelyDB client ID credential
|
617
|
+
sig do
|
618
|
+
params(
|
619
|
+
origin: String,
|
620
|
+
audience: String,
|
621
|
+
client_secret: String,
|
622
|
+
client_id: String
|
623
|
+
).void
|
624
|
+
end
|
625
|
+
def initialize(origin:, audience:, client_secret:, client_id:); end
|
626
|
+
|
627
|
+
# Fetch a new token from auth0
|
628
|
+
#
|
629
|
+
# _@return_ — The fetched TokenResult
|
630
|
+
sig { returns(TokenResult) }
|
631
|
+
def fetch; end
|
632
|
+
|
633
|
+
sig { returns(T.untyped) }
|
634
|
+
def close; end
|
635
|
+
end
|
636
|
+
|
637
|
+
# StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
|
638
|
+
class StatelyAccessTokenFetcher < StatelyDB::Common::Auth::TokenFetcher
|
639
|
+
NON_RETRYABLE_ERRORS = T.let([
|
640
|
+
GRPC::Core::StatusCodes::UNAUTHENTICATED,
|
641
|
+
GRPC::Core::StatusCodes::PERMISSION_DENIED,
|
642
|
+
GRPC::Core::StatusCodes::NOT_FOUND,
|
643
|
+
GRPC::Core::StatusCodes::UNIMPLEMENTED,
|
644
|
+
GRPC::Core::StatusCodes::INVALID_ARGUMENT
|
645
|
+
].freeze, T.untyped)
|
646
|
+
RETRY_ATTEMPTS = T.let(10, T.untyped)
|
647
|
+
|
648
|
+
# _@param_ `origin` — The origin of the OAuth server
|
649
|
+
#
|
650
|
+
# _@param_ `access_key` — The StatelyDB access key credential
|
651
|
+
#
|
652
|
+
# _@param_ `base_retry_backoff_secs` — The base backoff time in seconds
|
653
|
+
sig { params(origin: String, access_key: String, base_retry_backoff_secs: Float).void }
|
654
|
+
def initialize(origin:, access_key:, base_retry_backoff_secs:); end
|
655
|
+
|
656
|
+
# Fetch a new token from the StatelyDB API
|
657
|
+
#
|
658
|
+
# _@return_ — The fetched TokenResult
|
659
|
+
sig { returns(TokenResult) }
|
660
|
+
def fetch; end
|
661
|
+
|
662
|
+
sig { returns(T.untyped) }
|
663
|
+
def close; end
|
664
|
+
|
665
|
+
# Check if an error is retryable
|
666
|
+
#
|
667
|
+
# _@param_ `err` — The error to check
|
668
|
+
#
|
669
|
+
# _@return_ — True if the error is retryable
|
670
|
+
sig { params(err: StatelyDB::Error).returns(T::Boolean) }
|
671
|
+
def self.retryable_error?(err); end
|
672
|
+
end
|
673
|
+
|
575
674
|
# TokenProvider is an abstract base class that should be extended
|
576
675
|
# for individual token provider implementations
|
577
676
|
class TokenProvider
|
@@ -592,7 +691,7 @@ module StatelyDB
|
|
592
691
|
# which vends tokens from auth0 with the given client_id and client_secret.
|
593
692
|
# It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
|
594
693
|
# no credentials are explicitly passed and will throw an error if none are found.
|
595
|
-
class
|
694
|
+
class AuthTokenProvider < StatelyDB::Common::Auth::TokenProvider
|
596
695
|
# _@param_ `origin` — The origin of the OAuth server
|
597
696
|
#
|
598
697
|
# _@param_ `audience` — The OAuth Audience for the token
|
@@ -600,15 +699,21 @@ module StatelyDB
|
|
600
699
|
# _@param_ `client_secret` — The StatelyDB client secret credential
|
601
700
|
#
|
602
701
|
# _@param_ `client_id` — The StatelyDB client ID credential
|
702
|
+
#
|
703
|
+
# _@param_ `access_key` — The StatelyDB access key credential
|
704
|
+
#
|
705
|
+
# _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
|
603
706
|
sig do
|
604
707
|
params(
|
605
708
|
origin: String,
|
606
709
|
audience: String,
|
607
710
|
client_secret: String,
|
608
|
-
client_id: String
|
711
|
+
client_id: String,
|
712
|
+
access_key: String,
|
713
|
+
base_retry_backoff_secs: Float
|
609
714
|
).void
|
610
715
|
end
|
611
|
-
def initialize(origin: "https://oauth.stately.cloud", audience: "api.stately.cloud", client_secret: ENV.fetch("STATELY_CLIENT_SECRET"), client_id: ENV.fetch("STATELY_CLIENT_ID")); end
|
716
|
+
def initialize(origin: "https://oauth.stately.cloud", audience: "api.stately.cloud", client_secret: ENV.fetch("STATELY_CLIENT_SECRET", nil), client_id: ENV.fetch("STATELY_CLIENT_ID", nil), access_key: ENV.fetch("STATELY_ACCESS_KEY", nil), base_retry_backoff_secs: 1); end
|
612
717
|
|
613
718
|
# Close the token provider and kill any background operations
|
614
719
|
# This just invokes the close method on the actor which should do the cleanup
|
@@ -631,15 +736,21 @@ module StatelyDB
|
|
631
736
|
# _@param_ `client_secret` — The StatelyDB client secret credential
|
632
737
|
#
|
633
738
|
# _@param_ `client_id` — The StatelyDB client ID credential
|
739
|
+
#
|
740
|
+
# _@param_ `access_key` — The StatelyDB access key credential
|
741
|
+
#
|
742
|
+
# _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
|
634
743
|
sig do
|
635
744
|
params(
|
636
745
|
origin: String,
|
637
746
|
audience: String,
|
638
747
|
client_secret: String,
|
639
|
-
client_id: String
|
748
|
+
client_id: String,
|
749
|
+
access_key: String,
|
750
|
+
base_retry_backoff_secs: Float
|
640
751
|
).void
|
641
752
|
end
|
642
|
-
def initialize(origin:, audience:, client_secret:, client_id:); end
|
753
|
+
def initialize(origin:, audience:, client_secret:, client_id:, access_key:, base_retry_backoff_secs:); end
|
643
754
|
|
644
755
|
# Initialize the actor. This runs on the actor thread which means
|
645
756
|
# we can dispatch async operations here.
|
@@ -675,9 +786,25 @@ module StatelyDB
|
|
675
786
|
# _@return_ — The new access token
|
676
787
|
sig { returns(String) }
|
677
788
|
def refresh_token_impl; end
|
789
|
+
end
|
790
|
+
|
791
|
+
# Persistent state for the token provider
|
792
|
+
class TokenState
|
793
|
+
# Create a new TokenState
|
794
|
+
#
|
795
|
+
# _@param_ `token` — The access token
|
796
|
+
#
|
797
|
+
# _@param_ `expires_at_unix_secs` — The unix timestamp when the token expires
|
798
|
+
sig { params(token: String, expires_at_unix_secs: Integer).void }
|
799
|
+
def initialize(token:, expires_at_unix_secs:); end
|
800
|
+
|
801
|
+
# Returns the value of attribute token.
|
802
|
+
sig { returns(T.untyped) }
|
803
|
+
attr_reader :token
|
678
804
|
|
805
|
+
# Returns the value of attribute expires_at_unix_secs.
|
679
806
|
sig { returns(T.untyped) }
|
680
|
-
|
807
|
+
attr_reader :expires_at_unix_secs
|
681
808
|
end
|
682
809
|
end
|
683
810
|
end
|
@@ -1069,6 +1196,22 @@ module Stately
|
|
1069
1196
|
end
|
1070
1197
|
end
|
1071
1198
|
|
1199
|
+
module Auth
|
1200
|
+
GetAuthTokenRequest = T.let(::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.auth.GetAuthTokenRequest").msgclass, T.untyped)
|
1201
|
+
GetAuthTokenResponse = T.let(::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.auth.GetAuthTokenResponse").msgclass, T.untyped)
|
1202
|
+
|
1203
|
+
module AuthService
|
1204
|
+
Stub = T.let(Service.rpc_stub_class, T.untyped)
|
1205
|
+
|
1206
|
+
# AuthService is the service for vending access tokens used to connect to
|
1207
|
+
# StatelyDB. This API is meant to be used from SDKs. Access Keys are created
|
1208
|
+
# and managed from the stately.dbmanagement.UserService.
|
1209
|
+
class Service
|
1210
|
+
include GRPC::GenericService
|
1211
|
+
end
|
1212
|
+
end
|
1213
|
+
end
|
1214
|
+
|
1072
1215
|
module Errors
|
1073
1216
|
StatelyErrorDetails = T.let(::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.errors.StatelyErrorDetails").msgclass, T.untyped)
|
1074
1217
|
end
|
data/sig/statelydb.rbs
CHANGED
@@ -496,6 +496,85 @@ module StatelyDB
|
|
496
496
|
def add_jwt_to_grpc_request: (metadata: ::Hash[untyped, untyped]) -> void
|
497
497
|
end
|
498
498
|
|
499
|
+
# Result from a token fetch operation
|
500
|
+
class TokenResult
|
501
|
+
# Create a new TokenResult
|
502
|
+
#
|
503
|
+
# _@param_ `token` — The access token
|
504
|
+
#
|
505
|
+
# _@param_ `expires_in_secs` — The number of seconds until the token expires
|
506
|
+
def initialize: (token: String, expires_in_secs: Integer) -> void
|
507
|
+
|
508
|
+
# Returns the value of attribute token.
|
509
|
+
attr_reader token: untyped
|
510
|
+
|
511
|
+
# Returns the value of attribute expires_in_secs.
|
512
|
+
attr_reader expires_in_secs: untyped
|
513
|
+
end
|
514
|
+
|
515
|
+
# TokenFetcher is an abstract base class that should be extended
|
516
|
+
# for individual token fetcher implementations
|
517
|
+
class TokenFetcher
|
518
|
+
# Get the current access token
|
519
|
+
#
|
520
|
+
# _@return_ — The fetched TokenResult
|
521
|
+
def fetch: () -> TokenResult
|
522
|
+
|
523
|
+
# Close the token provider and kill any background operations
|
524
|
+
def close: () -> untyped
|
525
|
+
end
|
526
|
+
|
527
|
+
# Auth0TokenFetcher is a TokenFetcher that fetches tokens from an Auth0 server
|
528
|
+
class Auth0TokenFetcher < StatelyDB::Common::Auth::TokenFetcher
|
529
|
+
# _@param_ `origin` — The origin of the OAuth server
|
530
|
+
#
|
531
|
+
# _@param_ `audience` — The OAuth Audience for the token
|
532
|
+
#
|
533
|
+
# _@param_ `client_secret` — The StatelyDB client secret credential
|
534
|
+
#
|
535
|
+
# _@param_ `client_id` — The StatelyDB client ID credential
|
536
|
+
def initialize: (
|
537
|
+
origin: String,
|
538
|
+
audience: String,
|
539
|
+
client_secret: String,
|
540
|
+
client_id: String
|
541
|
+
) -> void
|
542
|
+
|
543
|
+
# Fetch a new token from auth0
|
544
|
+
#
|
545
|
+
# _@return_ — The fetched TokenResult
|
546
|
+
def fetch: () -> TokenResult
|
547
|
+
|
548
|
+
def close: () -> untyped
|
549
|
+
end
|
550
|
+
|
551
|
+
# StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
|
552
|
+
class StatelyAccessTokenFetcher < StatelyDB::Common::Auth::TokenFetcher
|
553
|
+
NON_RETRYABLE_ERRORS: untyped
|
554
|
+
RETRY_ATTEMPTS: untyped
|
555
|
+
|
556
|
+
# _@param_ `origin` — The origin of the OAuth server
|
557
|
+
#
|
558
|
+
# _@param_ `access_key` — The StatelyDB access key credential
|
559
|
+
#
|
560
|
+
# _@param_ `base_retry_backoff_secs` — The base backoff time in seconds
|
561
|
+
def initialize: (origin: String, access_key: String, base_retry_backoff_secs: Float) -> void
|
562
|
+
|
563
|
+
# Fetch a new token from the StatelyDB API
|
564
|
+
#
|
565
|
+
# _@return_ — The fetched TokenResult
|
566
|
+
def fetch: () -> TokenResult
|
567
|
+
|
568
|
+
def close: () -> untyped
|
569
|
+
|
570
|
+
# Check if an error is retryable
|
571
|
+
#
|
572
|
+
# _@param_ `err` — The error to check
|
573
|
+
#
|
574
|
+
# _@return_ — True if the error is retryable
|
575
|
+
def self.retryable_error?: (StatelyDB::Error err) -> bool
|
576
|
+
end
|
577
|
+
|
499
578
|
# TokenProvider is an abstract base class that should be extended
|
500
579
|
# for individual token provider implementations
|
501
580
|
class TokenProvider
|
@@ -514,7 +593,7 @@ module StatelyDB
|
|
514
593
|
# which vends tokens from auth0 with the given client_id and client_secret.
|
515
594
|
# It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
|
516
595
|
# no credentials are explicitly passed and will throw an error if none are found.
|
517
|
-
class
|
596
|
+
class AuthTokenProvider < StatelyDB::Common::Auth::TokenProvider
|
518
597
|
# _@param_ `origin` — The origin of the OAuth server
|
519
598
|
#
|
520
599
|
# _@param_ `audience` — The OAuth Audience for the token
|
@@ -522,11 +601,17 @@ module StatelyDB
|
|
522
601
|
# _@param_ `client_secret` — The StatelyDB client secret credential
|
523
602
|
#
|
524
603
|
# _@param_ `client_id` — The StatelyDB client ID credential
|
604
|
+
#
|
605
|
+
# _@param_ `access_key` — The StatelyDB access key credential
|
606
|
+
#
|
607
|
+
# _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
|
525
608
|
def initialize: (
|
526
609
|
?origin: String,
|
527
610
|
?audience: String,
|
528
611
|
?client_secret: String,
|
529
|
-
?client_id: String
|
612
|
+
?client_id: String,
|
613
|
+
?access_key: String,
|
614
|
+
?base_retry_backoff_secs: Float
|
530
615
|
) -> void
|
531
616
|
|
532
617
|
# Close the token provider and kill any background operations
|
@@ -548,11 +633,17 @@ module StatelyDB
|
|
548
633
|
# _@param_ `client_secret` — The StatelyDB client secret credential
|
549
634
|
#
|
550
635
|
# _@param_ `client_id` — The StatelyDB client ID credential
|
636
|
+
#
|
637
|
+
# _@param_ `access_key` — The StatelyDB access key credential
|
638
|
+
#
|
639
|
+
# _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
|
551
640
|
def initialize: (
|
552
641
|
origin: String,
|
553
642
|
audience: String,
|
554
643
|
client_secret: String,
|
555
|
-
client_id: String
|
644
|
+
client_id: String,
|
645
|
+
access_key: String,
|
646
|
+
base_retry_backoff_secs: Float
|
556
647
|
) -> void
|
557
648
|
|
558
649
|
# Initialize the actor. This runs on the actor thread which means
|
@@ -583,8 +674,22 @@ module StatelyDB
|
|
583
674
|
#
|
584
675
|
# _@return_ — The new access token
|
585
676
|
def refresh_token_impl: () -> String
|
677
|
+
end
|
678
|
+
|
679
|
+
# Persistent state for the token provider
|
680
|
+
class TokenState
|
681
|
+
# Create a new TokenState
|
682
|
+
#
|
683
|
+
# _@param_ `token` — The access token
|
684
|
+
#
|
685
|
+
# _@param_ `expires_at_unix_secs` — The unix timestamp when the token expires
|
686
|
+
def initialize: (token: String, expires_at_unix_secs: Integer) -> void
|
586
687
|
|
587
|
-
|
688
|
+
# Returns the value of attribute token.
|
689
|
+
attr_reader token: untyped
|
690
|
+
|
691
|
+
# Returns the value of attribute expires_at_unix_secs.
|
692
|
+
attr_reader expires_at_unix_secs: untyped
|
588
693
|
end
|
589
694
|
end
|
590
695
|
end
|
@@ -936,6 +1041,22 @@ module Stately
|
|
936
1041
|
end
|
937
1042
|
end
|
938
1043
|
|
1044
|
+
module Auth
|
1045
|
+
GetAuthTokenRequest: untyped
|
1046
|
+
GetAuthTokenResponse: untyped
|
1047
|
+
|
1048
|
+
module AuthService
|
1049
|
+
Stub: untyped
|
1050
|
+
|
1051
|
+
# AuthService is the service for vending access tokens used to connect to
|
1052
|
+
# StatelyDB. This API is meant to be used from SDKs. Access Keys are created
|
1053
|
+
# and managed from the stately.dbmanagement.UserService.
|
1054
|
+
class Service
|
1055
|
+
include GRPC::GenericService
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
939
1060
|
module Errors
|
940
1061
|
StatelyErrorDetails: untyped
|
941
1062
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statelydb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stately Cloud, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -73,6 +73,9 @@ extensions: []
|
|
73
73
|
extra_rdoc_files: []
|
74
74
|
files:
|
75
75
|
- README.md
|
76
|
+
- lib/api/auth/get_auth_token_pb.rb
|
77
|
+
- lib/api/auth/service_pb.rb
|
78
|
+
- lib/api/auth/service_services_pb.rb
|
76
79
|
- lib/api/db/continue_list_pb.rb
|
77
80
|
- lib/api/db/delete_pb.rb
|
78
81
|
- lib/api/db/get_pb.rb
|
@@ -87,8 +90,9 @@ files:
|
|
87
90
|
- lib/api/db/sync_list_pb.rb
|
88
91
|
- lib/api/db/transaction_pb.rb
|
89
92
|
- lib/api/errors/error_details_pb.rb
|
90
|
-
- lib/common/auth/
|
93
|
+
- lib/common/auth/auth_token_provider.rb
|
91
94
|
- lib/common/auth/interceptor.rb
|
95
|
+
- lib/common/auth/token_fetcher.rb
|
92
96
|
- lib/common/auth/token_provider.rb
|
93
97
|
- lib/common/error_interceptor.rb
|
94
98
|
- lib/common/net/conn.rb
|