~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/actionpack/lib/action_controller/session/abstract_store.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
require 'rack/utils'
2
 
 
3
 
module ActionController
4
 
  module Session
5
 
    class AbstractStore
6
 
      ENV_SESSION_KEY = 'rack.session'.freeze
7
 
      ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
8
 
 
9
 
      HTTP_COOKIE = 'HTTP_COOKIE'.freeze
10
 
      SET_COOKIE = 'Set-Cookie'.freeze
11
 
 
12
 
      class SessionHash < Hash
13
 
        def initialize(by, env)
14
 
          super()
15
 
          @by = by
16
 
          @env = env
17
 
          @loaded = false
18
 
        end
19
 
 
20
 
        def session_id
21
 
          ActiveSupport::Deprecation.warn(
22
 
            "ActionController::Session::AbstractStore::SessionHash#session_id " +
23
 
            "has been deprecated. Please use request.session_options[:id] instead.", caller)
24
 
          @env[ENV_SESSION_OPTIONS_KEY][:id]
25
 
        end
26
 
 
27
 
        def [](key)
28
 
          load! unless @loaded
29
 
          super
30
 
        end
31
 
 
32
 
        def []=(key, value)
33
 
          load! unless @loaded
34
 
          super
35
 
        end
36
 
 
37
 
        def to_hash
38
 
          h = {}.replace(self)
39
 
          h.delete_if { |k,v| v.nil? }
40
 
          h
41
 
        end
42
 
 
43
 
        def data
44
 
         ActiveSupport::Deprecation.warn(
45
 
           "ActionController::Session::AbstractStore::SessionHash#data " +
46
 
           "has been deprecated. Please use #to_hash instead.", caller)
47
 
          to_hash
48
 
        end
49
 
 
50
 
        def inspect
51
 
          load! unless @loaded
52
 
          super
53
 
        end
54
 
 
55
 
        private
56
 
          def loaded?
57
 
            @loaded
58
 
          end
59
 
 
60
 
          def load!
61
 
            stale_session_check! do
62
 
              id, session = @by.send(:load_session, @env)
63
 
              (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
64
 
              replace(session)
65
 
              @loaded = true
66
 
            end
67
 
          end
68
 
 
69
 
          def stale_session_check!
70
 
            yield
71
 
          rescue ArgumentError => argument_error
72
 
            if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
73
 
              begin
74
 
                # Note that the regexp does not allow $1 to end with a ':'
75
 
                $1.constantize
76
 
              rescue LoadError, NameError => const_error
77
 
                raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
78
 
              end
79
 
 
80
 
              retry
81
 
            else
82
 
              raise
83
 
            end
84
 
          end
85
 
      end
86
 
 
87
 
      DEFAULT_OPTIONS = {
88
 
        :key =>           '_session_id',
89
 
        :path =>          '/',
90
 
        :domain =>        nil,
91
 
        :expire_after =>  nil,
92
 
        :secure =>        false,
93
 
        :httponly =>      true,
94
 
        :cookie_only =>   true
95
 
      }
96
 
 
97
 
      def initialize(app, options = {})
98
 
        # Process legacy CGI options
99
 
        options = options.symbolize_keys
100
 
        if options.has_key?(:session_path)
101
 
          options[:path] = options.delete(:session_path)
102
 
        end
103
 
        if options.has_key?(:session_key)
104
 
          options[:key] = options.delete(:session_key)
105
 
        end
106
 
        if options.has_key?(:session_http_only)
107
 
          options[:httponly] = options.delete(:session_http_only)
108
 
        end
109
 
 
110
 
        @app = app
111
 
        @default_options = DEFAULT_OPTIONS.merge(options)
112
 
        @key = @default_options[:key]
113
 
        @cookie_only = @default_options[:cookie_only]
114
 
      end
115
 
 
116
 
      def call(env)
117
 
        session = SessionHash.new(self, env)
118
 
 
119
 
        env[ENV_SESSION_KEY] = session
120
 
        env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
121
 
 
122
 
        response = @app.call(env)
123
 
 
124
 
        session_data = env[ENV_SESSION_KEY]
125
 
        options = env[ENV_SESSION_OPTIONS_KEY]
126
 
 
127
 
        if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
128
 
          session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
129
 
 
130
 
          sid = options[:id] || generate_sid
131
 
 
132
 
          unless set_session(env, sid, session_data.to_hash)
133
 
            return response
134
 
          end
135
 
 
136
 
          cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
137
 
          cookie << "; domain=#{options[:domain]}" if options[:domain]
138
 
          cookie << "; path=#{options[:path]}" if options[:path]
139
 
          if options[:expire_after]
140
 
            expiry = Time.now + options[:expire_after]
141
 
            cookie << "; expires=#{expiry.httpdate}"
142
 
          end
143
 
          cookie << "; Secure" if options[:secure]
144
 
          cookie << "; HttpOnly" if options[:httponly]
145
 
 
146
 
          headers = response[1]
147
 
          unless headers[SET_COOKIE].blank?
148
 
            headers[SET_COOKIE] << "\n#{cookie}"
149
 
          else
150
 
            headers[SET_COOKIE] = cookie
151
 
          end
152
 
        end
153
 
 
154
 
        response
155
 
      end
156
 
 
157
 
      private
158
 
        def generate_sid
159
 
          ActiveSupport::SecureRandom.hex(16)
160
 
        end
161
 
 
162
 
        def load_session(env)
163
 
          request = Rack::Request.new(env)
164
 
          sid = request.cookies[@key]
165
 
          unless @cookie_only
166
 
            sid ||= request.params[@key]
167
 
          end
168
 
          sid, session = get_session(env, sid)
169
 
          [sid, session]
170
 
        end
171
 
 
172
 
        def get_session(env, sid)
173
 
          raise '#get_session needs to be implemented.'
174
 
        end
175
 
 
176
 
        def set_session(env, sid, session_data)
177
 
          raise '#set_session needs to be implemented.'
178
 
        end
179
 
    end
180
 
  end
181
 
end