Files
ROC/src/persistence/user/cms_user_storage_sql_i.e
jvelilla e188625c43 Update Login Module.
- Added an API to mange user OAuth authentication.
   - Updated the Filter to use the new API.
   - Updated the Module to initialize if it needed the storages needed by the login module.
   - Updated gmail callback to use the new API.
   - Added a Persistence Layer

CMS_USER_API
   - clean api and related persistence code.
2015-06-08 18:32:34 -03:00

757 lines
22 KiB
Plaintext

note
description: "Summary description for {CMS_USER_STORAGE_SQL_I}."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
deferred class
CMS_USER_STORAGE_SQL_I
inherit
CMS_USER_STORAGE_I
CMS_STORAGE_SQL_I
REFACTORING_HELPER
SHARED_LOGGER
feature -- Access: user
has_user: BOOLEAN
-- Has any user?
do
Result := user_count > 0
end
user_count: INTEGER
-- Number of items users.
do
error_handler.reset
write_information_log (generator + ".user_count")
sql_query (select_users_count, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_32 (1)
end
error_handler.reset
end
users: LIST [CMS_USER]
do
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
error_handler.reset
write_information_log (generator + ".all_users")
from
sql_query (select_users, Void)
sql_start
until
sql_after
loop
if attached fetch_user as l_user then
Result.force (l_user)
end
sql_forth
end
end
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
-- User for the given id `a_id', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
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)
if sql_rows_count = 1 then
Result := fetch_user
else
check no_more_than_one: sql_rows_count = 0 end
end
end
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
-- User for the given name `a_name', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".user_by_name")
create l_parameters.make (1)
l_parameters.put (a_name, "name")
sql_query (select_user_by_name, 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_email (a_email: like {CMS_USER}.email): detachable CMS_USER
-- User for the given email `a_email', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".user_by_email")
create l_parameters.make (1)
l_parameters.put (a_email, "email")
sql_query (select_user_by_email, 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_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
do
if attached user_salt (l_auth_login) as l_hash then
if attached user_by_name (l_auth_login) as l_user then
create l_security
if
attached l_user.hashed_password as l_hashed_password and then
l_security.password_hash (l_auth_password, l_hash).is_case_insensitive_equal (l_hashed_password)
then
Result := True
else
write_information_log (generator + ".is_valid_credential User: wrong username or password" )
end
else
write_information_log (generator + ".is_valid_credential User:" + l_auth_login + "does not exist" )
end
end
end
feature -- Change: user
new_user (a_user: CMS_USER)
-- Add a new user `a_user'.
local
l_parameters: STRING_TABLE [detachable ANY]
l_password_salt, l_password_hash: STRING
l_security: SECURITY_PROVIDER
do
error_handler.reset
if
attached a_user.password as l_password and then
attached a_user.email as l_email
then
sql_begin_transaction
create l_security
l_password_salt := l_security.salt
l_password_hash := l_security.password_hash (l_password, l_password_salt)
write_information_log (generator + ".new_user")
create l_parameters.make (4)
l_parameters.put (a_user.name, "name")
l_parameters.put (l_password_hash, "password")
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
a_user.set_id (last_inserted_user_id)
end
sql_commit_transaction
else
-- set error
error_handler.add_custom_error (-1, "bad request" , "Missing password or email")
end
end
update_user (a_user: CMS_USER)
-- Save user `a_user'.
local
l_parameters: STRING_TABLE [detachable ANY]
l_password_salt, l_password_hash: detachable READABLE_STRING_8
l_security: SECURITY_PROVIDER
do
error_handler.reset
if attached a_user.password as l_password then
-- New password!
create l_security
l_password_salt := l_security.salt
l_password_hash := l_security.password_hash (l_password, l_password_salt)
else
-- Existing hashed password
l_password_hash := a_user.hashed_password
l_password_salt := user_salt (a_user.name)
end
if
l_password_hash /= Void and l_password_salt /= Void and
attached a_user.email as l_email
then
write_information_log (generator + ".update_user")
create l_parameters.make (6)
l_parameters.put (a_user.id, "uid")
l_parameters.put (a_user.name, "name")
l_parameters.put (l_password_hash, "password")
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
-- set error
error_handler.add_custom_error (-1, "bad request" , "Missing password or email")
end
end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".user_role_by_id")
create l_parameters.make (1)
l_parameters.put (a_id, "rid")
sql_query (select_user_role_by_id, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user_role
if Result /= Void and not has_error then
fill_user_role (Result)
end
else
check no_more_than_one: sql_rows_count = 0 end
end
end
user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE]
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".user_roles_for")
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
from
create l_parameters.make (1)
l_parameters.put (a_user.id, "uid")
sql_query (select_user_roles_by_user_id, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_user_role as l_role then
Result.force (l_role)
end
sql_forth
end
if not has_error then
across Result as ic loop
fill_user_role (ic.item)
end
end
end
user_roles: LIST [CMS_USER_ROLE]
local
l_role: detachable CMS_USER_ROLE
do
error_handler.reset
write_information_log (generator + ".user_roles")
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
from
sql_query (select_user_roles, Void)
sql_start
until
sql_after
loop
l_role := fetch_user_role
if l_role /= Void then
Result.force (l_role)
end
sql_forth
end
if not has_error then
across Result as ic loop
fill_user_role (ic.item)
end
end
end
fill_user_role (a_role: CMS_USER_ROLE)
require
a_role_has_id: a_role.has_id
do
if attached role_permissions_by_id (a_role.id) as l_permissions then
across
l_permissions as p_ic
loop
a_role.add_permission (p_ic.item)
end
end
end
role_permissions_by_id (a_role_id: INTEGER_32): LIST [READABLE_STRING_8]
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".role_permissions_by_id")
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (0)
from
create l_parameters.make (1)
l_parameters.put (a_role_id, "rid")
sql_query (select_role_permissions_by_role_id, l_parameters)
sql_start
until
sql_after
loop
if attached sql_read_string (1) as l_permission then
Result.force (l_permission)
end
-- if attached sql_read_string_32 (2) as l_module then
-- end
sql_forth
end
end
feature -- Change: roles and permissions
save_user_role (a_user_role: CMS_USER_ROLE)
local
l_parameters: STRING_TABLE [detachable ANY]
l_existing_role: detachable CMS_USER_ROLE
l_permissions: detachable LIST [READABLE_STRING_8]
p: READABLE_STRING_8
l_found: BOOLEAN
do
error_handler.reset
write_information_log (generator + ".save_user_role")
if a_user_role.has_id then
l_existing_role := user_role_by_id (a_user_role.id)
check l_existing_role /= Void and then l_existing_role.id = a_user_role.id end
if
l_existing_role /= Void and then
l_existing_role.name.same_string (a_user_role.name)
then
-- No update needed
else
create l_parameters.make (2)
l_parameters.put (a_user_role.id, "rid")
l_parameters.put (a_user_role.name, "name")
sql_change (sql_update_user_role, l_parameters)
end
if not a_user_role.permissions.is_empty then
-- FIXME: check if this is non set permissions,or none ...
if l_existing_role /= Void then
l_permissions := l_existing_role.permissions
fill_user_role (l_existing_role)
end
if l_permissions = Void or else l_permissions.is_empty then
l_permissions := role_permissions_by_id (a_user_role.id)
end
across
a_user_role.permissions as ic
loop
p := ic.item
from
l_found := False
l_permissions.start
until
l_found or l_permissions.after
loop
if p.is_case_insensitive_equal (l_permissions.item) then
l_found := True
else
l_permissions.forth
end
end
if l_found then
-- Already there, skip
else
-- Add permission
set_permission_for_role_id (p, a_user_role.id)
end
end
-- Remove other
across
l_permissions as ic
loop
unset_permission_for_role_id (ic.item, a_user_role.id)
end
end
else
create l_parameters.make (1)
l_parameters.put (a_user_role.name, "name")
sql_change (sql_insert_user_role, l_parameters)
if not error_handler.has_error then
a_user_role.set_id (last_inserted_user_role_id)
across
a_user_role.permissions as ic
loop
set_permission_for_role_id (ic.item, a_user_role.id)
end
end
end
end
set_permission_for_role_id (a_permission: READABLE_STRING_8; a_role_id: INTEGER)
require
a_role_id > 0
not a_permission.is_whitespace
local
l_parameters: STRING_TABLE [detachable ANY]
do
create l_parameters.make (3)
l_parameters.put (a_role_id, "rid")
l_parameters.put (a_permission, "permission")
l_parameters.put (Void, "module") -- FIXME: unsupported for now!
sql_change (sql_insert_user_role_permission, l_parameters)
end
unset_permission_for_role_id (a_permission: READABLE_STRING_8; a_role_id: INTEGER)
require
a_role_id > 0
not a_permission.is_whitespace
local
l_parameters: STRING_TABLE [detachable ANY]
do
create l_parameters.make (2)
l_parameters.put (a_role_id, "rid")
l_parameters.put (a_permission, "permission")
sql_change (sql_delete_user_role_permission, l_parameters)
end
last_inserted_user_role_id: INTEGER_32
-- Last inserted user role id.
do
error_handler.reset
write_information_log (generator + ".last_inserted_user_role_id")
sql_query (Sql_last_insert_user_role_id, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_32 (1)
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
-- User salt for the given user `a_username', if any.
local
l_parameters: STRING_TABLE [detachable ANY]
do
error_handler.reset
write_information_log (generator + ".user_salt")
create l_parameters.make (1)
l_parameters.put (a_username, "name")
sql_query (select_salt_by_username, l_parameters)
if sql_rows_count = 1 then
if attached sql_read_string (1) as l_salt then
Result := l_salt
end
end
end
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
last_inserted_user_id: INTEGER_64
-- Last insert user id.
do
error_handler.reset
write_information_log (generator + ".last_inserted_user_id")
sql_query (Sql_last_insert_user_id, Void)
if sql_rows_count = 1 then
Result := sql_read_integer_64 (1)
end
end
feature {NONE} -- Implementation: User role
fetch_user_role: detachable CMS_USER_ROLE
local
l_id: INTEGER_32
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_32 (1) as rid then
l_id := rid
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
end
feature {NONE} -- Sql Queries: USER
Select_users_count: STRING = "SELECT count(*) FROM Users;"
-- Number of users.
Sql_last_insert_user_id: STRING = "SELECT MAX(uid) FROM Users;"
Select_users: STRING = "SELECT * FROM Users;"
-- List of users.
Select_user_by_id: STRING = "SELECT * FROM Users WHERE uid =:uid;"
-- Retrieve user by id if exists.
Select_user_by_name: STRING = "SELECT * FROM Users WHERE name =:name;"
-- Retrieve user by name if exists.
Select_user_by_email: STRING = "SELECT * FROM Users WHERE email =:email;"
-- Retrieve user by email if exists.
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, 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, status=:status WHERE uid=:uid;"
-- SQL update to update an existing user.
feature {NONE} -- Sql Queries: USER ROLE
sql_last_insert_user_role_id: STRING = "SELECT MAX(rid) FROM roles;"
select_user_roles: STRING = "SELECT rid, name FROM roles;"
-- List of user roles.
sql_insert_user_role: STRING = "INSERT INTO roles (name) VALUES (:name);"
-- SQL Insert to add a new user role with name :name.
sql_update_user_role : STRING = "UPDATE roles SET name=:name WHERE rid=:rid;"
-- Update user role with id :rid.
select_user_roles_by_user_id: STRING = "SELECT rid, name FROM roles INNER JOIN users_roles ON users_roles.rid=roles.rid WHERE users_roles.uid=:uid;"
-- List of user roles for user id :uid.
select_user_role_by_id: STRING = "SELECT rid, name FROM roles WHERE rid=:rid;"
-- User role for role id :rid;
sql_insert_user_role_permission: STRING = "INSERT INTO role_permissions (rid, permission, module) VALUES (:rid, :permission, :module);"
-- SQL Insert a new permission :permission for user role :rid.
sql_delete_user_role_permission: STRING = "DELETE FROM role_permissions WHERE rid=:rid AND permission=:permission;"
-- SQL Delete permission :permission from user role :rid.
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} -- User Password Recovery
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