Class: BSV::Auth::SessionManager

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/auth/session_manager.rb

Overview

Thread-safe store for PeerSession objects.

Supports dual-index lookup: by session_nonce (primary) or by peer_identity_key (secondary). Multiple concurrent sessions per peer identity key are supported — the most recently updated session is returned when looking up by identity key.

Sessions expire after default_ttl seconds (default: 3600). Pass default_ttl: nil to disable expiry entirely. Expired sessions are removed lazily on access; call #sweep_expired for proactive cleanup.

Matches the ts-sdk SessionManager dual-index design.

Instance Method Summary collapse

Constructor Details

#initialize(default_ttl: 3600) ⇒ SessionManager

Returns a new instance of SessionManager.

Parameters:

  • default_ttl (Integer, nil) (defaults to: 3600)

    seconds before a session expires; nil disables TTL entirely.



20
21
22
23
24
25
26
27
# File 'lib/bsv/auth/session_manager.rb', line 20

def initialize(default_ttl: 3600)
  @default_ttl = default_ttl
  # session_nonce -> PeerSession
  @by_nonce    = {}
  # peer_identity_key -> Set of session_nonces
  @by_identity = {}
  @mutex       = Mutex.new
end

Instance Method Details

#add_session(session) ⇒ Object

Adds a session to the manager.

Parameters:

Raises:

  • (ArgumentError)

    if session_nonce is blank



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/bsv/auth/session_manager.rb', line 33

def add_session(session)
  raise ArgumentError, 'session_nonce is required' unless session.session_nonce.is_a?(String) && !session.session_nonce.empty?

  @mutex.synchronize do
    @by_nonce[session.session_nonce] = session

    if session.peer_identity_key.is_a?(String)
      nonces = @by_identity[session.peer_identity_key] ||= []
      nonces << session.session_nonce unless nonces.include?(session.session_nonce)
    end
  end
end

#get_session(identifier) ⇒ PeerSession?

Retrieves a session by session_nonce or peer_identity_key.

When the identifier is a session nonce, returns that exact session. When the identifier is a peer identity key, returns the most recently updated non-expired session for that peer.

Returns nil if the session has expired (and removes it from the store).

Parameters:

  • identifier (String)

Returns:



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/bsv/auth/session_manager.rb', line 66

def get_session(identifier)
  @mutex.synchronize do
    direct = @by_nonce[identifier]
    if direct
      if expired_locked?(direct)
        remove_session_locked(direct)
        return nil
      end
      return direct
    end

    nonces = @by_identity[identifier]
    return nil if nonces.nil? || nonces.empty?

    best = nil
    expired_nonces = []
    nonces.each do |nonce|
      s = @by_nonce[nonce]
      next if s.nil?

      if expired_locked?(s)
        expired_nonces << s
        next
      end

      best = s if best.nil? || (s.last_update || 0) > (best.last_update || 0)
    end
    expired_nonces.each { |s| remove_session_locked(s) }
    best
  end
end

#remove_session(session) ⇒ Object

Removes a session from the manager.

Parameters:



101
102
103
# File 'lib/bsv/auth/session_manager.rb', line 101

def remove_session(session)
  @mutex.synchronize { remove_session_locked(session) }
end

#session?(identifier) ⇒ Boolean

Parameters:

  • identifier (String)

    session nonce or identity key

Returns:

  • (Boolean)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/bsv/auth/session_manager.rb', line 107

def session?(identifier)
  @mutex.synchronize do
    direct = @by_nonce[identifier]
    if direct
      if expired_locked?(direct)
        remove_session_locked(direct)
        return false
      end
      return true
    end

    nonces = @by_identity[identifier]
    return false if nonces.nil? || nonces.empty?

    has_active = false
    expired_sessions = []
    nonces.each do |nonce|
      s = @by_nonce[nonce]
      next if s.nil?

      if expired_locked?(s)
        expired_sessions << s
      else
        has_active = true
      end
    end
    expired_sessions.each { |s| remove_session_locked(s) }
    has_active
  end
end

#sweep_expiredInteger

Removes all expired sessions from the store.

Not called automatically — intended for use in long-running servers that want to proactively reclaim memory.

Returns:

  • (Integer)

    number of sessions removed



144
145
146
147
148
149
150
151
152
153
154
# File 'lib/bsv/auth/session_manager.rb', line 144

def sweep_expired
  removed = 0
  @mutex.synchronize do
    expired = @by_nonce.values.select { |s| expired_locked?(s) }
    expired.each do |s|
      remove_session_locked(s)
      removed += 1
    end
  end
  removed
end

#update_session(session) ⇒ Object

Updates an existing session (removes old references and re-adds).

Parameters:



49
50
51
52
53
54
# File 'lib/bsv/auth/session_manager.rb', line 49

def update_session(session)
  @mutex.synchronize do
    remove_session_locked(session)
    add_session_locked(session)
  end
end