statelydb 0.12.2 → 0.14.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/api/db/get_pb.rb +1 -1
- data/lib/api/db/list_pb.rb +3 -3
- data/lib/api/db/put_pb.rb +1 -1
- data/lib/api/db/service_pb.rb +8 -8
- data/lib/api/db/service_services_pb.rb +1 -1
- data/lib/api/db/sync_list_pb.rb +2 -2
- data/lib/api/db/transaction_pb.rb +7 -7
- data/lib/common/auth/{auth0_token_provider.rb → auth_token_provider.rb} +52 -44
- data/lib/common/auth/interceptor.rb +1 -1
- data/lib/common/auth/token_fetcher.rb +104 -0
- data/lib/error.rb +0 -5
- data/lib/statelydb.rb +3 -7
- data/lib/token.rb +2 -3
- data/lib/transaction/transaction.rb +1 -1
- data/sig/grpc.rbs +80 -0
- data/sig/statelydb.rbi +1201 -0
- data/sig/statelydb.rbs +1053 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a723737fdce6e8ad2bec8937cfd79f6d968df0fc3e9e747c64e18af9ce769601
|
4
|
+
data.tar.gz: 1f0279a8b9507f4cffacc360ab82e2289471a40de39bd3891f0c7db5c63877c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 815835070a91366b18948afe145eb548ae4f6ecbc6559ed3b8193288cd1084b7ac1a416264b0769afea93f4a8c01e17b0474facb46fe90460b0b3db803466428
|
7
|
+
data.tar.gz: e0c75f31911432f2544c4097fbc0c7a1615624257e80495aef9e5b3a0dddc92dd9341683d24416c4a32ab19b8bd34a2d79abb577781d75ee41b55a02f2e2e11c
|
@@ -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
|
data/lib/api/db/get_pb.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
require 'google/protobuf'
|
6
6
|
|
7
|
-
require 'db/item_pb'
|
7
|
+
require 'api/db/item_pb'
|
8
8
|
|
9
9
|
|
10
10
|
descriptor_data = "\n\x0c\x64\x62/get.proto\x12\nstately.db\x1a\rdb/item.proto\"\xa3\x01\n\nGetRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12\'\n\x04gets\x18\x02 \x03(\x0b\x32\x13.stately.db.GetItemR\x04gets\x12\x1f\n\x0b\x61llow_stale\x18\x03 \x01(\x08R\nallowStale\x12*\n\x11schema_version_id\x18\x05 \x01(\rR\x0fschemaVersionIdJ\x04\x08\x04\x10\x05\"$\n\x07GetItem\x12\x19\n\x08key_path\x18\x01 \x01(\tR\x07keyPath\"5\n\x0bGetResponse\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05itemsBc\n\x0e\x63om.stately.dbB\x08GetProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
|
data/lib/api/db/list_pb.rb
CHANGED
@@ -4,9 +4,9 @@
|
|
4
4
|
|
5
5
|
require 'google/protobuf'
|
6
6
|
|
7
|
-
require 'db/item_pb'
|
8
|
-
require 'db/item_property_pb'
|
9
|
-
require 'db/list_token_pb'
|
7
|
+
require 'api/db/item_pb'
|
8
|
+
require 'api/db/item_property_pb'
|
9
|
+
require 'api/db/list_token_pb'
|
10
10
|
|
11
11
|
|
12
12
|
descriptor_data = "\n\rdb/list.proto\x12\nstately.db\x1a\rdb/item.proto\x1a\x16\x64\x62/item_property.proto\x1a\x13\x64\x62/list_token.proto\"\xbd\x02\n\x10\x42\x65ginListRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12&\n\x0fkey_path_prefix\x18\x02 \x01(\tR\rkeyPathPrefix\x12\x14\n\x05limit\x18\x03 \x01(\rR\x05limit\x12\x1f\n\x0b\x61llow_stale\x18\x04 \x01(\x08R\nallowStale\x12\x41\n\rsort_property\x18\x05 \x01(\x0e\x32\x1c.stately.db.SortablePropertyR\x0csortProperty\x12@\n\x0esort_direction\x18\x06 \x01(\x0e\x32\x19.stately.db.SortDirectionR\rsortDirection\x12*\n\x11schema_version_id\x18\x07 \x01(\rR\x0fschemaVersionId\"\x8b\x01\n\x0cListResponse\x12\x37\n\x06result\x18\x01 \x01(\x0b\x32\x1d.stately.db.ListPartialResultH\x00R\x06result\x12\x36\n\x08\x66inished\x18\x02 \x01(\x0b\x32\x18.stately.db.ListFinishedH\x00R\x08\x66inishedB\n\n\x08response\";\n\x11ListPartialResult\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05items\";\n\x0cListFinished\x12+\n\x05token\x18\x01 \x01(\x0b\x32\x15.stately.db.ListTokenR\x05token*8\n\rSortDirection\x12\x12\n\x0eSORT_ASCENDING\x10\x00\x12\x13\n\x0fSORT_DESCENDING\x10\x01\x42\x64\n\x0e\x63om.stately.dbB\tListProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
|
data/lib/api/db/put_pb.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
require 'google/protobuf'
|
6
6
|
|
7
|
-
require 'db/item_pb'
|
7
|
+
require 'api/db/item_pb'
|
8
8
|
|
9
9
|
|
10
10
|
descriptor_data = "\n\x0c\x64\x62/put.proto\x12\nstately.db\x1a\rdb/item.proto\"|\n\nPutRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12\'\n\x04puts\x18\x02 \x03(\x0b\x32\x13.stately.db.PutItemR\x04puts\x12*\n\x11schema_version_id\x18\x03 \x01(\rR\x0fschemaVersionId\"U\n\x07PutItem\x12$\n\x04item\x18\x01 \x01(\x0b\x32\x10.stately.db.ItemR\x04item\x12$\n\x0emust_not_exist\x18\x03 \x01(\x08R\x0cmustNotExist\"5\n\x0bPutResponse\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05itemsBc\n\x0e\x63om.stately.dbB\x08PutProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
|
data/lib/api/db/service_pb.rb
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
|
5
5
|
require 'google/protobuf'
|
6
6
|
|
7
|
-
require 'db/continue_list_pb'
|
8
|
-
require 'db/delete_pb'
|
9
|
-
require 'db/get_pb'
|
10
|
-
require 'db/list_pb'
|
11
|
-
require 'db/put_pb'
|
12
|
-
require 'db/scan_root_paths_pb'
|
13
|
-
require 'db/sync_list_pb'
|
14
|
-
require 'db/transaction_pb'
|
7
|
+
require 'api/db/continue_list_pb'
|
8
|
+
require 'api/db/delete_pb'
|
9
|
+
require 'api/db/get_pb'
|
10
|
+
require 'api/db/list_pb'
|
11
|
+
require 'api/db/put_pb'
|
12
|
+
require 'api/db/scan_root_paths_pb'
|
13
|
+
require 'api/db/sync_list_pb'
|
14
|
+
require 'api/db/transaction_pb'
|
15
15
|
|
16
16
|
|
17
17
|
descriptor_data = "\n\x10\x64\x62/service.proto\x12\nstately.db\x1a\x16\x64\x62/continue_list.proto\x1a\x0f\x64\x62/delete.proto\x1a\x0c\x64\x62/get.proto\x1a\rdb/list.proto\x1a\x0c\x64\x62/put.proto\x1a\x18\x64\x62/scan_root_paths.proto\x1a\x12\x64\x62/sync_list.proto\x1a\x14\x64\x62/transaction.proto2\xee\x04\n\x0f\x44\x61tabaseService\x12;\n\x03Put\x12\x16.stately.db.PutRequest\x1a\x17.stately.db.PutResponse\"\x03\x90\x02\x02\x12;\n\x03Get\x12\x16.stately.db.GetRequest\x1a\x17.stately.db.GetResponse\"\x03\x90\x02\x01\x12\x44\n\x06\x44\x65lete\x12\x19.stately.db.DeleteRequest\x1a\x1a.stately.db.DeleteResponse\"\x03\x90\x02\x02\x12J\n\tBeginList\x12\x1c.stately.db.BeginListRequest\x1a\x18.stately.db.ListResponse\"\x03\x90\x02\x01\x30\x01\x12P\n\x0c\x43ontinueList\x12\x1f.stately.db.ContinueListRequest\x1a\x18.stately.db.ListResponse\"\x03\x90\x02\x01\x30\x01\x12L\n\x08SyncList\x12\x1b.stately.db.SyncListRequest\x1a\x1c.stately.db.SyncListResponse\"\x03\x90\x02\x01\x30\x01\x12T\n\x0bTransaction\x12\x1e.stately.db.TransactionRequest\x1a\x1f.stately.db.TransactionResponse\"\x00(\x01\x30\x01\x12Y\n\rScanRootPaths\x12 .stately.db.ScanRootPathsRequest\x1a!.stately.db.ScanRootPathsResponse\"\x03\x90\x02\x01\x42g\n\x0e\x63om.stately.dbB\x0cServiceProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
|
data/lib/api/db/sync_list_pb.rb
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
|
5
5
|
require 'google/protobuf'
|
6
6
|
|
7
|
-
require 'db/item_pb'
|
8
|
-
require 'db/list_pb'
|
7
|
+
require 'api/db/item_pb'
|
8
|
+
require 'api/db/list_pb'
|
9
9
|
|
10
10
|
|
11
11
|
descriptor_data = "\n\x12\x64\x62/sync_list.proto\x12\nstately.db\x1a\rdb/item.proto\x1a\rdb/list.proto\"\\\n\x0fSyncListRequest\x12\x1d\n\ntoken_data\x18\x01 \x01(\x0cR\ttokenData\x12*\n\x11schema_version_id\x18\x05 \x01(\rR\x0fschemaVersionId\"\xc8\x01\n\x10SyncListResponse\x12\x31\n\x05reset\x18\x01 \x01(\x0b\x32\x19.stately.db.SyncListResetH\x00R\x05reset\x12=\n\x06result\x18\x02 \x01(\x0b\x32#.stately.db.SyncListPartialResponseH\x00R\x06result\x12\x36\n\x08\x66inished\x18\x03 \x01(\x0b\x32\x18.stately.db.ListFinishedH\x00R\x08\x66inishedB\n\n\x08response\"\x0f\n\rSyncListReset\"\xdf\x01\n\x17SyncListPartialResponse\x12\x35\n\rchanged_items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x0c\x63hangedItems\x12<\n\rdeleted_items\x18\x02 \x03(\x0b\x32\x17.stately.db.DeletedItemR\x0c\x64\x65letedItems\x12O\n%updated_item_keys_outside_list_window\x18\x03 \x03(\tR updatedItemKeysOutsideListWindow\"(\n\x0b\x44\x65letedItem\x12\x19\n\x08key_path\x18\x01 \x01(\tR\x07keyPathBh\n\x0e\x63om.stately.dbB\rSyncListProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
|
@@ -4,13 +4,13 @@
|
|
4
4
|
|
5
5
|
require 'google/protobuf'
|
6
6
|
|
7
|
-
require 'db/continue_list_pb'
|
8
|
-
require 'db/delete_pb'
|
9
|
-
require 'db/get_pb'
|
10
|
-
require 'db/item_pb'
|
11
|
-
require 'db/item_property_pb'
|
12
|
-
require 'db/list_pb'
|
13
|
-
require 'db/put_pb'
|
7
|
+
require 'api/db/continue_list_pb'
|
8
|
+
require 'api/db/delete_pb'
|
9
|
+
require 'api/db/get_pb'
|
10
|
+
require 'api/db/item_pb'
|
11
|
+
require 'api/db/item_property_pb'
|
12
|
+
require 'api/db/list_pb'
|
13
|
+
require 'api/db/put_pb'
|
14
14
|
require 'google/protobuf/empty_pb'
|
15
15
|
|
16
16
|
|
@@ -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,22 @@ 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
|
30
33
|
def initialize(
|
31
34
|
origin: "https://oauth.stately.cloud",
|
32
35
|
audience: "api.stately.cloud",
|
33
|
-
client_secret: ENV.fetch("STATELY_CLIENT_SECRET"),
|
34
|
-
client_id: ENV.fetch("STATELY_CLIENT_ID")
|
36
|
+
client_secret: ENV.fetch("STATELY_CLIENT_SECRET", nil),
|
37
|
+
client_id: ENV.fetch("STATELY_CLIENT_ID", nil),
|
38
|
+
access_key: ENV.fetch("STATELY_ACCESS_KEY", nil)
|
35
39
|
)
|
36
40
|
super()
|
37
41
|
@actor = Async::Actor.new(Actor.new(origin: origin, audience: audience,
|
38
|
-
client_secret: client_secret, client_id: client_id))
|
42
|
+
client_secret: client_secret, client_id: client_id, access_key: access_key))
|
39
43
|
# this initialization cannot happen in the constructor because it is async and must run on the event loop
|
40
44
|
# which is not available in the constructor
|
41
45
|
@actor.init
|
@@ -64,29 +68,41 @@ module StatelyDB
|
|
64
68
|
origin:,
|
65
69
|
audience:,
|
66
70
|
client_secret:,
|
67
|
-
client_id
|
71
|
+
client_id:,
|
72
|
+
access_key:
|
68
73
|
)
|
69
74
|
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
75
|
|
75
|
-
@
|
76
|
-
|
76
|
+
@token_fetcher = nil
|
77
|
+
if !access_key.nil?
|
78
|
+
@token_fetcher = StatelyDB::Common::Auth::StatelyAccessTokenFetcher.new(origin: origin, access_key: access_key)
|
79
|
+
elsif !client_secret.nil? && !client_id.nil?
|
80
|
+
@token_fetcher = StatelyDB::Common::Auth::Auth0TokenFetcher.new(origin: origin, audience: audience,
|
81
|
+
client_secret: client_secret, client_id: client_id)
|
82
|
+
else
|
83
|
+
raise StatelyDB::Error.new("unable to find client credentials in STATELY_ACCESS_KEY or STATELY_CLIENT_ID and " \
|
84
|
+
"STATELY_CLIENT_SECRET environment variables. Either pass your credentials in " \
|
85
|
+
"explicitly or set these environment variables",
|
86
|
+
code: GRPC::Core::StatusCodes::UNAUTHENTICATED,
|
87
|
+
stately_code: "Unauthenticated")
|
88
|
+
end
|
89
|
+
|
90
|
+
@token_state = nil
|
77
91
|
@pending_refresh = nil
|
78
92
|
end
|
79
93
|
|
80
94
|
# Initialize the actor. This runs on the actor thread which means
|
81
95
|
# we can dispatch async operations here.
|
82
96
|
def init
|
97
|
+
# disable the async lib logger. We do our own error handling and propagation
|
98
|
+
Console.logger.disable(Async::Task)
|
83
99
|
refresh_token
|
84
100
|
end
|
85
101
|
|
86
102
|
# Close the token provider and kill any background operations
|
87
103
|
def close
|
88
104
|
@scheduled&.stop
|
89
|
-
@
|
105
|
+
@token_fetcher&.close
|
90
106
|
end
|
91
107
|
|
92
108
|
# Get the current access token
|
@@ -94,8 +110,7 @@ module StatelyDB
|
|
94
110
|
# @return [String] The current access token
|
95
111
|
def get_token(force: false)
|
96
112
|
if force
|
97
|
-
@
|
98
|
-
@expires_at_unix_secs = nil
|
113
|
+
@token_state = nil
|
99
114
|
else
|
100
115
|
token, ok = valid_access_token
|
101
116
|
return token if ok
|
@@ -107,11 +122,10 @@ module StatelyDB
|
|
107
122
|
# Get the current access token and whether it is valid
|
108
123
|
# @return [Array] The current access token and whether it is valid
|
109
124
|
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
|
125
|
+
return "", false if @token_state.nil?
|
126
|
+
return "", false if @token_state.expires_at_unix_secs < Time.now.to_i
|
113
127
|
|
114
|
-
[@
|
128
|
+
[@token_state.token, true]
|
115
129
|
end
|
116
130
|
|
117
131
|
# Refresh the access token
|
@@ -151,19 +165,16 @@ module StatelyDB
|
|
151
165
|
# @return [String] The new access token
|
152
166
|
def refresh_token_impl
|
153
167
|
Sync do
|
154
|
-
|
155
|
-
|
156
|
-
new_access_token = resp_data["access_token"]
|
157
|
-
new_expires_in_secs = resp_data["expires_in"]
|
168
|
+
token_result = @token_fetcher.fetch
|
169
|
+
new_expires_in_secs = token_result.expires_in_secs
|
158
170
|
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
171
|
|
161
|
-
|
162
|
-
|
172
|
+
# only update the token state if the new expiry is later than the current one
|
173
|
+
if @token_state.nil? || new_expires_at_unix_secs > @token_state.expires_at_unix_secs
|
174
|
+
@token_state = TokenState.new(token: token_result.token, expires_at_unix_secs: new_expires_at_unix_secs)
|
163
175
|
else
|
164
|
-
|
165
|
-
|
166
|
-
new_expires_in_secs = @expires_at_unix_secs - Time.now.to_i
|
176
|
+
# otherwise use the existing expiry time for scheduling the refresh
|
177
|
+
new_expires_in_secs = @token_state.expires_at_unix_secs - Time.now.to_i
|
167
178
|
end
|
168
179
|
|
169
180
|
# Schedule a refresh of the token ahead of the expiry time
|
@@ -182,24 +193,21 @@ module StatelyDB
|
|
182
193
|
@scheduled = nil
|
183
194
|
end
|
184
195
|
|
185
|
-
|
196
|
+
@token_state.token
|
186
197
|
end
|
187
198
|
end
|
199
|
+
end
|
188
200
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
JSON.parse(response.read)
|
200
|
-
ensure
|
201
|
-
response&.close
|
202
|
-
end
|
201
|
+
# Persistent state for the token provider
|
202
|
+
class TokenState
|
203
|
+
attr_reader :token, :expires_at_unix_secs
|
204
|
+
|
205
|
+
# Create a new TokenState
|
206
|
+
# @param [String] token The access token
|
207
|
+
# @param [Integer] expires_at_unix_secs The unix timestamp when the token expires
|
208
|
+
def initialize(token:, expires_at_unix_secs:)
|
209
|
+
@token = token
|
210
|
+
@expires_at_unix_secs = expires_at_unix_secs
|
203
211
|
end
|
204
212
|
end
|
205
213
|
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,104 @@
|
|
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
|
+
# TODO: Wrap this in a retry loop and parse errors like we
|
62
|
+
# do in the Go SDK.
|
63
|
+
response = @client.post("/oauth/token", headers, body)
|
64
|
+
raise "Auth request failed" if response.status != 200
|
65
|
+
|
66
|
+
resp_data = JSON.parse(response.read)
|
67
|
+
TokenResult.new(token: resp_data["access_token"], expires_in_secs: resp_data["expires_in"])
|
68
|
+
ensure
|
69
|
+
response&.close
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def close
|
74
|
+
@client&.close
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
|
79
|
+
class StatelyAccessTokenFetcher < TokenFetcher
|
80
|
+
# @param [String] origin The origin of the OAuth server
|
81
|
+
# @param [String] access_key The StatelyDB access key credential
|
82
|
+
def initialize(origin:, access_key:)
|
83
|
+
super()
|
84
|
+
@access_key = access_key
|
85
|
+
@channel = Common::Net.new_channel(endpoint: origin)
|
86
|
+
error_interceptor = Common::ErrorInterceptor.new
|
87
|
+
@stub = Stately::Auth::AuthService::Stub.new(nil, nil, channel_override: @channel,
|
88
|
+
interceptors: [error_interceptor])
|
89
|
+
end
|
90
|
+
|
91
|
+
# Fetch a new token from the StatelyDB API
|
92
|
+
# @return [TokenResult] The fetched TokenResult
|
93
|
+
def fetch
|
94
|
+
resp = @stub.get_auth_token(Stately::Auth::GetAuthTokenRequest.new(access_key: @access_key))
|
95
|
+
TokenResult.new(token: resp.auth_token, expires_in_secs: resp.expires_in_s)
|
96
|
+
end
|
97
|
+
|
98
|
+
def close
|
99
|
+
@channel&.close
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/error.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Add the pb dir to the LOAD_PATH because generated proto imports are not relative and
|
4
|
-
# we don't want the protos polluting the main namespace.
|
5
|
-
# Tracking here: https://github.com/grpc/grpc/issues/6164
|
6
|
-
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/api"
|
7
|
-
|
8
3
|
require "api/errors/error_details_pb"
|
9
4
|
|
10
5
|
module StatelyDB
|
data/lib/statelydb.rb
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Add the pb dir to the LOAD_PATH because generated proto imports are not relative and
|
4
|
-
# we don't want the protos polluting the main namespace.
|
5
|
-
# Tracking here: https://github.com/grpc/grpc/issues/6164
|
6
|
-
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/api"
|
7
|
-
|
8
3
|
require "api/db/service_services_pb"
|
9
|
-
require "common/auth/
|
4
|
+
require "common/auth/auth_token_provider"
|
10
5
|
require "common/auth/interceptor"
|
11
6
|
require "common/net/conn"
|
12
7
|
require "common/error_interceptor"
|
@@ -35,7 +30,7 @@ module StatelyDB
|
|
35
30
|
# @param region [String] the region to connect to.
|
36
31
|
def initialize(store_id:,
|
37
32
|
schema:,
|
38
|
-
token_provider: Common::Auth::
|
33
|
+
token_provider: Common::Auth::AuthTokenProvider.new,
|
39
34
|
endpoint: nil,
|
40
35
|
region: nil)
|
41
36
|
if store_id.nil?
|
@@ -63,6 +58,7 @@ module StatelyDB
|
|
63
58
|
@allow_stale = false
|
64
59
|
end
|
65
60
|
|
61
|
+
# @return [void] nil
|
66
62
|
def close
|
67
63
|
@channel&.close
|
68
64
|
@token_provider&.close
|
data/lib/token.rb
CHANGED
@@ -254,7 +254,7 @@ module StatelyDB
|
|
254
254
|
#
|
255
255
|
# @param items [StatelyDB::Item, Array<StatelyDB::Item>] the items to store. Max
|
256
256
|
# 50 items.
|
257
|
-
# @return [Array<StatelyDB::UUID, String, Integer,
|
257
|
+
# @return [Array<StatelyDB::UUID, String, Integer, NilClass>] the ids of the items
|
258
258
|
#
|
259
259
|
# @example
|
260
260
|
# results = client.data.transaction do |txn|
|
data/sig/grpc.rbs
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module GRPC
|
2
|
+
class ActiveCall
|
3
|
+
end
|
4
|
+
|
5
|
+
class Interceptor
|
6
|
+
end
|
7
|
+
class ClientInterceptor < Interceptor
|
8
|
+
# gRPC client unary interceptor
|
9
|
+
#
|
10
|
+
# _@param_ `request` — The request object
|
11
|
+
#
|
12
|
+
# _@param_ `call` — The active call object
|
13
|
+
#
|
14
|
+
# _@param_ `method` — The method being called
|
15
|
+
#
|
16
|
+
# _@param_ `metadata` — The metadata hash
|
17
|
+
#
|
18
|
+
# _@return_ — The response object
|
19
|
+
def request_response: (
|
20
|
+
request: Object,
|
21
|
+
call: GRPC::ActiveCall,
|
22
|
+
method: Symbol,
|
23
|
+
metadata: ::Hash[untyped, untyped]
|
24
|
+
) -> Object
|
25
|
+
|
26
|
+
# gRPC client streaming interceptor
|
27
|
+
#
|
28
|
+
# _@param_ `requests` — The list of requests
|
29
|
+
#
|
30
|
+
# _@param_ `call` — The active call object
|
31
|
+
#
|
32
|
+
# _@param_ `method` — The method being called
|
33
|
+
#
|
34
|
+
# _@param_ `metadata` — The metadata hash
|
35
|
+
#
|
36
|
+
# _@return_ — The response enumerator
|
37
|
+
def client_streamer: (
|
38
|
+
requests: ::Enumerable[untyped],
|
39
|
+
call: GRPC::ActiveCall,
|
40
|
+
method: Symbol,
|
41
|
+
metadata: ::Hash[untyped, untyped]
|
42
|
+
) -> ::Enumerator[untyped]
|
43
|
+
|
44
|
+
# gRPC server streaming interceptor
|
45
|
+
#
|
46
|
+
# _@param_ `request` — The request object
|
47
|
+
#
|
48
|
+
# _@param_ `call` — The active call object
|
49
|
+
#
|
50
|
+
# _@param_ `method` — The method being called
|
51
|
+
#
|
52
|
+
# _@param_ `metadata` — The metadata hash
|
53
|
+
#
|
54
|
+
# _@return_ — The response enumerator
|
55
|
+
def server_streamer: (
|
56
|
+
request: Object,
|
57
|
+
call: GRPC::ActiveCall,
|
58
|
+
method: Symbol,
|
59
|
+
metadata: ::Hash[untyped, untyped]
|
60
|
+
) -> ::Enumerator[untyped]
|
61
|
+
|
62
|
+
# gRPC bidirectional streaming interceptor
|
63
|
+
#
|
64
|
+
# _@param_ `requests` — The list of requests
|
65
|
+
#
|
66
|
+
# _@param_ `call` — The active call object
|
67
|
+
#
|
68
|
+
# _@param_ `method` — The method being called
|
69
|
+
#
|
70
|
+
# _@param_ `metadata` — The metadata hash
|
71
|
+
#
|
72
|
+
# _@return_ — The response enumerator
|
73
|
+
def bidi_streamer: (
|
74
|
+
requests: ::Enumerable[untyped],
|
75
|
+
call: GRPC::ActiveCall,
|
76
|
+
method: Symbol,
|
77
|
+
metadata: ::Hash[untyped, untyped]
|
78
|
+
) -> ::Enumerator[untyped]
|
79
|
+
end
|
80
|
+
end
|