From ce8de442e9db90fa4c0cf81436878bc953f0b6a3 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 10 Dec 2015 11:21:20 +0100 Subject: [PATCH 01/14] Implemented taxonomy administration pages - create term, vocabulary, add or remove term from vocabularies, ... Fixed content editing related to taxonomy (especially with multiple terms vs tags). Fixed various SQL storage issue related to taxonomy and vocabularies. Added CMS_RESPONSE.wsf_theme as helper. --- .../modules/taxonomy/files/css/taxonomy.css | 5 + .../modules/taxonomy/files/scss/taxonomy.scss | 6 + .../themes/bootstrap/assets/css/style.css | 13 +- .../themes/bootstrap/assets/scss/style.scss | 15 +- .../admin/handler/cms_admin_cache_handler.e | 4 +- .../admin/handler/cms_admin_export_handler.e | 4 +- .../admin/handler/cms_admin_modules_handler.e | 4 +- modules/admin/handler/cms_admin_response.e | 20 - .../handler/role/cms_role_form_response.e | 20 - .../handler/role/cms_role_view_response.e | 21 - .../handler/user/cms_user_form_response.e | 21 - .../handler/user/cms_user_view_response.e | 21 - .../handler/cms_node_type_webform_manager.e | 122 +++--- modules/node/handler/node_form_response.e | 20 - modules/node/handler/node_view_response.e | 20 - .../cms_recent_changes_module.e | 2 +- modules/taxonomy/cms_taxonomy_api.e | 47 +- modules/taxonomy/cms_taxonomy_module.e | 50 ++- modules/taxonomy/cms_term_collection.e | 16 + modules/taxonomy/cms_vocabulary_collection.e | 16 + .../handler/taxonomy_term_admin_handler.e | 269 ++++++++++++ .../taxonomy_vocabulary_admin_handler.e | 414 ++++++++++++++++++ .../handler/taxonomy_vocabulary_handler.e | 129 ++++++ .../persistence/cms_taxonomy_storage_i.e | 34 +- .../persistence/cms_taxonomy_storage_null.e | 28 +- .../persistence/cms_taxonomy_storage_sql.e | 190 +++++++- modules/taxonomy/site/files/css/taxonomy.css | 5 + .../taxonomy/site/files/scss/taxonomy.scss | 6 + src/service/cms_api.e | 6 +- src/service/response/cms_response.e | 21 +- 30 files changed, 1302 insertions(+), 247 deletions(-) create mode 100644 modules/taxonomy/handler/taxonomy_term_admin_handler.e create mode 100644 modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e create mode 100644 modules/taxonomy/handler/taxonomy_vocabulary_handler.e diff --git a/examples/demo/site/modules/taxonomy/files/css/taxonomy.css b/examples/demo/site/modules/taxonomy/files/css/taxonomy.css index 41c87fa..dd216d0 100644 --- a/examples/demo/site/modules/taxonomy/files/css/taxonomy.css +++ b/examples/demo/site/modules/taxonomy/files/css/taxonomy.css @@ -19,3 +19,8 @@ ul.taxonomy li:hover { border-bottom: solid 1px #66f; background-color: #ddf; } + +table.taxonomy td { + border: solid 1px #ccc; + padding: 2px; +} diff --git a/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss b/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss index f409395..2ceff02 100644 --- a/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss +++ b/examples/demo/site/modules/taxonomy/files/scss/taxonomy.scss @@ -19,3 +19,9 @@ ul.taxonomy { } } } +table.taxonomy { + td { + border: solid 1px #ccc; + padding: 2px; + } +} diff --git a/examples/demo/site/themes/bootstrap/assets/css/style.css b/examples/demo/site/themes/bootstrap/assets/css/style.css index b16026b..5accf73 100644 --- a/examples/demo/site/themes/bootstrap/assets/css/style.css +++ b/examples/demo/site/themes/bootstrap/assets/css/style.css @@ -91,13 +91,10 @@ ul.horizontal li { padding: 5px 2px 5px 2px; } -ul.taxonomy-entities { - list-style-type: none; - padding: 0; +table.with_border thead td { + font-weight: bold; } -ul.taxonomy-entities li { - padding: 0; - margin-top: 5px; - margin-bottom: 10px; - border-top: dotted 1px #ccc; +table.with_border td { + border: solid 1px #ccc; + padding: 2px 5px 2px 5px; } diff --git a/examples/demo/site/themes/bootstrap/assets/scss/style.scss b/examples/demo/site/themes/bootstrap/assets/scss/style.scss index 1192673..90d2bb9 100644 --- a/examples/demo/site/themes/bootstrap/assets/scss/style.scss +++ b/examples/demo/site/themes/bootstrap/assets/scss/style.scss @@ -96,13 +96,12 @@ ul.horizontal { padding: 5px 2px 5px 2px; } -ul.taxonomy-entities { - list-style-type: none; - padding: 0; - li { - padding: 0; - margin-top: 5px; - margin-bottom: 10px; - border-top: dotted 1px #ccc; +table.with_border { + thead td { + font-weight: bold; + } + td { + border: solid 1px #ccc; + padding: 2px 5px 2px 5px; } } diff --git a/modules/admin/handler/cms_admin_cache_handler.e b/modules/admin/handler/cms_admin_cache_handler.e index 13dd922..5f53515 100644 --- a/modules/admin/handler/cms_admin_cache_handler.e +++ b/modules/admin/handler/cms_admin_cache_handler.e @@ -44,7 +44,7 @@ feature -- Execution create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api) f := clear_cache_web_form (l_response) create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end @@ -70,7 +70,7 @@ feature -- Execution end end create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end diff --git a/modules/admin/handler/cms_admin_export_handler.e b/modules/admin/handler/cms_admin_export_handler.e index 8f21b30..8fd510e 100644 --- a/modules/admin/handler/cms_admin_export_handler.e +++ b/modules/admin/handler/cms_admin_export_handler.e @@ -44,7 +44,7 @@ feature -- Execution create {GENERIC_VIEW_CMS_RESPONSE} l_response.make (req, res, api) f := exportation_web_form (l_response) create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end @@ -85,7 +85,7 @@ feature -- Execution end end create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (l_response, l_response.theme), s) + f.append_to_html (l_response.wsf_theme, s) l_response.set_main_content (s) l_response.execute end diff --git a/modules/admin/handler/cms_admin_modules_handler.e b/modules/admin/handler/cms_admin_modules_handler.e index 53ffb20..07e9d38 100644 --- a/modules/admin/handler/cms_admin_modules_handler.e +++ b/modules/admin/handler/cms_admin_modules_handler.e @@ -88,7 +88,7 @@ feature -- Execution create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) f := modules_collection_web_form (r) create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), s) + f.append_to_html (r.wsf_theme, s) r.set_page_title ("Modules") r.set_main_content (s) r.execute @@ -133,7 +133,7 @@ feature -- Execution then r.add_error_message ("Error occurred.") create s.make_empty - f.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), s) + f.append_to_html (r.wsf_theme, s) r.set_page_title ("Modules") r.set_main_content (s) else diff --git a/modules/admin/handler/cms_admin_response.e b/modules/admin/handler/cms_admin_response.e index 3274fb5..fb7dea4 100644 --- a/modules/admin/handler/cms_admin_response.e +++ b/modules/admin/handler/cms_admin_response.e @@ -8,30 +8,10 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Process process diff --git a/modules/admin/handler/role/cms_role_form_response.e b/modules/admin/handler/role/cms_role_form_response.e index b681549..7cdec58 100644 --- a/modules/admin/handler/role/cms_role_form_response.e +++ b/modules/admin/handler/role/cms_role_form_response.e @@ -8,32 +8,12 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end CMS_SHARED_SORTING_UTILITIES create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query role_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/admin/handler/role/cms_role_view_response.e b/modules/admin/handler/role/cms_role_view_response.e index 53b9038..a6887ae 100644 --- a/modules/admin/handler/role/cms_role_view_response.e +++ b/modules/admin/handler/role/cms_role_view_response.e @@ -8,31 +8,10 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end create make - -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query role_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/admin/handler/user/cms_user_form_response.e b/modules/admin/handler/user/cms_user_form_response.e index 5209706..40847f0 100644 --- a/modules/admin/handler/user/cms_user_form_response.e +++ b/modules/admin/handler/user/cms_user_form_response.e @@ -7,32 +7,11 @@ class CMS_USER_FORM_RESPONSE inherit - CMS_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query user_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/admin/handler/user/cms_user_view_response.e b/modules/admin/handler/user/cms_user_view_response.e index 0568fcc..4cf3716 100644 --- a/modules/admin/handler/user/cms_user_view_response.e +++ b/modules/admin/handler/user/cms_user_view_response.e @@ -8,31 +8,10 @@ class inherit CMS_RESPONSE - redefine - make, - initialize - end create make - -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api;) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Query user_id_path_parameter (req: WSF_REQUEST): INTEGER_64 diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e index 453a272..6e5ae7b 100644 --- a/modules/node/handler/cms_node_type_webform_manager.e +++ b/modules/node/handler/cms_node_type_webform_manager.e @@ -100,6 +100,7 @@ feature -- Forms ... populate_form_with_taxonomy (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) local ti: detachable WSF_FORM_TEXT_INPUT + th: WSF_FORM_HIDDEN_INPUT w_set: WSF_FORM_FIELD_SET w_select: WSF_FORM_SELECT w_opt: WSF_FORM_SELECT_OPTION @@ -126,6 +127,9 @@ feature -- Forms ... l_vocs as vocs_ic loop voc := vocs_ic.item + create th.make_with_text ({STRING_32} "taxonomy_vocabularies[" + voc.id.out + "]", voc.name) + w_set.extend (th) + l_terms := Void if a_node /= Void and then a_node.has_id then l_terms := l_taxonomy_api.terms_of_entity (a_node.content_type, a_node.id.out, voc) @@ -139,7 +143,7 @@ feature -- Forms ... if voc.is_tags then w_voc_set.set_legend (response.translation (voc.name, Void)) - create ti.make ({STRING_32} "taxonomy_terms[" + voc.name + "]") + create ti.make ({STRING_32} "taxonomy_" + voc.id.out) w_voc_set.extend (ti) if voc.is_term_required then ti.enable_required @@ -186,7 +190,8 @@ feature -- Forms ... voc as voc_terms_ic loop t := voc_terms_ic.item - create w_cb.make_with_value ({STRING_32} "taxonomy_terms[" + voc.name + "]", t.text) + create w_cb.make_with_value ({STRING_32} "taxonomy_" + voc.id.out + "[]", t.text) + w_cb.set_title (t.text) w_voc_set.extend (w_cb) if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then w_cb.set_checked (True) @@ -196,7 +201,7 @@ feature -- Forms ... end end else - create w_select.make ({STRING_32} "taxonomy_terms[" + voc.name + "]") + create w_select.make ({STRING_32} "taxonomy_" + voc.id.out) w_voc_set.extend (w_select) if attached voc.description as l_desc then @@ -248,68 +253,81 @@ feature -- Forms ... l_text: READABLE_STRING_GENERAL l_found: BOOLEAN t: detachable CMS_TERM + vid: INTEGER_64 do if a_node /= Void and then a_node.has_id and then - attached fd.table_item ("taxonomy_terms") as fd_terms + attached fd.table_item ("taxonomy_vocabularies") as fd_vocs then - across - fd_terms.values as ic - loop - if attached {WSF_STRING} ic.item as l_string then - l_voc_name := ic.key - l_new_terms := a_taxonomy_api.splitted_string (l_string.value, ',') - if attached a_vocs.item_by_name (l_voc_name) as voc then - if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + content_type.name + " taxonomy">>) then - create l_terms_to_remove.make (0) - if attached a_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, voc) as l_existing_terms then - across - l_existing_terms as t_ic + if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + content_type.name + " taxonomy">>) then + across + fd_vocs.values as ic + loop + vid := ic.key.to_integer_64 + l_voc_name := ic.item.string_representation + + if attached a_vocs.item_by_id (vid) as voc then + if attached fd.string_item ("taxonomy_" + vid.out) as l_string then + l_new_terms := a_taxonomy_api.splitted_string (l_string, ',') + elseif attached fd.table_item ("taxonomy_" + vid.out) as fd_terms then + create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (fd_terms.count) + across + fd_terms as t_ic + loop + l_new_terms.force (t_ic.item.string_representation) + end + else + create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (0) + end + + create l_terms_to_remove.make (0) + if attached a_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, voc) as l_existing_terms then + across + l_existing_terms as t_ic + loop + l_text := t_ic.item.text + from + l_found := False + l_new_terms.start + until + l_new_terms.after loop - l_text := t_ic.item.text - from - l_found := False - l_new_terms.start - until - l_new_terms.after - loop - if l_new_terms.item.same_string_general (l_text) then - -- Already associated with term `t_ic.text'. - l_found := True - l_new_terms.remove - else - l_new_terms.forth - end - end - if not l_found then - -- Remove term - l_terms_to_remove.force (t_ic.item) + if l_new_terms.item.same_string_general (l_text) then + -- Already associated with term `t_ic.text'. + l_found := True + l_new_terms.remove + else + l_new_terms.forth end end - across - l_terms_to_remove as t_ic - loop - a_taxonomy_api.unassociate_term_from_entity (t_ic.item, content_type.name, a_node.id.out) + if not l_found then + -- Remove term + l_terms_to_remove.force (t_ic.item) end end across - l_new_terms as t_ic + l_terms_to_remove as t_ic loop - t := a_taxonomy_api.term_by_text (t_ic.item, voc) - if - t = Void and voc.is_tags - then - -- Create new term! - create t.make (t_ic.item) - a_taxonomy_api.save_term (t, voc) - if a_taxonomy_api.has_error then - t := Void - end - end - if t /= Void then - a_taxonomy_api.associate_term_with_entity (t, content_type.name, a_node.id.out) + a_taxonomy_api.unassociate_term_from_entity (t_ic.item, content_type.name, a_node.id.out) + end + end + across + l_new_terms as t_ic + loop + t := a_taxonomy_api.term_by_text (t_ic.item, voc) + if + t = Void and voc.is_tags + then + -- Create new term! + create t.make (t_ic.item) + a_taxonomy_api.save_term (t, voc) + if a_taxonomy_api.has_error then + t := Void end end + if t /= Void then + a_taxonomy_api.associate_term_with_entity (t, content_type.name, a_node.id.out) + end end end end diff --git a/modules/node/handler/node_form_response.e b/modules/node/handler/node_form_response.e index f8da987..604bcb5 100644 --- a/modules/node/handler/node_form_response.e +++ b/modules/node/handler/node_form_response.e @@ -7,30 +7,10 @@ class inherit NODE_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api, a_node_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Execution process diff --git a/modules/node/handler/node_view_response.e b/modules/node/handler/node_view_response.e index 3a39f96..64b800b 100644 --- a/modules/node/handler/node_view_response.e +++ b/modules/node/handler/node_view_response.e @@ -8,30 +8,10 @@ class inherit NODE_RESPONSE - redefine - make, - initialize - end create make -feature {NONE} -- Initialization - - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_api: like api; a_node_api: like node_api) - do - create {WSF_NULL_THEME} wsf_theme.make - Precursor (req, res, a_api, a_node_api) - end - - initialize - do - Precursor - create {CMS_TO_WSF_THEME} wsf_theme.make (Current, theme) - end - - wsf_theme: WSF_THEME - feature -- Access node: detachable CMS_NODE diff --git a/modules/recent_changes/cms_recent_changes_module.e b/modules/recent_changes/cms_recent_changes_module.e index d685d4b..9715dff 100644 --- a/modules/recent_changes/cms_recent_changes_module.e +++ b/modules/recent_changes/cms_recent_changes_module.e @@ -284,7 +284,7 @@ feature -- Handler create l_submit.make_with_text ("op", "Filter") l_form.extend (l_submit) l_form.extend_html_text ("
") - l_form.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), l_content) + l_form.append_to_html (r.wsf_theme, l_content) end l_changes.reverse_sort diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e index 69534d8..677dbdf 100644 --- a/modules/taxonomy/cms_taxonomy_api.e +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -52,7 +52,7 @@ feature -- Access node Result := taxonomy_storage.vocabularies (a_limit, a_offset) end - vocabulary (a_id: INTEGER): detachable CMS_VOCABULARY + vocabulary (a_id: INTEGER_64): detachable CMS_VOCABULARY -- Vocabulary associated with id `a_id'. require valid_id: a_id > 0 @@ -66,6 +66,27 @@ feature -- Access node Result := taxonomy_storage.vocabularies_for_type (a_type_name) end + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- Type names associated with `a_vocab'. + do + Result := taxonomy_storage.types_associated_with_vocabulary (a_vocab) + end + + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- Vocabularies including `a_term'. + do + Result := taxonomy_storage.vocabularies_for_term (a_term) + end + + is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN + -- Is `a_term' inside `a_vocab' ? + require + valid_term: a_term.has_id + valid_vocabulary: a_vocab.has_id + do + Result := taxonomy_storage.is_term_inside_vocabulary (a_term, a_vocab) + end + fill_vocabularies_with_terms (a_vocab: CMS_VOCABULARY) -- Fill `a_vocab' with associated terms. do @@ -125,19 +146,39 @@ feature -- Access node feature -- Write save_vocabulary (a_voc: CMS_VOCABULARY) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True. do reset_error - taxonomy_storage.save_vocabulary (a_voc) + taxonomy_storage.save_vocabulary (a_voc, False) error_handler.append (taxonomy_storage.error_handler) end - save_term (a_term: CMS_TERM; voc: CMS_VOCABULARY) + save_vocabulary_and_terms (a_voc: CMS_VOCABULARY) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms. + do + reset_error + taxonomy_storage.save_vocabulary (a_voc, True) + error_handler.append (taxonomy_storage.error_handler) + end + + save_term (a_term: CMS_TERM; voc: detachable CMS_VOCABULARY) + -- Save `a_term' inside `voc' if set. do reset_error taxonomy_storage.save_term (a_term, voc) error_handler.append (taxonomy_storage.error_handler) end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Remove term `t' from vocabulary `voc'. + do + reset_error + taxonomy_storage.remove_term_from_vocabulary (t, voc) + error_handler.append (taxonomy_storage.error_handler) + end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) -- Associate term `a_term' with `(a_type_name, a_entity)'. do diff --git a/modules/taxonomy/cms_taxonomy_module.e b/modules/taxonomy/cms_taxonomy_module.e index bfbe5ea..0d3ddb5 100644 --- a/modules/taxonomy/cms_taxonomy_module.e +++ b/modules/taxonomy/cms_taxonomy_module.e @@ -109,6 +109,7 @@ feature -- Access: router do if attached taxonomy_api as l_taxonomy_api then configure_web (a_api, l_taxonomy_api, a_router) + configure_web_amin (a_api, l_taxonomy_api, a_router) else -- Issue with api/dependencies, -- thus Current module should not be used! @@ -120,9 +121,48 @@ feature -- Access: router -- Configure router mapping for web interface. local l_taxonomy_handler: TAXONOMY_HANDLER + l_voc_handler: TAXONOMY_VOCABULARY_HANDLER do create l_taxonomy_handler.make (a_api, a_taxonomy_api) a_router.handle ("/taxonomy/term/{termid}", l_taxonomy_handler, a_router.methods_get) + + create l_voc_handler.make (a_api, a_taxonomy_api) + a_router.handle ("/taxonomy/vocabulary/", l_voc_handler, a_router.methods_get) + a_router.handle ("/taxonomy/vocabulary/{vocid}", l_voc_handler, a_router.methods_get) + end + + configure_web_amin (a_api: CMS_API; a_taxonomy_api: CMS_TAXONOMY_API; a_router: WSF_ROUTER) + -- Configure router mapping for web interface. + local + l_taxonomy_handler: TAXONOMY_TERM_ADMIN_HANDLER + l_voc_handler: TAXONOMY_VOCABULARY_ADMIN_HANDLER + do + a_router.handle ("/admin/taxonomy/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_admin_taxonomy (?, ?, a_api)), a_router.methods_get) + + create l_taxonomy_handler.make (a_api, a_taxonomy_api) + a_router.handle ("/admin/taxonomy/term/", l_taxonomy_handler, a_router.methods_get_post) + a_router.handle ("/admin/taxonomy/term/{termid}", l_taxonomy_handler, a_router.methods_get_post) + + create l_voc_handler.make (a_api, a_taxonomy_api) + a_router.handle ("/admin/taxonomy/vocabulary/", l_voc_handler, a_router.methods_get_post) + a_router.handle ("/admin/taxonomy/vocabulary/{vocid}", l_voc_handler, a_router.methods_get_post) + end + +feature -- Handler + + handle_admin_taxonomy (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API) + local + l_page: CMS_RESPONSE + lnk: CMS_LOCAL_LINK + do + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create lnk.make ("Admin Vocabularies", "admin/taxonomy/vocabulary/") + l_page.add_to_primary_tabs (lnk) + + create lnk.make ("Create terms", "admin/taxonomy/term/") + l_page.add_to_primary_tabs (lnk) + + l_page.execute end feature -- Hooks @@ -139,10 +179,14 @@ feature -- Hooks end menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + local + lnk: CMS_LOCAL_LINK do - -- Add the link to the taxonomy to the main menu --- create lnk.make ("Taxonomy", "taxonomy/") --- a_menu_system.primary_menu.extend (lnk) + -- Add the link to the taxonomy to the main menu + if a_response.has_permission ("admin taxonomy") then + create lnk.make ("Taxonomy", "admin/taxonomy/") + a_menu_system.management_menu.extend (lnk) + end end end diff --git a/modules/taxonomy/cms_term_collection.e b/modules/taxonomy/cms_term_collection.e index 5e882e7..dc96960 100644 --- a/modules/taxonomy/cms_term_collection.e +++ b/modules/taxonomy/cms_term_collection.e @@ -19,6 +19,7 @@ feature {NONE} -- Initialization make (nb: INTEGER) do create items.make (nb) + items.compare_objects end feature -- Access @@ -48,6 +49,21 @@ feature -- Status report Result := items.has (a_term) end + term_by_id (tid: INTEGER_64): detachable CMS_TERM + -- Term of id `tid' contained in Current, if any. + do + across + items as ic + until + Result /= Void + loop + Result := ic.item + if Result.id /= tid then + Result := Void + end + end + end + feature -- Element change wipe_out diff --git a/modules/taxonomy/cms_vocabulary_collection.e b/modules/taxonomy/cms_vocabulary_collection.e index b4c3028..b8ccf05 100644 --- a/modules/taxonomy/cms_vocabulary_collection.e +++ b/modules/taxonomy/cms_vocabulary_collection.e @@ -19,6 +19,7 @@ feature {NONE} -- Initialization make (nb: INTEGER) do create items.make (nb) + items.compare_objects end feature -- Access @@ -37,6 +38,21 @@ feature -- Access end end + item_by_id (a_id: INTEGER_64): detachable CMS_VOCABULARY + -- Vocabulary of id `a_id' contained in Current, if any. + do + across + items as ic + until + Result /= Void + loop + Result := ic.item + if Result.id /= a_id then + Result := Void + end + end + end + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_VOCABULARY] -- do diff --git a/modules/taxonomy/handler/taxonomy_term_admin_handler.e b/modules/taxonomy/handler/taxonomy_term_admin_handler.e new file mode 100644 index 0000000..9e97d36 --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_term_admin_handler.e @@ -0,0 +1,269 @@ +note + description: "[ + Request handler related to + /admin/taxonomy/term/{termid} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_TERM_ADMIN_HANDLER + +inherit + CMS_MODULE_HANDLER [CMS_TAXONOMY_API] + rename + module_api as taxonomy_api + end + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post + end + + REFACTORING_HELPER + + CMS_API_ACCESS + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for any kind of mapping. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI mapping. + do + execute (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI-template mapping. + do + execute (req, res) + end + +feature -- HTTP Methods + + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: CMS_RESPONSE + tid: INTEGER_64 + s: STRING + f: CMS_FORM + t: detachable CMS_TERM + l_parents: detachable CMS_VOCABULARY_COLLECTION + do + if + attached {WSF_STRING} req.path_parameter ("termid") as p_termid and then + p_termid.is_integer + then + tid := p_termid.value.to_integer_64 + if tid > 0 then + t := taxonomy_api.term_by_id (tid) + end + end + + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + if l_page.has_permission ("admin taxonomy") then + + if t = Void then + l_page.set_title ("New term ...") + create t.make ("") + else + l_page.set_title (t.text) + end + create s.make_empty + f := edit_form (t, l_page, req) + f.process (l_page) + if + attached f.last_data as fd and then + fd.is_valid + then + if attached fd.string_item ("op") as l_op and then l_op.same_string ("Save changes") then + if attached fd.string_item ("text") as l_text then + t.set_text (l_text) + l_page.set_title (t.text) + end + if attached fd.string_item ("description") as l_description then + t.set_description (l_description) + end + if attached fd.string_item ("weight") as l_weight and then l_weight.is_integer then + t.set_weight (l_weight.to_integer) + end + taxonomy_api.save_term (t, Void) + if taxonomy_api.has_error then + fd.report_error ("Term creation failed!") + else + l_page.add_success_message ("Term creation succeed.") + s.append ("
View term: ") + s.append (l_page.link (t.text, "admin/taxonomy/term/" + t.id.out, Void)) + s.append ("
") + + if + attached fd.table_item ("vocabularies") as voc_tb and then + attached taxonomy_api.vocabularies (0, 0) as l_vocabularies + then + l_parents := taxonomy_api.vocabularies_for_term (t) + across + voc_tb as vid_ic + until + taxonomy_api.has_error + loop + if attached l_vocabularies.item_by_id (vid_ic.item.string_representation.to_integer_64) as v then + if l_parents /= Void and then attached l_parents.item_by_id (v.id) as l_v then + -- Already as parent! + l_parents.remove (l_v) + else + taxonomy_api.save_term (t, v) + l_vocabularies.remove (v) + end + end + end + if l_parents /= Void then + across + l_parents as v_ic + until + taxonomy_api.has_error + loop + taxonomy_api.remove_term_from_vocabulary (t, v_ic.item) + end + end + end + -- l_page.set_redirection (l_page.location) + end + else + fd.report_error ("Invalid form data!") + end + end + f.append_to_html (l_page.wsf_theme, s) + l_page.set_main_content (s) + l_page.execute + else + send_access_denied (req, res) + end + end + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + tid: INTEGER_64 + s: STRING + f: CMS_FORM + t: detachable CMS_TERM + do + if + attached {WSF_STRING} req.path_parameter ("termid") as p_termid and then + p_termid.is_integer + then + tid := p_termid.value.to_integer_64 + if tid > 0 then + t := taxonomy_api.term_by_id (tid) + end + end + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + if l_page.has_permission ("admin taxonomy") then + if t = Void then + l_page.set_title ("Create term ...") + create t.make ("") + else + l_page.set_title (t.text) + end + create s.make_empty + f := edit_form (t, l_page, req) + f.append_to_html (l_page.wsf_theme, s) + l_page.set_main_content (s) + l_page.execute + else + send_access_denied (req, res) + end + end + + edit_form (t: CMS_TERM; a_page: CMS_RESPONSE; req: WSF_REQUEST): CMS_FORM + local + f: CMS_FORM + voc: detachable CMS_VOCABULARY + w_tf: WSF_FORM_TEXT_INPUT + w_txt: WSF_FORM_TEXTAREA + w_set: WSF_FORM_FIELD_SET + w_cb: WSF_FORM_CHECKBOX_INPUT + l_parents: detachable CMS_VOCABULARY_COLLECTION + do + create f.make (req.percent_encoded_path_info, "taxonomy") + if t.has_id then + f.extend_html_text (a_page.link ("View associated entities", "taxonomy/term/" + t.id.out, Void)) + end + create w_tf.make_with_text ("text", t.text) + w_tf.set_is_required (True) + w_tf.set_label ("Text") + f.extend (w_tf) + + create w_txt.make ("description") + w_txt.set_label ("Description") + w_txt.set_rows (3) + w_txt.set_cols (60) + if attached t.description as l_desc then + w_txt.set_text_value (api.html_encoded (l_desc)) + end + w_txt.set_description ("Description of the terms; can be used by modules or administration.") + f.extend (w_txt) + + create w_tf.make_with_text ("weight", t.weight.out) + w_tf.set_label ("Weight") + w_tf.set_description ("Terms are sorted in ascending order by weight.") + f.extend (w_tf) + + if attached taxonomy_api.vocabularies (0, 0) as vocs then + if t.has_id then + l_parents := taxonomy_api.vocabularies_for_term (t) + end + create w_set.make + w_set.set_legend ("Associated vocabularies") + across + vocs as ic + loop + voc := ic.item + create w_cb.make_with_value ("vocabularies[]", ic.item.id.out) + w_cb.set_title (voc.name) + if + l_parents /= Void and then + across l_parents as p_ic some p_ic.item.id = ic.item.id end + then + w_cb.set_checked (True) + end + w_set.extend (w_cb) + end + if w_set.count > 0 then + f.extend (w_set) + end + end + + f.extend (create {WSF_FORM_SUBMIT_INPUT}.make_with_text ("op", "Save changes")) + Result := f + end + + +end diff --git a/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e b/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e new file mode 100644 index 0000000..c3ba3ac --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e @@ -0,0 +1,414 @@ +note + description: "[ + Request handler related to + /admin/taxonomy/vocabulary/ + /admin/taxonomy/vocabulary/{vocid} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_VOCABULARY_ADMIN_HANDLER + +inherit + CMS_MODULE_HANDLER [CMS_TAXONOMY_API] + rename + module_api as taxonomy_api + end + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post + end + + REFACTORING_HELPER + + CMS_API_ACCESS + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for any kind of mapping. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI mapping. + do + execute (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI-template mapping. + do + execute (req, res) + end + +feature -- HTTP Methods + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: CMS_RESPONSE + voc: CMS_VOCABULARY + l_typename: READABLE_STRING_GENERAL + s: STRING + do + if not api.user_has_permission (current_user (req), "admin taxonomy") then + send_access_denied (req, res) + else + if attached {WSF_STRING} req.form_parameter ("op") as p_op then + if p_op.same_string ("New Vocabulary") then + if + attached {WSF_STRING} req.form_parameter ("vocabulary_name") as p_name and then + not p_name.is_empty + then + create voc.make (p_name.value) + taxonomy_api.save_vocabulary (voc) + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + if taxonomy_api.has_error then + l_page.add_error_message ("Vocabulary creation failed!") + else + l_page.add_success_message ("Vocabulary creation succeed!") + l_page.set_redirection ("admin/taxonomy/vocabulary/" + voc.id.out) + end + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + elseif + p_op.same_string ("Save changes") and then + attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid and then p_vocid.is_integer and then + attached taxonomy_api.vocabulary (p_vocid.value.to_integer_64) as l_vocabulary + then + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create s.make_empty + l_page.add_notice_message ("Vocabulary " + l_vocabulary.id.out) + if attached {WSF_STRING} req.form_parameter ("name") as p_name then + l_vocabulary.set_name (p_name.value) + end + if attached {WSF_STRING} req.form_parameter ("description") as p_desc then + l_vocabulary.set_description (p_desc.value) + end + if attached {WSF_STRING} req.form_parameter ("weight") as p_weight and then p_weight.is_integer then + l_vocabulary.set_weight (p_weight.integer_value) + end + taxonomy_api.save_vocabulary (l_vocabulary) + if taxonomy_api.has_error then + l_page.add_error_message ("Could not save vocabulary") + elseif + attached {WSF_TABLE} req.form_parameter ("typenames") as typenames_table + then + across + typenames_table as ic + loop + l_typename := ic.item.string_representation + create voc.make_from_term (l_vocabulary) + voc.set_associated_content_type (l_typename, False, False, False) + l_page.add_notice_message ("Content type :" + api.html_encoded (l_typename)) + if attached {WSF_TABLE} req.form_parameter ({STRING_32} "vocabulary_" + l_typename.as_string_32) as opts then + across + opts as o_ic + loop + if o_ic.item.same_string ("tags") then + voc.set_is_tags (True) + elseif o_ic.item.same_string ("multiple") then + voc.allow_multiple_term (True) + elseif o_ic.item.same_string ("required") then + voc.set_is_term_required (True) + end + end + end + taxonomy_api.associate_vocabulary_with_type (voc, l_typename) + if taxonomy_api.has_error then + l_page.add_error_message ("Could not save vocabulary content type associations.") + end + end + end + if not taxonomy_api.has_error then + l_page.add_notice_message (l_page.link ({STRING_32} "Back to vocabulary %"" + l_vocabulary.name + "%"", "admin/taxonomy/vocabulary/" + l_vocabulary.id.out, Void)) + end + l_page.set_main_content (s) + else + create {NOT_IMPLEMENTED_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + l_page.execute + end + end + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + tid: INTEGER_64 + do + if not api.user_has_permission (current_user (req), "admin taxonomy") then + send_access_denied (req, res) + else + if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then + if p_vocid.is_integer then + tid := p_vocid.value.to_integer_64 + end + end + if tid > 0 then + do_get_vocabulary (tid, req, res) + else + do_get_vocabularies (req, res) + end + end + end + + do_get_vocabulary (tid: INTEGER_64; req: WSF_REQUEST; res: WSF_RESPONSE) + -- + require + valid_tid: tid > 0 + local + l_page: CMS_RESPONSE + s: STRING + l_typename: detachable READABLE_STRING_8 + v: detachable CMS_VOCABULARY + l_typenames: detachable LIST [READABLE_STRING_32] + f: CMS_FORM + wtb: WSF_WIDGET_TABLE + wtb_row: WSF_WIDGET_TABLE_ROW + wtb_item: WSF_WIDGET_TABLE_ITEM + voc: detachable CMS_VOCABULARY + l_term: detachable CMS_TERM + tf_input: WSF_FORM_TEXT_INPUT + tf_text: WSF_FORM_TEXTAREA + tf_num: WSF_FORM_NUMBER_INPUT + w_set: WSF_FORM_FIELD_SET + w_cb: WSF_FORM_CHECKBOX_INPUT + sub: WSF_FORM_SUBMIT_INPUT + do + voc := taxonomy_api.vocabulary (tid) + if voc /= Void then + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + l_page.set_title (voc.name) + taxonomy_api.fill_vocabularies_with_terms (voc) + + create f.make (req.percent_encoded_path_info, "taxonomy") + + create tf_input.make_with_text ("name", voc.name) + f.extend (tf_input) + + create tf_text.make ("description") + tf_text.set_text_value (voc.description) + tf_text.set_description ("Description of the vocabulary; also used as intructions to present to the user when selecting terms.") + tf_text.set_rows (3) + f.extend (tf_text) + + create tf_num.make_with_text ("weight", voc.weight.out) + tf_num.set_label ("weight") + tf_num.set_description ("Items are displayed in ascending order by weight.") + f.extend (tf_num) + + create wtb.make + wtb.add_css_class ("with_border") + create wtb_row.make (2) + create wtb_item.make_with_text ("Text") + wtb_row.set_item (wtb_item, 1) + create wtb_item.make_with_text ("Description") + wtb_row.set_item (wtb_item, 2) + wtb.add_head_row (wtb_row) + across + voc as ic + loop + l_term := ic.item + + create wtb_row.make (3) + wtb.add_row (wtb_row) + + create wtb_item.make_with_text (l_page.link (ic.item.text, "admin/taxonomy/term/" + l_term.id.out, Void)) + wtb_row.set_item (wtb_item, 1) + if attached ic.item.description as l_desc then + create wtb_item.make_with_text (api.html_encoded (l_desc)) + else + create wtb_item.make_with_text ("") + end + wtb_row.set_item (wtb_item, 2) + end + if wtb.body_row_count > 0 then + f.extend (wtb) + else + f.extend_raw_text ("No terms.") + end + + create w_set.make + w_set.set_legend ("Content types") + f.extend (w_set) + + + l_typenames := taxonomy_api.types_associated_with_vocabulary (voc) + create wtb.make + wtb.add_css_class ("with_border") + create wtb_row.make (5) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_text ("Type"), 1) + create wtb_item.make_with_text ("Settings ...") + wtb_item.add_html_attribute ("colspan", "3") + wtb_row.set_item (wtb_item, 2) + wtb.add_head_row (wtb_row) + + across + api.content_types as ic + loop + create wtb_row.make (4) + wtb.add_row (wtb_row) + + l_typename := ic.item.name + create w_cb.make_with_value ("typenames[]", api.html_encoded (l_typename)) + w_cb.set_title (ic.item.name) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 1) + + v := Void + if + l_typenames /= Void and then + across l_typenames as tn_ic some l_typename.is_case_insensitive_equal (tn_ic.item) end + then + w_cb.set_checked (True) + if attached taxonomy_api.vocabularies_for_type (l_typename) as v_list then + across v_list as v_ic until v /= Void loop + if v_ic.item.id = voc.id then + v := v_ic.item + end + end + end + end + create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "tags") + w_cb.set_title ("Tags") + w_cb.set_checked (v /= Void and then v.is_tags) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 2) + + create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "multiple") + w_cb.set_title ("Multiple Select") + w_cb.set_checked (v /= Void and then v.multiple_terms_allowed) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 3) + + create w_cb.make_with_value ("vocabulary_" + l_typename +"[]", "required") + w_cb.set_title ("Required") + w_cb.set_checked (v /= Void and then v.is_term_required) + wtb_row.set_item (create {WSF_WIDGET_TABLE_ITEM}.make_with_content (w_cb), 4) + end + if wtb.body_row_count > 0 then + w_set.extend (wtb) + end + + create sub.make_with_text ("op", "Save changes") + f.extend (sub) + + create s.make_empty + f.append_to_html (l_page.wsf_theme, s) + l_page.set_main_content (s) + else + -- Responding with `main_content_html (l_page)'. + create {NOT_FOUND_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + l_page.execute + end + + do_get_vocabularies (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + s: STRING + l_typenames: detachable LIST [READABLE_STRING_32] + f: CMS_FORM + wtb: WSF_WIDGET_TABLE + wtb_row: WSF_WIDGET_TABLE_ROW + wtb_item: WSF_WIDGET_TABLE_ITEM + voc: detachable CMS_VOCABULARY + tf_input: WSF_FORM_TEXT_INPUT + w_set: WSF_FORM_FIELD_SET + sub: WSF_FORM_SUBMIT_INPUT + do + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create wtb.make + wtb.add_css_class ("with_border") + create wtb_row.make (3) + create wtb_item.make_with_text ("Name") + wtb_row.set_item (wtb_item, 1) + create wtb_item.make_with_text ("Type") + wtb_row.set_item (wtb_item, 2) + create wtb_item.make_with_text ("Operations") + wtb_row.set_item (wtb_item, 3) + wtb.add_head_row (wtb_row) + + if attached taxonomy_api.vocabularies (0, 0) as lst then + across + lst as ic + loop + voc := ic.item + create wtb_row.make (3) + wtb.add_row (wtb_row) + + create wtb_item.make_with_text (l_page.link (ic.item.name, "admin/taxonomy/vocabulary/" + ic.item.id.out, Void)) +-- if attached ic.item.description as l_desc then +-- s.append (" : ") +-- s.append (api.html_encoded (l_desc)) +-- s.append ("") +-- end + wtb_row.set_item (wtb_item, 1) + l_typenames := taxonomy_api.types_associated_with_vocabulary (voc) + if l_typenames /= Void then + create s.make_empty + across + l_typenames as types_ic + loop + if not s.is_empty then + s.append_character (',') + s.append_character (' ') + end + s.append (api.html_encoded (types_ic.item)) + end + create wtb_item.make_with_text (s) + wtb_row.set_item (wtb_item, 2) + end + + s := l_page.link ("edit", "admin/taxonomy/vocabulary/" + voc.id.out, Void) + create wtb_item.make_with_text (s) + wtb_row.set_item (wtb_item, 3) + end + end + create f.make (req.percent_encoded_path_info, "taxonomy") + f.set_method_post + + f.extend (wtb) + + create w_set.make + w_set.set_legend ("Create a new vocabulary") + create tf_input.make_with_text ("vocabulary_name", "") + tf_input.set_label ("Vocabulary name") + w_set.extend (tf_input) + create sub.make_with_text ("op", "New Vocabulary") + w_set.extend (sub) + f.extend (w_set) + + create s.make_empty + f.append_to_html (l_page.wsf_theme, s) + l_page.set_title ("Vocabularies") + l_page.set_main_content (s) + l_page.execute + end + +end diff --git a/modules/taxonomy/handler/taxonomy_vocabulary_handler.e b/modules/taxonomy/handler/taxonomy_vocabulary_handler.e new file mode 100644 index 0000000..6db90f4 --- /dev/null +++ b/modules/taxonomy/handler/taxonomy_vocabulary_handler.e @@ -0,0 +1,129 @@ +note + description: "[ + Request handler related to + /taxonomy/vocabulary/ + /taxonomy/vocabulary/{vocid} + ]" + date: "$Date$" + revision: "$revision$" + +class + TAXONOMY_VOCABULARY_HANDLER + +inherit + CMS_MODULE_HANDLER [CMS_TAXONOMY_API] + rename + module_api as taxonomy_api + end + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + + CMS_API_ACCESS + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for any kind of mapping. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI mapping. + do + execute (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler for URI-template mapping. + do + execute (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + tid: INTEGER_64 + s: STRING + do + if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then + if p_vocid.is_integer then + tid := p_vocid.value.to_integer_64 + end + end + if tid > 0 then + if attached taxonomy_api.vocabulary (tid) as voc then + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + l_page.set_title (voc.name) + taxonomy_api.fill_vocabularies_with_terms (voc) + + create s.make_empty + s.append ("
    ") + across + voc as ic + loop + s.append ("
  • ") + s.append (l_page.link (ic.item.text, "taxonomy/term/" + ic.item.id.out, Void)) + if attached ic.item.description as l_desc then + s.append (" : ") + s.append (api.html_encoded (l_desc)) + s.append ("") + end + s.append ("
  • ") + end + s.append ("
") + l_page.set_main_content (s) + else + -- Responding with `main_content_html (l_page)'. + create {NOT_FOUND_ERROR_CMS_RESPONSE} l_page.make (req, res, api) + end + l_page.execute + else + -- Responding with `main_content_html (l_page)'. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + create s.make_empty + if attached taxonomy_api.vocabularies (0, 0) as lst then + s.append ("
    ") + across + lst as ic + loop + s.append ("
  • ") + s.append (l_page.link (ic.item.name, "taxonomy/vocabulary/" + ic.item.id.out, Void)) + s.append ("
  • ") + end + s.append ("
") + else + s.append ("No vocabulary!") + end + l_page.set_main_content (s) + l_page.execute + end + end + +end diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e index a915ebf..cfd7132 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_i.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_i.e @@ -41,6 +41,24 @@ feature -- Access deferred end + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- Vocabularies including `a_term'. + deferred + end + + is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN + -- Is `a_term' inside `a_vocab' ? + require + valid_term: a_term.has_id + valid_vocabulary: a_vocab.has_id + deferred + end + + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- Type names associated with `a_vocab'. + deferred + end + terms_count: INTEGER_64 -- Number of terms. deferred @@ -89,20 +107,28 @@ feature -- Access feature -- Store - save_vocabulary (a_voc: CMS_VOCABULARY) - -- Insert or update vocabulary `a_voc'. + save_vocabulary (a_voc: CMS_VOCABULARY; a_with_terms: BOOLEAN) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True. deferred ensure not error_handler.has_error implies a_voc.has_id and then vocabulary (a_voc.id) /= Void end - save_term (t: CMS_TERM; voc: CMS_VOCABULARY) - -- Insert or update term `t' as part of vocabulary `voc'. + save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY) + -- Insert or update term `t' as part of vocabulary `voc' if set. deferred ensure not error_handler.has_error implies t.has_id and then term_by_id (t.id) /= Void end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Remove term `t' from vocabulary `voc'. + require + t_has_id: t.has_id + deferred + end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) -- Associate term `a_term' with `(a_type_name, a_entity)'. require diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e index 2e663cd..f8336b6 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_null.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_null.e @@ -53,6 +53,21 @@ feature -- Access do end + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- + do + end + + is_term_inside_vocabulary (a_term: CMS_TERM; a_vocab: CMS_VOCABULARY): BOOLEAN + -- + do + end + + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- + do + end + terms_count: INTEGER_64 -- Number of terms. do @@ -85,18 +100,25 @@ feature -- Access feature -- Store - save_vocabulary (a_voc: CMS_VOCABULARY) - -- Insert or update vocabulary `a_voc'. + save_vocabulary (a_voc: CMS_VOCABULARY; a_with_terms: BOOLEAN) + -- Insert or update vocabulary `a_voc' + -- and also save {CMS_VOCABULARY}.terms if `a_with_terms' is True. do error_handler.add_custom_error (-1, "not implemented", "save_vocabulary") end - save_term (t: CMS_TERM; voc: CMS_VOCABULARY) + save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY) -- do error_handler.add_custom_error (-1, "not implemented", "save_term") end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + -- Remove term `t' from vocabulary `voc'. + do + error_handler.add_custom_error (-1, "not implemented", "remove_term_from_vocabulary") + end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) do error_handler.add_custom_error (-1, "not implemented", "associate_term_with_entity") diff --git a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e index 7bb850b..15cad57 100644 --- a/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e +++ b/modules/taxonomy/persistence/cms_taxonomy_storage_sql.e @@ -232,21 +232,41 @@ feature -- Access feature -- Store - save_vocabulary (voc: CMS_VOCABULARY) + save_vocabulary (voc: CMS_VOCABULARY; a_with_terms: BOOLEAN) + local + l_terms: CMS_TERM_COLLECTION do save_term (voc, create {CMS_VOCABULARY}.make_none) - across - voc.terms as ic - until - has_error - loop - save_term (ic.item, voc) + + if a_with_terms then + l_terms := terms (voc, 0, 0) + across + voc.terms as ic + until + has_error + loop + if attached l_terms.term_by_id (ic.item.id) as t then + -- Already contained. + -- Remove from `l_terms' to leave only terms to remove. + l_terms.remove (t) + else + save_term (ic.item, voc) + end + end + across + l_terms as ic + until + has_error + loop + remove_term_from_vocabulary (ic.item, voc) + end end end - save_term (t: CMS_TERM; voc: CMS_VOCABULARY) + save_term (t: CMS_TERM; voc: detachable CMS_VOCABULARY) local l_parameters: STRING_TABLE [detachable ANY] + l_insert_voc: BOOLEAN do error_handler.reset @@ -255,6 +275,8 @@ feature -- Store l_parameters.put (t.description, "description") l_parameters.put (t.weight, "weight") + l_insert_voc := voc /= Void and then is_term_inside_vocabulary (t, voc) + sql_begin_transaction if t.has_id then l_parameters.put (t.id, "tid") @@ -263,9 +285,18 @@ feature -- Store sql_insert (sql_insert_term, l_parameters) t.set_id (last_inserted_term_id) end - if not has_error then + if + not has_error and + voc /= Void and then + not l_insert_voc + then + l_parameters.wipe_out l_parameters.put (t.id, "tid") - l_parameters.put (voc.id, "parent_tid") + if voc.has_id then + l_parameters.put (voc.id, "parent_tid") + else + l_parameters.put (0, "parent_tid") + end sql_insert (sql_insert_term_in_vocabulary, l_parameters) end if has_error then @@ -276,6 +307,19 @@ feature -- Store sql_finalize end + remove_term_from_vocabulary (t: CMS_TERM; voc: CMS_VOCABULARY) + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + l_parameters.put (t.id, "tid") + l_parameters.put (voc.id, "parent_tid") + sql_modify (sql_remove_term_from_vocabulary, l_parameters) + sql_finalize + end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) -- Associate term `a_term' with `(a_type_name, a_entity)'. local @@ -376,6 +420,90 @@ feature -- Vocabulary and types end end + is_term_inside_vocabulary (a_term: CMS_TERM; a_voc: CMS_VOCABULARY): BOOLEAN + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (2) + l_parameters.put (a_term.id, "tid") + l_parameters.put (a_voc.id, "parent_tid") + sql_query (sql_select_term_in_vocabulary, l_parameters) + sql_start + if not has_error or sql_after then + Result := sql_read_integer_64 (1) > 0 + end + sql_finalize + end + + vocabularies_for_term (a_term: CMS_TERM): detachable CMS_VOCABULARY_COLLECTION + -- + local + voc: detachable CMS_VOCABULARY + l_parameters: STRING_TABLE [detachable ANY] + l_parent_id: INTEGER_64 + l_ids: ARRAYED_LIST [INTEGER_64] + do + error_handler.reset + + create l_parameters.make (3) + l_parameters.put (a_term.id, "tid") + sql_query (sql_select_vocabularies_for_term, l_parameters) + + create l_ids.make (1) + from + sql_start + until + sql_after or has_error + loop + l_parent_id := sql_read_integer_64 (1) + l_ids.force (l_parent_id) + sql_forth + end + sql_finalize + + if l_ids.count > 0 then + create Result.make (1) + across + l_ids as ic + loop + voc := vocabulary (ic.item) + if voc /= Void then + Result.force (voc) + end + end + if Result.count = 0 then + Result := Void + end + end + end + + types_associated_with_vocabulary (a_vocab: CMS_VOCABULARY): detachable LIST [READABLE_STRING_32] + -- Type names associated with `a_vocab'. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + + create l_parameters.make (1) + l_parameters.put (a_vocab.id, "tid") + sql_query (sql_select_type_associated_with_vocabulary, l_parameters) + + create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (3) + from + sql_start + until + sql_after or has_error + loop + if attached sql_read_string_32 (1) as l_typename then + Result.force (l_typename) + end + sql_forth + end + sql_finalize + end + associate_vocabulary_with_type (a_voc: CMS_VOCABULARY; a_type_name: READABLE_STRING_GENERAL) -- local @@ -396,10 +524,17 @@ feature -- Vocabulary and types i := i | mask_is_required end l_parameters.put ((- i).out, "entity") - l_parameters.put (a_type_name, "type") - sql_insert (sql_insert_term_index, l_parameters) + if + attached vocabularies_for_type (a_type_name) as lst and then + across lst as ic some a_voc.id = ic.item.id end + then + sql_modify (sql_update_term_index, l_parameters) + else + sql_insert (sql_insert_term_index, l_parameters) + end + sql_finalize end @@ -464,6 +599,20 @@ feature {NONE} -- Queries ]" -- Terms under :parent_tid. + sql_select_vocabularies_for_term: STRING = "[ + SELECT parent + FROM taxonomy_hierarchy + WHERE tid = :tid + ; + ]" + + sql_select_term_in_vocabulary: STRING = "[ + SELECT count(*) + FROM taxonomy_hierarchy + WHERE tid = :tid and parent = :parent_tid + ; + ]" + sql_select_terms_with_range: STRING = "[ SELECT taxonomy_term.tid, taxonomy_term.text, taxonomy_term.weight, taxonomy_term.description FROM taxonomy_term INNER JOIN taxonomy_hierarchy ON taxonomy_term.tid = taxonomy_hierarchy.tid @@ -505,6 +654,10 @@ feature {NONE} -- Queries VALUES (:tid, :parent_tid); ]" + sql_remove_term_from_vocabulary: STRING = "[ + DELETE FROM taxonomy_hierarchy WHERE tid=:tid AND parent=:parent_tid; + ]" + sql_select_terms_of_entity: STRING = "[ SELECT tid FROM taxonomy_index WHERE type=:type AND entity=:entity; ]" @@ -527,6 +680,19 @@ feature {NONE} -- Queries WHERE type=:type AND entity <= 0; ]" + sql_select_type_associated_with_vocabulary: STRING = "[ + SELECT type + FROM taxonomy_index + WHERE tid=:tid AND entity <= 0; + ]" + + sql_update_term_index: STRING = "[ + UPDATE taxonomy_index + SET entity=:entity + WHERE tid=:tid and type=:type + ; + ]" + sql_insert_term_index: STRING = "[ INSERT INTO taxonomy_index (tid, entity, type) VALUES (:tid, :entity, :type); diff --git a/modules/taxonomy/site/files/css/taxonomy.css b/modules/taxonomy/site/files/css/taxonomy.css index 41c87fa..dd216d0 100644 --- a/modules/taxonomy/site/files/css/taxonomy.css +++ b/modules/taxonomy/site/files/css/taxonomy.css @@ -19,3 +19,8 @@ ul.taxonomy li:hover { border-bottom: solid 1px #66f; background-color: #ddf; } + +table.taxonomy td { + border: solid 1px #ccc; + padding: 2px; +} diff --git a/modules/taxonomy/site/files/scss/taxonomy.scss b/modules/taxonomy/site/files/scss/taxonomy.scss index f409395..2ceff02 100644 --- a/modules/taxonomy/site/files/scss/taxonomy.scss +++ b/modules/taxonomy/site/files/scss/taxonomy.scss @@ -19,3 +19,9 @@ ul.taxonomy { } } } +table.taxonomy { + td { + border: solid 1px #ccc; + padding: 2px; + } +} diff --git a/src/service/cms_api.e b/src/service/cms_api.e index 78a6252..d3a6e4e 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -15,7 +15,7 @@ inherit REFACTORING_HELPER - CMS_ENCODERS + CMS_REQUEST_UTIL create make @@ -407,7 +407,7 @@ feature {NONE} -- Hooks l_hooks: like hooks do l_hooks := hooks - register_hooks (l_hooks) + setup_core_hooks (l_hooks) across enabled_modules as ic loop @@ -436,7 +436,7 @@ feature -- Query: API feature -- Hooks - register_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) + setup_core_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) -- Register hooks associated with the cms core. do a_hooks.subscribe_to_export_hook (Current) diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index 8746d00..ec7daeb 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -72,7 +72,6 @@ feature {NONE} -- Initialization l_module: CMS_MODULE l_enabled_modules: CMS_MODULE_COLLECTION do - api.register_hooks (hooks) l_enabled_modules := api.enabled_modules across l_enabled_modules as ic @@ -984,6 +983,26 @@ feature -- Theme end end +feature -- Theme helpers + + wsf_theme: WSF_THEME + -- WSF Theme from CMS `theme' for Current response. + local + t: like internal_wsf_theme + do + t := internal_wsf_theme + if t = Void then + create {CMS_TO_WSF_THEME} t.make (Current, theme) + internal_wsf_theme := t + end + Result := t + end + +feature {NONE} -- Theme helpers + + internal_wsf_theme: detachable WSF_THEME + -- Once per object for `wsf_theme'. + feature -- Element Change set_status_code (a_status: INTEGER) From 23d266497b245f0d0659539a64ad6745405e1146 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 10 Dec 2015 11:26:28 +0100 Subject: [PATCH 02/14] Made the SQL storage more flexible with INTEGER_32, by allowing to retrieve INTEGER_64 and convert to INTEGER_32 if value can be converted to integer 32. --- .../implementation/store/cms_storage_store_sql.e | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/library/persistence/implementation/store/cms_storage_store_sql.e b/library/persistence/implementation/store/cms_storage_store_sql.e index 0684964..aec4408 100644 --- a/library/persistence/implementation/store/cms_storage_store_sql.e +++ b/library/persistence/implementation/store/cms_storage_store_sql.e @@ -152,6 +152,7 @@ feature -- Query -- Retrieved value at `a_index' position in `item'. local l_item: like sql_item + i64: INTEGER_64 do l_item := sql_item (a_index) if attached {INTEGER_32} l_item as i then @@ -159,7 +160,18 @@ feature -- Query elseif attached {INTEGER_32_REF} l_item as l_value then Result := l_value.item else - check is_integer_32: False end + if attached {INTEGER_64} l_item as i then + i64 := i + elseif attached {INTEGER_64_REF} l_item as l_value then + i64 := l_value.item + else + check is_integer_32: False end + end + if i64 <= {INTEGER_32}.max_value then + Result := i64.to_integer_32 + else + check is_integer_32: False end + end end end From c25590c9cdf242c171318bdb68afc03c155656c5 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Sun, 13 Dec 2015 18:19:25 -0300 Subject: [PATCH 03/14] Added Module Session Authentication with Cookies. Updated Demo example with the Module Session (Authentication with Cookies) Fixed little issue with SQL query in OpenID module. --- examples/demo/demo-safe.ecf | 1 + .../session_auth/files/js/roc_basic_auth.js | 325 ++++++++++++++++ .../scripts/session_auth_table.sql | 11 + .../session_auth/templates/block_login.tpl | 37 ++ examples/demo/src/demo_cms_execution.e | 3 + modules/oauth20/cms_oauth_20_module.e | 2 +- modules/openid/cms_openid_module.e | 2 +- .../persitence/cms_openid_storage_sql.e | 2 +- modules/session_auth/cms_session_api.e | 63 ++++ .../session_auth/cms_session_auth-safe.ecf | 28 ++ .../session_auth/cms_session_auth_module.e | 351 ++++++++++++++++++ modules/session_auth/cms_session_constant.e | 13 + .../filter/cms_session_auth_filter.e | 55 +++ .../handler/cms_session_auth_logoff_handler.e | 132 +++++++ .../persistence/cms_session_auth_storage_i.e | 46 +++ .../cms_session_auth_storage_null.e | 47 +++ .../cms_session_auth_storage_sql.e | 169 +++++++++ .../session_auth/site/cms_token_generator.e | 153 ++++++++ .../site/files/js/roc_basic_auth.js | 325 ++++++++++++++++ .../site/scripts/session_auth_table.sql.tpl | 11 + .../site/templates/block_login.tpl | 37 ++ 21 files changed, 1810 insertions(+), 3 deletions(-) create mode 100644 examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js create mode 100644 examples/demo/site/modules/session_auth/scripts/session_auth_table.sql create mode 100644 examples/demo/site/modules/session_auth/templates/block_login.tpl create mode 100644 modules/session_auth/cms_session_api.e create mode 100644 modules/session_auth/cms_session_auth-safe.ecf create mode 100644 modules/session_auth/cms_session_auth_module.e create mode 100644 modules/session_auth/cms_session_constant.e create mode 100644 modules/session_auth/filter/cms_session_auth_filter.e create mode 100644 modules/session_auth/handler/cms_session_auth_logoff_handler.e create mode 100644 modules/session_auth/persistence/cms_session_auth_storage_i.e create mode 100644 modules/session_auth/persistence/cms_session_auth_storage_null.e create mode 100644 modules/session_auth/persistence/cms_session_auth_storage_sql.e create mode 100644 modules/session_auth/site/cms_token_generator.e create mode 100644 modules/session_auth/site/files/js/roc_basic_auth.js create mode 100644 modules/session_auth/site/scripts/session_auth_table.sql.tpl create mode 100644 modules/session_auth/site/templates/block_login.tpl diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index b8c67db..b00a415 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -31,6 +31,7 @@ + diff --git a/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js b/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js new file mode 100644 index 0000000..467bcd4 --- /dev/null +++ b/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js @@ -0,0 +1,325 @@ +var ROC_AUTH = ROC_AUTH || { }; + +var loginURL = "/basic_auth_login"; +var logoutURL = "/basic_auth_logoff"; + +var userAgent = navigator.userAgent.toLowerCase(); +var firstLogIn = true; + +ROC_AUTH.login = function() { + var form = document.forms['cms_basic_auth']; + var username = form.username.value; + var password = form.password.value; + //var host = form.host.value; + var origin = window.location.origin + window.location.pathname; + var _login = function(){ + + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", loginURL, true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + delete form; + window.location=window.location.origin; + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.login_with_redirect = function() { + var form = document.forms[2]; + var username = form.username.value; + var password = form.password.value; + var host = form.host.value; + var _login = function(){ + + var redirectURL = form.redirect && form.redirect.value || ""; + + + $("#imgProgressRedirect").show(); + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(loginURL), true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + if (redirectURL === "") { + window.location=host.concat("/"); + } else { + window.location=host.concat(redirectURL); + } + + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.getQueryParameterByName = function (name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +ROC_AUTH.logoff = function(callback){ + var form = document.forms[0]; + var host = form.host.value; + + if (userAgent.indexOf("msie") != -1) { + document.execCommand("ClearAuthenticationCache"); + } + else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + + var request1 = new XMLHttpRequest(); + var request2 = new XMLHttpRequest(); + + //Logout. Tell the server not to return the "WWW-Authenticate" header + request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); + request1.send(""); + request1.onreadystatechange = function(){ + if (request1.readyState == 4) { + + //Sign in with dummy credentials to clear the auth cache + request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request2.send(""); + + request2.onreadystatechange = function(){ + if (request2.readyState == 4) { + if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} + } + } + + } + } + } + else { + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request.send(""); + request.onreadystatechange = function(){ + if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); + } + } + } +}; + + +ROC_AUTH.remove = function (id) +{ + var element = document.getElementById(id); + element.outerHTML = ""; + delete element; + return; +}; + + + +$(document).ready(function() { + + if (typeof String.prototype.contains != 'function') { + String.prototype.contains = function (str){ + return this.indexOf(str) != -1; + }; + } + ROC_AUTH.progressive_loging(); + +}); + + +ROC_AUTH.progressive_loging = function () { + + ROC_AUTH.login_href(); +}; + + +$(document).keypress(function(e) { + if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { + ROC_AUTH.login(); + } +}); + +ROC_AUTH.OnOneClick = function(event) { + event.preventDefault(); + if ( document.forms[0] === undefined ) { + ROC_AUTH.create_form(); + } + return false; +}; + +ROC_AUTH.login_href = function() { + var els = document.getElementsByTagName("a"); + for (var i = 0, l = els.length; i < l; i++) { + var el = els[i]; + if (el.href.contains("/basic_auth_login?destination")) { + loginURL = el.href; + var OneClick = el; + OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); + } + } +}; + + +ROC_AUTH.create_form = function() { + + // Fetching HTML Elements in Variables by ID. + var createform = document.createElement('form'); // Create New Element Form + createform.setAttribute("action", ""); // Setting Action Attribute on Form + createform.setAttribute("method", "post"); // Setting Method Attribute on Form + $("body").append(createform); + + var heading = document.createElement('h2'); // Heading of Form + heading.innerHTML = "Login Form "; + createform.appendChild(heading); + + var line = document.createElement('hr'); // Giving Horizontal Row After Heading + createform.appendChild(line); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var namelabel = document.createElement('label'); // Create Label for Name Field + namelabel.innerHTML = "Username : "; // Set Field Labels + createform.appendChild(namelabel); + + var inputelement = document.createElement('input'); // Create Input Field for UserName + inputelement.setAttribute("type", "text"); + inputelement.setAttribute("name", "username"); + inputelement.setAttribute("required","required"); + createform.appendChild(inputelement); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var passwordlabel = document.createElement('label'); // Create Label for Password Field + passwordlabel.innerHTML = "Password : "; + createform.appendChild(passwordlabel); + + var passwordelement = document.createElement('input'); // Create Input Field for Password. + passwordelement.setAttribute("type", "password"); + passwordelement.setAttribute("name", "password"); + passwordelement.setAttribute("id", "password"); + passwordelement.setAttribute("required","required"); + createform.appendChild(passwordelement); + + + var passwordbreak = document.createElement('br'); + createform.appendChild(passwordbreak); + + + var submitelement = document.createElement('button'); // Append Submit Button + submitelement.setAttribute("type", "button"); + submitelement.setAttribute("onclick", "ROC_AUTH.login();"); + submitelement.innerHTML = "Sign In "; + createform.appendChild(submitelement); + +}; + + +var password = document.getElementById("password"); +var confirm_password = document.getElementById("confirm_password"); + +ROC_AUTH.validatePassword =function(){ + if ((password != null) && (confirm_password != null)) { + if(password.value != confirm_password.value) { + confirm_password.setCustomValidity("Passwords Don't Match"); + } else { + confirm_password.setCustomValidity(''); + } + } +} + +if ((password != null) && (confirm_password != null)) { + password.onchange = ROC_AUTH.validatePassword(); + confirm_password.onkeyup = ROC_AUTH.validatePassword; +} diff --git a/examples/demo/site/modules/session_auth/scripts/session_auth_table.sql b/examples/demo/site/modules/session_auth/scripts/session_auth_table.sql new file mode 100644 index 0000000..ada32db --- /dev/null +++ b/examples/demo/site/modules/session_auth/scripts/session_auth_table.sql @@ -0,0 +1,11 @@ + +CREATE TABLE session_auth ( + `uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0), + `access_token` TEXT NOT NULL, + `created` DATETIME NOT NULL, + CONSTRAINT `uid` + UNIQUE(`uid`), + CONSTRAINT `access_token` + UNIQUE(`access_token`) + ); + diff --git a/examples/demo/site/modules/session_auth/templates/block_login.tpl b/examples/demo/site/modules/session_auth/templates/block_login.tpl new file mode 100644 index 0000000..a344568 --- /dev/null +++ b/examples/demo/site/modules/session_auth/templates/block_login.tpl @@ -0,0 +1,37 @@ +
+ {unless isset="$user"} +

Login or Register

+
+
+
+
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ {/unless} + {if isset=$error} +
+
+

+ {$error/} +

+
+
+ {/if} +
diff --git a/examples/demo/src/demo_cms_execution.e b/examples/demo/src/demo_cms_execution.e index a3286d9..c73948f 100644 --- a/examples/demo/src/demo_cms_execution.e +++ b/examples/demo/src/demo_cms_execution.e @@ -89,6 +89,9 @@ feature -- CMS modules create {GOOGLE_CUSTOM_SEARCH_MODULE} m.make a_setup.register_module (m) + + create {CMS_SESSION_AUTH_MODULE} m.make + a_setup.register_module (m) end end diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index 1e96534..281d7a9 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -227,7 +227,7 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") then + if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then lnk2 := ic.item end end diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index e52184e..153305f 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -203,7 +203,7 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") then + if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then lnk2 := ic.item end end diff --git a/modules/openid/persitence/cms_openid_storage_sql.e b/modules/openid/persitence/cms_openid_storage_sql.e index f44c975..bb2f0d7 100644 --- a/modules/openid/persitence/cms_openid_storage_sql.e +++ b/modules/openid/persitence/cms_openid_storage_sql.e @@ -197,7 +197,7 @@ feature {NONE} -- User OpenID Sql_insert_openid: STRING = "INSERT INTO openid_items (uid, identity, created) VALUES (:uid, :identity, :utc_date);" - Sql_openid_consumers: STRING = "SELECT name FROM openid_consumers"; + Sql_openid_consumers: STRING = "SELECT name FROM openid_consumers;" feature {NONE} -- Consumer diff --git a/modules/session_auth/cms_session_api.e b/modules/session_auth/cms_session_api.e new file mode 100644 index 0000000..fd62dd2 --- /dev/null +++ b/modules/session_auth/cms_session_api.e @@ -0,0 +1,63 @@ +note + description: "API to manage CMS User session authentication" + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_API + + +inherit + CMS_MODULE_API + + REFACTORING_HELPER + +create {CMS_SESSION_AUTH_MODULE} + make_with_storage + +feature {NONE} -- Initialization + + make_with_storage (a_api: CMS_API; a_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I) + -- Create an object with api `a_api' and storage `a_session_auth_storage'. + do + session_auth_storage := a_session_auth_storage + make (a_api) + ensure + session_auth_storage_set: session_auth_storage = a_session_auth_storage + end + +feature {CMS_MODULE} -- Access: User session storage. + + session_auth_storage: CMS_SESSION_AUTH_STORAGE_I + -- storage interface. + +feature -- Access + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + do + Result := session_auth_storage.user_by_session_token (a_token) + end + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + do + Result := session_auth_storage.has_user_token (a_user) + end + +feature -- Change User session + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- New user session for user `a_user' with token `a_token'. + do + session_auth_storage.new_user_session_auth (a_token, a_user) + end + + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- Update user session for user `a_user' with token `a_token'. + do + session_auth_storage.update_user_session_auth (a_token, a_user) + end + +end diff --git a/modules/session_auth/cms_session_auth-safe.ecf b/modules/session_auth/cms_session_auth-safe.ecf new file mode 100644 index 0000000..95f44a5 --- /dev/null +++ b/modules/session_auth/cms_session_auth-safe.ecf @@ -0,0 +1,28 @@ + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + + + + + + diff --git a/modules/session_auth/cms_session_auth_module.e b/modules/session_auth/cms_session_auth_module.e new file mode 100644 index 0000000..52a8db8 --- /dev/null +++ b/modules/session_auth/cms_session_auth_module.e @@ -0,0 +1,351 @@ +note + description: "[ + This module allows the use Session Based Authentication using Cookies to restrict access + by looking up users in the given providers. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_MODULE + +inherit + CMS_MODULE + rename + module_api as user_session_api + redefine + filters, + setup_hooks, + initialize, + install, + user_session_api + end + + + CMS_HOOK_AUTO_REGISTER + + CMS_HOOK_BLOCK + + CMS_HOOK_MENU_SYSTEM_ALTER + + CMS_HOOK_VALUE_TABLE_ALTER + + SHARED_LOGGER + + CMS_REQUEST_UTIL + +create + make + +feature {NONE} -- Initialization + + make + do + version := "1.0" + description := "Service to manage cookie based authentication" + package := "authentication" + add_dependency ({CMS_AUTHENTICATION_MODULE}) + end + +feature -- Access + + name: STRING = "session_auth" + + + +feature {CMS_API} -- Module Initialization + + initialize (a_api: CMS_API) + -- + local + l_session_auth_api: like user_session_api + l_user_auth_storage: CMS_SESSION_AUTH_STORAGE_I + do + Precursor (a_api) + + -- Storage initialization + if attached a_api.storage.as_sql_storage as l_storage_sql then + create {CMS_SESSION_AUTH_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) + else + -- FIXME: in case of NULL storage, should Current be disabled? + create {CMS_SESSION_AUTH_STORAGE_NULL} l_user_auth_storage + end + + -- API initialization + create l_session_auth_api.make_with_storage (a_api, l_user_auth_storage) + user_session_api := l_session_auth_api + ensure then + session_auth_api_set: user_session_api /= Void + end + +feature {CMS_API} -- Module management + + install (api: CMS_API) + local + l_consumers: LIST [STRING] + do + -- Schema + if attached api.storage.as_sql_storage as l_sql_storage then + if not l_sql_storage.sql_table_exists ("session_auth") then + --| Schema + l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("session_auth_table.sql")), Void) + + if l_sql_storage.has_error then + api.logger.put_error ("Could not initialize database for blog module", generating_type) + end + end + l_sql_storage.sql_finalize + Precursor {CMS_MODULE}(api) + end + end + +feature {CMS_API} -- Access: API + + user_session_api: detachable CMS_SESSION_API + -- + +feature -- Access: router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- + do + a_router.handle ("/account/roc-session-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/roc-session-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/login-with-session", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,user_session_api, ?, ?)), a_router.methods_get_post) + end + +feature -- Access: filter + + filters (a_api: CMS_API): detachable LIST [WSF_FILTER] + -- Possibly list of Filter's module. + do + create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) + if attached user_session_api as l_session_api then + Result.extend (create {CMS_SESSION_AUTH_FILTER}.make (a_api, l_session_api)) + end + end + +feature {NONE} -- Implementation: routes + + handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + + handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_cookie: WSF_COOKIE + do + if + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_cookie_token and then + attached {CMS_USER} current_user (req) as l_user + then + -- Logout Session + create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_cookie_token.value) + l_cookie.set_path ("/") + l_cookie.set_max_age (-1) + res.add_cookie (l_cookie) + unset_current_user (req) + + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + r.set_redirection (req.absolute_script_url ("")) + r.execute + else + fixme (generator + ": missing else implementation in handle_logout!") + end + end + + handle_login_with_session (api: CMS_API; a_session_api: detachable CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_token: STRING + l_cookie: WSF_COOKIE + do + if + attached a_session_api as l_session_api and then + attached {WSF_STRING} req.form_parameter ("username") as l_username and then + attached {WSF_STRING} req.form_parameter ("password") as l_password and then + api.user_api.is_valid_credential (l_username.value, l_password.value) and then + attached api.user_api.user_by_name (l_username.value) as l_user + then + l_token := generate_token + if + a_session_api.has_user_token (l_user) + then + l_session_api.update_user_session_auth (l_token, l_user) + else + l_session_api.new_user_session_auth (l_token, l_user) + end + create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_token) + l_cookie.set_max_age (60*60*24*360) + l_cookie.set_path ("/") + res.add_cookie (l_cookie) + set_current_user (req, l_user) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_redirection (req.absolute_script_url ("")) + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + if attached template_block ("login", r) as l_tpl_block then + if attached {WSF_STRING} req.form_parameter ("username") as l_username then + l_tpl_block.set_value (l_username.value, "username") + end + l_tpl_block.set_value ("Wrong: Username or password ", "error") + r.add_block (l_tpl_block, "content") + end + r.execute + end + end + +feature -- Hooks configuration + + setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) + -- Module hooks configuration. + do + auto_subscribe_to_hooks (a_hooks) + a_hooks.subscribe_to_block_hook (Current) + a_hooks.subscribe_to_value_table_alter_hook (Current) + end + +feature -- Hooks + + value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) + -- + do + if + attached a_response.user as u and then + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + then + a_value.force ("account/roc-session-logout", "auth_login_strategy") + end + + end + + menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + -- Hook execution on collection of menu contained by `a_menu_system' + -- for related response `a_response'. + local + lnk: CMS_LOCAL_LINK + lnk2: detachable CMS_LINK + do + if + attached a_response.user as u and then + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + then + across + a_menu_system.primary_menu.items as ic + until + lnk2 /= Void + loop + if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then + lnk2 := ic.item + end + end + if lnk2 /= Void then + a_menu_system.primary_menu.remove (lnk2) + end + create lnk.make ("Logout", "account/roc-session-logout" ) + a_menu_system.primary_menu.extend (lnk) + else + if a_response.location.starts_with ("account/") then + create lnk.make ("Session", "account/roc-session-login") + a_response.add_to_primary_tabs (lnk) + end + end + end + + block_list: ITERABLE [like {CMS_BLOCK}.name] + local + l_string: STRING + do + Result := <<"login">> + debug ("roc") + create l_string.make_empty + across + Result as ic + loop + l_string.append (ic.item) + l_string.append_character (' ') + end + write_debug_log (generator + ".block_list:" + l_string ) + end + end + + get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + do + if + a_block_id.is_case_insensitive_equal_general ("login") and then + a_response.location.starts_with ("account/roc-session-login") + then + get_block_view_login (a_block_id, a_response) + end + end + +feature {NONE} -- Helpers + + template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK + -- Smarty content block for `a_block_id' + local + p: detachable PATH + do + create p.make_from_string ("templates") + p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") + + p := a_response.api.module_theme_resource_location (Current, p) + if p /= Void then + if attached p.entry as e then + create Result.make (a_block_id, Void, p.parent, e) + else + create Result.make (a_block_id, Void, p.parent, p) + end + end + end + +feature {NONE} -- Block views + + get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if attached template_block (a_block_id, a_response) as l_tpl_block then + create vals.make (1) + -- add the variable to the block + value_table_alter (vals, a_response) + across + vals as ic + loop + l_tpl_block.set_value (ic.item, ic.key) + end + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + + + generate_token: STRING + -- Generate token to use in a Session. + local + l_token: STRING + l_security: CMS_TOKEN_GENERATOR + 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/session_auth/cms_session_constant.e b/modules/session_auth/cms_session_constant.e new file mode 100644 index 0000000..8d44501 --- /dev/null +++ b/modules/session_auth/cms_session_constant.e @@ -0,0 +1,13 @@ +note + description: "Summary description for {CMS_SESSION_CONSTANT}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_CONSTANT + + +feature + session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_" + -- Name of Cookie used to keep the session info. +end diff --git a/modules/session_auth/filter/cms_session_auth_filter.e b/modules/session_auth/filter/cms_session_auth_filter.e new file mode 100644 index 0000000..dd96337 --- /dev/null +++ b/modules/session_auth/filter/cms_session_auth_filter.e @@ -0,0 +1,55 @@ +note + description: "[ + Processes a HTTP request's checking Session cookies, putting the result into the execution variable user. + ]" + date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" + revision: "$Revision: 96616 $" + +class + CMS_SESSION_AUTH_FILTER + +inherit + WSF_URI_TEMPLATE_HANDLER + + CMS_HANDLER + rename + make as make_handler + end + + WSF_FILTER + +create + make + +feature {NONE} -- Initialization + + make (a_api: CMS_API; a_session_oauth_api: CMS_SESSION_API) + do + make_handler (a_api) + session_oauth_api := a_session_oauth_api + end + + session_oauth_api: CMS_SESSION_API + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + do + api.logger.put_debug (generator + ".execute ", Void) + -- A valid user + if + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_roc_auth_session_token + then + if attached session_oauth_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then + set_current_user (req, l_user) + else + api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) + end + else + api.logger.put_debug (generator + ".execute without authentication", Void) + end + execute_next (req, res) + end + +end diff --git a/modules/session_auth/handler/cms_session_auth_logoff_handler.e b/modules/session_auth/handler/cms_session_auth_logoff_handler.e new file mode 100644 index 0000000..36a4e19 --- /dev/null +++ b/modules/session_auth/handler/cms_session_auth_logoff_handler.e @@ -0,0 +1,132 @@ +note + description: "Summary description for {CMS_SESSION_AUTH_LOGOFF_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_LOGOFF_HANDLER + +inherit + CMS_HANDLER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + l_url: STRING + i: INTEGER + l_message: STRING + do + api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) + if attached req.query_parameter ("prompt") as l_prompt then + unset_current_user (req) + send_access_denied_message (res) + else + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + unset_current_user (req) + l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection + l_url := req.absolute_script_url ("") + i := l_url.substring_index ("://", 1) + if i > 0 then + -- Note: this is a hack to have the logout effective on various browser + -- (firefox requires this). + l_url.replace_substring ("://_logout_basic_auth_@", i, i + 2) + end + if + attached req.http_user_agent as l_user_agent and then + browser_name (l_user_agent).is_case_insensitive_equal_general ("Firefox") + then + -- Set status to refirect + -- and redirect to the host page. + l_page.set_status_code ({HTTP_STATUS_CODE}.found) + l_page.set_redirection (l_url) + end + create l_message.make_from_string (logout_message) + l_message.replace_substring_all ("$site_login", req.absolute_script_url ("/account/roc-login")) + l_message.replace_substring_all ("$site_home", req.absolute_script_url ("")) + l_page.set_main_content (l_message) + l_page.execute + end + end + + + browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32 + -- Browser name. + -- Must contain Must not contain + -- Firefox Firefox/xyz Seamonkey/xyz + -- Seamonkey Seamonkey/xyz + -- Chrome Chrome/xyz Chromium/xyz + -- Chromium Chromium/xyz + -- Safari Safari/xyz Chrome/xyz + -- Chromium/xyz + -- Opera OPR/xyz [1] + -- Opera/xyz [2] + -- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format + + do + if + a_user_agent.has_substring ("Firefox") and then + not a_user_agent.has_substring ("Seamonkey") + then + Result := "Firefox" + elseif a_user_agent.has_substring ("Seamonkey") then + Result := "Seamonkey" + elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then + Result := "Chrome" + elseif a_user_agent.has_substring ("Chromium") then + Result := "Chromiun" + elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then + Result := "Safari" + elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then + Result := "Opera" + elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then + Result := "Internet Explorer" + else + Result := "Unknown" + end + end + + + feature {NONE}-- Lougout Message + + logout_message: STRING = "[ +
+

You are now signed out

+

You can log in again, or go to the front page.

+
+ ]" + + +end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_i.e b/modules/session_auth/persistence/cms_session_auth_storage_i.e new file mode 100644 index 0000000..f40b5bf --- /dev/null +++ b/modules/session_auth/persistence/cms_session_auth_storage_i.e @@ -0,0 +1,46 @@ +note + description: "[ + API to handle OAUTH storage + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_SESSION_AUTH_STORAGE_I + +inherit + SHARED_LOGGER + +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + deferred + end + +feature -- Access: Users + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + deferred + end + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + deferred + end + + +feature -- Change User session + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- New user session for user `a_user' with token `a_token'. + deferred + end + + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- Update user session for user `a_user' with token `a_token'. + deferred + end +end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_null.e b/modules/session_auth/persistence/cms_session_auth_storage_null.e new file mode 100644 index 0000000..ff2da97 --- /dev/null +++ b/modules/session_auth/persistence/cms_session_auth_storage_null.e @@ -0,0 +1,47 @@ +note + description: "Summary description for {CMS_SESSION_AUTH_STORAGE_NULL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_STORAGE_NULL + +inherit + + CMS_SESSION_AUTH_STORAGE_I + + +feature -- Error handler + + error_handler: ERROR_HANDLER + -- Error handler. + do + create Result.make + end + +feature -- Access + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + do + end + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + do + end + +feature -- Change User session + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- New user session for user `a_user' with token `a_token'. + do + end + + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- Update user session for user `a_user' with token `a_token'. + do + end + +end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_sql.e b/modules/session_auth/persistence/cms_session_auth_storage_sql.e new file mode 100644 index 0000000..a6b5d26 --- /dev/null +++ b/modules/session_auth/persistence/cms_session_auth_storage_sql.e @@ -0,0 +1,169 @@ +note + description: "Summary description for {CMS_SESSION_AUTH_STORAGE_SQL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_STORAGE_SQL + +inherit + CMS_SESSION_AUTH_STORAGE_I + + CMS_PROXY_STORAGE_SQL + + CMS_SESSION_AUTH_STORAGE_I + + CMS_STORAGE_SQL_I + + REFACTORING_HELPER + +create + make + +feature -- Access User + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".user_by_session_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_insert (Select_user_by_token, l_parameters) + if not has_error and not sql_after then + Result := fetch_user + sql_forth + if not sql_after then + check no_more_than_one: False end + Result := Void + end + end + sql_finalize + end + + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".has_user_token") + create l_parameters.make (1) + l_parameters.put (a_user.id, "uid") + sql_insert (Select_user_token, l_parameters) + if not has_error and not sql_after then + if sql_read_integer_64 (1) = 1 then + Result := True + else + Result := False + end + end + sql_finalize + end + + + +feature -- Change User token + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- Add a new user with oauth2 authentication. + -- . + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_session") + create l_parameters.make (3) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + sql_insert (sql_insert_session_auth, l_parameters) + sql_commit_transaction + sql_finalize + end + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".update_user_session_auth") + create l_parameters.make (3) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + + sql_modify (sql_update_session_auth, l_parameters) + sql_commit_transaction + sql_finalize + end + + +feature {NONE} -- Implementation + +fetch_user: detachable CMS_USER + local + l_id: INTEGER_64 + l_name: detachable READABLE_STRING_32 + do + if attached sql_read_integer_64 (1) as i then + l_id := i + end + if attached sql_read_string_32 (2) as s and then not s.is_whitespace then + l_name := s + end + + if l_name /= Void then + create Result.make (l_name) + if l_id > 0 then + Result.set_id (l_id) + end + elseif l_id > 0 then + create Result.make_with_id (l_id) + end + + if Result /= Void then + if attached sql_read_string (3) as l_password then + -- FIXME: should we return the password here ??? + Result.set_hashed_password (l_password) + end + if attached sql_read_string (5) as l_email then + Result.set_email (l_email) + end + if attached sql_read_integer_32 (6) as l_status then + Result.set_status (l_status) + end + else + check expected_valid_user: False end + end + end + + +feature {NONE} -- SQL statements + + Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" + --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. + + Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" + + + Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" + + + Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" + +end diff --git a/modules/session_auth/site/cms_token_generator.e b/modules/session_auth/site/cms_token_generator.e new file mode 100644 index 0000000..ba1adb8 --- /dev/null +++ b/modules/session_auth/site/cms_token_generator.e @@ -0,0 +1,153 @@ +note + description: "Provides security routine helpers" + date: "$Date$" + revision: "$Revision$" + +class + CMS_TOKEN_GENERATOR + +inherit + + REFACTORING_HELPER + +feature -- Access + + token: STRING + -- Cryptographic random base 64 string. + do + Result := salt_with_size (16) + -- Remove trailing equal sign + Result.keep_head (Result.count - 2) + end + + salt: STRING + -- Cryptographic random number of 16 bytes. + do + Result := salt_with_size (16) + end + + password: STRING + -- Cryptographic random password of 10 bytes. + do + Result := salt_with_size (10) + -- Remove trailing equal signs + Result.keep_head (Result.count - 2) + end + + password_hash (a_password, a_salt: STRING): STRING + -- Password hash based on password `a_password' and salt value `a_salt'. + do + Result := sha1_string (a_password + a_salt ) + end + +feature {NONE} -- Implementation + + salt_with_size (a_val: INTEGER): STRING + -- Return a salt with size `a_val'. + local + l_salt: SALT_XOR_SHIFT_64_GENERATOR + l_array: ARRAY [INTEGER_8] + i: INTEGER + do + create l_salt.make (a_val) + create l_array.make_empty + i := 1 + across + l_salt.new_sequence as c + loop + l_array.force (c.item.as_integer_8, i) + i := i + 1 + end + Result := encode_base_64 (l_array) + end + + sha1_string (a_str: STRING): STRING + -- SHA1 diggest of `a_str'. + do + sha1.update_from_string (a_str) + Result := sha1.digest_as_string + sha1.reset + end + + sha1: SHA1 + -- Create a SHA1 object. + once + create Result.make + end + +feature -- Encoding + + + encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 + -- Encodes a byte array into a STRING doing base64 encoding. + local + l_output: SPECIAL [INTEGER_8] + l_remaining: INTEGER + i, ptr: INTEGER + char: CHARACTER + do + to_implement ("Check existing code to do that!!!.") + create l_output.make_filled (0, ((bytes.count + 2) // 3) * 4) + l_remaining := bytes.count + from + i := 0 + ptr := 0 + until + l_remaining <= 3 + loop + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF)) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i + 1] & 0xF) |<< 2) | ((bytes [i + 2] |>> 6) & 0x3)) + ptr := ptr + 1 + l_output [ptr] := encode_value (bytes [i + 2] & 0x3F) + ptr := ptr + 1 + l_remaining := l_remaining - 3 + i := i + 3 + end + -- encode when exactly 1 element (left) to encode + char := '=' + if l_remaining = 1 then + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i]) & 0x3) |<< 4) + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + end + + -- encode when exactly 2 elements (left) to encode + if l_remaining = 2 then + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF)); + ptr := ptr + 1 + l_output [ptr] := encode_value ((bytes [i + 1] & 0xF) |<< 2); + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + end + Result := "" + across + l_output as elem + loop + Result.append_character (elem.item.to_character_8) + end + end + + base64_map: SPECIAL [CHARACTER_8] + -- Table for Base64 encoding. + once + Result := ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").area + end + + encode_value (i: INTEGER_8): INTEGER_8 + -- Encode `i'. + do + Result := base64_map [i & 0x3F].code.as_integer_8 + end + +end diff --git a/modules/session_auth/site/files/js/roc_basic_auth.js b/modules/session_auth/site/files/js/roc_basic_auth.js new file mode 100644 index 0000000..467bcd4 --- /dev/null +++ b/modules/session_auth/site/files/js/roc_basic_auth.js @@ -0,0 +1,325 @@ +var ROC_AUTH = ROC_AUTH || { }; + +var loginURL = "/basic_auth_login"; +var logoutURL = "/basic_auth_logoff"; + +var userAgent = navigator.userAgent.toLowerCase(); +var firstLogIn = true; + +ROC_AUTH.login = function() { + var form = document.forms['cms_basic_auth']; + var username = form.username.value; + var password = form.password.value; + //var host = form.host.value; + var origin = window.location.origin + window.location.pathname; + var _login = function(){ + + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", loginURL, true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + delete form; + window.location=window.location.origin; + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.login_with_redirect = function() { + var form = document.forms[2]; + var username = form.username.value; + var password = form.password.value; + var host = form.host.value; + var _login = function(){ + + var redirectURL = form.redirect && form.redirect.value || ""; + + + $("#imgProgressRedirect").show(); + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(loginURL), true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + if (redirectURL === "") { + window.location=host.concat("/"); + } else { + window.location=host.concat(redirectURL); + } + + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.getQueryParameterByName = function (name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +ROC_AUTH.logoff = function(callback){ + var form = document.forms[0]; + var host = form.host.value; + + if (userAgent.indexOf("msie") != -1) { + document.execCommand("ClearAuthenticationCache"); + } + else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + + var request1 = new XMLHttpRequest(); + var request2 = new XMLHttpRequest(); + + //Logout. Tell the server not to return the "WWW-Authenticate" header + request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); + request1.send(""); + request1.onreadystatechange = function(){ + if (request1.readyState == 4) { + + //Sign in with dummy credentials to clear the auth cache + request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request2.send(""); + + request2.onreadystatechange = function(){ + if (request2.readyState == 4) { + if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} + } + } + + } + } + } + else { + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request.send(""); + request.onreadystatechange = function(){ + if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); + } + } + } +}; + + +ROC_AUTH.remove = function (id) +{ + var element = document.getElementById(id); + element.outerHTML = ""; + delete element; + return; +}; + + + +$(document).ready(function() { + + if (typeof String.prototype.contains != 'function') { + String.prototype.contains = function (str){ + return this.indexOf(str) != -1; + }; + } + ROC_AUTH.progressive_loging(); + +}); + + +ROC_AUTH.progressive_loging = function () { + + ROC_AUTH.login_href(); +}; + + +$(document).keypress(function(e) { + if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { + ROC_AUTH.login(); + } +}); + +ROC_AUTH.OnOneClick = function(event) { + event.preventDefault(); + if ( document.forms[0] === undefined ) { + ROC_AUTH.create_form(); + } + return false; +}; + +ROC_AUTH.login_href = function() { + var els = document.getElementsByTagName("a"); + for (var i = 0, l = els.length; i < l; i++) { + var el = els[i]; + if (el.href.contains("/basic_auth_login?destination")) { + loginURL = el.href; + var OneClick = el; + OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); + } + } +}; + + +ROC_AUTH.create_form = function() { + + // Fetching HTML Elements in Variables by ID. + var createform = document.createElement('form'); // Create New Element Form + createform.setAttribute("action", ""); // Setting Action Attribute on Form + createform.setAttribute("method", "post"); // Setting Method Attribute on Form + $("body").append(createform); + + var heading = document.createElement('h2'); // Heading of Form + heading.innerHTML = "Login Form "; + createform.appendChild(heading); + + var line = document.createElement('hr'); // Giving Horizontal Row After Heading + createform.appendChild(line); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var namelabel = document.createElement('label'); // Create Label for Name Field + namelabel.innerHTML = "Username : "; // Set Field Labels + createform.appendChild(namelabel); + + var inputelement = document.createElement('input'); // Create Input Field for UserName + inputelement.setAttribute("type", "text"); + inputelement.setAttribute("name", "username"); + inputelement.setAttribute("required","required"); + createform.appendChild(inputelement); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var passwordlabel = document.createElement('label'); // Create Label for Password Field + passwordlabel.innerHTML = "Password : "; + createform.appendChild(passwordlabel); + + var passwordelement = document.createElement('input'); // Create Input Field for Password. + passwordelement.setAttribute("type", "password"); + passwordelement.setAttribute("name", "password"); + passwordelement.setAttribute("id", "password"); + passwordelement.setAttribute("required","required"); + createform.appendChild(passwordelement); + + + var passwordbreak = document.createElement('br'); + createform.appendChild(passwordbreak); + + + var submitelement = document.createElement('button'); // Append Submit Button + submitelement.setAttribute("type", "button"); + submitelement.setAttribute("onclick", "ROC_AUTH.login();"); + submitelement.innerHTML = "Sign In "; + createform.appendChild(submitelement); + +}; + + +var password = document.getElementById("password"); +var confirm_password = document.getElementById("confirm_password"); + +ROC_AUTH.validatePassword =function(){ + if ((password != null) && (confirm_password != null)) { + if(password.value != confirm_password.value) { + confirm_password.setCustomValidity("Passwords Don't Match"); + } else { + confirm_password.setCustomValidity(''); + } + } +} + +if ((password != null) && (confirm_password != null)) { + password.onchange = ROC_AUTH.validatePassword(); + confirm_password.onkeyup = ROC_AUTH.validatePassword; +} diff --git a/modules/session_auth/site/scripts/session_auth_table.sql.tpl b/modules/session_auth/site/scripts/session_auth_table.sql.tpl new file mode 100644 index 0000000..ada32db --- /dev/null +++ b/modules/session_auth/site/scripts/session_auth_table.sql.tpl @@ -0,0 +1,11 @@ + +CREATE TABLE session_auth ( + `uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0), + `access_token` TEXT NOT NULL, + `created` DATETIME NOT NULL, + CONSTRAINT `uid` + UNIQUE(`uid`), + CONSTRAINT `access_token` + UNIQUE(`access_token`) + ); + diff --git a/modules/session_auth/site/templates/block_login.tpl b/modules/session_auth/site/templates/block_login.tpl new file mode 100644 index 0000000..a344568 --- /dev/null +++ b/modules/session_auth/site/templates/block_login.tpl @@ -0,0 +1,37 @@ +
+ {unless isset="$user"} +

Login or Register

+
+
+
+
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ {/unless} + {if isset=$error} +
+
+

+ {$error/} +

+
+
+ {/if} +
From 089179e60e0564bba309e3bb68445bb5bb37f352 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 15 Dec 2015 15:32:31 -0300 Subject: [PATCH 04/14] Fixed typos Renamed class CMS_SESSION_CONSTANT to CMS_SESSION_CONSTANTS Removed unneeded classes and files. Update SQL implementation. --- modules/oauth20/cms_oauth_20_module.e | 9 +- modules/openid/cms_openid_module.e | 5 +- .../session_auth/cms_session_auth_module.e | 14 +- modules/session_auth/cms_session_constant.e | 13 - modules/session_auth/cms_session_constants.e | 19 + .../filter/cms_session_auth_filter.e | 2 +- .../handler/cms_session_auth_logoff_handler.e | 132 ------- .../cms_session_auth_storage_sql.e | 52 +-- .../site/files/js/roc_basic_auth.js | 325 ------------------ 9 files changed, 56 insertions(+), 515 deletions(-) delete mode 100644 modules/session_auth/cms_session_constant.e create mode 100644 modules/session_auth/cms_session_constants.e delete mode 100644 modules/session_auth/handler/cms_session_auth_logoff_handler.e delete mode 100644 modules/session_auth/site/files/js/roc_basic_auth.js diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index 281d7a9..7cf6bab 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -99,7 +99,7 @@ feature {CMS_API} -- Module management l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers.sql")), Void) if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for blog module", generating_type) + api.logger.put_error ("Could not initialize database for oauth_20 module", generating_type) end -- TODO workaround. l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers_initialize.sql")), Void) @@ -108,7 +108,7 @@ feature {CMS_API} -- Module management -- TODO workaround, until we have an admin module l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void) if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type) + api.logger.put_error ("Could not initialize database for differnent consumers", generating_type) else from l_sql_storage.sql_start @@ -227,7 +227,10 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then + if + ic.item.location.same_string ("account/roc-logout") or else + ic.item.location.same_string ("basic_auth_logoff") + then lnk2 := ic.item end end diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index 153305f..cc84735 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -203,7 +203,10 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then + if + ic.item.location.same_string ("account/roc-logout") or else + ic.item.location.same_string ("basic_auth_logoff") + then lnk2 := ic.item end end diff --git a/modules/session_auth/cms_session_auth_module.e b/modules/session_auth/cms_session_auth_module.e index 52a8db8..52dfaf1 100644 --- a/modules/session_auth/cms_session_auth_module.e +++ b/modules/session_auth/cms_session_auth_module.e @@ -91,7 +91,7 @@ feature {CMS_API} -- Module management l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("session_auth_table.sql")), Void) if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for blog module", generating_type) + api.logger.put_error ("Could not initialize database for session auth module", generating_type) end end l_sql_storage.sql_finalize @@ -141,11 +141,11 @@ feature {NONE} -- Implementation: routes l_cookie: WSF_COOKIE do if - attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_cookie_token and then + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_cookie_token and then attached {CMS_USER} current_user (req) as l_user then -- Logout Session - create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_cookie_token.value) + create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_cookie_token.value) l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) @@ -181,8 +181,8 @@ feature {NONE} -- Implementation: routes else l_session_api.new_user_session_auth (l_token, l_user) end - create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_token) - l_cookie.set_max_age (60*60*24*360) + create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_token) + l_cookie.set_max_age ({CMS_SESSION_CONSTANTS}.session_max_age) l_cookie.set_path ("/") res.add_cookie (l_cookie) set_current_user (req, l_user) @@ -219,7 +219,7 @@ feature -- Hooks do if attached a_response.user as u and then - attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) then a_value.force ("account/roc-session-logout", "auth_login_strategy") end @@ -235,7 +235,7 @@ feature -- Hooks do if attached a_response.user as u and then - attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) then across a_menu_system.primary_menu.items as ic diff --git a/modules/session_auth/cms_session_constant.e b/modules/session_auth/cms_session_constant.e deleted file mode 100644 index 8d44501..0000000 --- a/modules/session_auth/cms_session_constant.e +++ /dev/null @@ -1,13 +0,0 @@ -note - description: "Summary description for {CMS_SESSION_CONSTANT}." - date: "$Date$" - revision: "$Revision$" - -class - CMS_SESSION_CONSTANT - - -feature - session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_" - -- Name of Cookie used to keep the session info. -end diff --git a/modules/session_auth/cms_session_constants.e b/modules/session_auth/cms_session_constants.e new file mode 100644 index 0000000..a50a980 --- /dev/null +++ b/modules/session_auth/cms_session_constants.e @@ -0,0 +1,19 @@ +note + description: "Summary description for {CMS_SESSION_CONSTANTS}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_CONSTANTS + + +feature + session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_" + -- Name of Cookie used to keep the session info. + -- TODO add a config file to be able to customize this value via coniguration file. + + session_max_age: INTEGER = 86400 + -- Value of the Max-Age, before the cookie expires. + -- TODO add a config file to be able to customize this value via coniguration file. + +end diff --git a/modules/session_auth/filter/cms_session_auth_filter.e b/modules/session_auth/filter/cms_session_auth_filter.e index dd96337..1acaa53 100644 --- a/modules/session_auth/filter/cms_session_auth_filter.e +++ b/modules/session_auth/filter/cms_session_auth_filter.e @@ -39,7 +39,7 @@ feature -- Basic operations api.logger.put_debug (generator + ".execute ", Void) -- A valid user if - attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_roc_auth_session_token + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_roc_auth_session_token then if attached session_oauth_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) diff --git a/modules/session_auth/handler/cms_session_auth_logoff_handler.e b/modules/session_auth/handler/cms_session_auth_logoff_handler.e deleted file mode 100644 index 36a4e19..0000000 --- a/modules/session_auth/handler/cms_session_auth_logoff_handler.e +++ /dev/null @@ -1,132 +0,0 @@ -note - description: "Summary description for {CMS_SESSION_AUTH_LOGOFF_HANDLER}." - date: "$Date$" - revision: "$Revision$" - -class - CMS_SESSION_AUTH_LOGOFF_HANDLER - -inherit - CMS_HANDLER - - WSF_URI_HANDLER - rename - execute as uri_execute, - new_mapping as new_uri_mapping - end - - WSF_RESOURCE_HANDLER_HELPER - redefine - do_get - end - - REFACTORING_HELPER - -create - make - -feature -- execute - - execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute request handler. - do - execute_methods (req, res) - end - - uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute request handler. - do - execute_methods (req, res) - end - -feature -- HTTP Methods - - do_get (req: WSF_REQUEST; res: WSF_RESPONSE) - -- - local - l_page: CMS_RESPONSE - l_url: STRING - i: INTEGER - l_message: STRING - do - api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) - if attached req.query_parameter ("prompt") as l_prompt then - unset_current_user (req) - send_access_denied_message (res) - else - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) - unset_current_user (req) - l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection - l_url := req.absolute_script_url ("") - i := l_url.substring_index ("://", 1) - if i > 0 then - -- Note: this is a hack to have the logout effective on various browser - -- (firefox requires this). - l_url.replace_substring ("://_logout_basic_auth_@", i, i + 2) - end - if - attached req.http_user_agent as l_user_agent and then - browser_name (l_user_agent).is_case_insensitive_equal_general ("Firefox") - then - -- Set status to refirect - -- and redirect to the host page. - l_page.set_status_code ({HTTP_STATUS_CODE}.found) - l_page.set_redirection (l_url) - end - create l_message.make_from_string (logout_message) - l_message.replace_substring_all ("$site_login", req.absolute_script_url ("/account/roc-login")) - l_message.replace_substring_all ("$site_home", req.absolute_script_url ("")) - l_page.set_main_content (l_message) - l_page.execute - end - end - - - browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32 - -- Browser name. - -- Must contain Must not contain - -- Firefox Firefox/xyz Seamonkey/xyz - -- Seamonkey Seamonkey/xyz - -- Chrome Chrome/xyz Chromium/xyz - -- Chromium Chromium/xyz - -- Safari Safari/xyz Chrome/xyz - -- Chromium/xyz - -- Opera OPR/xyz [1] - -- Opera/xyz [2] - -- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format - - do - if - a_user_agent.has_substring ("Firefox") and then - not a_user_agent.has_substring ("Seamonkey") - then - Result := "Firefox" - elseif a_user_agent.has_substring ("Seamonkey") then - Result := "Seamonkey" - elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then - Result := "Chrome" - elseif a_user_agent.has_substring ("Chromium") then - Result := "Chromiun" - elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then - Result := "Safari" - elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then - Result := "Opera" - elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then - Result := "Internet Explorer" - else - Result := "Unknown" - end - end - - - feature {NONE}-- Lougout Message - - logout_message: STRING = "[ -
-

You are now signed out

-

You can log in again, or go to the front page.

-
- ]" - - -end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_sql.e b/modules/session_auth/persistence/cms_session_auth_storage_sql.e index a6b5d26..d472def 100644 --- a/modules/session_auth/persistence/cms_session_auth_storage_sql.e +++ b/modules/session_auth/persistence/cms_session_auth_storage_sql.e @@ -7,6 +7,7 @@ class CMS_SESSION_AUTH_STORAGE_SQL inherit + CMS_SESSION_AUTH_STORAGE_I CMS_PROXY_STORAGE_SQL @@ -26,36 +27,35 @@ feature -- Access User -- Retrieve user by token `a_token', if any. local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset write_information_log (generator + ".user_by_session_token") create l_parameters.make (1) l_parameters.put (a_token, "token") - sql_insert (Select_user_by_token, l_parameters) + sql_query (Select_user_by_token, l_parameters) if not has_error and not sql_after then Result := fetch_user sql_forth if not sql_after then - check no_more_than_one: False end + check + no_more_than_one: False + end Result := Void end end sql_finalize end - has_user_token (a_user: CMS_USER): BOOLEAN -- Has the user `a_user' and associated session token? local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset write_information_log (generator + ".has_user_token") create l_parameters.make (1) l_parameters.put (a_user.id, "uid") - sql_insert (Select_user_token, l_parameters) + sql_query (Select_user_token, l_parameters) if not has_error and not sql_after then if sql_read_integer_64 (1) = 1 then Result := True @@ -66,56 +66,45 @@ feature -- Access User sql_finalize end - - feature -- Change User token new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) - -- Add a new user with oauth2 authentication. - -- . + -- . local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset - sql_begin_transaction - write_information_log (generator + ".new_user_session") create l_parameters.make (3) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - + sql_begin_transaction sql_insert (sql_insert_session_auth, l_parameters) sql_commit_transaction sql_finalize end - update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER) -- local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset - sql_begin_transaction - write_information_log (generator + ".update_user_session_auth") create l_parameters.make (3) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - - + sql_begin_transaction sql_modify (sql_update_session_auth, l_parameters) sql_commit_transaction sql_finalize end - feature {NONE} -- Implementation -fetch_user: detachable CMS_USER + fetch_user: detachable CMS_USER local l_id: INTEGER_64 l_name: detachable READABLE_STRING_32 @@ -126,7 +115,6 @@ fetch_user: detachable CMS_USER if attached sql_read_string_32 (2) as s and then not s.is_whitespace then l_name := s end - if l_name /= Void then create Result.make (l_name) if l_id > 0 then @@ -135,7 +123,6 @@ fetch_user: detachable CMS_USER elseif l_id > 0 then create Result.make_with_id (l_id) end - if Result /= Void then if attached sql_read_string (3) as l_password then -- FIXME: should we return the password here ??? @@ -148,22 +135,21 @@ fetch_user: detachable CMS_USER Result.set_status (l_status) end else - check expected_valid_user: False end + check + expected_valid_user: False + end end end - feature {NONE} -- SQL statements - Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" - --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. + Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" + --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. - Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" + Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" + Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" - Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" - - - Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" + Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" end diff --git a/modules/session_auth/site/files/js/roc_basic_auth.js b/modules/session_auth/site/files/js/roc_basic_auth.js deleted file mode 100644 index 467bcd4..0000000 --- a/modules/session_auth/site/files/js/roc_basic_auth.js +++ /dev/null @@ -1,325 +0,0 @@ -var ROC_AUTH = ROC_AUTH || { }; - -var loginURL = "/basic_auth_login"; -var logoutURL = "/basic_auth_logoff"; - -var userAgent = navigator.userAgent.toLowerCase(); -var firstLogIn = true; - -ROC_AUTH.login = function() { - var form = document.forms['cms_basic_auth']; - var username = form.username.value; - var password = form.password.value; - //var host = form.host.value; - var origin = window.location.origin + window.location.pathname; - var _login = function(){ - - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", loginURL, true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - delete form; - window.location=window.location.origin; - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.login_with_redirect = function() { - var form = document.forms[2]; - var username = form.username.value; - var password = form.password.value; - var host = form.host.value; - var _login = function(){ - - var redirectURL = form.redirect && form.redirect.value || ""; - - - $("#imgProgressRedirect").show(); - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(loginURL), true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - if (redirectURL === "") { - window.location=host.concat("/"); - } else { - window.location=host.concat(redirectURL); - } - - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.getQueryParameterByName = function (name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -ROC_AUTH.logoff = function(callback){ - var form = document.forms[0]; - var host = form.host.value; - - if (userAgent.indexOf("msie") != -1) { - document.execCommand("ClearAuthenticationCache"); - } - else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - - var request1 = new XMLHttpRequest(); - var request2 = new XMLHttpRequest(); - - //Logout. Tell the server not to return the "WWW-Authenticate" header - request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); - request1.send(""); - request1.onreadystatechange = function(){ - if (request1.readyState == 4) { - - //Sign in with dummy credentials to clear the auth cache - request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request2.send(""); - - request2.onreadystatechange = function(){ - if (request2.readyState == 4) { - if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} - } - } - - } - } - } - else { - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request.send(""); - request.onreadystatechange = function(){ - if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); - } - } - } -}; - - -ROC_AUTH.remove = function (id) -{ - var element = document.getElementById(id); - element.outerHTML = ""; - delete element; - return; -}; - - - -$(document).ready(function() { - - if (typeof String.prototype.contains != 'function') { - String.prototype.contains = function (str){ - return this.indexOf(str) != -1; - }; - } - ROC_AUTH.progressive_loging(); - -}); - - -ROC_AUTH.progressive_loging = function () { - - ROC_AUTH.login_href(); -}; - - -$(document).keypress(function(e) { - if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { - ROC_AUTH.login(); - } -}); - -ROC_AUTH.OnOneClick = function(event) { - event.preventDefault(); - if ( document.forms[0] === undefined ) { - ROC_AUTH.create_form(); - } - return false; -}; - -ROC_AUTH.login_href = function() { - var els = document.getElementsByTagName("a"); - for (var i = 0, l = els.length; i < l; i++) { - var el = els[i]; - if (el.href.contains("/basic_auth_login?destination")) { - loginURL = el.href; - var OneClick = el; - OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); - } - } -}; - - -ROC_AUTH.create_form = function() { - - // Fetching HTML Elements in Variables by ID. - var createform = document.createElement('form'); // Create New Element Form - createform.setAttribute("action", ""); // Setting Action Attribute on Form - createform.setAttribute("method", "post"); // Setting Method Attribute on Form - $("body").append(createform); - - var heading = document.createElement('h2'); // Heading of Form - heading.innerHTML = "Login Form "; - createform.appendChild(heading); - - var line = document.createElement('hr'); // Giving Horizontal Row After Heading - createform.appendChild(line); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var namelabel = document.createElement('label'); // Create Label for Name Field - namelabel.innerHTML = "Username : "; // Set Field Labels - createform.appendChild(namelabel); - - var inputelement = document.createElement('input'); // Create Input Field for UserName - inputelement.setAttribute("type", "text"); - inputelement.setAttribute("name", "username"); - inputelement.setAttribute("required","required"); - createform.appendChild(inputelement); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var passwordlabel = document.createElement('label'); // Create Label for Password Field - passwordlabel.innerHTML = "Password : "; - createform.appendChild(passwordlabel); - - var passwordelement = document.createElement('input'); // Create Input Field for Password. - passwordelement.setAttribute("type", "password"); - passwordelement.setAttribute("name", "password"); - passwordelement.setAttribute("id", "password"); - passwordelement.setAttribute("required","required"); - createform.appendChild(passwordelement); - - - var passwordbreak = document.createElement('br'); - createform.appendChild(passwordbreak); - - - var submitelement = document.createElement('button'); // Append Submit Button - submitelement.setAttribute("type", "button"); - submitelement.setAttribute("onclick", "ROC_AUTH.login();"); - submitelement.innerHTML = "Sign In "; - createform.appendChild(submitelement); - -}; - - -var password = document.getElementById("password"); -var confirm_password = document.getElementById("confirm_password"); - -ROC_AUTH.validatePassword =function(){ - if ((password != null) && (confirm_password != null)) { - if(password.value != confirm_password.value) { - confirm_password.setCustomValidity("Passwords Don't Match"); - } else { - confirm_password.setCustomValidity(''); - } - } -} - -if ((password != null) && (confirm_password != null)) { - password.onchange = ROC_AUTH.validatePassword(); - confirm_password.onkeyup = ROC_AUTH.validatePassword; -} From 068943734f1101556af007e7c21754ac4088e5c4 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 16 Dec 2015 10:03:35 -0300 Subject: [PATCH 05/14] Updated class CMS_TOKEN_GENERATOR. Remove once in sha1 feature. Updated encoded_base_64 to base_64 --- modules/session_auth/site/cms_token_generator.e | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/session_auth/site/cms_token_generator.e b/modules/session_auth/site/cms_token_generator.e index ba1adb8..d18bd90 100644 --- a/modules/session_auth/site/cms_token_generator.e +++ b/modules/session_auth/site/cms_token_generator.e @@ -58,7 +58,7 @@ feature {NONE} -- Implementation l_array.force (c.item.as_integer_8, i) i := i + 1 end - Result := encode_base_64 (l_array) + Result := base_64 (l_array) end sha1_string (a_str: STRING): STRING @@ -71,14 +71,14 @@ feature {NONE} -- Implementation sha1: SHA1 -- Create a SHA1 object. - once + do create Result.make end feature -- Encoding - encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 + base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 -- Encodes a byte array into a STRING doing base64 encoding. local l_output: SPECIAL [INTEGER_8] @@ -130,7 +130,7 @@ feature -- Encoding l_output [ptr] := char.code.as_integer_8 ptr := ptr + 1 end - Result := "" + create Result.make_empty across l_output as elem loop From 3b88c746a187fee8476de03d84dc73c7f666ca44 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 16 Dec 2015 10:43:21 -0300 Subject: [PATCH 06/14] Removed unneeded file. --- .../session_auth/files/js/roc_basic_auth.js | 325 ------------------ 1 file changed, 325 deletions(-) delete mode 100644 examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js diff --git a/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js b/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js deleted file mode 100644 index 467bcd4..0000000 --- a/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js +++ /dev/null @@ -1,325 +0,0 @@ -var ROC_AUTH = ROC_AUTH || { }; - -var loginURL = "/basic_auth_login"; -var logoutURL = "/basic_auth_logoff"; - -var userAgent = navigator.userAgent.toLowerCase(); -var firstLogIn = true; - -ROC_AUTH.login = function() { - var form = document.forms['cms_basic_auth']; - var username = form.username.value; - var password = form.password.value; - //var host = form.host.value; - var origin = window.location.origin + window.location.pathname; - var _login = function(){ - - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", loginURL, true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - delete form; - window.location=window.location.origin; - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.login_with_redirect = function() { - var form = document.forms[2]; - var username = form.username.value; - var password = form.password.value; - var host = form.host.value; - var _login = function(){ - - var redirectURL = form.redirect && form.redirect.value || ""; - - - $("#imgProgressRedirect").show(); - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(loginURL), true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - if (redirectURL === "") { - window.location=host.concat("/"); - } else { - window.location=host.concat(redirectURL); - } - - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.getQueryParameterByName = function (name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -ROC_AUTH.logoff = function(callback){ - var form = document.forms[0]; - var host = form.host.value; - - if (userAgent.indexOf("msie") != -1) { - document.execCommand("ClearAuthenticationCache"); - } - else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - - var request1 = new XMLHttpRequest(); - var request2 = new XMLHttpRequest(); - - //Logout. Tell the server not to return the "WWW-Authenticate" header - request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); - request1.send(""); - request1.onreadystatechange = function(){ - if (request1.readyState == 4) { - - //Sign in with dummy credentials to clear the auth cache - request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request2.send(""); - - request2.onreadystatechange = function(){ - if (request2.readyState == 4) { - if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} - } - } - - } - } - } - else { - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request.send(""); - request.onreadystatechange = function(){ - if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); - } - } - } -}; - - -ROC_AUTH.remove = function (id) -{ - var element = document.getElementById(id); - element.outerHTML = ""; - delete element; - return; -}; - - - -$(document).ready(function() { - - if (typeof String.prototype.contains != 'function') { - String.prototype.contains = function (str){ - return this.indexOf(str) != -1; - }; - } - ROC_AUTH.progressive_loging(); - -}); - - -ROC_AUTH.progressive_loging = function () { - - ROC_AUTH.login_href(); -}; - - -$(document).keypress(function(e) { - if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { - ROC_AUTH.login(); - } -}); - -ROC_AUTH.OnOneClick = function(event) { - event.preventDefault(); - if ( document.forms[0] === undefined ) { - ROC_AUTH.create_form(); - } - return false; -}; - -ROC_AUTH.login_href = function() { - var els = document.getElementsByTagName("a"); - for (var i = 0, l = els.length; i < l; i++) { - var el = els[i]; - if (el.href.contains("/basic_auth_login?destination")) { - loginURL = el.href; - var OneClick = el; - OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); - } - } -}; - - -ROC_AUTH.create_form = function() { - - // Fetching HTML Elements in Variables by ID. - var createform = document.createElement('form'); // Create New Element Form - createform.setAttribute("action", ""); // Setting Action Attribute on Form - createform.setAttribute("method", "post"); // Setting Method Attribute on Form - $("body").append(createform); - - var heading = document.createElement('h2'); // Heading of Form - heading.innerHTML = "Login Form "; - createform.appendChild(heading); - - var line = document.createElement('hr'); // Giving Horizontal Row After Heading - createform.appendChild(line); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var namelabel = document.createElement('label'); // Create Label for Name Field - namelabel.innerHTML = "Username : "; // Set Field Labels - createform.appendChild(namelabel); - - var inputelement = document.createElement('input'); // Create Input Field for UserName - inputelement.setAttribute("type", "text"); - inputelement.setAttribute("name", "username"); - inputelement.setAttribute("required","required"); - createform.appendChild(inputelement); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var passwordlabel = document.createElement('label'); // Create Label for Password Field - passwordlabel.innerHTML = "Password : "; - createform.appendChild(passwordlabel); - - var passwordelement = document.createElement('input'); // Create Input Field for Password. - passwordelement.setAttribute("type", "password"); - passwordelement.setAttribute("name", "password"); - passwordelement.setAttribute("id", "password"); - passwordelement.setAttribute("required","required"); - createform.appendChild(passwordelement); - - - var passwordbreak = document.createElement('br'); - createform.appendChild(passwordbreak); - - - var submitelement = document.createElement('button'); // Append Submit Button - submitelement.setAttribute("type", "button"); - submitelement.setAttribute("onclick", "ROC_AUTH.login();"); - submitelement.innerHTML = "Sign In "; - createform.appendChild(submitelement); - -}; - - -var password = document.getElementById("password"); -var confirm_password = document.getElementById("confirm_password"); - -ROC_AUTH.validatePassword =function(){ - if ((password != null) && (confirm_password != null)) { - if(password.value != confirm_password.value) { - confirm_password.setCustomValidity("Passwords Don't Match"); - } else { - confirm_password.setCustomValidity(''); - } - } -} - -if ((password != null) && (confirm_password != null)) { - password.onchange = ROC_AUTH.validatePassword(); - confirm_password.onkeyup = ROC_AUTH.validatePassword; -} From e50fb6959e6d4bd1c14b2bb13e84ab0632339d68 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 16 Dec 2015 15:59:22 +0100 Subject: [PATCH 07/14] Moved taxonomy integration for web form inside CMS_TAXONOMY_API. Moved a few helpers routine from CMS_RESPONSE to CMS_API. Added CMS_CONTENT.identifier: detachable READABLE_STRING_32 . --- modules/auth/cms_authentication_module.e | 12 +- modules/node/content/cms_node.e | 10 +- .../handler/cms_node_type_webform_manager.e | 241 +--------------- modules/taxonomy/cms_taxonomy_api.e | 273 ++++++++++++++++++ src/service/cms_api.e | 20 ++ src/service/content/cms_content.e | 13 + src/service/response/cms_response.e | 36 +-- 7 files changed, 346 insertions(+), 259 deletions(-) diff --git a/modules/auth/cms_authentication_module.e b/modules/auth/cms_authentication_module.e index e0a7c55..3787b75 100644 --- a/modules/auth/cms_authentication_module.e +++ b/modules/auth/cms_authentication_module.e @@ -9,10 +9,10 @@ class inherit CMS_MODULE redefine - setup_hooks + setup_hooks, + permissions end - CMS_HOOK_AUTO_REGISTER CMS_HOOK_VALUE_TABLE_ALTER @@ -52,6 +52,13 @@ feature -- Access name: STRING = "auth" + permissions: LIST [READABLE_STRING_8] + -- List of permission ids, used by this module, and declared. + do + Result := Precursor + Result.force ("account register") + end + feature -- Access: docs root_dir: PATH @@ -228,6 +235,7 @@ feature -- Handler end else create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("You can also contact the webmaster to ask for an account.") end r.execute diff --git a/modules/node/content/cms_node.e b/modules/node/content/cms_node.e index f7a755c..0368c0e 100644 --- a/modules/node/content/cms_node.e +++ b/modules/node/content/cms_node.e @@ -11,8 +11,10 @@ deferred class inherit CMS_CONTENT + rename + has_identifier as has_id redefine - debug_output + debug_output, has_id end REFACTORING_HELPER @@ -63,6 +65,12 @@ feature -- Conversion feature -- Access + identifier: detachable IMMUTABLE_STRING_32 + -- Optional identifier. + do + create Result.make_from_string_general (id.out) + end + id: INTEGER_64 assign set_id -- Unique id. --| Should we use NATURAL_64 instead? diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e index 6e5ae7b..db5aac4 100644 --- a/modules/node/handler/cms_node_type_webform_manager.e +++ b/modules/node/handler/cms_node_type_webform_manager.e @@ -92,246 +92,15 @@ feature -- Forms ... f.extend (fset) - -- Path alias + -- Path alias populate_form_with_taxonomy (response, f, a_node) populate_form_with_path_alias (response, f, a_node) end - populate_form_with_taxonomy (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) - local - ti: detachable WSF_FORM_TEXT_INPUT - th: WSF_FORM_HIDDEN_INPUT - w_set: WSF_FORM_FIELD_SET - w_select: WSF_FORM_SELECT - w_opt: WSF_FORM_SELECT_OPTION - w_cb: WSF_FORM_CHECKBOX_INPUT - w_voc_set: WSF_FORM_FIELD_SET - s: STRING_32 - voc: CMS_VOCABULARY - t: detachable CMS_TERM - l_terms: detachable CMS_TERM_COLLECTION - l_has_edit_permission: BOOLEAN + populate_form_with_taxonomy (response: CMS_RESPONSE; f: CMS_FORM; a_content: detachable CMS_CONTENT) do - if - attached {CMS_TAXONOMY_API} response.api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then - attached l_taxonomy_api.vocabularies_for_type (content_type.name) as l_vocs and then not l_vocs.is_empty - then - - l_has_edit_permission := response.has_permissions (<<"update any taxonomy", "update " + content_type.name + " taxonomy">>) - - -- Handle Taxonomy fields, if any associated with `content_type'. - create w_set.make - w_set.add_css_class ("taxonomy") - l_vocs.sort - across - l_vocs as vocs_ic - loop - voc := vocs_ic.item - create th.make_with_text ({STRING_32} "taxonomy_vocabularies[" + voc.id.out + "]", voc.name) - w_set.extend (th) - - l_terms := Void - if a_node /= Void and then a_node.has_id then - l_terms := l_taxonomy_api.terms_of_entity (a_node.content_type, a_node.id.out, voc) - if l_terms /= Void then - l_terms.sort - end - end - create w_voc_set.make - w_set.extend (w_voc_set) - - if voc.is_tags then - w_voc_set.set_legend (response.translation (voc.name, Void)) - - create ti.make ({STRING_32} "taxonomy_" + voc.id.out) - w_voc_set.extend (ti) - if voc.is_term_required then - ti.enable_required - end - if attached voc.description as l_desc then - ti.set_description (response.html_encoded (response.translation (l_desc, Void))) - else - ti.set_description (response.html_encoded (response.translation (voc.name, Void))) - end - ti.set_size (70) - if l_terms /= Void then - create s.make_empty - across - l_terms as ic - loop - t := ic.item - if not s.is_empty then - s.append_character (',') - s.append_character (' ') - end - if ic.item.text.has (' ') then - s.append_character ('"') - s.append (t.text) - s.append_character ('"') - else - s.append (t.text) - end - end - ti.set_text_value (s) - end - if not l_has_edit_permission then - ti.set_is_readonly (True) - end - else - l_taxonomy_api.fill_vocabularies_with_terms (voc) - if not voc.terms.is_empty then - if voc.multiple_terms_allowed then - if attached voc.description as l_desc then - w_voc_set.set_legend (response.html_encoded (l_desc)) - else - w_voc_set.set_legend (response.html_encoded (voc.name)) - end - across - voc as voc_terms_ic - loop - t := voc_terms_ic.item - create w_cb.make_with_value ({STRING_32} "taxonomy_" + voc.id.out + "[]", t.text) - w_cb.set_title (t.text) - w_voc_set.extend (w_cb) - if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then - w_cb.set_checked (True) - end - if not l_has_edit_permission then - w_cb.set_is_readonly (True) - end - end - else - create w_select.make ({STRING_32} "taxonomy_" + voc.id.out) - w_voc_set.extend (w_select) - - if attached voc.description as l_desc then - w_select.set_description (response.html_encoded (l_desc)) - else - w_select.set_description (response.html_encoded (voc.name)) - end - w_voc_set.set_legend (response.html_encoded (voc.name)) - - across - voc as voc_terms_ic - loop - t := voc_terms_ic.item - create w_opt.make (response.html_encoded (t.text), response.html_encoded (t.text)) - w_select.add_option (w_opt) - - if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then - w_opt.set_is_selected (True) - end - end - if not l_has_edit_permission then - w_select.set_is_readonly (True) - end - end - end - end - end - - f.submit_actions.extend (agent taxonomy_submit_action (response, l_taxonomy_api, l_vocs, a_node, ?)) - - if - attached f.fields_by_name ("title") as l_title_fields and then - attached l_title_fields.first as l_title_field - then - f.insert_after (w_set, l_title_field) - else - f.extend (w_set) - end - end - end - - taxonomy_submit_action (a_response: CMS_RESPONSE; a_taxonomy_api: CMS_TAXONOMY_API; a_vocs: CMS_VOCABULARY_COLLECTION; a_node: detachable CMS_NODE fd: WSF_FORM_DATA) - require - vocs_not_empty: not a_vocs.is_empty - local - l_voc_name: READABLE_STRING_32 - l_terms_to_remove: ARRAYED_LIST [CMS_TERM] - l_new_terms: LIST [READABLE_STRING_32] - l_text: READABLE_STRING_GENERAL - l_found: BOOLEAN - t: detachable CMS_TERM - vid: INTEGER_64 - do - if - a_node /= Void and then a_node.has_id and then - attached fd.table_item ("taxonomy_vocabularies") as fd_vocs - then - if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + content_type.name + " taxonomy">>) then - across - fd_vocs.values as ic - loop - vid := ic.key.to_integer_64 - l_voc_name := ic.item.string_representation - - if attached a_vocs.item_by_id (vid) as voc then - if attached fd.string_item ("taxonomy_" + vid.out) as l_string then - l_new_terms := a_taxonomy_api.splitted_string (l_string, ',') - elseif attached fd.table_item ("taxonomy_" + vid.out) as fd_terms then - create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (fd_terms.count) - across - fd_terms as t_ic - loop - l_new_terms.force (t_ic.item.string_representation) - end - else - create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (0) - end - - create l_terms_to_remove.make (0) - if attached a_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, voc) as l_existing_terms then - across - l_existing_terms as t_ic - loop - l_text := t_ic.item.text - from - l_found := False - l_new_terms.start - until - l_new_terms.after - loop - if l_new_terms.item.same_string_general (l_text) then - -- Already associated with term `t_ic.text'. - l_found := True - l_new_terms.remove - else - l_new_terms.forth - end - end - if not l_found then - -- Remove term - l_terms_to_remove.force (t_ic.item) - end - end - across - l_terms_to_remove as t_ic - loop - a_taxonomy_api.unassociate_term_from_entity (t_ic.item, content_type.name, a_node.id.out) - end - end - across - l_new_terms as t_ic - loop - t := a_taxonomy_api.term_by_text (t_ic.item, voc) - if - t = Void and voc.is_tags - then - -- Create new term! - create t.make (t_ic.item) - a_taxonomy_api.save_term (t, voc) - if a_taxonomy_api.has_error then - t := Void - end - end - if t /= Void then - a_taxonomy_api.associate_term_with_entity (t, content_type.name, a_node.id.out) - end - end - end - end - end + if attached {CMS_TAXONOMY_API} response.api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api then + l_taxonomy_api.populate_edit_form (response, f, content_type.name, a_content) end end @@ -573,7 +342,7 @@ feature -- Output vocs as ic loop if - attached l_taxonomy_api.terms_of_entity (content_type.name, a_node.id.out, ic.item) as l_terms and then + attached l_taxonomy_api.terms_of_content (a_node, ic.item) as l_terms and then not l_terms.is_empty then a_output.append ("
    ") diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e index 677dbdf..5c9ffe6 100644 --- a/modules/taxonomy/cms_taxonomy_api.e +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -109,6 +109,17 @@ feature -- Access node Result := taxonomy_storage.term_count_from_vocabulary (a_vocab) end + terms_of_content (a_content: CMS_CONTENT; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION + -- Terms related to `a_content', and if `a_vocabulary' is set + -- constrain to be part of `a_vocabulary'. + require + content_with_identifier: a_content.has_identifier + do + if attached a_content.identifier as l_id then + Result := terms_of_entity (a_content.content_type, l_id, a_vocabulary) + end + end + terms_of_entity (a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL; a_vocabulary: detachable CMS_VOCABULARY): detachable CMS_TERM_COLLECTION -- Terms related to `(a_type_name,a_entity)', and if `a_vocabulary' is set -- constrain to be part of `a_vocabulary'. @@ -179,6 +190,30 @@ feature -- Write error_handler.append (taxonomy_storage.error_handler) end + associate_term_with_content (a_term: CMS_TERM; a_content: CMS_CONTENT) + -- Associate term `a_term' with `a_content'. + require + content_with_identifier: a_content.has_identifier + do + reset_error + if attached a_content.identifier as l_id then + taxonomy_storage.associate_term_with_entity (a_term, a_content.content_type, l_id) + error_handler.append (taxonomy_storage.error_handler) + end + end + + unassociate_term_from_content (a_term: CMS_TERM; a_content: CMS_CONTENT) + -- Unassociate term `a_term' from `a_content'. + require + content_with_identifier: a_content.has_identifier + do + reset_error + if attached a_content.identifier as l_id then + taxonomy_storage.unassociate_term_from_entity (a_term, a_content.content_type, l_id) + error_handler.append (taxonomy_storage.error_handler) + end + end + associate_term_with_entity (a_term: CMS_TERM; a_type_name: READABLE_STRING_GENERAL; a_entity: READABLE_STRING_GENERAL) -- Associate term `a_term' with `(a_type_name, a_entity)'. do @@ -215,6 +250,244 @@ feature -- Write error_handler.append (taxonomy_storage.error_handler) end +feature -- Web forms + + populate_edit_form (a_response: CMS_RESPONSE; a_form: CMS_FORM; a_content_type_name: READABLE_STRING_8; a_content: detachable CMS_CONTENT) + local + ti: detachable WSF_FORM_TEXT_INPUT + th: WSF_FORM_HIDDEN_INPUT + w_set: WSF_FORM_FIELD_SET + w_select: WSF_FORM_SELECT + w_opt: WSF_FORM_SELECT_OPTION + w_cb: WSF_FORM_CHECKBOX_INPUT + w_voc_set: WSF_FORM_FIELD_SET + s: STRING_32 + voc: CMS_VOCABULARY + t: detachable CMS_TERM + l_terms: detachable CMS_TERM_COLLECTION + l_has_edit_permission: BOOLEAN + do + if + attached vocabularies_for_type (a_content_type_name) as l_vocs and then not l_vocs.is_empty + then + l_has_edit_permission := a_response.has_permissions (<<"update any taxonomy", "update " + a_content_type_name + " taxonomy">>) + + -- Handle Taxonomy fields, if any associated with `content_type'. + create w_set.make + w_set.add_css_class ("taxonomy") + l_vocs.sort + across + l_vocs as vocs_ic + loop + voc := vocs_ic.item + create th.make_with_text ({STRING_32} "taxonomy_vocabularies[" + voc.id.out + "]", voc.name) + w_set.extend (th) + + l_terms := Void + if a_content /= Void then + l_terms := terms_of_content (a_content, voc) + if l_terms /= Void then + l_terms.sort + end + end + create w_voc_set.make + w_set.extend (w_voc_set) + + if voc.is_tags then + w_voc_set.set_legend (cms_api.translation (voc.name, Void)) + + create ti.make ({STRING_32} "taxonomy_" + voc.id.out) + w_voc_set.extend (ti) + if voc.is_term_required then + ti.enable_required + end + if attached voc.description as l_desc then + ti.set_description (cms_api.html_encoded (cms_api.translation (l_desc, Void))) + else + ti.set_description (a_response.html_encoded (cms_api.translation (voc.name, Void))) + end + ti.set_size (70) + if l_terms /= Void then + create s.make_empty + across + l_terms as ic + loop + t := ic.item + if not s.is_empty then + s.append_character (',') + s.append_character (' ') + end + if ic.item.text.has (' ') then + s.append_character ('"') + s.append (t.text) + s.append_character ('"') + else + s.append (t.text) + end + end + ti.set_text_value (s) + end + if not l_has_edit_permission then + ti.set_is_readonly (True) + end + else + fill_vocabularies_with_terms (voc) + if not voc.terms.is_empty then + if voc.multiple_terms_allowed then + if attached voc.description as l_desc then + w_voc_set.set_legend (cms_api.html_encoded (l_desc)) + else + w_voc_set.set_legend (cms_api.html_encoded (voc.name)) + end + across + voc as voc_terms_ic + loop + t := voc_terms_ic.item + create w_cb.make_with_value ({STRING_32} "taxonomy_" + voc.id.out + "[]", t.text) + w_cb.set_title (t.text) + w_voc_set.extend (w_cb) + if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then + w_cb.set_checked (True) + end + if not l_has_edit_permission then + w_cb.set_is_readonly (True) + end + end + else + create w_select.make ({STRING_32} "taxonomy_" + voc.id.out) + w_voc_set.extend (w_select) + + if attached voc.description as l_desc then + w_select.set_description (cms_api.html_encoded (l_desc)) + else + w_select.set_description (cms_api.html_encoded (voc.name)) + end + w_voc_set.set_legend (cms_api.html_encoded (voc.name)) + + across + voc as voc_terms_ic + loop + t := voc_terms_ic.item + create w_opt.make (cms_api.html_encoded (t.text), cms_api.html_encoded (t.text)) + w_select.add_option (w_opt) + + if l_terms /= Void and then across l_terms as ic some ic.item.text.same_string (t.text) end then + w_opt.set_is_selected (True) + end + end + if not l_has_edit_permission then + w_select.set_is_readonly (True) + end + end + end + end + end + + a_form.submit_actions.extend (agent taxonomy_submit_action (a_response, Current, l_vocs, a_content, ?)) + + if + attached a_form.fields_by_name ("title") as l_title_fields and then + attached l_title_fields.first as l_title_field + then + a_form.insert_after (w_set, l_title_field) + else + a_form.extend (w_set) + end + end + end + + taxonomy_submit_action (a_response: CMS_RESPONSE; a_taxonomy_api: CMS_TAXONOMY_API; a_vocs: CMS_VOCABULARY_COLLECTION; a_content: detachable CMS_CONTENT fd: WSF_FORM_DATA) + require + vocs_not_empty: not a_vocs.is_empty + local + l_voc_name: READABLE_STRING_32 + l_terms_to_remove: ARRAYED_LIST [CMS_TERM] + l_new_terms: LIST [READABLE_STRING_32] + l_text: READABLE_STRING_GENERAL + l_found: BOOLEAN + t: detachable CMS_TERM + vid: INTEGER_64 + do + if + a_content /= Void and then a_content.has_identifier and then + attached fd.table_item ("taxonomy_vocabularies") as fd_vocs + then + if a_response.has_permissions (<<{STRING_32} "update any taxonomy", {STRING_32} "update " + a_content.content_type + " taxonomy">>) then + across + fd_vocs.values as ic + loop + vid := ic.key.to_integer_64 + l_voc_name := ic.item.string_representation + + if attached a_vocs.item_by_id (vid) as voc then + if attached fd.string_item ("taxonomy_" + vid.out) as l_string then + l_new_terms := a_taxonomy_api.splitted_string (l_string, ',') + elseif attached fd.table_item ("taxonomy_" + vid.out) as fd_terms then + create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (fd_terms.count) + across + fd_terms as t_ic + loop + l_new_terms.force (t_ic.item.string_representation) + end + else + create {ARRAYED_LIST [READABLE_STRING_32]} l_new_terms.make (0) + end + + create l_terms_to_remove.make (0) + if attached a_taxonomy_api.terms_of_content (a_content, voc) as l_existing_terms then + across + l_existing_terms as t_ic + loop + l_text := t_ic.item.text + from + l_found := False + l_new_terms.start + until + l_new_terms.after + loop + if l_new_terms.item.same_string_general (l_text) then + -- Already associated with term `t_ic.text'. + l_found := True + l_new_terms.remove + else + l_new_terms.forth + end + end + if not l_found then + -- Remove term + l_terms_to_remove.force (t_ic.item) + end + end + across + l_terms_to_remove as t_ic + loop + a_taxonomy_api.unassociate_term_from_content (t_ic.item, a_content) + end + end + across + l_new_terms as t_ic + loop + t := a_taxonomy_api.term_by_text (t_ic.item, voc) + if + t = Void and voc.is_tags + then + -- Create new term! + create t.make (t_ic.item) + a_taxonomy_api.save_term (t, voc) + if a_taxonomy_api.has_error then + t := Void + end + end + if t /= Void then + a_taxonomy_api.associate_term_with_content (t, a_content) + end + end + end + end + end + end + end + feature -- Helpers splitted_string (s: READABLE_STRING_32; sep: CHARACTER): LIST [READABLE_STRING_32] diff --git a/src/service/cms_api.e b/src/service/cms_api.e index d3a6e4e..ce43c95 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -292,6 +292,26 @@ feature -- Logging end end +feature -- Internationalization (i18n) + + translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32 + -- Translated text `a_text' according to expected context (lang, ...) + -- and adapt according to options eventually set by `opts'. + do + to_implement ("Implement i18n support [2015-may]") + Result := a_text.as_string_32 + end + + formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32 + -- Format `a_text' using arguments `args'. + --| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact" + local + l_formatter: CMS_STRING_FORMATTER + do + create l_formatter + Result := l_formatter.formatted_string (a_text, args) + end + feature -- Emails new_email (a_to_address: READABLE_STRING_8; a_subject: READABLE_STRING_8; a_content: READABLE_STRING_8): CMS_EMAIL diff --git a/src/service/content/cms_content.e b/src/service/content/cms_content.e index e6c9444..62acafb 100644 --- a/src/service/content/cms_content.e +++ b/src/service/content/cms_content.e @@ -13,6 +13,11 @@ inherit feature -- Access + identifier: detachable READABLE_STRING_32 + -- Optional identifier. + deferred + end + title: detachable READABLE_STRING_32 -- Title associated with Current content. deferred @@ -37,6 +42,14 @@ feature -- Access feature -- Status report + has_identifier: BOOLEAN + -- Current content has identifier? + do + Result := identifier /= Void + ensure + Result implies identifier /= Void + end + is_typed_as (a_content_type: READABLE_STRING_GENERAL): BOOLEAN -- Is current node of type `a_content_type' ? do diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index ec7daeb..751075c 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -113,26 +113,6 @@ feature -- Access end end -feature -- Internationalization (i18n) - - translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32 - -- Translated text `a_text' according to expected context (lang, ...) - -- and adapt according to options eventually set by `opts'. - do - to_implement ("Implement i18n support [2015-may]") - Result := a_text.as_string_32 - end - - formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32 - -- Format `a_text' using arguments `args'. - --| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact" - local - l_formatter: CMS_STRING_FORMATTER - do - create l_formatter - Result := l_formatter.formatted_string (a_text, args) - end - feature -- API api: CMS_API @@ -896,6 +876,22 @@ feature -- Menu: change m.extend (lnk) end +feature -- Internationalization (i18n) + + translation (a_text: READABLE_STRING_GENERAL; opts: detachable CMS_API_OPTIONS): STRING_32 + -- Translated text `a_text' according to expected context (lang, ...) + -- and adapt according to options eventually set by `opts'. + do + Result := api.translation (a_text, opts) + end + + formatted_string (a_text: READABLE_STRING_GENERAL; args: TUPLE): STRING_32 + -- Format `a_text' using arguments `args'. + --| ex: formatted_string ("hello $1, see page $title.", ["bob", "contact"] -> "hello bob, see page contact" + do + Result := api.formatted_string (a_text, args) + end + feature -- Message add_message (a_msg: READABLE_STRING_8; a_category: detachable READABLE_STRING_8) From 2255fcc0f65dd821497496ca5da2d68b2d7935fe Mon Sep 17 00:00:00 2001 From: jvelilla Date: Sun, 13 Dec 2015 18:19:25 -0300 Subject: [PATCH 08/14] Added Module Session Authentication with Cookies. Updated Demo example with the Module Session (Authentication with Cookies) Fixed little issue with SQL query in OpenID module. --- examples/demo/demo-safe.ecf | 1 + .../session_auth/files/js/roc_basic_auth.js | 325 ++++++++++++++++ .../scripts/session_auth_table.sql | 11 + .../session_auth/templates/block_login.tpl | 37 ++ examples/demo/src/demo_cms_execution.e | 3 + modules/oauth20/cms_oauth_20_module.e | 2 +- modules/openid/cms_openid_module.e | 2 +- .../persitence/cms_openid_storage_sql.e | 2 +- modules/session_auth/cms_session_api.e | 63 ++++ .../session_auth/cms_session_auth-safe.ecf | 28 ++ .../session_auth/cms_session_auth_module.e | 351 ++++++++++++++++++ modules/session_auth/cms_session_constant.e | 13 + .../filter/cms_session_auth_filter.e | 55 +++ .../handler/cms_session_auth_logoff_handler.e | 132 +++++++ .../persistence/cms_session_auth_storage_i.e | 46 +++ .../cms_session_auth_storage_null.e | 47 +++ .../cms_session_auth_storage_sql.e | 169 +++++++++ .../session_auth/site/cms_token_generator.e | 153 ++++++++ .../site/files/js/roc_basic_auth.js | 325 ++++++++++++++++ .../site/scripts/session_auth_table.sql.tpl | 11 + .../site/templates/block_login.tpl | 37 ++ 21 files changed, 1810 insertions(+), 3 deletions(-) create mode 100644 examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js create mode 100644 examples/demo/site/modules/session_auth/scripts/session_auth_table.sql create mode 100644 examples/demo/site/modules/session_auth/templates/block_login.tpl create mode 100644 modules/session_auth/cms_session_api.e create mode 100644 modules/session_auth/cms_session_auth-safe.ecf create mode 100644 modules/session_auth/cms_session_auth_module.e create mode 100644 modules/session_auth/cms_session_constant.e create mode 100644 modules/session_auth/filter/cms_session_auth_filter.e create mode 100644 modules/session_auth/handler/cms_session_auth_logoff_handler.e create mode 100644 modules/session_auth/persistence/cms_session_auth_storage_i.e create mode 100644 modules/session_auth/persistence/cms_session_auth_storage_null.e create mode 100644 modules/session_auth/persistence/cms_session_auth_storage_sql.e create mode 100644 modules/session_auth/site/cms_token_generator.e create mode 100644 modules/session_auth/site/files/js/roc_basic_auth.js create mode 100644 modules/session_auth/site/scripts/session_auth_table.sql.tpl create mode 100644 modules/session_auth/site/templates/block_login.tpl diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index b8c67db..b00a415 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -31,6 +31,7 @@ + diff --git a/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js b/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js new file mode 100644 index 0000000..467bcd4 --- /dev/null +++ b/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js @@ -0,0 +1,325 @@ +var ROC_AUTH = ROC_AUTH || { }; + +var loginURL = "/basic_auth_login"; +var logoutURL = "/basic_auth_logoff"; + +var userAgent = navigator.userAgent.toLowerCase(); +var firstLogIn = true; + +ROC_AUTH.login = function() { + var form = document.forms['cms_basic_auth']; + var username = form.username.value; + var password = form.password.value; + //var host = form.host.value; + var origin = window.location.origin + window.location.pathname; + var _login = function(){ + + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", loginURL, true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + delete form; + window.location=window.location.origin; + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.login_with_redirect = function() { + var form = document.forms[2]; + var username = form.username.value; + var password = form.password.value; + var host = form.host.value; + var _login = function(){ + + var redirectURL = form.redirect && form.redirect.value || ""; + + + $("#imgProgressRedirect").show(); + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(loginURL), true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + if (redirectURL === "") { + window.location=host.concat("/"); + } else { + window.location=host.concat(redirectURL); + } + + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.getQueryParameterByName = function (name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +ROC_AUTH.logoff = function(callback){ + var form = document.forms[0]; + var host = form.host.value; + + if (userAgent.indexOf("msie") != -1) { + document.execCommand("ClearAuthenticationCache"); + } + else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + + var request1 = new XMLHttpRequest(); + var request2 = new XMLHttpRequest(); + + //Logout. Tell the server not to return the "WWW-Authenticate" header + request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); + request1.send(""); + request1.onreadystatechange = function(){ + if (request1.readyState == 4) { + + //Sign in with dummy credentials to clear the auth cache + request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request2.send(""); + + request2.onreadystatechange = function(){ + if (request2.readyState == 4) { + if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} + } + } + + } + } + } + else { + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request.send(""); + request.onreadystatechange = function(){ + if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); + } + } + } +}; + + +ROC_AUTH.remove = function (id) +{ + var element = document.getElementById(id); + element.outerHTML = ""; + delete element; + return; +}; + + + +$(document).ready(function() { + + if (typeof String.prototype.contains != 'function') { + String.prototype.contains = function (str){ + return this.indexOf(str) != -1; + }; + } + ROC_AUTH.progressive_loging(); + +}); + + +ROC_AUTH.progressive_loging = function () { + + ROC_AUTH.login_href(); +}; + + +$(document).keypress(function(e) { + if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { + ROC_AUTH.login(); + } +}); + +ROC_AUTH.OnOneClick = function(event) { + event.preventDefault(); + if ( document.forms[0] === undefined ) { + ROC_AUTH.create_form(); + } + return false; +}; + +ROC_AUTH.login_href = function() { + var els = document.getElementsByTagName("a"); + for (var i = 0, l = els.length; i < l; i++) { + var el = els[i]; + if (el.href.contains("/basic_auth_login?destination")) { + loginURL = el.href; + var OneClick = el; + OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); + } + } +}; + + +ROC_AUTH.create_form = function() { + + // Fetching HTML Elements in Variables by ID. + var createform = document.createElement('form'); // Create New Element Form + createform.setAttribute("action", ""); // Setting Action Attribute on Form + createform.setAttribute("method", "post"); // Setting Method Attribute on Form + $("body").append(createform); + + var heading = document.createElement('h2'); // Heading of Form + heading.innerHTML = "Login Form "; + createform.appendChild(heading); + + var line = document.createElement('hr'); // Giving Horizontal Row After Heading + createform.appendChild(line); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var namelabel = document.createElement('label'); // Create Label for Name Field + namelabel.innerHTML = "Username : "; // Set Field Labels + createform.appendChild(namelabel); + + var inputelement = document.createElement('input'); // Create Input Field for UserName + inputelement.setAttribute("type", "text"); + inputelement.setAttribute("name", "username"); + inputelement.setAttribute("required","required"); + createform.appendChild(inputelement); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var passwordlabel = document.createElement('label'); // Create Label for Password Field + passwordlabel.innerHTML = "Password : "; + createform.appendChild(passwordlabel); + + var passwordelement = document.createElement('input'); // Create Input Field for Password. + passwordelement.setAttribute("type", "password"); + passwordelement.setAttribute("name", "password"); + passwordelement.setAttribute("id", "password"); + passwordelement.setAttribute("required","required"); + createform.appendChild(passwordelement); + + + var passwordbreak = document.createElement('br'); + createform.appendChild(passwordbreak); + + + var submitelement = document.createElement('button'); // Append Submit Button + submitelement.setAttribute("type", "button"); + submitelement.setAttribute("onclick", "ROC_AUTH.login();"); + submitelement.innerHTML = "Sign In "; + createform.appendChild(submitelement); + +}; + + +var password = document.getElementById("password"); +var confirm_password = document.getElementById("confirm_password"); + +ROC_AUTH.validatePassword =function(){ + if ((password != null) && (confirm_password != null)) { + if(password.value != confirm_password.value) { + confirm_password.setCustomValidity("Passwords Don't Match"); + } else { + confirm_password.setCustomValidity(''); + } + } +} + +if ((password != null) && (confirm_password != null)) { + password.onchange = ROC_AUTH.validatePassword(); + confirm_password.onkeyup = ROC_AUTH.validatePassword; +} diff --git a/examples/demo/site/modules/session_auth/scripts/session_auth_table.sql b/examples/demo/site/modules/session_auth/scripts/session_auth_table.sql new file mode 100644 index 0000000..ada32db --- /dev/null +++ b/examples/demo/site/modules/session_auth/scripts/session_auth_table.sql @@ -0,0 +1,11 @@ + +CREATE TABLE session_auth ( + `uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0), + `access_token` TEXT NOT NULL, + `created` DATETIME NOT NULL, + CONSTRAINT `uid` + UNIQUE(`uid`), + CONSTRAINT `access_token` + UNIQUE(`access_token`) + ); + diff --git a/examples/demo/site/modules/session_auth/templates/block_login.tpl b/examples/demo/site/modules/session_auth/templates/block_login.tpl new file mode 100644 index 0000000..a344568 --- /dev/null +++ b/examples/demo/site/modules/session_auth/templates/block_login.tpl @@ -0,0 +1,37 @@ +
    + {unless isset="$user"} +

    Login or Register

    +
    +
    +
    +
    + + +
    + +
    + + +
    + +
    +
    +
    +
    + +
    + {/unless} + {if isset=$error} +
    +
    +

    + {$error/} +

    +
    +
    + {/if} +
    diff --git a/examples/demo/src/demo_cms_execution.e b/examples/demo/src/demo_cms_execution.e index a3286d9..c73948f 100644 --- a/examples/demo/src/demo_cms_execution.e +++ b/examples/demo/src/demo_cms_execution.e @@ -89,6 +89,9 @@ feature -- CMS modules create {GOOGLE_CUSTOM_SEARCH_MODULE} m.make a_setup.register_module (m) + + create {CMS_SESSION_AUTH_MODULE} m.make + a_setup.register_module (m) end end diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index 1e96534..281d7a9 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -227,7 +227,7 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") then + if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then lnk2 := ic.item end end diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index e52184e..153305f 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -203,7 +203,7 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") then + if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then lnk2 := ic.item end end diff --git a/modules/openid/persitence/cms_openid_storage_sql.e b/modules/openid/persitence/cms_openid_storage_sql.e index f44c975..bb2f0d7 100644 --- a/modules/openid/persitence/cms_openid_storage_sql.e +++ b/modules/openid/persitence/cms_openid_storage_sql.e @@ -197,7 +197,7 @@ feature {NONE} -- User OpenID Sql_insert_openid: STRING = "INSERT INTO openid_items (uid, identity, created) VALUES (:uid, :identity, :utc_date);" - Sql_openid_consumers: STRING = "SELECT name FROM openid_consumers"; + Sql_openid_consumers: STRING = "SELECT name FROM openid_consumers;" feature {NONE} -- Consumer diff --git a/modules/session_auth/cms_session_api.e b/modules/session_auth/cms_session_api.e new file mode 100644 index 0000000..fd62dd2 --- /dev/null +++ b/modules/session_auth/cms_session_api.e @@ -0,0 +1,63 @@ +note + description: "API to manage CMS User session authentication" + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_API + + +inherit + CMS_MODULE_API + + REFACTORING_HELPER + +create {CMS_SESSION_AUTH_MODULE} + make_with_storage + +feature {NONE} -- Initialization + + make_with_storage (a_api: CMS_API; a_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I) + -- Create an object with api `a_api' and storage `a_session_auth_storage'. + do + session_auth_storage := a_session_auth_storage + make (a_api) + ensure + session_auth_storage_set: session_auth_storage = a_session_auth_storage + end + +feature {CMS_MODULE} -- Access: User session storage. + + session_auth_storage: CMS_SESSION_AUTH_STORAGE_I + -- storage interface. + +feature -- Access + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + do + Result := session_auth_storage.user_by_session_token (a_token) + end + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + do + Result := session_auth_storage.has_user_token (a_user) + end + +feature -- Change User session + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- New user session for user `a_user' with token `a_token'. + do + session_auth_storage.new_user_session_auth (a_token, a_user) + end + + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- Update user session for user `a_user' with token `a_token'. + do + session_auth_storage.update_user_session_auth (a_token, a_user) + end + +end diff --git a/modules/session_auth/cms_session_auth-safe.ecf b/modules/session_auth/cms_session_auth-safe.ecf new file mode 100644 index 0000000..95f44a5 --- /dev/null +++ b/modules/session_auth/cms_session_auth-safe.ecf @@ -0,0 +1,28 @@ + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + + + + + + diff --git a/modules/session_auth/cms_session_auth_module.e b/modules/session_auth/cms_session_auth_module.e new file mode 100644 index 0000000..52a8db8 --- /dev/null +++ b/modules/session_auth/cms_session_auth_module.e @@ -0,0 +1,351 @@ +note + description: "[ + This module allows the use Session Based Authentication using Cookies to restrict access + by looking up users in the given providers. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_MODULE + +inherit + CMS_MODULE + rename + module_api as user_session_api + redefine + filters, + setup_hooks, + initialize, + install, + user_session_api + end + + + CMS_HOOK_AUTO_REGISTER + + CMS_HOOK_BLOCK + + CMS_HOOK_MENU_SYSTEM_ALTER + + CMS_HOOK_VALUE_TABLE_ALTER + + SHARED_LOGGER + + CMS_REQUEST_UTIL + +create + make + +feature {NONE} -- Initialization + + make + do + version := "1.0" + description := "Service to manage cookie based authentication" + package := "authentication" + add_dependency ({CMS_AUTHENTICATION_MODULE}) + end + +feature -- Access + + name: STRING = "session_auth" + + + +feature {CMS_API} -- Module Initialization + + initialize (a_api: CMS_API) + -- + local + l_session_auth_api: like user_session_api + l_user_auth_storage: CMS_SESSION_AUTH_STORAGE_I + do + Precursor (a_api) + + -- Storage initialization + if attached a_api.storage.as_sql_storage as l_storage_sql then + create {CMS_SESSION_AUTH_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) + else + -- FIXME: in case of NULL storage, should Current be disabled? + create {CMS_SESSION_AUTH_STORAGE_NULL} l_user_auth_storage + end + + -- API initialization + create l_session_auth_api.make_with_storage (a_api, l_user_auth_storage) + user_session_api := l_session_auth_api + ensure then + session_auth_api_set: user_session_api /= Void + end + +feature {CMS_API} -- Module management + + install (api: CMS_API) + local + l_consumers: LIST [STRING] + do + -- Schema + if attached api.storage.as_sql_storage as l_sql_storage then + if not l_sql_storage.sql_table_exists ("session_auth") then + --| Schema + l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("session_auth_table.sql")), Void) + + if l_sql_storage.has_error then + api.logger.put_error ("Could not initialize database for blog module", generating_type) + end + end + l_sql_storage.sql_finalize + Precursor {CMS_MODULE}(api) + end + end + +feature {CMS_API} -- Access: API + + user_session_api: detachable CMS_SESSION_API + -- + +feature -- Access: router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- + do + a_router.handle ("/account/roc-session-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/account/roc-session-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/account/login-with-session", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,user_session_api, ?, ?)), a_router.methods_get_post) + end + +feature -- Access: filter + + filters (a_api: CMS_API): detachable LIST [WSF_FILTER] + -- Possibly list of Filter's module. + do + create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) + if attached user_session_api as l_session_api then + Result.extend (create {CMS_SESSION_AUTH_FILTER}.make (a_api, l_session_api)) + end + end + +feature {NONE} -- Implementation: routes + + handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + + handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_cookie: WSF_COOKIE + do + if + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_cookie_token and then + attached {CMS_USER} current_user (req) as l_user + then + -- Logout Session + create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_cookie_token.value) + l_cookie.set_path ("/") + l_cookie.set_max_age (-1) + res.add_cookie (l_cookie) + unset_current_user (req) + + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + r.set_redirection (req.absolute_script_url ("")) + r.execute + else + fixme (generator + ": missing else implementation in handle_logout!") + end + end + + handle_login_with_session (api: CMS_API; a_session_api: detachable CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_token: STRING + l_cookie: WSF_COOKIE + do + if + attached a_session_api as l_session_api and then + attached {WSF_STRING} req.form_parameter ("username") as l_username and then + attached {WSF_STRING} req.form_parameter ("password") as l_password and then + api.user_api.is_valid_credential (l_username.value, l_password.value) and then + attached api.user_api.user_by_name (l_username.value) as l_user + then + l_token := generate_token + if + a_session_api.has_user_token (l_user) + then + l_session_api.update_user_session_auth (l_token, l_user) + else + l_session_api.new_user_session_auth (l_token, l_user) + end + create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_token) + l_cookie.set_max_age (60*60*24*360) + l_cookie.set_path ("/") + res.add_cookie (l_cookie) + set_current_user (req, l_user) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_redirection (req.absolute_script_url ("")) + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + if attached template_block ("login", r) as l_tpl_block then + if attached {WSF_STRING} req.form_parameter ("username") as l_username then + l_tpl_block.set_value (l_username.value, "username") + end + l_tpl_block.set_value ("Wrong: Username or password ", "error") + r.add_block (l_tpl_block, "content") + end + r.execute + end + end + +feature -- Hooks configuration + + setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) + -- Module hooks configuration. + do + auto_subscribe_to_hooks (a_hooks) + a_hooks.subscribe_to_block_hook (Current) + a_hooks.subscribe_to_value_table_alter_hook (Current) + end + +feature -- Hooks + + value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) + -- + do + if + attached a_response.user as u and then + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + then + a_value.force ("account/roc-session-logout", "auth_login_strategy") + end + + end + + menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + -- Hook execution on collection of menu contained by `a_menu_system' + -- for related response `a_response'. + local + lnk: CMS_LOCAL_LINK + lnk2: detachable CMS_LINK + do + if + attached a_response.user as u and then + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + then + across + a_menu_system.primary_menu.items as ic + until + lnk2 /= Void + loop + if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then + lnk2 := ic.item + end + end + if lnk2 /= Void then + a_menu_system.primary_menu.remove (lnk2) + end + create lnk.make ("Logout", "account/roc-session-logout" ) + a_menu_system.primary_menu.extend (lnk) + else + if a_response.location.starts_with ("account/") then + create lnk.make ("Session", "account/roc-session-login") + a_response.add_to_primary_tabs (lnk) + end + end + end + + block_list: ITERABLE [like {CMS_BLOCK}.name] + local + l_string: STRING + do + Result := <<"login">> + debug ("roc") + create l_string.make_empty + across + Result as ic + loop + l_string.append (ic.item) + l_string.append_character (' ') + end + write_debug_log (generator + ".block_list:" + l_string ) + end + end + + get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + do + if + a_block_id.is_case_insensitive_equal_general ("login") and then + a_response.location.starts_with ("account/roc-session-login") + then + get_block_view_login (a_block_id, a_response) + end + end + +feature {NONE} -- Helpers + + template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK + -- Smarty content block for `a_block_id' + local + p: detachable PATH + do + create p.make_from_string ("templates") + p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") + + p := a_response.api.module_theme_resource_location (Current, p) + if p /= Void then + if attached p.entry as e then + create Result.make (a_block_id, Void, p.parent, e) + else + create Result.make (a_block_id, Void, p.parent, p) + end + end + end + +feature {NONE} -- Block views + + get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if attached template_block (a_block_id, a_response) as l_tpl_block then + create vals.make (1) + -- add the variable to the block + value_table_alter (vals, a_response) + across + vals as ic + loop + l_tpl_block.set_value (ic.item, ic.key) + end + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + + + generate_token: STRING + -- Generate token to use in a Session. + local + l_token: STRING + l_security: CMS_TOKEN_GENERATOR + 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/session_auth/cms_session_constant.e b/modules/session_auth/cms_session_constant.e new file mode 100644 index 0000000..8d44501 --- /dev/null +++ b/modules/session_auth/cms_session_constant.e @@ -0,0 +1,13 @@ +note + description: "Summary description for {CMS_SESSION_CONSTANT}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_CONSTANT + + +feature + session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_" + -- Name of Cookie used to keep the session info. +end diff --git a/modules/session_auth/filter/cms_session_auth_filter.e b/modules/session_auth/filter/cms_session_auth_filter.e new file mode 100644 index 0000000..dd96337 --- /dev/null +++ b/modules/session_auth/filter/cms_session_auth_filter.e @@ -0,0 +1,55 @@ +note + description: "[ + Processes a HTTP request's checking Session cookies, putting the result into the execution variable user. + ]" + date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" + revision: "$Revision: 96616 $" + +class + CMS_SESSION_AUTH_FILTER + +inherit + WSF_URI_TEMPLATE_HANDLER + + CMS_HANDLER + rename + make as make_handler + end + + WSF_FILTER + +create + make + +feature {NONE} -- Initialization + + make (a_api: CMS_API; a_session_oauth_api: CMS_SESSION_API) + do + make_handler (a_api) + session_oauth_api := a_session_oauth_api + end + + session_oauth_api: CMS_SESSION_API + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + do + api.logger.put_debug (generator + ".execute ", Void) + -- A valid user + if + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_roc_auth_session_token + then + if attached session_oauth_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then + set_current_user (req, l_user) + else + api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) + end + else + api.logger.put_debug (generator + ".execute without authentication", Void) + end + execute_next (req, res) + end + +end diff --git a/modules/session_auth/handler/cms_session_auth_logoff_handler.e b/modules/session_auth/handler/cms_session_auth_logoff_handler.e new file mode 100644 index 0000000..36a4e19 --- /dev/null +++ b/modules/session_auth/handler/cms_session_auth_logoff_handler.e @@ -0,0 +1,132 @@ +note + description: "Summary description for {CMS_SESSION_AUTH_LOGOFF_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_LOGOFF_HANDLER + +inherit + CMS_HANDLER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + l_url: STRING + i: INTEGER + l_message: STRING + do + api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) + if attached req.query_parameter ("prompt") as l_prompt then + unset_current_user (req) + send_access_denied_message (res) + else + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) + unset_current_user (req) + l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection + l_url := req.absolute_script_url ("") + i := l_url.substring_index ("://", 1) + if i > 0 then + -- Note: this is a hack to have the logout effective on various browser + -- (firefox requires this). + l_url.replace_substring ("://_logout_basic_auth_@", i, i + 2) + end + if + attached req.http_user_agent as l_user_agent and then + browser_name (l_user_agent).is_case_insensitive_equal_general ("Firefox") + then + -- Set status to refirect + -- and redirect to the host page. + l_page.set_status_code ({HTTP_STATUS_CODE}.found) + l_page.set_redirection (l_url) + end + create l_message.make_from_string (logout_message) + l_message.replace_substring_all ("$site_login", req.absolute_script_url ("/account/roc-login")) + l_message.replace_substring_all ("$site_home", req.absolute_script_url ("")) + l_page.set_main_content (l_message) + l_page.execute + end + end + + + browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32 + -- Browser name. + -- Must contain Must not contain + -- Firefox Firefox/xyz Seamonkey/xyz + -- Seamonkey Seamonkey/xyz + -- Chrome Chrome/xyz Chromium/xyz + -- Chromium Chromium/xyz + -- Safari Safari/xyz Chrome/xyz + -- Chromium/xyz + -- Opera OPR/xyz [1] + -- Opera/xyz [2] + -- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format + + do + if + a_user_agent.has_substring ("Firefox") and then + not a_user_agent.has_substring ("Seamonkey") + then + Result := "Firefox" + elseif a_user_agent.has_substring ("Seamonkey") then + Result := "Seamonkey" + elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then + Result := "Chrome" + elseif a_user_agent.has_substring ("Chromium") then + Result := "Chromiun" + elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then + Result := "Safari" + elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then + Result := "Opera" + elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then + Result := "Internet Explorer" + else + Result := "Unknown" + end + end + + + feature {NONE}-- Lougout Message + + logout_message: STRING = "[ +
    +

    You are now signed out

    +

    You can log in again, or go to the front page.

    +
    + ]" + + +end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_i.e b/modules/session_auth/persistence/cms_session_auth_storage_i.e new file mode 100644 index 0000000..f40b5bf --- /dev/null +++ b/modules/session_auth/persistence/cms_session_auth_storage_i.e @@ -0,0 +1,46 @@ +note + description: "[ + API to handle OAUTH storage + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_SESSION_AUTH_STORAGE_I + +inherit + SHARED_LOGGER + +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + deferred + end + +feature -- Access: Users + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + deferred + end + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + deferred + end + + +feature -- Change User session + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- New user session for user `a_user' with token `a_token'. + deferred + end + + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- Update user session for user `a_user' with token `a_token'. + deferred + end +end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_null.e b/modules/session_auth/persistence/cms_session_auth_storage_null.e new file mode 100644 index 0000000..ff2da97 --- /dev/null +++ b/modules/session_auth/persistence/cms_session_auth_storage_null.e @@ -0,0 +1,47 @@ +note + description: "Summary description for {CMS_SESSION_AUTH_STORAGE_NULL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_STORAGE_NULL + +inherit + + CMS_SESSION_AUTH_STORAGE_I + + +feature -- Error handler + + error_handler: ERROR_HANDLER + -- Error handler. + do + create Result.make + end + +feature -- Access + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + do + end + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + do + end + +feature -- Change User session + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- New user session for user `a_user' with token `a_token'. + do + end + + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- Update user session for user `a_user' with token `a_token'. + do + end + +end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_sql.e b/modules/session_auth/persistence/cms_session_auth_storage_sql.e new file mode 100644 index 0000000..a6b5d26 --- /dev/null +++ b/modules/session_auth/persistence/cms_session_auth_storage_sql.e @@ -0,0 +1,169 @@ +note + description: "Summary description for {CMS_SESSION_AUTH_STORAGE_SQL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_AUTH_STORAGE_SQL + +inherit + CMS_SESSION_AUTH_STORAGE_I + + CMS_PROXY_STORAGE_SQL + + CMS_SESSION_AUTH_STORAGE_I + + CMS_STORAGE_SQL_I + + REFACTORING_HELPER + +create + make + +feature -- Access User + + user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- Retrieve user by token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".user_by_session_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_insert (Select_user_by_token, l_parameters) + if not has_error and not sql_after then + Result := fetch_user + sql_forth + if not sql_after then + check no_more_than_one: False end + Result := Void + end + end + sql_finalize + end + + + has_user_token (a_user: CMS_USER): BOOLEAN + -- Has the user `a_user' and associated session token? + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".has_user_token") + create l_parameters.make (1) + l_parameters.put (a_user.id, "uid") + sql_insert (Select_user_token, l_parameters) + if not has_error and not sql_after then + if sql_read_integer_64 (1) = 1 then + Result := True + else + Result := False + end + end + sql_finalize + end + + + +feature -- Change User token + + new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) + -- Add a new user with oauth2 authentication. + -- . + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_session") + create l_parameters.make (3) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + sql_insert (sql_insert_session_auth, l_parameters) + sql_commit_transaction + sql_finalize + end + + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".update_user_session_auth") + create l_parameters.make (3) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + + sql_modify (sql_update_session_auth, l_parameters) + sql_commit_transaction + sql_finalize + end + + +feature {NONE} -- Implementation + +fetch_user: detachable CMS_USER + local + l_id: INTEGER_64 + l_name: detachable READABLE_STRING_32 + do + if attached sql_read_integer_64 (1) as i then + l_id := i + end + if attached sql_read_string_32 (2) as s and then not s.is_whitespace then + l_name := s + end + + if l_name /= Void then + create Result.make (l_name) + if l_id > 0 then + Result.set_id (l_id) + end + elseif l_id > 0 then + create Result.make_with_id (l_id) + end + + if Result /= Void then + if attached sql_read_string (3) as l_password then + -- FIXME: should we return the password here ??? + Result.set_hashed_password (l_password) + end + if attached sql_read_string (5) as l_email then + Result.set_email (l_email) + end + if attached sql_read_integer_32 (6) as l_status then + Result.set_status (l_status) + end + else + check expected_valid_user: False end + end + end + + +feature {NONE} -- SQL statements + + Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" + --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. + + Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" + + + Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" + + + Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" + +end diff --git a/modules/session_auth/site/cms_token_generator.e b/modules/session_auth/site/cms_token_generator.e new file mode 100644 index 0000000..ba1adb8 --- /dev/null +++ b/modules/session_auth/site/cms_token_generator.e @@ -0,0 +1,153 @@ +note + description: "Provides security routine helpers" + date: "$Date$" + revision: "$Revision$" + +class + CMS_TOKEN_GENERATOR + +inherit + + REFACTORING_HELPER + +feature -- Access + + token: STRING + -- Cryptographic random base 64 string. + do + Result := salt_with_size (16) + -- Remove trailing equal sign + Result.keep_head (Result.count - 2) + end + + salt: STRING + -- Cryptographic random number of 16 bytes. + do + Result := salt_with_size (16) + end + + password: STRING + -- Cryptographic random password of 10 bytes. + do + Result := salt_with_size (10) + -- Remove trailing equal signs + Result.keep_head (Result.count - 2) + end + + password_hash (a_password, a_salt: STRING): STRING + -- Password hash based on password `a_password' and salt value `a_salt'. + do + Result := sha1_string (a_password + a_salt ) + end + +feature {NONE} -- Implementation + + salt_with_size (a_val: INTEGER): STRING + -- Return a salt with size `a_val'. + local + l_salt: SALT_XOR_SHIFT_64_GENERATOR + l_array: ARRAY [INTEGER_8] + i: INTEGER + do + create l_salt.make (a_val) + create l_array.make_empty + i := 1 + across + l_salt.new_sequence as c + loop + l_array.force (c.item.as_integer_8, i) + i := i + 1 + end + Result := encode_base_64 (l_array) + end + + sha1_string (a_str: STRING): STRING + -- SHA1 diggest of `a_str'. + do + sha1.update_from_string (a_str) + Result := sha1.digest_as_string + sha1.reset + end + + sha1: SHA1 + -- Create a SHA1 object. + once + create Result.make + end + +feature -- Encoding + + + encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 + -- Encodes a byte array into a STRING doing base64 encoding. + local + l_output: SPECIAL [INTEGER_8] + l_remaining: INTEGER + i, ptr: INTEGER + char: CHARACTER + do + to_implement ("Check existing code to do that!!!.") + create l_output.make_filled (0, ((bytes.count + 2) // 3) * 4) + l_remaining := bytes.count + from + i := 0 + ptr := 0 + until + l_remaining <= 3 + loop + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF)) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i + 1] & 0xF) |<< 2) | ((bytes [i + 2] |>> 6) & 0x3)) + ptr := ptr + 1 + l_output [ptr] := encode_value (bytes [i + 2] & 0x3F) + ptr := ptr + 1 + l_remaining := l_remaining - 3 + i := i + 3 + end + -- encode when exactly 1 element (left) to encode + char := '=' + if l_remaining = 1 then + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i]) & 0x3) |<< 4) + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + end + + -- encode when exactly 2 elements (left) to encode + if l_remaining = 2 then + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF)); + ptr := ptr + 1 + l_output [ptr] := encode_value ((bytes [i + 1] & 0xF) |<< 2); + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + end + Result := "" + across + l_output as elem + loop + Result.append_character (elem.item.to_character_8) + end + end + + base64_map: SPECIAL [CHARACTER_8] + -- Table for Base64 encoding. + once + Result := ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").area + end + + encode_value (i: INTEGER_8): INTEGER_8 + -- Encode `i'. + do + Result := base64_map [i & 0x3F].code.as_integer_8 + end + +end diff --git a/modules/session_auth/site/files/js/roc_basic_auth.js b/modules/session_auth/site/files/js/roc_basic_auth.js new file mode 100644 index 0000000..467bcd4 --- /dev/null +++ b/modules/session_auth/site/files/js/roc_basic_auth.js @@ -0,0 +1,325 @@ +var ROC_AUTH = ROC_AUTH || { }; + +var loginURL = "/basic_auth_login"; +var logoutURL = "/basic_auth_logoff"; + +var userAgent = navigator.userAgent.toLowerCase(); +var firstLogIn = true; + +ROC_AUTH.login = function() { + var form = document.forms['cms_basic_auth']; + var username = form.username.value; + var password = form.password.value; + //var host = form.host.value; + var origin = window.location.origin + window.location.pathname; + var _login = function(){ + + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", loginURL, true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + delete form; + window.location=window.location.origin; + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.login_with_redirect = function() { + var form = document.forms[2]; + var username = form.username.value; + var password = form.password.value; + var host = form.host.value; + var _login = function(){ + + var redirectURL = form.redirect && form.redirect.value || ""; + + + $("#imgProgressRedirect").show(); + + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } + + + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + }else{ + + //Instantiate HTTP Request + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(loginURL), true, username, password); + request.send(null); + + //Process Response + request.onreadystatechange = function(){ + if (request.readyState == 4) { + if (request.status==200) { + if (redirectURL === "") { + window.location=host.concat("/"); + } else { + window.location=host.concat(redirectURL); + } + + } + else{ + if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ + } + + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
    Invalid Credentials
    "; + newdiv.id = 'myModalFormId'; + $(".primary-tabs").append(newdiv); + $("#imgProgressRedirect").hide(); + } + + } + } + } + } + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) _login(); + else logoff(_login); + } + else{ + _login(); + } + + if (firstLogIn) firstLogIn = false; +}; + + +ROC_AUTH.getQueryParameterByName = function (name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); +} + +ROC_AUTH.logoff = function(callback){ + var form = document.forms[0]; + var host = form.host.value; + + if (userAgent.indexOf("msie") != -1) { + document.execCommand("ClearAuthenticationCache"); + } + else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + + var request1 = new XMLHttpRequest(); + var request2 = new XMLHttpRequest(); + + //Logout. Tell the server not to return the "WWW-Authenticate" header + request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); + request1.send(""); + request1.onreadystatechange = function(){ + if (request1.readyState == 4) { + + //Sign in with dummy credentials to clear the auth cache + request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request2.send(""); + + request2.onreadystatechange = function(){ + if (request2.readyState == 4) { + if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} + } + } + + } + } + } + else { + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host.concat(logoutURL), true, "logout", "logout"); + request.send(""); + request.onreadystatechange = function(){ + if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); + } + } + } +}; + + +ROC_AUTH.remove = function (id) +{ + var element = document.getElementById(id); + element.outerHTML = ""; + delete element; + return; +}; + + + +$(document).ready(function() { + + if (typeof String.prototype.contains != 'function') { + String.prototype.contains = function (str){ + return this.indexOf(str) != -1; + }; + } + ROC_AUTH.progressive_loging(); + +}); + + +ROC_AUTH.progressive_loging = function () { + + ROC_AUTH.login_href(); +}; + + +$(document).keypress(function(e) { + if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { + ROC_AUTH.login(); + } +}); + +ROC_AUTH.OnOneClick = function(event) { + event.preventDefault(); + if ( document.forms[0] === undefined ) { + ROC_AUTH.create_form(); + } + return false; +}; + +ROC_AUTH.login_href = function() { + var els = document.getElementsByTagName("a"); + for (var i = 0, l = els.length; i < l; i++) { + var el = els[i]; + if (el.href.contains("/basic_auth_login?destination")) { + loginURL = el.href; + var OneClick = el; + OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); + } + } +}; + + +ROC_AUTH.create_form = function() { + + // Fetching HTML Elements in Variables by ID. + var createform = document.createElement('form'); // Create New Element Form + createform.setAttribute("action", ""); // Setting Action Attribute on Form + createform.setAttribute("method", "post"); // Setting Method Attribute on Form + $("body").append(createform); + + var heading = document.createElement('h2'); // Heading of Form + heading.innerHTML = "Login Form "; + createform.appendChild(heading); + + var line = document.createElement('hr'); // Giving Horizontal Row After Heading + createform.appendChild(line); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var namelabel = document.createElement('label'); // Create Label for Name Field + namelabel.innerHTML = "Username : "; // Set Field Labels + createform.appendChild(namelabel); + + var inputelement = document.createElement('input'); // Create Input Field for UserName + inputelement.setAttribute("type", "text"); + inputelement.setAttribute("name", "username"); + inputelement.setAttribute("required","required"); + createform.appendChild(inputelement); + + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); + + var passwordlabel = document.createElement('label'); // Create Label for Password Field + passwordlabel.innerHTML = "Password : "; + createform.appendChild(passwordlabel); + + var passwordelement = document.createElement('input'); // Create Input Field for Password. + passwordelement.setAttribute("type", "password"); + passwordelement.setAttribute("name", "password"); + passwordelement.setAttribute("id", "password"); + passwordelement.setAttribute("required","required"); + createform.appendChild(passwordelement); + + + var passwordbreak = document.createElement('br'); + createform.appendChild(passwordbreak); + + + var submitelement = document.createElement('button'); // Append Submit Button + submitelement.setAttribute("type", "button"); + submitelement.setAttribute("onclick", "ROC_AUTH.login();"); + submitelement.innerHTML = "Sign In "; + createform.appendChild(submitelement); + +}; + + +var password = document.getElementById("password"); +var confirm_password = document.getElementById("confirm_password"); + +ROC_AUTH.validatePassword =function(){ + if ((password != null) && (confirm_password != null)) { + if(password.value != confirm_password.value) { + confirm_password.setCustomValidity("Passwords Don't Match"); + } else { + confirm_password.setCustomValidity(''); + } + } +} + +if ((password != null) && (confirm_password != null)) { + password.onchange = ROC_AUTH.validatePassword(); + confirm_password.onkeyup = ROC_AUTH.validatePassword; +} diff --git a/modules/session_auth/site/scripts/session_auth_table.sql.tpl b/modules/session_auth/site/scripts/session_auth_table.sql.tpl new file mode 100644 index 0000000..ada32db --- /dev/null +++ b/modules/session_auth/site/scripts/session_auth_table.sql.tpl @@ -0,0 +1,11 @@ + +CREATE TABLE session_auth ( + `uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0), + `access_token` TEXT NOT NULL, + `created` DATETIME NOT NULL, + CONSTRAINT `uid` + UNIQUE(`uid`), + CONSTRAINT `access_token` + UNIQUE(`access_token`) + ); + diff --git a/modules/session_auth/site/templates/block_login.tpl b/modules/session_auth/site/templates/block_login.tpl new file mode 100644 index 0000000..a344568 --- /dev/null +++ b/modules/session_auth/site/templates/block_login.tpl @@ -0,0 +1,37 @@ +
    + {unless isset="$user"} +

    Login or Register

    +
    +
    +
    +
    + + +
    + +
    + + +
    + +
    +
    +
    +
    + +
    + {/unless} + {if isset=$error} +
    +
    +

    + {$error/} +

    +
    +
    + {/if} +
    From e05c4dca3aad413b74e2cbb27deaec94816600d3 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 15 Dec 2015 15:32:31 -0300 Subject: [PATCH 09/14] Fixed typos Renamed class CMS_SESSION_CONSTANT to CMS_SESSION_CONSTANTS Removed unneeded classes and files. Update SQL implementation. --- modules/oauth20/cms_oauth_20_module.e | 9 +- modules/openid/cms_openid_module.e | 5 +- .../session_auth/cms_session_auth_module.e | 14 +- modules/session_auth/cms_session_constant.e | 13 - modules/session_auth/cms_session_constants.e | 19 + .../filter/cms_session_auth_filter.e | 2 +- .../handler/cms_session_auth_logoff_handler.e | 132 ------- .../cms_session_auth_storage_sql.e | 52 +-- .../site/files/js/roc_basic_auth.js | 325 ------------------ 9 files changed, 56 insertions(+), 515 deletions(-) delete mode 100644 modules/session_auth/cms_session_constant.e create mode 100644 modules/session_auth/cms_session_constants.e delete mode 100644 modules/session_auth/handler/cms_session_auth_logoff_handler.e delete mode 100644 modules/session_auth/site/files/js/roc_basic_auth.js diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index 281d7a9..7cf6bab 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -99,7 +99,7 @@ feature {CMS_API} -- Module management l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers.sql")), Void) if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for blog module", generating_type) + api.logger.put_error ("Could not initialize database for oauth_20 module", generating_type) end -- TODO workaround. l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers_initialize.sql")), Void) @@ -108,7 +108,7 @@ feature {CMS_API} -- Module management -- TODO workaround, until we have an admin module l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void) if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type) + api.logger.put_error ("Could not initialize database for differnent consumers", generating_type) else from l_sql_storage.sql_start @@ -227,7 +227,10 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then + if + ic.item.location.same_string ("account/roc-logout") or else + ic.item.location.same_string ("basic_auth_logoff") + then lnk2 := ic.item end end diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index 153305f..cc84735 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -203,7 +203,10 @@ feature -- Hooks until lnk2 /= Void loop - if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then + if + ic.item.location.same_string ("account/roc-logout") or else + ic.item.location.same_string ("basic_auth_logoff") + then lnk2 := ic.item end end diff --git a/modules/session_auth/cms_session_auth_module.e b/modules/session_auth/cms_session_auth_module.e index 52a8db8..52dfaf1 100644 --- a/modules/session_auth/cms_session_auth_module.e +++ b/modules/session_auth/cms_session_auth_module.e @@ -91,7 +91,7 @@ feature {CMS_API} -- Module management l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("session_auth_table.sql")), Void) if l_sql_storage.has_error then - api.logger.put_error ("Could not initialize database for blog module", generating_type) + api.logger.put_error ("Could not initialize database for session auth module", generating_type) end end l_sql_storage.sql_finalize @@ -141,11 +141,11 @@ feature {NONE} -- Implementation: routes l_cookie: WSF_COOKIE do if - attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_cookie_token and then + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_cookie_token and then attached {CMS_USER} current_user (req) as l_user then -- Logout Session - create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_cookie_token.value) + create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_cookie_token.value) l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) @@ -181,8 +181,8 @@ feature {NONE} -- Implementation: routes else l_session_api.new_user_session_auth (l_token, l_user) end - create l_cookie.make ({CMS_SESSION_CONSTANT}.session_auth_token, l_token) - l_cookie.set_max_age (60*60*24*360) + create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_token) + l_cookie.set_max_age ({CMS_SESSION_CONSTANTS}.session_max_age) l_cookie.set_path ("/") res.add_cookie (l_cookie) set_current_user (req, l_user) @@ -219,7 +219,7 @@ feature -- Hooks do if attached a_response.user as u and then - attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) then a_value.force ("account/roc-session-logout", "auth_login_strategy") end @@ -235,7 +235,7 @@ feature -- Hooks do if attached a_response.user as u and then - attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) + attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) then across a_menu_system.primary_menu.items as ic diff --git a/modules/session_auth/cms_session_constant.e b/modules/session_auth/cms_session_constant.e deleted file mode 100644 index 8d44501..0000000 --- a/modules/session_auth/cms_session_constant.e +++ /dev/null @@ -1,13 +0,0 @@ -note - description: "Summary description for {CMS_SESSION_CONSTANT}." - date: "$Date$" - revision: "$Revision$" - -class - CMS_SESSION_CONSTANT - - -feature - session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_" - -- Name of Cookie used to keep the session info. -end diff --git a/modules/session_auth/cms_session_constants.e b/modules/session_auth/cms_session_constants.e new file mode 100644 index 0000000..a50a980 --- /dev/null +++ b/modules/session_auth/cms_session_constants.e @@ -0,0 +1,19 @@ +note + description: "Summary description for {CMS_SESSION_CONSTANTS}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_SESSION_CONSTANTS + + +feature + session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_" + -- Name of Cookie used to keep the session info. + -- TODO add a config file to be able to customize this value via coniguration file. + + session_max_age: INTEGER = 86400 + -- Value of the Max-Age, before the cookie expires. + -- TODO add a config file to be able to customize this value via coniguration file. + +end diff --git a/modules/session_auth/filter/cms_session_auth_filter.e b/modules/session_auth/filter/cms_session_auth_filter.e index dd96337..1acaa53 100644 --- a/modules/session_auth/filter/cms_session_auth_filter.e +++ b/modules/session_auth/filter/cms_session_auth_filter.e @@ -39,7 +39,7 @@ feature -- Basic operations api.logger.put_debug (generator + ".execute ", Void) -- A valid user if - attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANT}.session_auth_token) as l_roc_auth_session_token + attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_roc_auth_session_token then if attached session_oauth_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) diff --git a/modules/session_auth/handler/cms_session_auth_logoff_handler.e b/modules/session_auth/handler/cms_session_auth_logoff_handler.e deleted file mode 100644 index 36a4e19..0000000 --- a/modules/session_auth/handler/cms_session_auth_logoff_handler.e +++ /dev/null @@ -1,132 +0,0 @@ -note - description: "Summary description for {CMS_SESSION_AUTH_LOGOFF_HANDLER}." - date: "$Date$" - revision: "$Revision$" - -class - CMS_SESSION_AUTH_LOGOFF_HANDLER - -inherit - CMS_HANDLER - - WSF_URI_HANDLER - rename - execute as uri_execute, - new_mapping as new_uri_mapping - end - - WSF_RESOURCE_HANDLER_HELPER - redefine - do_get - end - - REFACTORING_HELPER - -create - make - -feature -- execute - - execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute request handler. - do - execute_methods (req, res) - end - - uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute request handler. - do - execute_methods (req, res) - end - -feature -- HTTP Methods - - do_get (req: WSF_REQUEST; res: WSF_RESPONSE) - -- - local - l_page: CMS_RESPONSE - l_url: STRING - i: INTEGER - l_message: STRING - do - api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) - if attached req.query_parameter ("prompt") as l_prompt then - unset_current_user (req) - send_access_denied_message (res) - else - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) - unset_current_user (req) - l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection - l_url := req.absolute_script_url ("") - i := l_url.substring_index ("://", 1) - if i > 0 then - -- Note: this is a hack to have the logout effective on various browser - -- (firefox requires this). - l_url.replace_substring ("://_logout_basic_auth_@", i, i + 2) - end - if - attached req.http_user_agent as l_user_agent and then - browser_name (l_user_agent).is_case_insensitive_equal_general ("Firefox") - then - -- Set status to refirect - -- and redirect to the host page. - l_page.set_status_code ({HTTP_STATUS_CODE}.found) - l_page.set_redirection (l_url) - end - create l_message.make_from_string (logout_message) - l_message.replace_substring_all ("$site_login", req.absolute_script_url ("/account/roc-login")) - l_message.replace_substring_all ("$site_home", req.absolute_script_url ("")) - l_page.set_main_content (l_message) - l_page.execute - end - end - - - browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32 - -- Browser name. - -- Must contain Must not contain - -- Firefox Firefox/xyz Seamonkey/xyz - -- Seamonkey Seamonkey/xyz - -- Chrome Chrome/xyz Chromium/xyz - -- Chromium Chromium/xyz - -- Safari Safari/xyz Chrome/xyz - -- Chromium/xyz - -- Opera OPR/xyz [1] - -- Opera/xyz [2] - -- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format - - do - if - a_user_agent.has_substring ("Firefox") and then - not a_user_agent.has_substring ("Seamonkey") - then - Result := "Firefox" - elseif a_user_agent.has_substring ("Seamonkey") then - Result := "Seamonkey" - elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then - Result := "Chrome" - elseif a_user_agent.has_substring ("Chromium") then - Result := "Chromiun" - elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then - Result := "Safari" - elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then - Result := "Opera" - elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then - Result := "Internet Explorer" - else - Result := "Unknown" - end - end - - - feature {NONE}-- Lougout Message - - logout_message: STRING = "[ -
    -

    You are now signed out

    -

    You can log in again, or go to the front page.

    -
    - ]" - - -end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_sql.e b/modules/session_auth/persistence/cms_session_auth_storage_sql.e index a6b5d26..d472def 100644 --- a/modules/session_auth/persistence/cms_session_auth_storage_sql.e +++ b/modules/session_auth/persistence/cms_session_auth_storage_sql.e @@ -7,6 +7,7 @@ class CMS_SESSION_AUTH_STORAGE_SQL inherit + CMS_SESSION_AUTH_STORAGE_I CMS_PROXY_STORAGE_SQL @@ -26,36 +27,35 @@ feature -- Access User -- Retrieve user by token `a_token', if any. local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset write_information_log (generator + ".user_by_session_token") create l_parameters.make (1) l_parameters.put (a_token, "token") - sql_insert (Select_user_by_token, l_parameters) + sql_query (Select_user_by_token, l_parameters) if not has_error and not sql_after then Result := fetch_user sql_forth if not sql_after then - check no_more_than_one: False end + check + no_more_than_one: False + end Result := Void end end sql_finalize end - has_user_token (a_user: CMS_USER): BOOLEAN -- Has the user `a_user' and associated session token? local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset write_information_log (generator + ".has_user_token") create l_parameters.make (1) l_parameters.put (a_user.id, "uid") - sql_insert (Select_user_token, l_parameters) + sql_query (Select_user_token, l_parameters) if not has_error and not sql_after then if sql_read_integer_64 (1) = 1 then Result := True @@ -66,56 +66,45 @@ feature -- Access User sql_finalize end - - feature -- Change User token new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) - -- Add a new user with oauth2 authentication. - -- . + -- . local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset - sql_begin_transaction - write_information_log (generator + ".new_user_session") create l_parameters.make (3) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - + sql_begin_transaction sql_insert (sql_insert_session_auth, l_parameters) sql_commit_transaction sql_finalize end - update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER ) + update_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER) -- local l_parameters: STRING_TABLE [detachable ANY] - l_string: STRING do error_handler.reset - sql_begin_transaction - write_information_log (generator + ".update_user_session_auth") create l_parameters.make (3) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - - + sql_begin_transaction sql_modify (sql_update_session_auth, l_parameters) sql_commit_transaction sql_finalize end - feature {NONE} -- Implementation -fetch_user: detachable CMS_USER + fetch_user: detachable CMS_USER local l_id: INTEGER_64 l_name: detachable READABLE_STRING_32 @@ -126,7 +115,6 @@ fetch_user: detachable CMS_USER if attached sql_read_string_32 (2) as s and then not s.is_whitespace then l_name := s end - if l_name /= Void then create Result.make (l_name) if l_id > 0 then @@ -135,7 +123,6 @@ fetch_user: detachable CMS_USER elseif l_id > 0 then create Result.make_with_id (l_id) end - if Result /= Void then if attached sql_read_string (3) as l_password then -- FIXME: should we return the password here ??? @@ -148,22 +135,21 @@ fetch_user: detachable CMS_USER Result.set_status (l_status) end else - check expected_valid_user: False end + check + expected_valid_user: False + end end end - feature {NONE} -- SQL statements - Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" - --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. + Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" + --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. - Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" + Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" + Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" - Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" - - - Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" + Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" end diff --git a/modules/session_auth/site/files/js/roc_basic_auth.js b/modules/session_auth/site/files/js/roc_basic_auth.js deleted file mode 100644 index 467bcd4..0000000 --- a/modules/session_auth/site/files/js/roc_basic_auth.js +++ /dev/null @@ -1,325 +0,0 @@ -var ROC_AUTH = ROC_AUTH || { }; - -var loginURL = "/basic_auth_login"; -var logoutURL = "/basic_auth_logoff"; - -var userAgent = navigator.userAgent.toLowerCase(); -var firstLogIn = true; - -ROC_AUTH.login = function() { - var form = document.forms['cms_basic_auth']; - var username = form.username.value; - var password = form.password.value; - //var host = form.host.value; - var origin = window.location.origin + window.location.pathname; - var _login = function(){ - - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", loginURL, true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - delete form; - window.location=window.location.origin; - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.login_with_redirect = function() { - var form = document.forms[2]; - var username = form.username.value; - var password = form.password.value; - var host = form.host.value; - var _login = function(){ - - var redirectURL = form.redirect && form.redirect.value || ""; - - - $("#imgProgressRedirect").show(); - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(loginURL), true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - if (redirectURL === "") { - window.location=host.concat("/"); - } else { - window.location=host.concat(redirectURL); - } - - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.getQueryParameterByName = function (name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -ROC_AUTH.logoff = function(callback){ - var form = document.forms[0]; - var host = form.host.value; - - if (userAgent.indexOf("msie") != -1) { - document.execCommand("ClearAuthenticationCache"); - } - else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - - var request1 = new XMLHttpRequest(); - var request2 = new XMLHttpRequest(); - - //Logout. Tell the server not to return the "WWW-Authenticate" header - request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); - request1.send(""); - request1.onreadystatechange = function(){ - if (request1.readyState == 4) { - - //Sign in with dummy credentials to clear the auth cache - request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request2.send(""); - - request2.onreadystatechange = function(){ - if (request2.readyState == 4) { - if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} - } - } - - } - } - } - else { - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request.send(""); - request.onreadystatechange = function(){ - if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); - } - } - } -}; - - -ROC_AUTH.remove = function (id) -{ - var element = document.getElementById(id); - element.outerHTML = ""; - delete element; - return; -}; - - - -$(document).ready(function() { - - if (typeof String.prototype.contains != 'function') { - String.prototype.contains = function (str){ - return this.indexOf(str) != -1; - }; - } - ROC_AUTH.progressive_loging(); - -}); - - -ROC_AUTH.progressive_loging = function () { - - ROC_AUTH.login_href(); -}; - - -$(document).keypress(function(e) { - if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { - ROC_AUTH.login(); - } -}); - -ROC_AUTH.OnOneClick = function(event) { - event.preventDefault(); - if ( document.forms[0] === undefined ) { - ROC_AUTH.create_form(); - } - return false; -}; - -ROC_AUTH.login_href = function() { - var els = document.getElementsByTagName("a"); - for (var i = 0, l = els.length; i < l; i++) { - var el = els[i]; - if (el.href.contains("/basic_auth_login?destination")) { - loginURL = el.href; - var OneClick = el; - OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); - } - } -}; - - -ROC_AUTH.create_form = function() { - - // Fetching HTML Elements in Variables by ID. - var createform = document.createElement('form'); // Create New Element Form - createform.setAttribute("action", ""); // Setting Action Attribute on Form - createform.setAttribute("method", "post"); // Setting Method Attribute on Form - $("body").append(createform); - - var heading = document.createElement('h2'); // Heading of Form - heading.innerHTML = "Login Form "; - createform.appendChild(heading); - - var line = document.createElement('hr'); // Giving Horizontal Row After Heading - createform.appendChild(line); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var namelabel = document.createElement('label'); // Create Label for Name Field - namelabel.innerHTML = "Username : "; // Set Field Labels - createform.appendChild(namelabel); - - var inputelement = document.createElement('input'); // Create Input Field for UserName - inputelement.setAttribute("type", "text"); - inputelement.setAttribute("name", "username"); - inputelement.setAttribute("required","required"); - createform.appendChild(inputelement); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var passwordlabel = document.createElement('label'); // Create Label for Password Field - passwordlabel.innerHTML = "Password : "; - createform.appendChild(passwordlabel); - - var passwordelement = document.createElement('input'); // Create Input Field for Password. - passwordelement.setAttribute("type", "password"); - passwordelement.setAttribute("name", "password"); - passwordelement.setAttribute("id", "password"); - passwordelement.setAttribute("required","required"); - createform.appendChild(passwordelement); - - - var passwordbreak = document.createElement('br'); - createform.appendChild(passwordbreak); - - - var submitelement = document.createElement('button'); // Append Submit Button - submitelement.setAttribute("type", "button"); - submitelement.setAttribute("onclick", "ROC_AUTH.login();"); - submitelement.innerHTML = "Sign In "; - createform.appendChild(submitelement); - -}; - - -var password = document.getElementById("password"); -var confirm_password = document.getElementById("confirm_password"); - -ROC_AUTH.validatePassword =function(){ - if ((password != null) && (confirm_password != null)) { - if(password.value != confirm_password.value) { - confirm_password.setCustomValidity("Passwords Don't Match"); - } else { - confirm_password.setCustomValidity(''); - } - } -} - -if ((password != null) && (confirm_password != null)) { - password.onchange = ROC_AUTH.validatePassword(); - confirm_password.onkeyup = ROC_AUTH.validatePassword; -} From 090a48eb85b88bbf88d53a916ad487e0eb07304c Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 16 Dec 2015 10:03:35 -0300 Subject: [PATCH 10/14] Updated class CMS_TOKEN_GENERATOR. Remove once in sha1 feature. Updated encoded_base_64 to base_64 --- modules/session_auth/site/cms_token_generator.e | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/session_auth/site/cms_token_generator.e b/modules/session_auth/site/cms_token_generator.e index ba1adb8..d18bd90 100644 --- a/modules/session_auth/site/cms_token_generator.e +++ b/modules/session_auth/site/cms_token_generator.e @@ -58,7 +58,7 @@ feature {NONE} -- Implementation l_array.force (c.item.as_integer_8, i) i := i + 1 end - Result := encode_base_64 (l_array) + Result := base_64 (l_array) end sha1_string (a_str: STRING): STRING @@ -71,14 +71,14 @@ feature {NONE} -- Implementation sha1: SHA1 -- Create a SHA1 object. - once + do create Result.make end feature -- Encoding - encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 + base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 -- Encodes a byte array into a STRING doing base64 encoding. local l_output: SPECIAL [INTEGER_8] @@ -130,7 +130,7 @@ feature -- Encoding l_output [ptr] := char.code.as_integer_8 ptr := ptr + 1 end - Result := "" + create Result.make_empty across l_output as elem loop From 22528315cba321456a1e42e6589727f937d0c13c Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 16 Dec 2015 10:43:21 -0300 Subject: [PATCH 11/14] Removed unneeded file. --- .../session_auth/files/js/roc_basic_auth.js | 325 ------------------ 1 file changed, 325 deletions(-) delete mode 100644 examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js diff --git a/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js b/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js deleted file mode 100644 index 467bcd4..0000000 --- a/examples/demo/site/modules/session_auth/files/js/roc_basic_auth.js +++ /dev/null @@ -1,325 +0,0 @@ -var ROC_AUTH = ROC_AUTH || { }; - -var loginURL = "/basic_auth_login"; -var logoutURL = "/basic_auth_logoff"; - -var userAgent = navigator.userAgent.toLowerCase(); -var firstLogIn = true; - -ROC_AUTH.login = function() { - var form = document.forms['cms_basic_auth']; - var username = form.username.value; - var password = form.password.value; - //var host = form.host.value; - var origin = window.location.origin + window.location.pathname; - var _login = function(){ - - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", loginURL, true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - delete form; - window.location=window.location.origin; - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.login_with_redirect = function() { - var form = document.forms[2]; - var username = form.username.value; - var password = form.password.value; - var host = form.host.value; - var _login = function(){ - - var redirectURL = form.redirect && form.redirect.value || ""; - - - $("#imgProgressRedirect").show(); - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(loginURL), true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - if (redirectURL === "") { - window.location=host.concat("/"); - } else { - window.location=host.concat(redirectURL); - } - - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
    Invalid Credentials
    "; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; -}; - - -ROC_AUTH.getQueryParameterByName = function (name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); -} - -ROC_AUTH.logoff = function(callback){ - var form = document.forms[0]; - var host = form.host.value; - - if (userAgent.indexOf("msie") != -1) { - document.execCommand("ClearAuthenticationCache"); - } - else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - - var request1 = new XMLHttpRequest(); - var request2 = new XMLHttpRequest(); - - //Logout. Tell the server not to return the "WWW-Authenticate" header - request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); - request1.send(""); - request1.onreadystatechange = function(){ - if (request1.readyState == 4) { - - //Sign in with dummy credentials to clear the auth cache - request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request2.send(""); - - request2.onreadystatechange = function(){ - if (request2.readyState == 4) { - if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} - } - } - - } - } - } - else { - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request.send(""); - request.onreadystatechange = function(){ - if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); - } - } - } -}; - - -ROC_AUTH.remove = function (id) -{ - var element = document.getElementById(id); - element.outerHTML = ""; - delete element; - return; -}; - - - -$(document).ready(function() { - - if (typeof String.prototype.contains != 'function') { - String.prototype.contains = function (str){ - return this.indexOf(str) != -1; - }; - } - ROC_AUTH.progressive_loging(); - -}); - - -ROC_AUTH.progressive_loging = function () { - - ROC_AUTH.login_href(); -}; - - -$(document).keypress(function(e) { - if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { - ROC_AUTH.login(); - } -}); - -ROC_AUTH.OnOneClick = function(event) { - event.preventDefault(); - if ( document.forms[0] === undefined ) { - ROC_AUTH.create_form(); - } - return false; -}; - -ROC_AUTH.login_href = function() { - var els = document.getElementsByTagName("a"); - for (var i = 0, l = els.length; i < l; i++) { - var el = els[i]; - if (el.href.contains("/basic_auth_login?destination")) { - loginURL = el.href; - var OneClick = el; - OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); - } - } -}; - - -ROC_AUTH.create_form = function() { - - // Fetching HTML Elements in Variables by ID. - var createform = document.createElement('form'); // Create New Element Form - createform.setAttribute("action", ""); // Setting Action Attribute on Form - createform.setAttribute("method", "post"); // Setting Method Attribute on Form - $("body").append(createform); - - var heading = document.createElement('h2'); // Heading of Form - heading.innerHTML = "Login Form "; - createform.appendChild(heading); - - var line = document.createElement('hr'); // Giving Horizontal Row After Heading - createform.appendChild(line); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var namelabel = document.createElement('label'); // Create Label for Name Field - namelabel.innerHTML = "Username : "; // Set Field Labels - createform.appendChild(namelabel); - - var inputelement = document.createElement('input'); // Create Input Field for UserName - inputelement.setAttribute("type", "text"); - inputelement.setAttribute("name", "username"); - inputelement.setAttribute("required","required"); - createform.appendChild(inputelement); - - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); - - var passwordlabel = document.createElement('label'); // Create Label for Password Field - passwordlabel.innerHTML = "Password : "; - createform.appendChild(passwordlabel); - - var passwordelement = document.createElement('input'); // Create Input Field for Password. - passwordelement.setAttribute("type", "password"); - passwordelement.setAttribute("name", "password"); - passwordelement.setAttribute("id", "password"); - passwordelement.setAttribute("required","required"); - createform.appendChild(passwordelement); - - - var passwordbreak = document.createElement('br'); - createform.appendChild(passwordbreak); - - - var submitelement = document.createElement('button'); // Append Submit Button - submitelement.setAttribute("type", "button"); - submitelement.setAttribute("onclick", "ROC_AUTH.login();"); - submitelement.innerHTML = "Sign In "; - createform.appendChild(submitelement); - -}; - - -var password = document.getElementById("password"); -var confirm_password = document.getElementById("confirm_password"); - -ROC_AUTH.validatePassword =function(){ - if ((password != null) && (confirm_password != null)) { - if(password.value != confirm_password.value) { - confirm_password.setCustomValidity("Passwords Don't Match"); - } else { - confirm_password.setCustomValidity(''); - } - } -} - -if ((password != null) && (confirm_password != null)) { - password.onchange = ROC_AUTH.validatePassword(); - confirm_password.onkeyup = ROC_AUTH.validatePassword; -} From bbbdac12c8b293c26f2db72d80e81700bb42f798 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 16 Dec 2015 21:03:03 +0100 Subject: [PATCH 12/14] Moved taxonomy html generation to CMS_TAXONOMY_API. --- .../handler/cms_node_type_webform_manager.e | 25 ++------------- modules/taxonomy/cms_taxonomy_api.e | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/modules/node/handler/cms_node_type_webform_manager.e b/modules/node/handler/cms_node_type_webform_manager.e index db5aac4..10e866d 100644 --- a/modules/node/handler/cms_node_type_webform_manager.e +++ b/modules/node/handler/cms_node_type_webform_manager.e @@ -334,30 +334,9 @@ feature -- Output if a_response /= Void and then - attached {CMS_TAXONOMY_API} cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api and then - attached l_taxonomy_api.vocabularies_for_type (content_type.name) as vocs and then not vocs.is_empty + attached {CMS_TAXONOMY_API} cms_api.module_api ({CMS_TAXONOMY_MODULE}) as l_taxonomy_api then - vocs.sort - across - vocs as ic - loop - if - attached l_taxonomy_api.terms_of_content (a_node, ic.item) as l_terms and then - not l_terms.is_empty - then - a_output.append ("
      ") - a_output.append (l_node_api.html_encoded (ic.item.name)) - a_output.append (": ") - across - l_terms as t_ic - loop - a_output.append ("
    • ") - a_response.append_link_to_html (t_ic.item.text, "taxonomy/term/" + t_ic.item.id.out, Void, a_output) - a_output.append ("
    • ") - end - a_output.append ("
    %N") - end - end + l_taxonomy_api.append_taxonomy_to_xhtml (a_node, a_response, a_output) end -- We don't show the summary on the detail page, since its just a short view of the full content. Otherwise we would write the same thing twice. diff --git a/modules/taxonomy/cms_taxonomy_api.e b/modules/taxonomy/cms_taxonomy_api.e index 5c9ffe6..7fec959 100644 --- a/modules/taxonomy/cms_taxonomy_api.e +++ b/modules/taxonomy/cms_taxonomy_api.e @@ -488,6 +488,37 @@ feature -- Web forms end end + append_taxonomy_to_xhtml (a_content: CMS_CONTENT; a_response: CMS_RESPONSE; a_output: STRING) + -- Append taxonomy related to `a_content' to xhtml string `a_output', + -- using `a_response' helper routines. + do + if + attached vocabularies_for_type (a_content.content_type) as vocs and then not vocs.is_empty + then + vocs.sort + across + vocs as ic + loop + if + attached terms_of_content (a_content, ic.item) as l_terms and then + not l_terms.is_empty + then + a_output.append ("
      ") + a_output.append (cms_api.html_encoded (ic.item.name)) + a_output.append (": ") + across + l_terms as t_ic + loop + a_output.append ("
    • ") + a_response.append_link_to_html (t_ic.item.text, "taxonomy/term/" + t_ic.item.id.out, Void, a_output) + a_output.append ("
    • ") + end + a_output.append ("
    %N") + end + end + end + end + feature -- Helpers splitted_string (s: READABLE_STRING_32; sep: CHARACTER): LIST [READABLE_STRING_32] From e7c9a54f3ff7b31d56b5e061808358164cb31161 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 16 Dec 2015 21:03:15 +0100 Subject: [PATCH 13/14] Removed unused local. --- modules/session_auth/cms_session_auth_module.e | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/session_auth/cms_session_auth_module.e b/modules/session_auth/cms_session_auth_module.e index 52dfaf1..cf097d4 100644 --- a/modules/session_auth/cms_session_auth_module.e +++ b/modules/session_auth/cms_session_auth_module.e @@ -81,8 +81,6 @@ feature {CMS_API} -- Module Initialization feature {CMS_API} -- Module management install (api: CMS_API) - local - l_consumers: LIST [STRING] do -- Schema if attached api.storage.as_sql_storage as l_sql_storage then From 0813abe0bb1cd8c52e5693b2a9007c46ddfa6e66 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Dec 2015 15:29:43 +0100 Subject: [PATCH 14/14] Fixed ROC CMS library compilation. --- src/service/content/cms_partial_content.e | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/service/content/cms_partial_content.e b/src/service/content/cms_partial_content.e index 8b114b0..4c37ea1 100644 --- a/src/service/content/cms_partial_content.e +++ b/src/service/content/cms_partial_content.e @@ -26,6 +26,9 @@ feature {NONE} -- Initialization feature -- Access + identifier: detachable READABLE_STRING_32 + -- + title: detachable READABLE_STRING_32 -- Title associated with Current content. @@ -42,6 +45,15 @@ feature -- Access feature -- Element change + set_identifier (a_identifier: detachable READABLE_STRING_GENERAL) + do + if a_identifier = Void then + identifier := Void + else + create {IMMUTABLE_STRING_32} identifier.make_from_string_general (a_identifier) + end + end + set_title (a_title: detachable READABLE_STRING_GENERAL) do if a_title = Void then