Files
ROC/modules/session_auth/cms_session_auth_module.e
Jocelyn Fiat bd3fe63976 Fixed "destination" support when login.
(i.e when visitor click on signin from page A, one he is signed, he will be redirected to the initial page A.)
2016-04-13 10:56:28 +02:00

312 lines
9.0 KiB
Plaintext

note
description: "[
This module allows the use Session Based Authentication using Cookies to restrict access
by looking up users in the given providers.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_SESSION_AUTH_MODULE
inherit
CMS_AUTH_MODULE_I
rename
module_api as session_api
redefine
make,
filters,
setup_hooks,
initialize,
install,
session_api
end
CMS_HOOK_BLOCK
create
make
feature {NONE} -- Initialization
make
do
Precursor
version := "1.0"
description := "Service to manage cookie based authentication"
end
feature -- Access
name: STRING = "session_auth"
feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API)
-- <Precursor>
local
l_session_auth_api: like session_api
l_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I
do
Precursor (a_api)
-- Storage initialization
if attached a_api.storage.as_sql_storage as l_storage_sql then
create {CMS_SESSION_AUTH_STORAGE_SQL} l_session_auth_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_SESSION_AUTH_STORAGE_NULL} l_session_auth_storage
end
-- API initialization
create l_session_auth_api.make_with_storage (a_api, l_session_auth_storage)
session_api := l_session_auth_api
ensure then
session_auth_api_set: session_api /= Void
end
feature {CMS_API} -- Module management
install (api: CMS_API)
do
-- Schema
if attached api.storage.as_sql_storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install.sql")), Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
else
Precursor {CMS_AUTH_MODULE_I} (api) -- Mark it as installed.
end
end
end
feature {CMS_API} -- Access: API
session_api: detachable CMS_SESSION_API
-- <Precursor>
feature -- Access: auth strategy
login_title: STRING = "Session"
-- Module specific login title.
login_location: STRING = "account/auth/roc-session-login"
logout_location: STRING = "account/auth/roc-session-logout"
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- <Precursor>
do
if
a_response.is_authenticated and then
attached session_api as l_session_api and then
attached a_response.request.cookie (l_session_api.session_token)
then
Result := True
end
end
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
if attached session_api as l_session_api then
a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/" + logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_session_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/auth/roc-session-login", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,session_api, ?, ?)), a_router.methods_get_post)
end
end
feature -- Access: filter
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
-- Possibly list of Filter's module.
do
if attached session_api as l_session_api then
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
Result.extend (create {CMS_SESSION_AUTH_FILTER}.make (a_api, l_session_api))
end
end
feature {NONE} -- Implementation: routes
handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
vals: CMS_VALUE_TABLE
r: CMS_RESPONSE
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if api.user_is_authenticated then
r.add_error_message ("You are already signed in!")
else
if attached smarty_template_login_block (req, Current, "login", api) as l_tpl_block then
create vals.make (1)
-- add the variable to the block
l_tpl_block.set_value (api.user, "user")
l_tpl_block.set_value (r.url ("", Void), "site_url")
api.hooks.invoke_value_table_alter (vals, r)
across
vals as ic
loop
l_tpl_block.set_value (ic.item, ic.key)
end
r.add_block (l_tpl_block, "content")
else
r.add_error_message ("Error: missing block [login]")
end
end
r.execute
end
handle_logout (api: CMS_API; a_session_api: CMS_SESSION_API ; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_cookie: WSF_COOKIE
tok: STRING
do
tok := a_session_api.session_token
if
attached {WSF_STRING} req.cookie (tok) as l_cookie_token and then
attached api.user as l_user
then
-- Logout Session
create l_cookie.make (tok, l_cookie_token.value) -- FIXME: unicode issue?
l_cookie.set_path ("/")
l_cookie.set_max_age (-1)
res.add_cookie (l_cookie)
api.unset_user
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
fixme (generator + ": missing else implementation in handle_logout!")
end
end
handle_login_with_session (api: CMS_API; a_session_api: detachable CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_token: STRING
l_cookie: WSF_COOKIE
do
if
attached a_session_api as l_session_api and then
attached {WSF_STRING} req.form_parameter ("username") as l_username and then
attached {WSF_STRING} req.form_parameter ("password") as l_password
then
if
api.user_api.is_valid_credential (l_username.value, l_password.value) and then
attached api.user_api.user_by_name (l_username.value) as l_user
then
l_token := generate_token
if a_session_api.has_user_token (l_user) then
l_session_api.update_user_session_auth (l_token, l_user)
else
l_session_api.new_user_session_auth (l_token, l_user)
end
create l_cookie.make (a_session_api.session_token, l_token)
l_cookie.set_max_age (a_session_api.session_max_age)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
api.set_user (l_user)
api.record_user_login (l_user)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.item ("destination") as p_destination then
r.set_redirection (p_destination.url_encoded_value)
else
r.set_redirection ("")
end
else
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached smarty_template_login_block (req, Current, "login", api) as l_tpl_block then
l_tpl_block.set_value (l_username.value, "username")
l_tpl_block.set_value ("Wrong: Username or password ", "error")
r.add_block (l_tpl_block, "content")
end
end
r.execute
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
if attached smarty_template_login_block (req, Current, "login", api) as l_tpl_block then
if attached {WSF_STRING} req.form_parameter ("username") as l_username then
l_tpl_block.set_value (l_username.value, "username")
end
l_tpl_block.set_value ("Wrong: Username or password ", "error")
r.add_block (l_tpl_block, "content")
end
r.execute
end
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
Precursor (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
end
feature -- Hooks
block_list: ITERABLE [like {CMS_BLOCK}.name]
do
Result := <<"?login">>
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") then
get_block_view_login (a_block_id, a_response)
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 smarty_template_login_block (a_response.request, Current, a_block_id, a_response.api) as l_tpl_block then
create vals.make (1)
-- add the variable to the block
a_response.api.hooks.invoke_value_table_alter (vals, a_response)
across
vals as ic
loop
l_tpl_block.set_value (ic.item, ic.key)
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
generate_token: STRING
-- Generate token to use in a Session.
local
l_token: STRING
l_security: CMS_TOKEN_GENERATOR
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
end