Added CMS_API.request: WSF_REQUEST to ease dev of ROC CMS code.

- Removed CMS_REQUEST_UTIL
  - centralize a few request related code into CMS_API
Added CMS_API.user, CMS_API.set_user (CMS_USER), ... and user related routines.

Refactored Auth related code
  - added various abstractions to factorize implementation and harmonize solutions.
  - revisited the logout strategy.
  - updated the account info page, and remove info user should not care about.
  - simplified the process, and encourage auth module to follow same design.

Added CMS_LINK helper routines to modify the related query string.
Removed CMS_USER.profile (and related routines)
   - It was not used so far.
   - it will probably a specific module later, if needed.

Update various module to avoid fetching user from sql directly, and let this task to CMS_USER_API.

Removed CMS_NODE_API.node_author (a_node: CMS_NODE): detachable CMS_USER,
   - as the info is already in CMS_NODE.author

Added CMS_RESPONSE.redirection_delay, if ever one code want to redirect after a few seconds.
Added the request uri info to the not found cms response.
This commit is contained in:
2016-01-29 21:58:49 +01:00
parent 41ac45d07b
commit 3496536751
67 changed files with 1742 additions and 1820 deletions

View File

@@ -12,6 +12,9 @@ class
inherit
CMS_BLOCK
redefine
append_to_html
end
create
make_with_block
@@ -47,6 +50,12 @@ feature -- Status report
feature -- Conversion
append_to_html (a_theme: CMS_THEME; a_output: STRING_8)
-- Append HTML representation of Current block to `a_output'.
do
origin.append_to_html (a_theme, a_output)
end
to_html (a_theme: CMS_THEME): STRING_8
-- HTML representation of Current block.
do
@@ -54,6 +63,6 @@ feature -- Conversion
end
;note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -82,6 +82,12 @@ feature -- Element change
feature -- Conversion
append_to_html (a_theme: CMS_THEME; a_output: STRING_8)
-- Append HTML representation of Current block to `a_output'.
do
a_output.append (to_html (a_theme))
end
to_html (a_theme: CMS_THEME): STRING_8
-- HTML representation of Current block.
deferred
@@ -112,6 +118,6 @@ feature -- Status report
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -78,7 +78,7 @@ feature -- Conversion
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -27,6 +27,16 @@ feature -- Encoders
Result := html_encoder.general_encoded_string (a_string)
end
safe_html_encoded (a_string: detachable READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded for html output or empty string.
do
if a_string /= Void then
Result := html_encoded (a_string)
else
Result := ""
end
end
url_encoded,
percent_encoded (a_string: READABLE_STRING_GENERAL): STRING_8
-- `a_string' encoded with percent encoding, mainly used for url.
@@ -34,4 +44,7 @@ feature -- Encoders
Result := percent_encoder.percent_encoded_string (a_string)
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -190,6 +190,6 @@ feature -- Debug
Result.append ("%N}")
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -118,33 +118,8 @@ feature -- Change: user
deferred
end
feature -- Access: roles and permissions
-- user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN
-- -- Anonymous or user `u' has permission for `s' ?
-- --| `s' could be "create page",
-- do
---- if s = Void then
---- Result := True
---- elseif u = Void then
------ Result := user_role_has_permission (anonymous_user_role, s)
---- else
---- Result := user_role_has_permission (authenticated_user_role, s)
---- if not Result and attached u.roles as l_roles then
---- across
---- l_roles as r
---- until
---- Result
---- loop
---- if attached user_role_by_id (r.item) as ur then
---- Result := user_role_has_permission (ur, s)
---- end
---- end
---- end
---- end
-- end
user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN
do
Result := a_role.has_permission (s)

View File

@@ -76,7 +76,6 @@ feature -- Change: user
do
end
feature -- Access: roles and permissions
user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE

View File

@@ -267,13 +267,14 @@ feature -- Change: user
sql_begin_transaction
write_information_log (generator + ".update_user")
create l_parameters.make (6)
create l_parameters.make (7)
l_parameters.put (a_user.id, "uid")
l_parameters.put (a_user.name, "name")
l_parameters.put (l_password_hash, "password")
l_parameters.put (l_password_salt, "salt")
l_parameters.put (l_email, "email")
l_parameters.put (a_user.status, "status")
l_parameters.put (a_user.last_login_date, "signed")
sql_modify (sql_update_user, l_parameters)
sql_finalize
@@ -307,6 +308,8 @@ feature -- Change: user
sql_finalize
end
feature -- Change: roles
update_user_roles (a_user: CMS_USER)
-- Update roles of `a_user'
require
@@ -847,6 +850,9 @@ feature {NONE} -- Implementation: User
if attached sql_read_integer_32 (6) as l_status then
Result.set_status (l_status)
end
if attached sql_read_date_time (8) as l_signed_date then
Result.set_last_login_date (l_signed_date)
end
else
check expected_valid_user: False end
end
@@ -903,7 +909,7 @@ feature {NONE} -- Sql Queries: USER
sql_insert_user: STRING = "INSERT INTO users (name, password, salt, email, created, status) VALUES (:name, :password, :salt, :email, :created, :status);"
-- SQL Insert to add a new user.
sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email, status=:status WHERE uid=:uid;"
sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email, status=:status, signed=:signed WHERE uid=:uid;"
-- SQL update to update an existing user.
sql_delete_user: STRING = "DELETE FROM users WHERE uid=:uid;"

View File

@@ -9,22 +9,24 @@ class
inherit
ANY
CMS_ENCODERS
CMS_HOOK_EXPORT
CMS_EXPORT_JSON_UTILITIES
REFACTORING_HELPER
CMS_REQUEST_UTIL
create
make
feature {NONE} -- Initialize
make (a_setup: CMS_SETUP)
-- Create the API service with a setup `a_setup'
make (a_setup: CMS_SETUP; req: WSF_REQUEST)
-- Create the API service with a setup `a_setup'
-- and request `req'.
do
request := req
setup := a_setup
create error_handler.make
create {CMS_ENV_LOGGER} logger.make
@@ -168,6 +170,12 @@ feature -- Access
storage: CMS_STORAGE
-- Default persistence storage.
feature {NONE} -- Access: request
request: WSF_REQUEST
-- Associated http request.
--| note: here for the sole purpose of CMS_API.
feature -- Content
content_types: ARRAYED_LIST [CMS_CONTENT_TYPE]
@@ -375,6 +383,13 @@ feature {NONE} -- Emails implementation
feature -- Permissions system
has_permission (a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN
-- Anonymous or user `user' has permission for `a_permission'?
--| `a_permission' could be for instance "create page".
do
Result := user_api.user_has_permission (user, a_permission)
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".
@@ -866,6 +881,113 @@ feature -- Hook
end
end
feature -- Access: active user
user_is_authenticated: BOOLEAN
-- Is user authenticated?
do
Result := user /= Void
ensure
Result implies user /= Void
end
user: detachable CMS_USER
-- Current user or Void in case of visitor.
note
EIS: "eiffel:?class=CMS_BASIC_AUTH_FILTER&feature=execute"
do
Result := current_user (request)
end
set_user (a_user: CMS_USER)
-- Set `a_user' as current `user'.
require
a_user_attached: a_user /= Void
do
set_current_user (request, a_user)
end
unset_user
-- Unset `user'.
do
unset_current_user (request)
end
record_user_login (a_user: CMS_USER)
-- Record login event for `a_user'.
require
user_has_id: a_user.has_id
do
a_user.set_last_login_date_now
user_api.update_user (a_user)
end
feature -- Request utilities
execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY
-- Execution variable related to `a_name'
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
Result := request.execution_variable (a_name)
end
set_execution_variable (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY)
do
request.set_execution_variable (a_name, a_value)
ensure
param_set: execution_variable (a_name) = a_value
end
unset_execution_variable (a_name: READABLE_STRING_GENERAL)
do
request.unset_execution_variable (a_name)
ensure
param_unset: execution_variable (a_name) = Void
end
feature {CMS_API_ACCESS, CMS_RESPONSE, CMS_MODULE} -- Request utilities
current_user (req: WSF_REQUEST): detachable CMS_USER
-- Current user or Void in case of Guest user.
do
check req = request end
if attached {CMS_USER} execution_variable (cms_execution_variable_name ("user")) as l_user then
Result := l_user
end
end
set_current_user (req: WSF_REQUEST; a_user: CMS_USER)
-- Set `a_user' as `current_user'.
do
check req = request end
set_execution_variable (cms_execution_variable_name ("user"), a_user)
ensure
user_set: current_user (req) ~ a_user
end
unset_current_user (req: WSF_REQUEST)
-- Unset current user.
do
check req = request end
req.unset_execution_variable (cms_execution_variable_name ("user"))
ensure
user_unset: current_user (req) = Void
end
feature {NONE} -- Implementation: current user
cms_execution_variable_name (a_name: READABLE_STRING_GENERAL): READABLE_STRING_GENERAL
-- Execution variable name for `a_name'.
local
s32: STRING_32
do
create s32.make_from_string_general (once "_roccms_.")
s32.append_string_general (a_name)
Result := s32
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -43,7 +43,7 @@ feature {NONE} -- Initialization
l_setup := initial_cms_setup
setup_storage (l_setup)
setup_modules (l_setup)
create api.make (l_setup)
create api.make (l_setup, request)
modules := api.enabled_modules
initialize_cms
@@ -314,7 +314,7 @@ feature -- Execution
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -9,6 +9,8 @@ deferred class
inherit
REFACTORING_HELPER
CMS_ENCODERS
feature -- Access
is_enabled: BOOLEAN
@@ -193,6 +195,6 @@ invariant
version_set: not version.is_whitespace
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -11,7 +11,9 @@ deferred class
inherit
WSF_HANDLER
CMS_REQUEST_UTIL
CMS_API_ACCESS
CMS_ENCODERS
REFACTORING_HELPER
@@ -93,4 +95,7 @@ feature -- Response helpers
r.execute
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -1,77 +0,0 @@
note
description: "Set of helper features related to CMS Request needs."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
deferred class
CMS_REQUEST_UTIL
inherit
CMS_ENCODERS
REFACTORING_HELPER
feature -- User
current_user_name (req: WSF_REQUEST): detachable READABLE_STRING_32
-- Current user name or Void in case of Guest users.
note
EIS: "src=eiffel:?class=AUTHENTICATION_FILTER&feature=execute"
do
if attached {CMS_USER} current_user (req) as l_user then
Result := l_user.name
end
end
current_user (req: WSF_REQUEST): detachable CMS_USER
-- Current user or Void in case of Guest user.
-- note: if a CMS_RESPONSE is available, always prefer {CMS_RESPONSE}.user if relevant.
note
EIS: "eiffel:?class=AUTHENTICATION_FILTER&feature=execute"
do
if attached {CMS_USER} req.execution_variable (current_user_execution_variable_name) as l_user then
Result := l_user
end
end
feature -- Change
set_current_user (req: WSF_REQUEST; a_user: detachable CMS_USER)
-- Set `a_user' as `current_user'.
do
if a_user = Void then
req.unset_execution_variable (current_user_execution_variable_name)
else
req.set_execution_variable (current_user_execution_variable_name, a_user)
end
ensure
user_set: current_user (req) ~ a_user
end
unset_current_user (req: WSF_REQUEST)
-- Unset current user.
do
req.unset_execution_variable (current_user_execution_variable_name)
ensure
user_unset: current_user (req) = Void
end
feature {NONE} -- Implementation: current user
current_user_execution_variable_name: STRING = "_cms_active_user_"
-- Execution variable name used to keep current user data.
feature -- Media Type
current_media_type (req: WSF_REQUEST): detachable READABLE_STRING_32
-- Current media type or Void if it's not acceptable.
do
if attached {STRING} req.execution_variable ("media_type") as l_type then
Result := l_type
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {CMS_URL_UTILITIES}."
author: ""
description: "Collection of helper routines to manipulate URL for CMS."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
@@ -8,7 +7,7 @@ deferred class
CMS_URL_UTILITIES
inherit
CMS_REQUEST_UTIL
CMS_ENCODERS
feature -- Core
@@ -43,12 +42,16 @@ feature -- Core
feature -- Link
link (a_text: detachable READABLE_STRING_GENERAL; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
-- HTML link with title `a_text' and href `a_path'.
-- `opts' is used for additional settings.
do
create Result.make (32)
append_link_to_html (a_text, a_path, opts, Result)
end
link_with_raw_text (a_raw_text: detachable READABLE_STRING_8; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
-- HTML link with title the html code `a_raw_text' and href `a_path'.
-- `opts' is used for additional settings.
do
create Result.make (32)
append_link_with_raw_text_to_html (a_raw_text, a_path, opts, Result)
@@ -180,6 +183,7 @@ feature -- Url
checked_url (a_url: READABLE_STRING_8): READABLE_STRING_8
do
-- FIXME: implement a way to check if `a_url' is safe, and does not reveal security issue.
Result := a_url
end
@@ -189,6 +193,6 @@ feature -- Url
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -116,6 +116,9 @@ feature -- Access: metadata
redirection: detachable READABLE_STRING_8
-- Location for eventual redirection.
redirection_delay: NATURAL
-- Optional redirection delay in seconds.
feature -- Access: query
location: STRING_8
@@ -196,8 +199,23 @@ feature -- User access
end
user: detachable CMS_USER
-- Active user if authenticated.
do
Result := current_user (request)
Result := api.user
end
set_user (u: CMS_USER)
-- Set active user to `u'.
require
attached_u: u /= Void
do
api.set_user (u)
end
unset_user
-- Unset active user.
do
api.unset_user
end
feature -- Permission
@@ -356,6 +374,11 @@ feature -- Element change
redirection := a_location
end
set_redirection_delay (nb_secs: NATURAL)
do
redirection_delay := nb_secs
end
feature -- Logging
log (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_level: INTEGER; a_link: detachable CMS_LINK)
@@ -1212,8 +1235,8 @@ feature -- Generation
page.register_variable (absolute_url ("", Void), "site_url")
page.register_variable (absolute_url ("", Void), "host") -- Same as `site_url'.
page.register_variable (request.is_https, "is_https")
if attached current_user_name (request) as l_user then
page.register_variable (l_user, "user")
if attached user as l_user then
page.register_variable (l_user.name, "user")
end
page.register_variable (title, "site_title")
page.set_is_front (is_front)
@@ -1326,16 +1349,40 @@ feature -- Helpers: cms link
end
end
user_html_link (u: CMS_USER): like link
feature -- Helpers: html links
user_html_link (u: CMS_USER): STRING
do
Result := link (u.name, "user/" + u.id.out, Void)
end
feature -- Helpers: URLs
location_absolute_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
-- Absolute URL for `a_location'.
--| Options `opts' could be
--| - absolute: True|False => return absolute url
--| - query: string => append "?query"
--| - fragment: string => append "#fragment"
do
Result := absolute_url (a_location, opts)
end
location_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING
-- URL for `a_location'.
--| Options `opts' could be
--| - absolute: True|False => return absolute url
--| - query: string => append "?query"
--| - fragment: string => append "#fragment"
do
Result := url (a_location, opts)
end
user_url (u: CMS_USER): like url
require
u_with_id: u.has_id
do
Result := url ("user/" + u.id.out, Void)
Result := location_url ("user/" + u.id.out, Void)
end
feature -- Execution
@@ -1363,8 +1410,22 @@ feature {NONE} -- Execution
page: CMS_HTML_PAGE_RESPONSE
utf: UTF_CONVERTER
h: HTTP_HEADER
l_new_location: READABLE_STRING_8
l_new_location: detachable READABLE_STRING_8
l_redirection_delay: like redirection_delay
do
if attached redirection as l_location then
-- FIXME: find out if this is safe or not.
if l_location.has_substring ("://") then
l_new_location := l_location
else
l_new_location := location_absolute_url (l_location, Void)
end
l_redirection_delay := redirection_delay
if l_redirection_delay > 0 then
add_additional_head_line ("<meta http-equiv=%"refresh%" content=%"" + l_redirection_delay.out + ";url=" + l_new_location + "%" />", True)
end
end
if attached {READABLE_STRING_GENERAL} values.item ("optional_content_type") as l_type then
create cms_page.make_typed (utf.utf_32_string_to_utf_8_string_8 (l_type))
else
@@ -1376,14 +1437,7 @@ feature {NONE} -- Execution
h := page.header
h.put_content_length (page.html.count)
h.put_current_date
if attached redirection as l_location then
-- FIXME: find out if this is safe or not.
if l_location.has_substring ("://") then
l_new_location := l_location
else
l_new_location := absolute_url (l_location, Void)
end
-- h.put_location (l_new_location)
if l_new_location /= Void and l_redirection_delay = 0 then
response.redirect_now (l_new_location)
else
h.put_header_object (header)

View File

@@ -33,10 +33,10 @@ feature -- Execution
do
set_title ("Not Found")
set_page_title ("Not Found")
set_main_content ("<em>The requested page could not be found.</em>")
set_main_content ("<em>The requested page %"" + request.request_uri + "%"could not be found.</em>")
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -14,7 +14,7 @@ inherit
create
make
feature -- Access
feature -- Access: user
user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER
-- User by id `a_id', if any.
@@ -58,6 +58,44 @@ feature -- Access
Result := storage.recent_users (params.offset.to_integer_32, params.size.to_integer_32)
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
storage.new_user (a_user)
error_handler.append (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_user (a_user: CMS_USER)
-- Update user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.update_user (a_user)
error_handler.append (storage.error_handler)
end
delete_user (a_user: CMS_USER)
-- Delete user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.delete_user (a_user)
error_handler.append (storage.error_handler)
end
feature -- Status report
@@ -241,45 +279,6 @@ feature -- Change User role
error_handler.append (storage.error_handler)
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
storage.new_user (a_user)
error_handler.append (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_user (a_user: CMS_USER)
-- Update user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.update_user (a_user)
error_handler.append (storage.error_handler)
end
delete_user (a_user: CMS_USER)
-- Delete user `a_user'.
require
has_id: a_user.has_id
do
reset_error
storage.delete_user (a_user)
error_handler.append (storage.error_handler)
end
feature -- User Activation
new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)