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:
Jocelyn Fiat
2017-09-05 15:54:40 +02:00
parent 34f0aa5844
commit ac9d29b971
88 changed files with 3552 additions and 553 deletions

View File

@@ -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)"

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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