From c3841fa563de82b7cae67daa68458cbba6a131e4 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Sat, 31 Jul 2010 04:03:13 -0500 Subject: [PATCH] Updated the data objects sessions table to include ip and agent. No longer insert user agent entries on each request cycle, but on session creates. --- lib/harbor/application.rb | 3 +- lib/harbor/contrib/session/data_mapper.rb | 9 ++-- lib/harbor/contrib/session/data_objects.rb | 41 +++++++++++-------- lib/harbor/contrib/stats.rb | 11 ++++- .../events/session_created_event_context.rb | 15 +++++++ lib/harbor/session.rb | 8 +++- lib/harbor/session/abstract.rb | 2 +- lib/harbor/session/cookie.rb | 9 +++- lib/harbor/version.rb | 2 +- test/contrib/session/data_objects_test.rb | 11 ++--- 10 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 lib/harbor/events/session_created_event_context.rb diff --git a/lib/harbor/application.rb b/lib/harbor/application.rb index 5f25ad4..dab4b5d 100644 --- a/lib/harbor/application.rb +++ b/lib/harbor/application.rb @@ -3,14 +3,15 @@ require "yaml" +require Pathname(__FILE__).dirname + "events" require Pathname(__FILE__).dirname + "request" require Pathname(__FILE__).dirname + "response" require Pathname(__FILE__).dirname + "block_io" require Pathname(__FILE__).dirname + "zipped_io" -require Pathname(__FILE__).dirname + "events" require Pathname(__FILE__).dirname + "events" + "dispatch_request_event" require Pathname(__FILE__).dirname + "events" + "not_found_event" require Pathname(__FILE__).dirname + "events" + "application_exception_event" +require Pathname(__FILE__).dirname + "events" + "session_created_event_context" require Pathname(__FILE__).dirname + "event_context" require Pathname(__FILE__).dirname + "messages" diff --git a/lib/harbor/contrib/session/data_mapper.rb b/lib/harbor/contrib/session/data_mapper.rb index eeb2738..f132821 100644 --- a/lib/harbor/contrib/session/data_mapper.rb +++ b/lib/harbor/contrib/session/data_mapper.rb @@ -16,7 +16,7 @@ class Session # A basic Session resource is defined for you. ## class DataMapper < Harbor::Session::Abstract - + class SessionHash < Hash def initialize(instance) super() @@ -43,14 +43,17 @@ def instance end end - def self.load_session(cookie) + def self.load_session(delegate, cookie, request = nil) session = if expire_after = Harbor::Session.options[:expire_after] ::Session.first(:id => cookie, :updated_at.gte => Time.now - expire_after) else ::Session.get(cookie) end - session ||= ::Session.create + unless session + session = ::Session.create + delegate.session_created(session.id, request.remote_ip, request.env["HTTP_USER_AGENT"]) + end SessionHash.new(session) end diff --git a/lib/harbor/contrib/session/data_objects.rb b/lib/harbor/contrib/session/data_objects.rb index 877ee73..3b06bd6 100644 --- a/lib/harbor/contrib/session/data_objects.rb +++ b/lib/harbor/contrib/session/data_objects.rb @@ -15,8 +15,8 @@ class Session ## class DataObjects < Harbor::Session::Abstract class UnsupportedDatabaseError < StandardError; end - - class SessionHash + + class SessionHash def initialize(raw) @raw = raw @data = nil @@ -24,7 +24,7 @@ def initialize(raw) end def [](key) - if key == :session_id or key == :user_id + if [:session_id, :user_id, :remote_ip, :user_agent_raw].include?(key) @raw[key] else load_data![key] @@ -35,9 +35,8 @@ def []=(key, value) raise ArgumentError.new("You cannot manually set the session_id for a session.") if key == :session_id @dirty = true - - if key == :user_id - @raw[:user_id] = value + if [:session_id, :user_id, :remote_ip, :user_agent_raw].include?(key) + @raw[key] = value else load_data![key] = value end @@ -57,14 +56,18 @@ def load_data! @data = DataObjects.load(@raw[:data]) end + def clear + @data.clear + end + def to_hash @data end end - def self.load_session(cookie) + def self.load_session(delegate, cookie, request = nil) create_session_table unless session_table_exists? - + if cookie raw_session = if expire_after = Harbor::Session.options[:expire_after] get_raw_session(cookie, Time.now - expire_after) @@ -73,7 +76,7 @@ def self.load_session(cookie) end end - raw_session ||= create_session + raw_session ||= create_session(delegate, {}, request) SessionHash.new(raw_session) end @@ -116,21 +119,25 @@ def self.create_session_table @table_exists = true end - def self.create_session(data = {}) + def self.create_session(delegate, data = {}, request = nil) session_id = `uuidgen`.chomp user_id = data.delete(:user_id) + remote_ip = request ? request.remote_ip : nil + user_agent_raw = request ? request.env["HTTP_USER_AGENT"] : nil + data = self.dump(data) now = Time.now - statement = "INSERT INTO sessions (id, data, user_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?);" - execute(statement, session_id, data, user_id, now, now) + statement = "INSERT INTO sessions (id, data, user_id, remote_ip, user_agent_raw, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?);" + execute(statement, session_id, data, user_id, remote_ip, user_agent_raw, now, now) + delegate.session_created(session_id, remote_ip, user_agent_raw) - {:session_id => session_id, :data => data, :user_id => user_id} + {:session_id => session_id, :data => data, :user_id => user_id, :remote_ip => remote_ip, :user_agent_raw => user_agent_raw} end def self.get_raw_session(cookie, updated_at=nil) - query = "SELECT id, data, user_id FROM sessions WHERE id = ? " + query = "SELECT id, data, user_id, remote_ip, user_agent_raw FROM sessions WHERE id = ? " params = [cookie] if updated_at @@ -148,6 +155,8 @@ def self.get_raw_session(cookie, updated_at=nil) raw[:session_id] = reader.values[0] raw[:data] = reader.values[1] raw[:user_id] = reader.values[2] + raw[:remote_ip] = reader.values[3] + raw[:user_agent_raw] = reader.values[4] else raw = nil end @@ -193,9 +202,9 @@ def self.with_connection def self.create_session_table_sql case scheme when :sqlite3 - "CREATE TABLE sessions (id VARCHAR(50) NOT NULL, user_id INTEGER, data TEXT, created_at DATETIME, updated_at DATETIME, PRIMARY KEY(id))" + "CREATE TABLE sessions (id VARCHAR(50) NOT NULL, user_id INTEGER, remote_ip INET, user_agent_raw TEXT, data TEXT, created_at DATETIME, updated_at DATETIME, PRIMARY KEY(id))" when :postgres - "CREATE TABLE sessions (id VARCHAR(50) NOT NULL, user_id INTEGER, data TEXT, created_at TIMESTAMP, updated_at TIMESTAMP, PRIMARY KEY(id))" + "CREATE TABLE sessions (id VARCHAR(50) NOT NULL, user_id INTEGER, remote_ip INET, user_agent_raw TEXT, data TEXT, created_at TIMESTAMP, updated_at TIMESTAMP, PRIMARY KEY(id))" else raise UnsupportedDatabaseError.new("Only SQLite3 and PostgreSQL are supported at the moment") end diff --git a/lib/harbor/contrib/stats.rb b/lib/harbor/contrib/stats.rb index 6b7b523..d596d7e 100644 --- a/lib/harbor/contrib/stats.rb +++ b/lib/harbor/contrib/stats.rb @@ -19,6 +19,14 @@ def self.orm end end +Harbor::Session.register_event_handler(:session_created) do |event| + if orm = Harbor::Contrib::Stats.orm + orm::UserAgent.create(event.session_id, event.remote_ip, event.user_agent) + else + warn "Harbor::Contrib::Stats::orm must be set to generate statistics." + end +end + Harbor::Application.register_event_handler(:request_complete) do |event| request = event.request response = event.response @@ -28,9 +36,8 @@ def self.orm # We only record a PageView if we get a 200 and it's an actual page rendering, not providing an image or downloading a file orm::PageView.create(session.id, request.uri, request.referrer) if %w(text/html text/xml text/json).include?(response.content_type) && response.status == 200 - orm::UserAgent.create(session.id, request.remote_ip, request.env["HTTP_USER_AGENT"]) end else warn "Harbor::Contrib::Stats::orm must be set to generate statistics." end -end +end \ No newline at end of file diff --git a/lib/harbor/events/session_created_event_context.rb b/lib/harbor/events/session_created_event_context.rb new file mode 100644 index 0000000..1b4c991 --- /dev/null +++ b/lib/harbor/events/session_created_event_context.rb @@ -0,0 +1,15 @@ +module Harbor + module Events + class SessionCreatedEventContext + + attr_reader :session_id, :remote_ip, :user_agent + + def initialize(session_id, remote_ip, user_agent) + @session_id = session_id + @remote_ip = remote_ip + @user_agent = user_agent + end + + end + end +end diff --git a/lib/harbor/session.rb b/lib/harbor/session.rb index d284ed7..1e4407d 100644 --- a/lib/harbor/session.rb +++ b/lib/harbor/session.rb @@ -4,6 +4,8 @@ module Harbor class Session + include Harbor::Events + DEFAULT_OPTIONS = { :key => "harbor.session", :domain => nil, @@ -36,9 +38,13 @@ def initialize(request, key = nil) @cookie = request.cookies[key] || request.cookies[@options[:key]] @store = self.class.options[:store] @request = request - @data ||= @store.load_session(@cookie) + @data ||= @store.load_session(self, @cookie, @request) end + def session_created(session_id, remote_ip, user_agent_raw) + raise_event2(:session_created, Harbor::Events::SessionCreatedEventContext.new(session_id, remote_ip, user_agent_raw)) + end + def key @options[:key] end diff --git a/lib/harbor/session/abstract.rb b/lib/harbor/session/abstract.rb index 61486ea..255eeb0 100644 --- a/lib/harbor/session/abstract.rb +++ b/lib/harbor/session/abstract.rb @@ -10,7 +10,7 @@ class Abstract # Receives the raw cookie data, and should return a hash # of the data for the session. ## - def self.load_session(cookie) + def self.load_session(delegate, cookie, request = nil) Marshal.load(cookie.unpack("m*")[0]) rescue {} end diff --git a/lib/harbor/session/cookie.rb b/lib/harbor/session/cookie.rb index e544eda..7e7206d 100644 --- a/lib/harbor/session/cookie.rb +++ b/lib/harbor/session/cookie.rb @@ -6,9 +6,14 @@ class Session # session_id's. ## class Cookie < Abstract - def self.load_session(cookie) + def self.load_session(delegate, cookie, request = nil) cookie = super - cookie[:session_id] ||= `uuidgen`.chomp + + unless cookie[:session_id] + cookie[:session_id] = `uuidgen`.chomp + delegate.session_created(cookie[:session_id], request.remote_ip, request.env["HTTP_USER_AGENT"]) + end + cookie end end diff --git a/lib/harbor/version.rb b/lib/harbor/version.rb index 76b312f..41371ba 100644 --- a/lib/harbor/version.rb +++ b/lib/harbor/version.rb @@ -1,3 +1,3 @@ module Harbor - VERSION = "0.18.37" + VERSION = "0.18.38" end diff --git a/test/contrib/session/data_objects_test.rb b/test/contrib/session/data_objects_test.rb index 5d31eb5..f03a4af 100644 --- a/test/contrib/session/data_objects_test.rb +++ b/test/contrib/session/data_objects_test.rb @@ -29,7 +29,7 @@ def setup end end - def teardown + def teardown Harbor::Session.configure do |session| session[:store] = Harbor::Session::Cookie session.delete(:connection_uri) @@ -163,7 +163,7 @@ def test_data_is_lazy_parsed def assert_session_valid_and_save(time_elapsed, request, value, new_value) Time.warp(time_elapsed) do request_session = Harbor::Session.new(request) - + assert_equal 1, session_records_count assert_equal value, request_session[:value] @@ -187,15 +187,16 @@ def get_raw_session(cookie, updated_at=nil) end def create_session(data = {}) - Harbor::Contrib::Session::DataObjects.create_session(data) + @_session = Harbor::Session.new(CookieRequest.new) + Harbor::Contrib::Session::DataObjects.create_session(@_session, data) end def session_records_count count = 0 Harbor::Contrib::Session::DataObjects.with_connection do |connection| - cmd = connection.create_command("SELECT COUNT(id) FROM sessions;") - reader = cmd.execute_reader + cmd = connection.create_command("SELECT COUNT(id) FROM sessions WHERE id NOT IN (?);") + reader = cmd.execute_reader(@_session ? @_session.id : '') if reader.next! then count = reader.values[0] end reader.close end