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,148 @@
|
|
1
|
+
module OpenID
|
2
|
+
class Consumer
|
3
|
+
# Code returned when either the of the
|
4
|
+
# OpenID::OpenIDConsumer.begin_auth or OpenID::OpenIDConsumer.complete_auth
|
5
|
+
# methods return successfully.
|
6
|
+
SUCCESS = :success
|
7
|
+
|
8
|
+
# Code OpenID::OpenIDConsumer.complete_auth
|
9
|
+
# returns when the value it received indicated an invalid login.
|
10
|
+
FAILURE = :failure
|
11
|
+
|
12
|
+
# Code returned by OpenIDConsumer.complete_auth when the user
|
13
|
+
# cancels the operation from the server.
|
14
|
+
CANCEL = :cancel
|
15
|
+
|
16
|
+
# Code returned by OpenID::OpenIDConsumer.complete_auth when the
|
17
|
+
# OpenIDConsumer instance is in immediate mode and ther server sends back a
|
18
|
+
# URL for the user to login with.
|
19
|
+
SETUP_NEEDED = :setup_needed
|
20
|
+
|
21
|
+
|
22
|
+
module Response
|
23
|
+
attr_reader :endpoint
|
24
|
+
|
25
|
+
def status
|
26
|
+
self.class::STATUS
|
27
|
+
end
|
28
|
+
|
29
|
+
# The identity URL that has been authenticated; the Claimed Identifier.
|
30
|
+
# See also display_identifier.
|
31
|
+
def identity_url
|
32
|
+
@endpoint ? @endpoint.claimed_id : nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# The display identifier is related to the Claimed Identifier, but the
|
36
|
+
# two are not always identical. The display identifier is something the
|
37
|
+
# user should recognize as what they entered, whereas the response's
|
38
|
+
# claimed identifier (in the identity_url attribute) may have extra
|
39
|
+
# information for better persistence.
|
40
|
+
#
|
41
|
+
# URLs will be stripped of their fragments for display. XRIs will
|
42
|
+
# display the human-readable identifier (i-name) instead of the
|
43
|
+
# persistent identifier (i-number).
|
44
|
+
#
|
45
|
+
# Use the display identifier in your user interface. Use identity_url
|
46
|
+
# for querying your database or authorization server, or other
|
47
|
+
# identifier equality comparisons.
|
48
|
+
def display_identifier
|
49
|
+
@endpoint ? @endpoint.display_identifier : nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# A successful acknowledgement from the OpenID server that the
|
54
|
+
# supplied URL is, indeed controlled by the requesting agent.
|
55
|
+
class SuccessResponse
|
56
|
+
include Response
|
57
|
+
|
58
|
+
STATUS = SUCCESS
|
59
|
+
|
60
|
+
attr_reader :message, :signed_fields
|
61
|
+
|
62
|
+
def initialize(endpoint, message, signed_fields)
|
63
|
+
# Don't use :endpoint=, because endpoint should never be nil
|
64
|
+
# for a successfull transaction.
|
65
|
+
@endpoint = endpoint
|
66
|
+
@identity_url = endpoint.claimed_id
|
67
|
+
@message = message
|
68
|
+
@signed_fields = signed_fields
|
69
|
+
end
|
70
|
+
|
71
|
+
# Was this authentication response an OpenID 1 authentication
|
72
|
+
# response?
|
73
|
+
def is_openid1
|
74
|
+
@message.is_openid1
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return whether a particular key is signed, regardless of its
|
78
|
+
# namespace alias
|
79
|
+
def signed?(ns_uri, ns_key)
|
80
|
+
@signed_fields.member?(@message.get_key(ns_uri, ns_key))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return the specified signed field if available, otherwise
|
84
|
+
# return default
|
85
|
+
def get_signed(ns_uri, ns_key, default=nil)
|
86
|
+
if signed?(ns_uri, ns_key)
|
87
|
+
return @message.get_arg(ns_uri, ns_key, default)
|
88
|
+
else
|
89
|
+
return default
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get signed arguments from the response message. Return a dict
|
94
|
+
# of all arguments in the specified namespace. If any of the
|
95
|
+
# arguments are not signed, return nil.
|
96
|
+
def get_signed_ns(ns_uri)
|
97
|
+
msg_args = @message.get_args(ns_uri)
|
98
|
+
msg_args.each_key do |key|
|
99
|
+
if !signed?(ns_uri, key)
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
return msg_args
|
104
|
+
end
|
105
|
+
|
106
|
+
# Return response arguments in the specified namespace.
|
107
|
+
# If require_signed is true and the arguments are not signed,
|
108
|
+
# return nil.
|
109
|
+
def extension_response(namespace_uri, require_signed)
|
110
|
+
if require_signed
|
111
|
+
get_signed_ns(namespace_uri)
|
112
|
+
else
|
113
|
+
@message.get_args(namespace_uri)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class FailureResponse
|
119
|
+
include Response
|
120
|
+
STATUS = FAILURE
|
121
|
+
|
122
|
+
attr_reader :message, :contact, :reference
|
123
|
+
def initialize(endpoint, message, contact=nil, reference=nil)
|
124
|
+
@endpoint = endpoint
|
125
|
+
@message = message
|
126
|
+
@contact = contact
|
127
|
+
@reference = reference
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class CancelResponse
|
132
|
+
include Response
|
133
|
+
STATUS = CANCEL
|
134
|
+
def initialize(endpoint)
|
135
|
+
@endpoint = endpoint
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class SetupNeededResponse
|
140
|
+
include Response
|
141
|
+
STATUS = SETUP_NEEDED
|
142
|
+
def initialize(endpoint, setup_url)
|
143
|
+
@endpoint = endpoint
|
144
|
+
@setup_url = setup_url
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "openid/util"
|
2
|
+
require "digest/sha1"
|
3
|
+
require "digest/sha2"
|
4
|
+
begin
|
5
|
+
require "digest/hmac"
|
6
|
+
rescue LoadError
|
7
|
+
begin
|
8
|
+
# Try loading the ruby-hmac files if they exist
|
9
|
+
require "hmac-sha1"
|
10
|
+
require "hmac-sha2"
|
11
|
+
rescue LoadError
|
12
|
+
# Nothing exists use included hmac files
|
13
|
+
require "hmac/sha1"
|
14
|
+
require "hmac/sha2"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module OpenID
|
19
|
+
# This module contains everything needed to perform low-level
|
20
|
+
# cryptograph and data manipulation tasks.
|
21
|
+
module CryptUtil
|
22
|
+
|
23
|
+
# Generate a random number, doing a little extra work to make it
|
24
|
+
# more likely that it's suitable for cryptography. If your system
|
25
|
+
# doesn't have /dev/urandom then this number is not
|
26
|
+
# cryptographically safe. See
|
27
|
+
# <http://www.cosine.org/2007/08/07/security-ruby-kernel-rand/>
|
28
|
+
# for more information. max is the largest possible value of such
|
29
|
+
# a random number, where the result will be less than max.
|
30
|
+
def CryptUtil.rand(max)
|
31
|
+
Kernel.srand()
|
32
|
+
return Kernel.rand(max)
|
33
|
+
end
|
34
|
+
|
35
|
+
def CryptUtil.sha1(text)
|
36
|
+
return Digest::SHA1.digest(text)
|
37
|
+
end
|
38
|
+
|
39
|
+
def CryptUtil.hmac_sha1(key, text)
|
40
|
+
if Digest.const_defined? :HMAC
|
41
|
+
Digest::HMAC.new(key,Digest::SHA1).update(text).digest
|
42
|
+
else
|
43
|
+
return HMAC::SHA1.digest(key, text)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def CryptUtil.sha256(text)
|
48
|
+
return Digest::SHA256.digest(text)
|
49
|
+
end
|
50
|
+
|
51
|
+
def CryptUtil.hmac_sha256(key, text)
|
52
|
+
if Digest.const_defined? :HMAC
|
53
|
+
Digest::HMAC.new(key,Digest::SHA256).update(text).digest
|
54
|
+
else
|
55
|
+
return HMAC::SHA256.digest(key, text)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generate a random string of the given length, composed of the
|
60
|
+
# specified characters. If chars is nil, generate a string
|
61
|
+
# composed of characters in the range 0..255.
|
62
|
+
def CryptUtil.random_string(length, chars=nil)
|
63
|
+
s = ""
|
64
|
+
|
65
|
+
unless chars.nil?
|
66
|
+
length.times { s << chars[rand(chars.length)] }
|
67
|
+
else
|
68
|
+
length.times { s << rand(256).chr }
|
69
|
+
end
|
70
|
+
return s
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert a number to its binary representation; return a string
|
74
|
+
# of bytes.
|
75
|
+
def CryptUtil.num_to_binary(n)
|
76
|
+
bits = n.to_s(2)
|
77
|
+
prepend = (8 - bits.length % 8)
|
78
|
+
bits = ('0' * prepend) + bits
|
79
|
+
return [bits].pack('B*')
|
80
|
+
end
|
81
|
+
|
82
|
+
# Convert a string of bytes into a number.
|
83
|
+
def CryptUtil.binary_to_num(s)
|
84
|
+
# taken from openid-ruby 0.0.1
|
85
|
+
s = "\000" * (4 - (s.length % 4)) + s
|
86
|
+
num = 0
|
87
|
+
s.unpack('N*').each do |x|
|
88
|
+
num <<= 32
|
89
|
+
num |= x
|
90
|
+
end
|
91
|
+
return num
|
92
|
+
end
|
93
|
+
|
94
|
+
# Encode a number as a base64-encoded byte string.
|
95
|
+
def CryptUtil.num_to_base64(l)
|
96
|
+
return OpenID::Util.to_base64(num_to_binary(l))
|
97
|
+
end
|
98
|
+
|
99
|
+
# Decode a base64 byte string to a number.
|
100
|
+
def CryptUtil.base64_to_num(s)
|
101
|
+
return binary_to_num(OpenID::Util.from_base64(s))
|
102
|
+
end
|
103
|
+
|
104
|
+
def CryptUtil.const_eq(s1, s2)
|
105
|
+
if s1.length != s2.length
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
result = true
|
109
|
+
s1.length.times do |i|
|
110
|
+
result &= (s1[i] == s2[i])
|
111
|
+
end
|
112
|
+
return result
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/openid/dh.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require "openid/util"
|
2
|
+
require "openid/cryptutil"
|
3
|
+
|
4
|
+
module OpenID
|
5
|
+
|
6
|
+
# Encapsulates a Diffie-Hellman key exchange. This class is used
|
7
|
+
# internally by both the consumer and server objects.
|
8
|
+
#
|
9
|
+
# Read more about Diffie-Hellman on wikipedia:
|
10
|
+
# http://en.wikipedia.org/wiki/Diffie-Hellman
|
11
|
+
|
12
|
+
class DiffieHellman
|
13
|
+
|
14
|
+
# From the OpenID specification
|
15
|
+
@@default_mod = 155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443
|
16
|
+
@@default_gen = 2
|
17
|
+
|
18
|
+
attr_reader :modulus, :generator, :public
|
19
|
+
|
20
|
+
# A new DiffieHellman object, using the modulus and generator from
|
21
|
+
# the OpenID specification
|
22
|
+
def DiffieHellman.from_defaults
|
23
|
+
DiffieHellman.new(@@default_mod, @@default_gen)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(modulus=nil, generator=nil, priv=nil)
|
27
|
+
@modulus = modulus.nil? ? @@default_mod : modulus
|
28
|
+
@generator = generator.nil? ? @@default_gen : generator
|
29
|
+
set_private(priv.nil? ? OpenID::CryptUtil.rand(@modulus-2) + 1 : priv)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_shared_secret(composite)
|
33
|
+
DiffieHellman.powermod(composite, @private, @modulus)
|
34
|
+
end
|
35
|
+
|
36
|
+
def xor_secret(algorithm, composite, secret)
|
37
|
+
dh_shared = get_shared_secret(composite)
|
38
|
+
packed_dh_shared = OpenID::CryptUtil.num_to_binary(dh_shared)
|
39
|
+
hashed_dh_shared = algorithm.call(packed_dh_shared)
|
40
|
+
return DiffieHellman.strxor(secret, hashed_dh_shared)
|
41
|
+
end
|
42
|
+
|
43
|
+
def using_default_values?
|
44
|
+
@generator == @@default_gen && @modulus == @@default_mod
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def set_private(priv)
|
49
|
+
@private = priv
|
50
|
+
@public = DiffieHellman.powermod(@generator, @private, @modulus)
|
51
|
+
end
|
52
|
+
|
53
|
+
def DiffieHellman.strxor(s, t)
|
54
|
+
if s.length != t.length
|
55
|
+
raise ArgumentError, "strxor: lengths don't match. " +
|
56
|
+
"Inputs were #{s.inspect} and #{t.inspect}"
|
57
|
+
end
|
58
|
+
|
59
|
+
if String.method_defined? :bytes
|
60
|
+
s.bytes.zip(t.bytes).map{|sb,tb| sb^tb}.pack('C*')
|
61
|
+
else
|
62
|
+
indices = 0...(s.length)
|
63
|
+
chrs = indices.collect {|i| (s[i]^t[i]).chr}
|
64
|
+
chrs.join("")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# This code is taken from this post:
|
69
|
+
# <http://blade.nagaokaut.ac.jp/cgi-bin/scat.\rb/ruby/ruby-talk/19098>
|
70
|
+
# by Eric Lee Green.
|
71
|
+
def DiffieHellman.powermod(x, n, q)
|
72
|
+
counter=0
|
73
|
+
n_p=n
|
74
|
+
y_p=1
|
75
|
+
z_p=x
|
76
|
+
while n_p != 0
|
77
|
+
if n_p[0]==1
|
78
|
+
y_p=(y_p*z_p) % q
|
79
|
+
end
|
80
|
+
n_p = n_p >> 1
|
81
|
+
z_p = (z_p * z_p) % q
|
82
|
+
counter += 1
|
83
|
+
end
|
84
|
+
return y_p
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'openid/message'
|
2
|
+
|
3
|
+
module OpenID
|
4
|
+
# An interface for OpenID extensions.
|
5
|
+
class Extension < Object
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@ns_uri = nil
|
9
|
+
@ns_alias = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get the string arguments that should be added to an OpenID
|
13
|
+
# message for this extension.
|
14
|
+
def get_extension_args
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add the arguments from this extension to the provided
|
19
|
+
# message, or create a new message containing only those
|
20
|
+
# arguments. Returns the message with added extension args.
|
21
|
+
def to_message(message = nil)
|
22
|
+
if message.nil?
|
23
|
+
# warnings.warn('Passing None to Extension.toMessage is deprecated. '
|
24
|
+
# 'Creating a message assuming you want OpenID 2.',
|
25
|
+
# DeprecationWarning, stacklevel=2)
|
26
|
+
Message.new(OPENID2_NS)
|
27
|
+
end
|
28
|
+
message = Message.new if message.nil?
|
29
|
+
|
30
|
+
implicit = message.is_openid1()
|
31
|
+
|
32
|
+
message.namespaces.add_alias(@ns_uri, @ns_alias, implicit)
|
33
|
+
# XXX python ignores keyerror if m.ns.getAlias(uri) == alias
|
34
|
+
|
35
|
+
message.update_args(@ns_uri, get_extension_args)
|
36
|
+
return message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,539 @@
|
|
1
|
+
# Implements the OpenID attribute exchange specification, version 1.0
|
2
|
+
|
3
|
+
require 'openid/extension'
|
4
|
+
require 'openid/trustroot'
|
5
|
+
require 'openid/message'
|
6
|
+
|
7
|
+
module OpenID
|
8
|
+
module AX
|
9
|
+
|
10
|
+
UNLIMITED_VALUES = "unlimited"
|
11
|
+
MINIMUM_SUPPORTED_ALIAS_LENGTH = 32
|
12
|
+
|
13
|
+
# check alias for invalid characters, raise AXError if found
|
14
|
+
def self.check_alias(name)
|
15
|
+
if name.match(/(,|\.)/)
|
16
|
+
raise Error, ("Alias #{name.inspect} must not contain a "\
|
17
|
+
"comma or period.")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Raised when data does not comply with AX 1.0 specification
|
22
|
+
class Error < ArgumentError
|
23
|
+
end
|
24
|
+
|
25
|
+
# Abstract class containing common code for attribute exchange messages
|
26
|
+
class AXMessage < Extension
|
27
|
+
attr_accessor :ns_alias, :mode, :ns_uri
|
28
|
+
|
29
|
+
NS_URI = 'http://openid.net/srv/ax/1.0'
|
30
|
+
def initialize
|
31
|
+
@ns_alias = 'ax'
|
32
|
+
@ns_uri = NS_URI
|
33
|
+
@mode = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
# Raise an exception if the mode in the attribute exchange
|
39
|
+
# arguments does not match what is expected for this class.
|
40
|
+
def check_mode(ax_args)
|
41
|
+
actual_mode = ax_args['mode']
|
42
|
+
if actual_mode != @mode
|
43
|
+
raise Error, "Expected mode #{mode.inspect}, got #{actual_mode.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def new_args
|
48
|
+
{'mode' => @mode}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Represents a single attribute in an attribute exchange
|
53
|
+
# request. This should be added to an Request object in order to
|
54
|
+
# request the attribute.
|
55
|
+
#
|
56
|
+
# @ivar required: Whether the attribute will be marked as required
|
57
|
+
# when presented to the subject of the attribute exchange
|
58
|
+
# request.
|
59
|
+
# @type required: bool
|
60
|
+
#
|
61
|
+
# @ivar count: How many values of this type to request from the
|
62
|
+
# subject. Defaults to one.
|
63
|
+
# @type count: int
|
64
|
+
#
|
65
|
+
# @ivar type_uri: The identifier that determines what the attribute
|
66
|
+
# represents and how it is serialized. For example, one type URI
|
67
|
+
# representing dates could represent a Unix timestamp in base 10
|
68
|
+
# and another could represent a human-readable string.
|
69
|
+
# @type type_uri: str
|
70
|
+
#
|
71
|
+
# @ivar ns_alias: The name that should be given to this alias in the
|
72
|
+
# request. If it is not supplied, a generic name will be
|
73
|
+
# assigned. For example, if you want to call a Unix timestamp
|
74
|
+
# value 'tstamp', set its alias to that value. If two attributes
|
75
|
+
# in the same message request to use the same alias, the request
|
76
|
+
# will fail to be generated.
|
77
|
+
# @type alias: str or NoneType
|
78
|
+
class AttrInfo < Object
|
79
|
+
attr_reader :type_uri, :count, :ns_alias
|
80
|
+
attr_accessor :required
|
81
|
+
def initialize(type_uri, ns_alias=nil, required=false, count=1)
|
82
|
+
@type_uri = type_uri
|
83
|
+
@count = count
|
84
|
+
@required = required
|
85
|
+
@ns_alias = ns_alias
|
86
|
+
end
|
87
|
+
|
88
|
+
def wants_unlimited_values?
|
89
|
+
@count == UNLIMITED_VALUES
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Given a namespace mapping and a string containing a
|
94
|
+
# comma-separated list of namespace aliases, return a list of type
|
95
|
+
# URIs that correspond to those aliases.
|
96
|
+
# namespace_map: OpenID::NamespaceMap
|
97
|
+
def self.to_type_uris(namespace_map, alias_list_s)
|
98
|
+
return [] if alias_list_s.nil?
|
99
|
+
alias_list_s.split(',').inject([]) {|uris, name|
|
100
|
+
type_uri = namespace_map.get_namespace_uri(name)
|
101
|
+
raise IndexError, "No type defined for attribute name #{name.inspect}" if type_uri.nil?
|
102
|
+
uris << type_uri
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# An attribute exchange 'fetch_request' message. This message is
|
108
|
+
# sent by a relying party when it wishes to obtain attributes about
|
109
|
+
# the subject of an OpenID authentication request.
|
110
|
+
class FetchRequest < AXMessage
|
111
|
+
attr_reader :requested_attributes
|
112
|
+
attr_accessor :update_url
|
113
|
+
|
114
|
+
MODE = 'fetch_request'
|
115
|
+
|
116
|
+
def initialize(update_url = nil)
|
117
|
+
super()
|
118
|
+
@mode = MODE
|
119
|
+
@requested_attributes = {}
|
120
|
+
@update_url = update_url
|
121
|
+
end
|
122
|
+
|
123
|
+
# Add an attribute to this attribute exchange request.
|
124
|
+
# attribute: AttrInfo, the attribute being requested
|
125
|
+
# Raises IndexError if the requested attribute is already present
|
126
|
+
# in this request.
|
127
|
+
def add(attribute)
|
128
|
+
if @requested_attributes[attribute.type_uri]
|
129
|
+
raise IndexError, "The attribute #{attribute.type_uri} has already been requested"
|
130
|
+
end
|
131
|
+
@requested_attributes[attribute.type_uri] = attribute
|
132
|
+
end
|
133
|
+
|
134
|
+
# Get the serialized form of this attribute fetch request.
|
135
|
+
# returns a hash of the arguments
|
136
|
+
def get_extension_args
|
137
|
+
aliases = NamespaceMap.new
|
138
|
+
required = []
|
139
|
+
if_available = []
|
140
|
+
ax_args = new_args
|
141
|
+
@requested_attributes.each{|type_uri, attribute|
|
142
|
+
if attribute.ns_alias
|
143
|
+
name = aliases.add_alias(type_uri, attribute.ns_alias)
|
144
|
+
else
|
145
|
+
name = aliases.add(type_uri)
|
146
|
+
end
|
147
|
+
if attribute.required
|
148
|
+
required << name
|
149
|
+
else
|
150
|
+
if_available << name
|
151
|
+
end
|
152
|
+
if attribute.count != 1
|
153
|
+
ax_args["count.#{name}"] = attribute.count.to_s
|
154
|
+
end
|
155
|
+
ax_args["type.#{name}"] = type_uri
|
156
|
+
}
|
157
|
+
|
158
|
+
unless required.empty?
|
159
|
+
ax_args['required'] = required.join(',')
|
160
|
+
end
|
161
|
+
unless if_available.empty?
|
162
|
+
ax_args['if_available'] = if_available.join(',')
|
163
|
+
end
|
164
|
+
return ax_args
|
165
|
+
end
|
166
|
+
|
167
|
+
# Get the type URIs for all attributes that have been marked
|
168
|
+
# as required.
|
169
|
+
def get_required_attrs
|
170
|
+
@requested_attributes.inject([]) {|required, (type_uri, attribute)|
|
171
|
+
if attribute.required
|
172
|
+
required << type_uri
|
173
|
+
else
|
174
|
+
required
|
175
|
+
end
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
# Extract a FetchRequest from an OpenID message
|
180
|
+
# message: OpenID::Message
|
181
|
+
# return a FetchRequest or nil if AX arguments are not present
|
182
|
+
def self.from_openid_request(oidreq)
|
183
|
+
message = oidreq.message
|
184
|
+
ax_args = message.get_args(NS_URI)
|
185
|
+
return nil if ax_args == {} or ax_args['mode'] != MODE
|
186
|
+
req = new
|
187
|
+
req.parse_extension_args(ax_args)
|
188
|
+
|
189
|
+
if req.update_url
|
190
|
+
realm = message.get_arg(OPENID_NS, 'realm',
|
191
|
+
message.get_arg(OPENID_NS, 'return_to'))
|
192
|
+
if realm.nil? or realm.empty?
|
193
|
+
raise Error, "Cannot validate update_url #{req.update_url.inspect} against absent realm"
|
194
|
+
end
|
195
|
+
tr = TrustRoot::TrustRoot.parse(realm)
|
196
|
+
unless tr.validate_url(req.update_url)
|
197
|
+
raise Error, "Update URL #{req.update_url.inspect} failed validation against realm #{realm.inspect}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
return req
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_extension_args(ax_args)
|
205
|
+
check_mode(ax_args)
|
206
|
+
|
207
|
+
aliases = NamespaceMap.new
|
208
|
+
|
209
|
+
ax_args.each{|k,v|
|
210
|
+
if k.index('type.') == 0
|
211
|
+
name = k[5..-1]
|
212
|
+
type_uri = v
|
213
|
+
aliases.add_alias(type_uri, name)
|
214
|
+
|
215
|
+
count_key = 'count.'+name
|
216
|
+
count_s = ax_args[count_key]
|
217
|
+
count = 1
|
218
|
+
if count_s
|
219
|
+
if count_s == UNLIMITED_VALUES
|
220
|
+
count = count_s
|
221
|
+
else
|
222
|
+
count = count_s.to_i
|
223
|
+
if count <= 0
|
224
|
+
raise Error, "Invalid value for count #{count_key.inspect}: #{count_s.inspect}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
add(AttrInfo.new(type_uri, name, false, count))
|
229
|
+
end
|
230
|
+
}
|
231
|
+
|
232
|
+
required = AX.to_type_uris(aliases, ax_args['required'])
|
233
|
+
required.each{|type_uri|
|
234
|
+
@requested_attributes[type_uri].required = true
|
235
|
+
}
|
236
|
+
if_available = AX.to_type_uris(aliases, ax_args['if_available'])
|
237
|
+
all_type_uris = required + if_available
|
238
|
+
|
239
|
+
aliases.namespace_uris.each{|type_uri|
|
240
|
+
unless all_type_uris.member? type_uri
|
241
|
+
raise Error, "Type URI #{type_uri.inspect} was in the request but not present in 'required' or 'if_available'"
|
242
|
+
end
|
243
|
+
}
|
244
|
+
@update_url = ax_args['update_url']
|
245
|
+
end
|
246
|
+
|
247
|
+
# return the list of AttrInfo objects contained in the FetchRequest
|
248
|
+
def attributes
|
249
|
+
@requested_attributes.values
|
250
|
+
end
|
251
|
+
|
252
|
+
# return the list of requested attribute type URIs
|
253
|
+
def requested_types
|
254
|
+
@requested_attributes.keys
|
255
|
+
end
|
256
|
+
|
257
|
+
def member?(type_uri)
|
258
|
+
! @requested_attributes[type_uri].nil?
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
# Abstract class that implements a message that has attribute
|
264
|
+
# keys and values. It contains the common code between
|
265
|
+
# fetch_response and store_request.
|
266
|
+
class KeyValueMessage < AXMessage
|
267
|
+
attr_reader :data
|
268
|
+
def initialize
|
269
|
+
super()
|
270
|
+
@mode = nil
|
271
|
+
@data = {}
|
272
|
+
@data.default = []
|
273
|
+
end
|
274
|
+
|
275
|
+
# Add a single value for the given attribute type to the
|
276
|
+
# message. If there are already values specified for this type,
|
277
|
+
# this value will be sent in addition to the values already
|
278
|
+
# specified.
|
279
|
+
def add_value(type_uri, value)
|
280
|
+
@data[type_uri] = @data[type_uri] << value
|
281
|
+
end
|
282
|
+
|
283
|
+
# Set the values for the given attribute type. This replaces
|
284
|
+
# any values that have already been set for this attribute.
|
285
|
+
def set_values(type_uri, values)
|
286
|
+
@data[type_uri] = values
|
287
|
+
end
|
288
|
+
|
289
|
+
# Get the extension arguments for the key/value pairs
|
290
|
+
# contained in this message.
|
291
|
+
def _get_extension_kv_args(aliases = nil)
|
292
|
+
aliases = NamespaceMap.new if aliases.nil?
|
293
|
+
|
294
|
+
ax_args = new_args
|
295
|
+
|
296
|
+
@data.each{|type_uri, values|
|
297
|
+
name = aliases.add(type_uri)
|
298
|
+
ax_args['type.'+name] = type_uri
|
299
|
+
ax_args['count.'+name] = values.size.to_s
|
300
|
+
|
301
|
+
values.each_with_index{|value, i|
|
302
|
+
key = "value.#{name}.#{i+1}"
|
303
|
+
ax_args[key] = value
|
304
|
+
}
|
305
|
+
}
|
306
|
+
return ax_args
|
307
|
+
end
|
308
|
+
|
309
|
+
# Parse attribute exchange key/value arguments into this object.
|
310
|
+
|
311
|
+
def parse_extension_args(ax_args)
|
312
|
+
check_mode(ax_args)
|
313
|
+
aliases = NamespaceMap.new
|
314
|
+
|
315
|
+
ax_args.each{|k, v|
|
316
|
+
if k.index('type.') == 0
|
317
|
+
type_uri = v
|
318
|
+
name = k[5..-1]
|
319
|
+
|
320
|
+
AX.check_alias(name)
|
321
|
+
aliases.add_alias(type_uri,name)
|
322
|
+
end
|
323
|
+
}
|
324
|
+
|
325
|
+
aliases.each{|type_uri, name|
|
326
|
+
count_s = ax_args['count.'+name]
|
327
|
+
count = count_s.to_i
|
328
|
+
if count_s.nil?
|
329
|
+
value = ax_args['value.'+name]
|
330
|
+
if value.nil?
|
331
|
+
raise IndexError, "Missing #{'value.'+name} in FetchResponse"
|
332
|
+
elsif value.empty?
|
333
|
+
values = []
|
334
|
+
else
|
335
|
+
values = [value]
|
336
|
+
end
|
337
|
+
elsif count_s.to_i == 0
|
338
|
+
values = []
|
339
|
+
else
|
340
|
+
values = (1..count).inject([]){|l,i|
|
341
|
+
key = "value.#{name}.#{i}"
|
342
|
+
v = ax_args[key]
|
343
|
+
raise IndexError, "Missing #{key} in FetchResponse" if v.nil?
|
344
|
+
l << v
|
345
|
+
}
|
346
|
+
end
|
347
|
+
@data[type_uri] = values
|
348
|
+
}
|
349
|
+
end
|
350
|
+
|
351
|
+
# Get a single value for an attribute. If no value was sent
|
352
|
+
# for this attribute, use the supplied default. If there is more
|
353
|
+
# than one value for this attribute, this method will fail.
|
354
|
+
def get_single(type_uri, default = nil)
|
355
|
+
values = @data[type_uri]
|
356
|
+
return default if values.empty?
|
357
|
+
if values.size != 1
|
358
|
+
raise Error, "More than one value present for #{type_uri.inspect}"
|
359
|
+
else
|
360
|
+
return values[0]
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# retrieve the list of values for this attribute
|
365
|
+
def get(type_uri)
|
366
|
+
@data[type_uri]
|
367
|
+
end
|
368
|
+
|
369
|
+
# retrieve the list of values for this attribute
|
370
|
+
def [](type_uri)
|
371
|
+
@data[type_uri]
|
372
|
+
end
|
373
|
+
|
374
|
+
# get the number of responses for this attribute
|
375
|
+
def count(type_uri)
|
376
|
+
@data[type_uri].size
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
|
381
|
+
# A fetch_response attribute exchange message
|
382
|
+
class FetchResponse < KeyValueMessage
|
383
|
+
attr_reader :update_url
|
384
|
+
|
385
|
+
def initialize(update_url = nil)
|
386
|
+
super()
|
387
|
+
@mode = 'fetch_response'
|
388
|
+
@update_url = update_url
|
389
|
+
end
|
390
|
+
|
391
|
+
# Serialize this object into arguments in the attribute
|
392
|
+
# exchange namespace
|
393
|
+
# Takes an optional FetchRequest. If specified, the response will be
|
394
|
+
# validated against this request, and empty responses for requested
|
395
|
+
# fields with no data will be sent.
|
396
|
+
def get_extension_args(request = nil)
|
397
|
+
aliases = NamespaceMap.new
|
398
|
+
zero_value_types = []
|
399
|
+
|
400
|
+
if request
|
401
|
+
# Validate the data in the context of the request (the
|
402
|
+
# same attributes should be present in each, and the
|
403
|
+
# counts in the response must be no more than the counts
|
404
|
+
# in the request)
|
405
|
+
@data.keys.each{|type_uri|
|
406
|
+
unless request.member? type_uri
|
407
|
+
raise IndexError, "Response attribute not present in request: #{type_uri.inspect}"
|
408
|
+
end
|
409
|
+
}
|
410
|
+
|
411
|
+
request.attributes.each{|attr_info|
|
412
|
+
# Copy the aliases from the request so that reading
|
413
|
+
# the response in light of the request is easier
|
414
|
+
if attr_info.ns_alias.nil?
|
415
|
+
aliases.add(attr_info.type_uri)
|
416
|
+
else
|
417
|
+
aliases.add_alias(attr_info.type_uri, attr_info.ns_alias)
|
418
|
+
end
|
419
|
+
values = @data[attr_info.type_uri]
|
420
|
+
if values.empty? # @data defaults to []
|
421
|
+
zero_value_types << attr_info
|
422
|
+
end
|
423
|
+
if attr_info.count != UNLIMITED_VALUES and attr_info.count < values.size
|
424
|
+
raise Error, "More than the number of requested values were specified for #{attr_info.type_uri.inspect}"
|
425
|
+
end
|
426
|
+
}
|
427
|
+
end
|
428
|
+
|
429
|
+
kv_args = _get_extension_kv_args(aliases)
|
430
|
+
|
431
|
+
# Add the KV args into the response with the args that are
|
432
|
+
# unique to the fetch_response
|
433
|
+
ax_args = new_args
|
434
|
+
|
435
|
+
zero_value_types.each{|attr_info|
|
436
|
+
name = aliases.get_alias(attr_info.type_uri)
|
437
|
+
kv_args['type.' + name] = attr_info.type_uri
|
438
|
+
kv_args['count.' + name] = '0'
|
439
|
+
}
|
440
|
+
update_url = (request and request.update_url or @update_url)
|
441
|
+
ax_args['update_url'] = update_url unless update_url.nil?
|
442
|
+
ax_args.update(kv_args)
|
443
|
+
return ax_args
|
444
|
+
end
|
445
|
+
|
446
|
+
def parse_extension_args(ax_args)
|
447
|
+
super
|
448
|
+
@update_url = ax_args['update_url']
|
449
|
+
end
|
450
|
+
|
451
|
+
# Construct a FetchResponse object from an OpenID library
|
452
|
+
# SuccessResponse object.
|
453
|
+
def self.from_success_response(success_response, signed=true)
|
454
|
+
obj = self.new
|
455
|
+
if signed
|
456
|
+
ax_args = success_response.get_signed_ns(obj.ns_uri)
|
457
|
+
else
|
458
|
+
ax_args = success_response.message.get_args(obj.ns_uri)
|
459
|
+
end
|
460
|
+
|
461
|
+
begin
|
462
|
+
obj.parse_extension_args(ax_args)
|
463
|
+
return obj
|
464
|
+
rescue Error => e
|
465
|
+
return nil
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
# A store request attribute exchange message representation
|
471
|
+
class StoreRequest < KeyValueMessage
|
472
|
+
|
473
|
+
MODE = 'store_request'
|
474
|
+
|
475
|
+
def initialize
|
476
|
+
super
|
477
|
+
@mode = MODE
|
478
|
+
end
|
479
|
+
|
480
|
+
# Extract a StoreRequest from an OpenID message
|
481
|
+
# message: OpenID::Message
|
482
|
+
# return a StoreRequest or nil if AX arguments are not present
|
483
|
+
def self.from_openid_request(oidreq)
|
484
|
+
message = oidreq.message
|
485
|
+
ax_args = message.get_args(NS_URI)
|
486
|
+
return nil if ax_args.empty? or ax_args['mode'] != MODE
|
487
|
+
req = new
|
488
|
+
req.parse_extension_args(ax_args)
|
489
|
+
req
|
490
|
+
end
|
491
|
+
|
492
|
+
def get_extension_args(aliases=nil)
|
493
|
+
ax_args = new_args
|
494
|
+
kv_args = _get_extension_kv_args(aliases)
|
495
|
+
ax_args.update(kv_args)
|
496
|
+
return ax_args
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# An indication that the store request was processed along with
|
501
|
+
# this OpenID transaction.
|
502
|
+
class StoreResponse < AXMessage
|
503
|
+
SUCCESS_MODE = 'store_response_success'
|
504
|
+
FAILURE_MODE = 'store_response_failure'
|
505
|
+
attr_reader :error_message
|
506
|
+
|
507
|
+
def initialize(succeeded = true, error_message = nil)
|
508
|
+
super()
|
509
|
+
if succeeded and error_message
|
510
|
+
raise Error, "Error message included in a success response"
|
511
|
+
end
|
512
|
+
if succeeded
|
513
|
+
@mode = SUCCESS_MODE
|
514
|
+
else
|
515
|
+
@mode = FAILURE_MODE
|
516
|
+
end
|
517
|
+
@error_message = error_message
|
518
|
+
end
|
519
|
+
|
520
|
+
def self.from_success_response(success_response)
|
521
|
+
resp = nil
|
522
|
+
ax_args = success_response.message.get_args(NS_URI)
|
523
|
+
resp = ax_args.key?('error') ? new(false, ax_args['error']) : new
|
524
|
+
end
|
525
|
+
|
526
|
+
def succeeded?
|
527
|
+
@mode == SUCCESS_MODE
|
528
|
+
end
|
529
|
+
|
530
|
+
def get_extension_args
|
531
|
+
ax_args = new_args
|
532
|
+
if !succeeded? and error_message
|
533
|
+
ax_args['error'] = @error_message
|
534
|
+
end
|
535
|
+
return ax_args
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|