global_session 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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