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.
- data/History.txt +2 -0
- data/Manifest.txt +41 -0
- data/README.txt +72 -0
- data/Rakefile +111 -0
- data/TODO.txt +2 -0
- data/bin/pasaporte-fcgi.rb +17 -0
- data/lib/pasaporte/.DS_Store +0 -0
- data/lib/pasaporte/assets/.DS_Store +0 -0
- data/lib/pasaporte/assets/bgbar.png +0 -0
- data/lib/pasaporte/assets/lock.png +0 -0
- data/lib/pasaporte/assets/mainbg_green.gif +0 -0
- data/lib/pasaporte/assets/mainbg_red.gif +0 -0
- data/lib/pasaporte/assets/openid.png +0 -0
- data/lib/pasaporte/assets/pasaporte.css +192 -0
- data/lib/pasaporte/assets/pasaporte.js +10 -0
- data/lib/pasaporte/assets/user.png +0 -0
- data/lib/pasaporte/auth/cascade.rb +16 -0
- data/lib/pasaporte/auth/remote_web_workplace.rb +61 -0
- data/lib/pasaporte/auth/yaml_digest_table.rb +23 -0
- data/lib/pasaporte/auth/yaml_table.rb +43 -0
- data/lib/pasaporte/faster_openid.rb +39 -0
- data/lib/pasaporte/iso_countries.yml +247 -0
- data/lib/pasaporte/julik_state.rb +42 -0
- data/lib/pasaporte/markaby_ext.rb +8 -0
- data/lib/pasaporte/pasaporte_store.rb +60 -0
- data/lib/pasaporte/timezones.yml +797 -0
- data/lib/pasaporte.rb +1214 -0
- data/test/fixtures/pasaporte_approvals.yml +12 -0
- data/test/fixtures/pasaporte_profiles.yml +45 -0
- data/test/fixtures/pasaporte_throttles.yml +4 -0
- data/test/helper.rb +66 -0
- data/test/mosquito.rb +596 -0
- data/test/test_approval.rb +33 -0
- data/test/test_auth_backends.rb +59 -0
- data/test/test_openid.rb +363 -0
- data/test/test_pasaporte.rb +326 -0
- data/test/test_profile.rb +165 -0
- data/test/test_settings.rb +27 -0
- data/test/test_throttle.rb +70 -0
- data/test/testable_openid_fetcher.rb +82 -0
- metadata +151 -0
data/History.txt
ADDED
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,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
|
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
|