From 9b5ded80aecc8426e84c503518084d7c5a58fdf2 Mon Sep 17 00:00:00 2001 From: Niklaus Giger Date: Tue, 9 May 2017 13:58:09 +0200 Subject: [PATCH] SBSM: Changes needed for rack, viral modules and KnownUser --- lib/sbsm/app.rb | 34 +++++++++++++++++--------- lib/sbsm/logger.rb | 2 ++ lib/sbsm/lookandfeel.rb | 6 ++--- lib/sbsm/session.rb | 53 ++++++++++++++++++++++++---------------- lib/sbsm/session_store.rb | 10 ++++---- lib/sbsm/state.rb | 1 + test/simple_sbsm.rb | 2 +- test/test_application.rb | 59 ++++++++++++++++++++++++++++++--------------- test/test_customized_app.rb | 1 + test/test_session.rb | 16 +++++++----- 10 files changed, 117 insertions(+), 67 deletions(-) diff --git a/lib/sbsm/app.rb b/lib/sbsm/app.rb index 8003a5d..59e11e3 100644 --- a/lib/sbsm/app.rb +++ b/lib/sbsm/app.rb @@ -30,19 +30,31 @@ require 'sbsm/session_store' require 'sbsm/trans_handler' require 'sbsm/validator' require 'mimemagic' +require 'pry' module SBSM ### - # App as a member of session + class App + attr_reader :auth + + def initialize + SBSM.info "initialize #{self.class}" + end - def initialize() - SBSM.info "initialize" + def set_auth(auth) + SBSM.info "set_auth to #{auth.class}" + @auth = auth end + def auth + @auth + end + end class RackInterface attr_accessor :session # thread variable! + attr_reader :app, :session_store SESSION_ID = '_session_id' # Base class for a SBSM based WebRick HTTP server @@ -51,7 +63,8 @@ module SBSM # # === optional arguments # - # * +app+ - A Ruby class used by the session + # * +app+ - the App for the Rack-Interface + # * +auth+ - A sbsm:Auth needed for auto_session login # * +validator+ - A Ruby class overriding the SBSM::Validator class # * +trans_handler+ - A Ruby class overriding the SBSM::TransHandler class # * +session_class+ - A Ruby class overriding the SBSM::Session class @@ -64,7 +77,8 @@ module SBSM # Look at steinwies.ch # * https://github.com/zdavatz/steinwies.ch (simple, mostly static files, one form, no persistence layer) # - def initialize(app:, + def initialize(app: , + auth: nil, validator: nil, trans_handler: nil, session_class: nil, @@ -75,7 +89,8 @@ module SBSM ) @@last_session = nil @app = app - SBSM.info "initialize validator #{validator} th #{trans_handler} cookie #{cookie_name} session #{session_class} app #{app} multi_threaded #{multi_threaded}" + @app.set_auth(auth) if auth + SBSM.info "initialize validator #{validator} th #{trans_handler} cookie #{cookie_name} session #{session_class} app #{app.class} auth_class #{auth.class} multi_threaded #{multi_threaded}" @session_store = SessionStore.new(app: app, persistence_layer: persistence_layer, trans_handler: trans_handler, @@ -86,10 +101,6 @@ module SBSM multi_threaded: multi_threaded) end - def last_session - @@last_session - end - def call(env) ## mimick sbsm/lib/app.rb request = Rack::Request.new(env) response = Rack::Response.new @@ -115,6 +126,7 @@ module SBSM Thread.current.thread_variable_set(:session, @session_store[session_id]) session = Thread.current.thread_variable_get(:session) + @@last_session = session SBSM.debug "starting session_id #{session_id} session #{session.class} #{request.path}: cookies #{@cookie_name} are #{request.cookies} @cgi #{@cgi.class}" res = session.process_rack(rack_request: request) response.write res @@ -129,8 +141,6 @@ module SBSM end response.set_cookie(SESSION_ID, { :value => session_id, :path => '/' }) unless request.cookies[SESSION_ID] - # response.set_cookie(SBSM::Session.get_cookie_name, session_id) - @@last_session = session if response.headers['Set-Cookie'].to_s.index(session_id) SBSM.debug "finish session_id.1 #{session_id}: matches response.headers['Set-Cookie']" else diff --git a/lib/sbsm/logger.rb b/lib/sbsm/logger.rb index d72485a..da6f426 100644 --- a/lib/sbsm/logger.rb +++ b/lib/sbsm/logger.rb @@ -36,10 +36,12 @@ module SBSM # by the different process. Should probably later be replaced by a Rack based logger def self.info(msg) info = "#{File.basename(caller[0])} #{msg}" + puts info if defined? Pry @@logger.info(info) if @@logger end def self.debug(msg) info = "#{File.basename(caller[0])} #{msg}" + puts info if defined? Pry @@logger.debug(info) if @@logger end end diff --git a/lib/sbsm/lookandfeel.rb b/lib/sbsm/lookandfeel.rb index 6077c95..1241df7 100644 --- a/lib/sbsm/lookandfeel.rb +++ b/lib/sbsm/lookandfeel.rb @@ -132,7 +132,7 @@ module SBSM @session.navigation end def resource(rname, rstr=nil) - collect_resource([ self::class::RESOURCE_BASE, @session.flavor ], + collect_resource([ self::class::RESOURCE_BASE, @session.flavor ], rname, rstr) end def resource_external(rname) @@ -166,8 +166,8 @@ module SBSM end end def _collect_resource(base, part, rstr) - [ @session.http_protocol + ':/', - @session.server_name, + [ @session.http_protocol + ':/', + @session.server_name, base, part, rstr].flatten.compact.join('/') end def set_dictionary(language) diff --git a/lib/sbsm/session.rb b/lib/sbsm/session.rb index 3289ae1..39bf587 100644 --- a/lib/sbsm/session.rb +++ b/lib/sbsm/session.rb @@ -34,9 +34,11 @@ require 'sbsm/lookandfeelfactory' require 'delegate' module SBSM - class Session + class Session < SimpleDelegator + attr_reader :user, :active_thread, :key, :cookie_input, :cookie_name, :unsafe_input, :valid_input, :request_path, :cgi, :attended_states + include DRbUndumped attr_accessor :validator, :trans_handler, :app PERSISTENT_COOKIE_NAME = "sbsm-persistent-cookie" DEFAULT_FLAVOR = 'sbsm' @@ -111,40 +113,44 @@ module SBSM multi_threaded: false) SBSM.info "initialize th #{trans_handler} validator #{validator} app #{app.class}" @app = app - @unknown_user = unknown_user - @unknown_user ||= self.class::UNKNOWN_USER - @validator = validator - @validator ||= Validator.new + if unknown_user.is_a?(Class) + @unknown_user = unknown_user.new(self) + else + @unknown_user = unknown_user + end + @unknown_user_class = @unknown_user.class + @validator = (validator && validator.new) || Validator.new fail "invalid validator #{@validator}" unless @validator.is_a?(SBSM::Validator) - @trans_handler = trans_handler - @trans_handler ||= TransHandler.instance + @trans_handler = trans_handler || TransHandler.instance fail "invalid trans_handler #{@trans_handler}" unless @trans_handler.is_a?(SBSM::TransHandler) @cookie_name = cookie_name @cookie_name ||= self.class::PERSISTENT_COOKIE_NAME - @@cookie_name = @cookie_name @attended_states = {} @persistent_user_input = {} touch() reset_input() reset_cookie() - @user = @unknown_user - @unknown_user_class - @unknown_user_class = @unknown_user.class + @user = SBSM::UnknownUser.new @variables = {} @cgi = CGI.initialize_without_offline_prompt('html4') @multi_threaded = multi_threaded @mutex = multi_threaded ? Mutex.new: @@mutex @active_thread = nil - SBSM.debug "session initialized #{self} with @cgi #{@cgi} multi_threaded #{multi_threaded} app #{app.object_id}" + SBSM.debug "session initialized #{self} with @cgi #{@cgi} multi_threaded #{multi_threaded} app #{app.object_id} and user #{@user.class} @unknown_user #{@unknown_user.class}" end def self.get_cookie_name - @@cookie_name + @cookie_name end def method_missing(symbol, *args, &block) # Replaces old dispatch to DRb @app.send(symbol, *args, &block) + rescue => error + puts error + puts error.backtrace.join("\n") + binding.pry + raise error end def unknown_user - @unknown_user_class.new + @unknown_user || @unknown_user_class.new end def age(now=Time.now) now - @mtime @@ -209,12 +215,13 @@ module SBSM begin @request_method =rack_request.request_method @request_path = rack_request.path + @server_name = rack_request.env['SERVER_NAME'] logout unless @active_state validator.reset_errors() if validator && validator.respond_to?(:reset_errors) import_user_input(rack_request) import_cookies(rack_request) @state = active_state.trigger(event()) - SBSM.debug "active_state.trigger state #{@state.object_id} remember #{persistent_user_input(:remember).inspect}" + SBSM.debug "active_state.trigger state #{@state.object_id} #{@state.class} remember #{persistent_user_input(:remember).inspect}" #FIXME: is there a better way to distinguish returning states? # ... we could simply refuse to init if event == :sort, but that # would not solve the problem cleanly, I think. @@ -223,11 +230,11 @@ module SBSM @state.init end unless @state.volatile? - SBSM.debug "Changing from #{@active_state.object_id} to state #{@state.class} #{@state.object_id} remember #{persistent_user_input(:remember).inspect}" + SBSM.debug "Changing from #{@active_state.object_id} to state #{@state.class} #{@state.object_id} remember #{persistent_user_input(:remember).inspect} #{@user.class}" @active_state = @state @attended_states.store(@state.object_id, @state) else - SBSM.debug "Stay in volatile state #{@state.object_id}" + SBSM.debug "Stay in volatile state #{@state.object_id} #{@state.class}" end @zone = @active_state.zone @active_state.touch @@ -269,6 +276,7 @@ module SBSM age(now) > EXPIRES end def force_login(user) + SBSM.debug "force_login user #{user.class}" @user = user end def import_cookies(request) @@ -356,11 +364,12 @@ module SBSM cookie_set_or_get(:language) || default_language end def logged_in? - !@user.is_a?(@unknown_user_class) + !(@user.is_a?(@unknown_user_class) || @user.is_a?(SBSM::UnknownUser)) end def login + require 'pry'; binding.pry if(user = (@app && @app.respond_to?(:login) && @app.login(self))) - SBSM.debug "user is #{user} #{request_path.inspect}" + SBSM.debug "user is #{user.class} #{request_path.inspect}" @user = user else SBSM.debug "login no user #{request_path.inspect}" @@ -368,9 +377,11 @@ module SBSM end def logout __checkout - @user = unknown_user() + # @user = unknown_user() unless @user.is_a?(SBSM::UnknownUser) + @user = SBSM::UnknownUser.new + SBSM.debug "logout @user #{@user.class}" @active_state = @state = self::class::DEFAULT_STATE.new(self, @user) - SBSM.debug "logout #{request_path.inspect} setting @state #{@state.object_id} #{@state.class} remember #{persistent_user_input(:remember).inspect}" + SBSM.debug "logout #{request_path.inspect} setting @state #{@state.object_id} #{@state.class} remember #{persistent_user_input(:remember).inspect} #{@user.class}" @state.init @attended_states.store(@state.object_id, @state) end diff --git a/lib/sbsm/session_store.rb b/lib/sbsm/session_store.rb index 77604a9..146ddd7 100644 --- a/lib/sbsm/session_store.rb +++ b/lib/sbsm/session_store.rb @@ -169,12 +169,12 @@ module SBSM end def [](key) @mutex.synchronize do - unless((s = @sessions[key]) && !s.expired?) - s = @sessions[key] = @session_class.new(app: @app, cookie_name: @cookie_name, trans_handler: @trans_handler, validator: @validator, unknown_user: @unknown_user) + unless((session = @sessions[key]) && !session.expired?) + session = @sessions[key] = @session_class.new(app: @app, cookie_name: @cookie_name, trans_handler: @trans_handler, validator: @validator, unknown_user: @unknown_user) end - s.reset() - s.touch() - s + session.reset() + session.touch() + session end end end diff --git a/lib/sbsm/state.rb b/lib/sbsm/state.rb index 9f08c09..a0155cc 100644 --- a/lib/sbsm/state.rb +++ b/lib/sbsm/state.rb @@ -237,6 +237,7 @@ module SBSM } end model = @filter ? @filter.call(@model) : @model + puts "sbsm:state new view #{klass} for user #{@session.user.class}" view = klass.new(model, @session) @http_headers = view.http_headers unless @http_headers view diff --git a/test/simple_sbsm.rb b/test/simple_sbsm.rb index 0138c6e..095da5b 100755 --- a/test/simple_sbsm.rb +++ b/test/simple_sbsm.rb @@ -218,7 +218,7 @@ module Demo DEFAULT_STATE = HomeState end - class SimpleSBSM < SBSM::RackInterface + class SimpleSBSM < SBSM::App def initialize SBSM.info "SimpleSBSM.new" super(app: self) diff --git a/test/test_application.rb b/test/test_application.rb index a4b451d..d350285 100755 --- a/test/test_application.rb +++ b/test/test_application.rb @@ -12,8 +12,7 @@ require 'sbsm/app' require 'sbsm/session' require 'simple_sbsm' require 'nokogiri' - -RUN_ALL_TESTS=true unless defined?(RUN_ALL_TESTS) +require 'rack/utils' # Here we test, whether setting various class constant have the desired effect @@ -38,7 +37,19 @@ class AppVariantTest < Minitest::Test assert_equal expected, last_request.cookies assert_equal 'value2', @app.last_session.persistent_user_input('anrede') end -end if RUN_ALL_TESTS + def test_session_home_en_and_fr + get '/' + get '/fr/page/about' + first_session_id = last_response.cookies.values.first + assert last_response.ok? + assert_match ABOUT_HTML_CONTENT, last_response.body + get '/en/page/about' + second_session_id = last_response.cookies.values.first + assert last_response.ok? + assert_match ABOUT_HTML_CONTENT, last_response.body + assert_equal(first_session_id, second_session_id) + end +end class AppTestSimple < Minitest::Test include Rack::Test::Methods @@ -73,7 +84,7 @@ class AppTestSimple < Minitest::Test assert last_response.ok? assert_match CONFIRM_DONE_HTML_CONTENT, last_response.body end -if RUN_ALL_TESTS + def test_session_home get '/home' assert last_response.ok? @@ -136,21 +147,41 @@ if RUN_ALL_TESTS get '/home' assert last_response.ok? assert_match /^request_path is \/home$/, last_response.body - assert_match HOME_HTML_CONTENT, last_response.body get '/fr/page/about' assert last_response.ok? assert_match ABOUT_HTML_CONTENT, last_response.body end - def test_session_home_then_fr_about + def test_session_cookies + def cookies(header) + string = header['Set-Cookie'] + hash = Rack::Utils.parse_cookies_header string + to_return = {} + hash.each do |key, value| + if to_return.size == 0 + to_return[key] = { :value => value} + else + to_return[key] = value + end + end + to_return + end get '/home' assert last_response.ok? assert_match /^request_path is \/home$/, last_response.body - assert_match HOME_HTML_CONTENT, last_response.body + first = cookies(last_response.headers.clone) + assert(first.is_a?(Hash)) + assert(first['_session_id']) + first_session_id = first['_session_id'][:value] get '/fr/page/about' assert last_response.ok? - assert_match ABOUT_HTML_CONTENT, last_response.body + last = cookies(last_response.headers.clone) + assert(last.is_a?(Hash)) + assert(last['_session_id']) + last_session_id = last['_session_id'][:value] + assert_equal(last_session_id, first_session_id) end + def test_session_about_then_root get '/fr/page/about' assert last_response.ok? @@ -163,16 +194,6 @@ if RUN_ALL_TESTS def test_show_stats # We add it here to get some more or less useful statistics ::SBSM::Session.show_stats '/de/page' - end if RUN_ALL_TESTS -end - def test_session_home_then_fr_about - puts 888 - get '/home' - assert last_response.ok? - assert_match /^request_path is \/home$/, last_response.body - assert_match HOME_HTML_CONTENT, last_response.body - get '/fr/page/about' - assert last_response.ok? - assert_match ABOUT_HTML_CONTENT, last_response.body end + end \ No newline at end of file diff --git a/test/test_customized_app.rb b/test/test_customized_app.rb index 91476d6..6dc49d2 100644 --- a/test/test_customized_app.rb +++ b/test/test_customized_app.rb @@ -210,6 +210,7 @@ class CustomizedAppSessionValidatorLnf < Minitest::Test get '/fr/page/feedback' do # we patched to force a login end assert_equal(1, @app.last_session.attended_states.size) + binding.pry assert_equal Demo::FeedbackState, @app.last_session.active_state.class end diff --git a/test/test_session.rb b/test/test_session.rb index 99c399a..040760e 100755 --- a/test/test_session.rb +++ b/test/test_session.rb @@ -22,7 +22,7 @@ # ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Zürich, Switzerland # hwyss@ywesee.com # -# TestSession -- sbsm -- 22.10.2002 -- hwyss@ywesee.com +# TestSession -- sbsm -- 22.10.2002 -- hwyss@ywesee.com #++ require 'minitest/autorun' @@ -74,7 +74,7 @@ class StubSessionRequest < Rack::Request super(Rack::MockRequest.env_for("http://example.com:8080/#{path}", params)) end end -class StubSessionView +class StubSessionView def initialize(foo, bar) end def http_headers @@ -88,7 +88,7 @@ class StubSessionBarState < SBSM::State EVENT_MAP = { :foobar => StubSessionBarState, } -end +end class StubSessionBarfoosState < SBSM::State DIRECT_EVENT = :barfoos end @@ -167,6 +167,10 @@ class TestSession < Minitest::Test assert_equal('@session.valid_input', @session.persistent_user_input(:language)) assert_equal('@session.valid_input', @session.valid_input) end + def test_server_name + @session.process_rack(rack_request: @request) + assert_equal('example.com', @session.server_name) + end def test_user_input @request["foo"] = "bar" @request["baz"] = "zuv" @@ -225,12 +229,12 @@ class TestSession < Minitest::Test @session.process_rack(:rack_request =>req1) state1 = @session.state req2 = StubSessionRequest.new - req2["event"] = "foo" + req2["event"] = "foo" @session.process_rack(:rack_request =>req2) state2 = @session.state refute_equal(state1, state2) req3 = StubSessionRequest.new - req3["event"] = :bar + req3["event"] = :bar @session.process_rack(:rack_request =>req3) state3 = @session.state refute_equal(state1, state3) @@ -242,7 +246,7 @@ class TestSession < Minitest::Test } assert_equal(attended, @session.attended_states) req4 = StubSessionRequest.new - req4["event"] = :foobar + req4["event"] = :foobar @session.process_rack(:rack_request =>req4) @session.cap_max_states state4 = @session.state -- 2.10.2