Skip to content

Commit

Permalink
Make Camping::Session use Rack::Session::Cookie, this involves more c…
Browse files Browse the repository at this point in the history
…hanges:

* @env is now the original Hash passed to .call
* @State is now simply H[rack.session]
  • Loading branch information
judofyr committed Jun 11, 2009
1 parent 0d21c24 commit 332e0a1
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 217 deletions.
12 changes: 6 additions & 6 deletions lib/camping-unabridged.rb
Expand Up @@ -507,6 +507,7 @@ def r501(m)
# end
# end
def to_a
@env['rack.session'] = @state
r = Rack::Response.new(@body, @status, @headers)
@cookies.each do |k, v|
v = {:value => v, :path => self / "/"} if String===v
Expand All @@ -517,10 +518,10 @@ def to_a

def initialize(env, m) #:nodoc:
r = @request = Rack::Request.new(@env = env)
@root, p, @cookies,
@root, p, @cookies, @state,
@headers, @status, @method =
(env.SCRIPT_NAME||'').sub(/\/$/,''),
H[r.params], H[r.cookies],
(env['SCRIPT_NAME']||'').sub(/\/$/,''),
H[r.params], H[r.cookies], H[r.session],
{}, m =~ /r(\d+)/ ? $1.to_i : 200, m

@input = p.inject(H[]) do |h, (k, v)|
Expand Down Expand Up @@ -841,9 +842,8 @@ def goes(m)
# And array with [statuc, headers, body] is expected at the output.
def call(e)
X.M
e = H[e]
p = e.PATH_INFO = U.unescape(e.PATH_INFO)
k,m,*a=X.D p,(e.REQUEST_METHOD||'get').downcase
p = e['PATH_INFO'] = U.unescape(e['PATH_INFO'])
k,m,*a=X.D p,(e['REQUEST_METHOD']||'get').downcase
k.new(e,m).service(*a).to_a
rescue
r500(:I, k, m, $!, :env => e).to_a
Expand Down
12 changes: 6 additions & 6 deletions lib/camping.rb
Expand Up @@ -19,14 +19,14 @@ def r s,b,h={};b,h=h,b if Hash===b;@status=s;
@headers.merge!(h);@body=b;end;def redirect *a;r 302,'','Location'=>URL(*a).
to_s;end;def r404 p;P%"#{p} not found"end;def r500 k,m,e;raise e;end
def r501 m;P%"#{m.upcase} not implemented"end;def to_a
r=Rack::Response.new(@body,@status,@headers)
@env['rack.session']=@state;r=Rack::Response.new(@body,@status,@headers)
@cookies.each{|k,v|v={:value=>v,:path=>self/"/"} if String===v
r.set_cookie(k,v)}
r.to_a;end;def initialize(env,m)
r=@request=Rack::Request.new(@env=env)
@root,p,@cookies,@headers,@status,@method=
(env.SCRIPT_NAME||'').sub(/\/$/,''),H[r.params],
H[r.cookies],{},m=~/r(\d+)/?$1.to_i: 200,m
@root,p,@cookies,@state,@headers,@status,@method=
(env['SCRIPT_NAME']||'').sub(/\/$/,''),H[r.params],
H[r.cookies],H[r.session],{},m=~/r(\d+)/?$1.to_i: 200,m
@input=p.inject(H[]){|h,(k,v)|h.merge k.split(/[\]\[]+/).reverse.inject(v){|x,i|
H[i=>x]},&M};end;def service *a
r=catch(:halt){send(@method,*a)};@body||=r
Expand All @@ -42,8 +42,8 @@ def M;def M;end;constants.map{|c|k=const_get(c)
}if !k.respond_to?:urls}end end;I=R()
end;X=Controllers;class<<self;def goes m
Apps<<eval(S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING) end;def call e
X.M;e=H[e];p=e.PATH_INFO=U.unescape(e.PATH_INFO)
k,m,*a=X.D p,(e.REQUEST_METHOD||'get').downcase
X.M;p=e['PATH_INFO']=U.unescape(e['PATH_INFO'])
k,m,*a=X.D p,(e['REQUEST_METHOD']||'get').downcase
k.new(e,m).service(*a).to_a;rescue;r500(:I,k,m,$!,:env=>e).to_a;end
def method_missing m,c,*a;X.M;h=Hash===a[-1]?a.pop: {}
e=H[Rack::MockRequest.env_for('',h[:env]||{})]
Expand Down
132 changes: 0 additions & 132 deletions lib/camping/ar/session.rb

This file was deleted.

97 changes: 24 additions & 73 deletions lib/camping/session.rb
@@ -1,75 +1,26 @@
# == About camping/session.rb
# TODO: Clean everything up. Lots of just plain wrong stuff in here.
#
# This file contains two modules which supply basic sessioning to your Camping app.
# Again, we're dealing with a pretty little bit of code: approx. 60 lines.
#
# * Camping::Models::Session is a module which adds a single <tt>sessions</tt> table
# to your database.
# * Camping::Session is a module which you will mix into your application (or into
# specific controllers which require sessions) to supply a <tt>@state</tt> variable
# you can use in controllers and views.
#
# For a basic tutorial, see the *Getting Started* section of the Camping::Session module.
#require 'camping'
require 'base64'
require 'openssl'

module Camping
# The Camping::Session module is designed to be mixed into your application or into specific
# controllers which require sessions. This module defines a <tt>service</tt> method which
# intercepts all requests handed to those controllers.
#
# == Getting Started
#
# To get sessions working for your application:
#
# 1. <tt>require 'camping/session'</tt>
# 2. Mixin the module: <tt>module YourApp; include Camping::Session end</tt>
# 3. Define a secret (and keep it secret): <tt>module YourApp; @@state_secret = "SECRET!"; end</tt>
# 4. Throughout your application, use the <tt>@state</tt> var like a hash to store your application's data.
#
# == A Few Notes
#
# * The session is stored in a cookie. Look in <tt>@cookies.identity</tt>.
# * Session data is only saved if it has changed.
module Session
DIGEST = OpenSSL::Digest::SHA1.new
# This <tt>service</tt> method, when mixed into controllers, intercepts requests
# and wraps them with code to start and close the session. If a session isn't found
# in the cookie it is created. The <tt>@state</tt> variable is set and if it changes,
# it is saved back into the cookie.
def service(*a)
@session_blob = @input.camping_blob || @cookies.camping_blob
@session_hash = @input.camping_hash || @cookies.camping_hash
decoded_blob, data = '', {}
begin
if @session_blob && @session_hash && secure_blob_hasher(@session_blob) == @session_hash
decoded_blob = Base64.decode64(@session_blob)
data = Marshal.restore(decoded_blob)
end

app = C.name
@state = (data[app] ||= Camping::H[])
hash_before = decoded_blob.hash
return super(*a)
ensure
data[app] = @state
decoded_blob = Marshal.dump(data)
unless hash_before == decoded_blob.hash
@session_blob = Base64.encode64(decoded_blob).gsub("\n", '').strip
@session_hash = secure_blob_hasher(@session_blob)
raise "The session contains to much data" if @session_blob.length > 4096
@cookies.camping_blob = @session_blob
@cookies.camping_hash = @session_hash
end
end
# == Getting Started
#
# To get sessions working for your application:
# 1. <tt>require 'camping/session'</tt>
# 2. Mixin the module: <tt>include Camping::Session</tt>
# 3. Define a secret (and keep it secret): <tt>secret "SECRET!"</tt>
# 4. Throughout your application, use the <tt>@state</tt> var like a hash
# to store your application's data.
#
# require 'camping/session' # 1
#
# module MyApp
# include Camping::Session # 2
# secret "Oh yeah!" # 3
# end
module Session
def self.included(app)
key = "#{app}.state".downcase
secret = [__FILE__, File.mtime(__FILE__)].join(":")

app.meta_def(:secret) { |val| secret.replace(val) }
app.use Rack::Session::Cookie, :key => key, :secret => secret
end

def secure_blob_hasher(data)
OpenSSL::HMAC.hexdigest(DIGEST, state_secret, "#{@env.REMOTE_ADDR}#{data}")
end

def state_secret; [__FILE__, File.mtime(__FILE__)].join(":") end
end
end
end
end

0 comments on commit 332e0a1

Please sign in to comment.