Added auth_api: CMS_AUTHENTICATION_API, and for now moved registration instructions inside.
Added authentication module webapi, to provide registration via webapi. Improved the roles display by providing table of permissions if asked. Added various links in primary tabs to navigate back to roles or users, depending on the page. Added datetime to-from string converters in CMS_ENCODERS. Start removing CMS_ADMINISTRABLE. Added permission to use simple core access token. Added webapi for users: list, new, register.
This commit is contained in:
@@ -52,6 +52,27 @@ feature -- Encoders
|
||||
Result := percent_encoder.percent_encoded_string (a_string)
|
||||
end
|
||||
|
||||
date_time_to_string (dt: DATE_TIME): STRING_8
|
||||
-- Date time `dt` converted to standard output (using RFC1123)
|
||||
local
|
||||
hd: HTTP_DATE
|
||||
do
|
||||
create hd.make_from_date_time (dt)
|
||||
Result := hd.rfc1123_string
|
||||
end
|
||||
|
||||
date_time_from_string (s: READABLE_STRING_GENERAL): detachable DATE_TIME
|
||||
-- Date time from string `s`, if valid.
|
||||
local
|
||||
hd: HTTP_DATE
|
||||
do
|
||||
create hd.make_from_string (s)
|
||||
check not hd.has_error end
|
||||
if not hd.has_error then
|
||||
Result := hd.date_time
|
||||
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)"
|
||||
|
||||
@@ -74,12 +74,24 @@ feature -- Conversion
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
process (a_response: CMS_RESPONSE_I)
|
||||
do
|
||||
if attached {CMS_RESPONSE} a_response as rep_cms then
|
||||
process_cms_response (rep_cms)
|
||||
else
|
||||
-- FIXME: check webapi for hook need!
|
||||
process_form (a_response.request, Void, Void)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- CMS response
|
||||
|
||||
prepare (a_response: CMS_RESPONSE)
|
||||
do
|
||||
a_response.api.hooks.invoke_form_alter (Current, Void, a_response)
|
||||
end
|
||||
|
||||
process (a_response: CMS_RESPONSE)
|
||||
process_cms_response (a_response: CMS_RESPONSE)
|
||||
do
|
||||
process_form (a_response.request, agent on_prepared (a_response, ?), agent on_processed (a_response, ?))
|
||||
end
|
||||
@@ -96,6 +108,41 @@ feature -- Basic operation
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Webapi processing
|
||||
|
||||
process_webapi_response ()
|
||||
do
|
||||
|
||||
end
|
||||
|
||||
feature -- Helpers
|
||||
|
||||
extend_text_field (a_name: READABLE_STRING_8; a_text: detachable READABLE_STRING_GENERAL)
|
||||
-- Extend new text field `a_name` with value `a_text`.
|
||||
local
|
||||
tf: WSF_FORM_TEXT_INPUT
|
||||
do
|
||||
if a_text /= Void then
|
||||
create tf.make_with_text (a_name, a_text.as_string_32)
|
||||
else
|
||||
create tf.make (a_name)
|
||||
end
|
||||
extend (tf)
|
||||
end
|
||||
|
||||
extend_password_field (a_name: READABLE_STRING_8; a_text: detachable READABLE_STRING_GENERAL)
|
||||
-- Extend new password field `a_name` with value `a_text`.
|
||||
local
|
||||
tf: WSF_FORM_PASSWORD_INPUT
|
||||
do
|
||||
if a_text /= Void then
|
||||
create tf.make_with_text (a_name, a_text.as_string_32)
|
||||
else
|
||||
create tf.make (a_name)
|
||||
end
|
||||
extend (tf)
|
||||
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)"
|
||||
|
||||
@@ -85,7 +85,7 @@ feature {CMS_API} -- Module management
|
||||
create u.make ("admin")
|
||||
u.set_password ("istrator#")
|
||||
u.set_email (a_api.setup.site_email)
|
||||
u.set_status ({CMS_USER}.active)
|
||||
u.mark_active
|
||||
a_api.user_api.new_user (u)
|
||||
|
||||
--| Node
|
||||
@@ -130,6 +130,7 @@ feature -- Security
|
||||
Result.force ("import core")
|
||||
Result.force ("admin path_alias")
|
||||
Result.force ("edit path_alias")
|
||||
Result.force ("use access_token")
|
||||
end
|
||||
|
||||
feature {CMS_EXECUTION} -- Administration
|
||||
|
||||
@@ -329,7 +329,7 @@ feature -- User roles.
|
||||
loop
|
||||
lst := ic.item.permissions
|
||||
if
|
||||
attached {CMS_ADMINISTRABLE} ic.item as adm and then
|
||||
attached {CMS_WITH_MODULE_ADMINISTRATION} ic.item as adm and then
|
||||
attached adm.module_administration.permissions as adm_permissions and then
|
||||
not adm_permissions.is_empty
|
||||
then
|
||||
|
||||
@@ -119,17 +119,18 @@ feature -- Process Edit
|
||||
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"))
|
||||
if api.user_has_permission (a_user, "use access_token") then
|
||||
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
|
||||
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
|
||||
|
||||
@@ -18,6 +18,7 @@ feature -- Basic operations
|
||||
-- Execute the filter.
|
||||
local
|
||||
tok: READABLE_STRING_GENERAL
|
||||
u: CMS_USER
|
||||
do
|
||||
if
|
||||
attached req.http_authorization as l_auth and then
|
||||
@@ -26,7 +27,10 @@ feature -- Basic operations
|
||||
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)
|
||||
u := lst.first
|
||||
if api.user_has_permission (u, "use access_token") then
|
||||
api.set_user (u)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,6 +39,7 @@ feature {NONE} -- Router/administration
|
||||
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)
|
||||
a_router.handle ("/user/", create {CMS_USERS_WEBAPI_HANDLER}.make (a_api), a_router.methods_get_post)
|
||||
end
|
||||
|
||||
feature -- Access: filter
|
||||
|
||||
@@ -21,6 +21,8 @@ feature -- Execution
|
||||
do
|
||||
if req.is_get_request_method then
|
||||
execute_get (req, res)
|
||||
-- elseif req.is_post_request_method then
|
||||
-- execute_post (req, res)
|
||||
else
|
||||
send_bad_request (Void, req, res)
|
||||
end
|
||||
@@ -45,16 +47,24 @@ feature -- Execution
|
||||
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 ("uid", l_user.id.out)
|
||||
rep.add_string_field ("name", l_user.name)
|
||||
if attached l_user.email as l_email then
|
||||
rep.add_string_field ("email", l_email)
|
||||
end
|
||||
if attached u.profile_name as l_profile_name then
|
||||
if not l_user.is_active then
|
||||
rep.add_boolean_field ("is_active", False)
|
||||
end
|
||||
if attached l_user.profile_name as l_profile_name then
|
||||
rep.add_string_field ("profile_name", l_profile_name)
|
||||
end
|
||||
add_user_links_to (u, rep)
|
||||
if attached l_user.creation_date as dt then
|
||||
rep.add_string_field ("creation_date", date_time_to_string (dt))
|
||||
end
|
||||
if attached l_user.last_login_date as dt then
|
||||
rep.add_string_field ("last_login_date", date_time_to_string (dt))
|
||||
end
|
||||
add_user_links_to (l_user, rep)
|
||||
else
|
||||
rep := new_wepapi_error_response ("denied", req, res)
|
||||
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
|
||||
@@ -74,6 +84,39 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
|
||||
-- execute_post (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 and then api.has_permission ("admin users") 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
|
||||
-- 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)"
|
||||
|
||||
175
src/modules/core/webapi/cms_users_webapi_handler.e
Normal file
175
src/modules/core/webapi/cms_users_webapi_handler.e
Normal file
@@ -0,0 +1,175 @@
|
||||
note
|
||||
description: "Summary description for {CMS_USERS_WEBAPI_HANDLER}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CMS_USERS_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'.
|
||||
do
|
||||
if req.is_get_request_method then
|
||||
execute_get (req, res)
|
||||
elseif req.is_post_request_method then
|
||||
execute_post (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
|
||||
l_params: CMS_DATA_QUERY_PARAMETERS
|
||||
tb: STRING_TABLE [detachable ANY]
|
||||
arr: ARRAYED_LIST [STRING_TABLE [detachable ANY]]
|
||||
l_full: BOOLEAN
|
||||
nb: INTEGER
|
||||
do
|
||||
if api.has_permissions (<<"admin users", "view users">>) then
|
||||
if attached req.query_parameter ("full") as p and then p.is_case_insensitive_equal ("yes") then
|
||||
l_full := True
|
||||
end
|
||||
rep := new_webapi_response (req, res)
|
||||
nb := api.user_api.users_count
|
||||
rep.add_integer_64_field ("users_count", nb)
|
||||
create l_params.make (0, nb.to_natural_32)
|
||||
create arr.make (nb)
|
||||
across
|
||||
api.user_api.recent_users (l_params) as ic
|
||||
loop
|
||||
l_user := ic.item
|
||||
create tb.make_caseless (5)
|
||||
tb.force (api.webapi_path ("user/" + l_user.id.out), "href")
|
||||
tb.force (l_user.id.out, "uid")
|
||||
tb.force (l_user.name, "name")
|
||||
if attached l_user.profile_name as pn then
|
||||
tb.force (pn, "profile_name")
|
||||
end
|
||||
if not l_user.is_active then
|
||||
tb.force (False, "is_active")
|
||||
end
|
||||
if l_full then
|
||||
if l_user.has_email then
|
||||
tb.force (l_user.email, "email")
|
||||
end
|
||||
if attached l_user.creation_date as dt then
|
||||
tb.force (date_time_to_string (dt), "creation_date")
|
||||
end
|
||||
if attached l_user.last_login_date as dt then
|
||||
tb.force (date_time_to_string (dt), "last_login_date")
|
||||
end
|
||||
end
|
||||
arr.force (tb)
|
||||
end
|
||||
rep.add_iterator_field ("users", arr)
|
||||
rep.add_self (req.percent_encoded_path_info)
|
||||
rep.execute
|
||||
else
|
||||
send_access_denied (Void, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute handler for `req' and respond in `res'.
|
||||
local
|
||||
rep: HM_WEBAPI_RESPONSE
|
||||
l_user: detachable CMS_USER
|
||||
f: WSF_FORM
|
||||
tf: WSF_FORM_TEXT_INPUT
|
||||
err: STRING_32
|
||||
do
|
||||
if api.has_permission ("admin users") then
|
||||
create f.make (req.percent_encoded_path_info, "new-user")
|
||||
create tf.make ("username"); f.extend (tf)
|
||||
create tf.make ("password"); f.extend (tf)
|
||||
create tf.make ("email"); f.extend (tf)
|
||||
create tf.make ("profile_name"); f.extend (tf)
|
||||
|
||||
f.process (req, Void, Void)
|
||||
if attached f.last_data as fd then
|
||||
create err.make_empty
|
||||
if not fd.has_error and then attached fd.string_item ("username") as l_name then
|
||||
if api.user_api.user_by_id_or_name (l_name) /= Void then
|
||||
err.append ("Username already used!%N")
|
||||
fd.report_invalid_field ("username", "Username already used!")
|
||||
else
|
||||
create l_user.make (l_name)
|
||||
if attached fd.string_item ("password") as l_passwd then
|
||||
l_user.set_password (l_passwd)
|
||||
else
|
||||
err.append ("Missing password!%N")
|
||||
fd.report_invalid_field ("username", "Missing password!")
|
||||
end
|
||||
if attached fd.string_item ("email") as l_email then
|
||||
if l_email.is_valid_as_string_8 then
|
||||
l_user.set_email (l_email.to_string_8)
|
||||
else
|
||||
err.append ("Invalid email address!%N")
|
||||
end
|
||||
end
|
||||
if attached fd.string_item ("profile_name") as l_profile_name then
|
||||
l_user.set_profile_name (l_profile_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
if fd.has_error then
|
||||
-- Error !
|
||||
if attached fd.errors as lst then
|
||||
create err.make_empty
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
if attached ic.item.field as l_field then
|
||||
err.append (l_field.name + ": ")
|
||||
end
|
||||
if attached ic.item.message as msg then
|
||||
err.append (msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Keep `err`.
|
||||
end
|
||||
elseif l_user = Void then
|
||||
err := "Invalid new user request!"
|
||||
else
|
||||
err := Void
|
||||
l_user.mark_active
|
||||
api.user_api.new_user (l_user)
|
||||
if api.user_api.has_error then
|
||||
err := "Could not create user!"
|
||||
end
|
||||
end
|
||||
end
|
||||
if l_user = Void or else err /= Void then
|
||||
rep := new_wepapi_error_response (err, req, res)
|
||||
else
|
||||
rep := new_webapi_response (req, res)
|
||||
rep.add_string_field ("uid", l_user.id.out)
|
||||
add_user_links_to (l_user, rep)
|
||||
end
|
||||
rep.add_self (req.percent_encoded_path_info)
|
||||
rep.execute
|
||||
else
|
||||
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
|
||||
@@ -527,7 +527,7 @@ feature -- CMS links
|
||||
require
|
||||
u_with_name: not u.name.is_whitespace
|
||||
do
|
||||
Result := link (user_display_name (u), "user/" + u.id.out, Void)
|
||||
Result := link (real_user_display_name (u), "user/" + u.id.out, Void)
|
||||
end
|
||||
|
||||
feature -- Helpers: URLs
|
||||
@@ -784,6 +784,11 @@ feature -- Logging
|
||||
end
|
||||
end
|
||||
|
||||
log_debug (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_link: detachable CMS_LINK)
|
||||
do
|
||||
log (a_category, a_message, {CMS_LOG}.level_debug, a_link)
|
||||
end
|
||||
|
||||
feature -- Internationalization (i18n)
|
||||
|
||||
translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32
|
||||
@@ -1059,7 +1064,7 @@ feature {CMS_EXECUTION} -- Hooks
|
||||
loop
|
||||
l_module := ic.item
|
||||
if is_administration_mode then
|
||||
if attached {CMS_ADMINISTRABLE} l_module as adm then
|
||||
if attached {CMS_WITH_MODULE_ADMINISTRATION} l_module as adm then
|
||||
l_module := adm.module_administration
|
||||
else
|
||||
l_module := Void
|
||||
|
||||
@@ -194,6 +194,7 @@ feature -- Import
|
||||
end
|
||||
else
|
||||
user_api.new_user (u)
|
||||
-- FIXME: check what status to use...
|
||||
a_import_ctx.log ("New user %"" + u.name + "%" -> " + u.id.out + " .")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ note
|
||||
deferred class
|
||||
CMS_WITH_WEBAPI
|
||||
|
||||
feature -- Administration
|
||||
feature -- Webapi
|
||||
|
||||
module_webapi: like webapi
|
||||
-- Associated web api module.
|
||||
|
||||
Reference in New Issue
Block a user