volcano-sdk 0.5.3 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 939a91c580dbb07f0cc04f6930c2ea4be62ca7ab7f0106e93b8862d0a310d7b5
4
- data.tar.gz: f577bff2cca54ae9fbb5b2375cc45c1671f17fe8372b5f087273251929488ce8
3
+ metadata.gz: a36dee9d50b8138335752de5e3fdb5007ecd5dfa66547150190de691a048052d
4
+ data.tar.gz: 97edb400526abafd980ec19278304f80cff3ead5010d7704da7b55128390879e
5
5
  SHA512:
6
- metadata.gz: 5df58a522d1c82cea769ff810620b9837247781a8e13c25add391d9287fd1bee253ddd672508606f2c59eb93988790d5c68cda418b66a90cb8527a8afb5bac97
7
- data.tar.gz: c742eaa7aec57e3f151cb650a10fe1fc10641f862196158c259b6cc9c5cad439df908a4603b4c7d7818b1bcdd5aa0ebbca685cd0c75c78188958a2176921955f
6
+ metadata.gz: 5c3e7ed134b563780e8c1d64ba26f30f7ae9563d7c2ac219a265dabbf6aa04544600ef1a963abd09f9aa3f4efc2a6e32da0adeaf9a2a45905bd793daffd4101d
7
+ data.tar.gz: c2abfe5361e2ccff60f6f76a77aa4e2eedc0a033d1ef7649a63c5a6fa8f1edf0996c8141cc5eede6c08e5848e24fa8159e5a550b12edc5a32d16ea6dbf4311aa
data/README.md CHANGED
@@ -69,6 +69,20 @@ raise "session changed" unless current_session == session
69
69
  `current_session` reads immutable local state. It does not refresh or validate
70
70
  the token.
71
71
 
72
+ Sessions returned by authentication retain the user payload in `session.user`,
73
+ including metadata. The snapshot is deeply immutable and available without a
74
+ request. It is cached data, not proof of authentication; use `auth.user` to fetch
75
+ the server-validated profile. Existing three-field `Session` construction still
76
+ works, with `user: nil`. An adopted snapshot must have the same user ID.
77
+ Snapshots contain deeply frozen JSON data. Plain `Time` timestamps become UTC
78
+ ISO8601 strings with nanosecond precision; symbols become strings. Custom objects,
79
+ container/string/time subclasses, non-finite numbers, duplicate JSON keys, and
80
+ structures deeper than 100 levels raise `TypeError`. Use the typed `auth.user`
81
+ profile when you need Ruby `Time` values. Successful `user`, `update_user`,
82
+ `convert_anonymous`, and `confirm_email_change` calls update this snapshot
83
+ without changing credentials or emitting an authentication-state event.
84
+ Previously returned sessions remain unchanged.
85
+
72
86
  ### Get the current user
73
87
 
74
88
  ```ruby
@@ -78,8 +92,8 @@ raise "wrong user" unless user.id == session.user_id
78
92
 
79
93
  `user` sends the active access token to Volcano and returns an immutable,
80
94
  server-validated `Volcano::User`. The SDK recursively freezes its strings and
81
- metadata, but does not cache the profile or replace the session. A session
82
- change while the request is in flight raises
95
+ metadata and updates `current_session.user`. A session change while the request
96
+ is in flight raises
83
97
  `Volcano::Error::SessionChangedError` instead of returning a stale profile.
84
98
  `get_user` is available as a cross-SDK alias.
85
99
 
@@ -95,8 +109,8 @@ raise "wrong user" unless user.id == session.user_id
95
109
 
96
110
  `update_user` changes the current user's password, metadata, or both. Metadata
97
111
  is a shallow patch: omitted keys remain unchanged, and a `nil` value removes
98
- that key. The method returns an immutable `Volcano::User` without replacing the
99
- active session. It rejects a response if another authentication operation
112
+ that key. The method returns an immutable `Volcano::User` and updates the local
113
+ user snapshot. It rejects a response if another authentication operation
100
114
  replaces the session while the request is in flight.
101
115
 
102
116
  ### Request a password reset email
@@ -159,8 +173,8 @@ user = client.auth.confirm_email_change(token: "email-change-token")
159
173
  puts user.email
160
174
  ```
161
175
 
162
- The method returns the immutable updated user without replacing the active
163
- session. A successful stale response is rejected if another authentication
176
+ The method returns the immutable updated user and updates the local user
177
+ snapshot. A successful stale response is rejected if another authentication
164
178
  operation replaces that session in flight.
165
179
 
166
180
  ### List sessions
data/lib/volcano/auth.rb CHANGED
@@ -86,14 +86,21 @@ module Volcano
86
86
  owned_session(
87
87
  access_token: payload.fetch('access_token'),
88
88
  refresh_token: payload.fetch('refresh_token'),
89
- user_id: payload.fetch('user').fetch('id')
89
+ user_id: payload.fetch('user').fetch('id'),
90
+ user: payload.fetch('user')
90
91
  )
91
92
  end
92
93
 
93
94
  def complete_session?(session)
94
95
  return false unless session.is_a?(Session)
95
96
 
96
- session.to_h.values.all? { |value| value.is_a?(String) && !value.strip.empty? }
97
+ values = [session.access_token, session.refresh_token, session.user_id]
98
+ values.all? { |value| value.is_a?(String) && !value.strip.empty? } &&
99
+ matching_session_user?(session)
100
+ end
101
+
102
+ def matching_session_user?(session)
103
+ session.user.nil? || (session.user.is_a?(Hash) && session.user['id'] == session.user_id)
97
104
  end
98
105
 
99
106
  def owned_complete_session(session)
@@ -102,11 +109,12 @@ module Volcano
102
109
  owned_session(**session.to_h)
103
110
  end
104
111
 
105
- def owned_session(access_token:, refresh_token:, user_id:)
112
+ def owned_session(access_token:, refresh_token:, user_id:, user: nil)
106
113
  Session.new(
107
114
  access_token: access_token.dup.freeze,
108
115
  refresh_token: refresh_token.dup.freeze,
109
- user_id: user_id.dup.freeze
116
+ user_id: user_id.dup.freeze,
117
+ user: user
110
118
  )
111
119
  end
112
120
  end
@@ -20,10 +20,7 @@ module Volcano
20
20
  anonymous_conversion_response(current.access_token, email, password, metadata),
21
21
  200
22
22
  )
23
- user = build_user(user_payload(payload))
24
- raise Error::SessionChangedError unless @client.capture_session.first == generation
25
-
26
- user
23
+ cache_current_user(payload, generation)
27
24
  end
28
25
 
29
26
  private
@@ -30,10 +30,7 @@ module Volcano
30
30
  raise Error::AuthenticationError, 'No active session' unless current
31
31
 
32
32
  payload = Transport.body(confirm_email_change_response(current.access_token, token), 200)
33
- user = build_user(user_payload(payload))
34
- raise Error::SessionChangedError unless @client.capture_session.first == generation
35
-
36
- user
33
+ cache_current_user(payload, generation)
37
34
  end
38
35
 
39
36
  private
@@ -19,16 +19,21 @@ module Volcano
19
19
  raise Error::AuthenticationError, 'No active session' unless current
20
20
 
21
21
  payload = Transport.body(get_user_response(current.access_token), 200)
22
- user = build_user(user_payload(payload))
23
- raise Error::SessionChangedError unless @client.capture_session.first == generation
24
-
25
- user
22
+ cache_current_user(payload, generation)
26
23
  end
27
24
 
28
25
  alias get_user user
29
26
 
30
27
  private
31
28
 
29
+ def cache_current_user(payload, generation)
30
+ profile = user_payload(payload)
31
+ user = build_user(profile)
32
+ raise Error::SessionChangedError unless @client.update_session_user_if_current?(profile, generation)
33
+
34
+ user
35
+ end
36
+
32
37
  def get_user_response(access_token)
33
38
  Transport.invoke do
34
39
  @transport.auth_get_user(authorization: access_token)
@@ -50,10 +50,9 @@ module Volcano
50
50
 
51
51
  def store_if_current?(session, generation, event: :signed_in, notifications: nil)
52
52
  dispatch = @mutex.synchronize do
53
- callbacks = replace_if_current(session, generation, event)
54
- return false unless callbacks
53
+ return false unless generation == @generation
55
54
 
56
- enqueue_notification(callbacks, event, session)
55
+ enqueue_notification(replace(session, event), event, session)
57
56
  end
58
57
  return true unless dispatch
59
58
 
@@ -61,6 +60,16 @@ module Volcano
61
60
  true
62
61
  end
63
62
 
63
+ def update_user_if_current?(user, generation)
64
+ @mutex.synchronize do
65
+ return false unless generation == @generation && @session
66
+ raise Error::AuthenticationError, 'Profile belongs to a different user' unless user['id'] == @session.user_id
67
+
68
+ @session = Session.new(**@session.to_h, user: user)
69
+ true
70
+ end
71
+ end
72
+
64
73
  def subscribe(&callback)
65
74
  callback_id, dispatch = @mutex.synchronize do
66
75
  id, current = register(callback)
@@ -79,12 +88,6 @@ module Volcano
79
88
  @callbacks.keys
80
89
  end
81
90
 
82
- def replace_if_current(session, generation, event)
83
- return unless generation == @generation
84
-
85
- replace(session, event)
86
- end
87
-
88
91
  def register(callback)
89
92
  callback_id = @next_callback_id
90
93
  @next_callback_id += 1
@@ -130,7 +133,7 @@ module Volcano
130
133
  def notify(callback_ids, event, session)
131
134
  failure = nil
132
135
  callback_ids.each do |callback_id|
133
- notify_callback(callback_id, event, session)
136
+ @mutex.synchronize { @callbacks[callback_id] }&.call(event, session)
134
137
  rescue StandardError => e
135
138
  Warning.warn("Volcano auth-state callback failed (#{e.class})\n")
136
139
  rescue *CALLBACK_FAILURES => e
@@ -139,10 +142,5 @@ module Volcano
139
142
  end
140
143
  failure
141
144
  end
142
-
143
- def notify_callback(callback_id, event, session)
144
- callback = @mutex.synchronize { @callbacks[callback_id] }
145
- callback&.call(event, session)
146
- end
147
145
  end
148
146
  end
@@ -11,10 +11,7 @@ module Volcano
11
11
  update_user_response(current.access_token, password:, metadata:),
12
12
  200
13
13
  )
14
- user = build_user(user_payload(payload))
15
- raise Error::SessionChangedError unless @client.capture_session.first == generation
16
-
17
- user
14
+ cache_current_user(payload, generation)
18
15
  end
19
16
 
20
17
  private
@@ -89,6 +89,10 @@ module Volcano
89
89
  @auth_state.subscribe(...)
90
90
  end
91
91
 
92
+ def update_session_user_if_current?(...)
93
+ @auth_state.update_user_if_current?(...)
94
+ end
95
+
92
96
  private
93
97
 
94
98
  def database_with_token(name, token)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Volcano
4
- Session = Data.define(:access_token, :refresh_token, :user_id)
5
4
  AUTH_SESSION_ATTRIBUTES = %i[
6
5
  id user_id provider expires_at is_active is_current user_agent ip_address last_ip_address
7
6
  last_activity_at session_started_at created_at updated_at
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'time'
5
+
6
+ module Volcano
7
+ SESSION_USER_CODER = JSON::Coder.new(freeze: true, allow_duplicate_key: false) do |value|
8
+ value.instance_of?(Time) ? Time.at(value.to_r).utc.iso8601(9) : value
9
+ end
10
+ private_constant :SESSION_USER_CODER
11
+
12
+ # In-memory credentials with an optional, unverified local user snapshot.
13
+ Session = Data.define(:access_token, :refresh_token, :user_id, :user) do
14
+ def initialize(access_token:, refresh_token:, user_id:, user: nil)
15
+ super(access_token:, refresh_token:, user_id:, user: immutable_user(user))
16
+ end
17
+
18
+ private
19
+
20
+ def immutable_user(value)
21
+ SESSION_USER_CODER.load(SESSION_USER_CODER.dump(value))
22
+ rescue JSON::GeneratorError, JSON::NestingError
23
+ raise TypeError, 'Session user snapshot must contain JSON values or timestamps', cause: nil
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Volcano
4
- VERSION = '0.5.3'
4
+ VERSION = '0.7.0'
5
5
  end
data/lib/volcano.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative 'volcano/version'
4
4
  require_relative 'volcano/connection_string'
5
5
  require_relative 'volcano/models'
6
+ require_relative 'volcano/session'
6
7
  require_relative 'volcano/function_response'
7
8
  require_relative 'volcano/log_responses'
8
9
  require_relative 'volcano/storage_models'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volcano-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kong
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0.2'
68
+ - !ruby/object:Gem::Dependency
69
+ name: json
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.17'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.17'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: logger
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -560,6 +574,7 @@ files:
560
574
  - lib/volcano/realtime/protocol_recovery_position.rb
561
575
  - lib/volcano/realtime/reconnect.rb
562
576
  - lib/volcano/redaction.rb
577
+ - lib/volcano/session.rb
563
578
  - lib/volcano/sign_up_result.rb
564
579
  - lib/volcano/storage.rb
565
580
  - lib/volcano/storage_models.rb