global_session 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,162 @@
1
+ basedir = File.dirname(__FILE__)
2
+
3
+ # Make sure the namespace exists, to satisfy Rails auto-loading
4
+ module GlobalSession
5
+ module Rack
6
+ # Global session middleware. Note: this class relies on
7
+ # Rack::Cookies being used higher up in the chain.
8
+ class Middleware
9
+ # Make a new global session.
10
+ #
11
+ # The optional block here controls an alternate ticket retrieval
12
+ # method. If no ticket is stored in the cookie jar, this
13
+ # function is called. If it returns a non-nil value, that value
14
+ # is the ticket.
15
+ #
16
+ # === Parameters
17
+ # app(Rack client): application to run
18
+ # configuration(String or Configuration): global_session configuration.
19
+ # If a string, is interpreted as a
20
+ # filename to load the config from.
21
+ # directory(String or Directory): Directory object that provides
22
+ # trust services to the global
23
+ # session implementation. If a
24
+ # string, is interpreted as a
25
+ # filesystem directory containing
26
+ # the public and private keys of
27
+ # authorities, from which default
28
+ # trust services will be initialized.
29
+ #
30
+ # block: optional alternate ticket retrieval function
31
+ def initialize(app, configuration, directory, &block)
32
+ @app = app
33
+
34
+ if configuration.instance_of?(String)
35
+ @configuration = Configuration.new(configuration, ENV['RACK_ENV'] || 'development')
36
+ else
37
+ @configuration = configuration
38
+ end
39
+
40
+ if directory.instance_of?(String)
41
+ @directory = Directory.new(@configuration, directory)
42
+ else
43
+ @directory = directory
44
+ end
45
+
46
+ @cookie_retrieval = block
47
+ @cookie_name = @configuration['cookie']['name']
48
+ end
49
+
50
+ # Read a cookie from the Rack environment.
51
+ #
52
+ # === Parameters
53
+ # env(Hash): Rack environment.
54
+ def read_cookie(env)
55
+ if env['rack.cookies'].key?(@cookie_name)
56
+ env['global_session'] = Session.new(@directory,
57
+ env['rack.cookies'][@cookie_name])
58
+ elsif @cookie_retrieval && cookie = @cookie_retrieval.call(env)
59
+ env['global_session'] = Session.new(@directory, cookie)
60
+ else
61
+ env['global_session'] = Session.new(@directory)
62
+ end
63
+
64
+ true
65
+ end
66
+
67
+ # Renew the session ticket.
68
+ #
69
+ # === Parameters
70
+ # env(Hash): Rack environment
71
+ def renew_cookie(env)
72
+ if (renew = @configuration['renew']) && env['global_session'] &&
73
+ env['global_session.req.renew'] != false &&
74
+ env['global_session'].directory.local_authority_name &&
75
+ env['global_session'].expired_at < Time.at(Time.now.utc + 60 * renew.to_i)
76
+ env['global_session'].renew!
77
+ end
78
+ end
79
+
80
+ # Update the cookie jar with the revised ticket.
81
+ #
82
+ # === Parameters
83
+ # env(Hash): Rack environment
84
+ def update_cookie(env)
85
+ return if env['global_session.req.update'] == false
86
+
87
+ begin
88
+ domain = @configuration['cookie']['domain'] || env['SERVER_NAME']
89
+ if env['global_session'] && env['global_session'].valid?
90
+ value = env['global_session'].to_s
91
+ expires = @configuration['ephemeral'] ? nil : env['global_session'].expired_at
92
+ unless env['rack.cookies'].key?(@cookie_name) &&
93
+ env['rack.cookies'][@cookie_name] == value
94
+ env['rack.cookies'][@cookie_name] = {:value => value, :domain => domain, :expires => expires}
95
+ end
96
+ else
97
+ # write an empty cookie
98
+ env['rack.cookies'][@cookie_name] = {:value => nil, :domain => domain, :expires => Time.at(0)}
99
+ end
100
+ rescue Exception => e
101
+ wipe_cookie(env)
102
+ raise e
103
+ end
104
+ end
105
+
106
+ # Delete the ticket from the cookie jar.
107
+ #
108
+ # === Parameters
109
+ # env(Hash): Rack environment
110
+ def wipe_cookie(env)
111
+ domain = @configuration['cookie']['domain'] || env['SERVER_NAME']
112
+ env['rack.cookies'][@cookie_name] = {:value => nil, :domain => domain, :expires => Time.at(0)}
113
+ end
114
+
115
+ # Handle exceptions that occur during app invocation.
116
+ #
117
+ # === Parameters
118
+ # activity(String): name of activity in which error happened
119
+ # env(Hash): Rack environment
120
+ # e(Exception): error that happened
121
+ def handle_error(activity, env, e)
122
+ if e.is_a? ClientError
123
+ env['global_session.error'] = e
124
+ return @app.call(env)
125
+ elsif e.is_a? ConfigurationError
126
+ env['rack.logger'].error("#{e.class} while #{activity}: #{e} #{e.backtrace}") if env['rack.logger']
127
+ env['global_session.error'] = e
128
+ return @app.call(env)
129
+ else
130
+ raise e
131
+ end
132
+ end
133
+
134
+ # Rack request chain. Sets up the global session ticket from
135
+ # the environment and passes it up the chain.
136
+ def call(env)
137
+ env['rack.cookies'] = {} unless env['rack.cookies']
138
+
139
+ begin
140
+ read_cookie(env)
141
+ rescue Exception => e
142
+ env['global_session'] = Session.new(@directory)
143
+ return handle_error('reading session cookie', env, e)
144
+ end
145
+
146
+ begin
147
+ tuple = @app.call(env)
148
+ renew_cookie(env)
149
+ update_cookie(env)
150
+ return tuple
151
+ rescue Exception => e
152
+ wipe_cookie(env)
153
+ return handle_error('processing request', env, e)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ module Rack
161
+ GlobalSession = GlobalSession::Rack::Middleware unless defined?(GlobalSession)
162
+ end
@@ -0,0 +1,61 @@
1
+ module GlobalSession
2
+ module Rails
3
+ # Module that is mixed into ActionController's eigenclass; provides access to shared
4
+ # app-wide data such as the configuration object.
5
+ module ActionControllerClassMethods
6
+ def global_session_config
7
+ unless @global_session_config
8
+ config_file = File.join(::Rails.root, 'config', 'global_session.yml')
9
+ @global_session_config = GlobalSession::Configuration.new(config_file, RAILS_ENV)
10
+ end
11
+
12
+ return @global_session_config
13
+ end
14
+
15
+ def global_session_config=(config)
16
+ @global_session_config = config
17
+ end
18
+
19
+ def has_global_session(options={})
20
+ odefault = {:integrated=>false}
21
+ obase = self.superclass.global_session_options if self.superclass.respond_to?(:global_session_options)
22
+ options = odefault.merge(obase).merge(options)
23
+
24
+ self.global_session_options = HashWithIndifferentAccess.new(options)
25
+ options = self.global_session_options
26
+
27
+ include GlobalSession::Rails::ActionControllerInstanceMethods
28
+
29
+ fopt = {}
30
+ inverse_fopt = {}
31
+ fopt[:only] = options[:only] if options[:only]
32
+ fopt[:except] = options[:except] if options[:except]
33
+ inverse_fopt[:only] = options[:except] if options[:except]
34
+ inverse_fopt[:except] = options[:only] if options[:only]
35
+
36
+ if fopt[:only] || fopt[:except]
37
+ before_filter :global_session_skip_renew, inverse_fopt
38
+ before_filter :global_session_skip_update, inverse_fopt
39
+ end
40
+
41
+ before_filter :global_session_initialize, fopt
42
+ end
43
+
44
+ def no_global_session
45
+ skip_before_filter :global_session_initialize
46
+ before_filter :global_session_skip_renew
47
+ before_filter :global_session_skip_update
48
+ end
49
+
50
+ protected
51
+
52
+ def global_session_options
53
+ @global_session_options || {}
54
+ end
55
+
56
+ def global_session_options=(options)
57
+ @global_session_options = options
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,135 @@
1
+ module GlobalSession
2
+ # Rails integration for GlobalSession.
3
+ #
4
+ # The configuration file for Rails apps is located in +config/global_session.yml+ and a generator
5
+ # (global_session_config) is available for creating a sensible default.
6
+ #
7
+ # There is also a generator (global_session_authority) for creating authority keypairs.
8
+ #
9
+ # The main integration touchpoint for Rails is the module ActionControllerInstanceMethods,
10
+ # which gets mixed into ActionController::Base. This is where all of the magic happens..
11
+ #
12
+ module Rails
13
+ # Module that is mixed into ActionController-derived classes when the class method
14
+ # +has_global_session+ is called.
15
+ #
16
+ module ActionControllerInstanceMethods
17
+ def self.included(base) # :nodoc:
18
+ base.alias_method_chain :session, :global_session
19
+ end
20
+
21
+ # Shortcut accessor for global session configuration object.
22
+ #
23
+ # === Return
24
+ # config(GlobalSession::Configuration)
25
+ def global_session_config
26
+ request.env['global_session.config']
27
+ end
28
+
29
+ def global_session_options
30
+ self.class.instance_variable_get(:@global_session_options)
31
+ end
32
+
33
+ # Global session reader.
34
+ #
35
+ # === Return
36
+ # session(Session):: the global session associated with the current request, nil if none
37
+ def global_session
38
+ @global_session
39
+ end
40
+
41
+ # Aliased version of ActionController::Base#session which will return the integrated
42
+ # global-and-local session object (IntegratedSession).
43
+ #
44
+ # === Return
45
+ # session(IntegratedSession):: the integrated session
46
+ def session_with_global_session
47
+ if global_session
48
+ unless @integrated_session &&
49
+ (@integrated_session.local == session_without_global_session) &&
50
+ (@integrated_session.global == global_session)
51
+ @integrated_session =
52
+ IntegratedSession.new(session_without_global_session, global_session)
53
+ end
54
+
55
+ return @integrated_session
56
+ else
57
+ return session_without_global_session
58
+ end
59
+ end
60
+
61
+ # Filter to initialize the global session.
62
+ #
63
+ # === Return
64
+ # true:: Always returns true
65
+ def global_session_initialize
66
+ options = self.class.instance_variable_get(:@global_session_options) || {}
67
+
68
+ if (error = request.env['global_session.error'])
69
+ raise error unless options[:raise] == false
70
+ else
71
+ @global_session = request.env['global_session']
72
+ return true
73
+ end
74
+ end
75
+
76
+ # Filter to disable auto-renewal of the session.
77
+ #
78
+ # === Return
79
+ # true:: Always returns true
80
+ def global_session_skip_renew
81
+ request.env['global_session.req.renew'] = false
82
+ true
83
+ end
84
+
85
+ # Filter to disable updating of the session cookie
86
+ #
87
+ # === Return
88
+ # true:: Always returns true
89
+ def global_session_skip_update
90
+ request.env['global_session.req.update'] = false
91
+ true
92
+ end
93
+
94
+ # Override for the ActionController method of the same name that logs
95
+ # information about the request. Our version logs the global session ID
96
+ # instead of the local session ID.
97
+ #
98
+ # === Parameters
99
+ # name(Type):: Description
100
+ #
101
+ # === Return
102
+ # name(Type):: Description
103
+ def log_processing
104
+ if logger && logger.info?
105
+ log_processing_for_request_id
106
+ log_processing_for_parameters
107
+ end
108
+ end
109
+
110
+ def log_processing_for_request_id # :nodoc:
111
+ if global_session && global_session.id
112
+ session_id = global_session.id + " (#{session[:session_id]})"
113
+ elsif session[:session_id]
114
+ session_id = session[:session_id]
115
+ elsif request.session_options[:id]
116
+ session_id = request.session_options[:id]
117
+ end
118
+
119
+ request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
120
+ request_id << "to #{params[:format]} " if params[:format]
121
+ request_id << "(for #{request_origin.split[0]}) [#{request.method.to_s.upcase}]"
122
+ request_id << "\n Session ID: #{session_id}" if session_id
123
+
124
+ logger.info(request_id)
125
+ end
126
+
127
+ def log_processing_for_parameters # :nodoc:
128
+ parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
129
+ parameters = parameters.except!(:controller, :action, :format, :_method)
130
+
131
+ logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,30 @@
1
+ basedir = File.dirname(__FILE__)
2
+
3
+ require 'rack/contrib/cookies'
4
+
5
+
6
+ #Require the files necessary for Rails integration
7
+ require 'global_session/rack'
8
+ require 'global_session/rails/action_controller_class_methods'
9
+ require 'global_session/rails/action_controller_instance_methods'
10
+
11
+ module GlobalSession
12
+ module Rails
13
+ def self.activate(config)
14
+ # Enable ActionController integration.
15
+ class <<ActionController::Base
16
+ include GlobalSession::Rails::ActionControllerClassMethods
17
+ end
18
+
19
+ authorities = File.join(::Rails.root, 'config', 'authorities')
20
+ hgs_config = ActionController::Base.global_session_config
21
+ hgs_dir = GlobalSession::Directory.new(hgs_config, authorities)
22
+
23
+ # Add our middleware to the stack.
24
+ config.middleware.use ::Rack::Cookies
25
+ config.middleware.use ::Rack::GlobalSession, hgs_config, hgs_dir
26
+
27
+ return true
28
+ end
29
+ end
30
+ end