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 1739 additions and 1817 deletions
+126 -4
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)"
+2 -2
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
+3 -1
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
+6 -1
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
-77
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
+8 -4
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
+68 -14
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)
@@ -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
+39 -40
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)