From 72cfd1d6520854b9550e7808998cd07f38af5f78 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 30 Mar 2017 22:05:35 -0300 Subject: [PATCH 1/4] Initial import Google Custom Search 2.0 Updated demo to use it. --- examples/demo/demo.ecf | 1 + examples/demo/roc.cfg | 1 + .../config/google_search_20.json | 6 + .../templates/block_search.tpl | 7 + examples/demo/site/themes/bootstrap/page.tpl | 2 +- examples/demo/src/demo_cms_execution.e | 2 +- modules/google_search_20/Readme.md | 5 + .../google_search_20-safe.ecf | 20 +++ modules/google_search_20/google_search_20.ecf | 19 +++ .../site/config/google_search_20.json | 6 + .../site/templates/block_search.tpl | 7 + .../src/google_custom_search_module_20.e | 142 ++++++++++++++++++ 12 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 examples/demo/site/modules/google_search_20/config/google_search_20.json create mode 100644 examples/demo/site/modules/google_search_20/templates/block_search.tpl create mode 100644 modules/google_search_20/Readme.md create mode 100644 modules/google_search_20/google_search_20-safe.ecf create mode 100644 modules/google_search_20/google_search_20.ecf create mode 100644 modules/google_search_20/site/config/google_search_20.json create mode 100644 modules/google_search_20/site/templates/block_search.tpl create mode 100644 modules/google_search_20/src/google_custom_search_module_20.e diff --git a/examples/demo/demo.ecf b/examples/demo/demo.ecf index dcbec04..d58c22b 100644 --- a/examples/demo/demo.ecf +++ b/examples/demo/demo.ecf @@ -32,6 +32,7 @@ + diff --git a/examples/demo/roc.cfg b/examples/demo/roc.cfg index f44a5c3..e8ec181 100644 --- a/examples/demo/roc.cfg +++ b/examples/demo/roc.cfg @@ -12,6 +12,7 @@ "contact": { "location": "../../modules/contact" }, "feed_aggregator": { "location": "../../modules/feed_aggregator" }, "google_search": { "location": "../../modules/google_search" }, + "google_search_20": { "location": "../../modules/google_search_20" }, "node": { "location": "../../modules/node" }, "oauth20": { "location": "../../modules/oauth20" }, "openid": { "location": "../../modules/openid" }, diff --git a/examples/demo/site/modules/google_search_20/config/google_search_20.json b/examples/demo/site/modules/google_search_20/config/google_search_20.json new file mode 100644 index 0000000..9b7ab8c --- /dev/null +++ b/examples/demo/site/modules/google_search_20/config/google_search_20.json @@ -0,0 +1,6 @@ +{ + "gcse": { + "cx":"", + "secret_key":"" + } +} diff --git a/examples/demo/site/modules/google_search_20/templates/block_search.tpl b/examples/demo/site/modules/google_search_20/templates/block_search.tpl new file mode 100644 index 0000000..056ad79 --- /dev/null +++ b/examples/demo/site/modules/google_search_20/templates/block_search.tpl @@ -0,0 +1,7 @@ +
+
+

Results for {$cms_search_query/}

+
+ + +
diff --git a/examples/demo/site/themes/bootstrap/page.tpl b/examples/demo/site/themes/bootstrap/page.tpl index 47c331c..785cf06 100644 --- a/examples/demo/site/themes/bootstrap/page.tpl +++ b/examples/demo/site/themes/bootstrap/page.tpl @@ -40,7 +40,7 @@
-
+
diff --git a/examples/demo/src/demo_cms_execution.e b/examples/demo/src/demo_cms_execution.e index 4dc81a2..14bc6fb 100644 --- a/examples/demo/src/demo_cms_execution.e +++ b/examples/demo/src/demo_cms_execution.e @@ -86,7 +86,7 @@ feature -- CMS modules -- Miscellanious a_setup.register_module (create {CMS_MESSAGING_MODULE}.make) - a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE}.make) + a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE_20}.make) a_setup.register_module (create {CMS_CUSTOM_BLOCK_MODULE}.make) a_setup.register_module (create {CMS_DEBUG_MODULE}.make) a_setup.register_module (create {CMS_DEMO_MODULE}.make) diff --git a/modules/google_search_20/Readme.md b/modules/google_search_20/Readme.md new file mode 100644 index 0000000..3aaf7a9 --- /dev/null +++ b/modules/google_search_20/Readme.md @@ -0,0 +1,5 @@ +Google Custom Search Module 2.0. + +https://developers.google.com/custom-search/docs/element +The current solution is based on +Results only diff --git a/modules/google_search_20/google_search_20-safe.ecf b/modules/google_search_20/google_search_20-safe.ecf new file mode 100644 index 0000000..5e0ece5 --- /dev/null +++ b/modules/google_search_20/google_search_20-safe.ecf @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/google_search_20/google_search_20.ecf b/modules/google_search_20/google_search_20.ecf new file mode 100644 index 0000000..4c86d8f --- /dev/null +++ b/modules/google_search_20/google_search_20.ecf @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/modules/google_search_20/site/config/google_search_20.json b/modules/google_search_20/site/config/google_search_20.json new file mode 100644 index 0000000..9b7ab8c --- /dev/null +++ b/modules/google_search_20/site/config/google_search_20.json @@ -0,0 +1,6 @@ +{ + "gcse": { + "cx":"", + "secret_key":"" + } +} diff --git a/modules/google_search_20/site/templates/block_search.tpl b/modules/google_search_20/site/templates/block_search.tpl new file mode 100644 index 0000000..8126280 --- /dev/null +++ b/modules/google_search_20/site/templates/block_search.tpl @@ -0,0 +1,7 @@ +
+
+

Results for {$result.current_page.search_terms/}

+
+ + +
diff --git a/modules/google_search_20/src/google_custom_search_module_20.e b/modules/google_search_20/src/google_custom_search_module_20.e new file mode 100644 index 0000000..f297886 --- /dev/null +++ b/modules/google_search_20/src/google_custom_search_module_20.e @@ -0,0 +1,142 @@ +note + description: "[ + Module providing Google Custom Search functionality. + ]" + date: "$Date$" + revision: "$Revision$" + +class + GOOGLE_CUSTOM_SEARCH_MODULE_20 + +inherit + + CMS_MODULE + redefine + setup_hooks + end + + CMS_HOOK_AUTO_REGISTER + + REFACTORING_HELPER + + CMS_HOOK_RESPONSE_ALTER + + CMS_HOOK_BLOCK_HELPER + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + + REFACTORING_HELPER + + SHARED_LOGGER + +create + make + +feature {NONE} -- Initialization + + make + -- Create current module + do + version := "2.0" + description := "Google custome search module 2.0" + package := "search" + end + +feature -- Access + + name: STRING = "google_search_20" + -- + +feature -- Router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- Router configuration. + local + m: WSF_URI_MAPPING + do + create m.make_trailing_slash_ignored ("/gcse20", create {WSF_URI_AGENT_HANDLER}.make (agent handle_search (a_api, ?, ?))) + a_router.map (m, a_router.methods_head_get) + end + +feature -- GCSE Keys + + gcse_cx_key (api: CMS_API): detachable READABLE_STRING_8 + -- Get google custom search cx key. + local + utf: UTF_CONVERTER + do + if attached api.module_configuration (Current, Void) as cfg then + if + attached cfg.text_item ("gcse.cx") as l_gcse_cx_key and then + not l_gcse_cx_key.is_empty + then + Result := utf.utf_32_string_to_utf_8_string_8 (l_gcse_cx_key) + end + end + end + +feature -- Handler + + handle_search (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + do + write_debug_log (generator + ".handle_search") + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if + attached {WSF_STRING} req.query_parameter ("q") as l_query and then + not l_query.value.is_empty + then + if + attached gcse_cx_key (api) as l_cx + then + r.set_value (l_query.value, "cms_search_query") + if + attached smarty_template_block (Current, "search", api) as l_tpl_block + then + r.add_block (l_tpl_block, "content") + end + end + else + r.add_message ("No query submitted", Void) + end + r.execute + end + + +feature -- Hooks configuration + + setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) + -- Module hooks configuration. + do + auto_subscribe_to_hooks (a_hooks) + end + + response_alter (a_response: CMS_RESPONSE) + -- + local + l_script: STRING + do + if attached gcse_cx_key (a_response.api) as l_ctx then + create l_script.make_from_string (gcse_20_script) + l_script.replace_substring_all ("#CX_VALUE", l_ctx) + a_response.add_javascript_content (l_script) + end + end + + +feature {NONE} -- Implementation + + gcse_20_script: STRING = "[ + (function() { + var cx = '#CX_VALUE'; + var gcse = document.createElement('script'); gcse.type = 'text/javascript'; gcse.async = true; + gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + + '//www.google.com/cse/cse.js?cx=' + cx; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gcse, s); + })(); + ]" +end From 1eae3b7413a85e8d1b0e8ab6ca10984228620bcf Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 3 Apr 2017 11:34:02 -0300 Subject: [PATCH 2/4] Removed secret key from google search version 2.0 since is not needed. --- .../modules/google_search_20/config/google_search_20.json | 3 +-- modules/google_search_20/site/config/google_search_20.json | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/demo/site/modules/google_search_20/config/google_search_20.json b/examples/demo/site/modules/google_search_20/config/google_search_20.json index 9b7ab8c..9cf9e80 100644 --- a/examples/demo/site/modules/google_search_20/config/google_search_20.json +++ b/examples/demo/site/modules/google_search_20/config/google_search_20.json @@ -1,6 +1,5 @@ { "gcse": { - "cx":"", - "secret_key":"" + "cx":"" } } diff --git a/modules/google_search_20/site/config/google_search_20.json b/modules/google_search_20/site/config/google_search_20.json index 9b7ab8c..7403693 100644 --- a/modules/google_search_20/site/config/google_search_20.json +++ b/modules/google_search_20/site/config/google_search_20.json @@ -1,6 +1,5 @@ { "gcse": { - "cx":"", - "secret_key":"" - } + "cx":"" + } } From ad2b30f52b2daa785180f5c78ddd58cee8a4cdfd Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 6 Apr 2017 14:02:20 +0200 Subject: [PATCH 3/4] Changed the way search modules get integrated with the theme via a new region "search". Improved current module for google search 2.0. --- .../config/google_search_20.json | 5 -- .../config/google_search_20.json-dist | 2 +- .../templates/block_gcse_search_form.tpl | 6 +++ ...arch.tpl => block_gcse_search_results.tpl} | 0 examples/demo/site/themes/bootstrap/page.tpl | 9 ++-- .../demo/site/themes/bootstrap/search.tpl | 6 +++ .../demo/site/themes/bootstrap/theme.info | 1 + modules/google_search_20/Readme.md | 14 ++++-- .../site/config/google_search_20.json-dist | 5 ++ .../site/templates/block_gcse_search_form.tpl | 6 +++ ...arch.tpl => block_gcse_search_results.tpl} | 2 +- .../src/google_custom_search_module_20.e | 50 ++++++++++++------- 12 files changed, 72 insertions(+), 34 deletions(-) delete mode 100644 examples/demo/site/modules/google_search_20/config/google_search_20.json rename modules/google_search_20/site/config/google_search_20.json => examples/demo/site/modules/google_search_20/config/google_search_20.json-dist (51%) create mode 100644 examples/demo/site/modules/google_search_20/templates/block_gcse_search_form.tpl rename examples/demo/site/modules/google_search_20/templates/{block_search.tpl => block_gcse_search_results.tpl} (100%) create mode 100644 examples/demo/site/themes/bootstrap/search.tpl create mode 100644 modules/google_search_20/site/config/google_search_20.json-dist create mode 100644 modules/google_search_20/site/templates/block_gcse_search_form.tpl rename modules/google_search_20/site/templates/{block_search.tpl => block_gcse_search_results.tpl} (58%) diff --git a/examples/demo/site/modules/google_search_20/config/google_search_20.json b/examples/demo/site/modules/google_search_20/config/google_search_20.json deleted file mode 100644 index 9cf9e80..0000000 --- a/examples/demo/site/modules/google_search_20/config/google_search_20.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "gcse": { - "cx":"" - } -} diff --git a/modules/google_search_20/site/config/google_search_20.json b/examples/demo/site/modules/google_search_20/config/google_search_20.json-dist similarity index 51% rename from modules/google_search_20/site/config/google_search_20.json rename to examples/demo/site/modules/google_search_20/config/google_search_20.json-dist index 7403693..ea94f8a 100644 --- a/modules/google_search_20/site/config/google_search_20.json +++ b/examples/demo/site/modules/google_search_20/config/google_search_20.json-dist @@ -1,5 +1,5 @@ { "gcse": { - "cx":"" + "search_engine_id":"" } } diff --git a/examples/demo/site/modules/google_search_20/templates/block_gcse_search_form.tpl b/examples/demo/site/modules/google_search_20/templates/block_gcse_search_form.tpl new file mode 100644 index 0000000..4542f09 --- /dev/null +++ b/examples/demo/site/modules/google_search_20/templates/block_gcse_search_form.tpl @@ -0,0 +1,6 @@ + +
+ + +
+ diff --git a/examples/demo/site/modules/google_search_20/templates/block_search.tpl b/examples/demo/site/modules/google_search_20/templates/block_gcse_search_results.tpl similarity index 100% rename from examples/demo/site/modules/google_search_20/templates/block_search.tpl rename to examples/demo/site/modules/google_search_20/templates/block_gcse_search_results.tpl diff --git a/examples/demo/site/themes/bootstrap/page.tpl b/examples/demo/site/themes/bootstrap/page.tpl index 785cf06..e31018d 100644 --- a/examples/demo/site/themes/bootstrap/page.tpl +++ b/examples/demo/site/themes/bootstrap/page.tpl @@ -37,17 +37,14 @@ {$page.primary_nav/} {/if}
+ {if isset="$page.regions.search"}
-
-
- - -
-
+ {$page.regions.search/}
+ {/if}
diff --git a/examples/demo/site/themes/bootstrap/search.tpl b/examples/demo/site/themes/bootstrap/search.tpl new file mode 100644 index 0000000..1cedbb4 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/search.tpl @@ -0,0 +1,6 @@ +
+
+ + +
+
diff --git a/examples/demo/site/themes/bootstrap/theme.info b/examples/demo/site/themes/bootstrap/theme.info index 6c96570..9ba80a7 100644 --- a/examples/demo/site/themes/bootstrap/theme.info +++ b/examples/demo/site/themes/bootstrap/theme.info @@ -4,6 +4,7 @@ author=jvelilla version=0.1 regions[page_top] = Top regions[header] = Header +regions[search] = Search regions[content] = Content regions[highlighted] = Highlighted regions[help] = Help diff --git a/modules/google_search_20/Readme.md b/modules/google_search_20/Readme.md index 3aaf7a9..71df20c 100644 --- a/modules/google_search_20/Readme.md +++ b/modules/google_search_20/Readme.md @@ -1,5 +1,13 @@ Google Custom Search Module 2.0. -https://developers.google.com/custom-search/docs/element -The current solution is based on -Results only +# Based on Google Custom Search Engine (CSE) +- https://developers.google.com/custom-search/docs/element +- Search form provides a parameter `q` for the search query. +- Results are displayed in associated page /gcse2 (using ) + +# Settings +- edit the `site/modules/google_search_20/config/google_search_20.json` file, and set the value `gcse.search_engine_id`. + +# How to get a Custom Search engine ID? +- See https://support.google.com/customsearch/answer/2649143?hl=en +- https://cse.google.com/all diff --git a/modules/google_search_20/site/config/google_search_20.json-dist b/modules/google_search_20/site/config/google_search_20.json-dist new file mode 100644 index 0000000..ea94f8a --- /dev/null +++ b/modules/google_search_20/site/config/google_search_20.json-dist @@ -0,0 +1,5 @@ +{ + "gcse": { + "search_engine_id":"" + } +} diff --git a/modules/google_search_20/site/templates/block_gcse_search_form.tpl b/modules/google_search_20/site/templates/block_gcse_search_form.tpl new file mode 100644 index 0000000..4542f09 --- /dev/null +++ b/modules/google_search_20/site/templates/block_gcse_search_form.tpl @@ -0,0 +1,6 @@ +
+
+ + +
+
diff --git a/modules/google_search_20/site/templates/block_search.tpl b/modules/google_search_20/site/templates/block_gcse_search_results.tpl similarity index 58% rename from modules/google_search_20/site/templates/block_search.tpl rename to modules/google_search_20/site/templates/block_gcse_search_results.tpl index 8126280..056ad79 100644 --- a/modules/google_search_20/site/templates/block_search.tpl +++ b/modules/google_search_20/site/templates/block_gcse_search_results.tpl @@ -1,6 +1,6 @@
-

Results for {$result.current_page.search_terms/}

+

Results for {$cms_search_query/}

diff --git a/modules/google_search_20/src/google_custom_search_module_20.e b/modules/google_search_20/src/google_custom_search_module_20.e index f297886..2c85e12 100644 --- a/modules/google_search_20/src/google_custom_search_module_20.e +++ b/modules/google_search_20/src/google_custom_search_module_20.e @@ -9,7 +9,6 @@ class GOOGLE_CUSTOM_SEARCH_MODULE_20 inherit - CMS_MODULE redefine setup_hooks @@ -19,7 +18,7 @@ inherit REFACTORING_HELPER - CMS_HOOK_RESPONSE_ALTER + CMS_HOOK_BLOCK CMS_HOOK_BLOCK_HELPER @@ -64,13 +63,13 @@ feature -- Router feature -- GCSE Keys gcse_cx_key (api: CMS_API): detachable READABLE_STRING_8 - -- Get google custom search cx key. + -- Get google custom search engine id. local utf: UTF_CONVERTER do if attached api.module_configuration (Current, Void) as cfg then if - attached cfg.text_item ("gcse.cx") as l_gcse_cx_key and then + attached cfg.text_item ("gcse.search_engine_id") as l_gcse_cx_key and then not l_gcse_cx_key.is_empty then Result := utf.utf_32_string_to_utf_8_string_8 (l_gcse_cx_key) @@ -83,6 +82,8 @@ feature -- Handler handle_search (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE + l_script: STRING + b: CMS_CONTENT_BLOCK do write_debug_log (generator + ".handle_search") create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) @@ -95,9 +96,16 @@ feature -- Handler then r.set_value (l_query.value, "cms_search_query") if - attached smarty_template_block (Current, "search", api) as l_tpl_block + attached smarty_template_block (Current, "gcse_search_results", api) as l_tpl_block then r.add_block (l_tpl_block, "content") + + if attached gcse_cx_key (api) as l_ctx then + create l_script.make_from_string (gcse_20_script) + l_script.replace_substring_all ("#CX_VALUE", l_ctx) + create b.make_raw ("gcse2_js", Void, "", Void) + r.add_block (b, "footer") + end end end else @@ -106,7 +114,6 @@ feature -- Handler r.execute end - feature -- Hooks configuration setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) @@ -115,18 +122,25 @@ feature -- Hooks configuration auto_subscribe_to_hooks (a_hooks) end - response_alter (a_response: CMS_RESPONSE) - -- - local - l_script: STRING + block_list: ITERABLE [like {CMS_BLOCK}.name] do - if attached gcse_cx_key (a_response.api) as l_ctx then - create l_script.make_from_string (gcse_20_script) - l_script.replace_substring_all ("#CX_VALUE", l_ctx) - a_response.add_javascript_content (l_script) - end + Result := <<"gcse_search_form">> end + get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + do + if a_block_id.is_case_insensitive_equal_general ("gcse_search_form") then + if a_response.request.is_get_request_method then + if attached smarty_template_block (Current, a_block_id, a_response.api) as l_tpl_block then + a_response.add_block (l_tpl_block, "search") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + end feature {NONE} -- Implementation @@ -134,9 +148,9 @@ feature {NONE} -- Implementation (function() { var cx = '#CX_VALUE'; var gcse = document.createElement('script'); gcse.type = 'text/javascript'; gcse.async = true; - gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + - '//www.google.com/cse/cse.js?cx=' + cx; - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gcse, s); + gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//www.google.com/cse/cse.js?cx=' + cx; + var s = document.getElementsByTagName('script')[0]; + s.parentNode.insertBefore(gcse, s); })(); ]" end From 5915af6a9c3566cc7736e5fd25a0e41b9921efde Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 6 Apr 2017 15:33:10 +0200 Subject: [PATCH 4/4] Added masquerade dev module and disabled it by default. - it allows to login as a given user by passing security check. - it must be used only during development! - disabled by default! Updated the session auth module to make it easier to be reused. - masquerade module is based on the session auth module. --- dev_modules/masquerade_auth/masquerade_api.e | 87 ++++++ .../masquerade_auth/masquerade_auth-safe.ecf | 29 ++ .../masquerade_auth/masquerade_auth_module.e | 268 ++++++++++++++++++ examples/demo/demo.ecf | 2 +- examples/demo/roc.cfg | 3 +- examples/demo/site/config/cms.ini | 4 + examples/demo/src/demo_cms_execution.e | 3 + modules/session_auth/cms_session_api.e | 75 +++++ .../session_auth/cms_session_auth_module.e | 80 ++---- 9 files changed, 490 insertions(+), 61 deletions(-) create mode 100644 dev_modules/masquerade_auth/masquerade_api.e create mode 100644 dev_modules/masquerade_auth/masquerade_auth-safe.ecf create mode 100644 dev_modules/masquerade_auth/masquerade_auth_module.e diff --git a/dev_modules/masquerade_auth/masquerade_api.e b/dev_modules/masquerade_auth/masquerade_api.e new file mode 100644 index 0000000..b7bc732 --- /dev/null +++ b/dev_modules/masquerade_auth/masquerade_api.e @@ -0,0 +1,87 @@ +note + description: "API to manage CMS User session authentication" + date: "$Date$" + revision: "$Revision$" + +class + MASQUERADE_API + +inherit + CMS_AUTH_API_I + + REFACTORING_HELPER + +create {MASQUERADE_AUTH_MODULE} + make_with_session_api + +feature {NONE} -- Initialization + + make_with_session_api (a_api: CMS_API; a_session_api: CMS_SESSION_API) + do + session_api := a_session_api + make (a_api) + end + +feature -- Access + + session_api: CMS_SESSION_API + +feature -- Status report + + has_permission_to_masquerade (a_user: detachable CMS_USER): BOOLEAN + local + v: STRING + do + if attached cms_api.setup.string_8_item_or_default ("dev.masquerade", "permission") as s then + v := s + v.left_adjust + v.right_adjust + if v.is_case_insensitive_equal_general ("none") then + elseif v.is_case_insensitive_equal_general ("all") then + Result := True + elseif v.is_case_insensitive_equal_general ("permission") then + Result := cms_api.user_has_permission (a_user, "masquerade") + else + -- no! + end + end + end + + is_authenticating (a_response: CMS_RESPONSE): BOOLEAN + do + if + a_response.is_authenticated and then + attached a_response.request.cookie (session_api.session_token) + then + Result := True + end + end + +feature -- Basic operation + + process_user_login (a_user: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE) + do + session_api.process_user_login (a_user, req, res) + end + + process_user_logout (a_user: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE) + do + session_api.process_user_logout (a_user, req, res) + end + +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 + + +end diff --git a/dev_modules/masquerade_auth/masquerade_auth-safe.ecf b/dev_modules/masquerade_auth/masquerade_auth-safe.ecf new file mode 100644 index 0000000..7aac9ee --- /dev/null +++ b/dev_modules/masquerade_auth/masquerade_auth-safe.ecf @@ -0,0 +1,29 @@ + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + + + + + + + + diff --git a/dev_modules/masquerade_auth/masquerade_auth_module.e b/dev_modules/masquerade_auth/masquerade_auth_module.e new file mode 100644 index 0000000..ca8a1c1 --- /dev/null +++ b/dev_modules/masquerade_auth/masquerade_auth_module.e @@ -0,0 +1,268 @@ +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 + MASQUERADE_AUTH_MODULE + +inherit + CMS_AUTH_MODULE_I + rename + module_api as masquerade_api + redefine + make, + setup_hooks, + initialize, + install, + permissions, + masquerade_api, + menu_system_alter + end + + CMS_HOOK_BLOCK + + CMS_HOOK_MENU_SYSTEM_ALTER + +create + make + +feature {NONE} -- Initialization + + make + do + Precursor + version := "1.0" + description := "Service to easily log as user at development time" + package := "debug" + disable -- Disabled by default + add_dependency ({CMS_SESSION_AUTH_MODULE}) + end + +feature -- Access + + name: STRING = "masquerade_auth" + + permissions: LIST [READABLE_STRING_8] + -- List of permission ids, used by this module, and declared. + do + Result := Precursor + Result.extend ("masquerade") + end + +feature {CMS_API} -- Module Initialization + + initialize (a_api: CMS_API) + -- + do + Precursor (a_api) + + if attached {CMS_SESSION_API} a_api.module_api ({CMS_SESSION_AUTH_MODULE}) as l_session_api then + -- API initialization + create masquerade_api.make_with_session_api (a_api, l_session_api) + end + end + +feature {CMS_API} -- Module management + + install (api: CMS_API) + do + Precursor {CMS_AUTH_MODULE_I} (api) -- Mark it as installed. + end + +feature {CMS_API} -- Access: API + + masquerade_api: detachable MASQUERADE_API + -- + +feature -- Access: auth strategy + + login_title: STRING = "Masquerade" + -- Module specific login title. + + login_location: STRING = "account/auth/roc-masquerade-login" + + logout_location: STRING = "account/auth/roc-masquerade-logout" + + is_authenticating (a_response: CMS_RESPONSE): BOOLEAN + -- + do + if attached masquerade_api as l_masquerade_api then + Result := l_masquerade_api.is_authenticating (a_response) + end + end + +feature -- Access: router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- + do + if attached masquerade_api as l_masquerade_api then + a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) + a_router.handle ("/" + logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_masquerade_api, ?, ?)), a_router.methods_get_post) + a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_masquerade (a_api, l_masquerade_api,?, ?)), a_router.methods_post) + 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) + if + attached masquerade_api as l_masquerade_api and then + l_masquerade_api.has_permission_to_masquerade (api.user) + then + if api.user_is_authenticated then + r.add_warning_message ("You are signed.") + end + r.add_block (login_block ("login", Void, r), "content") + else + r.add_error_message ("You are not allowed to use masquerade authentication!") + end + r.execute + end + + handle_logout (api: CMS_API; a_masquerade_api: MASQUERADE_API ; req: WSF_REQUEST; res: WSF_RESPONSE) local + r: CMS_RESPONSE + do + if attached api.user as l_user then + a_masquerade_api.process_user_logout (l_user, req, res) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + else + -- Not loggued in ... redirect to home + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + end + if + attached {WSF_STRING} req.item ("destination") as p_destination and then + attached p_destination.value as v and then + v.is_valid_as_string_8 + then + r.set_redirection (v.to_string_8) + else + r.set_redirection (req.absolute_script_url ("")) + end + + r.execute + end + + handle_login_with_masquerade (api: CMS_API; a_masquerade_api: MASQUERADE_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + do + if a_masquerade_api.has_permission_to_masquerade (api.user) then + if + attached {WSF_STRING} req.form_parameter ("username") as l_username + then + if + attached api.user_api.user_by_name (l_username.value) as l_user + then + a_masquerade_api.process_user_login (l_user, req, res) + + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if + attached {WSF_STRING} req.item ("destination") as p_destination and then + attached p_destination.value as v and then + v.is_valid_as_string_8 + then + r.set_redirection (v.to_string_8) + else + r.set_redirection ("") + end + else + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.add_block (login_block ("login", Void, r), "content") + end + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.add_block (login_block ("login", "Wrong username", r), "content") + end + else + create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api) + end + r.execute + end + +feature -- Hooks configuration + + setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) + -- Module hooks configuration. + do + Precursor (a_hooks) + a_hooks.subscribe_to_block_hook (Current) + a_hooks.subscribe_to_menu_system_alter_hook (Current) + end + +feature -- Hooks + + block_list: ITERABLE [like {CMS_BLOCK}.name] + do + Result := <<"?login">> + 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") then + a_response.add_block (login_block (a_block_id, Void, a_response), "content") + 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 + u: detachable CMS_USER + do + u := a_response.api.user + if + attached masquerade_api as l_masquerade_api and then + l_masquerade_api.has_permission_to_masquerade (u) + then + Precursor (a_menu_system, a_response) + if u /= Void then + a_menu_system.navigation_menu.extend (a_response.local_link ("Masquerade", login_location)) + end + end + end + +feature {NONE} -- Block views + + login_block (a_block_id: READABLE_STRING_8; a_err: detachable READABLE_STRING_8; a_response: CMS_RESPONSE): CMS_CONTENT_BLOCK + do + create Result.make (a_block_id, Void, login_html (a_err, a_response), Void) + end + + login_html (a_err: detachable READABLE_STRING_8; a_response: CMS_RESPONSE): STRING + local + params: CMS_DATA_QUERY_PARAMETERS + u: CMS_USER + do + create Result.make_from_string ("
") + if a_err /= Void then + Result.append ("
") + Result.append (a_err) + Result.append ("
") + end + Result.append ("
%N") + Result.append ("
") + Result.append ("") + + create params.make (0, a_response.api.user_api.users_count.as_natural_32) + across + a_response.api.user_api.recent_users (params) as ic + loop + u := ic.item + Result.append ("
  • ") + Result.append (a_response.html_encoded (a_response.api.user_display_name (u))) + Result.append ("
  • %N") + end + Result.append ("
    ") + end + +end diff --git a/examples/demo/demo.ecf b/examples/demo/demo.ecf index d58c22b..ffc4e67 100644 --- a/examples/demo/demo.ecf +++ b/examples/demo/demo.ecf @@ -31,7 +31,6 @@ - @@ -42,6 +41,7 @@ + diff --git a/examples/demo/roc.cfg b/examples/demo/roc.cfg index e8ec181..9f2d2b8 100644 --- a/examples/demo/roc.cfg +++ b/examples/demo/roc.cfg @@ -26,6 +26,7 @@ "embedded_video": { "location": "../../modules/embedded_video" }, "wikitext": { "location": "../../modules/wikitext" }, "messaging": { "location": "../../modules/messaging" }, - "comments": { "location": "../../modules/comments" } + "comments": { "location": "../../modules/comments" }, + "masquerade": { "location": "../../dev_modules/masquerade" } } } diff --git a/examples/demo/site/config/cms.ini b/examples/demo/site/config/cms.ini index 03c4f01..0414897 100644 --- a/examples/demo/site/config/cms.ini +++ b/examples/demo/site/config/cms.ini @@ -63,3 +63,7 @@ base_path=/roc-admin #theme=admin # CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none) installation_access=all + +[dev] +# masquerade: all, none, permission. Default is none. +masquerade=none diff --git a/examples/demo/src/demo_cms_execution.e b/examples/demo/src/demo_cms_execution.e index 14bc6fb..71613e4 100644 --- a/examples/demo/src/demo_cms_execution.e +++ b/examples/demo/src/demo_cms_execution.e @@ -91,6 +91,9 @@ feature -- CMS modules a_setup.register_module (create {CMS_DEBUG_MODULE}.make) a_setup.register_module (create {CMS_DEMO_MODULE}.make) + -- Dev + a_setup.register_module (create {MASQUERADE_AUTH_MODULE}.make) + end end diff --git a/modules/session_auth/cms_session_api.e b/modules/session_auth/cms_session_api.e index ac02417..6def2a0 100644 --- a/modules/session_auth/cms_session_api.e +++ b/modules/session_auth/cms_session_api.e @@ -57,6 +57,18 @@ feature -- Settings session_max_age: INTEGER -- Value of the Max-Age, before the cookie expires. +feature -- Status report + + is_authenticating (a_response: CMS_RESPONSE): BOOLEAN + do + if + a_response.is_authenticated and then + attached a_response.request.cookie (session_token) + then + Result := True + end + end + feature -- Access user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER @@ -71,6 +83,47 @@ feature -- Access Result := session_auth_storage.has_user_token (a_user) end +feature -- Basic operation + + process_user_login (a_user: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_token: STRING + l_cookie: WSF_COOKIE + do + l_token := new_session_token + if has_user_token (a_user) then + update_user_session_auth (l_token, a_user) + else + new_user_session_auth (l_token, a_user) + end + create l_cookie.make (session_token, l_token) + l_cookie.set_max_age (session_max_age) + l_cookie.set_path ("/") + res.add_cookie (l_cookie) + cms_api.set_user (a_user) + cms_api.record_user_login (a_user) + end + + process_user_logout (a_user: CMS_USER; req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_cookie: WSF_COOKIE + do + if + attached session_token as tok and then + attached {WSF_STRING} req.cookie (tok) as l_cookie_token + then + -- Logout Session + create l_cookie.make (tok, "") -- l_cookie_token.value) -- FIXME: unicode issue? + l_cookie.set_path ("/") + l_cookie.unset_max_age + l_cookie.set_expiration_date (create {DATE_TIME}.make_from_epoch (0)) + res.add_cookie (l_cookie) + else + -- it seems the user was not login, as there is no associated cookie! + end + cms_api.unset_user + end + feature -- Change User session new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) @@ -86,4 +139,26 @@ feature -- Change User session session_auth_storage.update_user_session_auth (a_token, a_user) end +feature -- Token + + new_session_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_auth_module.e b/modules/session_auth/cms_session_auth_module.e index b9f4e5b..9abc865 100644 --- a/modules/session_auth/cms_session_auth_module.e +++ b/modules/session_auth/cms_session_auth_module.e @@ -100,11 +100,9 @@ feature -- Access: auth strategy -- do if - a_response.is_authenticated and then - attached session_api as l_session_api and then - attached a_response.request.cookie (l_session_api.session_token) + attached session_api as l_session_api then - Result := True + Result := l_session_api.is_authenticating (a_response) end end @@ -116,7 +114,7 @@ feature -- Access: router if attached session_api as l_session_api then a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) a_router.handle ("/" + logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_session_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,session_api, ?, ?)), a_router.methods_post) + a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_session (a_api, l_session_api, ?, ?)), a_router.methods_post) end end @@ -164,39 +162,33 @@ feature {NONE} -- Implementation: routes handle_logout (api: CMS_API; a_session_api: CMS_SESSION_API ; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_cookie: WSF_COOKIE - tok: STRING do - tok := a_session_api.session_token - if - attached {WSF_STRING} req.cookie (tok) as l_cookie_token and then - attached api.user as l_user - then - -- Logout Session - create l_cookie.make (tok, "") -- l_cookie_token.value) -- FIXME: unicode issue? - l_cookie.set_path ("/") - l_cookie.unset_max_age - l_cookie.set_expiration_date (create {DATE_TIME}.make_from_epoch (0)) - res.add_cookie (l_cookie) - api.unset_user - + if attached api.user as l_user then + a_session_api.process_user_logout (l_user, req, res) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + else + -- Not loggued in ... redirect to home 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 + if + attached {WSF_STRING} req.item ("destination") as p_destination and then + attached p_destination.value as v and then + v.is_valid_as_string_8 + then + r.set_redirection (v.to_string_8) + else + r.set_redirection (req.absolute_script_url ("")) + end + + r.execute end - handle_login_with_session (api: CMS_API; a_session_api: detachable CMS_SESSION_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_login_with_session (api: CMS_API; a_session_api: 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 then @@ -204,18 +196,7 @@ feature {NONE} -- Implementation: routes 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 (a_session_api.session_token, l_token) - l_cookie.set_max_age (a_session_api.session_max_age) - l_cookie.set_path ("/") - res.add_cookie (l_cookie) - api.set_user (l_user) - api.record_user_login (l_user) + a_session_api.process_user_login (l_user, req, res) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if @@ -295,23 +276,4 @@ feature {NONE} -- Block views 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