Class: RSence::SessionStorage

Inherits:
Object
  • Object
show all
Defined in:
lib/rsence/sessionstorage.rb

Overview

SessionStorage doesn’t do anything by itself, it’s simply the superclass for SessionManager that does all the boring housekeeping duties.

Spliced of as a separate file to reduce the complexity of SessionManager.

Direct Known Subclasses

SessionManager

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSessionStorage

Returns a new instance of SessionStorage.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/rsence/sessionstorage.rb', line 27

def initialize
  ## Session data storage (by ses_id)
  @sessions     = {}

  ## Session id by key
  @session_keys = {}

  ## Session id by cookie key
  @session_cookie_keys = {}

  @clone_origins = {
    # id => [ id, id, id ... ]
  }
  @clone_sources = {
    # id => id
  }
  @clone_targets = {
    # id => [ id, id, id ... ]
  }

  ## Disposable keys (new ses_key each request)
  @config = RSence.config[:session_conf]

  @db_uri = RSence.config[:database][:ses_db]

  if db_test
    @db_avail = true
    db_init
  else
    @db_avail = false
    puts "Warning: Session database is not available. Can't use persistent sessions."
    @id_counter = 0
  end

  @accept_requests = true

end

Instance Attribute Details

#accept_requestsObject (readonly)

Returns the value of attribute accept_requests.



65
66
67
# File 'lib/rsence/sessionstorage.rb', line 65

def accept_requests
  @accept_requests
end

#dbObject

Returns the value of attribute db.



26
27
28
# File 'lib/rsence/sessionstorage.rb', line 26

def db
  @db
end

Instance Method Details

#create_session_tableObject

Creates the ‘rsence_session’ table, if necessary This table is used to store sessions



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/rsence/sessionstorage.rb', line 113

def create_session_table
  db_open
  unless @db.table_exists?(:rsence_session)
    puts "Creating session table..." if RSence.args[:verbose]
    @db.create_table :rsence_session do
      primary_key( :id )
      column( :cookie_key,  String  )
      column( :ses_key,     String  )
      column( :ses_timeout, Integer )
      column( :user_id,     Integer )
      column( :ses_active,  TrueClass )
      column( :ses_stored,  Integer )
      column( :ses_data,    File    )
    end
  end
  db_close
end

#create_uploads_tableObject

Creates the ‘rsence_uploads’ table, if necessary This table is used for storing temporary uploads before processing



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/rsence/sessionstorage.rb', line 147

def create_uploads_table
  db_open
  unless @db.table_exists?(:rsence_uploads)
    puts "Creating uploads table..." if RSence.args[:verbose]
    @db.create_table :rsence_uploads do
      primary_key( :id )
      foreign_key( :ses_id, :rsence_session )
      column( :upload_date, Integer )
      column( :upload_done, Integer )
      column( :ticket_id,   String  )
      column( :file_size,   Integer )
      column( :file_name,   String  )
      column( :file_mime,   String  )
      column( :file_data,   File    )
    end
  end
  db_close
end

#create_version_tableObject

Creates the ‘rsence_version’ table, if necessary This table is used to check for the need of future database upgrades



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rsence/sessionstorage.rb', line 133

def create_version_table
  db_open
  unless @db.table_exists?(:rsence_version)
    puts "Creating version info table..." if RSence.args[:verbose]
    @db.create_table :rsence_version do
      Integer :version
    end
    @db[:rsence_version].insert(:version => 586)
  end
  db_close
end

#db_closeObject



98
99
100
# File 'lib/rsence/sessionstorage.rb', line 98

def db_close
  @db.disconnect
end

#db_initObject

Checks database connectivity and loads stored sessions from the database



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/rsence/sessionstorage.rb', line 175

def db_init

  create_session_table
  create_version_table
  create_uploads_table

  ## Used for future upgrades:
  # version = table_version

  return true
end

#db_openObject



102
103
104
105
106
107
108
109
# File 'lib/rsence/sessionstorage.rb', line 102

def db_open
  # work-around for windows (drive letters causing confusion)
  if @db_uri.start_with?('sqlite://')
    @db = Sequel.sqlite( @db_uri.split('sqlite://')[1] )
  else
    @db = Sequel.connect(@db_uri)
  end
end

#db_testObject



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/rsence/sessionstorage.rb', line 67

def db_test
  begin
    db_open
    if @db.table_exists?(:rsence_test)
      @db.drop_table(:rsence_test)
    end
    @db.create_table(:rsence_test) { primary_key :id; String :test }
    test_id = @db[:rsence_test].insert( :test => 'TestFoo' )
    @db[:rsence_test].filter( :id => test_id ).update( :test => 'TestFoo2' )
    @db[:rsence_test].filter( :id => test_id ).delete
    @db[:rsence_test].delete
    @db.drop_table(:rsence_test)
    db_close
    return true
  rescue => e
    if RSence.args[:debug]
      err_msg = [
        "ERROR: SessionStorage couldn't open database",
        "#{e.class.to_s}, #{e.message}",
        "Backtrace:",
        "\t"+e.backtrace.join("\n\t")
      ].join("\n")+"\n"
      $stderr.write( err_msg )
    elsif RSence.args[:verbose]
      puts "Failed to open database '#{@db_uri}'."
      puts "Run RSence in debug mode for full error output."
    end
    return false
  end
end

#expire_session(ses_id) ⇒ Object

Expires a session by its identifier



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/rsence/sessionstorage.rb', line 296

def expire_session( ses_id )

  return unless @sessions.has_key? ses_id

  ses_data = @sessions[ ses_id ]

  # Makes the session invalid for xhr's by deleting its key
  @session_keys.delete( ses_data[:ses_key] )

  # Makes the session invalid for all requests by deleting its cookie key
  @session_cookie_keys.delete( ses_data[:cookie_key] )

  # Deletes the session data itself
  @sessions.delete( ses_id )

  # Removes all ticket-based storage bound to the session
  if @plugins
    @plugins.delegate( :expire_ses, ses_data )
    @plugins.delegate( :expire_ses_id, ses_id )
  end

  # target -> source cleanup
  if @clone_sources.has_key?( ses_id )
    source_id = @clone_sources[ ses_id ]
    @clone_sources.delete( ses_id ) if @clone_sources.has_key?( ses_id )
    @clone_targets[ source_id ].delete( ses_id ) if @clone_targets.has_key?( source_id )
  end

  # source -> targets cleanup
  if @clone_targets.has_key?( ses_id )
    @clone_targets[ ses_id ].each do |target_id|
      @clone_sources.delete( target_id ) if @clone_sources.has_key?( target_id )
    end
    @clone_targets.delete( ses_id ) if @clone_targets.has_key?( ses_id )
  end

  if @db_avail
    db_open
    # Deletes the session's row from the database
    @db[:rsence_uploads].filter(:ses_id => ses_id).delete
    @db[:rsence_session].filter(:id => ses_id).delete
    db_close
  end

end

#expire_sessionsObject

Expires all sessions that meet the timeout criteria



343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/rsence/sessionstorage.rb', line 343

def expire_sessions

  # Loop through all sessions in memory:
  @sessions.each_key do |ses_id|
  
    timed_out = @sessions[ ses_id ][:timeout] < Time.now.to_i
  
    ## Deletes the session, if the session is too old
    expire_session( ses_id ) if timed_out
  
  end
end

#new_ses_id(cookie_key, ses_key, timeout_secs, user_id = 0) ⇒ Object

Returns a new, unique session identifier by storing the params to the database



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/rsence/sessionstorage.rb', line 279

def new_ses_id( cookie_key, ses_key, timeout_secs, user_id=0 )
  unless @db_avail
    @id_counter += 1
    return @id_counter
  end
  db_open
  new_id = @db[:rsence_session].insert(
    :cookie_key  => cookie_key,
    :ses_key     => ses_key,
    :ses_timeout => timeout_secs,
    :user_id     => user_id
  )
  db_close
  return new_id
end

#reset_sessionsObject

Deletes all rows from rsence_session as well as rsence_uploads



188
189
190
191
192
193
194
195
196
197
# File 'lib/rsence/sessionstorage.rb', line 188

def reset_sessions
  unless @db_avail
    puts "Warning: Can't reset sessions: No database!" if RSence.args[:verbose]
    return
  end
  db_open
  @db[:rsence_session].delete if @db.table_exists?(:rsence_session)
  @db[:rsence_uploads].delete if @db.table_exists?(:rsence_uploads)
  db_close
end

#restore_sessionsObject

Restores all saved sessions from db to ram



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/rsence/sessionstorage.rb', line 200

def restore_sessions
  unless @db_avail
    puts "Warning: Can't restore sessions: No database!" if RSence.args[:verbose]
    return
  end
  puts "Restoring sessions..." if RSence.args[:verbose]
  db_open
  @db[:rsence_session].all do |ses_row|
    ses_id = ses_row[:id]
    ses_data_dump = ses_row[:ses_data]
    
    if ses_data_dump == nil
      @db[:rsence_session].filter(:id => ses_id).delete
      @db[:rsence_uploads].filter(:ses_id => ses_id).delete
    else
      begin
        ses_data = Marshal.load( ses_data_dump )
        ses_key = ses_data[:ses_key]
        @sessions[ses_id] = ses_data
        @session_keys[ ses_key ] = ses_id
        @session_cookie_keys[ ses_data[:cookie_key] ] = ses_id
        if @plugins
          @plugins.delegate( :load_ses_id, ses_id )
          @plugins.delegate( :load_ses, ses_data )
        end
      rescue => e
        warn "Unable to load session: #{ses_id}, because: #{e.message}"
        @db[:rsence_session].filter(:id => ses_id).delete
        @db[:rsence_uploads].filter(:ses_id => ses_id).delete
      end
    end
  end
  db_close
end

#shutdownObject

Shut-down signal, triggers store_sessions for now



270
271
272
273
274
275
# File 'lib/rsence/sessionstorage.rb', line 270

def shutdown
  @accept_requests = false
  puts "Session shutdown in progress..." if RSence.args[:verbose]
  store_sessions
  puts "Session shutdown complete." if RSence.args[:verbose]
end

#store_sessionsObject

Stores all sessions to db from ram



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/rsence/sessionstorage.rb', line 236

def store_sessions
  unless @db_avail
    puts "Warning: Can't store sessions: No database!" if RSence.args[:verbose]
    return
  end
  puts "Storing sessions..." if RSence.args[:verbose]
  db_open
  ses_ids = @sessions.keys
  ses_ids.each do |ses_id|
    ses_data = @sessions[ses_id]
    if @plugins
      @plugins.delegate( :dump_ses, ses_data )
      @plugins.delegate( :dump_ses_id, ses_id )
    end
    begin
      ses_data_dump = Marshal.dump( ses_data )
      @db[:rsence_session].filter(
        :id => ses_id
      ).update(
        :cookie_key  => ses_data[:cookie_key],
        :ses_key     => ses_data[:ses_key],
        :user_id     => ses_data[:user_id],
        :ses_data    => ses_data_dump.to_sequel_blob,
        :ses_timeout => ses_data[:timeout],
        :ses_stored  => Time.now.to_i
      )
    rescue => e
      warn "Unable to dump session: #{ses_id}, because: #{e.message}"
    end
  end
  db_close
end

#table_versionObject

returns the version in the rsence_version table



167
168
169
170
171
172
# File 'lib/rsence/sessionstorage.rb', line 167

def table_version
  db_open
  rsence_version = @db[:rsence_version].select(:version).all[0][:version]
  db_close
  return rsence_version
end