ruby-openid 1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-openid might be problematic. Click here for more details.
- data/COPYING +21 -0
- data/INSTALL +34 -0
- data/README +67 -0
- data/TODO +9 -0
- data/examples/README +54 -0
- data/examples/cacert.pem +7815 -0
- data/examples/consumer.rb +285 -0
- data/examples/openid-store/associations/http-localhost_3A3000_2Fserver-EMQbAy3NnHVzA.s0u5KAcplKGzo +6 -0
- data/examples/openid-store/auth_key +1 -0
- data/examples/rails_active_record_store/README +59 -0
- data/examples/rails_active_record_store/XX_add_openidstore.rb +30 -0
- data/examples/rails_active_record_store/models/openid_association.rb +12 -0
- data/examples/rails_active_record_store/models/openid_nonce.rb +3 -0
- data/examples/rails_active_record_store/models/openid_setting.rb +2 -0
- data/examples/rails_active_record_store/openid_helper.rb +91 -0
- data/examples/rails_active_record_store/openidstore_test.rb +15 -0
- data/examples/rails_active_record_store/schema.mysql.sql +22 -0
- data/examples/rails_active_record_store/schema.postgresql.sql +21 -0
- data/examples/rails_active_record_store/schema.sqlite.sql +21 -0
- data/examples/rails_openid_login_generator/USAGE +23 -0
- data/examples/rails_openid_login_generator/openid_login_generator.rb +36 -0
- data/examples/rails_openid_login_generator/templates/README +116 -0
- data/examples/rails_openid_login_generator/templates/controller.rb +116 -0
- data/examples/rails_openid_login_generator/templates/controller_test.rb +0 -0
- data/examples/rails_openid_login_generator/templates/helper.rb +2 -0
- data/examples/rails_openid_login_generator/templates/openid_login_system.rb +87 -0
- data/examples/rails_openid_login_generator/templates/user.rb +14 -0
- data/examples/rails_openid_login_generator/templates/user_test.rb +0 -0
- data/examples/rails_openid_login_generator/templates/users.yml +0 -0
- data/examples/rails_openid_login_generator/templates/view_login.rhtml +15 -0
- data/examples/rails_openid_login_generator/templates/view_logout.rhtml +10 -0
- data/examples/rails_openid_login_generator/templates/view_welcome.rhtml +9 -0
- data/examples/rails_server/README +153 -0
- data/examples/rails_server/Rakefile +10 -0
- data/examples/rails_server/app/controllers/application.rb +4 -0
- data/examples/rails_server/app/controllers/login_controller.rb +35 -0
- data/examples/rails_server/app/controllers/server_controller.rb +185 -0
- data/examples/rails_server/app/helpers/application_helper.rb +3 -0
- data/examples/rails_server/app/helpers/login_helper.rb +2 -0
- data/examples/rails_server/app/helpers/server_helper.rb +9 -0
- data/examples/rails_server/app/views/layouts/server.rhtml +61 -0
- data/examples/rails_server/app/views/login/index.rhtml +32 -0
- data/examples/rails_server/app/views/server/decide.rhtml +11 -0
- data/examples/rails_server/config/boot.rb +19 -0
- data/examples/rails_server/config/database.yml +85 -0
- data/examples/rails_server/config/environment.rb +53 -0
- data/examples/rails_server/config/environments/development.rb +19 -0
- data/examples/rails_server/config/environments/production.rb +19 -0
- data/examples/rails_server/config/environments/test.rb +19 -0
- data/examples/rails_server/config/routes.rb +23 -0
- data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-YU.tkND1J4fEZhnuAoT5Zc0yCA0 +6 -0
- data/examples/rails_server/doc/README_FOR_APP +2 -0
- data/examples/rails_server/log/development.log +6059 -0
- data/examples/rails_server/log/production.log +0 -0
- data/examples/rails_server/log/server.log +0 -0
- data/examples/rails_server/log/test.log +0 -0
- data/examples/rails_server/public/404.html +8 -0
- data/examples/rails_server/public/500.html +8 -0
- data/examples/rails_server/public/dispatch.cgi +12 -0
- data/examples/rails_server/public/dispatch.fcgi +26 -0
- data/examples/rails_server/public/dispatch.rb +12 -0
- data/examples/rails_server/public/favicon.ico +0 -0
- data/examples/rails_server/public/images/rails.png +0 -0
- data/examples/rails_server/public/javascripts/controls.js +750 -0
- data/examples/rails_server/public/javascripts/dragdrop.js +584 -0
- data/examples/rails_server/public/javascripts/effects.js +854 -0
- data/examples/rails_server/public/javascripts/prototype.js +1785 -0
- data/examples/rails_server/public/robots.txt +1 -0
- data/examples/rails_server/script/about +3 -0
- data/examples/rails_server/script/breakpointer +3 -0
- data/examples/rails_server/script/console +3 -0
- data/examples/rails_server/script/destroy +3 -0
- data/examples/rails_server/script/generate +3 -0
- data/examples/rails_server/script/performance/benchmarker +3 -0
- data/examples/rails_server/script/performance/profiler +3 -0
- data/examples/rails_server/script/plugin +3 -0
- data/examples/rails_server/script/process/reaper +3 -0
- data/examples/rails_server/script/process/spawner +3 -0
- data/examples/rails_server/script/process/spinner +3 -0
- data/examples/rails_server/script/runner +3 -0
- data/examples/rails_server/script/server +3 -0
- data/examples/rails_server/test/functional/login_controller_test.rb +18 -0
- data/examples/rails_server/test/functional/server_controller_test.rb +18 -0
- data/examples/rails_server/test/test_helper.rb +28 -0
- data/lib/hmac-md5.rb +11 -0
- data/lib/hmac-rmd160.rb +11 -0
- data/lib/hmac-sha1.rb +11 -0
- data/lib/hmac-sha2.rb +25 -0
- data/lib/hmac.rb +112 -0
- data/lib/openid/association.rb +109 -0
- data/lib/openid/consumer.rb +928 -0
- data/lib/openid/dh.rb +48 -0
- data/lib/openid/discovery.rb +89 -0
- data/lib/openid/fetchers.rb +119 -0
- data/lib/openid/filestore.rb +315 -0
- data/lib/openid/htmltokenizer.rb +355 -0
- data/lib/openid/parse.rb +23 -0
- data/lib/openid/server.rb +951 -0
- data/lib/openid/service.rb +135 -0
- data/lib/openid/stores.rb +178 -0
- data/lib/openid/trustroot.rb +100 -0
- data/lib/openid/util.rb +273 -0
- data/test/assoc.rb +38 -0
- data/test/consumer.rb +384 -0
- data/test/dh.rb +20 -0
- data/test/extensions.rb +30 -0
- data/test/linkparse.rb +305 -0
- data/test/runtests.rb +11 -0
- data/test/server2.rb +1053 -0
- data/test/storetestcase.rb +172 -0
- data/test/teststore.rb +23 -0
- data/test/trustroot.rb +113 -0
- data/test/util.rb +56 -0
- metadata +218 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
require_gem 'ruby-yadis', '>=0.2.3'
|
6
|
+
rescue LoadError
|
7
|
+
require 'yadis/service'
|
8
|
+
end
|
9
|
+
|
10
|
+
module OpenID
|
11
|
+
|
12
|
+
# OpenIDService is an object representation of an OpenID server,
|
13
|
+
# and the services it provides. It contains a useful information such
|
14
|
+
# as the server URL, and information about the OpenID identity bound
|
15
|
+
# to the server. OpenIDService object should be produced using the
|
16
|
+
# OpenIDService.from_service class method with a Yadis Service object.
|
17
|
+
# See the ruby Yadis library for more information:
|
18
|
+
#
|
19
|
+
# http://www.openidenabled.com/yadis/libraries/ruby
|
20
|
+
#
|
21
|
+
# Unless you choose to do your own discovery and interface with
|
22
|
+
# OpenIDConsumer through the OpenIDConsumer.begin_without_discovery
|
23
|
+
# method, you won't need to ever use this object directly. It is used
|
24
|
+
# internally by the OpenIDConsumer object.
|
25
|
+
class OpenIDServiceEndpoint < ServiceEndpoint
|
26
|
+
|
27
|
+
@@namespace = {'openid' => 'http://openid.net/xmlns/1.0'}
|
28
|
+
attr_accessor :service_types, :uri, :yadis_url, :delegate_url
|
29
|
+
|
30
|
+
# Class method to produce OpenIDService objects. Call with a Yadis Service
|
31
|
+
# object. Will return nil if the Service object does not represent an
|
32
|
+
# an OpenID server.
|
33
|
+
def OpenIDServiceEndpoint.from_endpoint(service, versions=nil)
|
34
|
+
return nil unless OpenIDServiceEndpoint.is_type?(service, versions)
|
35
|
+
|
36
|
+
s = new
|
37
|
+
s.service_types = service.service_types
|
38
|
+
s.uri = service.uri
|
39
|
+
s.yadis_url = service.yadis.uri
|
40
|
+
|
41
|
+
s.delegate_url = nil
|
42
|
+
REXML::XPath.each(service.element, 'openid:Delegate', @@namespace) do |e|
|
43
|
+
s.delegate_url = e.text.strip
|
44
|
+
end
|
45
|
+
|
46
|
+
return s
|
47
|
+
end
|
48
|
+
|
49
|
+
# Class method to determine if a Yadis service object is an OpenID server.
|
50
|
+
# +versions+ is a list of Strings representing the versions of the OpenID
|
51
|
+
# protocol you support. Only service that match one of the versions will
|
52
|
+
# return a value that evaluates to true. If no +versions+ list is
|
53
|
+
# specified, all versions will be accepted.
|
54
|
+
def OpenIDServiceEndpoint.is_type?(service, versions=nil)
|
55
|
+
# escape the period in the version numbers
|
56
|
+
versions.collect! {|v| v.gsub('.', '\.')} if versions
|
57
|
+
|
58
|
+
base_url = 'http://openid\.net/signon/'
|
59
|
+
base_url += '(' + versions.join('|') + '){1}' if versions
|
60
|
+
|
61
|
+
service.service_types.each do |st|
|
62
|
+
return true if st.match(base_url)
|
63
|
+
end
|
64
|
+
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
|
68
|
+
# Alias for +supports?+
|
69
|
+
def uses_extension?(extension_url)
|
70
|
+
return self.supports?(extension_url)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Same as uses_extension? Checks to see if the provided URL is
|
74
|
+
# in the list of service types. Example that checks for support
|
75
|
+
# of the simple registratino protocol:
|
76
|
+
#
|
77
|
+
# service.supports?('http://openid.net/sreg/1.0')
|
78
|
+
#
|
79
|
+
def supports?(url)
|
80
|
+
return @service_types.member?(extension_url)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the OpenID delegate URL. This is the URL on the OpenID server,
|
84
|
+
# For example if example.com delegates to example-server.com/user, then
|
85
|
+
# this will return example-server.com/user
|
86
|
+
def delegate
|
87
|
+
@delegate_url or self.consumer_id
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the OpenID server endpoint URL.
|
91
|
+
def server_url
|
92
|
+
@uri
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns user's URL which resides on the OpenID server. For
|
96
|
+
# example if http://example.com/ delegates to http://example.myopenid.com/,
|
97
|
+
# then http://example.myopenid.com/ will be returned by this method.
|
98
|
+
def server_id
|
99
|
+
self.delegate
|
100
|
+
end
|
101
|
+
|
102
|
+
# The URL the user entered to authenticate. For example, if
|
103
|
+
# http://example.com/ delegates to http://example.myopenid.com/, this
|
104
|
+
# method will return http://example.com/
|
105
|
+
def consumer_id
|
106
|
+
@yadis_url
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# Used for providing an OpenIDService like object
|
112
|
+
# to the OpenID library for 1.X link rel discovery.
|
113
|
+
# See the documentation for OpenID::OpenIDService for more information
|
114
|
+
# on what this object does.
|
115
|
+
class FakeOpenIDServiceEndpoint < OpenIDServiceEndpoint
|
116
|
+
|
117
|
+
def initialize(consumer_id, server_id, server_url)
|
118
|
+
@uri = server_url
|
119
|
+
@delegate = server_id
|
120
|
+
@yadis_url = consumer_id
|
121
|
+
@service_types = ['http://openid.net/signon/1.0']
|
122
|
+
@yadis = nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def delegate
|
126
|
+
@delegate
|
127
|
+
end
|
128
|
+
|
129
|
+
def supports?(url)
|
130
|
+
false
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require "openid/util"
|
2
|
+
|
3
|
+
module OpenID
|
4
|
+
|
5
|
+
# Interface for the abstract Store
|
6
|
+
class Store
|
7
|
+
|
8
|
+
@@AUTH_KEY_LEN = 20
|
9
|
+
|
10
|
+
# Put a Association object into storace
|
11
|
+
def store_association(association)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a Association object from storage that matches
|
16
|
+
# the server_url. Returns nil if no such association is found or if
|
17
|
+
# the one matching association is expired. (Is allowed to GC expired
|
18
|
+
# associations when found.)
|
19
|
+
def get_association(server_url)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# If there is a matching association, remove it from the store and
|
24
|
+
# return true, otherwise return false.
|
25
|
+
def removeAssociation(server_url, handle)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Stores a nonce (which is passed in as a string).
|
30
|
+
def store_nonce(nonce)
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
# If the nonce is in the store, remove it and return true. Otherwise
|
35
|
+
# return false.
|
36
|
+
def use_nonce(nonce)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a 20-byte auth key used to sign the tokens, to ensure
|
41
|
+
# that they haven't been tampered with in transit. It must return
|
42
|
+
# the same key every time it is called.
|
43
|
+
def get_auth_key
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Method return true if the store is dumb-mode-style store.
|
48
|
+
def dumb?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class DumbStore < Store
|
56
|
+
|
57
|
+
def initialize(secret_phrase)
|
58
|
+
require "digest/sha1"
|
59
|
+
@auth_key = Digest::SHA1.hexdigest(secret_phrase)
|
60
|
+
end
|
61
|
+
|
62
|
+
def store_association(assoc)
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_association(server_url)
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def remove_association(server_url, handle)
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def store_nonce(nonce)
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def use_nonce(nonce)
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_auth_key
|
83
|
+
@auth_key
|
84
|
+
end
|
85
|
+
|
86
|
+
def dumb?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
class ServerAssocs
|
93
|
+
def initialize
|
94
|
+
@assocs = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def set(assoc)
|
98
|
+
@assocs[assoc.handle] = assoc
|
99
|
+
end
|
100
|
+
|
101
|
+
def get(handle)
|
102
|
+
@assocs[handle]
|
103
|
+
end
|
104
|
+
|
105
|
+
def remove(handle)
|
106
|
+
return @assocs.delete(handle)
|
107
|
+
end
|
108
|
+
|
109
|
+
def best
|
110
|
+
best = nil
|
111
|
+
@assocs.each do |k, assoc|
|
112
|
+
if best.nil? or best.issued < assoc.issued
|
113
|
+
best = assoc
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return best
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# An in-memory implementation of Store. This class is mainly used
|
121
|
+
# for testing, though it may be useful for long-running single process apps.
|
122
|
+
#
|
123
|
+
# You should probably be looking at OpenID::FilesystemStore
|
124
|
+
class MemoryStore < Store
|
125
|
+
|
126
|
+
def initialize
|
127
|
+
@server_assocs = {}
|
128
|
+
@nonces = {}
|
129
|
+
@auth_key = OpenID::Util.random_string(@@AUTH_KEY_LEN)
|
130
|
+
end
|
131
|
+
|
132
|
+
def dumb?
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
def store_association(server_url, assoc)
|
137
|
+
assocs = _get_server_assocs(server_url)
|
138
|
+
assocs.set(self.deepcopy(assoc))
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_association(server_url, handle=nil)
|
142
|
+
assocs = _get_server_assocs(server_url)
|
143
|
+
return assocs.best if handle.nil?
|
144
|
+
return assocs.get(handle)
|
145
|
+
end
|
146
|
+
|
147
|
+
def remove_association(server_url, handle)
|
148
|
+
assocs = _get_server_assocs(server_url)
|
149
|
+
return assocs.remove(handle)
|
150
|
+
end
|
151
|
+
|
152
|
+
def use_nonce(nonce)
|
153
|
+
return true if @nonces.delete(nonce)
|
154
|
+
return false
|
155
|
+
end
|
156
|
+
|
157
|
+
def store_nonce(nonce)
|
158
|
+
@nonces[nonce] = true
|
159
|
+
end
|
160
|
+
|
161
|
+
def get_auth_key
|
162
|
+
@auth_key
|
163
|
+
end
|
164
|
+
|
165
|
+
def _get_server_assocs(server_url)
|
166
|
+
unless @server_assocs.has_key?(server_url)
|
167
|
+
@server_assocs[server_url] = ServerAssocs.new
|
168
|
+
end
|
169
|
+
return @server_assocs[server_url]
|
170
|
+
end
|
171
|
+
|
172
|
+
def deepcopy(o)
|
173
|
+
Marshal.load(Marshal.dump(o))
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
TOP_LEVEL_DOMAINS = 'com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw'.split('|')
|
4
|
+
|
5
|
+
module OpenID
|
6
|
+
|
7
|
+
class TrustRoot
|
8
|
+
|
9
|
+
@@empty_re = Regexp.new('^http[s]*:\/\/\*\/$')
|
10
|
+
|
11
|
+
def TrustRoot._parse_url(url)
|
12
|
+
begin
|
13
|
+
parsed = URI::parse(url)
|
14
|
+
rescue
|
15
|
+
return nil
|
16
|
+
end
|
17
|
+
|
18
|
+
return [parsed.scheme, parsed.host, parsed.port, parsed.path]
|
19
|
+
end
|
20
|
+
|
21
|
+
def TrustRoot.parse(trust_root)
|
22
|
+
return nil unless trust_root.instance_of?(String)
|
23
|
+
|
24
|
+
unparsed = trust_root.dup
|
25
|
+
|
26
|
+
# look for wildcard
|
27
|
+
wildcard = (not trust_root.index('://*.').nil?)
|
28
|
+
trust_root.sub!('*.', '') if wildcard
|
29
|
+
|
30
|
+
# handle http://*/ case
|
31
|
+
if not wildcard and @@empty_re.match(trust_root)
|
32
|
+
proto = trust_root.split(':')[0]
|
33
|
+
port = proto == 'http' ? 80 : 443
|
34
|
+
return new(unparsed, proto, true, '', port, '/')
|
35
|
+
end
|
36
|
+
|
37
|
+
parts = TrustRoot._parse_url(trust_root)
|
38
|
+
return nil if parts.nil?
|
39
|
+
|
40
|
+
proto, host, port, path = parts
|
41
|
+
|
42
|
+
return nil unless ['http', 'https'].member?(proto)
|
43
|
+
return new(unparsed, proto, wildcard, host, port, path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def TrustRoot.check_sanity(trust_root)
|
47
|
+
tr = TrustRoot.parse(trust_root)
|
48
|
+
return false if tr.nil?
|
49
|
+
return tr.sane?
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def initialize(unparsed, proto, wildcard, host, port, path)
|
54
|
+
@unparsed = unparsed
|
55
|
+
@proto = proto
|
56
|
+
@wildcard = wildcard
|
57
|
+
@host = host
|
58
|
+
@port = port
|
59
|
+
@path = path
|
60
|
+
end
|
61
|
+
|
62
|
+
def sane?
|
63
|
+
return true if @host == 'localhost'
|
64
|
+
|
65
|
+
host_parts = @host.split('.')
|
66
|
+
return false unless TOP_LEVEL_DOMAINS.member?(host_parts[-1])
|
67
|
+
|
68
|
+
# wacky heurestic for extracting a sane tld
|
69
|
+
host = []
|
70
|
+
if host_parts[-1].length == 2 and host_parts.length > 1
|
71
|
+
if host_parts[-2].length <= 3
|
72
|
+
host = host_parts[0...-2]
|
73
|
+
end
|
74
|
+
elsif host_parts[-1].length == 3
|
75
|
+
host = host_parts[0...-1]
|
76
|
+
end
|
77
|
+
|
78
|
+
return (host.length > 0)
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_url(url)
|
82
|
+
parts = TrustRoot._parse_url(url)
|
83
|
+
return false if parts.nil?
|
84
|
+
|
85
|
+
proto, host, port, path = parts
|
86
|
+
|
87
|
+
return false unless proto == @proto
|
88
|
+
return false unless port == @port
|
89
|
+
return false unless path.index(@path) == 0
|
90
|
+
|
91
|
+
if @wildcard
|
92
|
+
return (not host.rindex(@host).nil?)
|
93
|
+
else
|
94
|
+
return (host == @host)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
data/lib/openid/util.rb
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "cgi"
|
3
|
+
require "digest/sha1"
|
4
|
+
require "hmac-sha1"
|
5
|
+
require "uri"
|
6
|
+
|
7
|
+
srand(Time.now.to_f)
|
8
|
+
|
9
|
+
class Object
|
10
|
+
|
11
|
+
def instance_variable_hash
|
12
|
+
h = {}
|
13
|
+
self.instance_variables.each { |k| h[k] = self.instance_variable_get(k) }
|
14
|
+
return h
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class String
|
20
|
+
|
21
|
+
def starts_with?(other)
|
22
|
+
other = other.to_str
|
23
|
+
head = self[0, other.length]
|
24
|
+
head == other
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
module OpenID
|
31
|
+
|
32
|
+
# Code returned when either the of the
|
33
|
+
# OpenID::OpenIDConsumer.begin_auth or OpenID::OpenIDConsumer.complete_auth
|
34
|
+
# methods return successfully.
|
35
|
+
SUCCESS = 'success'
|
36
|
+
|
37
|
+
# Code OpenID::OpenIDConsumer.complete_auth
|
38
|
+
# returns when the value it received indicated an invalid login.
|
39
|
+
FAILURE = 'failure'
|
40
|
+
|
41
|
+
# Code returned by OpenIDConsumer.complete_auth when the user
|
42
|
+
# cancels the operation from the server.
|
43
|
+
CANCEL = 'cancel'
|
44
|
+
|
45
|
+
# Code returned by OpenID::OpenIDConsumer.complete_auth when the
|
46
|
+
# OpenIDConsumer instance is in immediate mode and ther server sends back a
|
47
|
+
# URL for the user to login with.
|
48
|
+
SETUP_NEEDED = 'setup needed'
|
49
|
+
|
50
|
+
# Code returned by OpenID::OpenIDConsumer.begin_auth when it is unable
|
51
|
+
# to fetch the URL given by the user.
|
52
|
+
HTTP_FAILURE = 'http failure'
|
53
|
+
|
54
|
+
# Code returned by OpenID::OpenIDConsumer.begin_auth when the page fetched
|
55
|
+
# from the OpenID URL doesn't contain the necessary link tags to function
|
56
|
+
# as an identity page.
|
57
|
+
PARSE_ERROR = 'parse error'
|
58
|
+
|
59
|
+
module Util
|
60
|
+
|
61
|
+
HAS_URANDOM = File.chardev? '/dev/urandom'
|
62
|
+
|
63
|
+
def Util.get_openid_params(query)
|
64
|
+
params = {}
|
65
|
+
query.each do |k,v|
|
66
|
+
params[k] = v if k.index("openid.") == 0
|
67
|
+
end
|
68
|
+
params
|
69
|
+
end
|
70
|
+
|
71
|
+
def Util.hmac_sha1(key, text)
|
72
|
+
HMAC::SHA1.digest(key, text)
|
73
|
+
end
|
74
|
+
|
75
|
+
def Util.sha1(s)
|
76
|
+
Digest::SHA1.digest(s)
|
77
|
+
end
|
78
|
+
|
79
|
+
def Util.to_base64(s)
|
80
|
+
Base64.encode64(s).gsub("\n", "")
|
81
|
+
end
|
82
|
+
|
83
|
+
def Util.from_base64(s)
|
84
|
+
Base64.decode64(s)
|
85
|
+
end
|
86
|
+
|
87
|
+
def Util.kvform(hash)
|
88
|
+
form = ""
|
89
|
+
hash.each do |k,v|
|
90
|
+
form << "#{k}:#{v}\n"
|
91
|
+
end
|
92
|
+
form
|
93
|
+
end
|
94
|
+
|
95
|
+
def Util.parsekv(s)
|
96
|
+
s.strip!
|
97
|
+
form = {}
|
98
|
+
s.split("\n").each do |line|
|
99
|
+
pair = line.split(":", 2)
|
100
|
+
if pair.length == 2
|
101
|
+
k, v = pair
|
102
|
+
form[k.strip] = v.strip
|
103
|
+
end
|
104
|
+
end
|
105
|
+
form
|
106
|
+
end
|
107
|
+
|
108
|
+
def Util.num_to_str(n)
|
109
|
+
bits = n.to_s(2)
|
110
|
+
prepend = (8 - bits.length % 8)
|
111
|
+
bits = ('0' * prepend) + bits
|
112
|
+
[bits].pack('B*')
|
113
|
+
end
|
114
|
+
|
115
|
+
def Util.str_to_num(s)
|
116
|
+
# taken from openid-ruby 0.0.1
|
117
|
+
s = "\000" * (4 - (s.length % 4)) + s
|
118
|
+
num = 0
|
119
|
+
s.unpack('N*').each do |x|
|
120
|
+
num <<= 32
|
121
|
+
num |= x
|
122
|
+
end
|
123
|
+
num
|
124
|
+
end
|
125
|
+
|
126
|
+
def Util.num_to_base64(l)
|
127
|
+
return to_base64(num_to_str(l))
|
128
|
+
end
|
129
|
+
|
130
|
+
def Util.base64_to_num(s)
|
131
|
+
return str_to_num(from_base64(s))
|
132
|
+
end
|
133
|
+
|
134
|
+
def Util.random_string(length, chars=nil)
|
135
|
+
s = ""
|
136
|
+
|
137
|
+
unless chars.nil?
|
138
|
+
length.times { s << chars[Util.rand(chars.length)] }
|
139
|
+
else
|
140
|
+
length.times { s << Util.rand(256).chr }
|
141
|
+
end
|
142
|
+
s
|
143
|
+
end
|
144
|
+
|
145
|
+
def Util.urlencode(args)
|
146
|
+
a = []
|
147
|
+
args.each do |key, val|
|
148
|
+
a << (CGI::escape(key) + "=" + CGI::escape(val))
|
149
|
+
end
|
150
|
+
a.join("&")
|
151
|
+
end
|
152
|
+
|
153
|
+
def Util.parse_query(qs)
|
154
|
+
query = {}
|
155
|
+
CGI::parse(qs).each {|k,v| query[k] = v[0]}
|
156
|
+
return query
|
157
|
+
end
|
158
|
+
|
159
|
+
def Util.append_args(url, args)
|
160
|
+
url = url.dup
|
161
|
+
url if args.length == 0
|
162
|
+
url << (url.include?("?") ? "&" : "?")
|
163
|
+
url << Util.urlencode(args)
|
164
|
+
end
|
165
|
+
|
166
|
+
def Util.strxor(s1, s2)
|
167
|
+
raise ArgumentError if s1.length != s2.length
|
168
|
+
length = [s1.length, s2.length].min - 1
|
169
|
+
a = (0..length).collect {|i| (s1[i]^s2[i]).chr}
|
170
|
+
a.join("")
|
171
|
+
end
|
172
|
+
|
173
|
+
# Sign the given fields from the reply with the specified key.
|
174
|
+
# Return [signed, sig]
|
175
|
+
def Util.sign_reply(reply, key, signed_fields, prefix="openid.")
|
176
|
+
token = []
|
177
|
+
signed_fields.each do |sf|
|
178
|
+
token << [sf+":"+reply[prefix+sf].to_s+"\n"]
|
179
|
+
end
|
180
|
+
text = token.join("")
|
181
|
+
signed = Util.to_base64(Util.hmac_sha1(key, text))
|
182
|
+
return [signed_fields.join(","), signed]
|
183
|
+
end
|
184
|
+
|
185
|
+
# This code is taken from this post[http://blade.nagaokaut.ac.jp/cgi-bin/scat.\rb/ruby/ruby-talk/19098]
|
186
|
+
# by Eric Lee Green.
|
187
|
+
# This implementation is much faster than x ** n % q
|
188
|
+
def Util.powermod(x, n, q)
|
189
|
+
counter=0
|
190
|
+
n_p=n
|
191
|
+
y_p=1
|
192
|
+
z_p=x
|
193
|
+
while n_p != 0
|
194
|
+
if n_p[0]==1
|
195
|
+
y_p=(y_p*z_p) % q
|
196
|
+
end
|
197
|
+
n_p = n_p >> 1
|
198
|
+
z_p = (z_p * z_p) % q
|
199
|
+
counter += 1
|
200
|
+
end
|
201
|
+
return y_p
|
202
|
+
end
|
203
|
+
|
204
|
+
# Generate a random number less than max. Uses urandom if available.
|
205
|
+
def Util.rand(max)
|
206
|
+
unless Util::HAS_URANDOM
|
207
|
+
return Kernel::rand(max)
|
208
|
+
end
|
209
|
+
|
210
|
+
start = 0
|
211
|
+
stop = max
|
212
|
+
step = 1
|
213
|
+
r = ((stop-start)/step).to_i
|
214
|
+
|
215
|
+
# figure out how many bytes we need
|
216
|
+
rbytes = Util::num_to_str(r)
|
217
|
+
nbytes = rbytes.length
|
218
|
+
nbytes -= 1 if rbytes[0].chr == "\000"
|
219
|
+
|
220
|
+
bytes = "\000" + Util::get_random_bytes(nbytes)
|
221
|
+
n = Util::str_to_num(bytes)
|
222
|
+
|
223
|
+
return start + (n % r) * step
|
224
|
+
end
|
225
|
+
|
226
|
+
# change the message below to do whatever you like for logging
|
227
|
+
def Util.log(message)
|
228
|
+
STDERR.puts('OpenID Log: ' + message)
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
def Util.get_random_bytes(n)
|
233
|
+
bytes = ""
|
234
|
+
|
235
|
+
if Util::HAS_URANDOM
|
236
|
+
f = File.open("/dev/urandom")
|
237
|
+
while n != 0
|
238
|
+
_bytes = f.read(n)
|
239
|
+
n -= _bytes.length
|
240
|
+
bytes << _bytes
|
241
|
+
end
|
242
|
+
else
|
243
|
+
bytes = Util.random_string(n)
|
244
|
+
end
|
245
|
+
|
246
|
+
return bytes
|
247
|
+
end
|
248
|
+
|
249
|
+
def Util.normalize_url(url)
|
250
|
+
url = url.strip
|
251
|
+
|
252
|
+
unless url.starts_with?('http://') or url.starts_with?('https://')
|
253
|
+
url = 'http://' + url
|
254
|
+
end
|
255
|
+
|
256
|
+
begin
|
257
|
+
parsed = URI.parse(url)
|
258
|
+
rescue URI::InvalidURIError
|
259
|
+
return nil
|
260
|
+
else
|
261
|
+
return parsed.normalize.to_s
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def Util.urls_equal?(url1, url2)
|
266
|
+
url1 = Util.normalize_url(url1)
|
267
|
+
return false if url1.nil?
|
268
|
+
return url1 == Util.normalize_url(url2)
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|