From d4091a57c9271f962374dc2bd556630831bfbafa Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 27 Feb 2013 22:09:06 +0100 Subject: [PATCH] First version of OpenID consumer (light implementation) --- .../openid/consumer/demo/application.e | 148 +++++ .../openid/consumer/demo/demo-safe.ecf | 23 + .../security/openid/consumer/openid-safe.ecf | 20 + library/security/openid/consumer/openid.ecf | 21 + .../openid/consumer/src/openid_consumer.e | 510 ++++++++++++++++++ .../consumer/src/openid_consumer_validation.e | 265 +++++++++ .../openid/consumer/src/openid_discover.e | 59 ++ .../openid/consumer/tests/application.e | 31 ++ .../openid/consumer/tests/test_openid.e | 69 +++ .../openid/consumer/tests/tests-safe.ecf | 19 + 10 files changed, 1165 insertions(+) create mode 100644 library/security/openid/consumer/demo/application.e create mode 100644 library/security/openid/consumer/demo/demo-safe.ecf create mode 100644 library/security/openid/consumer/openid-safe.ecf create mode 100644 library/security/openid/consumer/openid.ecf create mode 100644 library/security/openid/consumer/src/openid_consumer.e create mode 100644 library/security/openid/consumer/src/openid_consumer_validation.e create mode 100644 library/security/openid/consumer/src/openid_discover.e create mode 100644 library/security/openid/consumer/tests/application.e create mode 100644 library/security/openid/consumer/tests/test_openid.e create mode 100644 library/security/openid/consumer/tests/tests-safe.ecf diff --git a/library/security/openid/consumer/demo/application.e b/library/security/openid/consumer/demo/application.e new file mode 100644 index 00000000..d1eca72e --- /dev/null +++ b/library/security/openid/consumer/demo/application.e @@ -0,0 +1,148 @@ +note + description : "OPENID demo application root class" + date : "$Date$" + revision : "$Revision$" + +class + APPLICATION + +inherit + WSF_URI_TEMPLATE_ROUTED_SERVICE + + WSF_SERVICE + +create + make_and_launch + +feature {NONE} -- Initialization + + make_and_launch + local + launcher: WSF_NINO_SERVICE_LAUNCHER + opts: WSF_SERVICE_LAUNCHER_OPTIONS + do + initialize_router + + create opts.make + opts.set_verbose (True) + opts.set_option ("port", 0) + create launcher.make (Current, opts) + launcher.on_launched_actions.extend (agent on_launched) + launcher.launch + end + + on_launched (conn: WGI_CONNECTOR) + local + e: EXECUTION_ENVIRONMENT + do + if attached {WGI_NINO_CONNECTOR} conn as nino then + create e + if attached e.item ("COMSPEC") as l_comspec then + e.launch (l_comspec + " /C start " + "http://localhost:" + nino.port.out + "/") + else + e.launch ("http://localhost:" + nino.port.out + "/") + end + end + end + + setup_router + do + map_uri_template_agent ("/", agent handle_root) + map_uri_template_agent ("/openid", agent handle_openid) + end + + handle_root (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_HTML_PAGE_RESPONSE + s: STRING + do + create m.make + m.set_title ("EWF::OpenID demo") + create s.make_empty + s.append ("
%N") + s.append ("OpenID identifier ") + s.append ("") + s.append ("
%N") + s.append ("
%N") + s.append ("OpenID identifier ") + s.append ("") + s.append ("
%N") + m.set_body (s) + res.send (m) + end + + handle_openid (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_HTML_PAGE_RESPONSE + redir: WSF_HTML_DELAYED_REDIRECTION_RESPONSE + s: STRING + o: OPENID_CONSUMER + v: OPENID_CONSUMER_VALIDATION + do + if attached req.string_item ("openid.mode") as l_openid_mode then + create m.make + m.set_title ("EWF::OpenID demo") + create s.make_empty + + if l_openid_mode.same_string ("id_res") then + o := new_openid_consumer (req) + create v.make_from_string (o, req.absolute_script_url (req.request_uri)) + v.validate + if v.is_valid then + s.append ("
User authenticated
") + s.append ("") + s.append ("") + else + s.append ("
User authentication failed!!!
") + end + else + s.append ("
Unexpected OpenID.mode=" + l_openid_mode + "
") + end + m.set_body (s) + res.send (m) + elseif attached req.string_item ("openid_identifier") as l_id then + create s.make_empty + + o := new_openid_consumer (req) + s.append ("Testing " + l_id + "
%N") + s.append ("Return-to" + o.return_url + "
") + if attached o.auth_url (l_id) as l_auth_url then + s.append ("Click to sign with " + l_id + "
") + create redir.make (l_auth_url, 1) + s.append ("Automatically follow link in " + redir.delay.out + " second(s)
") + redir.set_title ("EWF::OpenID demo") + redir.set_body (s) + res.send (redir) + else + create m.make + m.set_title ("EWF::OpenID demo") + m.set_body (s) + res.send (m) + end + else + res.redirect_now ("/") + end + end + + new_openid_consumer (req: WSF_REQUEST): OPENID_CONSUMER + do + create Result.make (req.absolute_script_url ("/openid")) + +-- Result.ask_email (True) + Result.ask_nickname (False) +-- Result.ask_fullname (False) + Result.ask_country (True) + end +end diff --git a/library/security/openid/consumer/demo/demo-safe.ecf b/library/security/openid/consumer/demo/demo-safe.ecf new file mode 100644 index 00000000..f442bc10 --- /dev/null +++ b/library/security/openid/consumer/demo/demo-safe.ecf @@ -0,0 +1,23 @@ + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + diff --git a/library/security/openid/consumer/openid-safe.ecf b/library/security/openid/consumer/openid-safe.ecf new file mode 100644 index 00000000..def4e275 --- /dev/null +++ b/library/security/openid/consumer/openid-safe.ecf @@ -0,0 +1,20 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + diff --git a/library/security/openid/consumer/openid.ecf b/library/security/openid/consumer/openid.ecf new file mode 100644 index 00000000..016e42cc --- /dev/null +++ b/library/security/openid/consumer/openid.ecf @@ -0,0 +1,21 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + diff --git a/library/security/openid/consumer/src/openid_consumer.e b/library/security/openid/consumer/src/openid_consumer.e new file mode 100644 index 00000000..48f010b6 --- /dev/null +++ b/library/security/openid/consumer/src/openid_consumer.e @@ -0,0 +1,510 @@ +note + description: "[ + Light implementation of {OPENID} consumer. + + Sign-on with OpenID is a two step process: + ]" + author: "" + date: "$Date$" + revision: "$Revision$" + +class + OPENID_CONSUMER + +create + make + +feature {NONE} -- Initialization + + make (a_server: READABLE_STRING_8) + do + trusted_root := a_server + return_url := a_server + create required_info.make (0) + create optional_info.make (0) + end + +feature -- Access + + trusted_root: READABLE_STRING_8 + + return_url: READABLE_STRING_8 + + required_info: ARRAYED_LIST [READABLE_STRING_8] + optional_info: ARRAYED_LIST [READABLE_STRING_8] + + error: detachable READABLE_STRING_8 + + has_error: BOOLEAN + do + Result := error /= Void + end + +feature -- Change + + ask_all_info (is_required: BOOLEAN) + do + across + ax_to_sreg_map as c + loop + ask_info (c.item, is_required) + end + end + + ask_required_info (s: READABLE_STRING_8) + do + required_info.force (s) + end + + ask_optional_info (s: READABLE_STRING_8) + do + optional_info.force (s) + end + + ask_info (s: READABLE_STRING_8; is_required: BOOLEAN) + do + if is_required then + ask_required_info (s) + else + ask_optional_info (s) + end + end + + ask_nickname (is_required: BOOLEAN) + do + ask_info ("namePerson/friendly", is_required) + end + + ask_email (is_required: BOOLEAN) + do + ask_info ("contact/email", is_required) + end + + ask_fullname (is_required: BOOLEAN) + do + ask_info ("namePerson", is_required) + end + + ask_birthdate (is_required: BOOLEAN) + do + ask_info ("birthDate", is_required) + end + + ask_gender (is_required: BOOLEAN) + do + ask_info ("person/gender", is_required) + end + + ask_postcode (is_required: BOOLEAN) + do + ask_info ("contact/postalCode/home", is_required) + end + + ask_country (is_required: BOOLEAN) + do + ask_info ("contact/country/home", is_required) + end + + ask_language (is_required: BOOLEAN) + do + ask_info ("pref/language", is_required) + end + + ask_timezone (is_required: BOOLEAN) + do + ask_info ("pref/timezone", is_required) + end + +feature -- Query + + auth_url (a_identity: READABLE_STRING_8): detachable READABLE_STRING_8 + do + error := Void + if attached discovering_info (a_identity) as d_info then + if d_info.version = 2 then + Result := auth_url_v2 (a_identity, d_info) + else + Result := auth_url_v1 (a_identity, d_info) -- FIXME for claimed_id + end + end + end + +feature {OPENID_CONSUMER_VALIDATION} -- Implementation + + discovering_info (id: READABLE_STRING_8): detachable OPENID_DISCOVER + local + cl: LIBCURL_HTTP_CLIENT + sess: HTTP_CLIENT_SESSION + ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT + xrds_location: detachable READABLE_STRING_8 + xml: XML_STANDARD_PARSER + tree: XML_CALLBACKS_DOCUMENT + xelt: detachable XML_ELEMENT + s: READABLE_STRING_32 + r_uri: detachable READABLE_STRING_8 + r_err: BOOLEAN + r_delegate: detachable READABLE_STRING_8 + r_sreg_supported, r_ax_supported, r_identifier_select: BOOLEAN + r_version: INTEGER + l_xrds_content: detachable READABLE_STRING_8 + do + create cl.make + sess := cl.new_session (id) + sess.set_is_insecure (True) + if attached sess.get ("", ctx) as rep then + if rep.error_occurred then + report_error ("Unable get answer from openid provider at " + rep.url) + else + if + attached rep.header ("Content-Type") as l_content_type and then + l_content_type.has_substring ("application/xrds+xml") + then + l_xrds_content := rep.body + elseif attached rep.header ("X-XRDS-Location") as loc then + xrds_location := loc + else + report_error ("Failed (probably %""+ id +"%" is an invalid openid identifier).") + end + end + end + if l_xrds_content = Void and xrds_location /= Void then + sess := cl.new_session (xrds_location) + sess.set_is_insecure (True) + if attached sess.get ("", ctx) as rep then + if rep.error_occurred then + r_err := True + report_error ("Can not get " + rep.url) + elseif attached rep.body as l_content then + l_xrds_content := l_content + else + r_err := True + report_error ("No content: " + rep.url) + end + end + end + if l_xrds_content = Void then + r_err := True + report_error ("Unable to get the XRDS message.") + else + create xml.make + create tree.make_null + xml.set_callbacks (tree) + xml.parse_from_string (l_xrds_content) + if attached tree.document as xrds then + xelt := Void + xelt := xrds.elements.first + xelt := xelt.elements.first + if attached xelt as l_xrd then + if attached l_xrd.elements_by_name ("Service") as l_services then + across + l_services as c + until + r_uri /= Void + loop + if attached c.item.elements_by_name ("Type") as l_types then + across + l_types as t + loop + s := xml_content (t.item) + if s.same_string ("http://openid.net/sreg/1.0") then + r_sreg_supported := True + elseif s.same_string ("http://openid.net/extensions/sreg/1.1") then + r_sreg_supported := True + elseif s.same_string ("http://openid.net/srv/ax/1.0") then + r_ax_supported := True + elseif s.same_string ("http://specs.openid.net/auth/2.0/signon") then + r_version := 2 + elseif s.same_string ("http://specs.openid.net/auth/2.0/server") then + r_version := 2 + r_identifier_select := True + elseif s.same_string ("http://openid.net/signon/1.1") then + r_version := 1 + end + end + end + if attached c.item.element_by_name ("URI") as l_uri then + r_uri := xml_content (l_uri) + end + if r_version = 1 then + if attached c.item.element_by_name ("openid:Delegate") as l_id then + r_delegate := xml_content (l_id) + end + if attached c.item.element_by_name ("LocalID") as l_id then + r_delegate := xml_content (l_id) + end + else + if attached c.item.element_by_name ("CanonicalID") as l_id then + r_delegate := xml_content (l_id) + end + if attached c.item.element_by_name ("LocalID") as l_id then + r_delegate := xml_content (l_id) + end + end + end + end + end + end + + end + if r_uri /= Void then + create Result.make (r_uri, r_version) + if r_delegate = Void then + r_delegate := id + end + Result.delegate := r_delegate + Result.ax_supported := r_ax_supported + Result.sreg_supported := r_sreg_supported + Result.identifier_select := r_identifier_select + Result.has_error := r_err + + end + end + +feature {NONE} -- Implementation + + auth_url_v1 (a_id: READABLE_STRING_8; a_info: OPENID_DISCOVER): READABLE_STRING_8 + local + u: URI + ret: URI + do + create u.make_from_string (a_info.server_uri) + create ret.make_from_string (return_url) + if + attached a_info.delegate as l_claimed_id and then + not a_id.same_string (l_claimed_id) + then + ret.add_query_parameter ("openid.claimed_id", l_claimed_id) + end + + u.add_query_parameter ("openid.return_to", ret.string) + u.add_query_parameter ("openid.mode", "checkid_setup") -- or "checkid_immediate" + u.add_query_parameter ("openid.identity", a_id) + u.add_query_parameter ("openid.trust_root", trusted_root) + + if a_info.sreg_supported then + add_sreg_parameters_to (u) + end + + Result := u.string + end + + auth_url_v2 (a_id: READABLE_STRING_8; a_info: OPENID_DISCOVER): READABLE_STRING_8 + local + u: URI + do + create u.make_from_string (a_info.server_uri) + u.add_query_parameter ("openid.ns", "http://specs.openid.net/auth/2.0") + u.add_query_parameter ("openid.mode", "checkid_setup") -- or "checkid_immediate" + u.add_query_parameter ("openid.return_to", return_url) + u.add_query_parameter ("openid.realm", trusted_root) + + if a_info.ax_supported then + add_ax_parameters_to (u) + end + if a_info.sreg_supported then + add_sreg_parameters_to (u) + end + if a_info.identifier_select then + u.add_query_parameter ("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select") + u.add_query_parameter ("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select") + else + u.add_query_parameter ("openid.identity", a_id) + u.add_query_parameter ("openid.claimed_id", a_id) -- Fixme + end + + Result := u.string + end + + add_ax_parameters_to (a_uri: URI) + local + lst: ARRAYED_LIST [READABLE_STRING_8] + l_aliases: STRING_TABLE [READABLE_STRING_8] + l_counts: STRING_TABLE [INTEGER] + l_alias: READABLE_STRING_8 + s: STRING + do + create lst.make (required_info.count + optional_info.count) + lst.append (required_info) + lst.append (optional_info) + if lst.count > 0 then + a_uri.add_query_parameter ("openid.ns.ax", "http://openid.net/srv/ax/1.0") + a_uri.add_query_parameter ("openid.ax.mode", "fetch_request"); + + create l_aliases.make (lst.count) + create l_counts.make (lst.count) + across + lst as c + loop + l_alias := ax_to_alias (c.item) + if l_aliases.has (l_alias) then + if attached l_counts.item (l_alias) as l_count then + l_counts.replace (l_count + 1, l_alias) + else + check has_alias: False end + l_counts.force (1, l_alias) + end + else + l_aliases.force ("http://axschema.org/" + c.item, l_alias) + l_counts.force (1, l_alias) + end + end + across + l_aliases as c + loop + a_uri.add_query_parameter ("openid.ax.type." + c.key, c.item) + end + across + l_counts as c + loop + if c.item > 1 then + a_uri.add_query_parameter ("openid.ax.count." + c.key, c.item.out) + end + end + -- required + create s.make_empty + across + required_info as c + loop + if not s.is_empty then + s.append_character (',') + end + s.append (ax_to_alias (c.item)) + end + if not s.is_empty then + a_uri.add_query_parameter ("openid.ax.required", s) + end + -- optional + create s.make_empty + across + optional_info as c + loop + if not s.is_empty then + s.append_character (',') + end + s.append (ax_to_alias (c.item)) + end + if not s.is_empty then + a_uri.add_query_parameter ("openid.ax.if_available", s) + end + end + end + + ax_to_alias (n: READABLE_STRING_8): STRING_8 + do + if attached ax_to_sreg (n) as s then + Result := s + else + Result := n.string + Result.replace_substring_all ("/", "_") + Result.replace_substring_all (".", "_") + end + end + + add_sreg_parameters_to (a_uri: URI) + local + s: STRING + do + -- We always use SREG 1.1, even if the server is advertising only support for 1.0. + -- That's because it's fully backwards compatibile with 1.0, and some providers + -- advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com + a_uri.add_query_parameter ("openid.ns.sreg", "http://openid.net/extensions/sreg/1.1") + if not required_info.is_empty then + create s.make_empty + across + required_info as c + loop + if attached ax_to_sreg (c.item) as sreg then + if not s.is_empty then + s.append_character (',') + end + s.append (sreg) + end + end + if not s.is_empty then + a_uri.add_query_parameter ("openid.sreg.required", s) + end + end + + if not optional_info.is_empty then + create s.make_empty + across + optional_info as c + loop + if attached ax_to_sreg (c.item) as sreg then + if not s.is_empty then + s.append_character (',') + end + s.append (sreg) + end + end + if not s.is_empty then + a_uri.add_query_parameter ("openid.sreg.optional", s) + end + end + end + + ax_to_sreg_map: STRING_TABLE [READABLE_STRING_8] + once + create Result.make (7) + Result.compare_objects + Result.force ("nickname", "namePerson/friendly") + Result.force ("email", "contact/email") + Result.force ("fullname", "namePerson") + Result.force ("dob", "birthDate") + Result.force ("gender", "person/gender") + Result.force ("postcode", "contact/postalCode/home") + Result.force ("country", "contact/country/home") + Result.force ("language", "pref/language") + Result.force ("timezone", "pref/timezone") + + -- extension + Result.force ("firstname", "namePerson/first") + Result.force ("lastname", "namePerson/last") + end + + ax_to_sreg (n: READABLE_STRING_8): detachable READABLE_STRING_8 + do + if attached ax_to_sreg_map.item (n) as v then + Result := v + end + end + + sreg_to_ax (n: READABLE_STRING_8): detachable READABLE_STRING_8 + do + if ax_to_sreg_map.has_item (n) and then + attached ax_to_sreg_map.found_item as v + then + Result := v + end + end + + report_error (m: READABLE_STRING_8) + local + err: like error + do + err := error + if err = Void then + error := m + else + error := err + "%N" + m + end + debug + print (m) + end + ensure + has_error + end + + xml_content (e: XML_ELEMENT): STRING_8 + do + create Result.make_empty + if attached e.contents as lst then + across + lst as c + loop + Result.append (c.item.content) + end + end + end + +end diff --git a/library/security/openid/consumer/src/openid_consumer_validation.e b/library/security/openid/consumer/src/openid_consumer_validation.e new file mode 100644 index 00000000..34e26499 --- /dev/null +++ b/library/security/openid/consumer/src/openid_consumer_validation.e @@ -0,0 +1,265 @@ +note + description: "Summary description for {OPENID_CONSUMER_VALIDATION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + OPENID_CONSUMER_VALIDATION + +create + make_from_uri, + make_from_string + +feature {NONE} -- Initialization + + make_from_uri (o: OPENID_CONSUMER; a_uri: URI) + do + openid := o + uri := a_uri + return_url := o.return_url + create attributes.make (0) + end + + make_from_string (o: OPENID_CONSUMER; a_uri: READABLE_STRING_8) + do + make_from_uri (o, create {URI}.make_from_string (a_uri)) + end + + uri: URI + + return_url: READABLE_STRING_8 + +feature -- Access + + openid: OPENID_CONSUMER + + identity: detachable READABLE_STRING_8 + + attributes: STRING_TABLE [READABLE_STRING_32] + +feature -- Basic operation + + validate + -- Is openid identifier validated? + local + l_claimed_id: detachable READABLE_STRING_8 + tb: STRING_TABLE [detachable READABLE_STRING_32] + cl: LIBCURL_HTTP_CLIENT + 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 uri.decoded_query_items as q_lst then + if attached item_by_name ("openid.claimed_id", q_lst) as q_claimed_id then + l_claimed_id := q_claimed_id.as_string_8 + elseif attached item_by_name ("openid.identity", q_lst) as l_id then + l_claimed_id := l_id + 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 (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 cl.make + 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 := cl.new_session (d_info.server_uri) + sess.set_is_insecure (True) + 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: LIST [TUPLE [name: READABLE_STRING_32; value: detachable READABLE_STRING_32]]) + local + s: READABLE_STRING_32 + sreg_keys: ARRAYED_LIST [READABLE_STRING_32] + do + attributes.wipe_out + + get_sreg_attributes (lst) + get_ax_attributes (lst) + end + + get_sreg_attributes (lst: LIST [TUPLE [name: READABLE_STRING_32; value: detachable READABLE_STRING_32]]) + local + s: READABLE_STRING_32 + sreg_keys: ARRAYED_LIST [READABLE_STRING_32] + do + if attached item_by_name ("openid.signed", lst) as l_signed then + -- sreg attributes + create sreg_keys.make (3) + 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: LIST [TUPLE [name: READABLE_STRING_32; value: detachable READABLE_STRING_32]]) + local + s: READABLE_STRING_32 + ax_keys: ARRAYED_LIST [READABLE_STRING_32] + l_alias: detachable READABLE_STRING_8 + k_value, k_type, k_count, k: STRING + i: INTEGER + do + if attached item_by_name ("openid.signed", lst) as l_signed then + -- ax attributes + across + l_signed.split (',') as c + loop + s := c.item + if s.starts_with ("ns.") then + if attached item_by_name (s, lst) as v then + if s.same_string ("ns.ax") and v.same_string ("http://openid.net/srv/ax/1.0") then + l_alias := "ax." + else + if v.same_string ("http://openid.net/srv/ax/1.0") then + l_alias := s.substring (("ns.").count, s.count) + "." + end + end + end + end + end + if l_alias /= Void then + create ax_keys.make (lst.count) + across + l_signed.split (',') as c + loop + s := c.item + if s.starts_with (l_alias) then + ax_keys.force ("openid." + s) + end + end + + k_value := "openid." + l_alias + "value." + k_type := "openid." + l_alias + "type." + k_count := "openid." + l_alias + "count." + across + ax_keys as c + loop + s := c.item + if attached item_by_name (s, lst) as v then + if s.starts_with (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 ("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 ("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 + end + attributes.force (v, s.substring (5, s.count)) + end + end + end + end + end + + item_by_name (a_name: READABLE_STRING_32; lst: like {URI}.decoded_query_items): 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 diff --git a/library/security/openid/consumer/src/openid_discover.e b/library/security/openid/consumer/src/openid_discover.e new file mode 100644 index 00000000..cf61e1b0 --- /dev/null +++ b/library/security/openid/consumer/src/openid_discover.e @@ -0,0 +1,59 @@ +note + description: "Summary description for {OPENID_DISCOVER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + OPENID_DISCOVER + +create + make + +feature {NONE} -- Initialization + + make (a_server_uri: READABLE_STRING_8; a_version: like version) + do + version := a_version + server_uri := a_server_uri + end + +feature -- Access + + version: INTEGER + server_uri: READABLE_STRING_8 + delegate: detachable READABLE_STRING_8 assign set_delegate + ax_supported: BOOLEAN assign set_ax_supported + sreg_supported: BOOLEAN assign set_sreg_supported + identifier_select: BOOLEAN assign set_identifier_select + + has_error: BOOLEAN assign set_has_error + +feature -- Change + + set_delegate (v: like delegate) + do + delegate := v + end + + set_ax_supported (v: like ax_supported) + do + ax_supported := v + end + + set_sreg_supported (v: like sreg_supported) + do + sreg_supported := v + end + + set_identifier_select (v: like identifier_select) + do + identifier_select := v + end + + set_has_error (v: like has_error) + do + has_error := v + end + +end diff --git a/library/security/openid/consumer/tests/application.e b/library/security/openid/consumer/tests/application.e new file mode 100644 index 00000000..73f5b62b --- /dev/null +++ b/library/security/openid/consumer/tests/application.e @@ -0,0 +1,31 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + APPLICATION + +inherit + ARGUMENTS + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + do + test_openid + end + + test_openid + local + t: TEST_OPENID + do + create t.make + end + +end diff --git a/library/security/openid/consumer/tests/test_openid.e b/library/security/openid/consumer/tests/test_openid.e new file mode 100644 index 00000000..80b495c0 --- /dev/null +++ b/library/security/openid/consumer/tests/test_openid.e @@ -0,0 +1,69 @@ +note + description: "Summary description for {TEST_OPENID}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + TEST_OPENID + +create + make + +feature + + make + local + o: OPENID_CONSUMER + v: OPENID_CONSUMER_VALIDATION + id: READABLE_STRING_8 + do + id := "https://www.google.com/accounts/o8/id" + create o.make ("http://localhost") + if attached o.auth_url (id) as l_url then + check o.error = Void end + get_openid_response_uri (l_url) + if attached openid_response_uri as u and then u.is_valid then + create v.make_from_uri (o, u) + v.validate + if v.is_valid then + print ("Succeed ...%N") + else + print ("Failed !!!%N") + end + else + print ("Failed !!!%N") + end + elseif attached o.error as l_err then + print (l_err) + else + print ("???") + end + end + + get_openid_response_uri (a_auth_url: READABLE_STRING_8) + local + f: RAW_FILE + e: EXECUTION_ENVIRONMENT + do + openid_response_uri := Void + + create f.make_create_read_write ("openid_tmp.html") + f.put_string ("Click to authenticate with openid") + f.put_new_line + f.close + + create e + e.system ("start " + f.name) + + io.put_string ("Returned URL (copy/paste the url from your browser's address bar )?%N") + io.read_line + + create openid_response_uri.make_from_string (io.last_string) + end + + openid_response_uri: detachable URI + +feature -- Access + +end diff --git a/library/security/openid/consumer/tests/tests-safe.ecf b/library/security/openid/consumer/tests/tests-safe.ecf new file mode 100644 index 00000000..e342594f --- /dev/null +++ b/library/security/openid/consumer/tests/tests-safe.ecf @@ -0,0 +1,19 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + +