From e04138c89e5659a3a4bf632f4a4de662947f8c8e Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 12 Sep 2017 23:07:45 +0200 Subject: [PATCH] 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. --- .../site/modules/admin/files/css/admin.css | 12 + .../site/modules/admin/files/scss/admin.scss | 8 + .../handler/role/cms_admin_roles_handler.e | 112 ++++++-- .../handler/role/cms_role_form_response.e | 31 ++- .../handler/role/cms_role_view_response.e | 4 +- .../user/cms_admin_user_form_response.e | 1 + .../user/cms_admin_user_view_response.e | 6 + modules/admin/site/files/css/admin.css | 12 + modules/admin/site/files/scss/admin.scss | 8 + modules/auth/cms_authentication_api.e | 68 +++++ modules/auth/cms_authentication_module.e | 251 +++++++++--------- .../auth/cms_authentication_module_webapi.e | 17 +- .../auth/cms_user_register_webapi_handler.e | 112 ++++++++ modules/node/cms_node_module.e | 2 + modules/node/node_module.e | 18 -- src/kernel/content/cms_encoders.e | 21 ++ src/kernel/form/cms_form.e | 49 +++- src/modules/core/cms_core_module.e | 3 +- src/modules/core/cms_user_api.e | 2 +- .../handler/user/cms_user_view_response.e | 21 +- .../cms_access_token_webapi_auth_filter.e | 6 +- .../core/webapi/cms_core_module_webapi.e | 1 + .../core/webapi/cms_user_webapi_handler.e | 55 +++- .../core/webapi/cms_users_webapi_handler.e | 175 ++++++++++++ src/service/cms_api.e | 9 +- src/service/cms_api_import_imp.e | 1 + src/service/webapi/cms_with_webapi.e | 2 +- 27 files changed, 799 insertions(+), 208 deletions(-) create mode 100644 modules/auth/cms_authentication_api.e create mode 100644 modules/auth/cms_user_register_webapi_handler.e delete mode 100644 modules/node/node_module.e create mode 100644 src/modules/core/webapi/cms_users_webapi_handler.e diff --git a/examples/demo/site/modules/admin/files/css/admin.css b/examples/demo/site/modules/admin/files/css/admin.css index 3edb00a..028e8a0 100644 --- a/examples/demo/site/modules/admin/files/css/admin.css +++ b/examples/demo/site/modules/admin/files/css/admin.css @@ -41,6 +41,18 @@ ul.cms-roles li.cms_role a::before { content: "[role] "; } +table.cms-roles { + border: solid 1px black; + border-collapse: collapse; +} +table.cms-roles th, table.cms-roles td { + padding: 2px; + border: solid 1px black; +} +table.cms-roles td.cms_role_permission { + font-style: italic; +} + ul.cms-permissions { list-style-type: none; padding: 3px 3px 3px 3px; diff --git a/examples/demo/site/modules/admin/files/scss/admin.scss b/examples/demo/site/modules/admin/files/scss/admin.scss index 36d8709..38d66ab 100644 --- a/examples/demo/site/modules/admin/files/scss/admin.scss +++ b/examples/demo/site/modules/admin/files/scss/admin.scss @@ -45,6 +45,14 @@ ul.cms-roles { content: "[role] "; } } +table.cms-roles { + border: solid 1px black; + border-collapse: collapse; + th,td {padding: 2px; border: solid 1px black; } + td.cms_role_permission { + font-style: italic; + } +} ul.cms-permissions { diff --git a/modules/admin/handler/role/cms_admin_roles_handler.e b/modules/admin/handler/role/cms_admin_roles_handler.e index 0c9fb6f..7a78471 100644 --- a/modules/admin/handler/role/cms_admin_roles_handler.e +++ b/modules/admin/handler/role/cms_admin_roles_handler.e @@ -29,6 +29,8 @@ inherit do_get end + CMS_SHARED_SORTING_UTILITIES + REFACTORING_HELPER create @@ -54,26 +56,30 @@ feature -- execute execute (req, res) end - feature -- HTTP Methods do_get (req: WSF_REQUEST; res: WSF_RESPONSE) local l_response: CMS_RESPONSE s: STRING - u: CMS_USER_ROLE + l_role: CMS_USER_ROLE + l_perm: READABLE_STRING_8 l_count: INTEGER user_api: CMS_USER_API + l_full: BOOLEAN + l_modname: STRING_8 + l_mods: ARRAYED_LIST [STRING_8] + l_perms: LIST [READABLE_STRING_8] do -- At the moment the template are hardcoded, but we can -- get them from the configuration file and load them into -- the setup class. - user_api := api.user_api - l_count := user_api.roles_count + l_full := attached {WSF_STRING} req.query_parameter ("full") as p and then p.is_case_insensitive_equal ("yes") + create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api) create s.make_empty @@ -84,27 +90,93 @@ feature -- HTTP Methods end if attached user_api.roles as lst then - s.append ("%N") - end + l_response.add_to_primary_tabs (api.local_link ("Permissions", l_response.location + "?full=yes")) + l_response.add_to_primary_tabs (api.local_link ("Roles", l_response.location)) + if l_full then + s.append ("") + across + lst as ic + loop + l_role := ic.item + s.append ("%N") + end + s.append ("") + if attached user_api.role_permissions as l_role_permissions then + create l_mods.make (l_role_permissions.count) + across + l_role_permissions as m_ic + loop + l_modname := m_ic.key + l_mods.force (l_modname) + end + string_sorter.sort (l_mods) + across + l_mods as m_ic + loop + l_modname := m_ic.item + l_perms := l_role_permissions.item (l_modname) + s.append ("") + if l_perms /= Void then + across + l_perms as p_ic + loop + l_perm := p_ic.item + if not l_perm.is_whitespace then + s.append ("") + across + lst as ic + loop + l_role := ic.item + s.append ("") + end + s.append ("") + end + end + end + end + end + s.append ("
Permissions") + s.append ("") + s.append (html_encoded (l_role.name)) + s.append ("") + s.append ("
") + if l_modname.is_whitespace then + s.append ("...") + else + s.append (html_encoded (l_modname)) + end + s.append ("
") + s.append (html_encoded (l_perm)) + s.append ("") + if l_role.has_permission (l_perm) then + s.append ("X") + end + s.append ("
") + else + s.append ("%N") + end + end + s.append ("
") if l_response.has_permission ("admin roles") then s.append (l_response.link ("Add Role", api.administration_path_location ("add/role"), Void)) end - l_response.set_main_content (s) l_response.execute end diff --git a/modules/admin/handler/role/cms_role_form_response.e b/modules/admin/handler/role/cms_role_form_response.e index 52c542c..e619534 100644 --- a/modules/admin/handler/role/cms_role_form_response.e +++ b/modules/admin/handler/role/cms_role_form_response.e @@ -37,11 +37,26 @@ feature -- Process b: STRING_8 uid: INTEGER_64 user_api: CMS_USER_API + lnk: CMS_LINK do user_api := api.user_api create b.make_empty uid := role_id_path_parameter (request) if uid > 0 and then attached user_api.user_role_by_id (uid.to_integer) as l_role then + if l_role.has_id then + lnk := api.administration_link (translation ("View", Void), "role/" + l_role.id.out) + lnk.set_weight (1) + add_to_primary_tabs (lnk) + lnk := api.administration_link (translation ("Edit", Void), "role/" + l_role.id.out + "/edit") + lnk.set_weight (2) + add_to_primary_tabs (lnk) + + lnk := api.administration_link (translation ("Delete", Void), "role/" + l_role.id.out + "/delete") + lnk.set_weight (3) + add_to_primary_tabs (lnk) + + end + fixme ("Issues with WSD_FORM_DATA.apply_to_associated_form") -- if we have a WSF_FORM_CHECKBOK_INPUT, cheked inputs, are not preserverd in case of error. if location.ends_with_general ("/edit") then @@ -52,6 +67,10 @@ feature -- Process else new_form end + lnk := api.administration_link (translation ("<< Roles", Void), "roles") + lnk.set_weight (10) + add_to_primary_tabs (lnk) + end feature -- Process Edit @@ -71,11 +90,7 @@ feature -- Process Edit f.process (Current) fd := f.last_data end - if a_role.has_id then - add_to_menu (api.administration_link (translation ("View", Void), "role/" + a_role.id.out), primary_tabs) - add_to_menu (api.administration_link (translation ("Edit", Void), "role/" + a_role.id.out + "/edit"), primary_tabs) - add_to_menu (api.administration_link (translation ("Delete", Void), "role/" + a_role.id.out + "/delete"), primary_tabs) - end + if attached redirection as l_location then -- FIXME: Hack for now set_title (a_role.name) @@ -102,11 +117,7 @@ feature -- Process Delete f.process (Current) fd := f.last_data end - if a_role.has_id then - add_to_menu (api.administration_link (translation ("View", Void), "role/" + a_role.id.out), primary_tabs) - add_to_menu (api.administration_link (translation ("Edit", Void), "role/" + a_role.id.out + "/edit"), primary_tabs) - add_to_menu (api.administration_link (translation ("Delete", Void), "role/" + a_role.id.out + "/delete"), primary_tabs) - end + if attached redirection as l_location then -- FIXME: Hack for now set_title (a_role.name) diff --git a/modules/admin/handler/role/cms_role_view_response.e b/modules/admin/handler/role/cms_role_view_response.e index 3fc44b3..4794074 100644 --- a/modules/admin/handler/role/cms_role_view_response.e +++ b/modules/admin/handler/role/cms_role_view_response.e @@ -45,7 +45,6 @@ feature -- Execution end end - append_html_to_output (a_role: CMS_USER_ROLE; a_response: CMS_RESPONSE ) local lnk: CMS_LOCAL_LINK @@ -66,6 +65,9 @@ feature -- Execution lnk.set_weight (3) a_response.add_to_primary_tabs (lnk) end + lnk := api.administration_link (translation ("<< Roles", Void), "roles") + lnk.set_weight (10) + add_to_primary_tabs (lnk) create s.make_empty s.append ("
") diff --git a/modules/admin/handler/user/cms_admin_user_form_response.e b/modules/admin/handler/user/cms_admin_user_form_response.e index 4da8244..18c74d2 100644 --- a/modules/admin/handler/user/cms_admin_user_form_response.e +++ b/modules/admin/handler/user/cms_admin_user_form_response.e @@ -516,6 +516,7 @@ feature -- Form create u.make (l_username) u.set_email (l_email.as_string_8) u.set_password (new_random_password (u)) + u.mark_active api.user_api.new_user (u) if api.user_api.has_error then -- handle error diff --git a/modules/admin/handler/user/cms_admin_user_view_response.e b/modules/admin/handler/user/cms_admin_user_view_response.e index 07d17e0..f8f4336 100644 --- a/modules/admin/handler/user/cms_admin_user_view_response.e +++ b/modules/admin/handler/user/cms_admin_user_view_response.e @@ -61,12 +61,18 @@ feature -- Execution lnk.set_weight (2) a_response.add_to_primary_tabs (lnk) + if a_user /= Void and then a_user.id > 0 then lnk := api.administration_link (a_response.translation ("Delete", Void), "user/" + a_user.id.out + "/delete") lnk.set_weight (3) a_response.add_to_primary_tabs (lnk) end + lnk := api.administration_link (a_response.translation ("<< Users", Void), "users") + lnk.set_weight (10) + a_response.add_to_primary_tabs (lnk) + + -- FIXME: [04/aug/2015] use a CMS_FORM rather than hardcoded html. -- So that other module may easily integrate them-selves to add information. create s.make_empty diff --git a/modules/admin/site/files/css/admin.css b/modules/admin/site/files/css/admin.css index 3edb00a..028e8a0 100644 --- a/modules/admin/site/files/css/admin.css +++ b/modules/admin/site/files/css/admin.css @@ -41,6 +41,18 @@ ul.cms-roles li.cms_role a::before { content: "[role] "; } +table.cms-roles { + border: solid 1px black; + border-collapse: collapse; +} +table.cms-roles th, table.cms-roles td { + padding: 2px; + border: solid 1px black; +} +table.cms-roles td.cms_role_permission { + font-style: italic; +} + ul.cms-permissions { list-style-type: none; padding: 3px 3px 3px 3px; diff --git a/modules/admin/site/files/scss/admin.scss b/modules/admin/site/files/scss/admin.scss index 36d8709..38d66ab 100644 --- a/modules/admin/site/files/scss/admin.scss +++ b/modules/admin/site/files/scss/admin.scss @@ -45,6 +45,14 @@ ul.cms-roles { content: "[role] "; } } +table.cms-roles { + border: solid 1px black; + border-collapse: collapse; + th,td {padding: 2px; border: solid 1px black; } + td.cms_role_permission { + font-style: italic; + } +} ul.cms-permissions { diff --git a/modules/auth/cms_authentication_api.e b/modules/auth/cms_authentication_api.e new file mode 100644 index 0000000..54adcb1 --- /dev/null +++ b/modules/auth/cms_authentication_api.e @@ -0,0 +1,68 @@ +note + description: "Summary description for {CMS_AUTHENTICATION_API}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_AUTHENTICATION_API + +inherit + CMS_AUTH_API_I + +create {CMS_AUTHENTICATION_MODULE} + make + +feature -- Token Generation + + register_user (u: CMS_TEMP_USER; a_email: READABLE_STRING_8; a_personal_information: READABLE_STRING_8) + local + l_user_api: CMS_USER_API + l_url_activate: STRING + l_url_reject: STRING + l_token: STRING + es: CMS_AUTHENTICATION_EMAIL_SERVICE + do + l_user_api := cms_api.user_api + l_user_api.new_temp_user (u) + -- Create activation token + l_token := new_token + l_user_api.new_activation (l_token, u.id) + l_url_activate := cms_api.absolute_url ("/account/activate/" + l_token, void) + l_url_reject := cms_api.absolute_url ("/account/reject/" + l_token, Void) + -- Send Email to webmaster + cms_api.log_debug ("registration", "send_register_email", Void) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (cms_api)) + es.send_account_evaluation (u, a_personal_information, l_url_activate, l_url_reject, cms_api.absolute_url ("", Void)) + + -- Send Email to user + cms_api.log_debug ("registration", "send_contact_email", Void) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (cms_api)) + es.send_contact_email (a_email, u, cms_api.absolute_url ("", Void)) + + cms_api.log ("registration", {STRING_32} "new user %"" + u.name + "%" <" + a_email + ">", {CMS_LOG}.level_info, Void) + end + + new_token: STRING + -- Generate a new token activation token + local + l_token: STRING + l_security: SECURITY_PROVIDER + l_encode: URL_ENCODER + do + create l_security + l_token := l_security.token + create l_encode + from + until + l_token.same_string (l_encode.encoded_string (l_token)) + loop + -- Loop ensure that we have a security token that does not contain characters that need encoding. + -- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token + -- but the user will need to use an unencoded token if activation has to be done manually. + l_token := l_security.token + end + Result := l_token + end + +end diff --git a/modules/auth/cms_authentication_module.e b/modules/auth/cms_authentication_module.e index a542a88..1d87126 100644 --- a/modules/auth/cms_authentication_module.e +++ b/modules/auth/cms_authentication_module.e @@ -8,12 +8,18 @@ class inherit CMS_MODULE + rename + module_api as auth_api redefine + initialize, setup_hooks, - permissions + permissions, + auth_api end - CMS_ADMINISTRABLE + CMS_WITH_MODULE_ADMINISTRATION + + CMS_WITH_WEBAPI CMS_HOOK_AUTO_REGISTER @@ -52,6 +58,15 @@ feature {NONE} -- Initialization enable -- Is enabled by default end +feature {CMS_API} -- Initialization + + initialize (api: CMS_API) + -- + do + create auth_api.make (api) + Precursor (api) + end + feature -- Access name: STRING = "auth" @@ -68,6 +83,8 @@ feature -- Access Result.force ("view user") end + auth_api: detachable CMS_AUTHENTICATION_API + feature {CMS_EXECUTION} -- Administration administration: CMS_AUTHENTICATION_MODULE_ADMINISTRATION @@ -75,6 +92,13 @@ feature {CMS_EXECUTION} -- Administration create Result.make (Current) end +feature -- Webapi + + webapi: CMS_AUTHENTICATION_MODULE_WEBAPI + do + create Result.make (Current) + end + feature -- Access: docs root_dir: PATH @@ -99,10 +123,12 @@ feature -- Router setup_router (a_router: WSF_ROUTER; a_api: CMS_API) -- do - configure_web (a_api, a_router) + if attached auth_api as l_auth_api then + configure_web (l_auth_api, a_router) + end end - configure_web (a_api: CMS_API; a_router: WSF_ROUTER) + configure_web (a_api: CMS_AUTHENTICATION_API; a_router: WSF_ROUTER) local m: WSF_URI_MAPPING do @@ -226,7 +252,7 @@ feature -- Handler view_account_form_id: STRING = "roccms-user-view" edit_account_form_id: STRING = "roccms-user-edit" - handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_account (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_user: detachable CMS_USER @@ -235,11 +261,11 @@ feature -- Handler f: CMS_FORM tf: WSF_FORM_TEXT_INPUT do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) create b.make_empty l_user := r.user create f.make (r.location, view_account_form_id) - if attached smarty_template_block (Current, "account_info", api) as l_tpl_block then + if attached smarty_template_block (Current, "account_info", a_auth_api.cms_api) as l_tpl_block then l_tpl_block.set_weight (-10) r.add_block (l_tpl_block, "content") else @@ -260,12 +286,12 @@ feature -- Handler tf.set_label ("Profile name") f.extend (tf) end - create tf.make_with_text ("creation", api.formatted_date_time_yyyy_mm_dd (l_user.creation_date)) + create tf.make_with_text ("creation", a_auth_api.cms_api.formatted_date_time_yyyy_mm_dd (l_user.creation_date)) tf.set_label ("Creation date") f.extend (tf) if attached l_user.last_login_date as dt then - create tf.make_with_text ("last_login", api.formatted_date_time_ago (dt)) + create tf.make_with_text ("last_login", a_auth_api.cms_api.formatted_date_time_ago (dt)) tf.set_label ("Last login") f.extend (tf) end @@ -282,7 +308,7 @@ feature -- Handler r.add_to_primary_tabs (lnk) end - api.hooks.invoke_form_alter (f, Void, r) + a_auth_api.cms_api.hooks.invoke_form_alter (f, Void, r) f.append_to_html (r.wsf_theme, b) r.set_main_content (b) @@ -293,7 +319,7 @@ feature -- Handler r.execute end - handle_edit_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_edit_account (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_user: detachable CMS_USER @@ -301,11 +327,11 @@ feature -- Handler lnk: CMS_LOCAL_LINK l_form: CMS_FORM do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) create b.make_empty l_user := r.user create l_form.make (r.location, edit_account_form_id) - if attached smarty_template_block (Current, "account_edit", api) as l_tpl_block then + if attached smarty_template_block (Current, "account_edit", a_auth_api.cms_api) as l_tpl_block then l_tpl_block.set_weight (-10) r.add_block (l_tpl_block, "content") else @@ -348,17 +374,17 @@ feature -- Handler r.execute end - handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_login (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE do - if api.user_is_authenticated then - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if a_auth_api.cms_api.user_is_authenticated then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) r.set_redirection ("account") r.execute - elseif attached api.module_by_name ("session_auth") then + elseif attached a_auth_api.cms_api.module_by_name ("session_auth") then -- FIXME: find better solution to support a default login system. - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if attached {WSF_STRING} req.item ("destination") as l_destination then r.set_redirection ("account/auth/roc-session-login?destination=" + l_destination.url_encoded_value) else @@ -367,9 +393,9 @@ feature -- Handler r.execute - elseif attached api.module_by_name ("basic_auth") then + elseif attached a_auth_api.cms_api.module_by_name ("basic_auth") then -- FIXME: find better solution to support a default login system. - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if attached {WSF_STRING} req.item ("destination") as l_destination then r.set_redirection ("account/auth/roc-basic-login?destination=" + l_destination.url_encoded_value) else @@ -378,17 +404,17 @@ feature -- Handler r.execute else - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) r.execute end end - handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_logout (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE loc: STRING do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if attached auth_strategy (req) as l_auth_strategy then loc := l_auth_strategy else @@ -402,8 +428,9 @@ feature -- Handler r.execute end - handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_register (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local + f: CMS_FORM r: CMS_RESPONSE l_user_api: CMS_USER_API u: CMS_TEMP_USER @@ -416,20 +443,29 @@ feature -- Handler l_email: READABLE_STRING_8 do if - api.has_permission ("account register") and then + a_auth_api.cms_api.has_permission ("account register") and then req.is_post_request_method then - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create f.make (req.percent_encoded_path_info, "roccms-user-register") + f.extend_text_field ("name", Void) + f.extend_password_field ("password", Void) + f.extend_text_field ("email", Void) + f.extend_text_field ("personal_information", Void) + + + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) + f.process (r) if - attached {WSF_STRING} req.form_parameter ("name") as l_name and then - attached {WSF_STRING} req.form_parameter ("password") as l_password and then - attached {WSF_STRING} req.form_parameter ("email") as p_email and then - attached {WSF_STRING} req.form_parameter ("personal_information") as l_personal_information + attached f.last_data as fd and then not fd.has_error and then + attached fd.string_item ("name") as l_name and then + attached fd.string_item ("password") as l_password and then + attached fd.string_item ("email") as s_email and then + attached fd.string_item ("personal_information") as l_personal_information then - if p_email.value.is_valid_as_string_8 then - l_email := p_email.value.to_string_8 - l_user_api := api.user_api - if attached l_user_api.user_by_name (l_name.value) or else attached l_user_api.temp_user_by_name (l_name.value) then + if s_email.is_valid_as_string_8 then + l_email := s_email.to_string_8 + l_user_api := a_auth_api.cms_api.user_api + if attached l_user_api.user_by_name (l_name) or else attached l_user_api.temp_user_by_name (l_name) then -- Username already exist. r.set_value ("User name already exists!", "error_name") l_exist := True @@ -439,7 +475,7 @@ feature -- Handler r.set_value ("An account is already associated with that email address!", "error_email") l_exist := True end - if attached recaptcha_secret_key (api) as l_recaptcha_key then + if attached recaptcha_secret_key (a_auth_api.cms_api) as l_recaptcha_key then if attached {WSF_STRING} req.form_parameter ("g-recaptcha-response") as l_recaptcha_response and then is_captcha_verified (l_recaptcha_key, l_recaptcha_response.url_encoded_value) then l_captcha_passed := True else @@ -452,47 +488,35 @@ feature -- Handler end if not l_exist then -- New temp user - create u.make (l_name.value) + create u.make (l_name) u.set_email (l_email) - u.set_password (l_password.value) - u.set_personal_information (l_personal_information.value) + u.set_password (l_password) + u.set_personal_information (l_personal_information) l_user_api.new_temp_user (u) - -- Create activation token - l_token := new_token - l_user_api.new_activation (l_token, u.id) - l_url_activate := req.absolute_script_url ("/account/activate/" + l_token) - l_url_reject := req.absolute_script_url ("/account/reject/" + l_token) - -- Send Email to webmaster - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) - write_debug_log (generator + ".handle register: send_register_email") - es.send_account_evaluation (u, l_personal_information.value, l_url_activate, l_url_reject, req.absolute_script_url ("")) - -- Send Email to user - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) - write_debug_log (generator + ".handle register: send_contact_email") - es.send_contact_email (l_email, u, req.absolute_script_url ("")) + a_auth_api.register_user (u, l_email, l_personal_information) else - r.set_value (l_name.value, "name") + r.set_value (l_name, "name") r.set_value (l_email, "email") - r.set_value (l_personal_information.value, "personal_information") + r.set_value (l_personal_information, "personal_information") r.set_status_code ({HTTP_CONSTANTS}.bad_request) end else - r.set_value (l_name.value, "name") - r.set_value (p_email.value, "email") - r.set_value (l_personal_information.value, "personal_information") + r.set_value (l_name, "name") + r.set_value (l_email, "email") + r.set_value (l_personal_information, "personal_information") r.set_status_code ({HTTP_CONSTANTS}.bad_request) end r.execute else - api.response_api.send_bad_request ("There were issue with your application, invalid or missing values.", req, res) + a_auth_api.cms_api.response_api.send_bad_request ("There were issue with your application, invalid or missing values.", req, res) end else - api.response_api.send_permissions_access_denied ("You can also contact the webmaster to ask for an account.", Void, req, res) + a_auth_api.cms_api.response_api.send_permissions_access_denied ("You can also contact the webmaster to ask for an account.", Void, req, res) end end - handle_activation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_activation (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_user_api: CMS_USER_API @@ -500,8 +524,8 @@ feature -- Handler es: CMS_AUTHENTICATION_EMAIL_SERVICE l_temp_id: INTEGER_64 do - if api.has_permission ("account activate") then - l_user_api := api.user_api + if a_auth_api.cms_api.has_permission ("account activate") then + l_user_api := a_auth_api.cms_api.user_api if attached {WSF_STRING} req.path_parameter ("token") as l_token then if attached {CMS_TEMP_USER} l_user_api.temp_user_by_activation_token (l_token.value) as l_temp_user then @@ -516,20 +540,25 @@ feature -- Handler l_temp_user.mark_active l_user_api.new_user_from_temp_user (l_temp_user) - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if not l_user_api.has_error and then attached l_user_api.user_by_name (l_temp_user.name) as l_new_user then + if attached l_temp_user.personal_information as l_perso_info then + -- Keep personal information in profile item! + a_auth_api.cms_api.user_api.save_user_profile_item (l_new_user, "personal_information", l_perso_info) + end -- Delete temporal User l_temp_user.set_id (l_temp_id) l_user_api.delete_temp_user (l_temp_user) l_user_api.remove_activation (l_token.value) - r.set_main_content ("

The account " + html_encoded (l_new_user.name) + " has been activated

") + r.set_main_content ("

The account " + a_auth_api.cms_api.user_html_link (l_new_user) + " has been activated

") -- Send Email if attached l_new_user.email as l_email then - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (a_auth_api.cms_api)) write_debug_log (generator + ".handle register: send_contact_activation_confirmation_email") es.send_contact_activation_confirmation_email (l_email, l_new_user, req.absolute_script_url ("")) end @@ -542,38 +571,38 @@ feature -- Handler end end else -- the token does not exist, or it was already used. - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) r.set_status_code ({HTTP_CONSTANTS}.bad_request) r.set_main_content ("

The token " + l_token.value + " is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "

") end r.execute else - create l_ir.make (req, res, api) + create l_ir.make (req, res, a_auth_api.cms_api) l_ir.execute end else - api.response_api.send_access_denied (Void, req, res) + a_auth_api.cms_api.response_api.send_access_denied (Void, req, res) end end - handle_reject (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_reject (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE es: CMS_AUTHENTICATION_EMAIL_SERVICE l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE l_user_api: CMS_USER_API do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if r.has_permission ("account reject") then if attached {WSF_STRING} req.path_parameter ("token") as l_token then - l_user_api := api.user_api + l_user_api := a_auth_api.cms_api.user_api if attached {CMS_TEMP_USER} l_user_api.temp_user_by_activation_token (l_token.value) as l_user then l_user_api.delete_temp_user (l_user) r.set_main_content ("

The temporal account for " + html_encoded (l_user.name) + " has been removed

") -- Send Email if attached l_user.email as l_email then - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (a_auth_api.cms_api)) write_debug_log (generator + ".handle register: send_contact_activation_reject_email") es.send_contact_activation_reject_email (l_email, l_user, req.absolute_script_url ("")) end @@ -584,15 +613,15 @@ feature -- Handler end r.execute else - create l_ir.make (req, res, api) + create l_ir.make (req, res, a_auth_api.cms_api) l_ir.execute end else - api.response_api.send_access_denied (Void, req, res) + a_auth_api.cms_api.response_api.send_access_denied (Void, req, res) end end - handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_reactivation (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE es: CMS_AUTHENTICATION_EMAIL_SERVICE @@ -602,26 +631,26 @@ feature -- Handler l_url_reject: STRING l_email: READABLE_STRING_8 do - if api.has_permission ("account reactivate") then - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if a_auth_api.cms_api.has_permission ("account reactivate") then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if req.is_post_request_method then if attached {WSF_STRING} req.form_parameter ("email") as p_email then if p_email.value.is_valid_as_string_8 then l_email := p_email.value.to_string_8 - l_user_api := api.user_api + l_user_api := a_auth_api.cms_api.user_api if attached {CMS_TEMP_USER} l_user_api.temp_user_by_email (l_email) as l_user then -- User exist create a new token and send a new email. if l_user.is_active then r.set_value ("The asociated user to the given email " + l_email + " , is already active", "is_active") r.set_status_code ({HTTP_CONSTANTS}.bad_request) else - l_token := new_token + l_token := a_auth_api.new_token l_user_api.new_activation (l_token, l_user.id) l_url_activate := req.absolute_script_url ("/account/activate/" + l_token) l_url_reject := req.absolute_script_url ("/account/reject/" + l_token) -- Send Email to webmaster if attached l_user.personal_information as l_personal_information then - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (a_auth_api.cms_api)) write_debug_log (generator + ".handle register: send_register_email") es.send_account_evaluation (l_user, l_personal_information, l_url_activate, l_url_reject, req.absolute_script_url ("")) end @@ -640,11 +669,11 @@ feature -- Handler end r.execute else - api.response_api.send_access_denied (Void, req, res) + a_auth_api.cms_api.response_api.send_access_denied (Void, req, res) end end - handle_new_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_new_password (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE es: CMS_AUTHENTICATION_EMAIL_SERVICE @@ -653,20 +682,20 @@ feature -- Handler l_url: STRING l_email: READABLE_STRING_8 do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if req.is_post_request_method then - l_user_api := api.user_api + l_user_api := a_auth_api.cms_api.user_api if attached {WSF_STRING} req.form_parameter ("email") as p_email then if p_email.value.is_valid_as_string_8 then l_email := p_email.value.to_string_8 if attached {CMS_USER} l_user_api.user_by_email (l_email) as l_user then -- User exist create a new token and send a new email. - l_token := new_token + l_token := a_auth_api.new_token l_user_api.new_password (l_token, l_user.id) l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token) -- Send Email - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (a_auth_api.cms_api)) write_debug_log (generator + ".handle register: send_contact_password_email") es.send_contact_password_email (l_email, l_user, l_url, req.absolute_script_url ("")) else @@ -685,12 +714,12 @@ feature -- Handler attached l_user.email as l_user_email then -- User exist create a new token and send a new email. - l_token := new_token + l_token := a_auth_api.new_token l_user_api.new_password (l_token, l_user.id) l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token) -- Send Email - create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (a_auth_api.cms_api)) write_debug_log (generator + ".handle register: send_contact_password_email") es.send_contact_password_email (l_user_email, l_user, l_url, req.absolute_script_url ("")) else @@ -703,13 +732,13 @@ feature -- Handler r.execute end - handle_reset_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_reset_password (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_user_api: CMS_USER_API do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - l_user_api := api.user_api + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) + l_user_api := a_auth_api.cms_api.user_api if attached {WSF_STRING} req.query_parameter ("token") as l_token then r.set_value (l_token.value, "token") if l_user_api.user_by_password_token (l_token.value) = Void then @@ -737,7 +766,7 @@ feature -- Handler r.execute end - handle_change_field (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_change_field (a_auth_api: CMS_AUTHENTICATION_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_user_api: CMS_USER_API @@ -750,9 +779,9 @@ feature -- Handler l_fieldname := p_field.url_encoded_value end if l_fieldname = Void then - api.response_api.send_bad_request (Void, req, res) + a_auth_api.cms_api.response_api.send_bad_request (Void, req, res) else - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_auth_api.cms_api) if r.is_authenticated then create lnk.make ("View", "account/") @@ -764,7 +793,7 @@ feature -- Handler r.add_to_primary_tabs (lnk) end - l_user_api := api.user_api + l_user_api := a_auth_api.cms_api.user_api if req.is_post_request_method then if attached r.user as l_user then if l_fieldname.is_case_insensitive_equal ("password") then @@ -811,7 +840,7 @@ feature -- Handler not fd.has_error and then attached fd.string_item ("new_profile_name") as l_new_profile_name then - check api.user_api.is_valid_profile_name (l_new_profile_name) end + check a_auth_api.cms_api.user_api.is_valid_profile_name (l_new_profile_name) end l_user.set_profile_name (l_new_profile_name) l_user_api.update_user (l_user) r.add_success_message ("Profile name updated.") @@ -822,7 +851,7 @@ feature -- Handler r.set_main_content (f.to_html (r.wsf_theme)) end elseif l_fieldname.is_case_insensitive_equal ("username") then - if api.has_permission ("change own username") then + if a_auth_api.cms_api.has_permission ("change own username") then f := new_change_username_form (r) f.process (r) if @@ -830,8 +859,8 @@ feature -- Handler not fd.has_error and then attached fd.string_item ("new_username") as l_new_username then - check api.user_api.is_valid_username (l_new_username) end - check api.user_api.user_by_name (l_new_username) = Void end + check a_auth_api.cms_api.user_api.is_valid_username (l_new_username) end + check a_auth_api.cms_api.user_api.user_by_name (l_new_username) = Void end l_user_api.update_username (l_user, l_new_username) r.add_success_message ("Username updated.") @@ -857,7 +886,7 @@ feature -- Handler f := new_change_email_form (r) f.append_to_html (r.wsf_theme, b) elseif l_fieldname.is_case_insensitive_equal_general ("new_username") then - if api.has_permission ("change own username") then + if a_auth_api.cms_api.has_permission ("change own username") then f := new_change_username_form (r) f.append_to_html (r.wsf_theme, b) end @@ -996,30 +1025,6 @@ feature -- Handler fs.extend_html_text ("") end -feature {NONE} -- Token Generation - - new_token: STRING - -- Generate a new token activation token - local - l_token: STRING - l_security: SECURITY_PROVIDER - l_encode: URL_ENCODER - do - create l_security - l_token := l_security.token - create l_encode - from - until - l_token.same_string (l_encode.encoded_string (l_token)) - loop - -- Loop ensure that we have a security token that does not contain characters that need encoding. - -- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token - -- but the user will need to use an unencoded token if activation has to be done manually. - l_token := l_security.token - end - Result := l_token - end - feature {NONE} -- Block views get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) diff --git a/modules/auth/cms_authentication_module_webapi.e b/modules/auth/cms_authentication_module_webapi.e index 6ea639e..9ae3540 100644 --- a/modules/auth/cms_authentication_module_webapi.e +++ b/modules/auth/cms_authentication_module_webapi.e @@ -22,7 +22,7 @@ feature -- Security -- List of permission ids, used by this module, and declared. do Result := Precursor - Result.force ("admin users") + Result.force ("account register") end feature {NONE} -- Router/administration @@ -30,18 +30,9 @@ feature {NONE} -- Router/administration setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API) -- do - a_router.handle ("/info", create {WSF_URI_AGENT_HANDLER}.make (agent handle_info (?, ?, a_api)), a_router.methods_get) - end - -feature -- Request handling - - handle_info (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API) - local - m: WSF_HTML_PAGE_RESPONSE - do - create m.make - m.set_body ("{%"info%":%"" + api.setup.site_name + "%"}") - res.send (m) + if attached module.auth_api as l_auth_api then + a_router.handle ("/account/register", create {CMS_USER_REGISTER_WEBAPI_HANDLER}.make_with_auth_api (l_auth_api), a_router.methods_post) + end end end diff --git a/modules/auth/cms_user_register_webapi_handler.e b/modules/auth/cms_user_register_webapi_handler.e new file mode 100644 index 0000000..e0683be --- /dev/null +++ b/modules/auth/cms_user_register_webapi_handler.e @@ -0,0 +1,112 @@ +note + description: "Summary description for {CMS_USER_REGISTER_WEBAPI_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER_REGISTER_WEBAPI_HANDLER + +inherit + CMS_WEBAPI_HANDLER + + WSF_URI_HANDLER + +create + make_with_auth_api + +feature {NONE} -- Initialization + + make_with_auth_api (a_auth_api: CMS_AUTHENTICATION_API) + do + auth_api := a_auth_api + make (a_auth_api.cms_api) + end + + auth_api: CMS_AUTHENTICATION_API + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute handler for `req' and respond in `res'. + do + if req.is_post_request_method then + register_user (req, res) + else + send_bad_request (Void, req, res) + end + end + + register_user (req: WSF_REQUEST; res: WSF_RESPONSE) + local + f: CMS_FORM + rep: like new_webapi_response + l_user_api: CMS_USER_API + u: CMS_TEMP_USER + l_exist: BOOLEAN + + l_url_activate: STRING + l_url_reject: STRING + l_token: STRING + l_captcha_passed: BOOLEAN + l_email: READABLE_STRING_8 + do + if + api.has_permission ("account register") and then + req.is_post_request_method + then + create f.make (req.percent_encoded_path_info, "roccms-user-register") + f.extend_text_field ("name", Void) + f.extend_password_field ("password", Void) + f.extend_text_field ("email", Void) + f.extend_text_field ("personal_information", Void) + + rep := new_webapi_response (req, res) + f.process (rep) + if + attached f.last_data as fd and then not fd.has_error and then + attached fd.string_item ("name") as l_name and then + attached fd.string_item ("password") as l_password and then + attached fd.string_item ("email") as s_email and then + attached fd.string_item ("personal_information") as l_personal_information + then + if s_email.is_valid_as_string_8 then + l_email := s_email.to_string_8 + l_user_api := api.user_api + if attached l_user_api.user_by_name (l_name) or else attached l_user_api.temp_user_by_name (l_name) then + -- Username already exists. + fd.report_invalid_field ("name", "User name already exists!") + l_exist := True + end + if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then + -- Email already exists. + fd.report_invalid_field ("email", "An account is already associated with that email address!") + l_exist := True + end + if fd.has_error or l_exist then + send_bad_request ("User name or email is already taken!", req, res) + else + -- New temp user + create u.make (l_name) + u.set_email (l_email) + u.set_password (l_password) + u.set_personal_information (l_personal_information) + + auth_api.register_user (u, l_email, l_personal_information) +-- add_user_links_to (u, rep) + rep.add_string_field ("status", "succeed") + rep.add_self (req.percent_encoded_path_info) + rep.execute + end + else + send_bad_request ("Invalid email", req, res) + end + else + send_bad_request ("There were issue with your application, invalid or missing values.", req, res) + end + else + send_access_denied ("You can also contact the webmaster to ask for an account.", req, res) + end + end + + +end diff --git a/modules/node/cms_node_module.e b/modules/node/cms_node_module.e index dea5323..6d0d32c 100644 --- a/modules/node/cms_node_module.e +++ b/modules/node/cms_node_module.e @@ -134,6 +134,8 @@ feature -- Access l_type_name := ic.item.name if not l_type_name.is_whitespace then Result.force ("create " + l_type_name) + Result.force ("delete " + l_type_name) + Result.force ("trash " + l_type_name) Result.force ("view any " + l_type_name) Result.force ("edit any " + l_type_name) diff --git a/modules/node/node_module.e b/modules/node/node_module.e deleted file mode 100644 index f1bb690..0000000 --- a/modules/node/node_module.e +++ /dev/null @@ -1,18 +0,0 @@ -note - description: "Summary description for {NODE_MODULE}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - NODE_MODULE - -obsolete "Use {CMS_NODE_MODULE}" - -inherit - CMS_NODE_MODULE - -create - make - -end diff --git a/src/kernel/content/cms_encoders.e b/src/kernel/content/cms_encoders.e index 42d4433..660cfca 100644 --- a/src/kernel/content/cms_encoders.e +++ b/src/kernel/content/cms_encoders.e @@ -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)" diff --git a/src/kernel/form/cms_form.e b/src/kernel/form/cms_form.e index 2d75f81..5da5498 100644 --- a/src/kernel/form/cms_form.e +++ b/src/kernel/form/cms_form.e @@ -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)" diff --git a/src/modules/core/cms_core_module.e b/src/modules/core/cms_core_module.e index 7fa26ea..5cfe067 100644 --- a/src/modules/core/cms_core_module.e +++ b/src/modules/core/cms_core_module.e @@ -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 diff --git a/src/modules/core/cms_user_api.e b/src/modules/core/cms_user_api.e index 6bcc946..eab6638 100644 --- a/src/modules/core/cms_user_api.e +++ b/src/modules/core/cms_user_api.e @@ -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 diff --git a/src/modules/core/handler/user/cms_user_view_response.e b/src/modules/core/handler/user/cms_user_view_response.e index 0a9c65b..bb97275 100644 --- a/src/modules/core/handler/user/cms_user_view_response.e +++ b/src/modules/core/handler/user/cms_user_view_response.e @@ -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 diff --git a/src/modules/core/webapi/cms_access_token_webapi_auth_filter.e b/src/modules/core/webapi/cms_access_token_webapi_auth_filter.e index e25fddd..62d1790 100644 --- a/src/modules/core/webapi/cms_access_token_webapi_auth_filter.e +++ b/src/modules/core/webapi/cms_access_token_webapi_auth_filter.e @@ -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 diff --git a/src/modules/core/webapi/cms_core_module_webapi.e b/src/modules/core/webapi/cms_core_module_webapi.e index f456ff4..781810c 100644 --- a/src/modules/core/webapi/cms_core_module_webapi.e +++ b/src/modules/core/webapi/cms_core_module_webapi.e @@ -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 diff --git a/src/modules/core/webapi/cms_user_webapi_handler.e b/src/modules/core/webapi/cms_user_webapi_handler.e index 2de81cd..a069a97 100644 --- a/src/modules/core/webapi/cms_user_webapi_handler.e +++ b/src/modules/core/webapi/cms_user_webapi_handler.e @@ -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)" diff --git a/src/modules/core/webapi/cms_users_webapi_handler.e b/src/modules/core/webapi/cms_users_webapi_handler.e new file mode 100644 index 0000000..fef81f4 --- /dev/null +++ b/src/modules/core/webapi/cms_users_webapi_handler.e @@ -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 diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 20d2848..b9f6f29 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -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 diff --git a/src/service/cms_api_import_imp.e b/src/service/cms_api_import_imp.e index a8ac53b..548f873 100644 --- a/src/service/cms_api_import_imp.e +++ b/src/service/cms_api_import_imp.e @@ -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 diff --git a/src/service/webapi/cms_with_webapi.e b/src/service/webapi/cms_with_webapi.e index 1366ae1..4e5df7d 100644 --- a/src/service/webapi/cms_with_webapi.e +++ b/src/service/webapi/cms_with_webapi.e @@ -6,7 +6,7 @@ note deferred class CMS_WITH_WEBAPI -feature -- Administration +feature -- Webapi module_webapi: like webapi -- Associated web api module.