Added basic webapi system to ROC CMS system.
Added sql_delete routine to replace sql_modify with "DELETE FROM .." sql statement.
Fixed filter setup when a module has more than one filter.
Fixed filter setup for site,admin and webapi modes.
Added CMS_AUTH_FILTER, and check if user is already authenticated, then skip following auth filters.
Added specific webapi handler classes for root, user, access token, ...
Added user profile system to the core module.
Moved /user/{uid} from auth module to core module.
Added possibility to add html before and after a cms form. (useful to add a form before or after, as nested form are forbidden).
Now theme can be installed using roc install command.
This commit is contained in:
@@ -10,10 +10,17 @@ inherit
|
||||
CMS_MODULE
|
||||
redefine
|
||||
initialize,
|
||||
setup_hooks,
|
||||
install,
|
||||
permissions
|
||||
end
|
||||
|
||||
CMS_WITH_WEBAPI
|
||||
|
||||
CMS_HOOK_AUTO_REGISTER
|
||||
|
||||
CMS_HOOK_FORM_ALTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -41,10 +48,16 @@ feature {CMS_API} -- Module Initialization
|
||||
feature {CMS_API} -- Module management
|
||||
|
||||
install (a_api: CMS_API)
|
||||
local
|
||||
l_parent_loc: PATH
|
||||
do
|
||||
-- Schema
|
||||
if attached a_api.storage.as_sql_storage as l_sql_storage then
|
||||
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name + ".sql")), Void)
|
||||
l_parent_loc := a_api.module_resource_location (Current, create {PATH}.make_from_string ("scripts"))
|
||||
l_sql_storage.sql_execute_file_script (l_parent_loc.extended (name + ".sql"), Void)
|
||||
if not l_sql_storage.has_error then
|
||||
l_sql_storage.sql_execute_file_script (l_parent_loc.extended ("user_profile.sql"), Void)
|
||||
end
|
||||
|
||||
if l_sql_storage.has_error then
|
||||
a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
|
||||
@@ -81,6 +94,7 @@ feature {CMS_API} -- Module management
|
||||
--! external configuration file?
|
||||
--! at the moment we only have 1 admin to the whole site.
|
||||
--! is that ok?
|
||||
|
||||
l_anonymous_role.add_permission ("view any page")
|
||||
a_api.user_api.save_user_role (l_anonymous_role)
|
||||
|
||||
@@ -101,6 +115,7 @@ feature -- Router
|
||||
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
do
|
||||
a_router.handle ("/user/{uid}", create {CMS_USER_HANDLER}.make (a_api), a_router.methods_get)
|
||||
end
|
||||
|
||||
feature -- Security
|
||||
@@ -117,6 +132,54 @@ feature -- Security
|
||||
Result.force ("edit path_alias")
|
||||
end
|
||||
|
||||
feature {CMS_EXECUTION} -- Administration
|
||||
|
||||
webapi: CMS_CORE_MODULE_WEBAPI
|
||||
do
|
||||
create Result.make (Current)
|
||||
end
|
||||
|
||||
feature -- Hooks
|
||||
|
||||
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
|
||||
do
|
||||
a_hooks.subscribe_to_form_alter_hook (Current)
|
||||
end
|
||||
|
||||
feature -- Hook
|
||||
|
||||
form_alter (a_form: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_response: CMS_RESPONSE)
|
||||
-- Hook execution on form `a_form' and its associated data `a_form_data',
|
||||
-- for related response `a_response'.
|
||||
local
|
||||
fset: WSF_FORM_FIELD_SET
|
||||
tf: WSF_FORM_TEXT_INPUT
|
||||
do
|
||||
if
|
||||
attached a_form.id as fid and then
|
||||
fid.same_string ("roccms-user-view")
|
||||
then
|
||||
if
|
||||
attached a_response.user as u and then
|
||||
attached a_response.api.user_api as l_user_profile_api and then
|
||||
attached l_user_profile_api.user_profile (u) as l_profile and then
|
||||
not l_profile.is_empty
|
||||
then
|
||||
create fset.make
|
||||
fset.set_legend ("User-Profile")
|
||||
a_form.extend (fset)
|
||||
across
|
||||
l_profile as ic
|
||||
loop
|
||||
create tf.make_with_text (ic.key.to_string_32, ic.item)
|
||||
tf.set_label (ic.key.to_string_32)
|
||||
a_form.extend (tf)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
612
src/modules/core/cms_user_api.e
Normal file
612
src/modules/core/cms_user_api.e
Normal file
@@ -0,0 +1,612 @@
|
||||
note
|
||||
description: "API providing user related features."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_API
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
CMS_USER_PROFILE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
Precursor {CMS_MODULE_API}
|
||||
Precursor {CMS_USER_PROFILE_API}
|
||||
user_storage := storage
|
||||
end
|
||||
|
||||
feature -- Storage
|
||||
|
||||
user_storage: CMS_USER_STORAGE_I
|
||||
-- User storage.
|
||||
|
||||
feature -- Validation
|
||||
|
||||
is_valid_username (a_name: READABLE_STRING_32): BOOLEAN
|
||||
local
|
||||
c: CHARACTER_32
|
||||
do
|
||||
if a_name.is_empty or a_name.is_whitespace then
|
||||
Result := False
|
||||
elseif a_name[1].is_space then
|
||||
Result := False
|
||||
elseif a_name[a_name.count].is_space then
|
||||
Result := False
|
||||
else
|
||||
Result := True
|
||||
across
|
||||
a_name as ic
|
||||
until
|
||||
not Result
|
||||
loop
|
||||
c := ic.item
|
||||
if c.is_alpha_numeric or c = '-' or c = '_' then
|
||||
else
|
||||
Result := False
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_profile_name (a_name: READABLE_STRING_32): BOOLEAN
|
||||
local
|
||||
c: CHARACTER_32
|
||||
do
|
||||
if a_name.is_empty or a_name.is_whitespace then
|
||||
Result := False
|
||||
elseif a_name[1].is_space then
|
||||
Result := False
|
||||
elseif a_name[a_name.count].is_space then
|
||||
Result := False
|
||||
else
|
||||
Result := True
|
||||
across
|
||||
a_name as ic
|
||||
until
|
||||
not Result
|
||||
loop
|
||||
c := ic.item
|
||||
if c.is_alpha_numeric or c = '-' or c = '_' or c.is_space or c = '%'' then
|
||||
else
|
||||
Result := False
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_display_name (u: CMS_USER): READABLE_STRING_32
|
||||
-- Display name for user `u`.
|
||||
do
|
||||
if attached u.profile_name as pn and then not pn.is_whitespace then
|
||||
Result := pn
|
||||
elseif not u.name.is_whitespace then
|
||||
Result := u.name
|
||||
else
|
||||
Result := {STRING_32} "user #" + u.id.out
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access: user
|
||||
|
||||
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
|
||||
-- User by id `a_id', if any.
|
||||
do
|
||||
Result := user_storage.user_by_id (a_id)
|
||||
end
|
||||
|
||||
user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by name `a_user_name', if any.
|
||||
do
|
||||
Result := user_storage.user_by_name (a_username)
|
||||
end
|
||||
|
||||
user_by_id_or_name (a_uid: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by id or name `a_uid`, if any.
|
||||
do
|
||||
if a_uid.is_integer_64 then
|
||||
Result := user_by_id (a_uid.to_integer_64)
|
||||
else
|
||||
Result := user_by_name (a_uid)
|
||||
end
|
||||
end
|
||||
|
||||
user_by_email (a_email: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by email `a_email', if any.
|
||||
do
|
||||
Result := user_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 := user_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 := user_storage.user_by_password_token (a_token)
|
||||
end
|
||||
|
||||
users_count: INTEGER
|
||||
-- Number of users.
|
||||
do
|
||||
Result := user_storage.users_count
|
||||
end
|
||||
|
||||
recent_users (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_USER]
|
||||
-- List of the `a_rows' most recent users starting from `a_offset'.
|
||||
do
|
||||
Result := user_storage.recent_users (params.offset.to_integer_32, params.size.to_integer_32)
|
||||
end
|
||||
|
||||
admin_user: detachable CMS_USER
|
||||
-- Admin user if any.
|
||||
do
|
||||
if
|
||||
attached user_by_id (1) as u and then
|
||||
is_admin_user (u)
|
||||
then
|
||||
Result := u
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change User
|
||||
|
||||
new_user (a_user: CMS_USER)
|
||||
-- Add a new user `a_user'.
|
||||
require
|
||||
no_id: not a_user.has_id
|
||||
no_hashed_password: a_user.hashed_password = Void
|
||||
do
|
||||
reset_error
|
||||
if
|
||||
attached a_user.email as l_email
|
||||
then
|
||||
user_storage.new_user (a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
else
|
||||
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
|
||||
end
|
||||
end
|
||||
|
||||
update_username (a_user: CMS_USER; a_new_username: READABLE_STRING_32)
|
||||
-- Update username of `a_user' to `a_new_username'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
valid_user_name: is_valid_username (a_new_username)
|
||||
user_by_name (a_new_username) = Void
|
||||
do
|
||||
reset_error
|
||||
user_storage.update_username (a_user, a_new_username)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
update_user (a_user: CMS_USER)
|
||||
-- Update user `a_user'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
do
|
||||
reset_error
|
||||
user_storage.update_user (a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
delete_user (a_user: CMS_USER)
|
||||
-- Delete user `a_user'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
do
|
||||
reset_error
|
||||
user_storage.delete_user (a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid_credential (a_auth_login, a_auth_password: READABLE_STRING_32): BOOLEAN
|
||||
-- Is the credentials `a_auth_login' and `a_auth_password' valid?
|
||||
do
|
||||
Result := user_storage.is_valid_credential (a_auth_login, a_auth_password)
|
||||
end
|
||||
|
||||
user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Anonymous or user `a_user' has permission for `a_permission'?
|
||||
--| `a_permission' could be for instance "create page".
|
||||
do
|
||||
if a_permission = Void then
|
||||
Result := True
|
||||
elseif a_user = Void then
|
||||
Result := user_role_has_permission (anonymous_user_role, a_permission)
|
||||
else
|
||||
if is_admin_user (a_user) then
|
||||
Result := True
|
||||
else
|
||||
Result := user_role_has_permission (authenticated_user_role, a_permission)
|
||||
if not Result then
|
||||
Result := across user_roles (a_user) as ic some user_role_has_permission (ic.item, a_permission) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_admin_user (u: CMS_USER): BOOLEAN
|
||||
do
|
||||
Result := u.id = 1
|
||||
end
|
||||
|
||||
user_roles (a_user: CMS_USER): LIST [CMS_USER_ROLE]
|
||||
local
|
||||
l_roles: detachable LIST [CMS_USER_ROLE]
|
||||
do
|
||||
l_roles := a_user.roles
|
||||
if l_roles = Void then
|
||||
-- Fill user with its roles.
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (0)
|
||||
l_roles := user_storage.user_roles_for (a_user)
|
||||
end
|
||||
Result := l_roles
|
||||
end
|
||||
|
||||
feature -- User roles.
|
||||
|
||||
anonymous_user_role: CMS_USER_ROLE
|
||||
do
|
||||
if attached user_role_by_id (1) as l_anonymous then
|
||||
Result := l_anonymous
|
||||
else
|
||||
create Result.make ("anonymous")
|
||||
end
|
||||
end
|
||||
|
||||
authenticated_user_role: CMS_USER_ROLE
|
||||
do
|
||||
if attached user_role_by_id (2) as l_authenticated then
|
||||
Result := l_authenticated
|
||||
else
|
||||
create Result.make ("authenticated")
|
||||
end
|
||||
end
|
||||
|
||||
user_role_has_permission (a_role: CMS_USER_ROLE; a_permission: READABLE_STRING_GENERAL): BOOLEAN
|
||||
do
|
||||
Result := a_role.has_permission (a_permission)
|
||||
end
|
||||
|
||||
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
|
||||
-- Retrieve a `Role' represented by an id `a_id' if any.
|
||||
do
|
||||
Result := user_storage.user_role_by_id (a_id)
|
||||
end
|
||||
|
||||
user_role_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER_ROLE
|
||||
-- Retrieve a `Role' represented by a name `a_name' if any.
|
||||
do
|
||||
Result := user_storage.user_role_by_name (a_name)
|
||||
end
|
||||
|
||||
role_permissions: HASH_TABLE [LIST [READABLE_STRING_8], STRING_8]
|
||||
-- Possible known permissions indexed by modules.
|
||||
local
|
||||
lst, l_full_lst, l_used_permissions: LIST [READABLE_STRING_8]
|
||||
do
|
||||
create Result.make (cms_api.enabled_modules.count + 1)
|
||||
|
||||
l_used_permissions := user_storage.role_permissions
|
||||
across
|
||||
cms_api.enabled_modules as ic
|
||||
loop
|
||||
lst := ic.item.permissions
|
||||
if
|
||||
attached {CMS_ADMINISTRABLE} ic.item as adm and then
|
||||
attached adm.module_administration.permissions as adm_permissions and then
|
||||
not adm_permissions.is_empty
|
||||
then
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} l_full_lst.make (lst.count)
|
||||
l_full_lst.compare_objects
|
||||
-- l_full_lst.append (lst)
|
||||
across
|
||||
lst as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
-- l_full_lst.append (adm_permissions)
|
||||
across
|
||||
adm_permissions as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
lst := l_full_lst
|
||||
end
|
||||
if
|
||||
attached {CMS_WITH_WEBAPI} ic.item as wapi and then
|
||||
attached wapi.module_webapi.permissions as wapi_permissions and then
|
||||
not wapi_permissions.is_empty
|
||||
then
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} l_full_lst.make (lst.count)
|
||||
l_full_lst.compare_objects
|
||||
-- l_full_lst.append (lst)
|
||||
across
|
||||
lst as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
-- l_full_lst.append (wapi_permissions)
|
||||
across
|
||||
wapi_permissions as lst_ic
|
||||
loop
|
||||
if not l_full_lst.has (lst_ic.item) then
|
||||
l_full_lst.extend (lst_ic.item)
|
||||
end
|
||||
end
|
||||
lst := l_full_lst
|
||||
end
|
||||
Result.force (lst, ic.item.name)
|
||||
across
|
||||
lst as p_ic
|
||||
loop
|
||||
from
|
||||
l_used_permissions.start
|
||||
until
|
||||
l_used_permissions.after
|
||||
loop
|
||||
if l_used_permissions.item.is_case_insensitive_equal (p_ic.item) then
|
||||
l_used_permissions.remove
|
||||
l_used_permissions.finish
|
||||
end
|
||||
l_used_permissions.forth
|
||||
end
|
||||
end
|
||||
|
||||
if not l_used_permissions.is_empty then
|
||||
Result.force (l_used_permissions, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
roles: LIST [CMS_USER_ROLE]
|
||||
-- List of possible roles.
|
||||
do
|
||||
Result := user_storage.user_roles
|
||||
end
|
||||
|
||||
effective_roles: LIST [CMS_USER_ROLE]
|
||||
-- List of possible roles, apart from anonymous and authenticated roles that are special.
|
||||
local
|
||||
l_roles: like roles
|
||||
r: CMS_USER_ROLE
|
||||
do
|
||||
l_roles := user_storage.user_roles
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (l_roles.count)
|
||||
across
|
||||
l_roles as ic
|
||||
loop
|
||||
r := ic.item
|
||||
if r.same_user_role (anonymous_user_role) or r.same_user_role (authenticated_user_role) then
|
||||
-- Ignore
|
||||
else
|
||||
Result.force (r)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
roles_count: INTEGER
|
||||
-- Number of roles
|
||||
do
|
||||
Result := user_storage.user_roles.count
|
||||
end
|
||||
|
||||
feature -- Change User role
|
||||
|
||||
save_user_role (a_user_role: CMS_USER_ROLE)
|
||||
do
|
||||
reset_error
|
||||
user_storage.save_user_role (a_user_role)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
unassign_role_from_user (a_role: CMS_USER_ROLE; a_user: CMS_USER; )
|
||||
-- Unassign user_role `a_role' to user `a_user'.
|
||||
do
|
||||
reset_error
|
||||
user_storage.unassign_role_from_user (a_role, a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
assign_role_to_user (a_role: CMS_USER_ROLE; a_user: CMS_USER; )
|
||||
-- Assign user_role `a_role' to user `a_user'.
|
||||
do
|
||||
reset_error
|
||||
user_storage.assign_role_to_user (a_role, a_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
delete_role (a_role: CMS_USER_ROLE)
|
||||
do
|
||||
reset_error
|
||||
user_storage.delete_role (a_role)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
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
|
||||
user_storage.save_activation (a_token, a_id)
|
||||
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
|
||||
user_storage.save_password (a_token, a_id)
|
||||
end
|
||||
|
||||
remove_password (a_token: READABLE_STRING_32)
|
||||
-- Remove password token `a_token', from the user_storage.
|
||||
do
|
||||
user_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 user_storage.
|
||||
|
||||
feature -- Access - Temp User
|
||||
|
||||
temp_users_count: INTEGER
|
||||
-- Number of pending users.
|
||||
--! to be accepted or rehected
|
||||
do
|
||||
Result := user_storage.temp_users_count
|
||||
end
|
||||
|
||||
temp_user_by_name (a_username: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User by name `a_user_name', if any.
|
||||
do
|
||||
Result := user_storage.temp_user_by_name (a_username.as_string_32)
|
||||
end
|
||||
|
||||
temp_user_by_email (a_email: READABLE_STRING_8): detachable CMS_USER
|
||||
-- User by email `a_email', if any.
|
||||
do
|
||||
Result := user_storage.temp_user_by_email (a_email)
|
||||
end
|
||||
|
||||
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- User by activation token `a_token'.
|
||||
do
|
||||
Result := user_storage.temp_user_by_activation_token (a_token)
|
||||
end
|
||||
|
||||
temp_recent_users (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_TEMP_USER]
|
||||
-- List of the `a_rows' most recent users starting from `a_offset'.
|
||||
do
|
||||
Result := user_storage.temp_recent_users (params.offset.to_integer_32, params.size.to_integer_32)
|
||||
end
|
||||
|
||||
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
|
||||
do
|
||||
Result := user_storage.token_by_temp_user_id (a_id)
|
||||
end
|
||||
|
||||
feature -- Change Temp User
|
||||
|
||||
new_user_from_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
-- Add a new user `a_temp_user'.
|
||||
require
|
||||
has_hashed_password: a_temp_user.hashed_password /= Void
|
||||
has_sal: a_temp_user.salt /= Void
|
||||
do
|
||||
reset_error
|
||||
if
|
||||
attached a_temp_user.hashed_password as l_password and then
|
||||
attached a_temp_user.salt as l_salt and then
|
||||
attached a_temp_user.email as l_email
|
||||
then
|
||||
user_storage.new_user_from_temp_user (a_temp_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
else
|
||||
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
|
||||
end
|
||||
end
|
||||
|
||||
new_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
-- Add a new user `a_temp_user'.
|
||||
require
|
||||
no_id: not a_temp_user.has_id
|
||||
no_hashed_password: a_temp_user.hashed_password = Void
|
||||
do
|
||||
reset_error
|
||||
if
|
||||
attached a_temp_user.password as l_password and then
|
||||
attached a_temp_user.email as l_email
|
||||
then
|
||||
user_storage.new_temp_user (a_temp_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
else
|
||||
error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!")
|
||||
end
|
||||
end
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- Remove activation token `a_token', from the user_storage.
|
||||
do
|
||||
user_storage.remove_activation (a_token)
|
||||
end
|
||||
|
||||
delete_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
-- Delete user `a_temp_user'.
|
||||
require
|
||||
has_id: a_temp_user.has_id
|
||||
do
|
||||
reset_error
|
||||
user_storage.delete_temp_user (a_temp_user)
|
||||
error_handler.append (user_storage.error_handler)
|
||||
end
|
||||
|
||||
--feature -- Access: User profile
|
||||
|
||||
-- user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- -- User profile for `a_user'.
|
||||
-- require
|
||||
-- valid_user: a_user.has_id
|
||||
-- do
|
||||
-- Result := user_profile_storage.user_profile (a_user)
|
||||
-- end
|
||||
|
||||
-- user_profile_item (a_item_name: READABLE_STRING_GENERAL; a_user: CMS_USER): detachable READABLE_STRING_32
|
||||
-- -- User profile item `a_item_name` for `a_user`.
|
||||
-- require
|
||||
-- valid_user: a_user.has_id
|
||||
-- do
|
||||
-- Result := user_profile_storage.user_profile_item (a_user, a_item_name)
|
||||
-- end
|
||||
|
||||
--feature -- Change: User profile
|
||||
|
||||
-- save_user_profile (a_user: CMS_USER; a_user_profile: CMS_USER_PROFILE)
|
||||
-- -- Save `a_user' profile `a_user_profile'.
|
||||
-- require
|
||||
-- valid_user: a_user.has_id
|
||||
-- do
|
||||
-- user_profile_storage.save_user_profile (a_user, a_user_profile)
|
||||
-- end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
87
src/modules/core/cms_user_profile_api.e
Normal file
87
src/modules/core/cms_user_profile_api.e
Normal file
@@ -0,0 +1,87 @@
|
||||
note
|
||||
description: "API to handle user profiles."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE_API
|
||||
|
||||
inherit
|
||||
CMS_MODULE_API
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- <Precursor>
|
||||
do
|
||||
Precursor
|
||||
-- Storage initialization
|
||||
if attached cms_api.storage.as_sql_storage as l_storage_sql then
|
||||
create {CMS_USER_PROFILE_STORAGE_SQL} user_profile_storage.make (l_storage_sql)
|
||||
else
|
||||
-- FIXME: in case of NULL storage, should Current be disabled?
|
||||
create {CMS_USER_PROFILE_STORAGE_NULL} user_profile_storage
|
||||
end
|
||||
end
|
||||
|
||||
feature {CMS_MODULE} -- Access nodes storage.
|
||||
|
||||
user_profile_storage: CMS_USER_PROFILE_STORAGE_I
|
||||
|
||||
feature -- Access: profile
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- User profile for `a_user'.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
Result := user_profile_storage.user_profile (a_user)
|
||||
end
|
||||
|
||||
user_profile_item (a_item_name: READABLE_STRING_GENERAL; a_user: CMS_USER): detachable READABLE_STRING_32
|
||||
-- User profile item `a_item_name` for `a_user`.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
Result := user_profile_storage.user_profile_item (a_user, a_item_name)
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- Users having a profile item `a_item_name:a_value`.
|
||||
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
|
||||
require
|
||||
not a_item_name.is_whitespace
|
||||
do
|
||||
Result := user_profile_storage.users_with_profile_item (a_item_name, a_value)
|
||||
end
|
||||
|
||||
feature -- Change: profile
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_user_profile: CMS_USER_PROFILE)
|
||||
-- Save `a_user' profile `a_user_profile'.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
user_profile_storage.save_user_profile (a_user, a_user_profile)
|
||||
end
|
||||
|
||||
save_user_profile_item (a_user: CMS_USER; a_user_profile_name, a_user_profile_value: READABLE_STRING_GENERAL)
|
||||
-- Save `a_user' profile item `a_user_profile_name=a_user_profile_value`.
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
user_profile_storage.save_user_profile_item (a_user, a_user_profile_name, a_user_profile_value)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
104
src/modules/core/handler/user/cms_user_handler.e
Normal file
104
src/modules/core/handler/user/cms_user_handler.e
Normal file
@@ -0,0 +1,104 @@
|
||||
note
|
||||
description: "[
|
||||
Handler for a CMS user in the CMS interface
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
rename
|
||||
execute as uri_execute,
|
||||
new_mapping as new_uri_mapping
|
||||
end
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
rename
|
||||
execute as uri_template_execute,
|
||||
new_mapping as new_uri_template_mapping
|
||||
select
|
||||
new_uri_template_mapping
|
||||
end
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get
|
||||
end
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- execute
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (req, res)
|
||||
end
|
||||
|
||||
uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute (req, res)
|
||||
end
|
||||
|
||||
uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute (req, res)
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_path_parameter (req: WSF_REQUEST): detachable CMS_USER
|
||||
-- User id (uid or username) passed as path parameter for request `req'.
|
||||
local
|
||||
s: STRING
|
||||
l_uid: INTEGER_64
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_nid then
|
||||
s := p_nid.value
|
||||
if s.is_integer_64 then
|
||||
l_uid := s.to_integer_64
|
||||
if l_uid > 0 then
|
||||
Result := api.user_api.user_by_id (l_uid)
|
||||
end
|
||||
else
|
||||
Result := api.user_api.user_by_name (s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- HTTP Methods
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_user: detachable CMS_USER
|
||||
do
|
||||
if api.has_permission ("view user") then
|
||||
-- Display existing node
|
||||
l_user := user_path_parameter (req)
|
||||
if
|
||||
l_user /= Void
|
||||
then
|
||||
(create {CMS_USER_VIEW_RESPONSE}.make_with_user (l_user, req, res, api)).execute
|
||||
else
|
||||
send_not_found (req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
137
src/modules/core/handler/user/cms_user_view_response.e
Normal file
137
src/modules/core/handler/user/cms_user_view_response.e
Normal file
@@ -0,0 +1,137 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_VIEW_RESPONSE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_VIEW_RESPONSE
|
||||
|
||||
inherit
|
||||
CMS_RESPONSE
|
||||
|
||||
create
|
||||
make_with_user
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_user (u: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api)
|
||||
do
|
||||
make (req, res, a_api)
|
||||
associated_user := u
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
associated_user: CMS_USER
|
||||
|
||||
feature -- Query
|
||||
|
||||
user_id_path_parameter (req: WSF_REQUEST): INTEGER_64
|
||||
-- User id passed as path parameter for request `req'.
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_nid then
|
||||
s := p_nid.value
|
||||
if s.is_integer_64 then
|
||||
Result := s.to_integer_64
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Process
|
||||
|
||||
process
|
||||
-- Computed response message.
|
||||
local
|
||||
b: STRING_8
|
||||
f: CMS_FORM
|
||||
do
|
||||
create b.make_empty
|
||||
if
|
||||
attached associated_user as l_user
|
||||
then
|
||||
if
|
||||
api.has_permission ("view user")
|
||||
or l_user.same_as (user) -- Same user
|
||||
then
|
||||
f := new_view_form (l_user, request.request_uri, "view-user")
|
||||
f.append_to_html (wsf_theme, b)
|
||||
else
|
||||
b.append ("You don't have the permission to view this user!")
|
||||
end
|
||||
else
|
||||
b.append ("User not found!")
|
||||
end
|
||||
set_main_content (b)
|
||||
end
|
||||
|
||||
feature -- Process Edit
|
||||
|
||||
new_view_form (a_user: detachable CMS_USER; a_url: READABLE_STRING_8; a_name: STRING): CMS_FORM
|
||||
-- Create a web form named `a_name' for user `a_user' (if set), using form action url `a_url'.
|
||||
local
|
||||
th: WSF_FORM_HIDDEN_INPUT
|
||||
do
|
||||
create Result.make (a_url, a_name)
|
||||
create th.make ("user-id")
|
||||
if a_user /= Void then
|
||||
th.set_text_value (a_user.id.out)
|
||||
else
|
||||
th.set_text_value ("0")
|
||||
end
|
||||
Result.extend (th)
|
||||
|
||||
populate_form (Result, a_user)
|
||||
end
|
||||
|
||||
populate_form (a_form: CMS_FORM; a_user: detachable CMS_USER)
|
||||
-- Fill the web form `a_form' with data from `a_node' if set,
|
||||
-- and apply this to content type `a_content_type'.
|
||||
local
|
||||
ti: WSF_FORM_TEXT_INPUT
|
||||
fs: WSF_FORM_FIELD_SET
|
||||
l_new_access_token_form: WSF_FORM
|
||||
l_access_token: detachable READABLE_STRING_32
|
||||
do
|
||||
if a_user /= Void then
|
||||
create fs.make
|
||||
fs.set_legend ("User Information")
|
||||
create ti.make_with_text ("profile_name", a_user.name)
|
||||
if attached a_user.profile_name as l_profile_name then
|
||||
ti.set_text_value (l_profile_name)
|
||||
end
|
||||
ti.set_label ("Profile name")
|
||||
ti.set_is_readonly (True)
|
||||
fs.extend (ti)
|
||||
a_form.extend (fs)
|
||||
if api.setup.webapi_enabled then
|
||||
create fs.make
|
||||
fs.set_legend ("Web API")
|
||||
l_access_token := api.user_api.user_profile_item ("access_token", a_user)
|
||||
if l_access_token /= Void then
|
||||
create ti.make_with_text ("api_access_token", a_user.name)
|
||||
ti.set_text_value (l_access_token)
|
||||
ti.set_label ("Access Token")
|
||||
ti.set_is_readonly (True)
|
||||
fs.extend (ti)
|
||||
end
|
||||
|
||||
create l_new_access_token_form.make (api.webapi_path ("access_token"), Void)
|
||||
l_new_access_token_form.set_method_post
|
||||
if l_access_token /= Void then
|
||||
l_new_access_token_form.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("access_token_op", "Refresh Access Token"))
|
||||
else
|
||||
l_new_access_token_form.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("access_token_op", "Create Access Token"))
|
||||
end
|
||||
l_new_access_token_form.extend (create {WSF_FORM_HIDDEN_INPUT}.make_with_text ("destination", request.percent_encoded_path_info))
|
||||
a_form.put_widget_after_form (l_new_access_token_form)
|
||||
a_form.extend (fs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
94
src/modules/core/persistence/core/cms_core_storage_i.e
Normal file
94
src/modules/core/persistence/core/cms_core_storage_i.e
Normal file
@@ -0,0 +1,94 @@
|
||||
note
|
||||
description: "[
|
||||
CMS Storage for core functionalities.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_CORE_STORAGE_I
|
||||
|
||||
inherit
|
||||
SHARED_LOGGER
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- URL aliases
|
||||
|
||||
set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
|
||||
-- Alias `a_source' with `a_alias'.
|
||||
deferred
|
||||
end
|
||||
|
||||
replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8)
|
||||
-- Replace eventual previous alias `a_previous_alias' with a new alias `a_alias'
|
||||
-- on source `a_source'.
|
||||
deferred
|
||||
end
|
||||
|
||||
unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
|
||||
-- Unalias `a_source' from `a_alias'.
|
||||
deferred
|
||||
end
|
||||
|
||||
path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- Return eventual path alias associated with `a_source'.
|
||||
deferred
|
||||
end
|
||||
|
||||
source_of_path_alias (a_alias: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
|
||||
-- Source path for alias `a_alias'.
|
||||
deferred
|
||||
end
|
||||
|
||||
path_aliases: STRING_TABLE [READABLE_STRING_8]
|
||||
-- All path aliases as a table containing sources indexed by alias.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Logs
|
||||
|
||||
save_log (a_log: CMS_LOG)
|
||||
-- Save `a_log'.
|
||||
deferred
|
||||
end
|
||||
|
||||
logs (a_category: detachable READABLE_STRING_GENERAL; a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG]
|
||||
-- List of recent logs from `a_lower' to `a_lower+a_count'.
|
||||
-- If `a_category' is set, filter to return only associated logs.
|
||||
-- If `a_count' <= 0 then, return all logs.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Misc
|
||||
|
||||
set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8)
|
||||
-- Save data `a_name:a_value' for type `a_type' (or default if none).
|
||||
deferred
|
||||
end
|
||||
|
||||
unset_custom_value (a_name: READABLE_STRING_8; a_type: detachable READABLE_STRING_8)
|
||||
-- Delete data `a_name' for type `a_type' (or default if none).
|
||||
deferred
|
||||
end
|
||||
|
||||
custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32
|
||||
-- Data for name `a_name' and type `a_type' (or default if none).
|
||||
deferred
|
||||
end
|
||||
|
||||
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
|
||||
-- Values as list of [name, type, value].
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
431
src/modules/core/persistence/core/cms_core_storage_sql_i.e
Normal file
431
src/modules/core/persistence/core/cms_core_storage_sql_i.e
Normal file
@@ -0,0 +1,431 @@
|
||||
note
|
||||
description: "[
|
||||
Objects that ...
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_CORE_STORAGE_SQL_I
|
||||
|
||||
inherit
|
||||
CMS_CORE_STORAGE_I
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_LOGGER
|
||||
|
||||
feature -- URL aliases
|
||||
|
||||
set_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_source: like source_of_path_alias
|
||||
l_continue: BOOLEAN
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_source, "source")
|
||||
l_parameters.put (a_alias, "alias")
|
||||
l_source := source_of_path_alias (a_alias)
|
||||
l_continue := True
|
||||
if
|
||||
l_source /= Void -- Alias exists!
|
||||
then
|
||||
if a_source.same_string (l_source) then
|
||||
if attached path_alias (l_source) as l_alias and then l_alias.same_string (a_alias) then
|
||||
-- already up to date
|
||||
l_continue := False
|
||||
else
|
||||
-- multiple alias and a_alias is not the default alias
|
||||
-- then unset, and set again !
|
||||
unset_path_alias (a_source, a_alias)
|
||||
end
|
||||
else
|
||||
l_continue := False
|
||||
error_handler.add_custom_error (0, "alias exists", "Path alias %"" + a_alias + "%" already exists!")
|
||||
end
|
||||
end
|
||||
if l_continue then
|
||||
sql_insert (sql_insert_path_alias, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
end
|
||||
|
||||
replace_path_alias (a_source: READABLE_STRING_8; a_previous_alias: detachable READABLE_STRING_8; a_alias: READABLE_STRING_8)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_previous_alias: detachable READABLE_STRING_8
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
if a_previous_alias = Void then
|
||||
l_previous_alias := path_alias (a_source)
|
||||
else
|
||||
l_previous_alias := a_previous_alias
|
||||
end
|
||||
if
|
||||
l_previous_alias /= Void and then
|
||||
not a_alias.same_string (l_previous_alias)
|
||||
then
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_source, "source")
|
||||
l_parameters.put (l_previous_alias, "old")
|
||||
l_parameters.put (a_alias, "alias")
|
||||
|
||||
sql_modify (sql_update_path_alias, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
end
|
||||
|
||||
unset_path_alias (a_source: READABLE_STRING_8; a_alias: READABLE_STRING_8)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
if attached source_of_path_alias (a_alias) as l_path then
|
||||
if a_source.same_string (l_path) then
|
||||
-- Found
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_alias, "alias")
|
||||
sql_modify (sql_delete_path_alias, l_parameters)
|
||||
sql_finalize
|
||||
else
|
||||
error_handler.add_custom_error (0, "alias mismatch", "Path alias %"" + a_alias + "%" is not related to source %"" + a_source + "%"!")
|
||||
end
|
||||
else
|
||||
-- No such alias
|
||||
end
|
||||
end
|
||||
|
||||
path_alias (a_source: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_source, "source")
|
||||
sql_query (sql_select_path_source, l_parameters)
|
||||
if not has_error and not sql_after then
|
||||
Result := sql_read_string (1)
|
||||
sql_forth
|
||||
check one_row: sql_after end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
source_of_path_alias (a_alias: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_alias, "alias")
|
||||
sql_query (sql_select_path_alias, l_parameters)
|
||||
if not has_error then
|
||||
if not has_error and not sql_after then
|
||||
Result := sql_read_string (1)
|
||||
sql_forth
|
||||
check one_row: sql_after end
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
path_aliases: STRING_TABLE [READABLE_STRING_8]
|
||||
-- All path aliases as a table containing sources indexed by alias.
|
||||
local
|
||||
l_source: READABLE_STRING_8
|
||||
do
|
||||
error_handler.reset
|
||||
create Result.make (5)
|
||||
sql_query (sql_select_all_path_alias, Void)
|
||||
if not has_error then
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if attached sql_read_string (1) as s_src then
|
||||
l_source := s_src
|
||||
if attached sql_read_string (2) as s_alias then
|
||||
Result.force (l_source, s_alias)
|
||||
end
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
sql_select_all_path_alias: STRING = "SELECT source, alias, lang FROM path_aliases ORDER BY pid DESC;"
|
||||
-- SQL select all path aliases.
|
||||
|
||||
sql_select_path_alias: STRING = "SELECT source FROM path_aliases WHERE alias=:alias ;"
|
||||
-- SQL select path aliases.
|
||||
|
||||
sql_select_path_source: STRING = "SELECT alias FROM path_aliases WHERE source=:source ORDER BY pid DESC LIMIT 1;"
|
||||
-- SQL select latest path aliasing :source.
|
||||
|
||||
sql_insert_path_alias: STRING = "INSERT INTO path_aliases (source, alias) VALUES (:source, :alias);"
|
||||
-- SQL insert path alias.
|
||||
|
||||
sql_update_path_alias: STRING = "UPDATE path_aliases SET alias=:alias WHERE source=:source AND alias=:old ;"
|
||||
-- SQL update path alias.
|
||||
|
||||
sql_delete_path_alias: STRING = "DELETE FROM path_aliases WHERE alias=:alias;"
|
||||
-- SQL delete path alias
|
||||
|
||||
feature -- Logs
|
||||
|
||||
save_log (a_log: CMS_LOG)
|
||||
-- Save `a_log'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
now: DATE_TIME
|
||||
s32: STRING_32
|
||||
do
|
||||
create now.make_now_utc
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (8)
|
||||
l_parameters.put (a_log.category, "category")
|
||||
l_parameters.put (a_log.level, "level")
|
||||
l_parameters.put (0, "uid") -- Unsupported for now
|
||||
l_parameters.put (a_log.message, "message")
|
||||
l_parameters.put (a_log.info, "info")
|
||||
if attached a_log.link as lnk then
|
||||
create s32.make_empty
|
||||
s32.append_character ('[')
|
||||
s32.append_string_general (lnk.location)
|
||||
s32.append_character (']')
|
||||
s32.append_character ('(')
|
||||
s32.append (lnk.title)
|
||||
s32.append_character (')')
|
||||
l_parameters.put (s32, "link")
|
||||
else
|
||||
l_parameters.put (Void, "link")
|
||||
end
|
||||
l_parameters.put (now, "date")
|
||||
sql_insert (sql_insert_log, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
logs (a_category: detachable READABLE_STRING_GENERAL; a_lower: INTEGER; a_count: INTEGER): ARRAYED_LIST [CMS_LOG]
|
||||
-- <Precursor>.
|
||||
local
|
||||
l_parameters: detachable STRING_TABLE [detachable ANY]
|
||||
l_sql: READABLE_STRING_8
|
||||
do
|
||||
error_handler.reset
|
||||
create l_parameters.make (3)
|
||||
if a_category /= Void then
|
||||
l_parameters.put (a_category, "category")
|
||||
l_sql := sql_select_categorized_logs
|
||||
else
|
||||
l_sql := sql_select_logs
|
||||
end
|
||||
if a_count > 0 then
|
||||
l_parameters.put (a_lower, "offset")
|
||||
l_parameters.put (a_count, "size")
|
||||
check l_sql.ends_with_general (";") end
|
||||
l_sql := l_sql.substring (1, l_sql.count - 1) -- Remove ';'
|
||||
+ "LIMIT :size OFFSET :offset ;"
|
||||
end
|
||||
|
||||
from
|
||||
if a_count > 0 then
|
||||
create Result.make (a_count)
|
||||
else
|
||||
create Result.make (10)
|
||||
end
|
||||
if l_parameters.is_empty then
|
||||
l_parameters := Void
|
||||
end
|
||||
sql_query (l_sql, l_parameters)
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if attached fetch_log as l_log then
|
||||
Result.force (l_log)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
fetch_log: detachable CMS_LOG
|
||||
-- SQL: 1:id, 2:category, 3:level, 4:uid, 5:message, 6:info, 7:link, 8:date
|
||||
local
|
||||
l_cat: detachable READABLE_STRING_8
|
||||
l_mesg: detachable READABLE_STRING_8
|
||||
l_level: INTEGER
|
||||
l_date: detachable DATE_TIME
|
||||
i: INTEGER
|
||||
lnk: CMS_LOCAL_LINK
|
||||
do
|
||||
l_cat := sql_read_string (2)
|
||||
l_mesg := sql_read_string (5)
|
||||
l_level := sql_read_integer_32 (3)
|
||||
l_date := sql_read_date_time (8)
|
||||
|
||||
if l_cat = Void then
|
||||
l_cat := "unknown"
|
||||
end
|
||||
if l_mesg = Void then
|
||||
l_mesg := ""
|
||||
end
|
||||
|
||||
create Result.make (l_cat, l_mesg, l_level, l_date)
|
||||
Result.set_id (sql_read_integer_64 (1))
|
||||
Result.set_info (sql_read_string (6))
|
||||
if attached sql_read_string_32 (7) as l_link_text then
|
||||
-- Format: "[title](location)"
|
||||
i := l_link_text.index_of ('(', 1)
|
||||
if i > 0 then
|
||||
create lnk.make (l_link_text.substring (2, i - 2), l_link_text.substring (i + 1, l_link_text.count - 1))
|
||||
Result.set_link (lnk)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sql_insert_log: STRING = "INSERT INTO logs (category, level, uid, message, info, link, date) VALUES (:category, :level, :uid, :message, :info, :link, :date);"
|
||||
-- SQL Insert to add a new node.
|
||||
|
||||
sql_select_logs: STRING = "SELECT id, category, level, uid, message, info, link, date FROM logs ORDER by date DESC;"
|
||||
-- SQL Insert to add a new node.
|
||||
|
||||
sql_select_categorized_logs: STRING = "SELECT id, category, level, uid, message, info, link, date FROM logs WHERE category=:category ORDER by date DESC;"
|
||||
-- SQL Insert to add a new node.
|
||||
|
||||
feature -- Misc
|
||||
|
||||
set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: detachable READABLE_STRING_8)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (3)
|
||||
if a_type /= Void then
|
||||
l_parameters.put (a_type, "type")
|
||||
else
|
||||
l_parameters.put (a_type, "default")
|
||||
end
|
||||
l_parameters.put (a_name, "name")
|
||||
l_parameters.put (a_value, "value")
|
||||
if attached custom_value (a_name, a_type) as l_value then
|
||||
if a_value.same_string (l_value) then
|
||||
-- already up to date
|
||||
else
|
||||
sql_modify (sql_update_custom_value, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
else
|
||||
sql_insert (sql_insert_custom_value, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
end
|
||||
|
||||
unset_custom_value (a_name: READABLE_STRING_8; a_type: detachable READABLE_STRING_8)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (3)
|
||||
if a_type /= Void then
|
||||
l_parameters.put (a_type, "type")
|
||||
else
|
||||
l_parameters.put (a_type, "default")
|
||||
end
|
||||
l_parameters.put (a_name, "name")
|
||||
sql_modify (sql_delete_custom_value, l_parameters)
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32
|
||||
-- <Precursor>
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
error_handler.reset
|
||||
|
||||
create l_parameters.make (2)
|
||||
if a_type /= Void then
|
||||
l_parameters.put (a_type, "type")
|
||||
else
|
||||
l_parameters.put (a_type, "default")
|
||||
end
|
||||
l_parameters.put (a_name, "name")
|
||||
sql_query (sql_select_custom_value, l_parameters)
|
||||
if not has_error and not sql_after then
|
||||
Result := sql_read_string_32 (1)
|
||||
sql_forth
|
||||
check one_row: sql_after end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
|
||||
-- Values as list of [name, type, value].
|
||||
local
|
||||
l_type, l_name: READABLE_STRING_8
|
||||
do
|
||||
error_handler.reset
|
||||
create {ARRAYED_LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]} Result.make (5)
|
||||
sql_query (sql_select_all_custom_values, Void)
|
||||
if not has_error then
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if attached sql_read_string (1) as s_type then
|
||||
l_type := s_type
|
||||
if attached sql_read_string (2) as s_name then
|
||||
l_name := s_name
|
||||
if attached sql_read_string_32 (3) as s_value then
|
||||
Result.force ([l_name, l_type, s_value])
|
||||
end
|
||||
end
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
sql_select_all_custom_values: STRING = "SELECT type, name, value FROM custom_values;"
|
||||
-- SQL Insert to add a new custom value.
|
||||
|
||||
sql_select_custom_value: STRING = "SELECT value FROM custom_values WHERE type=:type AND name=:name;"
|
||||
-- SQL Insert to add a new custom value.
|
||||
|
||||
sql_insert_custom_value: STRING = "INSERT INTO custom_values (type, name, value) VALUES (:type, :name, :value);"
|
||||
-- SQL Insert to add a new custom value.
|
||||
|
||||
sql_update_custom_value : STRING = "UPDATE custom_values SET value=:value WHERE type=:type AND name=:name;"
|
||||
-- SQL Update to modify a custom value.
|
||||
|
||||
sql_delete_custom_value: STRING = "DELETE FROM custom_values WHERE type=:type AND name=:name;"
|
||||
-- SQL delete custom value;
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
290
src/modules/core/persistence/user/cms_user_storage_i.e
Normal file
290
src/modules/core/persistence/user/cms_user_storage_i.e
Normal file
@@ -0,0 +1,290 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_STORAGE_I}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_USER_STORAGE_I
|
||||
|
||||
inherit
|
||||
SHARED_LOGGER
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_user: BOOLEAN
|
||||
-- Has any user?
|
||||
deferred
|
||||
end
|
||||
|
||||
users: LIST [CMS_USER]
|
||||
-- List of users.
|
||||
deferred
|
||||
end
|
||||
|
||||
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
|
||||
-- User with id `a_id', if any.
|
||||
require
|
||||
a_id > 0
|
||||
deferred
|
||||
ensure
|
||||
same_id: Result /= Void implies Result.id = a_id
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
user_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User with name `a_name', if any.
|
||||
require
|
||||
a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
ensure
|
||||
same_name: Result /= Void implies a_name ~ Result.name
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
user_by_email (a_email: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- User with name `a_email', if any.
|
||||
deferred
|
||||
ensure
|
||||
same_email: Result /= Void implies (attached Result.email as r_email and then a_email.same_string (r_email))
|
||||
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
|
||||
end
|
||||
|
||||
users_count: INTEGER
|
||||
-- Number of users
|
||||
deferred
|
||||
end
|
||||
|
||||
recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_USER]
|
||||
-- List of recent `a_count' users with an offset of `lower'.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Change: user
|
||||
|
||||
save_user (a_user: CMS_USER)
|
||||
-- Create or update `a_user'.
|
||||
do
|
||||
if a_user.has_id then
|
||||
update_user (a_user)
|
||||
else
|
||||
new_user (a_user)
|
||||
end
|
||||
end
|
||||
|
||||
new_user (a_user: CMS_USER)
|
||||
-- New user `a_user'.
|
||||
require
|
||||
no_id: not a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
update_username (a_user: CMS_USER; a_new_username: READABLE_STRING_32)
|
||||
-- Update username of `a_user' to `a_new_username`.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
update_user (a_user: CMS_USER)
|
||||
-- Save user `a_user'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
delete_user (a_user: CMS_USER)
|
||||
-- Delete user `a_user'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access: roles and permissions
|
||||
|
||||
user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN
|
||||
do
|
||||
Result := a_role.has_permission (s)
|
||||
end
|
||||
|
||||
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
|
||||
-- User role by id `a_id', if any.
|
||||
deferred
|
||||
end
|
||||
|
||||
user_role_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER_ROLE
|
||||
-- User role by name `a_id', if any.
|
||||
deferred
|
||||
end
|
||||
|
||||
user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE]
|
||||
-- User roles for user `a_user'.
|
||||
-- Note: anonymous and authenticated roles are not included.
|
||||
deferred
|
||||
end
|
||||
|
||||
user_roles: LIST [CMS_USER_ROLE]
|
||||
-- Possible list of user roles.
|
||||
deferred
|
||||
end
|
||||
|
||||
role_permissions: LIST [READABLE_STRING_8]
|
||||
-- Possible known role permissions.
|
||||
deferred
|
||||
ensure
|
||||
object_comparison: Result.object_comparison
|
||||
end
|
||||
|
||||
feature -- Change: roles and permissions
|
||||
|
||||
save_user_role (a_user_role: CMS_USER_ROLE)
|
||||
-- Save user role `a_user_role'
|
||||
deferred
|
||||
end
|
||||
|
||||
unassign_role_from_user (a_role: CMS_USER_ROLE; a_user: CMS_USER)
|
||||
-- Unassign user_role to user
|
||||
require
|
||||
a_user.has_id
|
||||
a_role.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
assign_role_to_user (a_role: CMS_USER_ROLE; a_user: CMS_USER)
|
||||
-- Assign user_role to user
|
||||
require
|
||||
a_user.has_id
|
||||
a_role.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
|
||||
delete_role (a_role: CMS_USER_ROLE)
|
||||
-- Remove role `a_role'.
|
||||
require
|
||||
a_role.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Change: User activation
|
||||
|
||||
save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <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
|
||||
|
||||
feature -- Access: Temp Users
|
||||
|
||||
temp_users_count: INTEGER
|
||||
-- Number of pending users
|
||||
--! to be accepted or rejected
|
||||
deferred
|
||||
end
|
||||
|
||||
temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- Retrieve a temporal user by id `a_uid' for the consumer `a_consumer', if aby.
|
||||
deferred
|
||||
end
|
||||
|
||||
temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
|
||||
-- User with name `a_name', if any.
|
||||
require
|
||||
a_name /= Void and then not a_name.is_empty
|
||||
deferred
|
||||
ensure
|
||||
same_name: Result /= Void implies a_name ~ Result.name
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
|
||||
-- User with name `a_email', if any.
|
||||
deferred
|
||||
ensure
|
||||
same_email: Result /= Void implies a_email ~ Result.email
|
||||
password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void)
|
||||
end
|
||||
|
||||
temp_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
|
||||
|
||||
temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER]
|
||||
-- List of recent `a_count' temporal users with an offset of `lower'.
|
||||
deferred
|
||||
end
|
||||
|
||||
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
|
||||
-- Retrieve activation token for user identified with id `a_id', if any.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- New Temp User
|
||||
|
||||
new_user_from_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
-- new user from temporal user `a_temp_user'
|
||||
deferred
|
||||
end
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- Remove activation by token `a_token'.
|
||||
deferred
|
||||
end
|
||||
|
||||
new_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
-- New temp user `a_temp_user'.
|
||||
require
|
||||
no_id: not a_temp_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
delete_temp_user (a_temp_user: CMS_TEMP_USER)
|
||||
-- Delete user `a_temp_user'.
|
||||
require
|
||||
has_id: a_temp_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
211
src/modules/core/persistence/user/cms_user_storage_null.e
Normal file
211
src/modules/core/persistence/user/cms_user_storage_null.e
Normal file
@@ -0,0 +1,211 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_STORAGE_NULL}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_USER_STORAGE_NULL
|
||||
|
||||
inherit
|
||||
CMS_USER_STORAGE_I
|
||||
|
||||
feature -- Access: user
|
||||
|
||||
has_user: BOOLEAN
|
||||
-- Has any user?
|
||||
do
|
||||
end
|
||||
|
||||
users: LIST [CMS_USER]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
|
||||
end
|
||||
|
||||
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
|
||||
do
|
||||
end
|
||||
|
||||
user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
|
||||
do
|
||||
end
|
||||
|
||||
user_by_email (a_email: READABLE_STRING_GENERAL): detachable CMS_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
|
||||
|
||||
users_count: INTEGER
|
||||
--<Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_USER]
|
||||
-- <Precursor>
|
||||
do
|
||||
create {ARRAYED_LIST[CMS_USER]} Result.make (0)
|
||||
end
|
||||
|
||||
feature -- Change: user
|
||||
|
||||
new_user (a_user: CMS_USER)
|
||||
-- Add a new user `a_user'.
|
||||
do
|
||||
a_user.set_id (1)
|
||||
end
|
||||
|
||||
update_username (a_user: CMS_USER; a_new_username: READABLE_STRING_32)
|
||||
do
|
||||
end
|
||||
|
||||
update_user (a_user: CMS_USER)
|
||||
-- Update user `a_user'.
|
||||
do
|
||||
end
|
||||
|
||||
delete_user (a_user: CMS_USER)
|
||||
-- Delete user `a_user'.
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Access: roles and permissions
|
||||
|
||||
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE
|
||||
do
|
||||
end
|
||||
|
||||
user_role_by_name (a_name: READABLE_STRING_GENERAL): detachable CMS_USER_ROLE
|
||||
do
|
||||
end
|
||||
|
||||
user_roles_for (a_user: CMS_USER): LIST [CMS_USER_ROLE]
|
||||
-- User roles for user `a_user'.
|
||||
-- Note: anonymous and authenticated roles are not included.
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
|
||||
end
|
||||
|
||||
user_roles: LIST [CMS_USER_ROLE]
|
||||
do
|
||||
create {ARRAYED_LIST [CMS_USER_ROLE]} Result.make (0)
|
||||
end
|
||||
|
||||
role_permissions: LIST [READABLE_STRING_8]
|
||||
-- Possible known permissions.
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (0)
|
||||
end
|
||||
|
||||
feature -- Change: roles and permissions
|
||||
|
||||
save_user_role (a_user_role: CMS_USER_ROLE)
|
||||
do
|
||||
end
|
||||
|
||||
unassign_role_from_user (a_user_role: CMS_USER_ROLE; a_user: CMS_USER)
|
||||
do
|
||||
end
|
||||
|
||||
assign_role_to_user (a_user_role: CMS_USER_ROLE; a_user: CMS_USER)
|
||||
do
|
||||
end
|
||||
|
||||
delete_role (a_role: CMS_USER_ROLE)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Change: User activation
|
||||
|
||||
save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)
|
||||
-- <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
|
||||
|
||||
feature -- Access: Users
|
||||
|
||||
temp_users_count: INTEGER
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
temp_user_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
temp_user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
temp_user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
temp_user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
temp_recent_users (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_TEMP_USER]
|
||||
-- List of recent `a_count' temporal users with an offset of `lower'.
|
||||
do
|
||||
create {ARRAYED_LIST[CMS_TEMP_USER]} Result.make (0)
|
||||
end
|
||||
|
||||
token_by_temp_user_id (a_id: like {CMS_USER}.id): detachable STRING
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Temp Users
|
||||
|
||||
new_user_from_temp_user (a_user: CMS_TEMP_USER)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
remove_activation (a_token: READABLE_STRING_32)
|
||||
-- <Precursor>.
|
||||
do
|
||||
end
|
||||
|
||||
new_temp_user (a_user: CMS_TEMP_USER)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
delete_temp_user (a_user: CMS_TEMP_USER)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
1409
src/modules/core/persistence/user/cms_user_storage_sql_i.e
Normal file
1409
src/modules/core/persistence/user/cms_user_storage_sql_i.e
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
note
|
||||
description: "Interface for accessing user profile contents from the database."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
CMS_USER_PROFILE_STORAGE_I
|
||||
|
||||
feature -- Error Handling
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- User profile for `a_user'.
|
||||
require
|
||||
has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
require
|
||||
valid_user: a_user.has_id
|
||||
do
|
||||
if attached user_profile (a_user) as pf then
|
||||
Result := pf.item (a_item_name)
|
||||
end
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- Users having a profile item `a_item_name:a_value`.
|
||||
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
|
||||
-- Save user profile `a_profile' for `a_user'.
|
||||
require
|
||||
user_has_id: a_user.has_id
|
||||
deferred
|
||||
end
|
||||
|
||||
save_user_profile_item (a_user: CMS_USER; a_profile_item_name: READABLE_STRING_GENERAL; a_profile_item_value: READABLE_STRING_GENERAL)
|
||||
require
|
||||
user_has_id: a_user.has_id
|
||||
local
|
||||
pf: detachable CMS_USER_PROFILE
|
||||
do
|
||||
pf := user_profile (a_user)
|
||||
if pf = Void then
|
||||
create pf.make
|
||||
end
|
||||
pf.force (a_profile_item_value, a_profile_item_name)
|
||||
save_user_profile (a_user, pf)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_PROFILE_STORAGE_NULL}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE_STORAGE_NULL
|
||||
|
||||
inherit
|
||||
CMS_USER_PROFILE_STORAGE_I
|
||||
|
||||
feature -- Error handler
|
||||
|
||||
error_handler: ERROR_HANDLER
|
||||
-- Error handler.
|
||||
do
|
||||
create Result.make
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,212 @@
|
||||
note
|
||||
description: "Interface for accessing user profile contents from SQL database."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_PROFILE_STORAGE_SQL
|
||||
|
||||
inherit
|
||||
CMS_USER_PROFILE_STORAGE_I
|
||||
redefine
|
||||
user_profile_item,
|
||||
save_user_profile_item
|
||||
end
|
||||
|
||||
CMS_PROXY_STORAGE_SQL
|
||||
|
||||
CMS_STORAGE_SQL_I
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
-- User profile for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
reset_error
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (a_item_name, "key")
|
||||
sql_query (sql_select_user_profile_item, l_parameters)
|
||||
if not has_error then
|
||||
Result := sql_read_string_32 (2)
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
user_profile (a_user: CMS_USER): detachable CMS_USER_PROFILE
|
||||
-- User profile for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
reset_error
|
||||
create l_parameters.make (1)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
sql_query (sql_select_user_profile_items, l_parameters)
|
||||
if not has_error then
|
||||
create Result.make
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if
|
||||
attached sql_read_string_32 (1) as l_key and
|
||||
attached sql_read_string_32 (2) as l_val
|
||||
then
|
||||
Result.force (l_val, l_key)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
|
||||
-- Users having a profile item `a_item_name:a_value`.
|
||||
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
l_uids: ARRAYED_LIST [INTEGER_64]
|
||||
do
|
||||
reset_error
|
||||
create l_parameters.make (2)
|
||||
l_parameters.put (a_item_name, "key")
|
||||
if a_value = Void then
|
||||
sql_query (sql_select_users_with_profile_item_named, l_parameters)
|
||||
else
|
||||
l_parameters.put (a_value, "value")
|
||||
sql_query (sql_select_users_with_profile_item, l_parameters)
|
||||
end
|
||||
if not has_error then
|
||||
create l_uids.make (0)
|
||||
from
|
||||
sql_start
|
||||
until
|
||||
sql_after or has_error
|
||||
loop
|
||||
if
|
||||
attached sql_read_integer_64 (1) as l_uid and then
|
||||
l_uid > 0
|
||||
then
|
||||
l_uids.force (l_uid)
|
||||
end
|
||||
sql_forth
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
if
|
||||
not has_error and
|
||||
l_uids /= Void and
|
||||
attached api as l_cms_api
|
||||
then
|
||||
create {ARRAYED_LIST [CMS_USER]} Result.make (l_uids.count)
|
||||
across
|
||||
l_uids as ic
|
||||
loop
|
||||
if attached l_cms_api.user_api.user_by_id (ic.item) as u then
|
||||
Result.force (u)
|
||||
else
|
||||
check known_user: False end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
save_user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL; a_item_value: READABLE_STRING_GENERAL)
|
||||
-- Save user profile item `a_item_name:a_item_value` for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
create l_parameters.make (3)
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (a_item_name, "key")
|
||||
l_parameters.put (a_item_value, "value")
|
||||
|
||||
reset_error
|
||||
if user_profile_item (a_user, a_item_name) = Void then
|
||||
sql_insert (sql_insert_user_profile_item, l_parameters)
|
||||
else
|
||||
sql_modify (sql_update_user_profile_item, l_parameters)
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
|
||||
-- Save user profile `a_profile' for `a_user'.
|
||||
local
|
||||
l_parameters: STRING_TABLE [detachable ANY]
|
||||
p: detachable CMS_USER_PROFILE
|
||||
l_item: like user_profile_item
|
||||
l_is_new: BOOLEAN
|
||||
l_has_diff: BOOLEAN
|
||||
do
|
||||
p := user_profile (a_user)
|
||||
|
||||
create l_parameters.make (3)
|
||||
|
||||
reset_error
|
||||
across
|
||||
a_profile as ic
|
||||
until
|
||||
has_error
|
||||
loop
|
||||
l_item := ic.item
|
||||
-- No previous profile, or no item with same name, or same value
|
||||
l_has_diff := True
|
||||
if p = Void then
|
||||
l_is_new := True
|
||||
elseif p.has_key (ic.key) then
|
||||
l_is_new := False
|
||||
l_has_diff := attached p.item (ic.key) as l_prev_item and then not l_prev_item.same_string (l_item)
|
||||
else
|
||||
l_is_new := True
|
||||
end
|
||||
if l_has_diff then
|
||||
l_parameters.put (a_user.id, "uid")
|
||||
l_parameters.put (ic.key, "key")
|
||||
l_parameters.put (l_item, "value")
|
||||
|
||||
if l_is_new then
|
||||
sql_insert (sql_insert_user_profile_item, l_parameters)
|
||||
else
|
||||
sql_modify (sql_update_user_profile_item, l_parameters)
|
||||
end
|
||||
l_parameters.wipe_out
|
||||
end
|
||||
end
|
||||
sql_finalize
|
||||
end
|
||||
|
||||
feature {NONE} -- Queries
|
||||
|
||||
sql_select_user_profile_items: STRING = "SELECT key, value FROM user_profiles WHERE uid=:uid;"
|
||||
-- user profile items for :uid;
|
||||
|
||||
sql_select_user_profile_item: STRING = "SELECT key, value FROM user_profiles WHERE uid=:uid AND key=:key"
|
||||
-- user profile items for :uid;
|
||||
|
||||
sql_select_users_with_profile_item: STRING = "SELECT uid FROM user_profiles WHERE key=:key and value=:value"
|
||||
-- users with profile item named :key and value :value;
|
||||
|
||||
sql_select_users_with_profile_item_named: STRING = "SELECT uid FROM user_profiles WHERE key=:key"
|
||||
-- users with profile item named :key;
|
||||
|
||||
sql_insert_user_profile_item: STRING = "INSERT INTO user_profiles (uid, key, value) VALUES (:uid, :key, :value);"
|
||||
-- new user profile item for :uid;
|
||||
|
||||
sql_update_user_profile_item: STRING = "UPDATE user_profiles SET value = :value WHERE uid = :uid AND key = :key;"
|
||||
-- user profile items for :uid;
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ACCESS_TOKEN_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_AUTH_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
local
|
||||
tok: READABLE_STRING_GENERAL
|
||||
do
|
||||
if
|
||||
attached req.http_authorization as l_auth and then
|
||||
l_auth.starts_with_general ("Bearer ")
|
||||
then
|
||||
tok := l_auth.substring (8, l_auth.count)
|
||||
if attached api.user_api.users_with_profile_item ("access_token", tok) as lst then
|
||||
if lst.count = 1 then
|
||||
api.set_user (lst.first)
|
||||
end
|
||||
end
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
182
src/modules/core/webapi/cms_access_token_webapi_handler.e
Normal file
182
src/modules/core/webapi/cms_access_token_webapi_handler.e
Normal file
@@ -0,0 +1,182 @@
|
||||
note
|
||||
description: "Summary description for {CMS_ACCESS_TOKEN_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ACCESS_TOKEN_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
l_uid: READABLE_STRING_GENERAL
|
||||
do
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_uid then
|
||||
l_uid := p_uid.value
|
||||
if req.is_post_request_method then
|
||||
post_access_token (l_uid, req, res)
|
||||
elseif req.is_get_request_method then
|
||||
get_access_token (l_uid, req, res)
|
||||
else
|
||||
send_bad_request (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_bad_request ("Missing {uid} parameter", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
user_by_uid (a_uid: READABLE_STRING_GENERAL): detachable CMS_USER
|
||||
do
|
||||
Result := api.user_api.user_by_id_or_name (a_uid)
|
||||
end
|
||||
|
||||
feature -- Request execution
|
||||
|
||||
get_access_token (a_uid: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
if attached user_by_uid (a_uid) as l_user then
|
||||
if attached api.user as u then
|
||||
if u.same_as (l_user) or api.user_api.is_admin_user (u) then
|
||||
rep := new_access_token_webapi_response (l_user, user_access_token (l_user), req, res)
|
||||
if attached {WSF_STRING} req.item ("destination") as dest then
|
||||
rep.set_redirection (dest.url_encoded_value)
|
||||
end
|
||||
rep.execute
|
||||
else
|
||||
-- Only admin, or current user can see its access_token!
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_not_found ("User not found", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
post_access_token (a_uid: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
l_access_token: detachable READABLE_STRING_32
|
||||
rep: like new_webapi_response
|
||||
do
|
||||
if attached user_by_uid (a_uid) as l_user then
|
||||
if attached api.user as u then
|
||||
if u.same_as (l_user) or api.user_api.is_admin_user (u) then
|
||||
if attached req.path_parameter ("application") then
|
||||
|
||||
end
|
||||
-- l_access_token := user_access_token (l_user)
|
||||
l_access_token := new_key (40)
|
||||
|
||||
-- if l_access_token /= Void then
|
||||
-- l_access_token := "Updated-" + (create {UUID_GENERATOR}).generate_uuid.out
|
||||
-- else
|
||||
-- l_access_token := "New-" + (create {UUID_GENERATOR}).generate_uuid.out
|
||||
-- end
|
||||
set_user_access_token (l_user, l_access_token)
|
||||
|
||||
rep := new_access_token_webapi_response (l_user, l_access_token, req, res)
|
||||
if attached {WSF_STRING} req.item ("destination") as dest then
|
||||
rep.set_redirection (dest.url_encoded_value)
|
||||
end
|
||||
rep.execute
|
||||
else
|
||||
-- Only admin, or current user can create the user access_token!
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
else
|
||||
send_not_found ("User not found", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
user_access_token (a_user: CMS_USER): detachable READABLE_STRING_8
|
||||
do
|
||||
if
|
||||
attached api.user_api.user_profile_item ("access_token", a_user) as l_access_token and then
|
||||
not l_access_token.is_whitespace and then
|
||||
l_access_token.is_valid_as_string_8
|
||||
then
|
||||
Result := l_access_token.to_string_8
|
||||
else
|
||||
-- Result := new_key (40)
|
||||
end
|
||||
end
|
||||
|
||||
set_user_access_token (a_user: CMS_USER; a_access_token: READABLE_STRING_GENERAL)
|
||||
do
|
||||
api.user_api.save_user_profile_item (a_user, "access_token", a_access_token)
|
||||
end
|
||||
|
||||
new_access_token_webapi_response (a_user: CMS_USER; a_access_token: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE): like new_webapi_response
|
||||
local
|
||||
tb: STRING_TABLE [detachable ANY]
|
||||
do
|
||||
Result := new_webapi_response (req, res)
|
||||
if a_access_token /= Void then
|
||||
Result.add_string_field ("access_token", a_access_token)
|
||||
else
|
||||
Result.add_string_field ("access_token", "NONE")
|
||||
Result.add_link ("new_access_token", "user/" + a_user.id.out + "/access_token", req.percent_encoded_path_info)
|
||||
end
|
||||
create tb.make_equal (3)
|
||||
tb.force (a_user.name, "name")
|
||||
tb.force (a_user.id, "uid")
|
||||
Result.add_table_iterator_field ("user", tb)
|
||||
|
||||
-- Result.add_string_field ("username", a_user.name)
|
||||
-- Result.add_integer_64_field ("uid", a_user.id)
|
||||
Result.add_self (req.percent_encoded_path_info)
|
||||
add_user_links_to (a_user, Result)
|
||||
end
|
||||
|
||||
new_key (len: INTEGER): STRING_8
|
||||
local
|
||||
rand: RANDOM
|
||||
n: INTEGER
|
||||
v: NATURAL_32
|
||||
do
|
||||
create rand.set_seed ((create {DATE_TIME}.make_now_utc).seconds)
|
||||
rand.start
|
||||
create Result.make (len)
|
||||
from
|
||||
n := 1
|
||||
until
|
||||
n = len
|
||||
loop
|
||||
rand.forth
|
||||
v := (rand.item \\ 16).to_natural_32
|
||||
check 0 <= v and v <= 15 end
|
||||
if v < 9 then
|
||||
Result.append_code (48 + v) -- 48 '0'
|
||||
else
|
||||
Result.append_code (97 + v - 9) -- 97 'a'
|
||||
end
|
||||
n := n + 1
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
43
src/modules/core/webapi/cms_basic_webapi_auth_filter.e
Normal file
43
src/modules/core/webapi/cms_basic_webapi_auth_filter.e
Normal file
@@ -0,0 +1,43 @@
|
||||
note
|
||||
description: "Summary description for {CMS_BASIC_WEBAPI_AUTH_FILTER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_BASIC_WEBAPI_AUTH_FILTER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_AUTH_FILTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
local
|
||||
l_auth: HTTP_AUTHORIZATION
|
||||
do
|
||||
create l_auth.make (req.http_authorization)
|
||||
if
|
||||
l_auth.is_basic and then
|
||||
attached l_auth.login as l_auth_login and then
|
||||
attached l_auth.password as l_auth_password
|
||||
then
|
||||
if
|
||||
api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then
|
||||
attached api.user_api.user_by_name (l_auth_login) as l_user
|
||||
then
|
||||
api.set_user (l_user)
|
||||
else
|
||||
-- not authenticated due to bad login or password.
|
||||
end
|
||||
end
|
||||
execute_next (req, res)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
57
src/modules/core/webapi/cms_core_module_webapi.e
Normal file
57
src/modules/core/webapi/cms_core_module_webapi.e
Normal file
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description: "Summary description for {CMS_CORE_MODULE_WEBAPI}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_CORE_MODULE_WEBAPI
|
||||
|
||||
inherit
|
||||
CMS_MODULE_WEBAPI [CMS_CORE_MODULE]
|
||||
redefine
|
||||
permissions,
|
||||
filters
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Security
|
||||
|
||||
permissions: LIST [READABLE_STRING_8]
|
||||
-- List of permission ids, used by this module, and declared.
|
||||
do
|
||||
Result := Precursor
|
||||
Result.force ("admin users")
|
||||
Result.force ("view users")
|
||||
end
|
||||
|
||||
feature {NONE} -- Router/administration
|
||||
|
||||
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
|
||||
-- <Precursor>
|
||||
local
|
||||
l_root: CMS_ROOT_WEBAPI_HANDLER
|
||||
do
|
||||
create l_root.make (a_api)
|
||||
a_router.handle ("", l_root, a_router.methods_get)
|
||||
a_router.handle ("/", l_root, a_router.methods_get)
|
||||
a_router.handle ("/user/{uid}/access_token", create {CMS_ACCESS_TOKEN_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post)
|
||||
a_router.handle ("/user/{uid}", create {CMS_USER_WEBAPI_HANDLER}.make (a_api), a_router.methods_get)
|
||||
end
|
||||
|
||||
feature -- Access: filter
|
||||
|
||||
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
|
||||
-- Possibly list of Filter's module.
|
||||
do
|
||||
create {ARRAYED_LIST [WSF_FILTER]} Result.make (2)
|
||||
Result.extend (create {CMS_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}.make (a_api))
|
||||
Result.extend (create {CMS_BASIC_WEBAPI_AUTH_FILTER}.make (a_api))
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
36
src/modules/core/webapi/cms_root_webapi_handler.e
Normal file
36
src/modules/core/webapi/cms_root_webapi_handler.e
Normal file
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Summary description for {CMS_ROOT_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_ROOT_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
WSF_URI_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
do
|
||||
rep := new_webapi_response (req, res)
|
||||
rep.add_string_field ("site_name", api.setup.site_name)
|
||||
if attached api.user as u then
|
||||
add_user_links_to (u, rep)
|
||||
end
|
||||
rep.add_self (req.percent_encoded_path_info)
|
||||
rep.execute
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
80
src/modules/core/webapi/cms_user_webapi_handler.e
Normal file
80
src/modules/core/webapi/cms_user_webapi_handler.e
Normal file
@@ -0,0 +1,80 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USER_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USER_WEBAPI_HANDLER
|
||||
|
||||
inherit
|
||||
CMS_WEBAPI_HANDLER
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
do
|
||||
if req.is_get_request_method then
|
||||
execute_get (req, res)
|
||||
else
|
||||
send_bad_request (Void, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
l_user: detachable CMS_USER
|
||||
do
|
||||
if attached api.user as u then
|
||||
if attached {WSF_STRING} req.path_parameter ("uid") as p_uid then
|
||||
if p_uid.is_integer then
|
||||
l_user := api.user_api.user_by_id (p_uid.integer_value)
|
||||
else
|
||||
l_user := api.user_api.user_by_name (p_uid.value)
|
||||
end
|
||||
-- if l_user = Void and p_uid.is_case_insensitive_equal ("me") then
|
||||
-- l_user := u
|
||||
-- end
|
||||
if l_user /= Void then
|
||||
if l_user.same_as (u) or api.has_permissions (<<"admin users", "view users">>) then
|
||||
rep := new_webapi_response (req, res)
|
||||
rep.add_string_field ("uid", u.id.out)
|
||||
|
||||
rep.add_string_field ("name", u.name)
|
||||
if attached u.email as l_email then
|
||||
rep.add_string_field ("email", l_email)
|
||||
end
|
||||
if attached u.profile_name as l_profile_name then
|
||||
rep.add_string_field ("profile_name", l_profile_name)
|
||||
end
|
||||
add_user_links_to (u, rep)
|
||||
else
|
||||
rep := new_wepapi_error_response ("denied", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
|
||||
end
|
||||
else
|
||||
rep := new_wepapi_error_response ("Not found", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
end
|
||||
else
|
||||
rep := new_wepapi_error_response ("Bad request", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.bad_request)
|
||||
end
|
||||
rep.execute
|
||||
else
|
||||
-- FIXME: use specific Web API response!
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
Reference in New Issue
Block a user