require 'minitest/autorun' require 'openid/extensions/ax' require 'openid/message' require 'openid/consumer/responses' require 'openid/consumer/discovery' require 'openid/consumer/checkid_request' module OpenID module AX class BogusAXMessage < AXMessage @mode = 'bogus' def get_extension_args new_args end def do_check_mode(args) check_mode(args) end def do_check_mode_new_args check_mode(new_args) end end class AXMessageTest < Minitest::Test def setup @bax = BogusAXMessage.new end def test_check_mode assert_raises(Error) { @bax.do_check_mode({'mode' => 'fetch_request'})} @bax.do_check_mode({'mode' => @bax.mode}) end def test_check_mode_new_args @bax.do_check_mode_new_args end end class AttrInfoTest < Minitest::Test def test_construct assert_raises(ArgumentError) { AttrInfo.new } type_uri = 'uri geller' ainfo = AttrInfo.new(type_uri) assert_equal(type_uri, ainfo.type_uri) assert_equal(1, ainfo.count) assert_equal(false, ainfo.required) assert_equal(nil, ainfo.ns_alias) end end class ToTypeURIsTest < Minitest::Test def setup @aliases = NamespaceMap.new end def test_empty [nil, ''].each{|empty| uris = AX.to_type_uris(@aliases, empty) assert_equal([], uris) } end def test_undefined assert_raises(IndexError) { AX.to_type_uris(@aliases, 'http://janrain.com/') } end def test_one uri = 'http://janrain.com/' name = 'openid_hackers' @aliases.add_alias(uri, name) uris = AX::to_type_uris(@aliases, name) assert_equal([uri], uris) end def test_two uri1 = 'http://janrain.com/' name1 = 'openid_hackers' @aliases.add_alias(uri1, name1) uri2 = 'http://jyte.com/' name2 = 'openid_hack' @aliases.add_alias(uri2, name2) uris = AX.to_type_uris(@aliases, [name1, name2].join(',')) assert_equal([uri1, uri2], uris) end end class ParseAXValuesTest < Minitest::Test def ax_values(ax_args, expected_args) msg = KeyValueMessage.new msg.parse_extension_args(ax_args) assert_equal(expected_args, msg.data) end def ax_error(ax_args, error) msg = KeyValueMessage.new assert_raises(error) { msg.parse_extension_args(ax_args) } end def test_empty_is_valid ax_values({}, {}) end def test_missing_value_for_alias_explodes ax_error({'type.foo'=>'urn:foo'}, IndexError) end def test_count_present_but_not_value ax_error({'type.foo'=>'urn:foo', 'count.foo' => '1'}, IndexError) end def test_invalid_count_value msg = FetchRequest.new assert_raises(Error) { msg.parse_extension_args({'type.foo'=>'urn:foo', 'count.foo' => 'bogus'}) } end def test_request_unlimited_values msg = FetchRequest.new args = {'mode' => 'fetch_request', 'required' => 'foo', 'type.foo' => 'urn:foo', 'count.foo' => UNLIMITED_VALUES } msg.parse_extension_args(args) foo = msg.attributes[0] assert_equal(UNLIMITED_VALUES, foo.count) assert(foo.wants_unlimited_values?) end def test_long_alias # spec says we must support at least 32 character-long aliases name = 'x' * MINIMUM_SUPPORTED_ALIAS_LENGTH msg = KeyValueMessage.new args = { "type.#{name}" => 'urn:foo', "count.#{name}" => '1', "value.#{name}.1" => 'first', } msg.parse_extension_args(args) assert_equal(['first'],msg['urn:foo']) end def test_invalid_alias types = [ KeyValueMessage, FetchRequest ] inputs = [ {'type.a.b'=>'urn:foo', 'count.a.b'=>'1'}, {'type.a,b'=>'urn:foo', 'count.a,b'=>'1'}, ] types.each{|t| inputs.each{|input| msg = t.new assert_raises(Error) {msg.parse_extension_args(input)} } } end def test_count_present_and_is_zero ax_values( {'type.foo'=>'urn:foo', 'count.foo'=>'0', }, {'urn:foo'=>[]} ) end def test_singleton_empty ax_values( {'type.foo'=>'urn:foo', 'value.foo'=>'', }, {'urn:foo'=>[]} ) end def test_double_alias ax_error( {'type.foo'=>'urn:foo', 'value.foo'=>'', 'type.bar'=>'urn:foo', 'value.bar'=>'', }, IndexError ) end def test_double_singleton ax_values( {'type.foo'=>'urn:foo', 'value.foo'=>'', 'type.bar'=>'urn:bar', 'value.bar'=>'', }, {'urn:foo'=>[],'urn:bar'=>[]} ) end def singleton_value ax_values( {'type.foo'=>'urn:foo', 'value.foo'=>'something', }, {'urn:foo'=>['something']} ) end end class FetchRequestTest < Minitest::Test def setup @msg = FetchRequest.new @type_a = 'http://janrain.example.com/a' @name_a = 'a' end def test_mode assert_equal('fetch_request', @msg.mode) end def test_construct assert_equal({}, @msg.requested_attributes) assert_equal(nil, @msg.update_url) msg = FetchRequest.new('hailstorm') assert_equal({}, msg.requested_attributes) assert_equal('hailstorm', msg.update_url) end def test_add uri = 'mud://puddle' assert(! @msg.member?(uri)) a = AttrInfo.new(uri) @msg.add(a) assert(@msg.member?(uri)) end def test_add_twice uri = 'its://raining' a = AttrInfo.new(uri) @msg.add(a) assert_raises(IndexError) {@msg.add(a)} end def do_extension_args(expected_args) expected_args['mode'] = @msg.mode assert_equal(expected_args, @msg.get_extension_args) end def test_get_extension_args_empty do_extension_args({}) end def test_get_extension_args_no_alias a = AttrInfo.new('foo://bar') @msg.add(a) ax_args = @msg.get_extension_args ax_args.each{|k,v| if v == a.type_uri and k.index('type.') == 0 @name = k[5..-1] break end } do_extension_args({'type.'+@name => a.type_uri, 'if_available' => @name}) end def test_get_extension_args_alias_if_available a = AttrInfo.new('type://of.transportation', 'transport') @msg.add(a) do_extension_args({'type.'+a.ns_alias => a.type_uri, 'if_available' => a.ns_alias}) end def test_get_extension_args_alias_req a = AttrInfo.new('type://of.transportation', 'transport', true) @msg.add(a) do_extension_args({'type.'+a.ns_alias => a.type_uri, 'required' => a.ns_alias}) end def test_get_required_attrs_empty assert_equal([], @msg.get_required_attrs) end def test_parse_extension_args_extra_type args = { 'mode' => 'fetch_request', 'type.' + @name_a => @type_a } assert_raises(Error) {@msg.parse_extension_args(args)} end def test_parse_extension_args args = { 'mode' => 'fetch_request', 'type.' + @name_a => @type_a, 'if_available' => @name_a } @msg.parse_extension_args(args) assert(@msg.member?(@type_a) ) assert_equal([@type_a], @msg.requested_types) ai = @msg.requested_attributes[@type_a] assert(ai.is_a?(AttrInfo)) assert(!ai.required) assert_equal(@type_a, ai.type_uri) assert_equal(@name_a, ai.ns_alias) assert_equal([ai], @msg.attributes) end def test_extension_args_idempotent args = { 'mode' => 'fetch_request', 'type.' + @name_a => @type_a, 'if_available' => @name_a } @msg.parse_extension_args(args) assert_equal(args, @msg.get_extension_args) assert(!@msg.requested_attributes[@type_a].required) end def test_extension_args_idempotent_count_required args = { 'mode' => 'fetch_request', 'type.' + @name_a => @type_a, 'count.' + @name_a => '2', 'required' => @name_a } @msg.parse_extension_args(args) assert_equal(args, @msg.get_extension_args) assert(@msg.requested_attributes[@type_a].required) end def test_extension_args_count1 args = { 'mode' => 'fetch_request', 'type.' + @name_a => @type_a, 'count.' + @name_a => '1', 'if_available' => @name_a } norm_args = { 'mode' => 'fetch_request', 'type.' + @name_a => @type_a, 'if_available' => @name_a } @msg.parse_extension_args(args) assert_equal(norm_args, @msg.get_extension_args) end def test_from_openid_request_no_ax message = Message.new openid_req = Server::OpenIDRequest.new openid_req.message = message ax_req = FetchRequest.from_openid_request(openid_req) assert(ax_req.nil?) end def test_from_openid_request_wrong_ax_mode uri = 'http://under.the.sea/' name = 'ext0' value = 'snarfblat' message = OpenID::Message.from_openid_args({ 'mode' => 'id_res', 'ns' => OPENID2_NS, 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'store_request', 'ax.type.' + name => uri, 'ax.count.' + name => '1', 'ax.value.' + name + '.1' => value }) openid_req = Server::OpenIDRequest.new openid_req.message = message ax_req = FetchRequest.from_openid_request(openid_req) assert(ax_req.nil?) end def test_openid_update_url_verification_error openid_req_msg = Message.from_openid_args({ 'mode' => 'checkid_setup', 'ns' => OPENID2_NS, 'realm' => 'http://example.com/realm', 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://different.site/path', 'ax.mode' => 'fetch_request', }) openid_req = Server::OpenIDRequest.new openid_req.message = openid_req_msg assert_raises(Error) { FetchRequest.from_openid_request(openid_req) } end def test_openid_no_realm openid_req_msg = Message.from_openid_args({ 'mode' => 'checkid_setup', 'ns' => OPENID2_NS, 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://different.site/path', 'ax.mode' => 'fetch_request', }) openid_req = Server::OpenIDRequest.new openid_req.message = openid_req_msg assert_raises(Error) { FetchRequest.from_openid_request(openid_req) } end def test_openid_update_url_verification_success openid_req_msg = Message.from_openid_args({ 'mode' => 'checkid_setup', 'ns' => OPENID2_NS, 'realm' => 'http://example.com/realm', 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'fetch_request', }) openid_req = Server::OpenIDRequest.new openid_req.message = openid_req_msg fr = FetchRequest.from_openid_request(openid_req) assert(fr.is_a?(FetchRequest)) end def test_openid_update_url_verification_success_return_to openid_req_msg = Message.from_openid_args({ 'mode' => 'checkid_setup', 'ns' => OPENID2_NS, 'return_to' => 'http://example.com/realm', 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'fetch_request', }) openid_req = Server::OpenIDRequest.new openid_req.message = openid_req_msg fr = FetchRequest.from_openid_request(openid_req) assert(fr.is_a?(FetchRequest)) end def test_add_extension openid_req_msg = Message.from_openid_args({ 'mode' => 'checkid_setup', 'ns' => OPENID2_NS, 'return_to' => 'http://example.com/realm', }) e = OpenID::OpenIDServiceEndpoint.new openid_req = Consumer::CheckIDRequest.new(nil, e) openid_req.message = openid_req_msg fr = FetchRequest.new fr.add(AttrInfo.new("urn:bogus")) openid_req.add_extension(fr) expected = { 'mode' => 'fetch_request', 'if_available' => 'ext0', 'type.ext0' => 'urn:bogus', } expected.each { |k,v| assert(openid_req.message.get_arg(AXMessage::NS_URI, k) == v) } end end class FetchResponseTest < Minitest::Test def setup @msg = FetchResponse.new @value_a = 'commodity' @value_a1 = 'value2' @type_a = 'http://blood.transfusion/' @name_a = 'george' @request_update_url = 'http://some.url.that.is.awesome/' end def test_construct assert_equal(nil, @msg.update_url) assert_equal({}, @msg.data) end def test_get_extension_args_empty eargs = { 'mode' => 'fetch_response' } assert_equal(eargs, @msg.get_extension_args) end def test_get_extension_args_empty_request eargs = { 'mode' => 'fetch_response' } req = FetchRequest.new assert_equal(eargs, @msg.get_extension_args(req)) end def test_get_extension_args_empty_request_some uri = 'http://not.found/' name = 'ext0' eargs = { 'mode' => 'fetch_response', 'type.' + name => uri, 'count.' + name => '0' } req = FetchRequest.new req.add(AttrInfo.new(uri)) assert_equal(eargs, @msg.get_extension_args(req)) end def test_update_url_in_response uri = 'http://not.found/' name = 'ext0' eargs = { 'mode' => 'fetch_response', 'update_url' => @request_update_url, 'type.' + name => uri, 'count.' + name => '0' } req = FetchRequest.new(@request_update_url) req.add(AttrInfo.new(uri)) assert_equal(eargs, @msg.get_extension_args(req)) end def test_get_extension_args_single_value_response # Single values do NOT have a count, and # do not use the array extension eargs = { 'mode' => 'fetch_response', 'type.' + @name_a => @type_a, 'value.' + @name_a => @value_a } req = FetchRequest.new req.add(AttrInfo.new(@type_a, @name_a)) @msg.add_value(@type_a, @value_a) assert_equal(eargs, @msg.get_extension_args(req)) end def test_get_extension_args_array_value_response # Multiple array values add the count, and array index # to each value eargs = { 'mode' => 'fetch_response', 'type.' + @name_a => @type_a, 'value.' + @name_a + ".1" => @value_a, 'value.' + @name_a + ".2" => @value_a1, 'count.' + @name_a => '2' } req = FetchRequest.new # Specify that this URI should have a count of 2 req.add(AttrInfo.new(@type_a, @name_a, true, 2)) # Push both values onto the array @msg.add_value(@type_a, @value_a) @msg.add_value(@type_a, @value_a1) assert_equal(eargs, @msg.get_extension_args(req)) end def test_get_extension_args_some_not_request req = FetchRequest.new @msg.add_value(@type_a, @value_a) assert_raises(IndexError) {@msg.get_extension_args(req)} end def test_get_single_success @msg.add_value(@type_a, @value_a) assert_equal(@value_a, @msg.get_single(@type_a)) end def test_get_single_none assert_equal(nil, @msg.get_single(@type_a)) end def test_get_single_extra @msg.set_values(@type_a, ['x', 'y']) assert_raises(Error) { @msg.get_single(@type_a) } end def test_from_unsigned_success_response uri = 'http://under.the.sea/' name = 'ext0' value = 'snarfblat' m = OpenID::Message.from_openid_args({ 'mode' => 'id_res', 'ns' => OPENID2_NS, 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'fetch_response', 'ax.type.' + name => uri, 'ax.count.' + name => '1', 'ax.value.' + name + '.1' => value }) e = OpenID::OpenIDServiceEndpoint.new() resp = OpenID::Consumer::SuccessResponse.new(e, m, []) ax_resp = FetchResponse.from_success_response(resp, false) values = ax_resp[uri] assert_equal(values, [value]) end def test_from_signed_success_response uri = 'http://under.the.sea/' name = 'ext0' value = 'snarfblat' oid_fields = { 'mode' => 'id_res', 'ns' => OPENID2_NS, 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'fetch_response', 'ax.type.' + name => uri, 'ax.count.' + name => '1', 'ax.value.' + name + '.1' => value } signed_fields = oid_fields.keys.map{|f| "openid.#{f}"} m = OpenID::Message.from_openid_args(oid_fields) e = OpenID::OpenIDServiceEndpoint.new() resp = OpenID::Consumer::SuccessResponse.new(e, m, signed_fields) ax_resp = FetchResponse.from_success_response(resp, true) values = ax_resp[uri] assert_equal(values, [value]) end def test_from_signed_success_response_with_unsigned_attributes uri = 'http://under.the.sea/' name = 'ext0' value = 'snarfblat' m = OpenID::Message.from_openid_args({ 'mode' => 'id_res', 'ns' => OPENID2_NS, 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'fetch_response', 'ax.type.' + name => uri, 'ax.count.' + name => '1', 'ax.value.' + name + '.1' => value }) e = OpenID::OpenIDServiceEndpoint.new() resp = OpenID::Consumer::SuccessResponse.new(e, m, []) assert_nil FetchResponse.from_success_response(resp, true) end def test_from_empty_success_response e = OpenID::OpenIDServiceEndpoint.new() m = OpenID::Message.from_openid_args({'mode' => 'id_res'}) resp = OpenID::Consumer::SuccessResponse.new(e, m, []) ax_resp = FetchResponse.from_success_response(resp) assert(ax_resp.nil?) end end class StoreRequestTest < Minitest::Test def setup @msg = StoreRequest.new @type_a = 'http://oranges.are.for/' @name_a = 'juggling' end def test_construct assert_equal({}, @msg.data) end def test_get_extension_args_empty eargs = { 'mode' => 'store_request' } assert_equal(eargs, @msg.get_extension_args) end def test_from_openid_request_wrong_ax_mode uri = 'http://under.the.sea/' name = 'ext0' value = 'snarfblat' message = OpenID::Message.from_openid_args({ 'mode' => 'id_res', 'ns' => OPENID2_NS, 'ns.ax' => AXMessage::NS_URI, 'ax.update_url' => 'http://example.com/realm/update_path', 'ax.mode' => 'fetch_request', 'ax.type.' + name => uri, 'ax.count.' + name => '1', 'ax.value.' + name + '.1' => value }) openid_req = Server::OpenIDRequest.new openid_req.message = message ax_req = StoreRequest.from_openid_request(openid_req) assert(ax_req.nil?) end def test_get_extension_args_nonempty @msg.set_values(@type_a, ['foo','bar']) aliases = NamespaceMap.new aliases.add_alias(@type_a, @name_a) eargs = { 'mode' => 'store_request', 'type.' + @name_a => @type_a, 'value.' + @name_a + '.1' => 'foo', 'value.' + @name_a + '.2' => 'bar', 'count.' + @name_a => '2' } assert_equal(eargs, @msg.get_extension_args(aliases)) end end class StoreResponseTest < Minitest::Test def test_success msg = StoreResponse.new assert(msg.succeeded?) assert(!msg.error_message) assert_equal({'mode' => 'store_response_success'}, msg.get_extension_args) end def test_fail_nomsg msg = StoreResponse.new(false) assert(! msg.succeeded? ) assert(! msg.error_message ) assert_equal({'mode' => 'store_response_failure'}, msg.get_extension_args) end def test_fail_msg reason = "because I said so" msg = StoreResponse.new(false, reason) assert(! msg.succeeded? ) assert_equal(reason, msg.error_message) assert_equal({'mode' => 'store_response_failure', 'error' => reason}, msg.get_extension_args) end end end end