nov-ruby-openid 2.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +215 -0
- data/CHANGES-2.1.0 +36 -0
- data/INSTALL +47 -0
- data/LICENSE +210 -0
- data/NOTICE +2 -0
- data/README +81 -0
- data/Rakefile +98 -0
- data/UPGRADE +127 -0
- data/VERSION +1 -0
- data/contrib/google/ruby-openid-apps-discovery-1.0.gem +0 -0
- data/contrib/google/ruby-openid-apps-discovery-1.01.gem +0 -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 +122 -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/.htaccess +40 -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 +20 -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 +148 -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/extensions/ui.rb +53 -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 +8 -0
- data/lib/openid/server.rb +1544 -0
- data/lib/openid/store/filesystem.rb +271 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memcache.rb +107 -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 +110 -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 +99 -0
- data/setup.rb +1551 -0
- data/test/data/accept.txt +124 -0
- data/test/data/dh.txt +29 -0
- data/test/data/example-xrds.xml +14 -0
- data/test/data/linkparse.txt +587 -0
- data/test/data/n2b64 +650 -0
- data/test/data/test1-discover.txt +137 -0
- data/test/data/test1-parsehtml.txt +152 -0
- data/test/data/test_discover/malformed_meta_tag.html +19 -0
- data/test/data/test_discover/openid.html +11 -0
- data/test/data/test_discover/openid2.html +11 -0
- data/test/data/test_discover/openid2_xrds.xml +12 -0
- data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
- data/test/data/test_discover/openid_1_and_2.html +11 -0
- data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
- data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
- data/test/data/test_discover/openid_and_yadis.html +12 -0
- data/test/data/test_discover/openid_no_delegate.html +10 -0
- data/test/data/test_discover/openid_utf8.html +11 -0
- data/test/data/test_discover/yadis_0entries.xml +12 -0
- data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
- data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
- data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
- data/test/data/test_discover/yadis_another_delegate.xml +14 -0
- data/test/data/test_discover/yadis_idp.xml +12 -0
- data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
- data/test/data/test_discover/yadis_no_delegate.xml +11 -0
- data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
- data/test/data/test_xrds/README +12 -0
- data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
- data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
- data/test/data/test_xrds/delegated-20060809.xrds +34 -0
- data/test/data/test_xrds/no-xrd.xml +7 -0
- data/test/data/test_xrds/not-xrds.xml +2 -0
- data/test/data/test_xrds/prefixsometimes.xrds +34 -0
- data/test/data/test_xrds/ref.xrds +109 -0
- data/test/data/test_xrds/sometimesprefix.xrds +34 -0
- data/test/data/test_xrds/spoof1.xrds +25 -0
- data/test/data/test_xrds/spoof2.xrds +25 -0
- data/test/data/test_xrds/spoof3.xrds +37 -0
- data/test/data/test_xrds/status222.xrds +9 -0
- data/test/data/test_xrds/subsegments.xrds +58 -0
- data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/data/trustroot.txt +153 -0
- data/test/data/urinorm.txt +79 -0
- data/test/discoverdata.rb +131 -0
- data/test/test_accept.rb +170 -0
- data/test/test_association.rb +266 -0
- data/test/test_associationmanager.rb +917 -0
- data/test/test_ax.rb +690 -0
- data/test/test_checkid_request.rb +294 -0
- data/test/test_consumer.rb +257 -0
- data/test/test_cryptutil.rb +119 -0
- data/test/test_dh.rb +86 -0
- data/test/test_discover.rb +852 -0
- data/test/test_discovery_manager.rb +262 -0
- data/test/test_extension.rb +46 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +565 -0
- data/test/test_filters.rb +270 -0
- data/test/test_idres.rb +963 -0
- data/test/test_kvform.rb +165 -0
- data/test/test_kvpost.rb +65 -0
- data/test/test_linkparse.rb +101 -0
- data/test/test_message.rb +1116 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_oauth.rb +175 -0
- data/test/test_openid_yadis.rb +178 -0
- data/test/test_pape.rb +247 -0
- data/test/test_parsehtml.rb +80 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2457 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +298 -0
- data/test/test_trustroot.rb +113 -0
- data/test/test_ui.rb +93 -0
- data/test/test_urinorm.rb +35 -0
- data/test/test_util.rb +145 -0
- data/test/test_xrds.rb +169 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +63 -0
- data/test/test_yadis_discovery.rb +220 -0
- data/test/testutil.rb +127 -0
- data/test/util.rb +53 -0
- metadata +336 -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,20 @@
|
|
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
|
+
module OpenID
|
16
|
+
VERSION = "2.1.8"
|
17
|
+
end
|
18
|
+
|
19
|
+
require "openid/consumer"
|
20
|
+
require 'openid/server'
|
@@ -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
|