statelydb 0.12.2 → 0.14.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/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
|