Class: TunnelRb::Server::TokenStore

Inherits:
Object
  • Object
show all
Defined in:
lib/tunnel_rb/server/token_store.rb

Overview

Persistent token <-> subdomain mapping with TTL. Tokens belonging to a currently-connected client never expire (they’re refreshed implicitly). Tokens of disconnected clients age out after TOKEN_TTL.

Constant Summary collapse

DEFAULT_PATH =
"/tmp/tunnel-rb-server-tokens.json".freeze
TOKEN_TTL =

seconds

24 * 3600
CLEANUP_INTERVAL =
600

Instance Method Summary collapse

Constructor Details

#initialize(path: DEFAULT_PATH, active_subdomains:, logger: nil) ⇒ TokenStore

active_subdomains: callable returning a Set/Array of subdomains held by currently-connected clients. Used to keep their tokens alive and to avoid colliding when generating new subdomains.



21
22
23
24
25
26
27
# File 'lib/tunnel_rb/server/token_store.rb', line 21

def initialize(path: DEFAULT_PATH, active_subdomains:, logger: nil)
  @path = path
  @active_subdomains = active_subdomains
  @logger = logger
  @mutex = Mutex.new
  @tokens = load_from_disk
end

Instance Method Details

#cleanup_expiredObject

Drops tokens whose subdomain has no active client and whose TTL has elapsed. Called both periodically and on every registration.



79
80
81
# File 'lib/tunnel_rb/server/token_store.rb', line 79

def cleanup_expired
  @mutex.synchronize { cleanup_unlocked }
end

#cleanup_loop(interval: CLEANUP_INTERVAL, stop_flag: nil) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/tunnel_rb/server/token_store.rb', line 83

def cleanup_loop(interval: CLEANUP_INTERVAL, stop_flag: nil)
  loop do
    interval.times do
      return if stop_flag && stop_flag.call

      sleep 1
    end
    return if stop_flag && stop_flag.call

    cleanup_expired
  end
end

#generate_subdomainObject



68
69
70
71
72
73
74
75
# File 'lib/tunnel_rb/server/token_store.rb', line 68

def generate_subdomain
  @mutex.synchronize do
    loop do
      candidate = SecureRandom.hex(4)
      return candidate unless taken_unlocked?(candidate)
    end
  end
end

#issue(subdomain) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/tunnel_rb/server/token_store.rb', line 29

def issue(subdomain)
  @mutex.synchronize do
    loop do
      token = SecureRandom.hex(16)
      next if @tokens.key?(token)

      @tokens[token] = entry(subdomain)
      persist
      return token
    end
  end
end

#persist_nowObject



96
97
98
# File 'lib/tunnel_rb/server/token_store.rb', line 96

def persist_now
  @mutex.synchronize { persist }
end

#resolve(token) ⇒ Object

Returns the subdomain bound to ‘token` if the token is valid, or nil. A token is valid if either (a) its subdomain currently has an active client, or (b) the token has not expired yet.



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/tunnel_rb/server/token_store.rb', line 55

def resolve(token)
  @mutex.synchronize do
    record = @tokens[token]
    return nil unless record

    sub = subdomain_of(record)
    return sub if active?(sub)
    return nil if expired?(record)

    sub
  end
end

#revoke(token) ⇒ Object



42
43
44
45
46
47
48
49
50
# File 'lib/tunnel_rb/server/token_store.rb', line 42

def revoke(token)
  return unless token

  @mutex.synchronize do
    next unless @tokens.delete(token)

    persist
  end
end

#to_hObject

Snapshot for tests/diagnostics.



101
102
103
# File 'lib/tunnel_rb/server/token_store.rb', line 101

def to_h
  @mutex.synchronize { @tokens.dup }
end