Renamed login module as auth (authentication) module

This commit is contained in:
2015-06-19 11:48:42 +02:00
parent 149de898c0
commit 2c72fe6738
14 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="auth_module" uuid="AAB9EE7D-A671-4727-8658-D417A48B2B57" library_target="auth_module">
<target name="auth_module">
<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="cms" location="$ISE_LIBRARY\unstable\library\web\cms\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="$ISE_LIBRARY\unstable\library\web\cms\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_model" location="$ISE_LIBRARY\unstable\library\web\cms\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="config" location="$ISE_LIBRARY\unstable\library\web\cms\library\configuration\config-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer-safe.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,13 @@
note
description: "Summary description for {CMS_AUTHENTICATION_CONSTANTS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_AUTHENTICATION_CONSTANTS
feature -- Access
oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_"
end

View File

@@ -0,0 +1,268 @@
note
description: "Summary description for {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS
inherit
EMAIL_SERVICE_PARAMETERS
create
make
feature {NONE} -- Initialization
make (a_cms_api: CMS_API)
local
utf: UTF_CONVERTER
l_site_name: READABLE_STRING_8
s: detachable READABLE_STRING_32
l_contact_email, l_subject_register, l_subject_activate, l_subject_password, l_subject_oauth: detachable READABLE_STRING_8
do
setup := a_cms_api.setup
-- Use global smtp setting if any, otherwise "localhost"
smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost"))
l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name)
admin_email := a_cms_api.setup.site_email
if not admin_email.has ('<') then
admin_email := l_site_name + " <" + admin_email +">"
end
if attached {CONFIG_READER} a_cms_api.module_configuration ("login", Void) as cfg then
if attached cfg.text_item ("smtp") as l_smtp then
-- Overwrite global smtp setting if any.
smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp)
end
s := cfg.text_item ("email")
if s /= Void then
l_contact_email := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_register")
if s /= Void then
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_activate")
if s /= Void then
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_password")
if s /= Void then
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_oauth")
if s /= Void then
l_subject_oauth := utf.utf_32_string_to_utf_8_string_8 (s)
end
end
if l_contact_email /= Void then
if not l_contact_email.has ('<') then
l_contact_email := l_site_name + " <" + l_contact_email + ">"
end
contact_email := l_contact_email
else
contact_email := admin_email
end
if l_subject_register /= Void then
contact_subject_register := l_subject_register
else
contact_subject_register := "Thank you for registering with us."
end
if l_subject_activate /= Void then
contact_subject_activate := l_subject_activate
else
contact_subject_activate := "New account activation token."
end
if l_subject_password /= Void then
contact_subject_password := l_subject_password
else
contact_subject_password := "Password Recovery."
end
if l_subject_oauth /= Void then
contact_subject_oauth := l_subject_oauth
else
contact_subject_oauth := "Welcome."
end
end
feature -- Access
smtp_server: IMMUTABLE_STRING_8
admin_email: IMMUTABLE_STRING_8
contact_email: IMMUTABLE_STRING_8
-- Contact email.
contact_subject_register: IMMUTABLE_STRING_8
contact_subject_activate: IMMUTABLE_STRING_8
contact_subject_password: IMMUTABLE_STRING_8
contact_subject_oauth: IMMUTABLE_STRING_8
account_activation: STRING
-- Account activation template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_activation.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_activation)
end
end
account_re_activation: STRING
-- Account re_activation template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("accunt_re_activation.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_re_activation)
end
end
account_password: STRING
-- Account password template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_new_password.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_new_password)
end
end
account_welcome: STRING
-- Account welcome template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_welcome.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_welcome)
end
end
feature {NONE} -- Implementation
setup: CMS_SETUP
read_template_file (a_path: PATH): detachable STRING
-- Read the content of the file at path `a_path'.
local
l_file: FILE
do
create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path)
if l_file.exists and then l_file.is_readable then
l_file.open_read
l_file.read_stream (l_file.count)
Result := l_file.last_string
l_file.close
else
-- Error
end
end
feature {NONE} -- Message email
template_account_activation: STRING= "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Thank you for registering at <a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
template_account_re_activation: STRING= "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>New Activation</title>
<meta name="description" content="New Activation token">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>You have request a new activation token at<a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
template_account_new_password: STRING= "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>New Password</title>
<meta name="description" content="New Password">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>You have required a new password at <a href="...">ROC CMS</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p>
<p><a href="$link">$link</a></p>
</body>
</html>
]"
template_account_welcome: STRING= "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Welcome</title>
<meta name="description" content="Welcome">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Welcome to<a href="...">ROC CMS</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
end

View File

@@ -0,0 +1,890 @@
note
description: "Module Logging supporting different authentication strategies"
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $"
revision: "$Revision: 97328 $"
class
CMS_AUTHENTICATION_MODULE
inherit
CMS_MODULE
rename
module_api as user_oauth_api
redefine
filters,
register_hooks,
initialize,
is_installed,
install,
user_oauth_api
end
CMS_HOOK_BLOCK
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
REFACTORING_HELPER
SHARED_LOGGER
CMS_REQUEST_UTIL
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
name := "login"
version := "1.0"
description := "Eiffel login module"
package := "login"
create root_dir.make_current
cache_duration := 0
end
feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API)
-- <Precursor>
local
l_user_auth_api: like user_oauth_api
l_user_auth_storage: CMS_OAUTH_20_STORAGE_I
do
Precursor (a_api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage
end
-- Node API initialization
create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage)
user_oauth_api := l_user_auth_api
ensure then
user_oauth_api_set: user_oauth_api /= Void
end
feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN
-- Is Current module installed?
do
Result := attached api.storage.custom_value ("is_initialized", "module-" + name) as v and then v.is_case_insensitive_equal_general ("yes")
end
install (api: CMS_API)
local
l_setup: CMS_SETUP
l_params: detachable STRING_TABLE [detachable ANY]
l_consumers: LIST [STRING]
do
l_setup := api.setup
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("oauth2_consumers") then
--| Schema
l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers.sql"))
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for blog module", generating_type)
end
-- TODO workaround.
l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers_initialize.sql"))
end
-- TODO workaround, until we have an admin module
l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type)
else
from
l_sql_storage.sql_start
create {ARRAYED_LIST[STRING]} l_consumers.make (2)
until
l_sql_storage.sql_after
loop
if attached l_sql_storage.sql_read_string (1) as l_name then
l_consumers.force ("oauth2_"+l_name)
end
l_sql_storage.sql_forth
end
across l_consumers as ic loop
if not l_sql_storage.sql_table_exists (ic.item) then
create l_params.make (1)
l_params.force (ic.item, "table_name")
l_sql_storage.sql_execute_file_script_with_params (l_setup.environment.path.extended ("scripts").extended ("oauth2_template.sql"), l_params)
end
end
end
api.storage.set_custom_value ("is_initialized", "module-" + name, "yes")
end
end
feature {CMS_API} -- Access: API
user_oauth_api: detachable CMS_OAUTH_20_API
-- <Precursor>
feature -- Filters
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
-- Possibly list of Filter's module.
do
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
if attached user_oauth_api as l_user_oauth_api then
Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api))
end
end
feature -- Access: docs
root_dir: PATH
cache_duration: INTEGER
-- Caching duration
--| 0: disable
--| -1: cache always valie
--| nb: cache expires after nb seconds.
cache_disabled: BOOLEAN
do
Result := cache_duration = 0
end
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
if attached user_oauth_api as l_user_oauth_api then
configure_web (a_api, l_user_oauth_api, a_router)
end
end
configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER)
do
a_router.handle ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api,a_user_oauth_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post)
end
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_response)
a_response.subscribe_to_block_hook (Current)
a_response.subscribe_to_value_table_alter_hook (Current)
end
feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if attached current_user (a_response.request) as l_user then
a_value.force (l_user, "user")
end
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
local
lnk: CMS_LOCAL_LINK
do
if attached a_response.current_user (a_response.request) as u then
create lnk.make (u.name + " (Logout)", "account/roc-logout" )
else
create lnk.make ("Login", "account/roc-login")
end
a_menu_system.primary_menu.extend (lnk)
lnk.set_weight (98)
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do
Result := <<"login","register","reactivate","new_password", "reset_password">>
create l_string.make_empty
across Result as ic loop
l_string.append (ic.item)
l_string.append_character (' ')
end
write_debug_log (generator + ".block_list:" + l_string )
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if
a_block_id.is_case_insensitive_equal_general ("login") and then
a_response.request.path_info.starts_with ("/account/roc-login")
then
get_block_view_login (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("register") and then
a_response.request.path_info.starts_with ("/account/roc-register")
then
get_block_view_register (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("reactivate") and then
a_response.request.path_info.starts_with ("/account/reactivate")
then
get_block_view_reactivate (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("new_password") and then
a_response.request.path_info.starts_with ("/account/new-password")
then
get_block_view_new_password (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("reset_password") and then
a_response.request.path_info.starts_with ("/account/reset-password")
then
get_block_view_reset_password (a_block_id, a_response)
end
end
handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_value ("Login", "optional_content_type")
r.execute
end
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_url: STRING
l_cookie: WSF_COOKIE
do
if
attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_cookie_token and then
attached {CMS_USER} current_user (req) as l_user
then
-- Logout gmail
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_cookie_token.value)
l_cookie.set_path ("/")
l_cookie.set_max_age (-1)
res.add_cookie (l_cookie)
unset_current_user (req)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found)
r.set_redirection (req.absolute_script_url (""))
r.execute
else
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found)
l_url := req.absolute_script_url ("")
l_url.append ("/basic_auth_logoff")
r.set_redirection (l_url)
r.execute
end
end
handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_user_api: CMS_USER_API
u: CMS_USER
l_roles: LIST [CMS_USER_ROLE]
l_exist: BOOLEAN
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_link: STRING
l_token: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_value ("Register", "optional_content_type")
if req.is_post_request_method then
if
attached {WSF_STRING} req.form_parameter ("name") as l_name and then
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
attached {WSF_STRING} req.form_parameter ("email") as l_email
then
l_user_api := api.user_api
if attached l_user_api.user_by_name (l_name.value) then
-- Username already exist.
r.values.force ("The user name exist!", "error_name")
l_exist := True
end
if attached l_user_api.user_by_email (l_email.value) then
-- Emails already exist.
r.values.force ("The email exist!", "error_email")
l_exist := True
end
if not l_exist then
-- New user
create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1)
l_roles.force (l_user_api.authenticated_user_role)
create u.make (l_name.value)
u.set_email (l_email.value)
u.set_password (l_password.value)
u.set_roles (l_roles)
l_user_api.new_user (u)
-- Create activation token
l_token := new_token
l_user_api.new_activation (l_token, u.id)
create l_link.make_from_string (req.server_url)
l_link.append ("/account/activate/")
l_link.append (l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_email")
es.send_contact_email (l_email.value, l_link)
else
r.values.force (l_name.value, "name")
r.values.force (l_email.value, "email")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
end
r.execute
end
handle_activation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_user_api: CMS_USER_API
l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE
do
l_user_api := api.user_api
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.path_parameter ("token") as l_token then
if attached {CMS_USER} l_user_api.user_by_activation_token (l_token.value) as l_user then
-- Valid user_id
l_user.mark_active
l_user_api.update_user (l_user)
l_user_api.remove_activation (l_token.value)
r.set_value ("Account activated", "optional_content_type")
r.set_main_content ("<p> Your account <i>"+ l_user.name +"</i> has been activated</p>")
else
-- the token does not exist, or it was already used.
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.set_value ("Account not activated", "optional_content_type")
r.set_main_content ("<p>The token <i>"+ l_token.value +"</i> is not valid <a href=%"/account/reactivate%">Reactivate Account</a></p>" )
end
r.execute
else
create l_ir.make (req, res, api)
l_ir.execute
end
end
handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_user_api: CMS_USER_API
l_token: STRING
l_link: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if req.is_post_request_method then
if
attached {WSF_STRING} req.form_parameter ("email") as l_email
then
l_user_api := api.user_api
if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then
-- User exist create a new token and send a new email.
if l_user.is_active then
r.values.force ("The asociated user to the given email " + l_email.value + " , is already active", "is_active")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
else
l_token := new_token
l_user_api.new_activation (l_token, l_user.id)
create l_link.make_from_string (req.server_url)
l_link.append ("/account/activate/")
l_link.append (l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_activation_email")
es.send_contact_activation_email (l_email.value, l_link)
end
else
r.values.force ("The email does not exist or !", "error_email")
r.values.force (l_email.value, "email")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
end
r.execute
end
handle_new_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_user_api: CMS_USER_API
l_token: STRING
l_link: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if req.is_post_request_method then
l_user_api := api.user_api
if attached {WSF_STRING} req.form_parameter ("email") as l_email then
if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then
-- User exist create a new token and send a new email.
l_token := new_token
l_user_api.new_password (l_token, l_user.id)
create l_link.make_from_string (req.server_url)
l_link.append ("/account/reset-password?token=")
l_link.append (l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_password_email")
es.send_contact_password_email (l_email.value, l_link)
else
r.values.force ("The email does not exist !", "error_email")
r.values.force (l_email.value, "email")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
end
r.execute
end
handle_reset_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_user_api: CMS_USER_API
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_user_api := api.user_api
if attached {WSF_STRING} req.query_parameter ("token") as l_token then
r.values.force (l_token.value, "token")
if l_user_api.user_by_password_token (l_token.value) = Void then
r.values.force ("The token " + l_token.value + " is not valid, click <a href=%"/account/new-password%">here</a> to generate a new token.", "error_token")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
if req.is_post_request_method then
if
attached {WSF_STRING} req.form_parameter ("token") as l_token and then
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password
then
-- Does the passwords match?
if l_password.value.same_string (l_confirm_password.value) then
-- is the token valid?
if attached {CMS_USER} l_user_api.user_by_password_token (l_token.value) as l_user then
l_user.set_password (l_password.value)
l_user_api.update_user (l_user)
l_user_api.remove_password (l_token.value)
end
else
r.values.force ("Passwords Don't Match", "error_password")
r.values.force (l_token.value, "token")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
end
r.execute
end
feature {NONE} -- Helpers
template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK
-- Smarty content block for `a_block_id'
local
p: detachable PATH
do
create p.make_from_string ("templates")
p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
p := a_response.module_resource_path (Current, p)
if p /= Void then
if attached p.entry as e then
create Result.make (a_block_id, Void, p.parent, e)
else
create Result.make (a_block_id, Void, p.parent, p)
end
end
end
feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
local
vals: CMS_VALUE_TABLE
do
if attached template_block (a_block_id, a_response) as l_tpl_block then
create vals.make (1)
-- add the variable to the block
value_table_alter (vals, a_response)
across
vals as ic
loop
l_tpl_block.set_value (ic.item, ic.key)
end
if
attached user_oauth_api as l_auth_api and then
attached l_auth_api.oauth2_consumers as l_list
then
l_tpl_block.set_value (l_list, "oauth_consumers")
end
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_name") or else a_response.values.has ("error_email") then
if attached template_block (a_block_id, a_response) as l_tpl_block then
l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name")
l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
l_tpl_block.set_value (a_response.values.item ("email"), "email")
l_tpl_block.set_value (a_response.values.item ("name"), "name")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
else
if attached template_block ("post_register", a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then
if attached template_block (a_block_id, a_response) as l_tpl_block then
l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
l_tpl_block.set_value (a_response.values.item ("email"), "email")
l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
else
if attached template_block ("post_reactivate", a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
get_block_view_new_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_email") then
if attached template_block (a_block_id, a_response) as l_tpl_block then
l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email")
l_tpl_block.set_value (a_response.values.item ("email"), "email")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
else
if attached template_block ("post_password", a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
get_block_view_reset_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_response.request.is_get_request_method then
if attached template_block (a_block_id, a_response) as l_tpl_block then
l_tpl_block.set_value (a_response.values.item ("token"), "token")
l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
elseif a_response.request.is_post_request_method then
if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then
if attached template_block (a_block_id, a_response) as l_tpl_block then
l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token")
l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password")
l_tpl_block.set_value (a_response.values.item ("token"), "token")
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
else
if attached template_block ("post_reset", a_response) as l_tpl_block then
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
feature -- OAuth2 Login with google.
handle_login_with_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_oauth: CMS_OAUTH_20_WORKFLOW
do
if
attached {WSF_STRING} req.path_parameter ("callback") as p_consumer and then
attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer
then
create l_oauth.make (req.server_url, l_consumer)
if attached l_oauth.authorization_url as l_authorization_url then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (l_authorization_url)
r.execute
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Bad request")
r.execute
end
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Bad request")
r.execute
end
end
handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_auth: CMS_OAUTH_20_WORKFLOW
l_user_api: CMS_USER_API
l_user: CMS_USER
l_roles: LIST [CMS_USER_ROLE]
l_cookie: WSF_COOKIE
es: CMS_AUTHENTICATON_EMAIL_SERVICE
do
if attached {WSF_STRING} req.path_parameter ("callback") as l_callback and then
attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then
attached {WSF_STRING} req.query_parameter ("code") as l_code
then
create l_auth.make (req.server_url, l_consumer)
l_auth.sign_request (l_code.value)
if
attached l_auth.access_token as l_access_token and then
attached l_auth.user_profile as l_user_profile
then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- extract user email
-- check if the user exist
l_user_api := api.user_api
-- 1 if the user exit put it in the context
if
attached l_auth.user_email as l_email
then
if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then
-- User with email exist
if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then
-- Update oauth entry
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
else
-- create a oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
end
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
else
create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1)
l_roles.force (l_user_api.authenticated_user_role)
-- Create a new user and oauth entry
create l_user.make (l_email)
l_user.set_email (l_email)
l_user.set_password (new_token) -- generate a random password.
l_user.set_roles (l_roles)
l_user.mark_active
l_user_api.new_user (l_user)
-- Add oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name )
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
set_current_user (req, l_user)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_welcome_email")
es.send_contact_welcome_email (l_email, "")
end
else
end
r.set_redirection (req.absolute_script_url (""))
r.execute
end
end
end
feature {NONE} -- Token Generation
new_token: STRING
-- Generate a new token activation token
local
l_token: STRING
l_security: SECURITY_PROVIDER
l_encode: URL_ENCODER
do
create l_security
l_token := l_security.token
create l_encode
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
-- Loop ensure that we have a security token that does not contain characters that need encoding.
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
-- but the user will need to use an unencoded token if activation has to be done manually.
l_token := l_security.token
end
Result := l_token
end
feature {NONE} -- Implementation: date and time
http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME
local
d: HTTP_DATE
do
create d.make_from_string (s)
if not d.has_error then
Result := d.date_time
end
end
file_date (p: PATH): DATE_TIME
require
path_exists: (create {FILE_UTILITIES}).file_path_exists (p)
local
f: RAW_FILE
do
create f.make_with_path (p)
Result := timestamp_to_date (f.date)
end
timestamp_to_date (n: INTEGER): DATE_TIME
local
d: HTTP_DATE
do
create d.make_from_timestamp (n)
Result := d.date_time
end
note
copyright: "Copyright (c) 1984-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,88 @@
note
description: "Summary description for {CMS_AUTHENTICATON_EMAIL_SERVICE}."
date: "$Date$"
revision: "$Revision$"
class
CMS_AUTHENTICATON_EMAIL_SERVICE
inherit
EMAIL_SERVICE
redefine
initialize,
parameters
end
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
contact_email := parameters.contact_email
end
parameters: CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS
-- Associated parameters.
feature -- Access
contact_email: IMMUTABLE_STRING_8
-- contact email.
feature -- Basic Operations
send_contact_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_activation)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_register, l_message)
end
send_contact_activation_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_re_activation)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_activate, l_message)
end
send_contact_password_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_password)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_password, l_message)
end
send_contact_welcome_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_welcome)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_oauth, l_message)
end
end

View File

@@ -0,0 +1,95 @@
note
description: "[
API to manage CMS User OAuth authentication.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_API
inherit
CMS_MODULE_API
REFACTORING_HELPER
create {CMS_AUTHENTICATION_MODULE}
make_with_storage
feature {NONE} -- Initialization
make_with_storage (a_api: CMS_API; a_oauth_storage: CMS_OAUTH_20_STORAGE_I)
-- Create an object with api `a_api' and storage `a_oauth_storage'.
do
oauth_20_storage := a_oauth_storage
make (a_api)
ensure
oauht_20_storage_set: oauth_20_storage = a_oauth_storage
end
feature {CMS_MODULE} -- Access: User oauth storage.
oauth_20_storage: CMS_OAUTH_20_STORAGE_I
-- storage interface.
feature -- Access: User Oauth20
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby.
do
Result := oauth_20_storage.user_oauth2_by_id (a_uid, a_consumer)
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER
-- Retrieve a user by token `a_token' for the consumer `a_consumer'.
do
Result := oauth_20_storage.user_oauth2_by_token (a_token, a_consumer)
end
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
do
Result := oauth_20_storage.user_oauth2_without_consumer_by_token (a_token)
end
feature -- Access: Consumers OAuth20
oauth2_consumers: LIST [STRING]
-- List of Oauth_20 consumers, if any, empty in other case.
do
Result := oauth_20_storage.oauth2_consumers
end
oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by name `a_name', if any.
do
Result := oauth_20_storage.oauth_consumer_by_name (a_name)
end
oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by callback `a_callback', if any.
do
Result := oauth_20_storage.oauth_consumer_by_callback (a_callback)
end
feature -- Change: User OAuth20
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32)
-- Add a new user with oauth20 using the consumer `a_consumer'.
require
has_id: a_user.has_id
do
oauth_20_storage.new_user_oauth2 (a_token, a_user_profile, a_user, a_consumer)
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
-- Updaate user `a_user' with oauth2 for the consumer `a_consumer'.
require
has_id: a_user.has_id
do
oauth_20_storage.update_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table)
end
end

View File

@@ -0,0 +1,152 @@
note
description: "Summary description for {CMS_OAUTH_CONSUMER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_CONSUMER
inherit
ANY
redefine
default_create
end
create
default_create
feature {NONE} -- Initialization
default_create
do
set_endpoint ("")
set_authorize_url ("")
set_extractor ("")
set_callback_name ("")
set_protected_resource_url ("")
set_scope ("")
set_api_key ("")
set_api_secret ("")
set_name ("")
end
feature -- Access
endpoint: READABLE_STRING_32
-- Url that receives the access token request.
authorize_url: READABLE_STRING_32
--
extractor: READABLE_STRING_32
-- text, json
callback_name: READABLE_STRING_32
-- consumer callback name
protected_resource_url: READABLE_STRING_32
-- consumer resource url
scope: READABLE_STRING_32
-- consumer scope
api_key: READABLE_STRING_32
-- consumer public key
api_secret: READABLE_STRING_32
-- consumer secret.
name: READABLE_STRING_32
-- consumer name.
id: INTEGER_64
-- unique identifier.
feature -- Element change
set_extractor (a_extractor: like extractor)
-- Assign `extractor' with `a_extractor'.
do
extractor := a_extractor
ensure
extractor_assigned: extractor = a_extractor
end
set_authorize_url (a_authorize_url: like authorize_url)
-- Assign `authorize_url' with `a_authorize_url'.
do
authorize_url := a_authorize_url
ensure
authorize_url_assigned: authorize_url = a_authorize_url
end
set_endpoint (a_endpoint: like endpoint)
-- Assign `endpoint' with `a_endpoint'.
do
endpoint := a_endpoint
ensure
endpoint_assigned: endpoint = a_endpoint
end
set_callback_name (a_callback_name: like callback_name)
-- Assign `callback_name' with `a_callback_name'.
do
callback_name := a_callback_name
ensure
callback_name_assigned: callback_name = a_callback_name
end
set_protected_resource_url (a_protected_resource_url: like protected_resource_url)
-- Assign `protected_resource_url' with `a_protected_resource_url'.
do
protected_resource_url := a_protected_resource_url
ensure
protected_resource_url_assigned: protected_resource_url = a_protected_resource_url
end
set_scope (a_scope: like scope)
-- Assign `scope' with `a_scope'.
do
scope := a_scope
ensure
scope_assigned: scope = a_scope
end
set_api_key (an_api_key: like api_key)
-- Assign `api_key' with `an_api_key'.
do
api_key := an_api_key
ensure
api_key_assigned: api_key = an_api_key
end
set_api_secret (an_api_secret: like api_secret)
-- Assign `api_secret' with `an_api_secret'.
do
api_secret := an_api_secret
ensure
api_secret_assigned: api_secret = an_api_secret
end
set_name (a_name: like name)
-- Assign `name' with `a_name'.
do
name := a_name
ensure
name_assigned: name = a_name
end
set_id (an_id: like id)
-- Assign `id' with `an_id'.
do
id := an_id
ensure
id_assigned: id = an_id
end
end

View File

@@ -0,0 +1,133 @@
note
description: "OAuth workflow"
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_WORKFLOW
inherit
SHARED_LOGGER
create
make
feature {NONE} -- Initialization
make (a_host: READABLE_STRING_32; a_consumer: CMS_OAUTH_20_CONSUMER)
-- Create an object with the host `a_host'.
do
initilize (a_consumer)
create config.make_default (a_consumer.api_key, a_consumer.api_secret)
config.set_callback (a_host + "/account/"+ a_consumer.callback_name)
config.set_scope (a_consumer.scope)
--Todo create a generic OAUTH_20_GENERIC_API
create oauth_api.make (a_consumer.endpoint, a_consumer.authorize_url, a_consumer.extractor)
api_service := oauth_api.create_service (config)
end
initilize (a_consumer: CMS_OAUTH_20_CONSUMER)
do
--Use configuration values if any if not defaul
api_key := a_consumer.api_key
api_secret := a_consumer.api_secret
scope := a_consumer.scope
protected_resource_url := a_consumer.protected_resource_url
end
feature -- Access
authorization_url: detachable READABLE_STRING_32
-- Obtain the Authorization URL.
do
-- Obtain the Authorization URL
write_debug_log (generator + ".authorization_url Fetching the Authorization URL..!")
if attached api_service.authorization_url (empty_token) as l_authorization_url then
write_debug_log (generator + ".authorization_url: Got the Authorization URL!")
write_debug_log (generator + ".authorization_url:" + l_authorization_url)
Result := l_authorization_url.as_string_32
end
end
sign_request (a_code: READABLE_STRING_32)
-- Sign request with code `a_code'.
--! To get the code `a_code' you need to do a request
--! using the authorization_url
local
request: OAUTH_REQUEST
do
-- Get the access token.
write_debug_log (generator + ".sign_request Fetching the access token with code [" + a_code + "]")
access_token := api_service.access_token_post (empty_token, create {OAUTH_VERIFIER}.make (a_code))
if attached access_token as l_access_token then
write_debug_log (generator + ".sign_request Got the Access Token [" + l_access_token.debug_output + "]")
-- Get the user email
--! at the moment the scope is mail, but we can change it to get more information.
create request.make ("GET", protected_resource_url)
request.add_header ("Authorization", "Bearer " + l_access_token.token)
api_service.sign_request (l_access_token, request)
if attached {OAUTH_RESPONSE} request.execute as l_response then
write_debug_log (generator + ".sign_request Sign_request response [" + l_response.status.out + "]")
if attached l_response.body as l_body then
user_profile := l_body
write_debug_log (generator + ".sign_request User profile [" + l_body + "]")
end
end
end
end
user_email: detachable READABLE_STRING_32
-- Retrieve user email if any.
local
l_json: JSON_CONFIG
do
if attached user_profile as l_profile then
create l_json.make_from_string (l_profile)
if
attached {JSON_ARRAY} l_json.item ("emails") as l_array and then
attached {JSON_OBJECT} l_array.i_th (1) as l_object and then
attached {JSON_STRING} l_object.item ("value") as l_email
then
Result := l_email.item
elseif attached {JSON_STRING} l_json.item ("email") as l_email then
Result := l_email.unescaped_string_32
end
end
end
feature -- Access
access_token: detachable OAUTH_TOKEN
-- JSON representing the access token.
user_profile: detachable READABLE_STRING_32
-- JSON representing the user profiles.
feature {NONE} -- Implementation
oauth_api: CMS_OAUTH_20_GENERIC_API
-- OAuth 2.0 Google API.
config: OAUTH_CONFIG
-- configuration.
api_service: OAUTH_SERVICE_I
-- Service.
api_key: STRING
-- public key.
api_secret: STRING
-- secret key.
scope: STRING
-- api scope to access protected resources.
protected_resource_url: STRING
-- Resource url.
empty_token: detachable OAUTH_TOKEN
-- fake token.
end

View File

@@ -0,0 +1,58 @@
note
description: "Summary description for {CMS_OAUTH_20_FILTER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_FILTER
inherit
WSF_URI_TEMPLATE_HANDLER
CMS_HANDLER
rename
make as make_handler
end
WSF_FILTER
create
make
feature {NONE} -- Initialization
make (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API)
do
make_handler (a_api)
user_oauth_api := a_user_oauth_api
end
user_oauth_api: CMS_OAUTH_20_API
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter.
local
do
api.logger.put_debug (generator + ".execute ", Void)
-- if attached req.raw_header_data as l_raw_data then
-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
-- end
-- A valid user
if
attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_roc_auth_session_token
then
if attached {CMS_USER} user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then
set_current_user (req, l_user)
execute_next (req, res)
else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void)
execute_next (req, res)
end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
execute_next (req, res)
end
end
end

View File

@@ -0,0 +1,94 @@
note
description: "Generic OAUTH2 API"
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_GENERIC_API
inherit
OAUTH_20_API
redefine
access_token_extractor,
access_token_verb
end
create
make
feature {NONE} -- Initialize
make (a_endpoint: READABLE_STRING_8; a_authorize_url: READABLE_STRING_8; a_extractor: READABLE_STRING_8)
do
endpoint := a_endpoint
authorize_url := a_authorize_url
extractor := a_extractor
ensure
endpoint_set: endpoint = a_endpoint
authorize_url_set: authorize_url = a_authorize_url
extractor_set: extractor = a_authorize_url
end
endpoint: READABLE_STRING_8
-- Url that receives the access token request.
authorize_url: READABLE_STRING_8
--
extractor: READABLE_STRING_8
-- text, json
feature -- Access
access_token_extractor: ACCESS_TOKEN_EXTRACTOR
-- Return token extractor, by default TOKEN_EXTRACTOR_20.
do
if extractor.is_case_insensitive_equal_general ("json") then
create {JSON_TOKEN_EXTRACTOR} Result
else
create {TOKEN_EXTRACTOR_20} Result
end
end
access_token_verb: STRING_8
do
Result := "POST"
end
access_token_endpoint: STRING_8
-- Url that receives the access token request
do
create Result.make_from_string (endpoint)
end
authorization_url (config: OAUTH_CONFIG): detachable STRING_8
-- Url where you should redirect your users to authneticate
local
l_result: STRING_8
do
if attached config.scope as l_scope then
create l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL)
l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8)
if attached config.callback as l_callback then
l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_8))
end
if attached config.callback as l_callback then
l_result.replace_substring_all ("$SCOPE", (create {OAUTH_ENCODER}).encoded_string (l_scope.as_STRING_8))
Result := l_result
end
else
create l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL)
l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8)
if attached config.callback as l_callback then
l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_8))
end
end
end
feature -- Implementation
Scoped_authorize_url: STRING = "&scope=$SCOPE";
end

View File

@@ -0,0 +1,66 @@
note
description: "[
API to handle OAUTH storage
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_OAUTH_20_STORAGE_I
inherit
SHARED_LOGGER
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access: Users
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby.
deferred
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
-- Retrieve a user by token `a_token' for the consumer `a_consumer'.
deferred
end
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
deferred
end
feature -- Access: Consumers
oauth2_consumers: LIST [STRING]
deferred
end
oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by name `a_name', if any.
deferred
end
oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by callback `a_callback', if any.
deferred
end
feature -- Change: User Oauth2
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
-- Add a new user with oauth2 authentication.
deferred
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 )
-- Update user `a_user' with oauth2 authentication.
deferred
end
end

View File

@@ -0,0 +1,69 @@
note
description: "Summary description for {CMS_OAUTH_20_STORAGE_NULL}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_STORAGE_NULL
inherit
CMS_OAUTH_20_STORAGE_I
feature -- Error handler
error_handler: ERROR_HANDLER
-- Error handler.
do
create Result.make
end
feature -- Access: Users
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
-- CMS User with Oauth credential by id if any.
do
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
-- -- CMS User with Oauth credential by access token `a_token' if any.
do
end
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
do
end
feature -- Access: Consumers
oauth2_consumers: LIST [STRING]
do
create {ARRAYED_LIST[STRING]} Result.make (0)
end
oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by name `a_name', if any.
do
end
oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by callback `a_callback', if any.
do
end
feature -- Change: User Oauth2
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
-- Add a new user with oauth2 authentication.
do
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 )
-- Update user `a_user' with oauth2 authentication.
do
end
end

View File

@@ -0,0 +1,293 @@
note
description: "Summary description for {CMS_OAUTH_20_STORAGE_SQL}."
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_STORAGE_SQL
inherit
CMS_OAUTH_20_STORAGE_I
CMS_PROXY_STORAGE_SQL
CMS_OAUTH_20_STORAGE_I
CMS_STORAGE_SQL_I
REFACTORING_HELPER
create
make
feature -- Access User Outh
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
local
l_list: LIST[STRING]
do
error_handler.reset
write_information_log (generator + ".user_oauth2_without_consumer_by_token")
l_list := oauth2_consumers
from
l_list.start
until
l_list.after or attached Result
loop
if attached {CMS_USER} user_oauth2_by_token (a_token, l_list.item) as l_user then
Result := l_user
end
l_list.forth
end
end
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING
do
error_handler.reset
write_information_log (generator + ".user_oauth2_by_id")
create l_parameters.make (1)
l_parameters.put (a_uid, "uid")
create l_string.make_from_string (select_user_oauth2_template_by_id)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
sql_query (l_string, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
else
check no_more_than_one: sql_rows_count = 0 end
end
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING
do
error_handler.reset
write_information_log (generator + ".user_by_oauth2_token")
create l_parameters.make (1)
l_parameters.put (a_token, "token")
create l_string.make_from_string (select_user_by_oauth2_template_token)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
sql_query (l_string, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
else
check no_more_than_one: sql_rows_count = 0 end
end
end
feature --Access: Consumers
oauth2_consumers: LIST[STRING]
-- Return a list of consumers, or empty
do
error_handler.reset
create {ARRAYED_LIST[STRING]}Result.make (0)
write_information_log (generator + ".user_by_oauth2_token")
sql_query (Sql_oauth_consumers, Void)
if not has_error then
from
sql_start
until
sql_after
loop
if attached sql_read_string (1) as l_name then
Result.force (l_name)
end
sql_forth
end
end
end
oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by name `a_name', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".oauth_consumer_by_name")
create l_parameters.make (1)
l_parameters.put (a_name, "name")
sql_query (sql_oauth_consumer_name, l_parameters)
if sql_rows_count = 1 then
Result := fetch_consumer
else
check no_more_than_one: sql_rows_count = 0 end
end
end
oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
-- Retrieve a consumer by callback `a_callback', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".oauth_consumer_by_callback")
create l_parameters.make (1)
l_parameters.put (a_callback, "name")
sql_query (sql_oauth_consumer_callback, l_parameters)
if sql_rows_count = 1 then
Result := fetch_consumer
else
check no_more_than_one: sql_rows_count = 0 end
end
end
feature -- Change: User OAuth
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32)
-- Add a new user with oauth2 authentication.
-- <Precursor>.
local
l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING
do
error_handler.reset
sql_begin_transaction
write_information_log (generator + ".new_user_oauth2")
create l_parameters.make (4)
l_parameters.put (a_user.id, "uid")
l_parameters.put (a_token, "token")
l_parameters.put (a_user_profile, "profile")
l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date")
create l_string.make_from_string (sql_insert_oauth2_template)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
sql_change (l_string, l_parameters)
sql_commit_transaction
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32 )
-- Update user `a_user' with oauth2 authentication.
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING
do
error_handler.reset
sql_begin_transaction
write_information_log (generator + ".new_user_oauth2")
create l_parameters.make (4)
l_parameters.put (a_user.id, "uid")
l_parameters.put (a_token, "token")
l_parameters.put (a_user_profile, "profile")
create l_string.make_from_string (sql_update_oauth2_template)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
sql_change (l_string, l_parameters)
sql_commit_transaction
end
feature {NONE} -- Implementation OAuth Consumer
fetch_consumer: detachable CMS_OAUTH_20_CONSUMER
do
if attached sql_read_integer_64 (1) as l_id then
create Result
Result.set_id (l_id)
end
if Result /= Void then
if attached sql_read_string_32 (2) as l_name then
Result.set_name (l_name)
end
if attached sql_read_string_32 (3) as l_api_secret then
Result.set_api_secret (l_api_secret)
end
if attached sql_read_string_32 (4) as l_api_key then
Result.set_api_key (l_api_key)
end
if attached sql_read_string_32 (5) as l_scope then
Result.set_scope (l_scope)
end
if attached sql_read_string_32 (6) as l_resource_url then
Result.set_protected_resource_url (l_resource_url)
end
if attached sql_read_string_32 (7) as l_callback_name then
Result.set_callback_name (l_callback_name)
end
if attached sql_read_string_32 (8) as l_extractor then
Result.set_extractor (l_extractor)
end
if attached sql_read_string_32 (9) as l_authorize_url then
Result.set_authorize_url (l_authorize_url)
end
if attached sql_read_string_32 (10) as l_endpoint then
Result.set_endpoint (l_endpoint)
end
end
end
feature {NONE} -- Implementation: User
fetch_user: detachable CMS_USER
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_32 (1) as i then
l_id := i
end
if attached sql_read_string_32 (2) as s and then not s.is_whitespace then
l_name := s
end
if l_name /= Void then
create Result.make (l_name)
if l_id > 0 then
Result.set_id (l_id)
end
elseif l_id > 0 then
create Result.make_with_id (l_id)
end
if Result /= Void then
if attached sql_read_string (3) as l_password then
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
if attached sql_read_integer_32 (6) as l_status then
Result.set_status (l_status)
end
else
check expected_valid_user: False end
end
end
feature -- {NONE} User OAuth2
sql_table_name (a_consumer: READABLE_STRING_8): STRING_8
do
Result := Sql_table_prefix.twin
Result.append (a_consumer)
end
Select_user_by_oauth2_template_token: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.access_token = :token;"
Select_user_oauth2_template_by_id: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.uid = :uid;"
Sql_insert_oauth2_template: STRING = "INSERT INTO $table_name (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);"
Sql_update_oauth2_template: STRING = "UPDATE $table_name SET access_token = :token, details = :profile WHERE uid =:uid;"
Sql_oauth_consumers: STRING = "SELECT name FROM oauth2_consumers";
Sql_table_prefix: STRING = "oauth2_"
feature -- {NONE} Consumer
Sql_oauth_consumer_callback: STRING ="SELECT * FROM oauth2_consumers where callback_name =:name;"
Sql_oauth_consumer_name: STRING ="SELECT * FROM oauth2_consumers where name =:name;"
end