entp-ruby-openid 2.2
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/CHANGELOG +215 -0
- data/INSTALL +47 -0
- data/LICENSE +210 -0
- data/NOTICE +2 -0
- data/README +85 -0
- data/UPGRADE +127 -0
- data/admin/runtests.rb +45 -0
- data/examples/README +32 -0
- data/examples/active_record_openid_store/README +58 -0
- data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
- data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
- data/examples/active_record_openid_store/init.rb +8 -0
- data/examples/active_record_openid_store/lib/association.rb +10 -0
- data/examples/active_record_openid_store/lib/nonce.rb +3 -0
- data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
- data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
- data/examples/active_record_openid_store/test/store_test.rb +212 -0
- data/examples/discover +49 -0
- data/examples/rails_openid/README +153 -0
- data/examples/rails_openid/Rakefile +10 -0
- data/examples/rails_openid/app/controllers/application.rb +4 -0
- data/examples/rails_openid/app/controllers/consumer_controller.rb +125 -0
- data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
- data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
- data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
- data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
- data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
- data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
- data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
- data/examples/rails_openid/app/views/login/index.rhtml +56 -0
- data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
- data/examples/rails_openid/config/boot.rb +19 -0
- data/examples/rails_openid/config/database.yml +74 -0
- data/examples/rails_openid/config/environment.rb +54 -0
- data/examples/rails_openid/config/environments/development.rb +19 -0
- data/examples/rails_openid/config/environments/production.rb +19 -0
- data/examples/rails_openid/config/environments/test.rb +19 -0
- data/examples/rails_openid/config/routes.rb +24 -0
- data/examples/rails_openid/doc/README_FOR_APP +2 -0
- data/examples/rails_openid/public/404.html +8 -0
- data/examples/rails_openid/public/500.html +8 -0
- data/examples/rails_openid/public/dispatch.cgi +12 -0
- data/examples/rails_openid/public/dispatch.fcgi +26 -0
- data/examples/rails_openid/public/dispatch.rb +12 -0
- data/examples/rails_openid/public/favicon.ico +0 -0
- data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
- data/examples/rails_openid/public/javascripts/controls.js +750 -0
- data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
- data/examples/rails_openid/public/javascripts/effects.js +854 -0
- data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
- data/examples/rails_openid/public/robots.txt +1 -0
- data/examples/rails_openid/script/about +3 -0
- data/examples/rails_openid/script/breakpointer +3 -0
- data/examples/rails_openid/script/console +3 -0
- data/examples/rails_openid/script/destroy +3 -0
- data/examples/rails_openid/script/generate +3 -0
- data/examples/rails_openid/script/performance/benchmarker +3 -0
- data/examples/rails_openid/script/performance/profiler +3 -0
- data/examples/rails_openid/script/plugin +3 -0
- data/examples/rails_openid/script/process/reaper +3 -0
- data/examples/rails_openid/script/process/spawner +3 -0
- data/examples/rails_openid/script/process/spinner +3 -0
- data/examples/rails_openid/script/runner +3 -0
- data/examples/rails_openid/script/server +3 -0
- data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
- data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
- data/examples/rails_openid/test/test_helper.rb +28 -0
- data/lib/hmac/hmac.rb +112 -0
- data/lib/hmac/sha1.rb +11 -0
- data/lib/hmac/sha2.rb +25 -0
- data/lib/openid.rb +22 -0
- data/lib/openid/association.rb +249 -0
- data/lib/openid/consumer.rb +395 -0
- data/lib/openid/consumer/associationmanager.rb +344 -0
- data/lib/openid/consumer/checkid_request.rb +186 -0
- data/lib/openid/consumer/discovery.rb +497 -0
- data/lib/openid/consumer/discovery_manager.rb +123 -0
- data/lib/openid/consumer/html_parse.rb +134 -0
- data/lib/openid/consumer/idres.rb +523 -0
- data/lib/openid/consumer/responses.rb +150 -0
- data/lib/openid/cryptutil.rb +115 -0
- data/lib/openid/dh.rb +89 -0
- data/lib/openid/extension.rb +39 -0
- data/lib/openid/extensions/ax.rb +539 -0
- data/lib/openid/extensions/oauth.rb +91 -0
- data/lib/openid/extensions/pape.rb +179 -0
- data/lib/openid/extensions/sreg.rb +277 -0
- data/lib/openid/extras.rb +11 -0
- data/lib/openid/fetchers.rb +258 -0
- data/lib/openid/kvform.rb +136 -0
- data/lib/openid/kvpost.rb +58 -0
- data/lib/openid/message.rb +553 -0
- data/lib/openid/protocolerror.rb +12 -0
- data/lib/openid/server.rb +1544 -0
- data/lib/openid/store.rb +10 -0
- data/lib/openid/store/filesystem.rb +272 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memcache.rb +109 -0
- data/lib/openid/store/memory.rb +84 -0
- data/lib/openid/store/nonce.rb +68 -0
- data/lib/openid/trustroot.rb +349 -0
- data/lib/openid/urinorm.rb +75 -0
- data/lib/openid/util.rb +119 -0
- data/lib/openid/version.rb +3 -0
- data/lib/openid/yadis.rb +15 -0
- data/lib/openid/yadis/accept.rb +148 -0
- data/lib/openid/yadis/constants.rb +21 -0
- data/lib/openid/yadis/discovery.rb +153 -0
- data/lib/openid/yadis/filters.rb +205 -0
- data/lib/openid/yadis/htmltokenizer.rb +305 -0
- data/lib/openid/yadis/parsehtml.rb +45 -0
- data/lib/openid/yadis/services.rb +42 -0
- data/lib/openid/yadis/xrds.rb +155 -0
- data/lib/openid/yadis/xri.rb +90 -0
- data/lib/openid/yadis/xrires.rb +91 -0
- data/test/data/test_discover/openid_utf8.html +11 -0
- data/test/support/test_data_mixin.rb +127 -0
- data/test/support/test_util.rb +53 -0
- data/test/support/yadis_data.rb +131 -0
- data/test/support/yadis_data/accept.txt +124 -0
- data/test/support/yadis_data/dh.txt +29 -0
- data/test/support/yadis_data/example-xrds.xml +14 -0
- data/test/support/yadis_data/linkparse.txt +587 -0
- data/test/support/yadis_data/n2b64 +650 -0
- data/test/support/yadis_data/test1-discover.txt +137 -0
- data/test/support/yadis_data/test1-parsehtml.txt +152 -0
- data/test/support/yadis_data/test_discover/malformed_meta_tag.html +19 -0
- data/test/support/yadis_data/test_discover/openid.html +11 -0
- data/test/support/yadis_data/test_discover/openid2.html +11 -0
- data/test/support/yadis_data/test_discover/openid2_xrds.xml +12 -0
- data/test/support/yadis_data/test_discover/openid2_xrds_no_local_id.xml +11 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2.html +11 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2_xrds.xml +16 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
- data/test/support/yadis_data/test_discover/openid_and_yadis.html +12 -0
- data/test/support/yadis_data/test_discover/openid_no_delegate.html +10 -0
- data/test/support/yadis_data/test_discover/openid_utf8.html +11 -0
- data/test/support/yadis_data/test_discover/yadis_0entries.xml +12 -0
- data/test/support/yadis_data/test_discover/yadis_2_bad_local_id.xml +15 -0
- data/test/support/yadis_data/test_discover/yadis_2entries_delegate.xml +22 -0
- data/test/support/yadis_data/test_discover/yadis_2entries_idp.xml +21 -0
- data/test/support/yadis_data/test_discover/yadis_another_delegate.xml +14 -0
- data/test/support/yadis_data/test_discover/yadis_idp.xml +12 -0
- data/test/support/yadis_data/test_discover/yadis_idp_delegate.xml +13 -0
- data/test/support/yadis_data/test_discover/yadis_no_delegate.xml +11 -0
- data/test/support/yadis_data/test_xrds/=j3h.2007.11.14.xrds +25 -0
- data/test/support/yadis_data/test_xrds/README +12 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809-r1.xrds +34 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809-r2.xrds +34 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809.xrds +34 -0
- data/test/support/yadis_data/test_xrds/no-xrd.xml +7 -0
- data/test/support/yadis_data/test_xrds/not-xrds.xml +2 -0
- data/test/support/yadis_data/test_xrds/prefixsometimes.xrds +34 -0
- data/test/support/yadis_data/test_xrds/ref.xrds +109 -0
- data/test/support/yadis_data/test_xrds/sometimesprefix.xrds +34 -0
- data/test/support/yadis_data/test_xrds/spoof1.xrds +25 -0
- data/test/support/yadis_data/test_xrds/spoof2.xrds +25 -0
- data/test/support/yadis_data/test_xrds/spoof3.xrds +37 -0
- data/test/support/yadis_data/test_xrds/status222.xrds +9 -0
- data/test/support/yadis_data/test_xrds/subsegments.xrds +58 -0
- data/test/support/yadis_data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/support/yadis_data/trustroot.txt +153 -0
- data/test/support/yadis_data/urinorm.txt +79 -0
- data/test/test_accept.rb +170 -0
- data/test/test_association.rb +268 -0
- data/test/test_associationmanager.rb +918 -0
- data/test/test_ax.rb +690 -0
- data/test/test_checkid_request.rb +293 -0
- data/test/test_consumer.rb +260 -0
- data/test/test_cryptutil.rb +119 -0
- data/test/test_dh.rb +85 -0
- data/test/test_discover.rb +848 -0
- data/test/test_discovery_manager.rb +259 -0
- data/test/test_extension.rb +46 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +554 -0
- data/test/test_filters.rb +269 -0
- data/test/test_helper.rb +4 -0
- data/test/test_idres.rb +961 -0
- data/test/test_kvform.rb +164 -0
- data/test/test_kvpost.rb +64 -0
- data/test/test_linkparse.rb +100 -0
- data/test/test_message.rb +1115 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_oauth.rb +176 -0
- data/test/test_openid_yadis.rb +177 -0
- data/test/test_pape.rb +248 -0
- data/test/test_parsehtml.rb +79 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2455 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +292 -0
- data/test/test_trustroot.rb +111 -0
- data/test/test_urinorm.rb +34 -0
- data/test/test_util.rb +145 -0
- data/test/test_xrds.rb +167 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +67 -0
- data/test/test_yadis_discovery.rb +218 -0
- metadata +268 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
require 'login_controller'
|
|
3
|
+
|
|
4
|
+
# Re-raise errors caught by the controller.
|
|
5
|
+
class LoginController; def rescue_action(e) raise e end; end
|
|
6
|
+
|
|
7
|
+
class LoginControllerTest < Test::Unit::TestCase
|
|
8
|
+
def setup
|
|
9
|
+
@controller = LoginController.new
|
|
10
|
+
@request = ActionController::TestRequest.new
|
|
11
|
+
@response = ActionController::TestResponse.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Replace this with your real tests.
|
|
15
|
+
def test_truth
|
|
16
|
+
assert true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
require 'server_controller'
|
|
3
|
+
|
|
4
|
+
# Re-raise errors caught by the controller.
|
|
5
|
+
class ServerController; def rescue_action(e) raise e end; end
|
|
6
|
+
|
|
7
|
+
class ServerControllerTest < Test::Unit::TestCase
|
|
8
|
+
def setup
|
|
9
|
+
@controller = ServerController.new
|
|
10
|
+
@request = ActionController::TestRequest.new
|
|
11
|
+
@response = ActionController::TestResponse.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Replace this with your real tests.
|
|
15
|
+
def test_truth
|
|
16
|
+
assert true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
|
3
|
+
require 'test_help'
|
|
4
|
+
|
|
5
|
+
class Test::Unit::TestCase
|
|
6
|
+
# Transactional fixtures accelerate your tests by wrapping each test method
|
|
7
|
+
# in a transaction that's rolled back on completion. This ensures that the
|
|
8
|
+
# test database remains unchanged so your fixtures don't have to be reloaded
|
|
9
|
+
# between every test method. Fewer database queries means faster tests.
|
|
10
|
+
#
|
|
11
|
+
# Read Mike Clark's excellent walkthrough at
|
|
12
|
+
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
|
13
|
+
#
|
|
14
|
+
# Every Active Record database supports transactions except MyISAM tables
|
|
15
|
+
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
|
16
|
+
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
|
17
|
+
# is recommended.
|
|
18
|
+
self.use_transactional_fixtures = true
|
|
19
|
+
|
|
20
|
+
# Instantiated fixtures are slow, but give you @david where otherwise you
|
|
21
|
+
# would need people(:david). If you don't want to migrate your existing
|
|
22
|
+
# test cases which use the @david style and don't mind the speed hit (each
|
|
23
|
+
# instantiated fixtures translates to a database query per test method),
|
|
24
|
+
# then set this back to true.
|
|
25
|
+
self.use_instantiated_fixtures = false
|
|
26
|
+
|
|
27
|
+
# Add more helper methods to be used by all tests here...
|
|
28
|
+
end
|
data/lib/hmac/hmac.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Copyright (C) 2001 Daiki Ueno <ueno@unixuser.org>
|
|
2
|
+
# This library is distributed under the terms of the Ruby license.
|
|
3
|
+
|
|
4
|
+
# This module provides common interface to HMAC engines.
|
|
5
|
+
# HMAC standard is documented in RFC 2104:
|
|
6
|
+
#
|
|
7
|
+
# H. Krawczyk et al., "HMAC: Keyed-Hashing for Message Authentication",
|
|
8
|
+
# RFC 2104, February 1997
|
|
9
|
+
#
|
|
10
|
+
# These APIs are inspired by JCE 1.2's javax.crypto.Mac interface.
|
|
11
|
+
#
|
|
12
|
+
# <URL:http://java.sun.com/security/JCE1.2/spec/apidoc/javax/crypto/Mac.html>
|
|
13
|
+
|
|
14
|
+
module HMAC
|
|
15
|
+
class Base
|
|
16
|
+
def initialize(algorithm, block_size, output_length, key)
|
|
17
|
+
@algorithm = algorithm
|
|
18
|
+
@block_size = block_size
|
|
19
|
+
@output_length = output_length
|
|
20
|
+
@status = STATUS_UNDEFINED
|
|
21
|
+
@key_xor_ipad = ''
|
|
22
|
+
@key_xor_opad = ''
|
|
23
|
+
set_key(key) unless key.nil?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
def check_status
|
|
28
|
+
unless @status == STATUS_INITIALIZED
|
|
29
|
+
raise RuntimeError,
|
|
30
|
+
"The underlying hash algorithm has not yet been initialized."
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
public
|
|
35
|
+
def set_key(key)
|
|
36
|
+
# If key is longer than the block size, apply hash function
|
|
37
|
+
# to key and use the result as a real key.
|
|
38
|
+
key = @algorithm.digest(key) if key.size > @block_size
|
|
39
|
+
key_xor_ipad = "\x36" * @block_size
|
|
40
|
+
key_xor_opad = "\x5C" * @block_size
|
|
41
|
+
for i in 0 .. key.size - 1
|
|
42
|
+
key_xor_ipad[i] ^= key[i]
|
|
43
|
+
key_xor_opad[i] ^= key[i]
|
|
44
|
+
end
|
|
45
|
+
@key_xor_ipad = key_xor_ipad
|
|
46
|
+
@key_xor_opad = key_xor_opad
|
|
47
|
+
@md = @algorithm.new
|
|
48
|
+
@status = STATUS_INITIALIZED
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def reset_key
|
|
52
|
+
@key_xor_ipad.gsub!(/./, '?')
|
|
53
|
+
@key_xor_opad.gsub!(/./, '?')
|
|
54
|
+
@key_xor_ipad[0..-1] = ''
|
|
55
|
+
@key_xor_opad[0..-1] = ''
|
|
56
|
+
@status = STATUS_UNDEFINED
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def update(text)
|
|
60
|
+
check_status
|
|
61
|
+
# perform inner H
|
|
62
|
+
md = @algorithm.new
|
|
63
|
+
md.update(@key_xor_ipad)
|
|
64
|
+
md.update(text)
|
|
65
|
+
str = md.digest
|
|
66
|
+
# perform outer H
|
|
67
|
+
md = @algorithm.new
|
|
68
|
+
md.update(@key_xor_opad)
|
|
69
|
+
md.update(str)
|
|
70
|
+
@md = md
|
|
71
|
+
end
|
|
72
|
+
alias << update
|
|
73
|
+
|
|
74
|
+
def digest
|
|
75
|
+
check_status
|
|
76
|
+
@md.digest
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def hexdigest
|
|
80
|
+
check_status
|
|
81
|
+
@md.hexdigest
|
|
82
|
+
end
|
|
83
|
+
alias to_s hexdigest
|
|
84
|
+
|
|
85
|
+
# These two class methods below are safer than using above
|
|
86
|
+
# instance methods combinatorially because an instance will have
|
|
87
|
+
# held a key even if it's no longer in use.
|
|
88
|
+
def Base.digest(key, text)
|
|
89
|
+
begin
|
|
90
|
+
hmac = self.new(key)
|
|
91
|
+
hmac.update(text)
|
|
92
|
+
hmac.digest
|
|
93
|
+
ensure
|
|
94
|
+
hmac.reset_key
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def Base.hexdigest(key, text)
|
|
99
|
+
begin
|
|
100
|
+
hmac = self.new(key)
|
|
101
|
+
hmac.update(text)
|
|
102
|
+
hmac.hexdigest
|
|
103
|
+
ensure
|
|
104
|
+
hmac.reset_key
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private_class_method :new, :digest, :hexdigest
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
STATUS_UNDEFINED, STATUS_INITIALIZED = 0, 1
|
|
112
|
+
end
|
data/lib/hmac/sha1.rb
ADDED
data/lib/hmac/sha2.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'hmac/hmac'
|
|
2
|
+
require 'digest/sha2'
|
|
3
|
+
|
|
4
|
+
module HMAC
|
|
5
|
+
class SHA256 < Base
|
|
6
|
+
def initialize(key = nil)
|
|
7
|
+
super(Digest::SHA256, 64, 32, key)
|
|
8
|
+
end
|
|
9
|
+
public_class_method :new, :digest, :hexdigest
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class SHA384 < Base
|
|
13
|
+
def initialize(key = nil)
|
|
14
|
+
super(Digest::SHA384, 128, 48, key)
|
|
15
|
+
end
|
|
16
|
+
public_class_method :new, :digest, :hexdigest
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class SHA512 < Base
|
|
20
|
+
def initialize(key = nil)
|
|
21
|
+
super(Digest::SHA512, 128, 64, key)
|
|
22
|
+
end
|
|
23
|
+
public_class_method :new, :digest, :hexdigest
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/openid.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright 2006-2007 JanRain, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
|
4
|
+
# may not use this file except in compliance with the License. You may
|
|
5
|
+
# obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
12
|
+
# implied. See the License for the specific language governing
|
|
13
|
+
# permissions and limitations under the License.
|
|
14
|
+
|
|
15
|
+
require "openid/version"
|
|
16
|
+
require 'openid/store'
|
|
17
|
+
require 'openid/yadis'
|
|
18
|
+
require "openid/consumer"
|
|
19
|
+
require 'openid/server'
|
|
20
|
+
|
|
21
|
+
module OpenID
|
|
22
|
+
end
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
require "openid/kvform"
|
|
2
|
+
require "openid/util"
|
|
3
|
+
require "openid/cryptutil"
|
|
4
|
+
require "openid/message"
|
|
5
|
+
|
|
6
|
+
module OpenID
|
|
7
|
+
|
|
8
|
+
def self.get_secret_size(assoc_type)
|
|
9
|
+
if assoc_type == 'HMAC-SHA1'
|
|
10
|
+
return 20
|
|
11
|
+
elsif assoc_type == 'HMAC-SHA256'
|
|
12
|
+
return 32
|
|
13
|
+
else
|
|
14
|
+
raise ArgumentError("Unsupported association type: #{assoc_type}")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# An Association holds the shared secret between a relying party and
|
|
19
|
+
# an OpenID provider.
|
|
20
|
+
class Association
|
|
21
|
+
attr_reader :handle, :secret, :issued, :lifetime, :assoc_type
|
|
22
|
+
|
|
23
|
+
FIELD_ORDER =
|
|
24
|
+
[:version, :handle, :secret, :issued, :lifetime, :assoc_type,]
|
|
25
|
+
|
|
26
|
+
# Load a serialized Association
|
|
27
|
+
def self.deserialize(serialized)
|
|
28
|
+
parsed = Util.kv_to_seq(serialized)
|
|
29
|
+
parsed_fields = parsed.map{|k, v| k.to_sym}
|
|
30
|
+
if parsed_fields != FIELD_ORDER
|
|
31
|
+
raise ProtocolError, 'Unexpected fields in serialized association'\
|
|
32
|
+
" (Expected #{FIELD_ORDER.inspect}, got #{parsed_fields.inspect})"
|
|
33
|
+
end
|
|
34
|
+
version, handle, secret64, issued_s, lifetime_s, assoc_type =
|
|
35
|
+
parsed.map {|field, value| value}
|
|
36
|
+
if version != '2'
|
|
37
|
+
raise ProtocolError, "Attempted to deserialize unsupported version "\
|
|
38
|
+
"(#{parsed[0][1].inspect})"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
self.new(handle,
|
|
42
|
+
Util.from_base64(secret64),
|
|
43
|
+
Time.at(issued_s.to_i),
|
|
44
|
+
lifetime_s.to_i,
|
|
45
|
+
assoc_type)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Create an Association with an issued time of now
|
|
49
|
+
def self.from_expires_in(expires_in, handle, secret, assoc_type)
|
|
50
|
+
issued = Time.now
|
|
51
|
+
self.new(handle, secret, issued, expires_in, assoc_type)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize(handle, secret, issued, lifetime, assoc_type)
|
|
55
|
+
@handle = handle
|
|
56
|
+
@secret = secret
|
|
57
|
+
@issued = issued
|
|
58
|
+
@lifetime = lifetime
|
|
59
|
+
@assoc_type = assoc_type
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Serialize the association to a form that's consistent across
|
|
63
|
+
# JanRain OpenID libraries.
|
|
64
|
+
def serialize
|
|
65
|
+
data = {
|
|
66
|
+
:version => '2',
|
|
67
|
+
:handle => handle,
|
|
68
|
+
:secret => Util.to_base64(secret),
|
|
69
|
+
:issued => issued.to_i.to_s,
|
|
70
|
+
:lifetime => lifetime.to_i.to_s,
|
|
71
|
+
:assoc_type => assoc_type,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Util.assert(data.length == FIELD_ORDER.length)
|
|
75
|
+
|
|
76
|
+
pairs = FIELD_ORDER.map{|field| [field.to_s, data[field]]}
|
|
77
|
+
return Util.seq_to_kv(pairs, strict=true)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The number of seconds until this association expires
|
|
81
|
+
def expires_in(now=nil)
|
|
82
|
+
if now.nil?
|
|
83
|
+
now = Time.now.to_i
|
|
84
|
+
else
|
|
85
|
+
now = now.to_i
|
|
86
|
+
end
|
|
87
|
+
time_diff = (issued.to_i + lifetime) - now
|
|
88
|
+
if time_diff < 0
|
|
89
|
+
return 0
|
|
90
|
+
else
|
|
91
|
+
return time_diff
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Generate a signature for a sequence of [key, value] pairs
|
|
96
|
+
def sign(pairs)
|
|
97
|
+
kv = Util.seq_to_kv(pairs)
|
|
98
|
+
case assoc_type
|
|
99
|
+
when 'HMAC-SHA1'
|
|
100
|
+
CryptUtil.hmac_sha1(@secret, kv)
|
|
101
|
+
when 'HMAC-SHA256'
|
|
102
|
+
CryptUtil.hmac_sha256(@secret, kv)
|
|
103
|
+
else
|
|
104
|
+
raise ProtocolError, "Association has unknown type: "\
|
|
105
|
+
"#{assoc_type.inspect}"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Generate the list of pairs that form the signed elements of the
|
|
110
|
+
# given message
|
|
111
|
+
def make_pairs(message)
|
|
112
|
+
signed = message.get_arg(OPENID_NS, 'signed')
|
|
113
|
+
if signed.nil?
|
|
114
|
+
raise ProtocolError, 'Missing signed list'
|
|
115
|
+
end
|
|
116
|
+
signed_fields = signed.split(',', -1)
|
|
117
|
+
data = message.to_post_args
|
|
118
|
+
signed_fields.map {|field| [field, data.fetch('openid.'+field,'')] }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Return whether the message's signature passes
|
|
122
|
+
def check_message_signature(message)
|
|
123
|
+
message_sig = message.get_arg(OPENID_NS, 'sig')
|
|
124
|
+
if message_sig.nil?
|
|
125
|
+
raise ProtocolError, "#{message} has no sig."
|
|
126
|
+
end
|
|
127
|
+
calculated_sig = get_message_signature(message)
|
|
128
|
+
return CryptUtil.const_eq(calculated_sig, message_sig)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Get the signature for this message
|
|
132
|
+
def get_message_signature(message)
|
|
133
|
+
Util.to_base64(sign(make_pairs(message)))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def ==(other)
|
|
137
|
+
(other.class == self.class and
|
|
138
|
+
other.handle == self.handle and
|
|
139
|
+
other.secret == self.secret and
|
|
140
|
+
|
|
141
|
+
# The internals of the time objects seemed to differ
|
|
142
|
+
# in an opaque way when serializing/unserializing.
|
|
143
|
+
# I don't think this will be a problem.
|
|
144
|
+
other.issued.to_i == self.issued.to_i and
|
|
145
|
+
|
|
146
|
+
other.lifetime == self.lifetime and
|
|
147
|
+
other.assoc_type == self.assoc_type)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Add a signature (and a signed list) to a message.
|
|
151
|
+
def sign_message(message)
|
|
152
|
+
if (message.has_key?(OPENID_NS, 'sig') or
|
|
153
|
+
message.has_key?(OPENID_NS, 'signed'))
|
|
154
|
+
raise ArgumentError, 'Message already has signed list or signature'
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
extant_handle = message.get_arg(OPENID_NS, 'assoc_handle')
|
|
158
|
+
if extant_handle and extant_handle != self.handle
|
|
159
|
+
raise ArgumentError, "Message has a different association handle"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
signed_message = message.copy()
|
|
163
|
+
signed_message.set_arg(OPENID_NS, 'assoc_handle', self.handle)
|
|
164
|
+
message_keys = signed_message.to_post_args.keys()
|
|
165
|
+
|
|
166
|
+
signed_list = []
|
|
167
|
+
message_keys.each { |k|
|
|
168
|
+
if k.starts_with?('openid.')
|
|
169
|
+
signed_list << k[7..-1]
|
|
170
|
+
end
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
signed_list << 'signed'
|
|
174
|
+
signed_list.sort!
|
|
175
|
+
|
|
176
|
+
signed_message.set_arg(OPENID_NS, 'signed', signed_list.join(','))
|
|
177
|
+
sig = get_message_signature(signed_message)
|
|
178
|
+
signed_message.set_arg(OPENID_NS, 'sig', sig)
|
|
179
|
+
return signed_message
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
class AssociationNegotiator
|
|
184
|
+
attr_reader :allowed_types
|
|
185
|
+
|
|
186
|
+
def self.get_session_types(assoc_type)
|
|
187
|
+
case assoc_type
|
|
188
|
+
when 'HMAC-SHA1'
|
|
189
|
+
['DH-SHA1', 'no-encryption']
|
|
190
|
+
when 'HMAC-SHA256'
|
|
191
|
+
['DH-SHA256', 'no-encryption']
|
|
192
|
+
else
|
|
193
|
+
raise ProtocolError, "Unknown association type #{assoc_type.inspect}"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def self.check_session_type(assoc_type, session_type)
|
|
198
|
+
if !get_session_types(assoc_type).include?(session_type)
|
|
199
|
+
raise ProtocolError, "Session type #{session_type.inspect} not "\
|
|
200
|
+
"valid for association type #{assoc_type.inspect}"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def initialize(allowed_types)
|
|
205
|
+
self.allowed_types=(allowed_types)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def copy
|
|
209
|
+
Marshal.load(Marshal.dump(self))
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def allowed_types=(allowed_types)
|
|
213
|
+
allowed_types.each do |assoc_type, session_type|
|
|
214
|
+
self.class.check_session_type(assoc_type, session_type)
|
|
215
|
+
end
|
|
216
|
+
@allowed_types = allowed_types
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def add_allowed_type(assoc_type, session_type=nil)
|
|
220
|
+
if session_type.nil?
|
|
221
|
+
session_types = self.class.get_session_types(assoc_type)
|
|
222
|
+
else
|
|
223
|
+
self.class.check_session_type(assoc_type, session_type)
|
|
224
|
+
session_types = [session_type]
|
|
225
|
+
end
|
|
226
|
+
for session_type in session_types do
|
|
227
|
+
@allowed_types << [assoc_type, session_type]
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def allowed?(assoc_type, session_type)
|
|
232
|
+
@allowed_types.include?([assoc_type, session_type])
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def get_allowed_type
|
|
236
|
+
@allowed_types.empty? ? nil : @allowed_types[0]
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
DefaultNegotiator =
|
|
241
|
+
AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'],
|
|
242
|
+
['HMAC-SHA1', 'no-encryption'],
|
|
243
|
+
['HMAC-SHA256', 'DH-SHA256'],
|
|
244
|
+
['HMAC-SHA256', 'no-encryption']])
|
|
245
|
+
|
|
246
|
+
EncryptedNegotiator =
|
|
247
|
+
AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'],
|
|
248
|
+
['HMAC-SHA256', 'DH-SHA256']])
|
|
249
|
+
end
|