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.