Updated CMS with Login Module.
-- The module handle basic_auth (at the moment).
-- Handle login, logout, register user, activate/reactivate an account, password recovery.
-- Send notification emails.
CMS Updates
-- Added a new service: email.
-- Updated Basic Auth Module to handle logout based on the browser type.
-- Updated persistence layer to save and remove and query activation token and password token.
-- Updated CMS_USER to handle status {active, not_active, trashed}.
-- Updated MySQL scripts to be in sync with SQLite scripts
This commit is contained in:
@@ -36,6 +36,7 @@ feature -- Initialization
|
||||
create u.make ("admin")
|
||||
u.set_password ("istrator#")
|
||||
u.set_email (a_setup.site_email)
|
||||
u.set_status ({CMS_USER}.active)
|
||||
a_storage.new_user (u)
|
||||
|
||||
--| Node
|
||||
@@ -74,16 +75,19 @@ feature -- Initialization
|
||||
create u.make ("auth")
|
||||
u.set_password ("enticated#")
|
||||
u.set_email (a_setup.site_email)
|
||||
u.set_status ({CMS_USER}.active)
|
||||
a_storage.new_user (u)
|
||||
|
||||
create u.make ("test")
|
||||
u.set_password ("test#")
|
||||
u.set_email (a_setup.site_email)
|
||||
u.set_status ({CMS_USER}.active)
|
||||
a_storage.new_user (u)
|
||||
|
||||
create u.make ("view")
|
||||
u.set_password ("only#")
|
||||
u.set_email (a_setup.site_email)
|
||||
u.set_status ({CMS_USER}.active)
|
||||
u.set_roles (l_roles)
|
||||
a_storage.new_user (u)
|
||||
end
|
||||
|
||||
@@ -56,6 +56,20 @@ feature -- Access
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User with activation token `a_token', if any.
|
||||
deferred
|
||||
ensure
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User with password token `a_token', if any.
|
||||
deferred
|
||||
ensure
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
is_valid_credential (a_u, a_p: READABLE_STRING_32): BOOLEAN
|
||||
-- Does account with username `a_username' and password `a_password' exist?
|
||||
deferred
|
||||
@@ -141,4 +155,27 @@ feature -- Change: roles and permissions
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Change: User activation
|
||||
|
||||
save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <Precursor>.
|
||||
deferred
|
||||
end
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Change: User password recovery
|
||||
|
||||
save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <Precursor>.
|
||||
deferred
|
||||
end
|
||||
|
||||
remove_password (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
deferred
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,6 +34,14 @@ feature -- Access: user
|
||||
do
|
||||
end
|
||||
|
||||
user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
do
|
||||
end
|
||||
|
||||
user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
do
|
||||
end
|
||||
|
||||
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN
|
||||
do
|
||||
end
|
||||
@@ -76,4 +84,27 @@ feature -- Change: roles and permissions
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Change: User activation
|
||||
|
||||
save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <Precursor>.
|
||||
do
|
||||
end
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Change: User password recovery
|
||||
|
||||
save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <Precursor>.
|
||||
do
|
||||
end
|
||||
|
||||
remove_password (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
do
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,7 +62,7 @@ feature -- Access: user
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".user")
|
||||
write_information_log (generator + ".user_by_id")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_id, "uid")
|
||||
sql_query (select_user_by_id, l_parameters)
|
||||
@@ -107,6 +107,40 @@ feature -- Access: user
|
||||
end
|
||||
end
|
||||
|
||||
user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User for the given activation token `a_token', if any.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".user_by_activation_token")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_query (select_user_by_activation_token, 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_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User for the given password token `a_token', if any.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".user_by_password_token")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_query (select_user_by_password_token, l_parameters)
|
||||
if sql_rows_count = 1 then
|
||||
Result := fetch_user
|
||||
else
|
||||
check no_more_than_one: sql_rows_count = 0 end
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN
|
||||
local
|
||||
l_security: SECURITY_PROVIDER
|
||||
@@ -155,6 +189,7 @@ feature -- Change: user
|
||||
l_parameters.put (l_password_salt, "salt")
|
||||
l_parameters.put (l_email, "email")
|
||||
l_parameters.put (create {DATE_TIME}.make_now_utc, "created")
|
||||
l_parameters.put (a_user.status, "status")
|
||||
|
||||
sql_change (sql_insert_user, l_parameters)
|
||||
if not error_handler.has_error then
|
||||
@@ -197,6 +232,7 @@ feature -- Change: user
|
||||
l_parameters.put (l_password_salt, "salt")
|
||||
l_parameters.put (l_email, "email")
|
||||
l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
|
||||
l_parameters.put (a_user.status, "status")
|
||||
|
||||
sql_change (sql_update_user, l_parameters)
|
||||
else
|
||||
@@ -441,6 +477,108 @@ feature -- Change: roles and permissions
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- Access: User activation
|
||||
|
||||
activation_elapsed_time (a_token: READABLE_STRING_32): INTEGER_32
|
||||
-- amount of time that has passed in days since the token `a_token' was saved.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".activation_elapsed_time")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_query (sql_select_activation_expiration, l_parameters)
|
||||
if sql_rows_count = 1 then
|
||||
Result := sql_read_integer_32 (1)
|
||||
end
|
||||
end
|
||||
|
||||
user_id_by_activation (a_token: READABLE_STRING_32): INTEGER_64
|
||||
-- User id associatied with a token `a_token', if any.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
write_information_log (generator + ".user_id_by_actication")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_query (sql_select_userid_activation, l_parameters)
|
||||
if sql_rows_count = 1 then
|
||||
Result := sql_read_integer_32 (1)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change: User activation
|
||||
|
||||
save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_utc_date: DATE_TIME
|
||||
do
|
||||
error_handler.reset
|
||||
sql_begin_transaction
|
||||
write_information_log (generator + ".save_activation")
|
||||
create l_utc_date.make_now_utc
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_token, "token")
|
||||
l_parameters.put (a_id, "uid")
|
||||
l_parameters.put (l_utc_date, "utc_date")
|
||||
sql_change (sql_insert_activation, l_parameters)
|
||||
sql_commit_transaction
|
||||
end
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
sql_begin_transaction
|
||||
write_information_log (generator + ".remove_activation")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_change (sql_remove_activation, l_parameters)
|
||||
sql_commit_transaction
|
||||
end
|
||||
|
||||
feature -- Change: User password recovery
|
||||
|
||||
save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_utc_date: DATE_TIME
|
||||
do
|
||||
error_handler.reset
|
||||
sql_begin_transaction
|
||||
write_information_log (generator + ".save_password")
|
||||
create l_utc_date.make_now_utc
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_token, "token")
|
||||
l_parameters.put (a_id, "uid")
|
||||
l_parameters.put (l_utc_date, "utc_date")
|
||||
sql_change (sql_insert_password, l_parameters)
|
||||
sql_commit_transaction
|
||||
end
|
||||
|
||||
remove_password (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
sql_begin_transaction
|
||||
write_information_log (generator + ".remove_password")
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_token, "token")
|
||||
sql_change (sql_remove_password, l_parameters)
|
||||
sql_commit_transaction
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Implementation: User
|
||||
|
||||
user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_8
|
||||
@@ -489,6 +627,9 @@ feature {NONE} -- Implementation: User
|
||||
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
|
||||
@@ -551,10 +692,11 @@ feature {NONE} -- Sql Queries: USER
|
||||
Select_salt_by_username: STRING = "SELECT salt FROM Users WHERE name =:name;"
|
||||
-- Retrieve salt by username if exists.
|
||||
|
||||
sql_insert_user: STRING = "INSERT INTO users (name, password, salt, email, created) VALUES (:name, :password, :salt, :email, :created);"
|
||||
-- SQL Insert to add a new node.
|
||||
sql_insert_user: STRING = "INSERT INTO users (name, password, salt, email, created, status) VALUES (:name, :password, :salt, :email, :created, :status);"
|
||||
-- SQL Insert to add a new user.
|
||||
|
||||
sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email WHERE uid=:uid;"
|
||||
sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email, status=:status WHERE uid=:uid;"
|
||||
-- SQL update to update an existing user.
|
||||
|
||||
feature {NONE} -- Sql Queries: USER ROLE
|
||||
|
||||
@@ -584,4 +726,34 @@ feature {NONE} -- Sql Queries: USER ROLE
|
||||
select_role_permissions_by_role_id: STRING = "SELECT permission, module FROM role_permissions WHERE rid=:rid;"
|
||||
-- User role permissions for role id :rid;
|
||||
|
||||
feature {NONE} -- Sql Queries: USER ACTIVATION
|
||||
|
||||
sql_insert_activation: STRING = "INSERT INTO users_activations (token, uid, created) VALUES (:token, :uid, :utc_date);"
|
||||
-- SQL insert a new activation :token.
|
||||
|
||||
sql_select_activation_expiration: STRING = "SELECT DATEDIFF(day,created,UTC_DATE()) FROM users_activations where token = :token;"
|
||||
-- elapsed time that has passed in days since the token `a_token' was saved.
|
||||
|
||||
sql_select_userid_activation: STRING = "SELECT uid FROM users_activations where token = :token;"
|
||||
-- Retrieve userid given the activation token.
|
||||
|
||||
Select_user_by_activation_token: STRING = "SELECT u.* FROM users as u JOIN users_activations as ua ON ua.uid = u.uid and ua.token = :token;"
|
||||
-- Retrieve user by activation token if exist.
|
||||
|
||||
Sql_remove_activation: STRING = "DELETE FROM users_activations WHERE token = :token;"
|
||||
-- Remove activation token.
|
||||
|
||||
feature {NONE}
|
||||
|
||||
sql_insert_password: STRING = "INSERT INTO users_password_recovery (token, uid, created) VALUES (:token, :uid, :utc_date);"
|
||||
-- SQL insert a new password recovery :token.
|
||||
|
||||
Sql_remove_password: STRING = "DELETE FROM users_password_recovery WHERE token = :token;"
|
||||
-- Retrieve password if exist.
|
||||
|
||||
Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;"
|
||||
-- Retrieve user by password token if exist.
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
102
src/service/email/email_service.e
Normal file
102
src/service/email/email_service.e
Normal file
@@ -0,0 +1,102 @@
|
||||
note
|
||||
description: "Basic Email Service"
|
||||
date: "$Date: 2015-04-30 05:45:25 -0300 (ju. 30 de abr. de 2015) $"
|
||||
revision: "$Revision: 97218 $"
|
||||
|
||||
class
|
||||
EMAIL_SERVICE
|
||||
|
||||
inherit
|
||||
|
||||
SHARED_ERROR
|
||||
SHARED_LOGGER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_params: like parameters)
|
||||
-- Create instance of {EMAIL_SERVICE} with smtp_server `a_params.smtp_server'.
|
||||
-- Using `a_params.admin_email' as admin email.
|
||||
do
|
||||
parameters := a_params
|
||||
initialize
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize service.
|
||||
local
|
||||
l_address_factory: INET_ADDRESS_FACTORY
|
||||
do
|
||||
admin_email := parameters.admin_email
|
||||
|
||||
-- Get local host name needed in creation of SMTP_PROTOCOL.
|
||||
create l_address_factory
|
||||
create smtp_protocol.make (parameters.smtp_server, l_address_factory.create_localhost.host_name)
|
||||
set_successful
|
||||
end
|
||||
|
||||
parameters: EMAIL_SERVICE_PARAMETERS
|
||||
-- Associated parameters.
|
||||
|
||||
admin_email: IMMUTABLE_STRING_8
|
||||
-- Site admin's email.
|
||||
|
||||
smtp_protocol: SMTP_PROTOCOL
|
||||
-- SMTP protocol.
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
send_internal_email (a_content: READABLE_STRING_GENERAL)
|
||||
do
|
||||
send_message (admin_email, admin_email, "Notification Contact", a_content)
|
||||
end
|
||||
|
||||
send_email_internal_server_error (a_content: READABLE_STRING_GENERAL)
|
||||
do
|
||||
send_message (admin_email, admin_email, "Internal Server Error", a_content)
|
||||
end
|
||||
|
||||
send_message (a_from_address, a_to_address: READABLE_STRING_8; a_subjet: READABLE_STRING_GENERAL; a_content: READABLE_STRING_GENERAL)
|
||||
local
|
||||
l_email: EMAIL
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
write_debug_log (generator + ".send_message: [from:" + a_from_address + ", to:" + a_to_address + ", subject:" + a_subjet + ", content:" + a_content)
|
||||
create l_email.make_with_entry (a_from_address, a_to_address)
|
||||
l_email.set_message (utf.escaped_utf_32_string_to_utf_8_string_8 (a_content))
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet))
|
||||
l_email.add_header_entry ("MIME-Version:", "1.0")
|
||||
l_email.add_header_entry ("Content-Type", "text/html; charset=utf-8")
|
||||
send_email (l_email)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send_email (a_email: EMAIL)
|
||||
-- Send the email represented by `a_email'.
|
||||
local
|
||||
l_retried: BOOLEAN
|
||||
do
|
||||
if not l_retried then
|
||||
write_information_log (generator + ".send_email Process send email.")
|
||||
smtp_protocol.initiate_protocol
|
||||
smtp_protocol.transfer (a_email)
|
||||
smtp_protocol.close_protocol
|
||||
write_information_log (generator + ".send_email Email sent.")
|
||||
if smtp_protocol.error then
|
||||
set_last_error ("smtp_protocol reported an error", generator + ".send_email")
|
||||
else
|
||||
set_successful
|
||||
end
|
||||
else
|
||||
write_error_log (generator + ".send_email Email not send " + last_error_message )
|
||||
end
|
||||
rescue
|
||||
set_last_error_from_exception (generator + ".send_email")
|
||||
l_retried := True
|
||||
retry
|
||||
end
|
||||
|
||||
end
|
||||
20
src/service/email/email_service_parameters.e
Normal file
20
src/service/email/email_service_parameters.e
Normal file
@@ -0,0 +1,20 @@
|
||||
note
|
||||
description: "Basic Email Service customized for cms site"
|
||||
author: ""
|
||||
date: "$Date: 2015-01-16 07:17:14 -0300 (vi. 16 de ene. de 2015) $"
|
||||
revision: "$Revision: 96467 $"
|
||||
|
||||
deferred class
|
||||
EMAIL_SERVICE_PARAMETERS
|
||||
|
||||
feature -- Access
|
||||
|
||||
smtp_server: IMMUTABLE_STRING_8
|
||||
deferred
|
||||
end
|
||||
|
||||
admin_email: IMMUTABLE_STRING_8
|
||||
deferred
|
||||
end
|
||||
|
||||
end
|
||||
@@ -29,6 +29,24 @@ feature -- Access
|
||||
Result := storage.user_by_name (a_username)
|
||||
end
|
||||
|
||||
user_by_email (a_email: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User by email `a_email', if any.
|
||||
do
|
||||
Result := storage.user_by_email (a_email)
|
||||
end
|
||||
|
||||
user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User by activation token `a_token'.
|
||||
do
|
||||
Result := storage.user_by_activation_token (a_token)
|
||||
end
|
||||
|
||||
user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User by password token `a_token'.
|
||||
do
|
||||
Result := storage.user_by_password_token (a_token)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid_credential (a_auth_login, a_auth_password: READABLE_STRING_32): BOOLEAN
|
||||
@@ -133,4 +151,43 @@ feature -- Change User
|
||||
storage.update_user (a_user)
|
||||
end
|
||||
|
||||
feature -- User Activation
|
||||
|
||||
new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- Save activation token `a_token', for the user with the id `a_id'.
|
||||
do
|
||||
storage.save_activation (a_token, a_id)
|
||||
end
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- Remove activation token `a_token', from the storage.
|
||||
do
|
||||
storage.remove_activation (a_token)
|
||||
end
|
||||
|
||||
feature -- User Password Recovery
|
||||
|
||||
new_password (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- Save password token `a_token', for the user with the id `a_id'.
|
||||
do
|
||||
storage.save_password (a_token, a_id)
|
||||
end
|
||||
|
||||
remove_password (a_token: READABLE_STRING_32)
|
||||
-- Remove password token `a_token', from the storage.
|
||||
do
|
||||
storage.remove_password (a_token)
|
||||
end
|
||||
|
||||
feature -- User status
|
||||
|
||||
not_active: INTEGER = 0
|
||||
-- The user is not active.
|
||||
|
||||
active: INTEGER = 1
|
||||
-- The user is active
|
||||
|
||||
Trashed: INTEGER = -1
|
||||
-- The user is trashed (soft delete), ready to be deleted/destroyed from storage.
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user