anycable-core 1.4.4 → 1.5.0.rc.1
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/CHANGELOG.md +22 -0
- data/lib/anycable/broadcast_adapters/http.rb +11 -5
- data/lib/anycable/config.rb +68 -13
- data/lib/anycable/httrpc/server.rb +5 -0
- data/lib/anycable/jwt.rb +110 -0
- data/lib/anycable/serializer.rb +44 -0
- data/lib/anycable/streams.rb +38 -0
- data/lib/anycable/version.rb +1 -1
- data/lib/anycable.rb +4 -0
- data/sig/anycable/config.rbs +19 -2
- data/sig/anycable/jwt.rbs +21 -0
- data/sig/anycable/serializer.rbs +14 -0
- data/sig/anycable/streams.rbs +8 -0
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3469966952c29b422b3177acbb3402fc9bf500786d55774f9b3311b904548bb
|
4
|
+
data.tar.gz: 7e5a7302b61c07af90f61b11878212a464ba1eac0f7d6b664f91b8dcbd3fe9e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57e7aafe42eeddf359db963a688cf89acf9b109935f4561ba2eb4cb9c994e3a47d44b721b24f717be41630d4b8234d5f77a12d91246b0033e2385772287c43af
|
7
|
+
data.tar.gz: 53785ae7eaf8c86d1f164a89dbfeaee2063be1c4180f2a263cb2f47ca3ca41bc528f545ba65c1232c691fa6515bd4be39e9f6200b903b9b2bb8af02f09f37ccc
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,28 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
- Added JWT utils. ([@palkan][])
|
6
|
+
|
7
|
+
You can now generate and verify AnyCable JWT tokens without using additional dependencies:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
token = AnyCable::JWT.encode({user_id: "1"})
|
11
|
+
|
12
|
+
identifiers = AnyCable::JWT.decode(token)
|
13
|
+
```
|
14
|
+
|
15
|
+
This change will deprecate `anycable-rails-jwt`.
|
16
|
+
|
17
|
+
- Added signed streams support. ([@palkan][])
|
18
|
+
|
19
|
+
You can generate signed stream names as follows:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
signed_name = AnyCable::Streams.sign("chat/2024")
|
23
|
+
```
|
24
|
+
|
25
|
+
- Added `secret` and `broadcast_key` configuration parameters. ([@palkan][])
|
26
|
+
|
5
27
|
## 1.4.3 (2023-10-15)
|
6
28
|
|
7
29
|
- Add broadcast options support. ([@palkan][])
|
@@ -40,15 +40,21 @@ module AnyCable
|
|
40
40
|
MAX_ATTEMPTS = 3
|
41
41
|
DELAY = 2
|
42
42
|
|
43
|
-
attr_reader :url, :headers, :
|
44
|
-
alias_method :authorized?, :authorized
|
43
|
+
attr_reader :url, :headers, :authorization
|
45
44
|
|
46
|
-
def initialize(url: AnyCable.config.http_broadcast_url, secret: AnyCable.config.
|
45
|
+
def initialize(url: AnyCable.config.http_broadcast_url, secret: AnyCable.config.broadcast_key)
|
47
46
|
@url = url
|
48
47
|
@headers = {}
|
48
|
+
@authorization = nil
|
49
|
+
|
50
|
+
if !secret
|
51
|
+
secret = AnyCable.config.broadcast_key!
|
52
|
+
@authorization = "with authorization key inferred from the application secret" if secret
|
53
|
+
end
|
54
|
+
|
49
55
|
if secret
|
50
56
|
headers["Authorization"] = "Bearer #{secret}"
|
51
|
-
@
|
57
|
+
@authorization ||= "with authorization"
|
52
58
|
end
|
53
59
|
|
54
60
|
@uri = URI.parse(url)
|
@@ -71,7 +77,7 @@ module AnyCable
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def announce!
|
74
|
-
logger.info "Broadcasting HTTP url: #{url}#{
|
80
|
+
logger.info "Broadcasting HTTP url: #{url} (#{authorization || "no authorization"})"
|
75
81
|
end
|
76
82
|
|
77
83
|
private
|
data/lib/anycable/config.rb
CHANGED
@@ -7,6 +7,11 @@ require "uri"
|
|
7
7
|
module AnyCable
|
8
8
|
# AnyCable configuration.
|
9
9
|
class Config < Anyway::Config
|
10
|
+
# These phareses are used to infer secret keys from the application secret
|
11
|
+
# and MUST match the ones used in AnyCable (Go)
|
12
|
+
BROADCAST_SECRET_PHRASE = "broadcast-cable"
|
13
|
+
HTTP_RPC_SECRET_PHRASE = "rpc-cable"
|
14
|
+
|
10
15
|
class << self
|
11
16
|
# Add usage txt for CLI
|
12
17
|
def usage(txt)
|
@@ -22,9 +27,18 @@ module AnyCable
|
|
22
27
|
|
23
28
|
attr_config(
|
24
29
|
presets: "",
|
30
|
+
secret: nil,
|
31
|
+
|
32
|
+
## Streams
|
33
|
+
streams_secret: nil,
|
25
34
|
|
26
|
-
##
|
35
|
+
## JWT
|
36
|
+
jwt_secret: nil,
|
37
|
+
jwt_ttl: 3600, # 1 hour
|
38
|
+
|
39
|
+
## Broadcasting
|
27
40
|
broadcast_adapter: :redis,
|
41
|
+
broadcast_key: nil,
|
28
42
|
|
29
43
|
### Redis options
|
30
44
|
redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379"),
|
@@ -42,6 +56,7 @@ module AnyCable
|
|
42
56
|
|
43
57
|
### HTTP broadcasting options
|
44
58
|
http_broadcast_url: "http://localhost:8090/_broadcast",
|
59
|
+
# DEPRECATED: use `broadcast_key` instead
|
45
60
|
http_broadcast_secret: nil,
|
46
61
|
|
47
62
|
### Logging options
|
@@ -86,36 +101,67 @@ module AnyCable
|
|
86
101
|
!http_health_port.nil? && http_health_port != ""
|
87
102
|
end
|
88
103
|
|
104
|
+
def broadcast_key!
|
105
|
+
if http_broadcast_secret && !broadcast_key
|
106
|
+
self.broadcast_key ||= http_broadcast_secret
|
107
|
+
warn "DEPRECATION WARNING: `http_broadcast_secret` is deprecated, use `broadcast_key` instead"
|
108
|
+
end
|
109
|
+
|
110
|
+
return broadcast_key if broadcast_key
|
111
|
+
return unless secret
|
112
|
+
|
113
|
+
self.broadcast_key = infer_from_application_secret(BROADCAST_SECRET_PHRASE)
|
114
|
+
end
|
115
|
+
|
116
|
+
def http_rpc_secret!
|
117
|
+
return http_rpc_secret if http_rpc_secret
|
118
|
+
return unless secret
|
119
|
+
|
120
|
+
self.http_rpc_secret = infer_from_application_secret(HTTP_RPC_SECRET_PHRASE)
|
121
|
+
end
|
122
|
+
|
123
|
+
def streams_secret
|
124
|
+
super || secret
|
125
|
+
end
|
126
|
+
|
127
|
+
def jwt_secret
|
128
|
+
super || secret
|
129
|
+
end
|
130
|
+
|
89
131
|
usage <<~TXT
|
90
132
|
APPLICATION
|
91
|
-
--broadcast-adapter=type
|
92
|
-
--
|
93
|
-
--
|
94
|
-
--
|
133
|
+
--broadcast-adapter=type Broadcasting adapter, default: redis
|
134
|
+
--secret=secret Application secret, default: <none>
|
135
|
+
--broadcast-key=key Broadcasting secret key, default: <none> (inferred from the application secret if any)
|
136
|
+
--streams-secret=secret Signed streams secret, default: <none> (inferred from the application secret if any)
|
95
137
|
|
96
138
|
HTTP HEALTH CHECKER
|
97
139
|
--http-health-port=port Port to run HTTP health server on, default: <none> (disabled)
|
98
140
|
--http-health-path=path Endpoint to serve health checks, default: "/health"
|
99
141
|
|
100
|
-
REDIS
|
101
|
-
--redis-url=url Redis URL for
|
142
|
+
REDIS
|
143
|
+
--redis-url=url Redis URL for broadcasting, default: REDIS_URL or "redis://localhost:6379"
|
102
144
|
--redis-channel=name Redis channel for broadcasting, default: "__anycable__"
|
103
145
|
--redis-sentinels=<...hosts> Redis Sentinel followers addresses (as a comma-separated list), default: nil
|
104
146
|
--redis-tls-verify=yes|no Whether to perform server certificate check in case of rediss:// protocol. Default: yes
|
105
147
|
--redis-tls-client_cert-path=path Default: nil
|
106
148
|
--redis-tls-client_key-path=path Default: nil
|
107
149
|
|
108
|
-
NATS
|
109
|
-
--nats-servers=<...addresses> NATS servers for
|
150
|
+
NATS
|
151
|
+
--nats-servers=<...addresses> NATS servers for broadcasting, default: "nats://localhost:4222"
|
110
152
|
--nats-channel=name NATS channel for broadcasting, default: "__anycable__"
|
111
153
|
--nats-dont-randomize-servers Pass this option to disable NATS servers randomization during (re-)connect
|
112
154
|
|
113
|
-
HTTP
|
114
|
-
--http-broadcast-url HTTP
|
115
|
-
--http-broadcast-secret HTTP pub/sub authorization secret, default: <none> (disabled)
|
155
|
+
HTTP BROADCASTING
|
156
|
+
--http-broadcast-url HTTP broadcasting endpoint URL, default: "http://localhost:8090/_broadcast"
|
116
157
|
|
117
158
|
HTTP RPC
|
118
|
-
--http-rpc-secret HTTP RPC authorization secret, default: <none> (
|
159
|
+
--http-rpc-secret HTTP RPC authorization secret, default: <none> (inferred from the application secret if any)
|
160
|
+
|
161
|
+
LOGGING
|
162
|
+
--log-level=level Logging level, default: "info"
|
163
|
+
--log-file=path Path to log file, default: <none> (log to STDOUT)
|
164
|
+
--debug Turn on verbose logging ("debug" level and verbose logging on)
|
119
165
|
TXT
|
120
166
|
|
121
167
|
# Build Redis parameters
|
@@ -216,5 +262,14 @@ module AnyCable
|
|
216
262
|
write_config_attr(key, value)
|
217
263
|
__trace__&.record_value(value, key, type: :preset, preset: preset)
|
218
264
|
end
|
265
|
+
|
266
|
+
def infer_from_application_secret(phrase)
|
267
|
+
app_secret = secret
|
268
|
+
return unless app_secret
|
269
|
+
|
270
|
+
require "openssl"
|
271
|
+
|
272
|
+
OpenSSL::HMAC.hexdigest("SHA256", app_secret, phrase)
|
273
|
+
end
|
219
274
|
end
|
220
275
|
end
|
@@ -4,6 +4,11 @@ module AnyCable
|
|
4
4
|
module HTTRPC
|
5
5
|
class Server
|
6
6
|
def initialize(token: AnyCable.config.http_rpc_secret)
|
7
|
+
if !token
|
8
|
+
token = AnyCable.config.http_rpc_secret!
|
9
|
+
AnyCable.logger.info("AnyCable HTTP RPC created with authorization key inferred from the application secret")
|
10
|
+
end
|
11
|
+
|
7
12
|
@token = token
|
8
13
|
end
|
9
14
|
|
data/lib/anycable/jwt.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
module AnyCable
|
7
|
+
module JWT
|
8
|
+
class DecodeError < StandardError; end
|
9
|
+
|
10
|
+
class VerificationError < DecodeError; end
|
11
|
+
|
12
|
+
class ExpiredSignature < DecodeError; end
|
13
|
+
|
14
|
+
# Basic JWT encode/decode implementation suitable to our needs
|
15
|
+
# and not requiring external dependencies
|
16
|
+
module BasicImpl
|
17
|
+
ALGORITHM = "HS256"
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def encode(payload, secret_key)
|
21
|
+
payload = ::Base64.urlsafe_encode64(payload.to_json, padding: false)
|
22
|
+
headers = ::Base64.urlsafe_encode64({"alg" => ALGORITHM}.to_json, padding: false)
|
23
|
+
|
24
|
+
header = "#{headers}.#{payload}"
|
25
|
+
signature = sign(header, secret_key)
|
26
|
+
|
27
|
+
"#{header}.#{signature}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def decode(token, secret_key)
|
31
|
+
header, payload, signature = token.split(".")
|
32
|
+
# Check segments
|
33
|
+
raise DecodeError, "Not enough or too many segments" unless header && payload && signature
|
34
|
+
|
35
|
+
# Verify the algorithm
|
36
|
+
decoded_header = ::JSON.parse(::Base64.urlsafe_decode64(header))
|
37
|
+
raise DecodeError, "Algorithm not supported" unless decoded_header["alg"] == ALGORITHM
|
38
|
+
|
39
|
+
# Verify the signature
|
40
|
+
expected_signature = sign("#{header}.#{payload}", secret_key)
|
41
|
+
raise VerificationError, "Signature verification failed" unless secure_compare(signature, expected_signature)
|
42
|
+
|
43
|
+
# Verify expiration
|
44
|
+
decoded_payload = ::JSON.parse(::Base64.urlsafe_decode64(payload))
|
45
|
+
if decoded_payload.key?("exp")
|
46
|
+
raise ExpiredSignature, "Signature has expired" if Time.now.to_i >= decoded_payload["exp"]
|
47
|
+
end
|
48
|
+
|
49
|
+
decoded_payload
|
50
|
+
rescue JSON::ParserError, ArgumentError
|
51
|
+
raise DecodeError, "Invalid segment encoding"
|
52
|
+
end
|
53
|
+
|
54
|
+
# We don't really care about timing attacks here,
|
55
|
+
# since verification is done on the AnyCable server side.
|
56
|
+
# But still, we can use constant-time comparison when it's available.
|
57
|
+
if OpenSSL.respond_to?(:fixed_length_secure_compare)
|
58
|
+
def secure_compare(a, b)
|
59
|
+
return false if a.bytesize != b.bytesize
|
60
|
+
|
61
|
+
OpenSSL.fixed_length_secure_compare(a, b)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
def secure_compare(a, b)
|
65
|
+
return false if a.bytesize != b.bytesize
|
66
|
+
|
67
|
+
a == b
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def sign(data, secret_key)
|
72
|
+
::Base64.urlsafe_encode64(
|
73
|
+
::OpenSSL::HMAC.digest("SHA256", secret_key, data),
|
74
|
+
padding: false
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class << self
|
81
|
+
attr_accessor :jwt_impl
|
82
|
+
|
83
|
+
def encode(payload, expires_at: nil, secret_key: AnyCable.config.jwt_secret, ttl: AnyCable.config.jwt_ttl)
|
84
|
+
raise ArgumentError, "JWT encryption key is not specified. Add it via `jwt_secret` or `secret` option" if secret_key.nil? || secret_key.empty?
|
85
|
+
|
86
|
+
encoded = Serializer.serialize(payload).to_json
|
87
|
+
|
88
|
+
data = {ext: encoded}
|
89
|
+
|
90
|
+
data[:exp] = expires_at.to_i if expires_at
|
91
|
+
|
92
|
+
if ttl&.positive? && !data.key?(:exp)
|
93
|
+
data[:exp] = Time.now.to_i + ttl
|
94
|
+
end
|
95
|
+
|
96
|
+
jwt_impl.encode(data, secret_key)
|
97
|
+
end
|
98
|
+
|
99
|
+
def decode(token, secret_key: AnyCable.config.jwt_secret)
|
100
|
+
raise ArgumentError, "JWT encryption key is not specified. Add it via `jwt_secret` or `secret` option" if secret_key.nil? || secret_key.empty?
|
101
|
+
|
102
|
+
jwt_impl.decode(token, secret_key).then do |decoded|
|
103
|
+
::JSON.parse(decoded.fetch("ext"), symbolize_names: true)
|
104
|
+
end.then { |data| Serializer.deserialize(data) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
self.jwt_impl = BasicImpl
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnyCable
|
4
|
+
# Serializer is responsible for converting Ruby objects to and from a transferrable format (e.g., for identifiers, connection/channel state, etc.).
|
5
|
+
# It relies on configurable `.object_serializer` to handle non-primitive values and handles Hash/Array seririlzation.
|
6
|
+
module Serializer
|
7
|
+
class << self
|
8
|
+
attr_accessor :object_serializer
|
9
|
+
|
10
|
+
def serialize(obj)
|
11
|
+
handled = object_serializer&.serialize(obj)
|
12
|
+
return handled if handled
|
13
|
+
|
14
|
+
case obj
|
15
|
+
when nil, true, false, Integer, Float, String, Symbol
|
16
|
+
obj
|
17
|
+
when Hash
|
18
|
+
obj.transform_values { |v| serialize(v) }
|
19
|
+
when Array
|
20
|
+
obj.map { |v| serialize(v) }
|
21
|
+
else
|
22
|
+
raise ArgumentError, "Can't serialize #{obj.inspect}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Deserialize previously serialized value to a Ruby object.
|
27
|
+
def deserialize(val)
|
28
|
+
if val.is_a?(::String)
|
29
|
+
handled = object_serializer&.deserialize(val)
|
30
|
+
return handled if handled
|
31
|
+
end
|
32
|
+
|
33
|
+
case val
|
34
|
+
when Hash
|
35
|
+
val.transform_values { |v| deserialize(v) }
|
36
|
+
when Array
|
37
|
+
val.map { |v| deserialize(v) }
|
38
|
+
else
|
39
|
+
val
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module AnyCable
|
8
|
+
module Streams
|
9
|
+
class << self
|
10
|
+
def signed(stream_name)
|
11
|
+
::Base64.urlsafe_encode64(::JSON.dump(stream_name)).then do |encoded|
|
12
|
+
"#{encoded}--#{signature(encoded)}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def verified(signed_stream_name)
|
17
|
+
encoded, sig = signed_stream_name.split("--")
|
18
|
+
raise ArgumentError, "stream name has incorrect format" unless encoded
|
19
|
+
|
20
|
+
return unless sig == signature(encoded)
|
21
|
+
|
22
|
+
::Base64.urlsafe_decode64(encoded).then do |decoded|
|
23
|
+
::JSON.parse(decoded)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def signature(val)
|
30
|
+
key = AnyCable.config.streams_secret
|
31
|
+
|
32
|
+
raise ArgumentError, "streams signing secret is missing" unless key
|
33
|
+
|
34
|
+
::OpenSSL::HMAC.hexdigest("SHA256", key, val)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/anycable/version.rb
CHANGED
data/lib/anycable.rb
CHANGED
@@ -26,6 +26,10 @@ require "anycable/httrpc/server"
|
|
26
26
|
#
|
27
27
|
# Broadcasting messages to WS is done through _broadcast adapter_ (Redis Pub/Sub by default).
|
28
28
|
module AnyCable
|
29
|
+
autoload :JWT, "anycable/jwt"
|
30
|
+
autoload :Serializer, "anycable/serializer"
|
31
|
+
autoload :Streams, "anycable/streams"
|
32
|
+
|
29
33
|
class << self
|
30
34
|
# Provide connection factory which
|
31
35
|
# is a callable object with build
|
data/sig/anycable/config.rbs
CHANGED
@@ -4,6 +4,16 @@ module AnyCable
|
|
4
4
|
def presets=: (Array[String]) -> void
|
5
5
|
def broadcast_adapter: () -> Symbol
|
6
6
|
def broadcast_adapter=: (Symbol) -> void
|
7
|
+
def secret: () -> String?
|
8
|
+
def secret=: (String?) -> void
|
9
|
+
def streams_secret: () -> String?
|
10
|
+
def streams_secret=: (String?) -> void
|
11
|
+
def jwt_secret: () -> String?
|
12
|
+
def jwt_secret=: (String?) -> void
|
13
|
+
def jwt_ttl: () -> Integer?
|
14
|
+
def jwt_ttl=: (Integer) -> void
|
15
|
+
def broadcast_key: () -> String?
|
16
|
+
def broadcast_key=: (String?) -> void
|
7
17
|
def redis_url: () -> String
|
8
18
|
def redis_url=: (String) -> void
|
9
19
|
def redis_sentinels: () -> Array[String | Hash[untyped, untyped]]?
|
@@ -41,8 +51,8 @@ module AnyCable
|
|
41
51
|
def http_health_port=: (Integer) -> void
|
42
52
|
def http_health_path: () -> String
|
43
53
|
def http_health_path=: (String) -> void
|
44
|
-
def http_rpc_secret: () -> String
|
45
|
-
def http_rpc_secret=: (String) -> void
|
54
|
+
def http_rpc_secret: () -> String?
|
55
|
+
def http_rpc_secret=: (String?) -> void
|
46
56
|
def version_check_enabled: () -> bool
|
47
57
|
def version_check_enabled=: (bool) -> void
|
48
58
|
def version_check_enabled?: () -> bool
|
@@ -52,6 +62,9 @@ module AnyCable
|
|
52
62
|
end
|
53
63
|
|
54
64
|
class Config < Anyway::Config
|
65
|
+
BROADCAST_SECRET_PHRASE: String
|
66
|
+
HTTP_RPC_SECRET_PHRASE: String
|
67
|
+
|
55
68
|
def self.usage: (String txt) -> void
|
56
69
|
def self.usages: () -> Array[String]
|
57
70
|
|
@@ -60,6 +73,9 @@ module AnyCable
|
|
60
73
|
alias debug? debug
|
61
74
|
alias version_check_enabled? version_check_enabled
|
62
75
|
|
76
|
+
def http_rpc_secret!: () -> String?
|
77
|
+
def broadcast_key!: () -> String?
|
78
|
+
|
63
79
|
def load: (*untyped) -> void
|
64
80
|
def http_health_port_provided?: () -> bool
|
65
81
|
def to_redis_params: () -> { url: String, sentinels: Array[untyped]?, ssl_params: Hash[Symbol, untyped]? }
|
@@ -74,5 +90,6 @@ module AnyCable
|
|
74
90
|
def write_preset: (Symbol, untyped, preset: String) -> void
|
75
91
|
def write_config_attr: (Symbol, untyped) -> void
|
76
92
|
def __trace__: () -> untyped
|
93
|
+
def infer_from_application_secret: (String) -> String?
|
77
94
|
end
|
78
95
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AnyCable
|
2
|
+
module JWT
|
3
|
+
interface _Impl
|
4
|
+
def encode: (Hash[String | Symbol, untyped] payload, String secret_key) -> String
|
5
|
+
def decode: (String token, String secret_key) -> Hash[String, untyped]
|
6
|
+
end
|
7
|
+
|
8
|
+
module BasicImpl
|
9
|
+
extend _Impl
|
10
|
+
|
11
|
+
def self.sign: (String data, String key) -> String
|
12
|
+
def self.secure_compare: (String a, String b) -> bool
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.jwt_impl: () -> _Impl
|
16
|
+
def self.jwt_impl=: (_Impl) -> void
|
17
|
+
|
18
|
+
def self.encode: (untyped payload, ?secret_key: String?, ?ttl: Integer, ?expires_at: Time | Integer) -> String
|
19
|
+
def self.decode: (String token, ?secret_key: String?) -> untyped
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module AnyCable
|
2
|
+
interface _ObjectSerializer
|
3
|
+
def serialize: (untyped) -> untyped
|
4
|
+
def deserialize: (String) -> untyped
|
5
|
+
end
|
6
|
+
|
7
|
+
module Serializer
|
8
|
+
def self.object_serializer: () -> _ObjectSerializer?
|
9
|
+
def self.object_serializer=: (_ObjectSerializer?) -> void
|
10
|
+
|
11
|
+
def self.serialize: (untyped) -> untyped
|
12
|
+
def self.deserialize: (untyped) -> untyped
|
13
|
+
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anycable-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0.rc.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: google-protobuf
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '3.
|
33
|
+
version: '3.13'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '3.
|
40
|
+
version: '3.13'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: redis
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -217,6 +217,7 @@ files:
|
|
217
217
|
- lib/anycable/grpc_kit/server.rb
|
218
218
|
- lib/anycable/health_server.rb
|
219
219
|
- lib/anycable/httrpc/server.rb
|
220
|
+
- lib/anycable/jwt.rb
|
220
221
|
- lib/anycable/middleware.rb
|
221
222
|
- lib/anycable/middleware_chain.rb
|
222
223
|
- lib/anycable/middlewares/check_version.rb
|
@@ -230,7 +231,9 @@ files:
|
|
230
231
|
- lib/anycable/rpc/handlers/disconnect.rb
|
231
232
|
- lib/anycable/rspec.rb
|
232
233
|
- lib/anycable/rspec/rpc_command_context.rb
|
234
|
+
- lib/anycable/serializer.rb
|
233
235
|
- lib/anycable/socket.rb
|
236
|
+
- lib/anycable/streams.rb
|
234
237
|
- lib/anycable/version.rb
|
235
238
|
- sig/anycable.rbs
|
236
239
|
- sig/anycable/broadcast_adapters.rbs
|
@@ -248,6 +251,7 @@ files:
|
|
248
251
|
- sig/anycable/grpc/server.rbs
|
249
252
|
- sig/anycable/health_server.rbs
|
250
253
|
- sig/anycable/httrpc/server.rbs
|
254
|
+
- sig/anycable/jwt.rbs
|
251
255
|
- sig/anycable/middleware.rbs
|
252
256
|
- sig/anycable/middleware_chain.rbs
|
253
257
|
- sig/anycable/middlewares/check_version.rbs
|
@@ -258,7 +262,9 @@ files:
|
|
258
262
|
- sig/anycable/rpc/handlers/command.rbs
|
259
263
|
- sig/anycable/rpc/handlers/connect.rbs
|
260
264
|
- sig/anycable/rpc/handlers/disconnect.rbs
|
265
|
+
- sig/anycable/serializer.rbs
|
261
266
|
- sig/anycable/socket.rbs
|
267
|
+
- sig/anycable/streams.rbs
|
262
268
|
- sig/anycable/version.rbs
|
263
269
|
- sig/manifest.yml
|
264
270
|
homepage: http://github.com/anycable/anycable
|
@@ -282,9 +288,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
282
288
|
version: 2.7.0
|
283
289
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
284
290
|
requirements:
|
285
|
-
- - "
|
291
|
+
- - ">"
|
286
292
|
- !ruby/object:Gem::Version
|
287
|
-
version:
|
293
|
+
version: 1.3.1
|
288
294
|
requirements: []
|
289
295
|
rubygems_version: 3.4.20
|
290
296
|
signing_key:
|