Skip to content

Commit 720ec67

Browse files
committed
initial commit
0 parents  commit 720ec67

File tree

7 files changed

+234
-0
lines changed

7 files changed

+234
-0
lines changed

CHANGES

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
= 0.1.0 - ???
2+
* Initial release

MANIFEST

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGES
2+
MANIFEST
3+
README
4+
Rakefile
5+
rack-auth-cookie.gemspec
6+
lib/rack/auth/cookie.rb
7+
test/test_rack_cookie.rb

README

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
= Description
2+
The rack-cookie library is a Rack library that uses a cookie as a token to
3+
authenticate requests from users that have authenticated earlier using some
4+
other way.
5+
6+
= Prerequisites
7+
rack 1.0.0 or later
8+
9+
= Usage
10+
use "Rack::Auth::Cookie", :secret => "foo", :cookie_name => "my_authentication_cookie"
11+
12+
= Default Fields
13+
The default value for cookie_name is "auth_token"
14+
15+
= Details
16+
The "secret" option works exactly as in Rails session cookies. This should be
17+
set to a hard-to-guess value (not "foo"!) to protect against cookie forgery.
18+
19+
This rack library only handles requests that contain a cookie named "auth_token"
20+
(or whatever name was passed as the :cookie_name option). If that is not present,
21+
the request is forwarded normally with no changes to the environment.
22+
23+
If the cookie is detected, then it is checked for validity. If valid, then the
24+
value for 'AUTH_USER' is copied from the cookie data into the environment. If
25+
invalid, then env['AUTH_USER'] is deleted and env['AUTH_FAIL'] is set to an error message explaining what went wrong.
26+
27+
Note that if env['AUTH_USER'] or env['AUTH_FAIL'] are already set, then the
28+
request is forwarded normally with no changes to the environment.
29+
30+
= Authors
31+
Daniel Berger
32+
Charlie O'Keefe

Rakefile

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require 'rake'
2+
require 'rake/testtask'
3+
require 'rbconfig'
4+
5+
desc 'Install the rack-auth-cookie library (non-gem)'
6+
task :install do
7+
dir = File.join(CONFIG['sitelibdir'], 'rack', 'auth')
8+
FileUtils.mkdir_p(dir) unless File.exists?(dir)
9+
file = 'lib/rack/auth/cookie.rb'
10+
FileUtils.cp_r(file, dir, :verbose => true)
11+
end
12+
13+
desc 'Build the gem'
14+
task :gem do
15+
spec = eval(IO.read('rack-auth-cookie.gemspec'))
16+
Gem::Builder.new(spec).build
17+
end
18+
19+
desc 'Install the rack-auth-cookie library as a gem'
20+
task :install_gem => [:gem] do
21+
file = Dir["*.gem"].first
22+
sh "gem install #{file}"
23+
end
24+
25+
desc 'Export the git archive to a .zip, .gz and .bz2 file in your home directory'
26+
task :export, :output_file do |t, args|
27+
file = args[:output_file]
28+
29+
sh "git archive --prefix #{file}/ --output #{ENV['HOME']}/#{file}.tar master"
30+
31+
Dir.chdir(ENV['HOME']) do
32+
sh "gzip -f #{ENV['HOME']}/#{file}.tar"
33+
end
34+
35+
sh "git archive --prefix #{file}/ --output #{ENV['HOME']}/#{file}.tar master"
36+
37+
Dir.chdir(ENV['HOME']) do
38+
sh "bzip2 -f #{ENV['HOME']}/#{file}.tar"
39+
end
40+
41+
sh "git archive --prefix #{file}/ --output #{ENV['HOME']}/#{file}.zip --format zip master"
42+
43+
Dir.chdir(ENV['HOME']) do
44+
sh "unzip #{file}.zip"
45+
Dir.chdir(file) do
46+
sh "rake gem"
47+
end
48+
end
49+
end
50+
51+
Rake::TestTask.new do |t|
52+
t.verbose = true
53+
t.warning = true
54+
end

lib/rack/auth/cookie.rb

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
require 'openssl'
2+
require 'rack/request'
3+
4+
module Rack
5+
module Auth
6+
class Cookie
7+
# Creates a new Rack::Auth::Cookie object. The +cookie_name+ param gives the
8+
# name of the cookie used to authenticate the requestor. The default is
9+
# 'auth_token'.
10+
#
11+
def initialize(app, options = {})
12+
@app = app
13+
@@secret = options[:secret]
14+
@@cookie_name = options[:cookie_name] || "auth_token"
15+
@@env = {}
16+
end
17+
18+
# The call method we've defined first checks to see if AUTH_USER or
19+
# AUTH_FAIL are set in the environment. If either is set, we assume that
20+
# the request has already either passed or failed authentication and move on.
21+
#
22+
# If neither is set, we check for the cookie with the name we've been
23+
# configured to use. If present, we attempt to authenticate the user using
24+
# the cookie. If successful then AUTH_USER is set to the username.
25+
#
26+
# If unsuccessful then AUTH_USER is not set and AUTH_FAIL is set to an
27+
# appropriate error message.
28+
#
29+
# It is then up to the application to check for the presence of AUTH_USER
30+
# and/or AUTH_FAIL and act as necessary.
31+
#
32+
def call(env)
33+
34+
# Do not authenticate if either one of these is set
35+
if env['AUTH_USER'] || env['AUTH_FAIL']
36+
return @app.call(env)
37+
end
38+
39+
request = Rack::Request.new(env)
40+
41+
# Only authenticate if there's a cookie in the request named @cookie_name
42+
unless request.cookies.has_key?(@@cookie_name)
43+
return @app.call(env)
44+
end
45+
46+
begin
47+
# Separate the cookie data and the digest
48+
cookie_with_digest = request.cookies[@@cookie_name]
49+
cookie_data, digest = cookie_with_digest.split("--")
50+
51+
# Make sure the cookie hasn't been tampered with
52+
unless digest == self.class.generate_hmac(cookie_data)
53+
env['AUTH_FAIL'] = "Invalid cookie digest!"
54+
return @app.call(env)
55+
end
56+
57+
# Unpack the cookie data back to a hash
58+
begin
59+
cookie_data = cookie_data.unpack("m*").first
60+
cookie_data = Marshal.load(cookie_data)
61+
rescue
62+
env['AUTH_FAIL'] = "Unable to read cookie!"
63+
return @app.call(env)
64+
end
65+
66+
# Put the values from the hash into the environment
67+
env['AUTH_USER'] = cookie_data['AUTH_USER']
68+
69+
env['AUTH_TYPE'] = "Cookie"
70+
rescue => err
71+
env.delete('AUTH_USER')
72+
env['AUTH_FAIL'] = "Unexpected failure during Cookie authentication"
73+
end
74+
75+
@app.call(env)
76+
end
77+
78+
def self.cookie_name
79+
@@cookie_name
80+
end
81+
82+
def self.create_auth_token(env)
83+
# Copy relevant auth info for storage in a token
84+
auth_info = Hash.new
85+
auth_info['AUTH_USER'] = env['AUTH_USER']
86+
87+
# Pack the auth_info hash for cookie storage
88+
cookie_data = Marshal.dump(auth_info)
89+
cookie_data = [cookie_data].pack("m*")
90+
91+
# Add a digest value to cookie_data to prevent tampering
92+
"#{cookie_data}--#{self.generate_hmac(cookie_data)}"
93+
end
94+
95+
def self.generate_hmac(data)
96+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @@secret, data)
97+
end
98+
end
99+
end
100+
end

rack-auth-cookie.gemspec

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
require 'rubygems'
2+
3+
Gem::Specification.new do |gem|
4+
gem.name = 'rack-auth-cookie'
5+
gem.version = '0.1.0'
6+
gem.authors = ["Daniel Berger", "Charlie O'Keefe"]
7+
gem.email = '[email protected]'
8+
gem.homepage = 'http://www.github.com/charlieok/rack-auth-cookie'
9+
gem.summary = 'A Rack library that authenticates requests using a cookie'
10+
gem.test_file = 'test/test_rack_auth_cookie.rb'
11+
gem.files = Dir['**/*'].delete_if{ |item| item.include?('git') }
12+
13+
gem.extra_rdoc_files = ['CHANGES', 'README', 'MANIFEST']
14+
15+
gem.add_dependency('rack', '>= 1.0.0')
16+
17+
gem.description = <<-EOF
18+
The rack-auth-cookie library provides a Rack middleware interface for
19+
authenticating users using a cookie
20+
EOF
21+
end

test/test_rack_auth_cookie.rb

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require 'test/unit'
2+
require 'rack/auth/cookie'
3+
4+
class TC_Rack_Auth_Cookie < Test::Unit::TestCase
5+
def setup
6+
@app = 1 # Placeholder
7+
@env = 1 # Placeholder
8+
@rack = Rack::Auth::Cookie.new(@app)
9+
end
10+
11+
def test_constructor_basic
12+
assert_nothing_raised{ Rack::Auth::Cookie.new(@app) }
13+
end
14+
15+
def teardown
16+
@rack = nil
17+
end
18+
end

0 commit comments

Comments
 (0)