@@ -58,163 +58,163 @@ def initialize(app, options = {})
58
58
def call ( env )
59
59
request = Rack ::Request . new ( env )
60
60
auth_fail = false
61
-
61
+
62
62
# Only authenticate if there's a cookie in the request named @@cookie_name
63
63
unless request . cookies . has_key? ( @@cookie_name )
64
64
return finish ( @app , env )
65
65
end
66
-
66
+
67
67
# Get the data from the cookie
68
68
begin
69
69
cookie_value = request . cookies [ @@cookie_name ]
70
70
hash_data = read_cookie ( cookie_value )
71
71
rescue Exception => e
72
72
auth_fail = e . message
73
73
end
74
-
74
+
75
75
# Do not authenticate if either one of these is set
76
76
# This check is done late so that we'll have already
77
77
# checked the cookie
78
78
if env [ 'AUTH_USER' ] || env [ 'AUTH_FAIL' ]
79
79
return finish ( @app , env , cookie_value )
80
80
end
81
-
81
+
82
82
if !auth_fail
83
83
auth_datetime = Time . at ( hash_data [ 'AUTH_DATETIME' ] ) . utc
84
84
auth_expire_datetime = Time . at ( hash_data [ 'AUTH_EXPIRE_DATETIME' ] ) . utc
85
-
85
+
86
86
if auth_datetime + @@max_lifetime < Time . now . utc
87
87
auth_fail = "You have been signed out since you signed in more than #{ @@max_lifetime /3600 } hours ago"
88
88
end
89
-
89
+
90
90
if auth_expire_datetime < Time . now . utc
91
91
auth_fail = "You have been signed out due to inactivity"
92
92
end
93
93
end
94
-
94
+
95
95
if auth_fail
96
96
env [ 'AUTH_FAIL' ] = auth_fail
97
97
else
98
98
# Put the values from the hash into the environment
99
99
env [ 'AUTH_USER' ] = hash_data [ 'AUTH_USER' ]
100
-
100
+
101
101
env [ 'AUTH_TYPE' ] = hash_data [ 'AUTH_TYPE' ]
102
102
env [ 'AUTH_TYPE_USER' ] = hash_data [ 'AUTH_TYPE_USER' ]
103
-
103
+
104
104
env [ 'AUTH_TYPE_THIS_REQUEST' ] = "Cookie"
105
-
105
+
106
106
env [ 'AUTH_DATETIME' ] = auth_datetime
107
107
env [ 'AUTH_EXPIRE_DATETIME' ] = auth_expire_datetime
108
108
end
109
-
109
+
110
110
finish ( @app , env , cookie_value )
111
111
end
112
-
112
+
113
113
def finish ( app , env , cookie_value_from_request = nil )
114
114
status , headers , body = @app . call ( env )
115
-
115
+
116
116
# Assume our cookie isn't in the response unless/until we find it
117
117
response_cookie = false
118
-
118
+
119
119
if headers . has_key? ( "Set-Cookie" )
120
120
set_cookie = headers [ "Set-Cookie" ]
121
121
set_cookie_pieces = set_cookie . split ( ";" )
122
-
122
+
123
123
# TODO: parse cookies from header and find @@cookie_name
124
124
set_cookie_pieces . each_with_index do |piece , index |
125
125
if piece [ @@cookie_name ]
126
126
response_cookie = true
127
127
end
128
128
end
129
129
end
130
-
130
+
131
131
# If the application isn't making any changes to the cookie, we can modify it
132
132
if cookie_value_from_request && !response_cookie
133
-
133
+
134
134
# If authentication succeeded earlier, send back a new token
135
135
if env [ 'AUTH_USER' ]
136
136
cookie = self . class . create_auth_cookie ( env )
137
-
137
+
138
138
if headers [ "Set-Cookie" ]
139
139
headers [ "Set-Cookie" ] << cookie
140
140
else
141
141
headers [ "Set-Cookie" ] = cookie
142
142
end
143
143
end
144
-
144
+
145
145
# If authentication failed earlier, tell the client to clear the cookie
146
146
if env [ 'AUTH_FAIL' ]
147
147
cookie = self . class . create_clear_cookie ( env )
148
-
148
+
149
149
if headers [ "Set-Cookie" ]
150
150
headers [ "Set-Cookie" ] << cookie
151
151
else
152
152
headers [ "Set-Cookie" ] = cookie
153
153
end
154
154
end
155
155
end
156
-
156
+
157
157
[ status , headers , body ]
158
158
end
159
-
159
+
160
160
def read_cookie ( cookie_value )
161
161
# Separate the cookie data and the digest
162
162
raw_data , digest = cookie_value . split ( "--" )
163
-
163
+
164
164
# Check for evidence of tampering
165
165
unless digest == self . class . generate_hmac ( raw_data )
166
166
raise "Invalid cookie digest!"
167
167
end
168
-
168
+
169
169
# Unpack the cookie data back to a hash
170
170
begin
171
171
unpacked_data = raw_data . unpack ( "m*" ) . first
172
172
hash_data = JSON . parse ( unpacked_data )
173
173
rescue
174
174
raise "Unable to read cookie!"
175
175
end
176
-
176
+
177
177
hash_data
178
178
end
179
-
179
+
180
180
def self . cookie_name
181
181
@@cookie_name
182
182
end
183
-
183
+
184
184
def self . create_auth_token ( env )
185
185
# Copy relevant auth info for storage in a token
186
186
auth_info = Hash . new
187
-
187
+
188
188
auth_info [ 'AUTH_USER' ] = env [ 'AUTH_USER' ]
189
-
189
+
190
190
auth_info [ 'AUTH_TYPE' ] = env [ 'AUTH_TYPE' ] || "Unknown"
191
191
auth_info [ 'AUTH_TYPE_USER' ] = env [ 'AUTH_TYPE_USER' ] || env [ 'AUTH_USER' ]
192
-
192
+
193
193
# Expecting env['AUTH_DATETIME'] to hold an instance of Time
194
194
if env [ 'AUTH_DATETIME' ]
195
195
auth_info [ 'AUTH_DATETIME' ] = env [ 'AUTH_DATETIME' ] . to_i
196
196
else
197
197
auth_info [ 'AUTH_DATETIME' ] = Time . now . utc . to_i
198
198
end
199
-
199
+
200
200
auth_info [ 'AUTH_EXPIRE_DATETIME' ] = Time . now . utc . to_i + @@idle_timeout
201
-
201
+
202
202
# Pack the auth_info hash for cookie storage
203
203
json_data = auth_info . to_json
204
204
packed_data = [ json_data ] . pack ( 'm*' )
205
-
205
+
206
206
# Add a digest value to cookie_data to prevent tampering
207
207
"#{ packed_data } --#{ generate_hmac ( packed_data ) } "
208
208
end
209
-
209
+
210
210
def self . create_auth_cookie ( env )
211
211
cookie_value = create_auth_token ( env )
212
212
cookie = "#{ @@cookie_name } =#{ URI . escape ( cookie_value ) } ; "
213
213
cookie += "domain=#{ cookie_domain ( env ) } ; "
214
214
cookie += "path=/; "
215
215
cookie += "HttpOnly; "
216
216
end
217
-
217
+
218
218
def self . create_clear_cookie ( env )
219
219
cookie_value = ""
220
220
cookie = "#{ @@cookie_name } =; "
@@ -223,11 +223,11 @@ def self.create_clear_cookie(env)
223
223
cookie += "expires=Thu, 01-Jan-1970 00:00:00 GMT; "
224
224
cookie += "HttpOnly; "
225
225
end
226
-
226
+
227
227
def self . generate_hmac ( data )
228
228
OpenSSL ::HMAC . hexdigest ( OpenSSL ::Digest ::SHA1 . new , @@secret , data )
229
229
end
230
-
230
+
231
231
def self . raw_host_with_port ( env )
232
232
if forwarded = env [ "HTTP_X_FORWARDED_HOST" ]
233
233
forwarded . split ( /,\s ?/ ) . last
@@ -236,24 +236,24 @@ def self.raw_host_with_port(env)
236
236
"#{ env [ 'SERVER_NAME' ] || env [ 'SERVER_ADDR' ] } :#{ env [ 'SERVER_PORT' ] } "
237
237
end
238
238
end
239
-
239
+
240
240
def self . host ( env )
241
241
raw_host_with_port ( env ) . sub ( /:\d +$/ , '' )
242
242
end
243
-
243
+
244
244
def self . cookie_domain ( env )
245
245
result = host ( env )
246
-
246
+
247
247
if @@domain_tree_depth != nil
248
248
components = result . split ( '.' )
249
249
components . slice! ( 0 , @@domain_tree_depth )
250
250
result = components . join ( '.' )
251
251
end
252
-
252
+
253
253
if @@share_with_subdomains
254
254
result = "." + result
255
255
end
256
-
256
+
257
257
result
258
258
end
259
259
end
0 commit comments