pasaporte 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +2 -2
- data/Manifest.txt +14 -5
- data/README.txt +52 -15
- data/Rakefile +4 -9
- data/bin/pasaporte-emit-app.rb +5 -0
- data/bin/pasaporte-fcgi.rb +5 -1
- data/lib/pasaporte.rb +200 -332
- data/lib/pasaporte/hacks.rb +10 -0
- data/lib/pasaporte/julik_state.rb +14 -10
- data/lib/pasaporte/lighttpd/cacert.pem +21 -0
- data/lib/pasaporte/lighttpd/cert_localhost_combined.pem +32 -0
- data/lib/pasaporte/lighttpd/sample-lighttpd-config.conf +27 -0
- data/lib/pasaporte/models.rb +254 -0
- data/lib/pasaporte/token_box.rb +43 -0
- data/test/helper.rb +7 -1
- data/test/test_edit_profile.rb +53 -0
- data/test/test_openid.rb +20 -13
- data/test/test_pasaporte.rb +0 -103
- data/test/test_profile.rb +3 -2
- data/test/test_public_signon.rb +26 -0
- data/test/test_settings.rb +1 -2
- data/test/test_signout.rb +24 -0
- data/test/test_token_box.rb +54 -0
- data/test/test_with_partial_ssl.rb +99 -0
- metadata +27 -9
- data/lib/pasaporte/.DS_Store +0 -0
- data/lib/pasaporte/assets/.DS_Store +0 -0
data/History.txt
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
===
|
2
|
-
*
|
1
|
+
=== 0.0.3 - 14.02.2009
|
2
|
+
* Most glaring bugs fixed
|
data/Manifest.txt
CHANGED
@@ -3,10 +3,9 @@ Manifest.txt
|
|
3
3
|
README.txt
|
4
4
|
Rakefile
|
5
5
|
TODO.txt
|
6
|
+
bin/pasaporte-emit-app.rb
|
6
7
|
bin/pasaporte-fcgi.rb
|
7
8
|
lib/pasaporte.rb
|
8
|
-
lib/pasaporte/.DS_Store
|
9
|
-
lib/pasaporte/assets/.DS_Store
|
10
9
|
lib/pasaporte/assets/bgbar.png
|
11
10
|
lib/pasaporte/assets/lock.png
|
12
11
|
lib/pasaporte/assets/mainbg_green.gif
|
@@ -20,22 +19,32 @@ lib/pasaporte/auth/remote_web_workplace.rb
|
|
20
19
|
lib/pasaporte/auth/yaml_digest_table.rb
|
21
20
|
lib/pasaporte/auth/yaml_table.rb
|
22
21
|
lib/pasaporte/faster_openid.rb
|
22
|
+
lib/pasaporte/hacks.rb
|
23
23
|
lib/pasaporte/iso_countries.yml
|
24
24
|
lib/pasaporte/julik_state.rb
|
25
|
+
lib/pasaporte/lighttpd/cacert.pem
|
26
|
+
lib/pasaporte/lighttpd/cert_localhost_combined.pem
|
27
|
+
lib/pasaporte/lighttpd/sample-lighttpd-config.conf
|
25
28
|
lib/pasaporte/markaby_ext.rb
|
29
|
+
lib/pasaporte/models.rb
|
26
30
|
lib/pasaporte/pasaporte_store.rb
|
27
31
|
lib/pasaporte/timezones.yml
|
32
|
+
lib/pasaporte/token_box.rb
|
28
33
|
test/fixtures/pasaporte_approvals.yml
|
29
34
|
test/fixtures/pasaporte_profiles.yml
|
30
35
|
test/fixtures/pasaporte_throttles.yml
|
31
36
|
test/helper.rb
|
32
37
|
test/mosquito.rb
|
33
|
-
test/test_throttle.rb
|
34
|
-
test/testable_openid_fetcher.rb
|
35
38
|
test/test_approval.rb
|
36
39
|
test/test_auth_backends.rb
|
40
|
+
test/test_edit_profile.rb
|
37
41
|
test/test_openid.rb
|
38
42
|
test/test_pasaporte.rb
|
39
43
|
test/test_profile.rb
|
44
|
+
test/test_public_signon.rb
|
40
45
|
test/test_settings.rb
|
41
|
-
test/
|
46
|
+
test/test_signout.rb
|
47
|
+
test/test_throttle.rb
|
48
|
+
test/test_token_box.rb
|
49
|
+
test/test_with_partial_ssl.rb
|
50
|
+
test/testable_openid_fetcher.rb
|
data/README.txt
CHANGED
@@ -4,7 +4,7 @@ This is Pasaporte, a small identity server with a colored bar on top. It's in th
|
|
4
4
|
of Crowd (but smaller). Will act as a mediator between OpenID and arbitary services where
|
5
5
|
users are distinguished by their nickname (login), their password and a domain name.
|
6
6
|
|
7
|
-
==The idea
|
7
|
+
== The idea
|
8
8
|
|
9
9
|
Pasaporte brings OpenID to the traditional simplicity of
|
10
10
|
|
@@ -15,29 +15,66 @@ that, when called, will return true or false. Yes, it's that simple. All the neg
|
|
15
15
|
smorgasbord, profile editing, encryptodecryption and other electrabombastic niceties are
|
16
16
|
going to be taken care of.
|
17
17
|
|
18
|
-
|
18
|
+
Here is an example of a simple auth procedure:
|
19
|
+
|
20
|
+
# Stick your super auth HERE. Should be a proc accepting login, pass and domain
|
21
|
+
Pasaporte::AUTH = lambda do | login, pass, domain |
|
22
|
+
allowd = {"joe" => "secret"}
|
23
|
+
return (allowd[login] && (allowd[login] == pass))
|
24
|
+
end
|
25
|
+
|
26
|
+
Obviously you can let your auth procedure look up in ACLs and things like that if you
|
27
|
+
really need it to.
|
28
|
+
|
29
|
+
If the password becomes stale or should the authentication backend say that it no
|
19
30
|
longer has the user in question the authorization tokens are immediately revoked, and any
|
20
31
|
authorization requests will be denied.
|
21
32
|
|
22
|
-
==
|
33
|
+
== Using SSL
|
34
|
+
|
35
|
+
It is recommended that you run pasaporte in full SSL mode. However,
|
36
|
+
some OpenID consumers disallow OpenID providers with self-signed (i.e. free)
|
37
|
+
SSL certificates. Pasaporte mitigates this by offering the "partial SSL" mode. When turned on,
|
38
|
+
only the signon page (where the password is entered) and subsequent pages with which the user
|
39
|
+
interacts will be protected with SSL encryption, while the public OpenID endpoint will NOT be
|
40
|
+
SSL-enabled. Same is true for the server-server step of OpenID handshake.
|
41
|
+
|
42
|
+
This will allow even stricter providers to use Pasaporte servers, but without passing the user login
|
43
|
+
over the wire in clear text.
|
44
|
+
|
45
|
+
When partial SSL is turned on, the profile page (OpenID identity) will forcibly be made
|
46
|
+
unencrypted (will redirect to non-secure port).
|
47
|
+
|
48
|
+
Partial SSL is disabled by default - to enable set PARTIAL_SSL to true.
|
49
|
+
|
50
|
+
== Current issues
|
51
|
+
|
52
|
+
As of now, we are not aware of sites that cannot consume OpenID from Pasaporte.
|
53
|
+
There is currently no provision for verifying that the URL you are logging in from is the
|
54
|
+
one Pasaporte manages.
|
55
|
+
|
56
|
+
== Configuration
|
23
57
|
|
24
58
|
The adventurous among us can override the defaults (Pasaporte constants) by placing a
|
25
59
|
hash-formatted YAML file called "config.yml" in the pasaporte dir. And don't ask me what
|
26
60
|
a "hash-formatted YAML file" is, because if you do you are not adventurous.
|
27
61
|
|
28
|
-
|
62
|
+
Here the rundown of the config parameters:
|
29
63
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
64
|
+
MAX_FAILED_LOGIN_ATTEMPTS - after how many login attempts the user will be trottled
|
65
|
+
THROTTLE_FOR - Trottle length in seconds
|
66
|
+
ALLOW_DELEGATION - if set to true, the user will be able to redirect his OpenID
|
67
|
+
SESSION_LIFETIME - in seconds - how long does a session remain valid
|
68
|
+
PARTIAL_SSL - see above
|
69
|
+
HTTP_PORT - if partial SSL is used, the port on which the standard version runs
|
70
|
+
SSL_PORT - if partial SSL is used, the port on which the secure version runs
|
34
71
|
|
35
|
-
==Profiles
|
72
|
+
== Profiles
|
36
73
|
|
37
74
|
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
|
75
|
+
for people who follow the OpenID profile URL. Sharing the information is entirelly optional.
|
39
76
|
|
40
|
-
==The all-id
|
77
|
+
== The all-id
|
41
78
|
|
42
79
|
The login that you use is ultimately the nickname that comes in the URL. For that reason
|
43
80
|
no other login name can be entered in the form. However the user still can verify that
|
@@ -52,20 +89,20 @@ It's important to understand that a Profile record is a helper for metadata and
|
|
52
89
|
something authoritative - it's the auth routine that takes the actual decision about the
|
53
90
|
user's state.
|
54
91
|
|
55
|
-
==Persistence
|
92
|
+
== Persistence
|
56
93
|
|
57
94
|
We store some data that the user might find useful to store and maybe display on his user
|
58
|
-
page. No
|
95
|
+
page. No sessions of the exchange are kept
|
59
96
|
except of the standard OpenID shared secrets (there are not linked to user records in any
|
60
97
|
way).
|
61
98
|
|
62
|
-
==SREG data sharing
|
99
|
+
== SREG data sharing
|
63
100
|
|
64
101
|
There is currently no provision for fetching SREG data (like email, date of birth and such)
|
65
102
|
from the autorizing routine. We might consider this in the future, for now the user has to
|
66
103
|
fill it in himself.
|
67
104
|
|
68
|
-
==Sharding
|
105
|
+
== Sharding
|
69
106
|
|
70
107
|
The users in Pasaporte are segregated by the domain name of Pasaporte server. That is, if
|
71
108
|
you have two domains pointed at +one+ Pasaporte, you will not have name clashes between
|
data/Rakefile
CHANGED
@@ -4,18 +4,11 @@ require 'hoe'
|
|
4
4
|
require File.dirname(__FILE__) + '/lib/pasaporte'
|
5
5
|
$KCODE = 'u'
|
6
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
7
|
# Disable spurious warnings when running tests, ActiveMagic cannot stand -w
|
15
8
|
Hoe::RUBY_FLAGS.replace ENV['RUBY_FLAGS'] || "-I#{%w(lib test).join(File::PATH_SEPARATOR)}" +
|
16
9
|
(Hoe::RUBY_DEBUG ? " #{RUBY_DEBUG}" : '')
|
17
10
|
|
18
|
-
|
11
|
+
psp = Hoe.new('Pasaporte', Pasaporte::VERSION) do |p|
|
19
12
|
p.name = "pasaporte"
|
20
13
|
p.author = "Julik Tarkhanov"
|
21
14
|
p.description = "An OpenID server with a colored bar on top"
|
@@ -25,9 +18,11 @@ KolkHoe.new('Pasaporte', Pasaporte::VERSION) do |p|
|
|
25
18
|
p.rdoc_pattern = /README.txt|CHANGELOG.txt|lib/
|
26
19
|
p.test_globs = 'test/test_*.rb'
|
27
20
|
p.need_zip = true
|
28
|
-
p.extra_deps = ['activerecord', 'camping', ['ruby-openid', '>=2.1.0'], 'flexmock']
|
21
|
+
p.extra_deps = ['activerecord', ['camping' '>=1.5.180'], ['ruby-openid', '>=2.1.0'], 'flexmock']
|
29
22
|
end
|
30
23
|
|
24
|
+
psp.spec.rdoc_options << "--charset" << "utf-8"
|
25
|
+
|
31
26
|
desc "Generate the proper list of country codes from the ISO list"
|
32
27
|
task :build_country_list do
|
33
28
|
ISO_LIST = 'http://www.iso.org/iso/iso3166_en_code_lists.txt'
|
data/bin/pasaporte-fcgi.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
#! /usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
3
|
require 'camping'
|
4
4
|
require 'camping/fastcgi'
|
@@ -12,6 +12,10 @@ Camping::Models::Base.establish_connection(
|
|
12
12
|
Pasaporte.create
|
13
13
|
Pasaporte::LOGGER = Logger.new(ENV['HOME'] + "/pasaporte.log")
|
14
14
|
|
15
|
+
ENV.keys.grep(/^PASAPORTE_/).each do | envar |
|
16
|
+
Pasaporte.const_set(envar.gsub(/^PASAPORTE_/, ''), ENV[envar])
|
17
|
+
end
|
18
|
+
|
15
19
|
serv = Camping::FastCGI.new
|
16
20
|
serv.mount('/', Pasaporte)
|
17
21
|
serv.start
|
data/lib/pasaporte.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
gem 'ruby-openid', '>=2.1.0'
|
2
|
+
gem 'ruby-openid', '>=2.1.0' # Not less!
|
3
3
|
|
4
4
|
$: << File.dirname(__FILE__)
|
5
5
|
|
@@ -8,16 +8,24 @@ $: << File.dirname(__FILE__)
|
|
8
8
|
camping/session
|
9
9
|
openid
|
10
10
|
openid/extensions/sreg
|
11
|
-
|
11
|
+
).each {|r| require r }
|
12
|
+
|
13
|
+
Camping.goes :Pasaporte
|
14
|
+
|
15
|
+
%w(
|
12
16
|
pasaporte/julik_state
|
13
17
|
pasaporte/markaby_ext
|
18
|
+
pasaporte/hacks
|
19
|
+
pasaporte/token_box
|
14
20
|
).each {|r| require r }
|
15
21
|
|
16
|
-
Camping.goes :Pasaporte
|
17
22
|
|
23
|
+
Markaby::Builder.set(:indent, 2)
|
18
24
|
Markaby::Builder.set(:output_xml_instruction, false)
|
19
25
|
|
20
26
|
module Pasaporte
|
27
|
+
VERSION = '0.0.3'
|
28
|
+
|
21
29
|
module Auth; end # Acts as a container for auth classes
|
22
30
|
|
23
31
|
MAX_FAILED_LOGIN_ATTEMPTS = 3
|
@@ -25,8 +33,12 @@ module Pasaporte
|
|
25
33
|
DEFAULT_COUNTRY = 'nl'
|
26
34
|
DEFAULT_TZ = 'Europe/Amsterdam'
|
27
35
|
ALLOW_DELEGATION = true
|
28
|
-
|
29
|
-
|
36
|
+
|
37
|
+
|
38
|
+
SESSION_LIFETIME = 12.hours
|
39
|
+
PARTIAL_SSL = false
|
40
|
+
HTTP_PORT = 80
|
41
|
+
SSL_PORT = 443
|
30
42
|
|
31
43
|
LOGGER = Logger.new(STDERR) #:nodoc:
|
32
44
|
PATH = File.expand_path(__FILE__) #:nodoc:
|
@@ -82,18 +94,13 @@ module Pasaporte
|
|
82
94
|
end
|
83
95
|
end
|
84
96
|
|
85
|
-
module LoggerHijack
|
86
|
-
#def service(*the_rest)
|
87
|
-
# _hijacked, OpenID::Util.logger = OpenID::Util.logger, LOGGER
|
88
|
-
# returning(super(*the_rest)) { OpenID::Util.logger = _hijacked }
|
89
|
-
#end
|
90
|
-
end
|
91
|
-
|
92
97
|
# Or a semblance thereof
|
93
98
|
module Secure
|
94
99
|
class PleaseLogin < RuntimeError; end
|
95
100
|
class Throttled < RuntimeError; end
|
96
101
|
class FullStop < RuntimeError; end
|
102
|
+
class RedirectToSSL < RuntimeError; end
|
103
|
+
class RedirectToPlain < RuntimeError; end
|
97
104
|
|
98
105
|
module CheckMethods
|
99
106
|
def _redir_to_login_page!(persistables)
|
@@ -105,7 +112,7 @@ module Pasaporte
|
|
105
112
|
end
|
106
113
|
|
107
114
|
# Allow the user to log in. Suspend any variables passed in the session.
|
108
|
-
def require_login(persistables = {})
|
115
|
+
def require_login!(persistables = {})
|
109
116
|
deny_throttled!
|
110
117
|
raise "No nickname" unless @nickname
|
111
118
|
_redir_to_login_page!(persistables) unless is_logged_in?
|
@@ -117,33 +124,66 @@ module Pasaporte
|
|
117
124
|
def deny_throttled!
|
118
125
|
raise Throttled if Models::Throttle.throttled?(env)
|
119
126
|
end
|
120
|
-
|
127
|
+
|
128
|
+
# When partial SSL is enabled we use this method to redirect
|
129
|
+
def require_ssl!
|
130
|
+
raise RedirectToSSL if PARTIAL_SSL && !@env.HTTPS
|
131
|
+
end
|
132
|
+
|
133
|
+
# When partial SSL is enabled we use this method to force a plain page
|
134
|
+
def require_plain!
|
135
|
+
raise RedirectToPlain if PARTIAL_SSL && @env.HTTPS
|
136
|
+
end
|
137
|
+
|
138
|
+
def validate_token!
|
139
|
+
#from = URI.parse(@env.HTTP_REFERER).path
|
140
|
+
#LOGGER.debug "Validating form token"
|
141
|
+
#token_box.validate!(from, @input.tok)
|
142
|
+
end
|
143
|
+
|
121
144
|
def profile_by_nickname(n)
|
122
145
|
::Pasaporte::Models::Profile.find_or_create_by_nickname_and_domain_name(n, my_domain)
|
123
146
|
end
|
124
147
|
end
|
125
148
|
|
126
|
-
def
|
127
|
-
|
128
|
-
# JulikState.expire_old(SESSION_LIFETIME)
|
129
|
-
# Camping::Models::Session.delete_all("created_at < '%s'" % day_zero)
|
149
|
+
def token_box
|
150
|
+
@state.token_box ||= TokenBox.new
|
130
151
|
end
|
131
152
|
|
132
153
|
def service(*a)
|
133
154
|
begin
|
134
|
-
expire_old_sessions!
|
135
155
|
@ctr = self.class.to_s.split('::').pop
|
136
156
|
super(*a)
|
137
157
|
rescue FullStop
|
138
158
|
return self
|
139
159
|
rescue PleaseLogin
|
140
|
-
LOGGER.info "#{env['REMOTE_ADDR']} - Redirecting to signon
|
160
|
+
LOGGER.info "#{env['REMOTE_ADDR']} - Redirecting to signon"
|
141
161
|
redirect R(Pasaporte::Controllers::Signon, @nickname)
|
142
162
|
return self
|
143
163
|
rescue Throttled
|
144
164
|
LOGGER.info "#{env['REMOTE_ADDR']} - Throttled user tried again"
|
145
165
|
redirect R(Pasaporte::Controllers::ThrottledPage)
|
146
166
|
return self
|
167
|
+
rescue TokenBox::Invalid => i
|
168
|
+
LOGGER.warn "Form token has been compromised on #{@env.REQUEST_URI} - #{i}"
|
169
|
+
LOGGER.warn @state.token_box.inspect
|
170
|
+
redirect R(Pasaporte::Controllers::FormExpired)
|
171
|
+
rescue RedirectToSSL
|
172
|
+
LOGGER.info "Forcing redirect to SSL page"
|
173
|
+
the_uri = URI.parse(@env.REQUEST_URI)
|
174
|
+
the_uri.host = @env.SERVER_NAME
|
175
|
+
the_uri.scheme = 'https'
|
176
|
+
the_uri.port = SSL_PORT unless SSL_PORT.to_i == 443
|
177
|
+
redirect the_uri.to_s
|
178
|
+
return self
|
179
|
+
rescue RedirectToPlain
|
180
|
+
LOGGER.info "Forcing redirect to plain (non-SSL) page"
|
181
|
+
the_uri = URI.parse(@env.REQUEST_URI)
|
182
|
+
the_uri.host = @env.SERVER_NAME
|
183
|
+
the_uri.scheme = 'http'
|
184
|
+
the_uri.port = HTTP_PORT unless HTTP_PORT.to_i == 80
|
185
|
+
redirect the_uri.to_s
|
186
|
+
return self
|
147
187
|
end
|
148
188
|
self
|
149
189
|
end
|
@@ -153,272 +193,19 @@ module Pasaporte
|
|
153
193
|
module CookiePreservingRedirect
|
154
194
|
def redirect(*args)
|
155
195
|
@headers['Set-Cookie'] = @cookies.map { |k,v| "#{k}=#{C.escape(v)}; path=#{self/"/"}" if v != @k[k] } - [nil]
|
196
|
+
force_session_save!
|
156
197
|
super(*args)
|
157
198
|
end
|
158
199
|
end
|
159
200
|
|
160
|
-
# The order here is important. Camping::Session has to come LAST (
|
201
|
+
# The order here is important. Camping::Session has to come LAST (outermost)
|
161
202
|
# otherwise you risk losing the session if one of the services upstream
|
162
203
|
# redirects.
|
163
|
-
[CampingFlash,
|
164
|
-
|
165
|
-
|
166
|
-
module Models
|
167
|
-
MAX = :limit # Thank you rails core, it was MAX before
|
168
|
-
class CreatePasaporte < V 1.0
|
169
|
-
def self.up
|
170
|
-
create_table :pasaporte_profiles, :force => true do |t|
|
171
|
-
# http://openid.net/specs/openid-simple-registration-extension-1_0.html
|
172
|
-
t.column :nickname, :string, MAX => 20
|
173
|
-
t.column :email, :string, MAX => 70
|
174
|
-
t.column :fullname, :string, MAX => 50
|
175
|
-
t.column :dob, :date, :null => true
|
176
|
-
t.column :gender, :string, MAX => 1
|
177
|
-
t.column :postcode, :string, MAX => 10
|
178
|
-
t.column :country, :string, MAX => 2
|
179
|
-
t.column :language, :string, MAX => 5
|
180
|
-
t.column :timezone, :string, MAX => 50
|
181
|
-
|
182
|
-
# And our extensions
|
183
|
-
# is the profile shared (visible to others)
|
184
|
-
t.column :shared, :boolean, :default => false
|
185
|
-
|
186
|
-
# his bio
|
187
|
-
t.column :info, :text
|
188
|
-
|
189
|
-
# when he last used Pasaporte
|
190
|
-
t.column :last_login, :datetime
|
191
|
-
|
192
|
-
# the encryption part that we generate for every user, the other is the pass
|
193
|
-
# the total encryption key for private data will be stored in the session only when
|
194
|
-
# the user is logged in
|
195
|
-
t.column :secret_salt, :integer
|
196
|
-
|
197
|
-
# Good servers delegate
|
198
|
-
t.column :openid_server, :string
|
199
|
-
t.column :openid_delegate, :string
|
200
|
-
|
201
|
-
# We shard by domain
|
202
|
-
t.column :domain_name, :string, :null => false, :default => 'localhost'
|
203
|
-
|
204
|
-
# Keep a close watch on those who
|
205
|
-
t.column :throttle_count, :integer, :default => 0
|
206
|
-
t.column :suspicious, :boolean, :default => false
|
207
|
-
end
|
208
|
-
|
209
|
-
add_index(:pasaporte_profiles, [:nickname, :domain_name], :unique)
|
210
|
-
|
211
|
-
create_table :pasaporte_settings do |t|
|
212
|
-
t.column :setting, :string
|
213
|
-
t.column :value, :binary
|
214
|
-
end
|
215
|
-
|
216
|
-
create_table :pasaporte_associations do |t|
|
217
|
-
# server_url is blob, because URLs could be longer
|
218
|
-
# than db can handle as a string
|
219
|
-
t.column :server_url, :binary
|
220
|
-
t.column :handle, :string
|
221
|
-
t.column :secret, :binary
|
222
|
-
t.column :issued, :integer
|
223
|
-
t.column :lifetime, :integer
|
224
|
-
t.column :assoc_type, :string
|
225
|
-
end
|
226
|
-
|
227
|
-
create_table :pasaporte_nonces do |t|
|
228
|
-
t.column :nonce, :string
|
229
|
-
t.column :created, :integer
|
230
|
-
end
|
204
|
+
[CampingFlash, Secure, JulikState, CookiePreservingRedirect].map{|m| include m }
|
231
205
|
|
232
|
-
create_table :pasaporte_throttles do |t|
|
233
|
-
t.column :created_at, :datetime
|
234
|
-
t.column :client_fingerprint, :string, MAX => 40
|
235
|
-
end
|
236
|
-
end
|
237
206
|
|
238
|
-
|
239
|
-
|
240
|
-
drop_table :pasaporte_settings
|
241
|
-
drop_table :pasaporte_associations
|
242
|
-
drop_table :pasaporte_nonces
|
243
|
-
drop_table :pasaporte_throttles
|
244
|
-
end
|
245
|
-
end
|
246
|
-
class AddAprovals < V(1.1)
|
247
|
-
def self.up
|
248
|
-
create_table :pasaporte_approvals do | t |
|
249
|
-
t.column :profile_id, :integer, :null => false
|
250
|
-
t.column :trust_root, :string, :null => false
|
251
|
-
end
|
252
|
-
add_index(:pasaporte_approvals, [:profile_id, :trust_root], :unique)
|
253
|
-
end
|
254
|
-
|
255
|
-
def self.down
|
256
|
-
drop_table :pasaporte_approvals
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
class MigrateOpenidTables < V(1.2)
|
261
|
-
def self.up
|
262
|
-
drop_table :pasaporte_settings
|
263
|
-
drop_table :pasaporte_nonces
|
264
|
-
create_table :pasaporte_nonces, :force => true do |t|
|
265
|
-
t.column :server_url, :string, :null => false
|
266
|
-
t.column :timestamp, :integer, :null => false
|
267
|
-
t.column :salt, :string, :null => false
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
def self.down
|
272
|
-
drop_table :pasaporte_nonces
|
273
|
-
create_table :pasaporte_nonces, :force => true do |t|
|
274
|
-
t.column "nonce", :string
|
275
|
-
t.column "created", :integer
|
276
|
-
end
|
277
|
-
|
278
|
-
create_table :pasaporte_settings, :force => true do |t|
|
279
|
-
t.column "setting", :string
|
280
|
-
t.column "value", :binary
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
class ShardOpenidTables < V(1.3)
|
286
|
-
def self.up
|
287
|
-
add_column :pasaporte_associations, :pasaporte_domain, :string, :null => false, :default => 'localhost'
|
288
|
-
add_column :pasaporte_nonces, :pasaporte_domain, :string, :null => false, :default => 'localhost'
|
289
|
-
end
|
290
|
-
|
291
|
-
def self.down
|
292
|
-
remove_column :pasaporte_nonces, :pasaporte_domain
|
293
|
-
remove_column :pasaporte_associations, :pasaporte_domain
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
# Minimal info we store about people. It's the container for the sreg data
|
298
|
-
# in the first place.
|
299
|
-
class Profile < Base
|
300
|
-
before_create { |p| p.secret_salt = rand(Time.now) }
|
301
|
-
before_save :validate_delegate_uris
|
302
|
-
validates_presence_of :nickname
|
303
|
-
validates_presence_of :domain_name
|
304
|
-
validates_uniqueness_of :nickname, :scope => :domain_name
|
305
|
-
attr_protected :domain_name, :nickname
|
306
|
-
has_many :approvals, :dependent => :delete_all
|
307
|
-
|
308
|
-
any_url_present = lambda do |r|
|
309
|
-
!r.openid_server.blank? || !r.openid_server.blank?
|
310
|
-
end
|
311
|
-
%w(openid_server openid_delegate).map do | c |
|
312
|
-
validates_presence_of c, :if => any_url_present
|
313
|
-
end
|
314
|
-
|
315
|
-
# Convert the profile to sreg according to the spec (YYYY-MM-DD for dob and such)
|
316
|
-
def to_sreg_fields(fields_to_extract = nil)
|
317
|
-
fields_to_extract ||= %w( nickname email fullname dob gender postcode country language timezone )
|
318
|
-
fields_to_extract.inject({}) do | out, field |
|
319
|
-
v = self[field]
|
320
|
-
v.blank? ? out : (out[field.to_s] = v.to_s; out)
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
# We have to override that because we want our protected attributes
|
325
|
-
def self.find_or_create_by_nickname_and_domain_name(nick, domain)
|
326
|
-
returning(super(nick, domain)) do | me |
|
327
|
-
((me.nickname, me.domain_name = nick, domain) && me.save) if me.new_record?
|
328
|
-
end
|
329
|
-
end
|
330
|
-
class << self
|
331
|
-
alias_method :find_or_create_by_domain_name_and_nickname,
|
332
|
-
:find_or_create_by_nickname_and_domain_name
|
333
|
-
end
|
334
|
-
|
335
|
-
def generate_sess_key
|
336
|
-
self.secret_salt ||= rand(Time.now)
|
337
|
-
s = [nickname, secret_salt, Time.now.year, Time.now.month].join('|')
|
338
|
-
OpenSSL::Digest::SHA1.new(s).hexdigest.to_s
|
339
|
-
end
|
340
|
-
|
341
|
-
# Check if this profile wants us to delegate his openid to a different identity provider.
|
342
|
-
# If both delegate and server are filled in properly this will return true
|
343
|
-
def delegates_openid?
|
344
|
-
ALLOW_DELEGATION && (!openid_server.blank? && !openid_delegate.blank?)
|
345
|
-
end
|
346
|
-
|
347
|
-
def delegates_openid=(nv)
|
348
|
-
(self.openid_server, self.openid_delegate = nil, nil) if [false, '0', 0, 'no'].include?(nv)
|
349
|
-
end
|
350
|
-
alias_method :delegates_openid, :delegates_openid? # for checkboxes
|
351
|
-
|
352
|
-
def to_s; nickname; end
|
353
|
-
|
354
|
-
private
|
355
|
-
def validate_delegate_uris
|
356
|
-
if ([self.openid_server, self.openid_delegate].select{|i| i.blank?}).length == 1
|
357
|
-
errors.add(:delegate_server, "If you use delegation you have to specify both addresses")
|
358
|
-
false
|
359
|
-
end
|
360
|
-
|
361
|
-
%w(openid_server openid_delegate).map do | attr |
|
362
|
-
return if self[attr].blank?
|
363
|
-
begin
|
364
|
-
self[attr] = OpenID::URINorm.urinorm(self[attr])
|
365
|
-
rescue Exception => e
|
366
|
-
errors.add(attr, e.message)
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
# A token that the user has approved a site (a site's trust root) as legal
|
373
|
-
# recipient of his information
|
374
|
-
class Approval < Base
|
375
|
-
belongs_to :profile
|
376
|
-
validates_presence_of :profile_id, :trust_root
|
377
|
-
validates_uniqueness_of :trust_root, :scope => :profile_id
|
378
|
-
def to_s; trust_root; end
|
379
|
-
end
|
380
|
-
|
381
|
-
# Openid setting
|
382
|
-
class Setting < Base; end
|
383
|
-
|
384
|
-
# Openid nonces
|
385
|
-
class Nonce < Base; end
|
386
|
-
|
387
|
-
# Openid assocs
|
388
|
-
class Association < Base
|
389
|
-
def from_record
|
390
|
-
OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
|
391
|
-
end
|
392
|
-
|
393
|
-
def expired?
|
394
|
-
Time.now.to_i > (issued + lifetime)
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
# Set throttles
|
399
|
-
class Throttle < Base
|
400
|
-
|
401
|
-
# Set a throttle with the environment of the request
|
402
|
-
def self.set!(e)
|
403
|
-
create(:client_fingerprint => env_hash(e))
|
404
|
-
end
|
405
|
-
|
406
|
-
# Check if an environment is throttled
|
407
|
-
def self.throttled?(e)
|
408
|
-
prune!
|
409
|
-
count(:conditions => ["client_fingerprint = ? AND created_at > ?", env_hash(e), cutoff]) > 0
|
410
|
-
end
|
411
|
-
|
412
|
-
private
|
413
|
-
def self.prune!; delete_all "created_at < '#{cutoff.to_s(:db)}'"; end
|
414
|
-
def self.cutoff; Time.now - THROTTLE_FOR; end
|
415
|
-
def self.env_hash(e)
|
416
|
-
OpenSSL::Digest::SHA1.new([e['REMOTE_ADDR'], e['HTTP_USER_AGENT']].map(&:to_s).join('|')).to_s
|
417
|
-
end
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
require File.dirname(__FILE__) + '/pasaporte/pasaporte_store'
|
207
|
+
require 'pasaporte/models'
|
208
|
+
require 'pasaporte/pasaporte_store'
|
422
209
|
|
423
210
|
module Controllers
|
424
211
|
|
@@ -437,6 +224,12 @@ module Pasaporte
|
|
437
224
|
post_with_nick(*extras)
|
438
225
|
end
|
439
226
|
|
227
|
+
def head(*extras)
|
228
|
+
raise "Nickname is required for this action" unless (@nickname = extras.shift)
|
229
|
+
raise "#{self.class} does not respond to head_with_nick" unless respond_to?(:head_with_nick)
|
230
|
+
head_with_nick(*extras)
|
231
|
+
end
|
232
|
+
|
440
233
|
# So that we can define put_with_nick and Camping sees it as #put being available
|
441
234
|
def respond_to?(m, *whatever)
|
442
235
|
super(m.to_sym) || super("#{m}_with_nick".to_sym)
|
@@ -457,29 +250,34 @@ module Pasaporte
|
|
457
250
|
include OpenID::Server
|
458
251
|
|
459
252
|
class Err < RuntimeError; end #:nodoc
|
460
|
-
class NeedsApproval <
|
253
|
+
class NeedsApproval < Err; end #:nodoc
|
461
254
|
class Denied < Err; end #:nodoc
|
462
|
-
class NoOpenidRequest <
|
255
|
+
class NoOpenidRequest < Err; end #:nodoc
|
256
|
+
class SwitchUser < Secure::PleaseLogin; end #:nodoc
|
463
257
|
|
464
258
|
def get_with_nick
|
259
|
+
require_plain!
|
465
260
|
begin
|
466
261
|
@oid_request = openid_request_from_input_or_session
|
467
262
|
|
468
|
-
LOGGER.info "
|
263
|
+
LOGGER.info "OpenID: user #{@nickname} must not be throttled"
|
469
264
|
deny_throttled!
|
470
265
|
|
471
|
-
LOGGER.info "
|
266
|
+
LOGGER.info "OpenID: nick must match the identity URL"
|
472
267
|
check_nickname_matches_identity_url
|
268
|
+
|
269
|
+
LOGGER.info "OpenID: identity must reside on our server"
|
270
|
+
check_identity_lives_here
|
473
271
|
|
474
|
-
LOGGER.info "
|
272
|
+
LOGGER.info "OpenID: user must be logged in"
|
475
273
|
check_logged_in
|
476
274
|
|
477
275
|
@profile = profile_by_nickname(@nickname)
|
478
276
|
|
479
|
-
LOGGER.info "
|
277
|
+
LOGGER.info "OpenID: trust root is on the approvals list"
|
480
278
|
check_if_previously_approved
|
481
279
|
|
482
|
-
LOGGER.info "
|
280
|
+
LOGGER.info "OpenID: OpenID verified, redirecting"
|
483
281
|
|
484
282
|
succesful_resp = @oid_request.answer(true)
|
485
283
|
add_sreg(@oid_request, succesful_resp)
|
@@ -487,28 +285,36 @@ module Pasaporte
|
|
487
285
|
rescue NoOpenidRequest
|
488
286
|
return 'This is an OpenID server endpoint.'
|
489
287
|
rescue ProtocolError => e
|
490
|
-
LOGGER.error "
|
288
|
+
LOGGER.error "OpenID: Cannot decode the OpenID request - #{e.message}"
|
491
289
|
return "Something went wrong processing your request"
|
290
|
+
rescue SwitchUser => e
|
291
|
+
# Force a session save, remove the current user from the session and throw
|
292
|
+
# to the login page for the user to switch to
|
293
|
+
@state.nickname = nil
|
294
|
+
force_session_save!
|
295
|
+
LOGGER.warn "OpenID: suspend - need to switch user first"
|
296
|
+
@oid_request.immediate ? ask_user_to_approve : (raise e)
|
492
297
|
rescue PleaseLogin => e
|
493
298
|
# There is a subtlety here. If the user had NO session before entering
|
494
299
|
# this, he will get a new SID upon arriving at the signon page and thus
|
495
300
|
# will loose his openid request
|
496
301
|
force_session_save!
|
497
|
-
LOGGER.warn "
|
302
|
+
LOGGER.warn "OpenID: suspend - the user needs to login first, saving session"
|
498
303
|
@oid_request.immediate ? ask_user_to_approve : (raise e)
|
499
304
|
rescue NeedsApproval
|
500
|
-
LOGGER.warn "
|
305
|
+
LOGGER.warn "OpenID: suspend - the URL needs approval first"
|
501
306
|
ask_user_to_approve
|
502
307
|
rescue Denied => d
|
503
|
-
LOGGER.warn "
|
308
|
+
LOGGER.warn "OpenID: deny OpenID to #{@nickname} - #{d.message}"
|
504
309
|
send_openid_response(@oid_request.answer(false))
|
505
310
|
rescue Secure::Throttled => e
|
506
|
-
LOGGER.warn "
|
311
|
+
LOGGER.warn "OpenID: deny OpenID to #{@nickname} - user is throttled"
|
507
312
|
send_openid_response(@oid_request.answer(false))
|
508
313
|
end
|
509
314
|
end
|
510
315
|
|
511
316
|
def post_with_nick
|
317
|
+
require_plain!
|
512
318
|
req = openid_server.decode_request(input)
|
513
319
|
raise ProtocolError, "The decoded request was nil" if req.nil?
|
514
320
|
# Check for dumb mode HIER!
|
@@ -523,7 +329,7 @@ module Pasaporte
|
|
523
329
|
if input.keys.grep(/openid/).any?
|
524
330
|
@state.delete(:pending_openid)
|
525
331
|
r = openid_server.decode_request(input)
|
526
|
-
LOGGER.info "Starting a
|
332
|
+
LOGGER.info "Starting a session #{r.trust_root} -> #{r.identity}"
|
527
333
|
@state.pending_openid = r
|
528
334
|
elsif @state.pending_openid
|
529
335
|
LOGGER.info "Resuming an OpenID session with #{@state.pending_openid.trust_root}"
|
@@ -534,19 +340,28 @@ module Pasaporte
|
|
534
340
|
end
|
535
341
|
|
536
342
|
def check_nickname_matches_identity_url
|
537
|
-
nick_from_uri = @oid_request.identity.to_s.split(/\//)
|
343
|
+
nick_from_uri = @oid_request.identity.to_s.split(/\//).pop
|
538
344
|
if (nick_from_uri != @nickname)
|
539
345
|
raise Denied, "The identity '#{@oid_request.claimed_id}' does not mach the URL realm"
|
540
346
|
end
|
541
|
-
|
347
|
+
|
542
348
|
if (@state.nickname && (nick_from_uri != @state.nickname))
|
543
|
-
raise
|
349
|
+
raise SwitchUser, "The identity '#{@oid_request.claimed_id}' is not the one of the current user"
|
544
350
|
end
|
545
351
|
end
|
546
|
-
|
352
|
+
|
353
|
+
# TODO: we need to give the user a chance to say "I want these URLs to be legit too"
|
354
|
+
def check_identity_lives_here
|
355
|
+
the_id = URI.parse(@oid_request.identity)
|
356
|
+
# HTTP_HOST check
|
357
|
+
if (the_id.host != @env['HTTP_HOST'])
|
358
|
+
raise Denied, "The identity '#{@oid_request.claimed_id}' is not in this server(#{@env['HTTP_HOST']}"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
547
362
|
def check_logged_in
|
548
363
|
message = "Before authorizing '%s' you will need to login" % input["openid.trust_root"]
|
549
|
-
require_login(:pending_openid => @oid_request, :msg => message)
|
364
|
+
require_login!(:pending_openid => @oid_request, :msg => message)
|
550
365
|
end
|
551
366
|
|
552
367
|
def ask_user_to_approve
|
@@ -576,29 +391,28 @@ module Pasaporte
|
|
576
391
|
|
577
392
|
# Return the yadis autodiscovery XML for the user
|
578
393
|
class Yadis < personal(:yadis)
|
579
|
-
YADIS_TPL = %{
|
580
|
-
<xrds:XRDS xmlns:xrds="xri://$xrds"
|
581
|
-
xmlns="xri://$xrd*($v*2.0)"
|
582
|
-
xmlns:openid="http://openid.net/xmlns/1.0">
|
394
|
+
YADIS_TPL = %{<?xml version="1.0" encoding="UTF-8"?>
|
395
|
+
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
|
583
396
|
<XRD>
|
584
|
-
<Service
|
397
|
+
<Service>
|
585
398
|
<Type>http://openid.net/signon/1.0</Type>
|
586
399
|
<URI>%s</URI>
|
587
|
-
<openid:Delegate>%s</openid:Delegate>
|
588
400
|
</Service>
|
589
401
|
</XRD>
|
590
402
|
</xrds:XRDS>
|
591
403
|
}
|
592
404
|
|
593
405
|
def get_with_nick
|
594
|
-
@headers["Content-
|
406
|
+
@headers["Content-Type"] = "application/xrds+xml"
|
407
|
+
LOGGER.info "YADIS requested for #{@nickname}"
|
595
408
|
@skip_layout = true
|
596
|
-
|
409
|
+
# We only use the server for now
|
410
|
+
YADIS_TPL % get_endpoints[0]
|
597
411
|
end
|
598
412
|
|
599
413
|
private
|
600
414
|
def get_endpoints
|
601
|
-
defaults = [_our_endpoint_uri,
|
415
|
+
defaults = [_our_endpoint_uri, _our_identity_url]
|
602
416
|
@profile = Profile.find_by_nickname_and_domain_name(@nickname, my_domain)
|
603
417
|
return defaults unless @profile && @profile.delegates_openid?
|
604
418
|
[@profile.openid_server, @profile.openid_delegate]
|
@@ -607,7 +421,7 @@ module Pasaporte
|
|
607
421
|
|
608
422
|
class ApprovalsPage < personal(:approvals)
|
609
423
|
def get_with_nick
|
610
|
-
require_login
|
424
|
+
require_login!
|
611
425
|
@approvals = @profile.approvals
|
612
426
|
if @approvals.empty?
|
613
427
|
@msg = 'You currently do not have any associations with other sites through us'
|
@@ -620,7 +434,7 @@ module Pasaporte
|
|
620
434
|
|
621
435
|
class DeleteApproval < personal('disapprove/(\d+)')
|
622
436
|
def get_with_nick(appr_id)
|
623
|
-
require_login
|
437
|
+
require_login!
|
624
438
|
ap = @profile.approvals.find(appr_id); ap.destroy
|
625
439
|
show_message "The site #{ap} has been removed from your approvals list"
|
626
440
|
redirect R(ApprovalsPage, @nickname)
|
@@ -633,18 +447,27 @@ module Pasaporte
|
|
633
447
|
include Secure::CheckMethods
|
634
448
|
|
635
449
|
def get(nick=nil)
|
636
|
-
LOGGER.info "Entered signon
|
450
|
+
LOGGER.info "Entered signon, #{@env.HTTPS ? :HTTPS : :HTTP_plain }"
|
637
451
|
deny_throttled!
|
638
452
|
return redirect(DashPage, @state.nickname) if @state.nickname
|
639
453
|
if nick && @state.pending_openid
|
640
|
-
humane =
|
454
|
+
humane = begin
|
455
|
+
URI.parse(@state.pending_openid.trust_root).host
|
456
|
+
rescue URI::InvalidURIError
|
457
|
+
LOGGER.error "Failed to parse #{@state.pending_openid.trust_root}"
|
458
|
+
@state.pending_openid.trust_root
|
459
|
+
end
|
641
460
|
show_message "Before authorizing with <b>#{humane}</b> you will need to login"
|
642
461
|
end
|
462
|
+
|
463
|
+
require_ssl!
|
464
|
+
|
643
465
|
@nickname = nick;
|
644
466
|
render :signon_form
|
645
467
|
end
|
646
468
|
|
647
469
|
def post(n=nil)
|
470
|
+
|
648
471
|
begin
|
649
472
|
deny_throttled!
|
650
473
|
rescue Pasaporte::Secure::Throttled => th
|
@@ -654,11 +477,19 @@ module Pasaporte
|
|
654
477
|
end
|
655
478
|
raise th
|
656
479
|
end
|
480
|
+
|
481
|
+
require_ssl!
|
482
|
+
|
657
483
|
@nickname = @input.login || n || (raise "No nickname to authenticate")
|
484
|
+
|
658
485
|
# The throttling logic must be moved into throttles apparently
|
486
|
+
|
659
487
|
# Start counting
|
660
488
|
@state.failed_logins ||= 0
|
661
489
|
|
490
|
+
# Validate token
|
491
|
+
validate_token!
|
492
|
+
|
662
493
|
# If the user reaches the failed login limit we ban him for a while and
|
663
494
|
# tell the OpenID requesting party to go away
|
664
495
|
if Pasaporte::AUTH.call(@nickname, input.pass, my_domain)
|
@@ -726,8 +557,7 @@ module Pasaporte
|
|
726
557
|
class Signout < personal(:signout)
|
727
558
|
def get_with_nick
|
728
559
|
(redirect R(Signon, @nickname); return) unless is_logged_in?
|
729
|
-
|
730
|
-
@state = Camping::H.new
|
560
|
+
reset_session!
|
731
561
|
@state.msg = "Thanks for using the service and goodbye"
|
732
562
|
redirect R(Signon, @nickname)
|
733
563
|
end
|
@@ -737,13 +567,16 @@ module Pasaporte
|
|
737
567
|
# when associating for the first time
|
738
568
|
class Decide < personal(:decide)
|
739
569
|
def get_with_nick
|
740
|
-
|
570
|
+
require_ssl!
|
571
|
+
require_login!
|
572
|
+
|
741
573
|
@oid_request = @state.pending_openid
|
742
574
|
render :decide
|
743
575
|
end
|
744
576
|
|
745
577
|
def post_with_nick
|
746
|
-
|
578
|
+
require_ssl!
|
579
|
+
require_login!
|
747
580
|
if !@state.pending_openid
|
748
581
|
@report = "There is no OpenID request to approve anymore. Looks like it went out already."
|
749
582
|
render :bailout
|
@@ -760,12 +593,16 @@ module Pasaporte
|
|
760
593
|
# Allows the user to modify the settings
|
761
594
|
class EditProfile < personal(:edit)
|
762
595
|
def get_with_nick
|
763
|
-
require_login
|
596
|
+
require_login!
|
597
|
+
require_ssl!
|
764
598
|
render :profile_form
|
765
599
|
end
|
766
600
|
|
767
601
|
def post_with_nick
|
768
|
-
require_login
|
602
|
+
require_login!
|
603
|
+
require_ssl!
|
604
|
+
validate_token!
|
605
|
+
|
769
606
|
_collapse_checkbox_input(input)
|
770
607
|
|
771
608
|
if @profile.update_attributes(input.profile)
|
@@ -821,13 +658,32 @@ module Pasaporte
|
|
821
658
|
render :bailout
|
822
659
|
end
|
823
660
|
end
|
661
|
+
|
662
|
+
class FormExpired < R('/form-expired')
|
663
|
+
def get
|
664
|
+
@report = "The form has expired unfortunately"
|
665
|
+
render :bailout
|
666
|
+
end
|
667
|
+
end
|
824
668
|
|
825
669
|
# Just show a public profile page. Before the user logs in for the first time
|
826
670
|
# it works like a dummy page. After the user has been succesfully authenticated he can
|
827
671
|
# show his personal info on this page (this is his identity URL).
|
828
672
|
class ProfilePage < personal
|
673
|
+
|
674
|
+
def head(nick)
|
675
|
+
get(nick)
|
676
|
+
return
|
677
|
+
end
|
678
|
+
|
829
679
|
def get(nick)
|
830
680
|
@nickname = nick
|
681
|
+
|
682
|
+
# Redirect the OpenID requesting party to the usual HTTP so that
|
683
|
+
# the OpenID procedure takes place without SSL, if partial SSL is turned on
|
684
|
+
require_plain! if PARTIAL_SSL
|
685
|
+
|
686
|
+
LOGGER.info "Profile page GET for #{nick}, sending YADIS header"
|
831
687
|
@headers['X-XRDS-Location'] = _our_identity_url + '/yadis'
|
832
688
|
@title = "#{@nickname}'s profile"
|
833
689
|
@profile = Profile.find_by_nickname_and_domain_name(@nickname, my_domain)
|
@@ -837,7 +693,7 @@ module Pasaporte
|
|
837
693
|
end
|
838
694
|
|
839
695
|
class DashPage < personal(:prefs)
|
840
|
-
def get_with_nick; require_login
|
696
|
+
def get_with_nick; require_login!; render :dash; end
|
841
697
|
end
|
842
698
|
end
|
843
699
|
|
@@ -849,7 +705,8 @@ module Pasaporte
|
|
849
705
|
|
850
706
|
# Return a RELATIVELY reliable domain key
|
851
707
|
def my_domain
|
852
|
-
env["SERVER_NAME"].gsub(/^www\./i, '')
|
708
|
+
server = env["SERVER_NAME"].gsub(/^www\./i, '')
|
709
|
+
(server.mb_chars rescue server.chars).downcase.to_s
|
853
710
|
end
|
854
711
|
|
855
712
|
# Camping processes double values (hidden field with 0 and checkbox with 1) as an array.
|
@@ -859,15 +716,21 @@ module Pasaporte
|
|
859
716
|
(v == ["0", "1"]) ? (ha[k] = "1") : (v.is_a?(Hash) ? _collapse_checkbox_input(v) : true)
|
860
717
|
end
|
861
718
|
end
|
862
|
-
|
719
|
+
|
720
|
+
def _csrf_token
|
721
|
+
#input :name => :tok, :type => :hidden, :value => token_box.procure!(@env.REQUEST_URI)
|
722
|
+
#LOGGER.warn "After token procurement #{token_box.inspect}"
|
723
|
+
end
|
724
|
+
|
863
725
|
def openid_server
|
864
726
|
@store ||= PasaporteStore.new
|
865
727
|
|
866
728
|
# Associations etc are sharded per domain on which Pasaporte sits
|
867
729
|
@store.pasaporte_domain = @env['SERVER_NAME']
|
868
730
|
|
869
|
-
# we also need to provide endopint URL - this is where Pasaporte is mounted
|
870
|
-
|
731
|
+
# we also need to provide endopint URL - this is where Pasaporte is mounted.
|
732
|
+
# Op-endpoint is the endpoint used by the server
|
733
|
+
@server ||= OpenID::Server::Server.new(@store, _our_endpoint_uri)
|
871
734
|
@server
|
872
735
|
end
|
873
736
|
|
@@ -960,22 +823,25 @@ module Pasaporte
|
|
960
823
|
self << "Your password:"
|
961
824
|
input.pass! :name => "pass", :type => "password"
|
962
825
|
end
|
826
|
+
_csrf_token
|
963
827
|
input :type => :submit, :value => 'Log me in'
|
964
828
|
end
|
965
829
|
end
|
966
830
|
|
967
831
|
def layout
|
968
832
|
@headers['Cache-Control'] = 'no-cache; must-revalidate'
|
833
|
+
@headers['Content-Type'] ||= 'text/html'
|
834
|
+
|
969
835
|
if @skip_layout
|
970
836
|
self << yield; return
|
971
837
|
end
|
972
838
|
|
973
|
-
@headers['Content-type'] = 'text/html'
|
974
839
|
xhtml_transitional do
|
975
840
|
head do
|
976
|
-
meta
|
977
|
-
link :rel => "openid.server", :href => _openid_server_uri
|
841
|
+
self << '<meta http-equiv="X-XRDS-Location" content="%s/yadis" />' % _our_identity_url
|
842
|
+
link :rel => "openid.server", :href => _openid_server_uri
|
978
843
|
link :rel => "openid.delegate", :href => _openid_delegate_uri
|
844
|
+
|
979
845
|
link :rel => "stylesheet", :href => _s("pasaporte.css")
|
980
846
|
script :type=>'text/javascript', :src => _s("pasaporte.js")
|
981
847
|
title(@title || ('%s : pasaporte' % env['SERVER_NAME']))
|
@@ -1006,14 +872,14 @@ module Pasaporte
|
|
1006
872
|
R(Assets, file)
|
1007
873
|
end
|
1008
874
|
|
1009
|
-
# Render either our
|
875
|
+
# Render either our endpoint URL or the URL of the delegate
|
1010
876
|
def _openid_server_uri
|
1011
877
|
(@profile && @profile.delegates_openid?) ? @profile.openid_server : _our_endpoint_uri
|
1012
878
|
end
|
1013
879
|
|
1014
|
-
# Render either our
|
880
|
+
# Render either our identity URL or the URL of the delegate
|
1015
881
|
def _openid_delegate_uri
|
1016
|
-
(@profile && @profile.delegates_openid?) ? @profile.openid_delegate :
|
882
|
+
(@profile && @profile.delegates_openid?) ? @profile.openid_delegate : _our_identity_url
|
1017
883
|
end
|
1018
884
|
|
1019
885
|
# Canonicalized URL of our endpoint
|
@@ -1050,12 +916,13 @@ module Pasaporte
|
|
1050
916
|
end
|
1051
917
|
|
1052
918
|
form :method => :post do
|
919
|
+
_csrf_token
|
1053
920
|
input :name => :pleasedo, :type => :submit, :value => " Yes, do allow "
|
1054
921
|
self << ' '
|
1055
922
|
input :name => :nope, :type => :submit, :value => " No, they are villains! "
|
1056
923
|
end
|
1057
924
|
end
|
1058
|
-
|
925
|
+
|
1059
926
|
def _toolbar
|
1060
927
|
# my profile button
|
1061
928
|
# log me out button
|
@@ -1103,7 +970,7 @@ module Pasaporte
|
|
1103
970
|
form(:method => :post) do
|
1104
971
|
|
1105
972
|
h2 "Your profile"
|
1106
|
-
|
973
|
+
_csrf_token
|
1107
974
|
label.cblabel :for => :share_info do
|
1108
975
|
_cbox :profile, :shared, :id => :share_info
|
1109
976
|
self << '  Share your info on your OpenID page'
|
@@ -1206,9 +1073,10 @@ module Pasaporte
|
|
1206
1073
|
def self.create
|
1207
1074
|
JulikState.create_schema
|
1208
1075
|
self::Models.create_schema
|
1209
|
-
LOGGER.warn "Deleting
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1076
|
+
self::LOGGER.warn "Deleting sessions, assocs and nonces"
|
1077
|
+
[self::Models::Throttle, self::Models::Nonce,
|
1078
|
+
self::Models::Association, JulikState::State].each do | m |
|
1079
|
+
m.delete_all
|
1080
|
+
end
|
1213
1081
|
end
|
1214
1082
|
end
|