Attributes")
+ across
+ v.attributes as c
+ loop
+ s.append ("
" + c.key + "=" + c.item + "
")
+ end
+ 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$
+
+
+
+
+
+
+
+
+
+