statelydb 0.13.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|