OpenID consumer implementation
REQUIRES EiffelStudio 7.2
This commit is contained in:
@@ -1,148 +0,0 @@
|
||||
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 ("<form action=%"" + req.script_url ("/openid") + "%" method=%"POST%">%N")
|
||||
s.append ("<strong>OpenID identifier</strong> <input type='text' name='openid_identifier' value='' size='60'/>")
|
||||
s.append ("<input type='submit' name='op' value='sign with OpenID' />")
|
||||
s.append ("</form>%N")
|
||||
s.append ("<form action=%"" + req.script_url ("/openid") + "%" method=%"POST%">%N")
|
||||
s.append ("<strong>OpenID identifier</strong> <input type='text' name='openid_identifier' value='https://www.google.com/accounts/o8/id' size='60'/>")
|
||||
s.append ("<input type='submit' name='op' value='sign with Google' />")
|
||||
s.append ("</form>%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 ("<div>User authenticated</div>")
|
||||
s.append ("<ul>Query")
|
||||
across
|
||||
req.query_parameters as c
|
||||
loop
|
||||
s.append ("<li>" + c.item.url_encoded_name + "=" + c.item.string_representation + "</li>")
|
||||
end
|
||||
s.append ("</ul>")
|
||||
s.append ("<ul>Attributes")
|
||||
across
|
||||
v.attributes as c
|
||||
loop
|
||||
s.append ("<li>" + c.key + "=" + c.item + "</li>")
|
||||
end
|
||||
s.append ("</ul>")
|
||||
else
|
||||
s.append ("<div>User authentication failed!!!</div>")
|
||||
end
|
||||
else
|
||||
s.append ("<div>Unexpected OpenID.mode=" + l_openid_mode + "</div>")
|
||||
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 + "<br>%N")
|
||||
s.append ("Return-to" + o.return_url + "<br>")
|
||||
if attached o.auth_url (l_id) as l_auth_url then
|
||||
s.append ("<a href=%""+ l_auth_url + "%">Click to sign with " + l_id + "</a><br>")
|
||||
create redir.make (l_auth_url, 1)
|
||||
s.append ("Automatically follow link in " + redir.delay.out + " second(s)<br>")
|
||||
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
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="demo" uuid="DC4D6549-D5F4-4E1A-959A-6BD536737A21" library_target="demo">
|
||||
<target name="demo">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="..\..\..\..\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="wsf_nino_connector" location="..\..\..\..\server\wsf\connector\nino-safe.ecf" readonly="false"/>
|
||||
<library name="ewsgi" location="..\..\..\..\server\ewsgi\ewsgi-safe.ecf" readonly="false"/>
|
||||
<library name="ewsgi_nino_connector" location="..\..\..\..\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\..\..\..\server\wsf\wsf-safe.ecf"/>
|
||||
<library name="openid" location="../openid-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="openid" uuid="FCDB4F81-31EC-462B-9183-D506E6798C0B" library_target="openid">
|
||||
<target name="openid">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base-safe.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf" readonly="false"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf" readonly="false"/>
|
||||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>
|
||||
<library name="xml_tree" location="$ISE_LIBRARY\library\text\parser\xml\tree\xml_tree-safe.ecf" readonly="false"/>
|
||||
<library name="http_client" location="..\..\..\network\http_client\http_client-safe.ecf"/>
|
||||
<cluster name="src" location="src" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="openid" uuid="FCDB4F81-31EC-462B-9183-D506E6798C0B" library_target="openid">
|
||||
<target name="openid">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf" readonly="false"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf" readonly="false"/>
|
||||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser.ecf"/>
|
||||
<library name="xml_tree" location="$ISE_LIBRARY\library\text\parser\xml\tree\xml_tree.ecf" readonly="false"/>
|
||||
<library name="http_client" location="..\..\..\network\http_client\http_client.ecf"/>
|
||||
<cluster name="src" location="src" recursive="true"/>
|
||||
|
||||
</target>
|
||||
</system>
|
||||
@@ -1,510 +0,0 @@
|
||||
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
|
||||
@@ -1,265 +0,0 @@
|
||||
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
|
||||
@@ -1,59 +0,0 @@
|
||||
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
|
||||
@@ -1,31 +0,0 @@
|
||||
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
|
||||
@@ -1,69 +0,0 @@
|
||||
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 ("<a href=%"" + a_auth_url + "%">Click to authenticate with openid</a>")
|
||||
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
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="tests" uuid="A36A6621-CF4A-4FB6-8CB2-D420D630A134">
|
||||
<target name="tests">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="openid" location="..\openid-safe.ecf" readonly="false"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<tests name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
Reference in New Issue
Block a user