note description: "Summary description for {OPENID_CONSUMER_VALIDATION}." author: "" date: "$Date$" revision: "$Revision$" class OPENID_CONSUMER_VALIDATION create make_from_items feature {NONE} -- Initialization make_from_items (o: OPENID_CONSUMER; lst: like values) do openid := o values := lst return_url := o.return_url create attributes.make (0) end values: detachable ITERABLE [TUPLE [name: READABLE_STRING_32; value: detachable READABLE_STRING_32]] return_url: READABLE_STRING_8 feature -- Access openid: OPENID_CONSUMER identity: detachable READABLE_STRING_8 attributes: HASH_TABLE [READABLE_STRING_32, STRING_8] feature -- Access: attributes email_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("contact/email") end nickname_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("namePerson/friendly") end fullname_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("namePerson") end gender_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("person/gender") end postcode_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("contact/postalCode/home") end country_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("contact/country/home") end language_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("pref/language") end timezone_attribute: detachable READABLE_STRING_32 do Result := attributes.item ("pref/timezone") end feature -- Basic operation validate -- Is openid identifier validated? local l_claimed_id: detachable READABLE_STRING_8 tb: HASH_TABLE [detachable READABLE_STRING_32, STRING_8] ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT ret: URI sess: HTTP_CLIENT_SESSION do is_valid := False create ret.make_from_string (return_url) create tb.make (5) if attached values as q_lst then if attached item_by_name ("openid.claimed_id", q_lst) as q_claimed_id and then q_claimed_id.is_valid_as_string_8 then l_claimed_id := q_claimed_id.to_string_8 elseif attached item_by_name ("openid.identity", q_lst) as l_id and then l_id.is_valid_as_string_8 then l_claimed_id := l_id.to_string_8 end identity := l_claimed_id tb.force (item_by_name ("openid.assoc_handle", q_lst), "openid.assoc_handle") tb.force (item_by_name ("openid.signed", q_lst), "openid.signed") tb.force (item_by_name ("openid.sig", q_lst), "openid.sig") if attached item_by_name ("openid.ns", q_lst) as q_ns then -- We're dealing with an OpenID 2.0 server, so let's set an ns -- Even though we should know location of the endpoint, -- we still need to verify it by discovery, so $server is not set here tb.force ("http://specs.openid.net/auth/2.0", "openid.ns") elseif attached item_by_name ("openid.claimed_id", q_lst) as q_claimed_id and then (not attached item_by_name ("openid.identity", q_lst) as l_identity or else not q_claimed_id.same_string (l_identity)) then -- If it's an OpenID 1 provider, and we've got claimed_id, -- we have to append it to the returnUrl, like authUrl_v1 does. ret.add_query_parameter ("openid.claimed_id", q_claimed_id) return_url := ret.string else end if attached item_by_name ("openid.return_to", q_lst) as q_return_to and then not return_url.same_string_general (q_return_to) then -- The return_to url must match the url of current request. -- I'm assuing that noone will set the returnUrl to something that doesn't make sense. -- False, FIXME, exception ... end if l_claimed_id /= Void then if attached openid.discovering_info (l_claimed_id) as d_info and then not openid.has_error and then not d_info.has_error then if attached item_by_name ("openid.signed", q_lst) as lst_signed then across lst_signed.split (',') as c loop tb.force (item_by_name ("openid." + c.item, q_lst), "openid." + c.item) end end tb.force ("check_authentication", "openid.mode") create ctx.make across tb as c loop if attached c.item as l_value then ctx.add_form_parameter (c.key.to_string_32, l_value) end end sess := openid.new_session (d_info.server_uri) if attached sess.post ("", ctx, Void) as res then if res.error_occurred then elseif attached {STRING} res.body as l_body then is_valid := l_body.substring_index ("is_valid:true", 1) > 0 if is_valid then get_attributes (q_lst) end end end end end end end get_attributes (lst: like values) do attributes.wipe_out get_sreg_attributes (lst) get_ax_attributes (lst) end get_sreg_attributes (lst: like values) local s: READABLE_STRING_32 sreg_keys: ARRAYED_LIST [READABLE_STRING_32] do if lst /= Void and then attached item_by_name ("openid.signed", lst) as l_signed then -- sreg attributes create sreg_keys.make (5) across l_signed.split (',') as c loop s := c.item if s.starts_with ("sreg.") then sreg_keys.force ("openid." + s) end end across sreg_keys as c loop s := c.item if attached item_by_name (s, lst) as v then attributes.force (v, s.substring (5, s.count)) end end end end get_ax_attributes (lst: like values) local s: READABLE_STRING_32 ax_keys: ARRAYED_LIST [READABLE_STRING_32] l_alias: detachable READABLE_STRING_8 k_value, k_type, k_count: READABLE_STRING_8 k: STRING_8 i: INTEGER do if lst /= Void and then attached item_by_name ("openid.signed", lst) as l_signed then -- ax attributes across l_signed.split (',') as c loop i := i + 1 s := c.item if s.starts_with ({STRING_32} "ns.") then if attached item_by_name ({STRING_32} "openid." + s, lst) as v then if s.same_string ({STRING_32} "ns.ax") and v.same_string ({STRING_32} "http://openid.net/srv/ax/1.0") then l_alias := "ax." else if v.same_string_general ("http://openid.net/srv/ax/1.0") then l_alias := s.substring (("ns.").count + 1, s.count).to_string_8 + "." end end end end end if l_alias /= Void then k_value := l_alias + "value." k_type := l_alias + "type." k_count := l_alias + "count." create ax_keys.make (i) across l_signed.split (',') as c loop s := c.item if s.starts_with_general (k_value) or s.starts_with_general (k_type) then ax_keys.force ("openid." + s) end end k_value := "openid." + k_value k_type := "openid." + k_type k_count := "openid." + k_count across ax_keys as c loop s := c.item if attached item_by_name (s, lst) as v then if s.starts_with_general (k_value) then k := s.substring (k_value.count + 1, s.count) i := k.index_of ('.', 1) if i > 1 then k.keep_head (i - 1) end if attached item_by_name (k_type + k, lst) as l_type then if l_type.starts_with_general ("http://axschema.org/") then check ("http://axschema.org/").count = 20 end attributes.force (v, l_type.substring (21, l_type.count)) elseif l_type.starts_with_general ("http://schema.openid.net/") then check ("http://schema.openid.net/").count = 25 end attributes.force (v, l_type.substring (26, l_type.count)) else -- unsupported schema domain. end else -- no alias !!! end -- attributes.force (v, k) end end end end end end item_by_name (a_name: READABLE_STRING_GENERAL; lst: like values): detachable READABLE_STRING_32 local l_found: BOOLEAN do if lst /= Void then across lst as c until l_found loop if a_name.same_string (c.item.name) then Result := c.item.value l_found := True end end end end feature -- Access is_valid: BOOLEAN end