hancock 0.0.2

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.
Files changed (37) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +134 -0
  3. data/Rakefile +72 -0
  4. data/lib/hancock.rb +47 -0
  5. data/lib/models/consumer.rb +24 -0
  6. data/lib/models/user.rb +63 -0
  7. data/lib/sinatra/hancock/defaults.rb +25 -0
  8. data/lib/sinatra/hancock/openid_server.rb +96 -0
  9. data/lib/sinatra/hancock/sessions.rb +53 -0
  10. data/lib/sinatra/hancock/users.rb +79 -0
  11. data/lib/sinatra/hancock/views/openid_server/yadis.erb +13 -0
  12. data/lib/sinatra/hancock/views/sessions/unauthenticated.haml +14 -0
  13. data/lib/sinatra/hancock/views/users/register_form.haml +12 -0
  14. data/lib/sinatra/hancock/views/users/signup_confirmation.haml +13 -0
  15. data/lib/sinatra/hancock/views/users/signup_form.haml +18 -0
  16. data/spec/acceptance/signing_up_spec.rb +90 -0
  17. data/spec/app.rb +29 -0
  18. data/spec/features/sessions.feature +24 -0
  19. data/spec/features/signup.feature +26 -0
  20. data/spec/features/sso.feature +35 -0
  21. data/spec/features/step_definitions/sessions_steps.rb +31 -0
  22. data/spec/features/step_definitions/signup_steps.rb +56 -0
  23. data/spec/features/step_definitions/sso_steps.rb +74 -0
  24. data/spec/features/support/env.rb +16 -0
  25. data/spec/fixtures.rb +39 -0
  26. data/spec/matchers.rb +170 -0
  27. data/spec/spec_helper.rb +61 -0
  28. data/spec/units/app_spec.rb +15 -0
  29. data/spec/units/consumer_spec.rb +43 -0
  30. data/spec/units/identity_provider_spec.rb +27 -0
  31. data/spec/units/landing_page_spec.rb +42 -0
  32. data/spec/units/login_spec.rb +61 -0
  33. data/spec/units/logout_spec.rb +22 -0
  34. data/spec/units/openid_spec.rb +154 -0
  35. data/spec/units/signup_spec.rb +34 -0
  36. data/spec/units/user_spec.rb +10 -0
  37. metadata +143 -0
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe Hancock::App do
4
+ describe "default sreg parameters" do
5
+ it "returns sane defaults" do
6
+ Hancock::App.sreg_params.should eql([:email, :first_name, :last_name, :internal])
7
+ end
8
+ end
9
+ describe "setting the default sreg parameters" do
10
+ it "returns the values set by the method" do
11
+ Hancock::App.sreg_params = [:email, :birthdate, :gender]
12
+ Hancock::App.sreg_params.should eql([:email, :birthdate, :gender])
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe Hancock::Consumer do
4
+ describe "when queried about a disallowed host" do
5
+ it "returns false" do
6
+ Hancock::Consumer.allowed?('http://blogspot.com').should be_false
7
+ end
8
+ end
9
+
10
+ describe "visible to staff" do
11
+ before(:each) do
12
+ @consumer = Hancock::Consumer.gen(:internal)
13
+ @consumer.save
14
+ end
15
+ describe "when queried about an allowed host" do
16
+ it "returns true" do
17
+ Hancock::Consumer.allowed?(@consumer.url).should be_true
18
+ end
19
+ end
20
+ end
21
+ describe "visible to customers and staff" do
22
+ before(:each) do
23
+ @consumer = Hancock::Consumer.gen(:visible_to_all)
24
+ @consumer.save
25
+ end
26
+ describe "when queried about an allowed host" do
27
+ it "returns true" do
28
+ Hancock::Consumer.allowed?(@consumer.url).should be_true
29
+ end
30
+ end
31
+ end
32
+ describe "hidden (API) apps" do
33
+ before(:each) do
34
+ @consumer = Hancock::Consumer.gen(:hidden)
35
+ @consumer.save
36
+ end
37
+ describe "when queried about an allowed host" do
38
+ it "returns true" do
39
+ Hancock::Consumer.allowed?(@consumer.url).should be_true
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe "Requesting the server's xrds" do
4
+ describe "when accepting xrds+xml" do
5
+ it "renders the provider idp page" do
6
+ get '/sso/xrds'
7
+ last_response.headers['Content-Type'].should eql('application/xrds+xml')
8
+ last_response.should have_xpath("//xrd/service[uri='http://example.org/sso']")
9
+ last_response.should have_xpath("//xrd/service[type='http://specs.openid.net/auth/2.0/server']")
10
+ end
11
+ end
12
+ end
13
+
14
+ describe "Requesting a user's xrds" do
15
+ before(:each) do
16
+ @user = Hancock::User.gen
17
+ end
18
+
19
+ it "renders the users idp page" do
20
+ get "/sso/users/#{@user.id}"
21
+
22
+ last_response.headers['Content-Type'].should eql('application/xrds+xml')
23
+ last_response.headers['X-XRDS-Location'].should eql("http://example.org/sso/users/#{@user.id}")
24
+ last_response.body.should have_xpath("//xrd/service[uri='http://example.org/sso']")
25
+ last_response.body.should have_xpath("//xrd/service[type='http://specs.openid.net/auth/2.0/signon']")
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe "visiting /" do
4
+ before(:each) do
5
+ @last = Hancock::Consumer.gen(:internal)
6
+ @first = Hancock::Consumer.gen(:visible_to_all)
7
+ end
8
+ describe "when authenticated" do
9
+ describe "as an internal user" do
10
+ before(:each) do
11
+ @user = Hancock::User.gen(:internal)
12
+ end
13
+ it "should greet the user" do
14
+ login(@user)
15
+ get '/'
16
+
17
+ last_response.body.to_s.should have_selector("h3:contains('Hello #{@user.first_name} #{@user.last_name}')")
18
+ last_response.body.to_s.should have_selector("ul#consumers li a[href='#{@first.url}']:contains('#{@first.label}')")
19
+ last_response.body.to_s.should have_selector("ul#consumers li a[href='#{@last.url}']:contains('#{@last.label}')")
20
+ end
21
+ end
22
+ describe "as an external user" do
23
+ before(:each) do
24
+ @user = Hancock::User.gen
25
+ end
26
+ it "should greet the user" do
27
+ login(@user)
28
+ get '/'
29
+
30
+ last_response.body.to_s.should have_selector("h3:contains('Hello #{@user.first_name} #{@user.last_name}')")
31
+ last_response.body.to_s.should have_selector("ul#consumers li a[href='#{@first.url}']:contains('#{@first.label}')")
32
+ last_response.body.to_s.should_not have_selector("ul#consumers li a[href='#{@last.url}']:contains('#{@last.label}')")
33
+ end
34
+ end
35
+ end
36
+ describe "when unauthenticated" do
37
+ it "should prompt the user to login" do
38
+ get '/'
39
+ last_response.should be_a_login_form
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe "posting to /sso/login" do
4
+ before(:each) do
5
+ @user = Hancock::User.gen
6
+ @consumer = Hancock::Consumer.gen(:internal)
7
+ end
8
+ describe "with a valid password" do
9
+ it "should authenticate a user and redirect to /" do
10
+ post '/sso/login', :email => @user.email, :password => @user.password
11
+ last_response.status.should eql(302)
12
+ last_response.headers['Location'].should eql('/')
13
+ end
14
+ end
15
+ describe "with an invalid password" do
16
+ it "should display a form to login" do
17
+ post '/sso/login', :email => @user.email, :password => 's3cr3t'
18
+ last_response.body.should be_a_login_form
19
+ end
20
+ end
21
+ end
22
+ describe "getting /sso/login" do
23
+ before(:each) do
24
+ @user = Hancock::User.gen
25
+ @consumer = Hancock::Consumer.gen(:internal)
26
+ end
27
+ describe "with a valid session" do
28
+ it "should redirect to the consumer w/ the id for openid discovery" do
29
+ get '/sso/login', :return_to => @consumer.url
30
+
31
+ post '/sso/login', :email => @user.email, :password => @user.password
32
+ follow_redirect!
33
+
34
+ get '/sso/login'
35
+ last_response.status.should eql(302)
36
+
37
+ uri = Addressable::URI.parse(last_response.headers['Location'])
38
+ @consumer.url.should eql("#{uri.scheme}://#{uri.host}#{uri.path}")
39
+
40
+ uri.query_values['id'].should eql("#{@user.id}")
41
+ end
42
+
43
+ describe "from an invalid consumer" do
44
+ it "should return forbidden" do
45
+ get '/sso/login', { 'return_to' => 'http://rogueconsumerapp.com/login' }
46
+
47
+ login(@user)
48
+
49
+ get '/sso/login', { 'return_to' => 'http://rogueconsumerapp.com/login' }
50
+
51
+ last_response.status.should eql(403)
52
+ end
53
+ end
54
+ end
55
+ describe "without a valid session" do
56
+ it "should prompt the user to login" do
57
+ get '/sso/login', { 'return_to' => @consumer.url }
58
+ last_response.body.should be_a_login_form
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe "visiting /sso/logout" do
4
+ before(:each) do
5
+ @user = Hancock::User.gen
6
+ @consumer = Hancock::Consumer.gen(:internal)
7
+ end
8
+ describe "when authenticated" do
9
+ it "should clear the session and redirec to /" do
10
+ get '/sso/logout'
11
+ last_response.status.should eql(302)
12
+ last_response.headers['Location'].should eql('/')
13
+ end
14
+ end
15
+ describe "when unauthenticated" do
16
+ it "should redirect to /" do
17
+ get '/sso/logout'
18
+ last_response.status.should eql(302)
19
+ last_response.headers['Location'].should eql('/')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,154 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe "visiting /sso" do
4
+ before(:each) do
5
+ @user = Hancock::User.gen
6
+ @consumer = Hancock::Consumer.gen(:internal)
7
+ @identity_url = "http://example.org/sso/users/#{@user.id}"
8
+ end
9
+ it "should throw a bad request if there aren't any openid params" do
10
+ get '/sso'
11
+ last_response.status.should eql(400)
12
+ end
13
+ describe "with openid mode of associate" do
14
+ it "should respond with Diffie Hellman data in kv format" do
15
+ session = OpenID::Consumer::AssociationManager.create_session("DH-SHA1")
16
+ params = {"openid.ns" => 'http://specs.openid.net/auth/2.0',
17
+ "openid.mode" => "associate",
18
+ "openid.session_type" => 'DH-SHA1',
19
+ "openid.assoc_type" => 'HMAC-SHA1',
20
+ "openid.dh_consumer_public"=> session.get_request['dh_consumer_public']}
21
+
22
+ get "/sso", params
23
+
24
+ last_response.should be_an_openid_associate_response(session)
25
+ end
26
+ end
27
+ describe "with openid mode of checkid_setup" do
28
+ describe "authenticated" do
29
+ it "should redirect to the consumer app" do
30
+ params = {
31
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
32
+ "openid.mode" => "checkid_setup",
33
+ "openid.return_to" => @consumer.url,
34
+ "openid.identity" => @identity_url,
35
+ "openid.claimed_id" => @identity_url
36
+ }
37
+
38
+ login(@user)
39
+ get "/sso", params
40
+ last_response.should be_a_redirect_to_the_consumer(@consumer, @user)
41
+ end
42
+
43
+ describe "attempting to access another identity" do
44
+ it "should return forbidden" do
45
+ params = {
46
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
47
+ "openid.mode" => "checkid_setup",
48
+ "openid.return_to" => @consumer.url,
49
+ "openid.identity" => "http://example.org/sso/users/42",
50
+ "openid.claimed_id" => "http://example.org/sso/users/42"
51
+ }
52
+
53
+ login(@user)
54
+ get "/sso", params
55
+ last_response.status.should == 403
56
+ end
57
+ end
58
+ describe "attempting to access from an untrusted consumer" do
59
+ it "cancel the openid request" do
60
+ params = {
61
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
62
+ "openid.mode" => "checkid_setup",
63
+ "openid.return_to" => "http://rogueconsumerapp.com/",
64
+ "openid.identity" => @identity_url,
65
+ "openid.claimed_id" => @identity_url
66
+ }
67
+
68
+ login(@user)
69
+ get "/sso", params
70
+ last_response.status.should == 403
71
+ end
72
+ end
73
+ end
74
+ describe "unauthenticated user" do
75
+ it "should require authentication" do
76
+ params = {
77
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
78
+ "openid.mode" => "checkid_setup",
79
+ "openid.return_to" => @consumer.url,
80
+ "openid.identity" => @identity_url,
81
+ "openid.claimed_id" => @identity_url
82
+ }
83
+
84
+ get "/sso", params
85
+ last_response.body.should be_a_login_form
86
+ end
87
+ end
88
+ end
89
+ describe "with openid mode of checkid_immediate" do
90
+ describe "unauthenticated user" do
91
+ it "should require authentication" do
92
+ params = {
93
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
94
+ "openid.mode" => "checkid_immediate",
95
+ "openid.return_to" => @consumer.url,
96
+ "openid.identity" => @identity_url,
97
+ "openid.claimed_id" => @identity_url
98
+ }
99
+
100
+ get "/sso", params
101
+ last_response.body.should be_a_login_form
102
+ end
103
+ end
104
+ describe "authenticated user" do
105
+ describe "with appropriate request parameters" do
106
+ it "should redirect to the consumer app" do
107
+ params = {
108
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
109
+ "openid.mode" => "checkid_immediate",
110
+ "openid.return_to" => @consumer.url,
111
+ "openid.identity" => @identity_url,
112
+ "openid.claimed_id" => @identity_url
113
+ }
114
+
115
+ login(@user)
116
+ get "/sso", params
117
+ last_response.should be_an_openid_immediate_response(@consumer, @user)
118
+ end
119
+ end
120
+
121
+ describe "attempting to access another identity" do
122
+ it "should return forbidden" do
123
+ params = {
124
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
125
+ "openid.mode" => "checkid_immediate",
126
+ "openid.return_to" => @consumer.url,
127
+ "openid.identity" => "http://example.org/sso/users/42",
128
+ "openid.claimed_id" => "http://example.org/sso/users/42"
129
+ }
130
+
131
+ login(@user)
132
+ get "/sso", params
133
+ last_response.status.should == 403
134
+ end
135
+ end
136
+
137
+ describe "attempting to access from an untrusted consumer" do
138
+ it "cancel the openid request" do
139
+ params = {
140
+ "openid.ns" => "http://specs.openid.net/auth/2.0",
141
+ "openid.mode" => "checkid_immediate",
142
+ "openid.return_to" => "http://rogueconsumerapp.com/",
143
+ "openid.identity" => @identity_url,
144
+ "openid.claimed_id" => @identity_url
145
+ }
146
+
147
+ login(@user)
148
+ get "/sso", params
149
+ last_response.status.should == 403
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe "posting to /sso/signup" do
4
+ before(:each) do
5
+ @existing_user = Hancock::User.gen
6
+ @user = Hancock::User.new(:email => /\w+@\w+\.\w{2,3}/.gen.downcase,
7
+ :first_name => /\w+/.gen.capitalize,
8
+ :last_name => /\w+/.gen.capitalize)
9
+ @consumer = Hancock::Consumer.gen(:internal)
10
+ end
11
+ describe "with valid information" do
12
+ it "should sign the user up" do
13
+ post '/sso/signup', :email => @user.email,
14
+ :first_name => @user.first_name,
15
+ :last_name => @user.last_name
16
+
17
+ last_response.body.to_s.should have_selector("h3:contains('Success')")
18
+ last_response.body.to_s.should have_selector('p:contains("Check your email and you\'ll see a registration link!")')
19
+ last_response.body.to_s.should match(%r!href='http://example.org/sso/register/\w{40}'!)
20
+ Sinatra::Mailer::Email.should have(1).deliveries
21
+ end
22
+ end
23
+ describe "with invalid information" do
24
+ it "should not sign the user up" do
25
+ post '/sso/signup', :email => @existing_user.email,
26
+ :first_name => @existing_user.first_name,
27
+ :last_name => @existing_user.last_name
28
+ last_response.should have_selector("h3:contains('Signup Failed')")
29
+ last_response.should have_selector("#errors p:contains('Email is already taken')")
30
+ last_response.should have_selector("p a[href='/sso/signup']:contains('Try Again?')")
31
+ Sinatra::Mailer::Email.should have(0).deliveries
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
+
3
+ describe Hancock::User do
4
+ before(:each) do
5
+ @user = Hancock::User.gen
6
+ end
7
+ it "should save successfully" do
8
+ @user.save.should be_true
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hancock
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Corey Donohoe
8
+ - Tim Carey-Smith
9
+ autorequire: hancock
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-03-19 00:00:00 -06:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: dm-core
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 0.9.10
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby-openid
28
+ type: :runtime
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 2.1.2
35
+ version:
36
+ - !ruby/object:Gem::Dependency
37
+ name: sinatra
38
+ type: :runtime
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 0.9.1.1
45
+ version:
46
+ - !ruby/object:Gem::Dependency
47
+ name: guid
48
+ type: :runtime
49
+ version_requirement:
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.1
55
+ version:
56
+ description: A gem that provides a Single Sign On server
57
+ email:
58
+ - atmos@atmos.org
59
+ - tim@spork.in
60
+ executables: []
61
+
62
+ extensions: []
63
+
64
+ extra_rdoc_files:
65
+ - README.md
66
+ - LICENSE
67
+ files:
68
+ - LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - lib/hancock.rb
72
+ - lib/models
73
+ - lib/models/consumer.rb
74
+ - lib/models/user.rb
75
+ - lib/sinatra
76
+ - lib/sinatra/hancock
77
+ - lib/sinatra/hancock/defaults.rb
78
+ - lib/sinatra/hancock/openid_server.rb
79
+ - lib/sinatra/hancock/sessions.rb
80
+ - lib/sinatra/hancock/users.rb
81
+ - lib/sinatra/hancock/views
82
+ - lib/sinatra/hancock/views/openid_server
83
+ - lib/sinatra/hancock/views/openid_server/yadis.erb
84
+ - lib/sinatra/hancock/views/sessions
85
+ - lib/sinatra/hancock/views/sessions/unauthenticated.haml
86
+ - lib/sinatra/hancock/views/users
87
+ - lib/sinatra/hancock/views/users/register_form.haml
88
+ - lib/sinatra/hancock/views/users/signup_confirmation.haml
89
+ - lib/sinatra/hancock/views/users/signup_form.haml
90
+ - spec/acceptance
91
+ - spec/acceptance/signing_up_spec.rb
92
+ - spec/app.rb
93
+ - spec/features
94
+ - spec/features/sessions.feature
95
+ - spec/features/signup.feature
96
+ - spec/features/sso.feature
97
+ - spec/features/step_definitions
98
+ - spec/features/step_definitions/sessions_steps.rb
99
+ - spec/features/step_definitions/signup_steps.rb
100
+ - spec/features/step_definitions/sso_steps.rb
101
+ - spec/features/support
102
+ - spec/features/support/env.rb
103
+ - spec/fixtures.rb
104
+ - spec/matchers.rb
105
+ - spec/spec_helper.rb
106
+ - spec/units
107
+ - spec/units/app_spec.rb
108
+ - spec/units/consumer_spec.rb
109
+ - spec/units/identity_provider_spec.rb
110
+ - spec/units/landing_page_spec.rb
111
+ - spec/units/login_spec.rb
112
+ - spec/units/logout_spec.rb
113
+ - spec/units/openid_spec.rb
114
+ - spec/units/signup_spec.rb
115
+ - spec/units/user_spec.rb
116
+ has_rdoc: true
117
+ homepage: http://github.com/atmos/hancock
118
+ post_install_message:
119
+ rdoc_options: []
120
+
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: "0"
128
+ version:
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: "0"
134
+ version:
135
+ requirements: []
136
+
137
+ rubyforge_project:
138
+ rubygems_version: 1.3.1
139
+ signing_key:
140
+ specification_version: 2
141
+ summary: A gem that provides a Single Sign On server
142
+ test_files: []
143
+