pasaporte 0.0.1

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 (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
data/History.txt ADDED
@@ -0,0 +1,2 @@
1
+ === HEAD
2
+ * Initial rollout
data/Manifest.txt ADDED
@@ -0,0 +1,41 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ TODO.txt
6
+ bin/pasaporte-fcgi.rb
7
+ lib/pasaporte.rb
8
+ lib/pasaporte/.DS_Store
9
+ lib/pasaporte/assets/.DS_Store
10
+ lib/pasaporte/assets/bgbar.png
11
+ lib/pasaporte/assets/lock.png
12
+ lib/pasaporte/assets/mainbg_green.gif
13
+ lib/pasaporte/assets/mainbg_red.gif
14
+ lib/pasaporte/assets/openid.png
15
+ lib/pasaporte/assets/pasaporte.css
16
+ lib/pasaporte/assets/pasaporte.js
17
+ lib/pasaporte/assets/user.png
18
+ lib/pasaporte/auth/cascade.rb
19
+ lib/pasaporte/auth/remote_web_workplace.rb
20
+ lib/pasaporte/auth/yaml_digest_table.rb
21
+ lib/pasaporte/auth/yaml_table.rb
22
+ lib/pasaporte/faster_openid.rb
23
+ lib/pasaporte/iso_countries.yml
24
+ lib/pasaporte/julik_state.rb
25
+ lib/pasaporte/markaby_ext.rb
26
+ lib/pasaporte/pasaporte_store.rb
27
+ lib/pasaporte/timezones.yml
28
+ test/fixtures/pasaporte_approvals.yml
29
+ test/fixtures/pasaporte_profiles.yml
30
+ test/fixtures/pasaporte_throttles.yml
31
+ test/helper.rb
32
+ test/mosquito.rb
33
+ test/test_throttle.rb
34
+ test/testable_openid_fetcher.rb
35
+ test/test_approval.rb
36
+ test/test_auth_backends.rb
37
+ test/test_openid.rb
38
+ test/test_pasaporte.rb
39
+ test/test_profile.rb
40
+ test/test_settings.rb
41
+ test/test_throttle.rb
data/README.txt ADDED
@@ -0,0 +1,72 @@
1
+ = Onde está seu pasaporte?
2
+
3
+ This is Pasaporte, a small identity server with a colored bar on top. It's in the style
4
+ of Crowd (but smaller). Will act as a mediator between OpenID and arbitary services where
5
+ users are distinguished by their nickname (login), their password and a domain name.
6
+
7
+ ==The idea
8
+
9
+ Pasaporte brings OpenID to the traditional simplicity of
10
+
11
+ is_a_villain = check_password(login, password, domain)
12
+
13
+ The only thing you WILL need to change is the AUTH constant. It should contain the proc
14
+ that, when called, will return true or false. Yes, it's that simple. All the negotiation
15
+ smorgasbord, profile editing, encryptodecryption and other electrabombastic niceties are
16
+ going to be taken care of.
17
+
18
+ Should the password become stale or should the authentication backend say that it no
19
+ longer has the user in question the authorization tokens are immediately revoked, and any
20
+ authorization requests will be denied.
21
+
22
+ ==Configuration
23
+
24
+ The adventurous among us can override the defaults (Pasaporte constants) by placing a
25
+ hash-formatted YAML file called "config.yml" in the pasaporte dir. And don't ask me what
26
+ a "hash-formatted YAML file" is, because if you do you are not adventurous.
27
+
28
+ ==A word of warning
29
+
30
+ Considering the clear-text passwords issue, we strongly recommend running Pasaporte under
31
+ SSL and under SSL only. But of course this might be prohibitive especially if you cannot
32
+ be self-signed or don't have an extra IP at hand. When you run Pasaporte under HTTPS all
33
+ URLs are going to be rewritten automatically to redirect and link to the HTTPS site.
34
+
35
+ ==Profiles
36
+
37
+ Pasaporte allows the user to have a simple passport page, where some info can be placed
38
+ for people who follow the OpenID profile URL. Sharing the information is entirelly optional.
39
+
40
+ ==The all-id
41
+
42
+ The login that you use is ultimately the nickname that comes in the URL. For that reason
43
+ no other login name can be entered in the form. However the user still can verify that
44
+ it's his nickname and change it should he need to (or if he mistyped).
45
+
46
+ In essence, Pasaporte offers a page for any nickname and shows an identitifer in this
47
+ page that allows the OpenID consumer to find the server endpoint. However if this user
48
+ does not exist, never logged in or has hidden his profile that's the only thing that will
49
+ be shown - a blank page with some OpenID metadata for basic interop.
50
+
51
+ It's important to understand that a Profile record is a helper for metadata and not
52
+ something authoritative - it's the auth routine that takes the actual decision about the
53
+ user's state.
54
+
55
+ ==Persistence
56
+
57
+ We store some data that the user might find useful to store and maybe display on his user
58
+ page. No sites that the user authorizes are stored. No sessions of the exchange are kept
59
+ except of the standard OpenID shared secrets (there are not linked to user records in any
60
+ way).
61
+
62
+ ==SREG data sharing
63
+
64
+ There is currently no provision for fetching SREG data (like email, date of birth and such)
65
+ from the autorizing routine. We might consider this in the future, for now the user has to
66
+ fill it in himself.
67
+
68
+ ==Sharding
69
+
70
+ The users in Pasaporte are segregated by the domain name of Pasaporte server. That is, if
71
+ you have two domains pointed at +one+ Pasaporte, you will not have name clashes between
72
+ the two domain names - the users are going to be split.
data/Rakefile ADDED
@@ -0,0 +1,111 @@
1
+ $:.reject! { |e| e.include? 'TextMate' }
2
+ require 'rubygems'
3
+ require 'hoe'
4
+ require File.dirname(__FILE__) + '/lib/pasaporte'
5
+ $KCODE = 'u'
6
+
7
+ class KolkHoe < Hoe
8
+ def define_tasks
9
+ extra_deps.reject! {|e| e[0] == 'hoe' }
10
+ super
11
+ end
12
+ end
13
+
14
+ # Disable spurious warnings when running tests, ActiveMagic cannot stand -w
15
+ Hoe::RUBY_FLAGS.replace ENV['RUBY_FLAGS'] || "-I#{%w(lib test).join(File::PATH_SEPARATOR)}" +
16
+ (Hoe::RUBY_DEBUG ? " #{RUBY_DEBUG}" : '')
17
+
18
+ KolkHoe.new('Pasaporte', Pasaporte::VERSION) do |p|
19
+ p.name = "pasaporte"
20
+ p.author = "Julik Tarkhanov"
21
+ p.description = "An OpenID server with a colored bar on top"
22
+ p.email = 'me@julik.nl'
23
+ p.summary = "Downgrades the OpenID providing business to the usual login-password stupidity."
24
+ p.url = "http://pasaporte.rubyforge.org"
25
+ p.rdoc_pattern = /README.txt|CHANGELOG.txt|lib/
26
+ p.test_globs = 'test/test_*.rb'
27
+ p.need_zip = true
28
+ p.extra_deps = ['activerecord', 'camping', ['ruby-openid', '>=2.1.0'], 'flexmock']
29
+ end
30
+
31
+ desc "Generate the proper list of country codes from the ISO list"
32
+ task :build_country_list do
33
+ ISO_LIST = 'http://www.iso.org/iso/iso3166_en_code_lists.txt'
34
+ require 'open-uri'
35
+ require 'iconv'
36
+
37
+ # By a twist of faith ISO provides the list in, uhm, ISO encoding AND with \\r \\n.
38
+ # line endings. I bet if they could have it formatted it would be Troglodyte Bold.
39
+ # Convert the whole shebang to UTF straight away!
40
+ iso_in_latin1 = open(ISO_LIST).read
41
+
42
+ c = Iconv.new('UTF-8', 'ISO-8859-1')
43
+ iso = c.iconv(iso_in_latin1)
44
+
45
+ # Remove the bad line breaks
46
+ iso.gsub!(/\r\n/, "\n")
47
+
48
+ # Throw away the header declaration
49
+ iso = iso.split(/\n\n/).pop.split(/\n/)
50
+
51
+ # Split ito something machine-readable
52
+ iso.map!{ | line | line.split(/;/) }
53
+
54
+ # If you use the raw ISO data you ARE SHOUTING IN ALL CAPS which makes everyone feel like an idiot.
55
+ # We have to do something about that.
56
+ iso.map! do | country, code |
57
+ country_for_humans = country.split(/\s/).map do | word |
58
+ # Prevent some abbrevs like U.S. from being casefolded
59
+ if (word.scan(/\./).length == word.scan(/\w/).length)
60
+ word
61
+ elsif %w( OF AND THE).include?(word)
62
+ word.downcase
63
+ else
64
+ word.chars.downcase.capitalize
65
+ end
66
+ end.join(" ")
67
+
68
+ [code.chars.downcase, country_for_humans].map(&:to_s)
69
+ end
70
+
71
+ # Assemble the resulting hash and flush it
72
+ outfile = File.dirname(__FILE__) + '/lib/pasaporte/iso_countries.yml'
73
+ File.open(outfile, 'w') do | out |
74
+ out << "# This has been autogenerated from #{ISO_LIST} on #{Time.now}. See Rakefile for details.\n"
75
+ out << "# This file is bonafide UTF-8 for YAML but do not edit it manually!\n"
76
+ out << Hash[*iso.flatten].to_yaml
77
+ end
78
+ end
79
+
80
+ desc "Generate the proper ist of world timezones from the tzinfo database"
81
+ task :build_timezone_list do
82
+ require 'open-uri'
83
+ # TODO - this should not be hardcoded because versions change
84
+ TZ_DATA = "ftp://elsie.nci.nih.gov/pub/tzdata2007g.tar.gz"
85
+
86
+ begin
87
+ FileUtils.mkdir_p(File.dirname(__FILE__) + '/tmp')
88
+ File.open("tmp/tzd.tar.gz", "w") {|f| f << open("ftp://elsie.nci.nih.gov/pub/tzdata2007g.tar.gz").read }
89
+ `cd tmp; tar xfz tzd.tar.gz`
90
+ zone_names = []
91
+ File.open(File.dirname(__FILE__) + '/tmp/zone.tab') do | f |
92
+ f.each_line do | l |
93
+ next if l =~ /#/
94
+ name = l.split(/\t/)[2..-1].join(" ").strip
95
+ next if name.blank?
96
+ # The formal value that is required per sreg' specs comes first.
97
+ # The humanized variant is everything before the first tab, with underscores
98
+ # replaced by spaces
99
+ zone_names << [name, name.split(/\s/).shift.gsub(/_/, ' ')]
100
+ end
101
+ end
102
+ outfile = File.dirname(__FILE__) + '/lib/pasaporte/timezones.yml'
103
+ File.open(outfile, 'w') do | out |
104
+ out << "# This has been autogenerated from #{TZ_DATA} on #{Time.now}. See Rakefile for details.\n"
105
+ out << "# This file is bonafide UTF-8 for YAML but do not edit it manually!\n"
106
+ out << zone_names.to_yaml
107
+ end
108
+ ensure
109
+ FileUtils.rm_rf(File.dirname(__FILE__) + '/tmp')
110
+ end
111
+ end
data/TODO.txt ADDED
@@ -0,0 +1,2 @@
1
+ * Proper flow when the user logs in but has no site to go to
2
+ * Fast-track whitelist for intranets (no "Decide" screen)
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'camping'
4
+ require 'camping/fastcgi'
5
+ require File.dirname(__FILE__) + '/../lib/pasaporte'
6
+
7
+ Camping::Models::Base.establish_connection(
8
+ :adapter => 'sqlite3',
9
+ :database => ENV['HOME'] + '/pasaporte.sqlitedb'
10
+ )
11
+
12
+ Pasaporte.create
13
+ Pasaporte::LOGGER = Logger.new(ENV['HOME'] + "/pasaporte.log")
14
+
15
+ serv = Camping::FastCGI.new
16
+ serv.mount('/', Pasaporte)
17
+ serv.start
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,192 @@
1
+ /* Superresetthemall */
2
+ * { margin: 0; padding: 0; }
3
+ html {
4
+ background: rgb(136, 138, 140) url("bgbar.png") top left repeat-x;
5
+ /* Force scrollbar */
6
+ height: 100%; margin-bottom: 1px;
7
+ /* I know trying myriad is stupid but I hope folks that have Adobe CS installed will appreciate */
8
+ font-family: "Myriad Pro", "Helvetica", "Lucida Grande", "Lucida Sans Unicode", "Helvetica", "Arial", sans-serif;
9
+ }
10
+
11
+ #toolbar, #work {
12
+ margin-top: 1em;
13
+ margin-left: auto;
14
+ margin-right: auto;
15
+ width: 19em;
16
+ background: rgb(215, 215, 215);
17
+ padding-left: 20px;
18
+ padding-right: 20px;
19
+ }
20
+
21
+ #toolbar {
22
+ height: 32px;
23
+ line-height: 32px;
24
+ background: rgb(222,222,222);
25
+ position: relative;
26
+ /* Graceful */
27
+ -webkit-border-radius: 6px;
28
+ -moz-border-radius: 6px;
29
+ }
30
+ #toolbar * {
31
+ color: rgb(80, 80, 80);
32
+ }
33
+ /* OpenID logo */
34
+ #toolbar img {
35
+ position: absolute;
36
+ top: -6px;
37
+ right: 0px;
38
+ }
39
+
40
+ #toolbar #loginBtn {
41
+ display: block;
42
+ background: url("lock.png") 0px -40px no-repeat;
43
+ padding-left: 32px;
44
+ margin-left: -16px;
45
+ padding-right: 10px;
46
+ float: left;
47
+ }
48
+ /* When signed in */
49
+ #toolbar a#loginBtn {
50
+ background-position: 0px -39px;
51
+ }
52
+
53
+ /* When not signed in */
54
+ #toolbar b#loginBtn {
55
+ background-position: 0px -5px;
56
+ }
57
+
58
+ #toolbar #profBtn {
59
+ display: block;
60
+ float: left;
61
+ background: url("user.png") 0px 0px no-repeat;
62
+ padding-left: 28px;
63
+ padding-right: 20px;
64
+ }
65
+
66
+ #work {
67
+ padding-top: .2em;
68
+ padding-bottom: .5em;
69
+ background: white url("mainbg_green.gif") top left repeat-x;
70
+ padding: .2em 20px .5em 20px;
71
+ color: rgb(102,102,102);
72
+ /* kinda shadyw */
73
+ border: 3px solid rgb(50,50,50);
74
+ border-top: none;
75
+ border-left: none;
76
+ }
77
+
78
+ .ProfilePage #work {
79
+ background-position: 0px -5px;
80
+ }
81
+ /* Show a red bar until the user is authed */
82
+ #work.notAuth { background-image: url("mainbg_red.gif"); }
83
+
84
+ h1, h2, h3 { line-height: 1.2em; font-weight: normal; margin-top: .5em; margin-bottom: .1em;}
85
+ h1 { font-size: 2em; }
86
+ h1 { font-size: 2em; }
87
+
88
+ /* Only the important stuff is true black
89
+ We also avoid the bolds of lucida */
90
+ b, strong { color: rgb(20, 20, 20); font-weight: normal; }
91
+
92
+ /* make the input bigger and swoosh the wacom around */
93
+ input { font-size: inherit; }
94
+
95
+ form#signon {
96
+ margin-top: .5em;
97
+ }
98
+ /* big center-aligned form */
99
+ form#signon label {
100
+ font-size: 1.2em;
101
+ display: block;
102
+ position: relative;
103
+ margin-bottom: 4px;
104
+ margin-top: 4px;
105
+ clear: both;
106
+ text-align: left;
107
+ }
108
+
109
+ form#signon input.bigb {
110
+ margin-top: 10px;
111
+ }
112
+
113
+ form#signon label * {
114
+ width: 50%;
115
+ display: block;
116
+ position: absolute;
117
+ top: 0;
118
+ left: 43%;
119
+ }
120
+
121
+ div#msg {
122
+ margin: 1.1em 0 0.5em 0;
123
+ padding: 5px 4px 4px 16px;
124
+ color: rgb(20,20,20);
125
+ font-size: small;
126
+ background: rgb(167, 207, 137);
127
+ }
128
+
129
+ div#msg.e {
130
+ background: rgb(250, 154, 132);
131
+ }
132
+
133
+ form { margin-bottom: .4em; }
134
+ p { margin: .2em 0 .6em 0; }
135
+ textarea { letter-spacing: .04em; margin-top: 1em; width: 100%; border-width: 1px;}
136
+
137
+ /* Here we have to be careful to keep Aqua widgets in Safari 3. The magic property is background.
138
+ http://particletree.com/notebook/design-friendly-select-elements-in-safari-3 */
139
+ input, textarea, option { font-family: inherit; margin-top: 1px; font-size: inherit;}
140
+ hr { visibility: hidden; margin-top: 7px; margin-bottom: 3px; }
141
+ input[type=submit] { padding: .1em; }
142
+ .fb {
143
+ padding-top: 3px; display: block; height: 1.3em;
144
+ margin-bottom: .3em; position: relative;
145
+ }
146
+ .sel {
147
+ padding-top: 3px; display: block; height: 3em;
148
+ margin-bottom: .3em; position: relative;
149
+ }
150
+ .sel select {
151
+ width: 100%;
152
+ margin-top: 3px;
153
+ margin-bottom: 7px;
154
+ }
155
+ .fb input {
156
+ display: block; position: absolute; width: 60%;
157
+ height: inherit; font-size: inherit; right: 0; top: 0;
158
+ }
159
+ .rad {
160
+ padding-top: 3px; margin-bottom: 6px; display: block;
161
+ }
162
+ .rad input {
163
+ font-size: x-small;
164
+ }
165
+
166
+ .inlineSelects {
167
+ display: block;
168
+ }
169
+
170
+ .inlineSelects select {
171
+ width: auto;
172
+ }
173
+
174
+ #work ul li {
175
+ margin-left: 1.2em;
176
+ }
177
+ /* Checkbox labels need some indent */
178
+ label.cblabel {
179
+ display: block;
180
+ padding-left: 1em;
181
+ text-indent: -1.3em;
182
+ padding-bottom: .5em;
183
+ }
184
+ .smaller {
185
+ font-size: .9em;
186
+ line-height: 1.1em;
187
+ }
188
+ a { color: #3258B8; }
189
+ a:hover { color: #24459C; }
190
+ a:active { color: blue ; }
191
+ /* Some think that if a label is clickable the link inside should be shadowed (lookin' at you safari 3). */
192
+ label a { z-index: 300; cursor: pointer;}
@@ -0,0 +1,10 @@
1
+ function cboxToggle(checkBox, depItem) {
2
+ if (checkBox.checked) { document.getElementById(depItem).style.display = "block";
3
+ } else { document.getElementById(depItem).style.display = "none"; }
4
+ }
5
+
6
+ function attachCheckbox(cBox, depItem) {
7
+ var cbox = document.getElementById(cBox);
8
+ cbox.onchange = function(the) { cboxToggle((the.target || the.srcElement), depItem); }
9
+ cboxToggle(cbox, depItem);
10
+ }
Binary file
@@ -0,0 +1,16 @@
1
+ # Make an array of backends and try to authenticate against them all until true is returned.
2
+ # Otherwise deny.
3
+ class Pasaporte::Auth::Cascade
4
+ attr_reader :backends
5
+ def initialize
6
+ @backends = []
7
+ end
8
+
9
+ def call(u, p, d)
10
+ @backends.each do | b |
11
+ Pasaporte::LOGGER.info("Running cascade auth against #{b}")
12
+ b.call(u,p,d) || next
13
+ end
14
+ false
15
+ end
16
+ end
@@ -0,0 +1,61 @@
1
+ # =begin
2
+ # ActiveDirectory-Exchange-Blaggh authentication guerilla-style. Will take the give username and password and try to authenticate
3
+ # them against the given Remote Web Workplace server. This assumes that the user on a domain also has an email address
4
+ # with OWA of course. If the user is found RWW will give us a specific response. No credentials or user information is
5
+ # retrieved.
6
+ # =end
7
+ class RemoteWebWorkplace
8
+ attr_accessor :use_ssl
9
+
10
+ VIEW_STATE_PAT = /name="__VIEWSTATE" value="([^"]+)"/
11
+ LOGIN_URL = "/Remote/logon.aspx"
12
+ BASE_LOGIN_URL = '/Remote/logon.aspx?ReturnUrl=%2fRemote%2fDefault.aspx'
13
+
14
+ def initialize(server)
15
+ @server = server
16
+ require 'net/http'
17
+ require 'net/https'
18
+ end
19
+
20
+ # Will run the auth
21
+ def logs_in?(user, password)
22
+
23
+ @http = (@use_ssl? ? Net::HTTPS : Net::HTTP).new(@server, (@use_ssl ? 443 : 80))
24
+
25
+ with_viewstate_and_connection(@http) do | payload |
26
+ login_form_values = {
27
+ "txtUserName" => user.to_s,
28
+ "txtUserPass" => password.to_s,
29
+ "cmdLogin" => "cmdLogin",
30
+ "listSpeed" => "Broadband",
31
+ "__VIEWSTATE" => payload,
32
+ }
33
+
34
+ begin
35
+ @http.start do |http|
36
+ form_post = Net::HTTP::Post.new(LOGIN_URL)
37
+ form_post.set_form_data(login_form_values, '&')
38
+ response = http.request(form_post); response.value
39
+ end
40
+ rescue Net::HTTPRetriableError => e
41
+ if e.message =~ /302/ # RWW will return a redirect if the user is found
42
+ return true
43
+ end
44
+ end
45
+ return false
46
+ end
47
+ end
48
+
49
+ private
50
+ def with_viewstate_and_connection(conn)
51
+ begin
52
+ viewstate_payload = conn.start do |http|
53
+ response = http.get(@base_login_url); response.value
54
+ response.body.scan(VIEW_STATE_PAT).pop.pop
55
+ end
56
+ yield viewstate_payload
57
+ rescue SocketError => e
58
+ raise "Cannot connect to Outlook Web Access at #{@server}: #{e.message}"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,23 @@
1
+ # Authenticate agains a simple YAML table of logins-passwords per domain. Don't forget
2
+ # the dash to begin a new entry!
3
+ #
4
+ # google.com:
5
+ # - :login: george
6
+ # :pass_md5: acbd18db4cc2f85cedef654fccc4a4d8
7
+ # - :login: james
8
+ # :pass_md5: ceba333c1d30a6dd94a57f9d34d73ff7
9
+ #
10
+ # Place the table in 'pasaporte/users_per_domain.yml'
11
+ require File.dirname(__FILE__) + '/yaml_table'
12
+ require 'digest/md5'
13
+
14
+ class Pasaporte::Auth::YamlDigestTable < Pasaporte::Auth::YamlTable
15
+ def call(login, pass, domain)
16
+ refresh
17
+ d = @table[domain]
18
+ return false unless d.is_a?(Array)
19
+ d.find do | user |
20
+ (user["login"] == login) && (user["pass_md5"] == Digest::MD5.hexdigest(pass))
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,43 @@
1
+ # Authenticate agains a simple YAML table of logins-passwords per domain. Don't forget
2
+ # the dash to begin a new entry!
3
+ #
4
+ # google.com:
5
+ # - :login: george
6
+ # :pass: foo
7
+ # - :login: james
8
+ # :pass: trinkle
9
+ #
10
+ # Place the table in 'pasaporte/users_per_domain.yml'
11
+ class Pasaporte::Auth::YamlTable
12
+ YAML_TABLE = File.dirname(Pasaporte::PATH) + '/pasaporte/users_per_domain.yml'
13
+ attr_accessor :table_path
14
+
15
+ def table_path
16
+ @table_path || YAML_TABLE
17
+ end
18
+
19
+ def initialize
20
+ refresh
21
+ end
22
+
23
+ def call(login, pass, domain)
24
+ refresh
25
+ d = @table[domain]
26
+ return false unless d.is_a?(Array)
27
+ u = d.find do | user |
28
+ (user["login"] == login) && (user["pass"] == pass)
29
+ end
30
+ !!u
31
+ end
32
+
33
+ private
34
+ def refresh
35
+ mt = File.stat(table_path).mtime.to_i
36
+ if @modtime && (@modtime == mt)
37
+ return
38
+ else
39
+ f, @modtime = File.read(table_path), mt
40
+ @table = YAML::load(f)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,39 @@
1
+ # This is something the JanRain folks omitted. The test suite for OpenID that
2
+ # is in Pasaporte takes 26 seconds to run with the standard library and
3
+ # 19 seconds with this. Lo and behold.
4
+ begin
5
+ require 'openssl'
6
+ module ::OpenID::CryptUtil
7
+ def self.hmac_sha1(key, text)
8
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('SHA1'), key, text).to_s
9
+ end
10
+ def self.sha1(s)
11
+ OpenSSL::Digest::SHA1.new(s).digest.to_s
12
+ end
13
+ end
14
+
15
+ class OpenID::DiffieHellman
16
+ # Convert any number of ints to OpenSSL bignums
17
+ def self._bignos(*args)
18
+ # The trick here is that Integer#to_bn does not work because you need to feed strings
19
+ args.map do |a|
20
+ raise ArgumentError, "Cannot convert nil to OpenSSL bignum!" if a.nil?
21
+ OpenSSL::BN.new(a.to_s)
22
+ end
23
+ end
24
+
25
+ def self.powermod(base, power, mod)
26
+ base, power, mod = _bignos(base, power, mod)
27
+ base.mod_exp(power, mod).to_i
28
+ end
29
+ end
30
+ rescue LoadError
31
+ $stderr.puts "Pasaporte will use slow ruby crypto. Please consider installing OpenSSL for Ruby."
32
+ end
33
+
34
+ # Redirect the logging to Pasaporte
35
+ # module ::OpenID::Util
36
+ # def self.log(message)
37
+ # Pasaporte::LOGGER.info('OpenID: ' + message)
38
+ # end
39
+ # end