pasaporte 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/History.txt +2 -0
  2. data/Manifest.txt +41 -0
  3. data/README.txt +72 -0
  4. data/Rakefile +111 -0
  5. data/TODO.txt +2 -0
  6. data/bin/pasaporte-fcgi.rb +17 -0
  7. data/lib/pasaporte/.DS_Store +0 -0
  8. data/lib/pasaporte/assets/.DS_Store +0 -0
  9. data/lib/pasaporte/assets/bgbar.png +0 -0
  10. data/lib/pasaporte/assets/lock.png +0 -0
  11. data/lib/pasaporte/assets/mainbg_green.gif +0 -0
  12. data/lib/pasaporte/assets/mainbg_red.gif +0 -0
  13. data/lib/pasaporte/assets/openid.png +0 -0
  14. data/lib/pasaporte/assets/pasaporte.css +192 -0
  15. data/lib/pasaporte/assets/pasaporte.js +10 -0
  16. data/lib/pasaporte/assets/user.png +0 -0
  17. data/lib/pasaporte/auth/cascade.rb +16 -0
  18. data/lib/pasaporte/auth/remote_web_workplace.rb +61 -0
  19. data/lib/pasaporte/auth/yaml_digest_table.rb +23 -0
  20. data/lib/pasaporte/auth/yaml_table.rb +43 -0
  21. data/lib/pasaporte/faster_openid.rb +39 -0
  22. data/lib/pasaporte/iso_countries.yml +247 -0
  23. data/lib/pasaporte/julik_state.rb +42 -0
  24. data/lib/pasaporte/markaby_ext.rb +8 -0
  25. data/lib/pasaporte/pasaporte_store.rb +60 -0
  26. data/lib/pasaporte/timezones.yml +797 -0
  27. data/lib/pasaporte.rb +1214 -0
  28. data/test/fixtures/pasaporte_approvals.yml +12 -0
  29. data/test/fixtures/pasaporte_profiles.yml +45 -0
  30. data/test/fixtures/pasaporte_throttles.yml +4 -0
  31. data/test/helper.rb +66 -0
  32. data/test/mosquito.rb +596 -0
  33. data/test/test_approval.rb +33 -0
  34. data/test/test_auth_backends.rb +59 -0
  35. data/test/test_openid.rb +363 -0
  36. data/test/test_pasaporte.rb +326 -0
  37. data/test/test_profile.rb +165 -0
  38. data/test/test_settings.rb +27 -0
  39. data/test/test_throttle.rb +70 -0
  40. data/test/testable_openid_fetcher.rb +82 -0
  41. metadata +151 -0
@@ -0,0 +1,165 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestProfile < Camping::ModelTest
4
+ DOMAIN = 'my-pasaporte.com'
5
+ fixtures :pasaporte_profiles
6
+
7
+ def test_aa_fixtures_loaded
8
+ assert_equal 4, Profile.count
9
+ end
10
+
11
+ def test_assumes_localhost_as_default_domain
12
+ @profile = Profile.new
13
+ assert_equal 'localhost', @profile.domain_name
14
+ end
15
+
16
+ def test_requires_nickname_and_domain
17
+ @profile = Profile.new
18
+
19
+ deny @profile.valid?, "The profile without a nickname and a domain name is invalid"
20
+ @profile.nickname = 'xxx'
21
+ @profile.domain_name = ''
22
+
23
+ deny @profile.valid?, "The profile without a domain name is invalid"
24
+
25
+ @profile.domain_name = 'somedomain'
26
+ assert @profile.valid?
27
+ end
28
+
29
+ def test_domain_name_and_nickname_are_protected
30
+ @profile = Profile.new
31
+ @profile.attributes = {:nickname => 'xyz', :domain_name => 'google'}
32
+
33
+ assert_not_equal @profile.nickname, 'xyz'
34
+ assert_not_equal @profile.domain_name, 'google'
35
+ end
36
+
37
+ def test_find_or_create_by_domain_and_nickname_exempted_from_attribute_protection
38
+ prof = create("julik", "julik.nl")
39
+ assert prof.valid?
40
+ deny prof.new_record?
41
+ assert_equal 'julik.nl', prof.domain_name
42
+ assert_equal 'julik', prof.nickname
43
+ prof = create("julik", "julik.nl")
44
+ assert prof.valid?
45
+ deny prof.new_record?
46
+ assert_equal 'julik.nl', prof.domain_name
47
+ assert_equal 'julik', prof.nickname
48
+ end
49
+
50
+ def test_secret_integer_generated
51
+ secrets = %w( foo bar baz daz daing ).map do | word |
52
+ prof = Profile.find_or_create_by_nickname_and_domain_name(word, word)
53
+ deny prof.new_record?
54
+ prof.secret_salt
55
+ end
56
+ assert_equal secrets.length, secrets.uniq.length,
57
+ "All generated secrets should be autogenerated and unique"
58
+ end
59
+
60
+ def test_enforces_unique_nickname_and_domain
61
+ prof1 = create("MisterZed", "google.com")
62
+ prof2 = Profile.new do | p |
63
+ p.nickname = "MisterZed"
64
+ p.domain_name = "google.com"
65
+ end
66
+
67
+ assert prof1.valid?, "The first created should be valid"
68
+ deny prof2.valid?, "The second one clashes"
69
+
70
+ prof2.domain_name = "yahoo.com"
71
+ assert prof2.valid?, "The second one is valid too because there are no clashes anymore"
72
+ end
73
+
74
+ def test_validates_both_delegate_urls
75
+ p = create("strained", "test.host")
76
+ assert p.valid?, "The newly created profile should be valid"
77
+
78
+ p.openid_server = "watatoe.com/openid"
79
+ deny p.valid?, "The profile cannot be valid with only the server URL"
80
+ assert_not_nil p.errors[:openid_delegate], "Should require delegate URL"
81
+
82
+ p.openid_delegate = "watatoe.com/openid/backend"
83
+ assert p.valid?, "As two URLs are present the profile becomes valid"
84
+ end
85
+
86
+ def test_delegate_urls_removed_when_delegate_set_to_false
87
+ p = create('julik', DOMAIN,
88
+ :openid_server => 'xxx.com', :openid_delegate => 'xyz.org/x')
89
+ assert p.delegates_openid?, "This profile delegates"
90
+ p.delegates_openid = false
91
+ p.save!
92
+
93
+ assert p.openid_server.blank?, "The openid server should have been removed"
94
+ assert p.openid_delegate.blank?, "The openid delegate should have been removed"
95
+ deny p.delegates_openid?, "Delegation is now turned off because we sent a bool switch of false"
96
+ end
97
+
98
+ def test_normalizes_both_delegate_urls
99
+ p = create('julik', DOMAIN,
100
+ :openid_server => 'xxx.com', :openid_delegate => 'xyz.org/x')
101
+ assert_equal 'http://xxx.com/', p.openid_server,
102
+ "The URL should be normalized with HTTP scheme and trailing slash"
103
+ assert_equal 'http://xyz.org/x', p.openid_delegate,
104
+ "The URL should be normalized with HTTP scheme"
105
+ end
106
+
107
+ def test_to_sreg_fields_by_default
108
+ p = create('julik', DOMAIN,
109
+ :email => 'foo@var.com', :dob => Date.parse("10.15.1983"))
110
+ all_fields = p.to_sreg_fields
111
+ r = {"dob"=>"1983-10-15", "nickname"=>"julik", "email"=>"foo@var.com"}
112
+ assert_equal r, all_fields
113
+ end
114
+
115
+ def test_to_sreg_fields_with_requested_fields
116
+ p = create('julik', DOMAIN,
117
+ :email => 'foo@var.com', :dob => Date.parse("10.15.1983"))
118
+ partial_fields = p.to_sreg_fields([:dob, :gender, :country])
119
+ r = {"dob"=>"1983-10-15"}
120
+ assert_equal r, partial_fields
121
+ end
122
+
123
+ def test_to_sreg_fields_with_lotso_data
124
+ mh = Profile.find(1)
125
+ ref = {"dob"=>"1953-01-12", "postcode"=>"1234",
126
+ "nickname"=>"monsieur-hulot",
127
+ "country"=>"fr", "fullname"=>"Monsieur Hulot", "gender"=>"m"}
128
+ assert_equal ref, mh.to_sreg_fields
129
+ end
130
+
131
+ def test_openid_requestor
132
+ begin
133
+ o = Pasaporte::ALLOW_DELEGATION
134
+ silence_warnings { Pasaporte.const_set(:ALLOW_DELEGATION, true) }
135
+
136
+ mh = Profile.find(1)
137
+ deny mh.delegates_openid?
138
+ assert mh.update_attributes(
139
+ :openid_server => 'http://tativille.fr/oid',
140
+ :openid_delegate => 'http://tativille.fr/oid/proc'
141
+ )
142
+
143
+ assert mh.delegates_openid?, "delegates_openid? should be true"
144
+ assert mh.update_attributes(:openid_server => '', :openid_delegate => '')
145
+ deny mh.delegates_openid?, "There are no URLS - no delegation happens"
146
+
147
+ assert mh.update_attributes(
148
+ :openid_server => 'http://tativille.fr/oid',
149
+ :openid_delegate => 'http://tativille.fr/oid/proc'
150
+ )
151
+ silence_warnings { Pasaporte.const_set(:ALLOW_DELEGATION, false) }
152
+ deny mh.delegates_openid?, "Delegation is turned off - "+
153
+ "no delegation happens"
154
+ ensure
155
+ silence_warnings { Pasaporte.const_set(:ALLOW_DELEGATION, o) }
156
+ end
157
+ end
158
+
159
+ private
160
+ def create(nick, domain, extras = {})
161
+ p = Profile.find_or_create_by_nickname_and_domain_name(nick, domain)
162
+ p.update_attributes(extras) if extras.any?
163
+ p
164
+ end
165
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSettings < Camping::Test
4
+ CONFIG = File.dirname(Pasaporte::PATH) + '/pasaporte/config.yml'
5
+
6
+ def test_application
7
+ emit :throttle_for => 45.minutes
8
+ assert_nothing_raised { Pasaporte.apply_config! }
9
+ assert_equal 45.minutes, Pasaporte::THROTTLE_FOR, "The setting should have " +
10
+ "been applied"
11
+ end
12
+
13
+ def test_bail_on_unknowns
14
+ emit :achtung => "shtoink"
15
+ e = assert_raise(NameError) { Pasaporte.apply_config! }
16
+ assert_match /ACHTUNG/i, e.message
17
+ end
18
+
19
+ def teardown
20
+ begin; File.unlink(CONFIG); rescue Errno::ENOENT; end
21
+ end
22
+
23
+ private
24
+ def emit(hash = {})
25
+ File.open(CONFIG, 'w') { | f | f << hash.to_yaml }
26
+ end
27
+ end
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestThrottle < Camping::ModelTest
4
+ DOMAIN = 'my-pasaporte.com'
5
+ HEADERS = Mosquito::MockRequest::DEFAULT_HEADERS.merge('REMOTE_ADDR' => '120.171.0.1')
6
+
7
+ def setup
8
+ silence_warnings { Pasaporte.const_set(:THROTTLE_FOR, 2.minutes) }
9
+ super
10
+ end
11
+
12
+ def test_throttle_set
13
+ deny Throttle.throttled?(HEADERS)
14
+
15
+ t = Throttle.set!(HEADERS)
16
+ assert_kind_of Throttle, t
17
+ deny t.new_record?
18
+ assert Throttle.throttled?(HEADERS)
19
+ end
20
+
21
+ def test_throttle_response_depends_on_cutoff
22
+ Throttle.set!(HEADERS)
23
+ flexmock(Throttle).should_receive(:cutoff).at_least.once.and_return(Time.now + 10)
24
+ deny Throttle.throttled?(HEADERS)
25
+ end
26
+
27
+ def test_cutoff_depends_on_app_setting
28
+ assert_time_matches (Time.now - Pasaporte::THROTTLE_FOR), Throttle.send(:cutoff)
29
+ silence_warnings{ Pasaporte.const_set(:THROTTLE_FOR, 14) }
30
+ assert_time_matches Time.now - 14, Throttle.send(:cutoff)
31
+ end
32
+
33
+ def test_throttle_response_depends_on_env_params
34
+ envs = [
35
+ {'HTTP_USER_AGENT' => 'KitchenSink, NOT like IE',
36
+ 'REMOTE_ADDR' => '164.10.10.1'},
37
+ {'HTTP_USER_AGENT' => 'WebKit (like Gecko)',
38
+ 'REMOTE_ADDR' => '164.10.10.1'},
39
+ {'HTTP_USER_AGENT' => 'KitchenSink, NOT like IE',
40
+ 'REMOTE_ADDR' => '164.10.10.1'},
41
+ ]
42
+
43
+ Throttle.set!(envs[0])
44
+ assert Throttle.throttled?(envs[0])
45
+ assert Throttle.throttled?(envs[2])
46
+ deny Throttle.throttled?(envs[1]), "This environment is not throttled"
47
+ end
48
+
49
+ def teardown
50
+ Throttle.delete_all
51
+ super
52
+ end
53
+
54
+ def test_throttle_autoexpiry_on_check
55
+ Throttle.set!(HEADERS)
56
+ assert_equal 1, Throttle.count
57
+ flexmock(Throttle).should_receive(:cutoff).at_least.once.and_return(Time.now + 10)
58
+
59
+ deny Throttle.throttled?(HEADERS)
60
+ assert_equal 0, Throttle.count
61
+ end
62
+
63
+ private
64
+ def assert_time_matches(ref, actual)
65
+ assert_kind_of Time, ref, "The reference value should be Time"
66
+ assert_kind_of Time, actual, "The actual value should be Time"
67
+ assert_in_delta(ref.to_i, actual.to_i, 2,
68
+ "The times passed should be within 2s")
69
+ end
70
+ end
@@ -0,0 +1,82 @@
1
+ # Implements OpenID::Fetcher. Will run the request through the mosquito test case instead of calling
2
+ # out via HTTP
3
+ class TestableOpenidFetcher
4
+ # Will be raised when the fetcher tries to get a URL which is
5
+ # not within the application being tested
6
+ class ExternalResource < RuntimeError; end
7
+
8
+ # We need a separate Mosquito tester class for things that will
9
+ # happen via POST, because this is a different flow -
10
+ # the requests of the server instead of the browser.
11
+ # If you post directly from the same test case you are wiring yourself into the
12
+ # session ID that has been gotten by the simulated browser, that's why we use that.
13
+ class OpenidPoster < Pasaporte::WebTest
14
+ attr_reader :request, :response
15
+ def test_foo; assert true; end
16
+ end
17
+
18
+ def initialize(test_case)
19
+ @browser_getter = test_case
20
+ @browser_getter.request.headers['HTTP_HOST'] = 'test.host'
21
+ @server_poster = OpenidPoster.new("test_foo")
22
+ @server_poster.setup # manually yes
23
+ end
24
+
25
+ # This is used by OpenID lib 2
26
+ def fetch(url, body=nil, headers=nil, redirect_limit=10)
27
+ url, url_stringified = URI::parse(url), url.dup
28
+
29
+ h = headers || {}
30
+
31
+ raise_on_external url, @browser_getter
32
+
33
+ camping_controller_with_response = (body.blank? ? get(url.request_uri, h) : post(url.request_uri, h, body))
34
+ ::OpenID::HTTPResponse._from_net_response(FakeResponse.new(camping_controller_with_response), url_stringified)
35
+ end
36
+
37
+ # An adapter to make a Mosquito response (Camping controller) behave like Net::HTTPResponse
38
+ class FakeResponse < ::Net::HTTPResponse
39
+ def initialize(mosquito_response)
40
+ @the = mosquito_response
41
+ super('1.0', @the.status.to_s, 'Found') # http version, resp code and message
42
+ flat_headers = @the.headers.inject({}) { |n, k| n.merge k[0] => k[1].to_s } rescue {}
43
+ initialize_http_header(flat_headers)
44
+ end
45
+
46
+ def body
47
+ @the.body
48
+ end
49
+
50
+ def code
51
+ @the.status.to_s
52
+ end
53
+ end
54
+
55
+ private
56
+ def get(uri, headers = {})
57
+ Pasaporte::LOGGER.debug "OpenID requested GET on #{uri}"
58
+ @browser_getter.get relativized(uri) # this fails somehow
59
+ @browser_getter.response
60
+ end
61
+
62
+ def post(uri, headers = {}, body = '')
63
+ Pasaporte::LOGGER.debug "OpenID requested POST on #{uri}"
64
+ @server_poster.post relativized(uri), body
65
+ @server_poster.response
66
+ end
67
+
68
+ def relativized(uri)
69
+ # Here we need to replace the mount point URL otherwise
70
+ # OpenID gets confused and actually posts into it
71
+ # - Mosquito does not like that
72
+ u = URI.parse(uri)
73
+ u.path.gsub(/^\/pasaporte/, '')
74
+ end
75
+
76
+ # Check if we are calling to the outside world
77
+ def raise_on_external(uri, testcase)
78
+ unless ((uri.host == testcase.request.http_host) || uri.host.blank?)
79
+ raise ExternalResource, "Called out to external resource: OpenID consumer wants to have #{uri}"
80
+ end
81
+ end
82
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pasaporte
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-03 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: camping
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: ruby-openid
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.1.0
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: flexmock
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: hoe
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.8.2
64
+ version:
65
+ description: An OpenID server with a colored bar on top
66
+ email: me@julik.nl
67
+ executables:
68
+ - pasaporte-fcgi.rb
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - History.txt
73
+ - Manifest.txt
74
+ - README.txt
75
+ - TODO.txt
76
+ files:
77
+ - History.txt
78
+ - Manifest.txt
79
+ - README.txt
80
+ - Rakefile
81
+ - TODO.txt
82
+ - bin/pasaporte-fcgi.rb
83
+ - lib/pasaporte.rb
84
+ - lib/pasaporte/.DS_Store
85
+ - lib/pasaporte/assets/.DS_Store
86
+ - lib/pasaporte/assets/bgbar.png
87
+ - lib/pasaporte/assets/lock.png
88
+ - lib/pasaporte/assets/mainbg_green.gif
89
+ - lib/pasaporte/assets/mainbg_red.gif
90
+ - lib/pasaporte/assets/openid.png
91
+ - lib/pasaporte/assets/pasaporte.css
92
+ - lib/pasaporte/assets/pasaporte.js
93
+ - lib/pasaporte/assets/user.png
94
+ - lib/pasaporte/auth/cascade.rb
95
+ - lib/pasaporte/auth/remote_web_workplace.rb
96
+ - lib/pasaporte/auth/yaml_digest_table.rb
97
+ - lib/pasaporte/auth/yaml_table.rb
98
+ - lib/pasaporte/faster_openid.rb
99
+ - lib/pasaporte/iso_countries.yml
100
+ - lib/pasaporte/julik_state.rb
101
+ - lib/pasaporte/markaby_ext.rb
102
+ - lib/pasaporte/pasaporte_store.rb
103
+ - lib/pasaporte/timezones.yml
104
+ - test/fixtures/pasaporte_approvals.yml
105
+ - test/fixtures/pasaporte_profiles.yml
106
+ - test/fixtures/pasaporte_throttles.yml
107
+ - test/helper.rb
108
+ - test/mosquito.rb
109
+ - test/test_throttle.rb
110
+ - test/testable_openid_fetcher.rb
111
+ - test/test_approval.rb
112
+ - test/test_auth_backends.rb
113
+ - test/test_openid.rb
114
+ - test/test_pasaporte.rb
115
+ - test/test_profile.rb
116
+ - test/test_settings.rb
117
+ has_rdoc: true
118
+ homepage: http://pasaporte.rubyforge.org
119
+ post_install_message:
120
+ rdoc_options:
121
+ - --main
122
+ - README.txt
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: "0"
130
+ version:
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: "0"
136
+ version:
137
+ requirements: []
138
+
139
+ rubyforge_project: pasaporte
140
+ rubygems_version: 1.2.0
141
+ signing_key:
142
+ specification_version: 2
143
+ summary: Downgrades the OpenID providing business to the usual login-password stupidity.
144
+ test_files:
145
+ - test/test_approval.rb
146
+ - test/test_auth_backends.rb
147
+ - test/test_openid.rb
148
+ - test/test_pasaporte.rb
149
+ - test/test_profile.rb
150
+ - test/test_settings.rb
151
+ - test/test_throttle.rb