factorylabs-casrack_the_authenticator 1.6.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.
- data/.gitignore +3 -0
- data/README.rdoc +87 -0
- data/Rakefile +7 -0
- data/VERSION +1 -0
- data/casrack_the_authenticator.gemspec +83 -0
- data/developer_tasks/doc.rake +22 -0
- data/developer_tasks/gem.rake +20 -0
- data/developer_tasks/test.rake +24 -0
- data/features/fake.feature +34 -0
- data/features/require_cas.feature +19 -0
- data/features/simple.feature +32 -0
- data/features/step_definitions/fake_cas_steps.rb +14 -0
- data/features/step_definitions/rack_steps.rb +69 -0
- data/features/support/assertions.rb +3 -0
- data/features/support/rack_support.rb +89 -0
- data/lib/casrack_the_authenticator/configuration.rb +88 -0
- data/lib/casrack_the_authenticator/fake.rb +49 -0
- data/lib/casrack_the_authenticator/require_cas.rb +36 -0
- data/lib/casrack_the_authenticator/service_ticket_validator.rb +55 -0
- data/lib/casrack_the_authenticator/simple.rb +82 -0
- data/lib/casrack_the_authenticator.rb +11 -0
- data/test/configuration_test.rb +83 -0
- data/test/fake_test.rb +94 -0
- data/test/service_ticket_validator_test.rb +85 -0
- data/test/simple_test.rb +131 -0
- data/test/test_helper.rb +16 -0
- metadata +165 -0
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
Casrack the Authenticator is a
|
2
|
+
Rack[http://github.com/chneukirchen/rack] middleware
|
3
|
+
that provides CAS[http://www.jasig.org/cas] support.
|
4
|
+
|
5
|
+
As of the current version, Casrack the Authenticator only supports the most basic
|
6
|
+
of CAS scenarios: it requires CAS authentication if it receives a 401 Unauthorized
|
7
|
+
response from lower-down in the Rack stack, and it stores the authentication token
|
8
|
+
in the session (so logout happens when users close their browers). Casrack the Authenticator
|
9
|
+
is a very open-minded beast, though, so please contribute (well-tested) additions to do
|
10
|
+
proxy-authentication and single-sign-out, or for anything else you desire.
|
11
|
+
|
12
|
+
=== How-To
|
13
|
+
|
14
|
+
==== 1: install
|
15
|
+
|
16
|
+
[sudo] gem install casrack_the_authenticator
|
17
|
+
|
18
|
+
==== 2: set up the middleware:
|
19
|
+
|
20
|
+
# in your rackup:
|
21
|
+
use CasrackTheAuthenticator::Simple, :cas_server => "http://cas.mycompany.com/cas"
|
22
|
+
# or "config.middleware.use" if you're on Rails
|
23
|
+
|
24
|
+
See CasrackTheAuthenticator::Configuration for specifics on that Hash argument.
|
25
|
+
|
26
|
+
==== 3: optionally install CasrackTheAuthenticator::RequireCAS if you want _every_ request to require CAS authentication:
|
27
|
+
|
28
|
+
# in your rackup:
|
29
|
+
use CasrackTheAuthenticator::Simple, :cas_server => ...
|
30
|
+
use CasrackTheAuthenticator::RequireCAS
|
31
|
+
# or "config.middleware.use" if you're on Rails
|
32
|
+
|
33
|
+
==== 4: pull the authenticated CAS username out of the Rack session:
|
34
|
+
|
35
|
+
# in a Rack app:
|
36
|
+
def call(env)
|
37
|
+
user = cas_user(env)
|
38
|
+
...
|
39
|
+
end
|
40
|
+
|
41
|
+
def cas_user(env)
|
42
|
+
username = Rack::Request.new(env).session[CasrackTheAuthenticator::USERNAME_PARAM]
|
43
|
+
User.find_by_username(username)
|
44
|
+
end
|
45
|
+
|
46
|
+
# or, in a Rails controller:
|
47
|
+
|
48
|
+
def cas_user
|
49
|
+
username = Rack::Request.new(request.env).session[CasrackTheAuthenticator::USERNAME_PARAM]
|
50
|
+
User.find_by_username(username)
|
51
|
+
end
|
52
|
+
|
53
|
+
=== Disconnected (Fake) Mode
|
54
|
+
|
55
|
+
I've often found myself working on a CAS-ified project while away from the office
|
56
|
+
and unable to access the CAS server. To support this type of disconnected development,
|
57
|
+
just substitute in the CasrackTheAuthenticator::Fake middleware. It acts like
|
58
|
+
CasrackTheAuthenticator::Simple, but it uses HTTP Basic authentication against a
|
59
|
+
preset list of usernames.
|
60
|
+
|
61
|
+
A common pattern for Rails apps is to create a disconnected environment:
|
62
|
+
|
63
|
+
==== 1: set up <tt>[rails_root]/config/database.yml</tt>
|
64
|
+
|
65
|
+
development: &DEV
|
66
|
+
adapter: sqlite3
|
67
|
+
database: db/development.sqlite3
|
68
|
+
pool: 5
|
69
|
+
timeout: 5000
|
70
|
+
|
71
|
+
# development mode when disconnected from MITRE
|
72
|
+
disconnected:
|
73
|
+
<<: *DEV
|
74
|
+
|
75
|
+
==== 2: set up <tt>[rails_root]/config/disconnected.rb</tt>:
|
76
|
+
|
77
|
+
load './development.rb'
|
78
|
+
config.middleware.swap 'CasrackTheAuthenticator::Simple', CasrackTheAuthenticator::Fake, 'jimbob', 'sueann'
|
79
|
+
|
80
|
+
==== 3: run in disconnected mode:
|
81
|
+
|
82
|
+
script/server -e disconnected
|
83
|
+
|
84
|
+
==== 4: login as 'jimbob' or 'sueann'
|
85
|
+
|
86
|
+
Passwords are ignored.
|
87
|
+
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.6.0
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{casrack_the_authenticator}
|
8
|
+
s.version = "1.6.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["James Rosen"]
|
12
|
+
s.date = %q{2009-09-23}
|
13
|
+
s.description = %q{CAS Authentication via Rack Middleware}
|
14
|
+
s.email = %q{james.a.rosen@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"README.rdoc",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"casrack_the_authenticator.gemspec",
|
24
|
+
"developer_tasks/doc.rake",
|
25
|
+
"developer_tasks/gem.rake",
|
26
|
+
"developer_tasks/test.rake",
|
27
|
+
"features/fake.feature",
|
28
|
+
"features/require_cas.feature",
|
29
|
+
"features/simple.feature",
|
30
|
+
"features/step_definitions/fake_cas_steps.rb",
|
31
|
+
"features/step_definitions/rack_steps.rb",
|
32
|
+
"features/support/assertions.rb",
|
33
|
+
"features/support/rack_support.rb",
|
34
|
+
"lib/casrack_the_authenticator.rb",
|
35
|
+
"lib/casrack_the_authenticator/configuration.rb",
|
36
|
+
"lib/casrack_the_authenticator/fake.rb",
|
37
|
+
"lib/casrack_the_authenticator/require_cas.rb",
|
38
|
+
"lib/casrack_the_authenticator/service_ticket_validator.rb",
|
39
|
+
"lib/casrack_the_authenticator/simple.rb",
|
40
|
+
"test/configuration_test.rb",
|
41
|
+
"test/fake_test.rb",
|
42
|
+
"test/service_ticket_validator_test.rb",
|
43
|
+
"test/simple_test.rb",
|
44
|
+
"test/test_helper.rb"
|
45
|
+
]
|
46
|
+
s.homepage = %q{http://github.com/gcnovus/casrack_the_authenticator}
|
47
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Casrack the Authenticator: RDoc", "--charset", "utf-8"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = %q{1.3.3}
|
50
|
+
s.summary = %q{CAS Authentication via Rack Middleware}
|
51
|
+
s.test_files = [
|
52
|
+
"test/configuration_test.rb",
|
53
|
+
"test/fake_test.rb",
|
54
|
+
"test/service_ticket_validator_test.rb",
|
55
|
+
"test/simple_test.rb",
|
56
|
+
"test/test_helper.rb"
|
57
|
+
]
|
58
|
+
|
59
|
+
if s.respond_to? :specification_version then
|
60
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
|
+
s.specification_version = 3
|
62
|
+
|
63
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
64
|
+
s.add_runtime_dependency(%q<nokogiri>, ["~> 1.3.2"])
|
65
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, ["~> 2.10.2"])
|
66
|
+
s.add_development_dependency(%q<jferris-mocha>, ["~> 0.9.7"])
|
67
|
+
s.add_development_dependency(%q<redgreen>, ["~> 1.2.2"])
|
68
|
+
s.add_development_dependency(%q<rack>, ["~> 1.0.0"])
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<nokogiri>, ["~> 1.3.2"])
|
71
|
+
s.add_dependency(%q<thoughtbot-shoulda>, ["~> 2.10.2"])
|
72
|
+
s.add_dependency(%q<jferris-mocha>, ["~> 0.9.7"])
|
73
|
+
s.add_dependency(%q<redgreen>, ["~> 1.2.2"])
|
74
|
+
s.add_dependency(%q<rack>, ["~> 1.0.0"])
|
75
|
+
end
|
76
|
+
else
|
77
|
+
s.add_dependency(%q<nokogiri>, ["~> 1.3.2"])
|
78
|
+
s.add_dependency(%q<thoughtbot-shoulda>, ["~> 2.10.2"])
|
79
|
+
s.add_dependency(%q<jferris-mocha>, ["~> 0.9.7"])
|
80
|
+
s.add_dependency(%q<redgreen>, ["~> 1.2.2"])
|
81
|
+
s.add_dependency(%q<rack>, ["~> 1.0.0"])
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'yard'
|
2
|
+
require 'yard/rake/yardoc_task'
|
3
|
+
|
4
|
+
desc "Generate RDoc"
|
5
|
+
task :doc => ['doc:generate']
|
6
|
+
|
7
|
+
namespace :doc do
|
8
|
+
|
9
|
+
doc_dir = File.join(CASRACK_PROJECT_ROOT, 'doc', 'rdoc')
|
10
|
+
|
11
|
+
YARD::Rake::YardocTask.new(:generate) do |yt|
|
12
|
+
yt.files = Dir.glob(File.join(CASRACK_PROJECT_ROOT, 'lib', '**', '*.rb')) +
|
13
|
+
[ File.join(CASRACK_PROJECT_ROOT, 'README.rdoc') ]
|
14
|
+
yt.options = ['--output-dir', doc_dir, '--readme', 'README.rdoc']
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Remove generated documenation"
|
18
|
+
task :clean do
|
19
|
+
rm_r doc_dir if File.exists?(doc_dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "factorylabs-casrack_the_authenticator"
|
5
|
+
gemspec.summary = "CAS Authentication via Rack Middleware"
|
6
|
+
gemspec.description = gemspec.summary
|
7
|
+
gemspec.email = "james.a.rosen@gmail.com"
|
8
|
+
gemspec.homepage = "http://github.com/gcnovus/casrack_the_authenticator"
|
9
|
+
gemspec.authors = ["James Rosen"]
|
10
|
+
gemspec.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Casrack the Authenticator: RDoc", "--charset", "utf-8"]
|
11
|
+
gemspec.platform = Gem::Platform::RUBY
|
12
|
+
gemspec.add_dependency 'nokogiri', '~> 1.4.1'
|
13
|
+
gemspec.add_development_dependency 'thoughtbot-shoulda', '~> 2.10.2'
|
14
|
+
gemspec.add_development_dependency 'jferris-mocha', '~> 0.9.7'
|
15
|
+
gemspec.add_development_dependency 'redgreen', '~> 1.2.2'
|
16
|
+
gemspec.add_development_dependency 'rack', '~> 1.0.0'
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'cucumber'
|
2
|
+
require 'cucumber/rake/task'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
6
|
+
t.cucumber_opts = "features --format pretty"
|
7
|
+
end
|
8
|
+
|
9
|
+
LIB_DIRECTORIES = FileList.new do |fl|
|
10
|
+
fl.include "#{CASRACK_PROJECT_ROOT}/lib"
|
11
|
+
fl.include "#{CASRACK_PROJECT_ROOT}/test/lib"
|
12
|
+
end
|
13
|
+
|
14
|
+
TEST_FILES = FileList.new do |fl|
|
15
|
+
fl.include "#{CASRACK_PROJECT_ROOT}/test/**/*_test.rb"
|
16
|
+
fl.exclude "#{CASRACK_PROJECT_ROOT}/test/test_helper.rb"
|
17
|
+
fl.exclude "#{CASRACK_PROJECT_ROOT}/test/lib/**/*.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
Rake::TestTask.new(:test) do |t|
|
21
|
+
t.libs = LIB_DIRECTORIES
|
22
|
+
t.test_files = TEST_FILES
|
23
|
+
t.verbose = true
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Feature: Fake CAS Authentication
|
2
|
+
In order to support developers who work away from the office
|
3
|
+
Casrack the Authenticator provides a Fake middleware.
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a Rack application exists
|
7
|
+
And the fake Casrack middleware is installed with user "missy"
|
8
|
+
|
9
|
+
Scenario: not-signed-in user makes a request to a public area
|
10
|
+
Given the underlying Rack application returns [200, {}, "Public Information"]
|
11
|
+
When I make a request
|
12
|
+
Then the response should be successful
|
13
|
+
And the response body should include "Public Information"
|
14
|
+
And the CAS user should be nil
|
15
|
+
|
16
|
+
Scenario: not-signed-in user makes a request to a private area
|
17
|
+
Given the underlying Rack application returns [401, {}, 'Restricted. Go away.']
|
18
|
+
When I make a request
|
19
|
+
Then I should be presented with a HTTP Basic authentication request
|
20
|
+
And the CAS user should be nil
|
21
|
+
|
22
|
+
Scenario: user presenting invalid credentials makes a request to a private area
|
23
|
+
Given the underlying Rack application returns [401, {}, 'Restricted. Go away.']
|
24
|
+
When I make a request as "thomas"
|
25
|
+
Then I should be presented with a HTTP Basic authentication request
|
26
|
+
And the CAS user should be nil
|
27
|
+
|
28
|
+
Scenario: user presenting credentials makes a request to a private area
|
29
|
+
Given the underlying Rack application returns [200, {}, 'Restricted. Shh.']
|
30
|
+
When I make a request as "missy"
|
31
|
+
Then the response should be successful
|
32
|
+
And the response body should include "Restricted. Shh."
|
33
|
+
And the CAS user should be "missy"
|
34
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Feature: Required CAS Authentication
|
2
|
+
In order make it dead-simple for users to implement a CAS-login requirement
|
3
|
+
Casrack the Authenticator provides a RequireCAS middleware.
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a Rack application exists
|
7
|
+
And the RequireCAS middleware is installed
|
8
|
+
And the simple version of Casrack the Authenticator is installed
|
9
|
+
And the underlying Rack application returns [200, {}, "Public Information"]
|
10
|
+
|
11
|
+
Scenario: not-signed-in user makes a request
|
12
|
+
When I make a request
|
13
|
+
Then I should be redirected to CAS
|
14
|
+
And the "Content-Type" header should be "text/plain"
|
15
|
+
|
16
|
+
Scenario: signed-in-user makes a request
|
17
|
+
When I return to "http://myapp.org/bar" with a valid CAS ticket for "tperon"
|
18
|
+
Then the response should be successful
|
19
|
+
And the response body should include "Public Information"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: Simple CAS Authentication
|
2
|
+
In order to maintain privacy and accountability while keeping IT costs low
|
3
|
+
"Upper Management" wants to use CAS authentication
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a Rack application exists
|
7
|
+
And the simple version of Casrack the Authenticator is installed
|
8
|
+
|
9
|
+
Scenario: not-signed-in user accesses public material
|
10
|
+
Given the underlying Rack application returns [200, {}, "Public Information"]
|
11
|
+
When I make a request
|
12
|
+
Then the response should be successful
|
13
|
+
And the response body should include "Public Information"
|
14
|
+
|
15
|
+
Scenario: not-signed-in user accesses restricted material
|
16
|
+
Given the underlying Rack application returns [401, {}, "Restricted!"]
|
17
|
+
When I make a request to "http://myapp.com/foo?bar=baz"
|
18
|
+
Then I should be redirected to CAS
|
19
|
+
And the "Content-Type" header should be "text/plain"
|
20
|
+
And CAS should return me to "http://myapp.com/foo?bar=baz"
|
21
|
+
|
22
|
+
Scenario: returning from a successful CAS sign-in
|
23
|
+
Given the underlying Rack application returns [200, {}, "Information for jswanson"]
|
24
|
+
When I return to "http://myapp.org/bar" with a valid CAS ticket for "jswanson"
|
25
|
+
Then the CAS user should be "jswanson"
|
26
|
+
And the response should be successful
|
27
|
+
And the response body should include "Information for jswanson"
|
28
|
+
|
29
|
+
Scenario: returning from an unsuccessful CAS sign-in
|
30
|
+
Given the underlying Rack application returns [401, {}, "Restricted!"]
|
31
|
+
When I return to "http://myapp.org/bar" with an invalid CAS ticket
|
32
|
+
Then the CAS user should be nil
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Given /^the fake Casrack middleware is installed with user "([^\"]*)"$/ do |user|
|
2
|
+
self.app = CasrackTheAuthenticator::Fake.new(app, user)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^I should be presented with a HTTP Basic authentication request$/ do
|
6
|
+
assert_equal 401, response.status
|
7
|
+
assert_equal 'text/plain', response.headers['Content-Type']
|
8
|
+
assert_equal '0', response.headers['Content-Length']
|
9
|
+
assert_equal 'Basic realm="CasrackTheAuthenticator::Fake"', response.headers['WWW-Authenticate']
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I make a request as "([^\"]*)"$/ do |username|
|
13
|
+
get '/', { 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:sekret"].pack("m*") }
|
14
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'test', 'test_helper.rb')
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
Given /^a Rack application exists$/ do
|
5
|
+
self.underlying_app = lambda { |env| nil }
|
6
|
+
self.app = underlying_app
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^the simple version of Casrack the Authenticator is installed$/ do
|
10
|
+
self.app = CasrackTheAuthenticator::Simple.new(app, :cas_server => 'http://cas.test/cas')
|
11
|
+
end
|
12
|
+
|
13
|
+
Given /^the RequireCAS middleware is installed$/ do
|
14
|
+
self.app = CasrackTheAuthenticator::RequireCAS.new(app)
|
15
|
+
end
|
16
|
+
|
17
|
+
Given /^the underlying Rack application returns (.+)$/ do |response|
|
18
|
+
underlying_app.stubs(:call).returns(eval(response))
|
19
|
+
end
|
20
|
+
|
21
|
+
When /^I make a request$/ do
|
22
|
+
get '/'
|
23
|
+
end
|
24
|
+
|
25
|
+
When /^I make a request to "([^\"]*)"$/ do |url|
|
26
|
+
get url
|
27
|
+
end
|
28
|
+
|
29
|
+
When /^I return to "([^\"]*)" with a valid CAS ticket for "([^\"]*)"$/ do |url, user|
|
30
|
+
http_request_returns_valid_cas_user user
|
31
|
+
url << (url.include?('?') ? '&' : '?') << 'ticket=ST-123455'
|
32
|
+
When "I make a request to \"#{url}\""
|
33
|
+
end
|
34
|
+
|
35
|
+
When /^I return to "([^\"]*)" with an invalid CAS ticket$/ do |url|
|
36
|
+
http_request_returns_error
|
37
|
+
url << (url.include?('?') ? '&' : '?') << 'ticket=ST-not-a-valid-ticket'
|
38
|
+
When "I make a request to \"#{url}\""
|
39
|
+
end
|
40
|
+
|
41
|
+
Then /^the CAS user should be nil$/ do
|
42
|
+
assert_equal nil, session[:cas_user]
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^the CAS user should be "([^\"]*)"$/ do |username|
|
46
|
+
assert_equal username, session[:cas_user]
|
47
|
+
end
|
48
|
+
|
49
|
+
Then /^the response should be successful$/ do
|
50
|
+
assert((200..299).include?(response.status), "Expected success, but was #{response.status}")
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /^the response body should include "([^\"]*)"$/ do |text|
|
54
|
+
assert response.body.include?(text)
|
55
|
+
end
|
56
|
+
|
57
|
+
Then /^I should be redirected to CAS$/ do
|
58
|
+
assert((300..399).include?(response.status), "Expected redirect, but was #{response.status}")
|
59
|
+
assert !redirected_to.nil?
|
60
|
+
assert redirected_to.to_s =~ /cas/i
|
61
|
+
end
|
62
|
+
|
63
|
+
Then /^CAS should return me to "([^\"]*)"$/ do |return_to|
|
64
|
+
assert_equal return_to, service_url
|
65
|
+
end
|
66
|
+
|
67
|
+
Then /^the "([^\"]*)" header should be "([^\"]*)"$/ do |header, value|
|
68
|
+
assert_equal value, response.headers[header]
|
69
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/mock'
|
3
|
+
Rack::MockRequest.class_eval do
|
4
|
+
|
5
|
+
class <<self
|
6
|
+
def env_for_with_hook(*args)
|
7
|
+
return ::RackSupport.current_env unless ::RackSupport.current_env.nil?
|
8
|
+
env = env_for_without_hook(*args)
|
9
|
+
::RackSupport.current_env = env
|
10
|
+
env
|
11
|
+
end
|
12
|
+
alias_method :env_for_without_hook, :env_for
|
13
|
+
alias_method :env_for, :env_for_with_hook
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
module RackSupport
|
19
|
+
|
20
|
+
VALID_CAS_USER_XML = <<-EOX
|
21
|
+
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
|
22
|
+
<cas:authenticationSuccess>
|
23
|
+
<cas:user>%s</cas:user>
|
24
|
+
</cas:authenticationSuccess>
|
25
|
+
</cas:serviceResponse>
|
26
|
+
EOX
|
27
|
+
|
28
|
+
@@current_env = nil
|
29
|
+
|
30
|
+
def self.current_env
|
31
|
+
@@current_env
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.current_env=(env)
|
35
|
+
@@current_env = env
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :underlying_app, :app, :response, :session
|
39
|
+
|
40
|
+
def cleanup_rack_variables
|
41
|
+
::RackSupport.current_env = nil
|
42
|
+
self.underlying_app = self.app = self.session = self.response = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def get(url, headers = {})
|
46
|
+
env = RackSupport.current_env = Rack::MockRequest.env_for(url, headers)
|
47
|
+
if session
|
48
|
+
env['rack.session'] = session
|
49
|
+
else
|
50
|
+
self.session = Rack::Request.new(env).session
|
51
|
+
end
|
52
|
+
self.response = Rack::MockRequest.new(app).get url, headers
|
53
|
+
end
|
54
|
+
|
55
|
+
def redirected_to
|
56
|
+
return nil if response.headers['Location'].nil?
|
57
|
+
URI::parse(response.headers['Location'])
|
58
|
+
end
|
59
|
+
|
60
|
+
def service_url
|
61
|
+
return nil if redirected_to.nil?
|
62
|
+
Rack::Utils.parse_nested_query(redirected_to.query)['service']
|
63
|
+
end
|
64
|
+
|
65
|
+
def http_request_returns_valid_cas_user(username)
|
66
|
+
http_request_returns VALID_CAS_USER_XML % username
|
67
|
+
end
|
68
|
+
|
69
|
+
def http_request_returns_error
|
70
|
+
http_request_returns "this is not a valid CAS service-ticket-validation response!"
|
71
|
+
end
|
72
|
+
|
73
|
+
def http_request_returns(content)
|
74
|
+
server = Object.new
|
75
|
+
connection = Object.new
|
76
|
+
response = Object.new
|
77
|
+
Net::HTTP.stubs(:new).returns(server)
|
78
|
+
server.stubs(:start).yields(connection)
|
79
|
+
connection.stubs(:get).returns(response)
|
80
|
+
response.stubs(:body).returns(content)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
After do
|
86
|
+
cleanup_rack_variables
|
87
|
+
end
|
88
|
+
|
89
|
+
World(RackSupport)
|