Compare commits

..

23 Commits

Author SHA1 Message Date
28ab4786a1 Provided a CMS_EMAIL, and CMS_API.process_email (CMS_EMAIL) 2015-06-30 16:11:49 +02:00
8294a47f17 Added usage of notification_email library.
Added CMS_SETUP.mailer
Updated implementation of email_service to use notification_email library
2015-06-30 15:53:02 +02:00
e45dac84c8 Removing unused local variables.
Fixed .ecf location for cms related libraries.
2015-06-29 18:42:11 +02:00
bb3e3b992f Merge branch 'ewf_v1_roctool' into ewf_v1 2015-06-29 17:30:39 +02:00
ebc5924c01 Made CMS_MODULE.name deferred, and implemented by constant so that it can be use as static call.
Copied site resources on related module source folder.
Renamed "login" module as "auth" module, and updated related locations and files.
2015-06-29 16:24:17 +02:00
48b0ad5195 Merge remote-tracking branch 'jvelilla/roc_tool' into ewf_v1_roctool 2015-06-26 11:25:12 +02:00
ae9eea99dd Integrate Authentication modules.
Updated code for sql that should not use parameters
   to expand :table_name in table name usage,
   since Eiffel Store will use quote,
   and MySQL does not like them.

Merge remote-tracking branch 'jvelilla/roc_auth_v1' into ewf_v1_mod_env

Conflicts:
	examples/demo/demo-safe.ecf
	modules/auth/cms_authentication_module.e
2015-06-25 23:20:51 +02:00
jvelilla
268f53e53f Fixed UUID for Oauth20 module. 2015-06-25 15:22:31 -03:00
jvelilla
e17fc570a1 Updated Demo with the new OAuth20 module
Added basic example to extend CMS Authentication using Smarty templates.
2015-06-25 13:20:04 -03:00
ba7ef17d34 Adapted to new layout, with module files inside site/modules/$module_name/... 2015-06-25 17:55:09 +02:00
jvelilla
c8bbac664b Initial commit, added new module oauth20. 2015-06-25 10:07:06 -03:00
04e98dbb48 Merge remote-tracking branch 'ewf/ewf_v1' into ewf_v1 2015-06-24 18:51:33 +02:00
2886c90782 Moved all location related queries into CMS_API, instead of CMS_SETUP.
Note that CMS_SETUP provides locations set by default or from configuration file.
Now theme related resources can be found under site/modules/$mod_name/... or site/themes/$theme/modules/...
  so only theme related resources can be overriden for now.
2015-06-24 17:15:05 +02:00
jvelilla
db6799d55b Merge branch 'jvelilla-roc_ewf_v1_email' into ewf_v1 2015-06-24 11:21:52 -03:00
jvelilla
7c0032ada4 Updated CMS:
Extract email service as a library.
Updated modules to use the email library.
Fixed compilation issue with database_connection_null.e
2015-06-24 11:17:17 -03:00
jvelilla
fa5efede2c Merge branch 'jocelyn-roc_auth_20150619' into ewf_v1 2015-06-23 15:30:01 -03:00
0fca03a4d1 Improved Authentication module code.
Updated to match recent changes from cypress the OAuth Eiffel library.
2015-06-22 21:47:06 +02:00
642b901856 Make sure CMS_BLOCK knows about page variables values.
For now only for smarty template blocks.
2015-06-19 22:48:20 +02:00
4f3bcf290f Fixing issue with index. 2015-06-19 17:58:09 +02:00
6ca8a9ce82 Fixed parts of SQL statements handling (mostly for SQL script execution). 2015-06-19 17:42:02 +02:00
2c72fe6738 Renamed login module as auth (authentication) module 2015-06-19 11:48:42 +02:00
149de898c0 Updated location for cypress the OAuth Eiffel lib.
prepare login module renaming to auth module.
2015-06-19 11:44:41 +02:00
jvelilla
a94a8857ae Initial Import ROC tool 2015-06-17 20:45:28 -03:00
95 changed files with 2414 additions and 769 deletions

View File

@@ -21,6 +21,7 @@
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email-safe.ecf"/>
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty-safe.ecf" readonly="false"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>

View File

@@ -22,6 +22,7 @@
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="i18n" location="$ISE_LIBRARY\library\i18n\i18n.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email.ecf"/>
<library name="smarty" location="$ISE_LIBRARY\contrib\library\text\template\smarty\smarty.ecf" readonly="false"/>
<library name="text_filter" location="$ISE_LIBRARY\unstable\library\text\text_filter\text_filter.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>

View File

@@ -1,81 +1,84 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<description>Example/demo for Eiffel ROC CMS library</description>
<target name="common" abstract="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
<library name="cms_blog_module" location="modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_login_module" location="..\..\modules\login\login-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" readonly="false"/>
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target>
<target name="demo_any" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi-safe.ecf"/>
<library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi-safe.ecf"/>
<library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino-safe.ecf"/>
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_standalone" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<option debug="true">
<debug name="dbglog" enabled="true"/>
</option>
<variable name="httpd_ssl_disabled" value="true"/><!-- for now ... due to issue with libcurl+eiffelnet ssl -->
<setting name="concurrency" value="thread"/>
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_standalone_none" extends="demo_standalone">
<setting name="concurrency" value="none"/>
</target>
<target name="demo_standalone_mt" extends="demo_standalone">
<setting name="concurrency" value="thread"/>
</target>
<target name="demo_standalone_scoop" extends="demo_standalone">
<setting name="concurrency" value="scoop"/>
</target>
<target name="demo_nino" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_cgi" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_libfcgi" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo" extends="demo_standalone">
</target>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="demo" uuid="3643E657-BCBE-46AA-931B-71EAEA877A18" library_target="demo">
<description>Example/demo for Eiffel ROC CMS library</description>
<target name="common" abstract="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_auth_module" location="..\..\modules\auth\auth-safe.ecf" readonly="false"/>
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
<library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-safe.ecf" readonly="false"/>
<library name="cms_blog_module" location="modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<!--
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" readonly="false"/>
-->
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf" readonly="false"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target>
<target name="demo_any" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<library name="cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\cgi-safe.ecf"/>
<library name="libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\libfcgi-safe.ecf"/>
<library name="nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\nino-safe.ecf"/>
<library name="standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\connector\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\any\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_standalone" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<option debug="true">
<debug name="dbglog" enabled="true"/>
</option>
<setting name="concurrency" value="thread"/>
<variable name="httpd_ssl_disabled" value="true"/>
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_standalone_none" extends="demo_standalone">
<setting name="concurrency" value="none"/>
</target>
<target name="demo_standalone_mt" extends="demo_standalone">
<setting name="concurrency" value="thread"/>
</target>
<target name="demo_standalone_scoop" extends="demo_standalone">
<setting name="concurrency" value="scoop"/>
</target>
<target name="demo_nino" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_nino" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\nino-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_cgi" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_cgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\cgi-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo_libfcgi" extends="common">
<root class="EWF_ROC_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="none"/>
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="demo" extends="demo_standalone">
</target>
</system>

View File

@@ -27,12 +27,15 @@ feature {NONE} -- Initialization
make
do
name := "Blog demo module"
version := "1.0"
description := "Service to demonstrate new node for blog"
package := "demo"
end
feature -- Access
name: STRING = "blog"
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
@@ -74,7 +77,7 @@ CREATE TABLE blog_post_nodes(
`tags` VARCHAR(255)
);
]"
l_sql_storage.sql_execute_script (sql)
l_sql_storage.sql_execute_script (sql, Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for blog module", generating_type)
end
@@ -117,18 +120,18 @@ feature -- Access: router
-- Let the class BLOG_HANDLER handle the requests on "/blogs"
create l_uri_mapping.make_trailing_slash_ignored ("/blogs", l_blog_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
a_router.map (l_uri_mapping, a_router.methods_get)
-- We can add a page number after /blogs/ to get older posts
a_router.handle_with_request_methods ("/blogs/page/{page}", l_blog_handler, a_router.methods_get)
a_router.handle ("/blogs/page/{page}", l_blog_handler, a_router.methods_get)
-- If a user id is given route with blog user handler
--| FIXME: maybe /user/{user}/blogs/ would be better.
a_router.handle_with_request_methods ("/blogs/user/{user}", l_blog_user_handler, a_router.methods_get)
a_router.handle ("/blogs/user/{user}", l_blog_user_handler, a_router.methods_get)
-- If a user id is given we also want to allow different pages
--| FIXME: what about /user/{user}/blogs/?page={page} ?
a_router.handle_with_request_methods ("/blogs/user/{user}/page/{page}", l_blog_user_handler, a_router.methods_get)
a_router.handle ("/blogs/user/{user}/page/{page}", l_blog_user_handler, a_router.methods_get)
end

View File

@@ -86,8 +86,6 @@ feature -- Query
-- User id from path /blogs/{user}.
-- Unsigned integer since negative ids are not allowed.
-- If no valid id can be read it returns -1
local
s: STRING
do
Result := -1
if attached {WSF_STRING} req.path_parameter ("user") as l_user_id then

View File

@@ -27,12 +27,15 @@ feature {NONE} -- Initialization
make
do
name := "Demo module"
version := "1.0"
description := "Service to demonstrate and test cms system"
package := "demo"
end
feature -- Access
name: STRING = "demo"
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
@@ -69,7 +72,7 @@ CREATE TABLE tb_demo(
`value` TEXT
);
]"
l_sql_storage.sql_execute_script (sql)
l_sql_storage.sql_execute_script (sql, Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for demo module", generating_type)
end
@@ -83,8 +86,8 @@ feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api))
map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api))
map_uri_template_agent (a_router, "/demo/", agent handle_demo (?,?,a_api), Void)
map_uri_template_agent (a_router, "/demo/{id}", agent handle_demo_entry (?,?,a_api), Void)
end
feature -- Hooks
@@ -144,42 +147,24 @@ feature -- Handler
feature -- Mapping helper: uri template
map_uri_template (a_router: WSF_ROUTER; a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER)
-- Map `h' as handler for `a_tpl'
require
a_tpl_attached: a_tpl /= Void
h_attached: h /= Void
do
map_uri_template_with_request_methods (a_router, a_tpl, h, Void)
end
map_uri_template_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS)
map_uri_template (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `h' as handler for `a_tpl' for request methods `rqst_methods'.
require
a_tpl_attached: a_tpl /= Void
h_attached: h /= Void
do
a_router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods)
a_router.map (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods)
end
feature -- Mapping helper: uri template agent
map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]])
-- Map `proc' as handler for `a_tpl'
require
a_tpl_attached: a_tpl /= Void
proc_attached: proc /= Void
do
map_uri_template_agent_with_request_methods (a_router, a_tpl, proc, Void)
end
map_uri_template_agent_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS)
map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'.
require
a_tpl_attached: a_tpl /= Void
proc_attached: proc /= Void
do
map_uri_template_with_request_methods (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods)
map_uri_template (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods)
end
end

View File

@@ -1,11 +1,14 @@
[layout]
root-dir=site/www
themes-dir=site/themes
#themes-dir=site/themes
#modules-dir=site/modules
[site]
name=Eiffel CMS
email=your@email.com
theme=bootstrap
[misc]
smtp=localhost
[mailer]
smtp=localhost:25
#sendmail=/usr/bin/sendmail
#output=@stderr

View File

@@ -1,6 +1,6 @@
<div class="primary-tabs">
{unless isset="$user"}
<h3>Login or <a href="/account/roc-register">Register</a></h3>
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div>
<div>
<form action method="POST">
@@ -21,14 +21,14 @@
<div>
<div>
<p>
<a href="/account/new-password">Forgot password?</a>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div>
<div>
{foreach item="item" from="$oauth_consumers"}
<a href="/account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
{/foreach}
</div>
{/unless}
</div>
</div>

View File

@@ -1,5 +1,5 @@
CREATE TABLE `oauth2_consumers`(
CREATE TABLE oauth2_consumers(
`cid` INTEGER PRIMARY KEY NOT NULL CHECK(`cid`>=0),
`name` VARCHAR(255) NOT NULL,
`api_secret` TEXT NOT NULL,

View File

@@ -0,0 +1,7 @@
-- Change the values TO_COMPLETE based on your API.
-- API SECTET KEY AND API PUBLIC KEY
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint)
VALUES ('google', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', 'callback_google', 'json','https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://accounts.google.com/o/oauth2/token');
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint )
VALUES ('facebook', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', 'callback_facebook','text','https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://graph.facebook.com/oauth/access_token');

View File

@@ -1,5 +1,5 @@
CREATE TABLE :table_name (
CREATE TABLE $table_name (
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
`access_token` TEXT NOT NULL,
`created` DATETIME NOT NULL,

View File

@@ -0,0 +1,7 @@
<div class="primary-tabs">
<div>
{foreach item="item" from="$oauth_consumers"}
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
{/foreach}
</div>
</div>

View File

@@ -1,7 +0,0 @@
-- Change the values `TO_COMPLETE` based on your API.
-- API SECTET KEY AND API PUBLIC KEY
INSERT INTO `oauth2_consumers` ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint")
VALUES ("google", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', "callback_google", "json","https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://accounts.google.com/o/oauth2/token");
INSERT INTO "oauth2_consumers" ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint" )
VALUES ("facebook", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', "callback_facebook","text","https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://graph.facebook.com/oauth/access_token");

View File

@@ -30,19 +30,19 @@ CREATE TABLE `role_permissions`(
`module` VARCHAR(255)
);
CREATE TABLE "users_activations" (
"aid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("aid" >= 0),
"token" VARCHAR(255) NOT NULL,
"uid" INTEGER NOT NULL CHECK ("uid" >= 0),
"created" DATETIME NOT NULL,
CONSTRAINT "token" UNIQUE ("token")
CREATE TABLE `users_activations` (
`aid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK (`aid` >= 0),
`token` VARCHAR(255) NOT NULL,
`uid` INTEGER NOT NULL CHECK (`uid` >= 0),
`created` DATETIME NOT NULL,
CONSTRAINT `token` UNIQUE (`token`)
);
CREATE TABLE "users_password_recovery" (
"aid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("aid" >= 0),
"token" VARCHAR(255) NOT NULL,
"uid" INTEGER NOT NULL CHECK ("uid" >= 0),
"created" DATETIME NOT NULL,
CONSTRAINT "token" UNIQUE ("token")
CREATE TABLE `users_password_recovery` (
`aid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK (`aid` >= 0),
`token` VARCHAR(255) NOT NULL,
`uid` INTEGER NOT NULL CHECK (`uid` >= 0),
`created` DATETIME NOT NULL,
CONSTRAINT `token` UNIQUE (`token`)
);

View File

@@ -69,6 +69,10 @@ feature -- CMS setup
a_setup.register_module (m)
end
create {CMS_OAUTH_20_MODULE} m.make
m.enable
a_setup.register_module (m)
create {CMS_DEBUG_MODULE} m.make
m.enable
a_setup.register_module (m)

View File

@@ -109,7 +109,7 @@ feature -- Access: internal
do
p := internal_application_config_path
if p = Void then
p := config_path.extended ("application_configuration.json")
p := config_path.extended (name + ".json")
internal_application_config_path := p
end
Result := p

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="email_service" uuid="261DC9D5-C3A0-498D-A063-0BB6C80423CC" library_target="email_service">
<target name="email_service">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms_app_env" location="..\app_env\app_env-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="notification_mailer" location="$ISE_LIBRARY\contrib\library\runtime\process\notification_email\notification_email-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -7,8 +7,8 @@ class
EMAIL_SERVICE
inherit
SHARED_ERROR
SHARED_LOGGER
create
@@ -31,9 +31,7 @@ feature {NONE} -- Initialization
do
admin_email := parameters.admin_email
-- Get local host name needed in creation of SMTP_PROTOCOL.
create l_address_factory
create smtp_protocol.make (parameters.smtp_server, l_address_factory.create_localhost.host_name)
create {NOTIFICATION_SMTP_MAILER} mailer.make (parameters.smtp_server)
set_successful
end
@@ -43,7 +41,7 @@ feature {NONE} -- Initialization
admin_email: IMMUTABLE_STRING_8
-- Site admin's email.
smtp_protocol: SMTP_PROTOCOL
mailer: NOTIFICATION_MAILER
-- SMTP protocol.
feature -- Basic Operations
@@ -60,38 +58,34 @@ feature -- Basic Operations
send_message (a_from_address, a_to_address: READABLE_STRING_8; a_subjet: READABLE_STRING_GENERAL; a_content: READABLE_STRING_GENERAL)
local
l_email: EMAIL
l_email: NOTIFICATION_EMAIL
utf: UTF_CONVERTER
do
write_debug_log (generator + ".send_message: [from:" + a_from_address + ", to:" + a_to_address + ", subject:" + a_subjet + ", content:" + a_content)
create l_email.make_with_entry (a_from_address, a_to_address)
l_email.set_message (utf.escaped_utf_32_string_to_utf_8_string_8 (a_content))
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet))
l_email.add_header_entry ("MIME-Version:", "1.0")
l_email.add_header_entry ("Content-Type", "text/html; charset=utf-8")
create l_email.make (a_from_address, a_to_address, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet) , utf.escaped_utf_32_string_to_utf_8_string_8 (a_content))
l_email.add_header_line ("MIME-Version:1.0")
l_email.add_header_line ("Content-Type: text/html; charset=utf-8")
send_email (l_email)
end
feature {NONE} -- Implementation
send_email (a_email: EMAIL)
send_email (a_email: NOTIFICATION_EMAIL)
-- Send the email represented by `a_email'.
local
l_retried: BOOLEAN
do
if not l_retried then
write_information_log (generator + ".send_email Process send email.")
smtp_protocol.initiate_protocol
smtp_protocol.transfer (a_email)
smtp_protocol.close_protocol
mailer.process_email (a_email)
write_information_log (generator + ".send_email Email sent.")
if smtp_protocol.error then
if mailer.has_error then
set_last_error ("smtp_protocol reported an error", generator + ".send_email")
else
set_successful
end
else
write_error_log (generator + ".send_email Email not send " + last_error_message )
write_error_log (generator + ".send_email Email not send " + last_error_message)
end
rescue
set_last_error_from_exception (generator + ".send_email")

View File

@@ -23,9 +23,9 @@ feature -- Initialization
-- Create a database handler for ODBC with common settings.
do
create database_error_handler.make
create db_application.login (username, password)
db_application.set_hostname (hostname)
db_application.set_data_source (database_name)
create db_application.login (default_username, default_password)
db_application.set_hostname (default_username)
db_application.set_data_source (default_database_name)
db_application.set_base
create db_control.make
end

3
license.lic Normal file
View File

@@ -0,0 +1,3 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="login" uuid="AAB9EE7D-A671-4727-8658-D417A48B2B57" library_target="login">
<target name="login">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="auth_module" uuid="AAB9EE7D-A671-4727-8658-D417A48B2B57" library_target="auth_module">
<target name="auth_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
@@ -10,18 +10,22 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="$ISE_LIBRARY\unstable\library\web\cms\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="$ISE_LIBRARY\unstable\library\web\cms\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_model" location="$ISE_LIBRARY\unstable\library\web\cms\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="config" location="$ISE_LIBRARY\unstable\library\web\cms\library\configuration\config-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="config" location="..\..\library\configuration\config-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\communication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\communication\oauth\cypress\consumer\cypress_consumer-safe.ecf" readonly="false"/>
<library name="email_service" location="..\..\library\email\email-safe.ecf"/>
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer-safe.ecf" readonly="false"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>

View File

@@ -21,7 +21,7 @@ feature {NONE} -- Initialization
s: detachable READABLE_STRING_32
l_contact_email, l_subject_register, l_subject_activate, l_subject_password, l_subject_oauth: detachable READABLE_STRING_8
do
setup := a_cms_api.setup
cms_api := a_cms_api
-- Use global smtp setting if any, otherwise "localhost"
smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost"))
l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name)
@@ -31,7 +31,7 @@ feature {NONE} -- Initialization
admin_email := l_site_name + " <" + admin_email +">"
end
if attached {CONFIG_READER} a_cms_api.module_configuration ("login", Void) as cfg then
if attached {CONFIG_READER} a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
if attached cfg.text_item ("smtp") as l_smtp then
-- Overwrite global smtp setting if any.
smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp)
@@ -92,6 +92,8 @@ feature {NONE} -- Initialization
feature -- Access
cms_api: CMS_API
smtp_server: IMMUTABLE_STRING_8
admin_email: IMMUTABLE_STRING_8
@@ -104,74 +106,67 @@ feature -- Access
contact_subject_password: IMMUTABLE_STRING_8
contact_subject_oauth: IMMUTABLE_STRING_8
account_activation: STRING
-- Account activation template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_activation.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_activation)
end
Result := template_string ("account_activation.html", default_template_account_activation)
end
account_re_activation: STRING
-- Account re_activation template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("accunt_re_activation.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_re_activation)
end
Result := template_string ("accunt_re_activation.html", default_template_account_re_activation)
end
account_password: STRING
-- Account password template email message.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_new_password.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_new_password)
end
Result := template_string ("account_new_password.html", default_template_account_new_password)
end
account_welcome: STRING
-- Account welcome template email message.
do
Result := template_string ("account_welcome.html", default_template_account_welcome)
end
feature {NONE} -- Implementation: Template
template_path (a_name: READABLE_STRING_GENERAL): PATH
-- Location of template named `a_name'.
local
p: PATH
do
p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_welcome.html")
create p.make_from_string (a_name)
Result := cms_api.module_location_by_name ({CMS_AUTHENTICATION_MODULE}.name).extended ("mail_templates").extended (a_name)
end
template_string (a_name: READABLE_STRING_GENERAL; a_default: STRING): STRING
-- Content of template named `a_name', or `a_default' if template is not found.
local
p: PATH
do
p := template_path ("account_activation.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (template_account_welcome)
create Result.make_from_string (a_default)
end
end
feature {NONE} -- Implementation
setup: CMS_SETUP
read_template_file (a_path: PATH): detachable STRING
-- Read the content of the file at path `a_path'.
local
l_file: FILE
n: INTEGER
do
create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path)
if l_file.exists and then l_file.is_readable then
n := l_file.count
l_file.open_read
l_file.read_stream (l_file.count)
l_file.read_stream (n)
Result := l_file.last_string
l_file.close
else
@@ -182,7 +177,7 @@ feature {NONE} -- Implementation
feature {NONE} -- Message email
template_account_activation: STRING= "[
default_template_account_activation: STRING = "[
<!doctype html>
<html lang="en">
<head>
@@ -195,7 +190,7 @@ feature {NONE} -- Message email
<body>
<p>Thank you for registering at <a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p>To complete your registration, please click on the following link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
@@ -204,7 +199,7 @@ feature {NONE} -- Message email
]"
template_account_re_activation: STRING= "[
default_template_account_re_activation: STRING = "[
<!doctype html>
<html lang="en">
<head>
@@ -215,9 +210,9 @@ feature {NONE} -- Message email
</head>
<body>
<p>You have request a new activation token at<a href="...">ROC CMS</a></p>
<p>You have requested a new activation token at <a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p>To complete your registration, please click on the following link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
@@ -227,7 +222,7 @@ feature {NONE} -- Message email
template_account_new_password: STRING= "[
default_template_account_new_password: STRING = "[
<!doctype html>
<html lang="en">
<head>
@@ -240,7 +235,7 @@ feature {NONE} -- Message email
<body>
<p>You have required a new password at <a href="...">ROC CMS</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p>
<p>To complete your request, please click on this link to generate a new password:<p>
<p><a href="$link">$link</a></p>
</body>
@@ -248,7 +243,7 @@ feature {NONE} -- Message email
]"
template_account_welcome: STRING= "[
default_template_account_welcome: STRING = "[
<!doctype html>
<html lang="en">
<head>

View File

@@ -1,5 +1,5 @@
note
description: "Module Logging supporting different authentication strategies"
description: "Module Auth"
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $"
revision: "$Revision: 97328 $"
@@ -8,18 +8,10 @@ class
inherit
CMS_MODULE
rename
module_api as user_oauth_api
redefine
filters,
register_hooks,
initialize,
is_installed,
install,
user_oauth_api
register_hooks
end
CMS_HOOK_BLOCK
CMS_HOOK_AUTO_REGISTER
@@ -39,7 +31,6 @@ inherit
CMS_REQUEST_UTIL
create
make
@@ -48,112 +39,17 @@ feature {NONE} -- Initialization
make
-- Create current module
do
name := "login"
version := "1.0"
description := "Eiffel login module"
package := "login"
description := "Authentication module"
package := "authentication"
create root_dir.make_current
cache_duration := 0
end
feature {CMS_API} -- Module Initialization
feature -- Access
initialize (a_api: CMS_API)
-- <Precursor>
local
l_user_auth_api: like user_oauth_api
l_user_auth_storage: CMS_OAUTH_20_STORAGE_I
do
Precursor (a_api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage
end
-- Node API initialization
create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage)
user_oauth_api := l_user_auth_api
ensure then
user_oauth_api_set: user_oauth_api /= Void
end
feature {CMS_API} -- Module management
is_installed (api: CMS_API): BOOLEAN
-- Is Current module installed?
do
Result := attached api.storage.custom_value ("is_initialized", "module-" + name) as v and then v.is_case_insensitive_equal_general ("yes")
end
install (api: CMS_API)
local
l_setup: CMS_SETUP
l_params: detachable STRING_TABLE [detachable ANY]
l_consumers: LIST [STRING]
do
l_setup := api.setup
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("oauth2_consumers") then
--| Schema
l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers.sql"))
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for blog module", generating_type)
end
-- TODO workaround.
l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers_initialize.sql"))
end
-- 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)
else
from
l_sql_storage.sql_start
create {ARRAYED_LIST[STRING]} l_consumers.make (2)
until
l_sql_storage.sql_after
loop
if attached l_sql_storage.sql_read_string (1) as l_name then
l_consumers.force ("oauth2_"+l_name)
end
l_sql_storage.sql_forth
end
across l_consumers as ic loop
if not l_sql_storage.sql_table_exists (ic.item) then
create l_params.make (1)
l_params.force (ic.item, "table_name")
l_sql_storage.sql_execute_file_script_with_params (l_setup.environment.path.extended ("scripts").extended ("oauth2_template.sql"), l_params)
end
end
end
api.storage.set_custom_value ("is_initialized", "module-" + name, "yes")
end
end
feature {CMS_API} -- Access: API
user_oauth_api: detachable CMS_OAUTH_20_API
-- <Precursor>
feature -- Filters
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_oauth_api as l_user_oauth_api then
Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api))
end
end
name: STRING = "auth"
feature -- Access: docs
@@ -172,30 +68,24 @@ feature -- Access: docs
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
if attached user_oauth_api as l_user_oauth_api then
configure_web (a_api, l_user_oauth_api, a_router)
end
configure_web (a_api, a_router)
end
configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER)
configure_web (a_api: CMS_API; a_router: WSF_ROUTER)
do
a_router.handle ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/roc-basic-auth", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_basic_auth (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api,a_user_oauth_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post)
end
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
@@ -229,46 +119,55 @@ feature -- Hooks
end
a_menu_system.primary_menu.extend (lnk)
lnk.set_weight (98)
if a_response.location.starts_with ("account/roc-login") then
create lnk.make ("Basic Auth", "account/roc-basic-auth")
lnk.set_expandable (True)
a_response.add_to_primary_tabs (lnk)
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do
Result := <<"login","register","reactivate","new_password", "reset_password">>
create l_string.make_empty
across Result as ic loop
Result := <<"login", "register", "reactivate", "new_password", "reset_password">>
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 )
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.request.path_info.starts_with ("/account/roc-login")
a_response.location.starts_with ("account/roc-basic-auth")
then
get_block_view_login (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("register") and then
a_response.request.path_info.starts_with ("/account/roc-register")
a_response.location.starts_with ("account/roc-register")
then
get_block_view_register (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("reactivate") and then
a_response.request.path_info.starts_with ("/account/reactivate")
a_response.location.starts_with ("account/reactivate")
then
get_block_view_reactivate (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("new_password") and then
a_response.request.path_info.starts_with ("/account/new-password")
a_response.location.starts_with ("account/new-password")
then
get_block_view_new_password (a_block_id, a_response)
elseif
a_block_id.is_case_insensitive_equal_general ("reset_password") and then
a_response.request.path_info.starts_with ("/account/reset-password")
a_response.location.starts_with ("account/reset-password")
then
get_block_view_reset_password (a_block_id, a_response)
end
@@ -283,34 +182,25 @@ feature -- Hooks
r.execute
end
handle_login_basic_auth (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.set_value ("Basic Auth", "optional_content_type")
r.execute
end
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_url: STRING
l_cookie: WSF_COOKIE
do
if
attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_cookie_token and then
attached {CMS_USER} current_user (req) as l_user
then
-- Logout gmail
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, 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
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found)
l_url := req.absolute_script_url ("")
l_url.append ("/basic_auth_logoff")
r.set_redirection (l_url)
r.execute
end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found)
l_url := req.absolute_script_url ("/basic_auth_logoff")
r.set_redirection (l_url)
r.execute
end
handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
@@ -321,7 +211,7 @@ feature -- Hooks
l_roles: LIST [CMS_USER_ROLE]
l_exist: BOOLEAN
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_link: STRING
l_url: STRING
l_token: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
@@ -359,15 +249,12 @@ feature -- Hooks
-- Create activation token
l_token := new_token
l_user_api.new_activation (l_token, u.id)
create l_link.make_from_string (req.server_url)
l_link.append ("/account/activate/")
l_link.append (l_token)
l_url := req.absolute_script_url ("/account/activate/" + l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_email")
es.send_contact_email (l_email.value, l_link)
es.send_contact_email (l_email.value, l_url)
else
r.values.force (l_name.value, "name")
@@ -401,8 +288,7 @@ feature -- Hooks
-- the token does not exist, or it was already used.
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.set_value ("Account not activated", "optional_content_type")
r.set_main_content ("<p>The token <i>"+ l_token.value +"</i> is not valid <a href=%"/account/reactivate%">Reactivate Account</a></p>" )
r.set_main_content ("<p>The token <i>" + l_token.value +"</i> is not valid " + r.link ("Reactivate Account", "account/reactivate", Void) + "</p>")
end
r.execute
else
@@ -418,7 +304,7 @@ feature -- Hooks
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_user_api: CMS_USER_API
l_token: STRING
l_link: STRING
l_url: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if req.is_post_request_method then
@@ -434,14 +320,12 @@ feature -- Hooks
else
l_token := new_token
l_user_api.new_activation (l_token, l_user.id)
create l_link.make_from_string (req.server_url)
l_link.append ("/account/activate/")
l_link.append (l_token)
l_url := req.absolute_script_url ("/account/activate/" + l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_activation_email")
es.send_contact_activation_email (l_email.value, l_link)
es.send_contact_activation_email (l_email.value, l_url)
end
else
r.values.force ("The email does not exist or !", "error_email")
@@ -460,7 +344,7 @@ feature -- Hooks
es: CMS_AUTHENTICATON_EMAIL_SERVICE
l_user_api: CMS_USER_API
l_token: STRING
l_link: STRING
l_url: STRING
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if req.is_post_request_method then
@@ -470,14 +354,12 @@ feature -- Hooks
-- User exist create a new token and send a new email.
l_token := new_token
l_user_api.new_password (l_token, l_user.id)
create l_link.make_from_string (req.server_url)
l_link.append ("/account/reset-password?token=")
l_link.append (l_token)
l_url := req.absolute_script_url ("/account/reset-password?token=" + l_token)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_password_email")
es.send_contact_password_email (l_email.value, l_link)
es.send_contact_password_email (l_email.value, l_url)
else
r.values.force ("The email does not exist !", "error_email")
r.values.force (l_email.value, "email")
@@ -499,7 +381,7 @@ feature -- Hooks
if attached {WSF_STRING} req.query_parameter ("token") as l_token then
r.values.force (l_token.value, "token")
if l_user_api.user_by_password_token (l_token.value) = Void then
r.values.force ("The token " + l_token.value + " is not valid, click <a href=%"/account/new-password%">here</a> to generate a new token.", "error_token")
r.values.force ("The token " + l_token.value + " is not valid, " + r.link ("click here" , "account/new-password", Void) + " to generate a new token.", "error_token")
r.set_status_code ({HTTP_CONSTANTS}.bad_request)
end
end
@@ -538,7 +420,8 @@ feature {NONE} -- Helpers
do
create p.make_from_string ("templates")
p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl")
p := a_response.module_resource_path (Current, p)
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)
@@ -563,13 +446,6 @@ feature {NONE} -- Block views
loop
l_tpl_block.set_value (ic.item, ic.key)
end
if
attached user_oauth_api as l_auth_api and then
attached l_auth_api.oauth2_consumers as l_list
then
l_tpl_block.set_value (l_list, "oauth_consumers")
end
a_response.add_block (l_tpl_block, "content")
else
debug ("cms")
@@ -717,111 +593,6 @@ feature {NONE} -- Block views
end
end
feature -- OAuth2 Login with google.
handle_login_with_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_oauth: CMS_OAUTH_20_WORKFLOW
do
if
attached {WSF_STRING} req.path_parameter ("callback") as p_consumer and then
attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer
then
create l_oauth.make (req.server_url, l_consumer)
if attached l_oauth.authorization_url as l_authorization_url then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (l_authorization_url)
r.execute
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Bad request")
r.execute
end
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Bad request")
r.execute
end
end
handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_auth: CMS_OAUTH_20_WORKFLOW
l_user_api: CMS_USER_API
l_user: CMS_USER
l_roles: LIST [CMS_USER_ROLE]
l_cookie: WSF_COOKIE
es: CMS_AUTHENTICATON_EMAIL_SERVICE
do
if attached {WSF_STRING} req.path_parameter ("callback") as l_callback and then
attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then
attached {WSF_STRING} req.query_parameter ("code") as l_code
then
create l_auth.make (req.server_url, l_consumer)
l_auth.sign_request (l_code.value)
if
attached l_auth.access_token as l_access_token and then
attached l_auth.user_profile as l_user_profile
then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- extract user email
-- check if the user exist
l_user_api := api.user_api
-- 1 if the user exit put it in the context
if
attached l_auth.user_email as l_email
then
if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then
-- User with email exist
if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then
-- Update oauth entry
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
else
-- create a oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
end
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
else
create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1)
l_roles.force (l_user_api.authenticated_user_role)
-- Create a new user and oauth entry
create l_user.make (l_email)
l_user.set_email (l_email)
l_user.set_password (new_token) -- generate a random password.
l_user.set_roles (l_roles)
l_user.mark_active
l_user_api.new_user (l_user)
-- Add oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name )
create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
set_current_user (req, l_user)
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle register: send_contact_welcome_email")
es.send_contact_welcome_email (l_email, "")
end
else
end
r.set_redirection (req.absolute_script_url (""))
r.execute
end
end
end
feature {NONE} -- Token Generation
@@ -845,7 +616,6 @@ feature {NONE} -- Token Generation
end
feature {NONE} -- Implementation: date and time
http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME

View File

@@ -0,0 +1,10 @@
note
description: "Summary description for {CMS_LOGIN_FORM}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_LOGIN_FORM
end

View File

@@ -0,0 +1,8 @@
{
"email": "webmaster@example.com",
"subjet_register": "Thank you for regitering with us, activate account",
"subjet_activate": "New account ativation token",
"subjet_password": "Password Recovery!!!",
"subjet_oauth": "Welcome",
"smtp": "127.0.0.1"
}

View File

@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Thank you for registering at <a href="$host">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>New Password</title>
<meta name="description" content="New Password">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>You have required a new password at <a href="...">ROC CMS</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p>
<p><a href="$link">$link</a></p>
</body>
</html>

View File

@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>New Activation</title>
<meta name="description" content="New Activation token">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>You have request a new activation token at<a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Welcome</title>
<meta name="description" content="Welcome">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Welcome to<a href="...">ROC CMS</a></p>
<p>Thank you for joining us.</p>
</body>
</html>

View File

@@ -0,0 +1,7 @@
{
"api_secret":"ADD_YOUR_SECRET_KEY",
"api_key":"ADD_YOUR_PUBLIC_KEY",
"scope": "email",
"api_revoke":"https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN",
"protected_resource_url":"https://www.googleapis.com/plus/v1/people/me"
}

View File

@@ -0,0 +1,34 @@
<div class="primary-tabs">
{unless isset="$user"}
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div>
<div>
<form action method="POST">
<div>
<input type="text" name="username" required>
<label>Username</label>
</div>
<div>
<input type="password" name="password" required>
<label>Password</label>
</div>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
</div>
</div>
<div>
<div>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div>
<div>
{foreach item="item" from="$oauth_consumers"}
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
{/foreach}
</div>
{/unless}
</div>

View File

@@ -0,0 +1,16 @@
<div>
<form action="/account/new-password" method="post">
<fieldset>
<legend>Require new password</legend>
<div>
<input type="email" id="email" name="email" value="{$email/}" required/>
<label for="email">Email</label>
{if isset="$error_email"}
<span><i>{$error_email/}</i></span> <br>
{/if}
<br>
</div>
<button type="submit">Send</button>
</fieldset>
</form>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<p>We have send you a new token code, check your email to generate a new password</p>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<p>We have send you a new activation code, check your email to activate your account.</p>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<p>Thanks for register, check your email to activate your account.</p>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<p>You new password has been saved!</p>
</div>

View File

@@ -0,0 +1,19 @@
<div>
<form action="/account/reactivate" method="post">
<fieldset>
<legend>Reactivate Form</legend>
<div>
<input type="email" id="email" name="email" value="{$email/}" required/>
<label for="email">Email</label>
{if isset="$error_email"}
<span><i>{$error_email/}</i></span> <br>
{/if}
<br>
{if isset="$is_active"}
<span><i>{$is_active/}</i></span> <br>
{/if}
</div>
<button type="submit">Reactivate</button>
</fieldset>
</form>
</div>

View File

@@ -0,0 +1,28 @@
<div>
<form action="/account/roc-register" method="post">
<fieldset>
<legend>Register Form</legend>
<div>
<input type="text" id="name" name="name" value="{$name/}" required autofocus />
<label for="name">Name</label>
{if isset="$error_name"}
<span><i>{$error_name/}</i></span> <br>
{/if}
</div>
<div>
<input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label>
</div>
<div>
<input type="email" id="email" name="email" value="{$email/}" required/>
<label for="email">Email</label>
{if isset="$error_email"}
<span><i>{$error_email/}</i></span> <br>
{/if}
</div>
<button type="submit">Register</button>
</fieldset>
</form>
</div>

View File

@@ -0,0 +1,28 @@
<div>
<form action="/account/reset-password" method="post">
<fieldset>
<legend>Generate New Password Form</legend>
<div>
<input type="text" id="token" name="token" value="{$token/}" required />
<label for="token">Token</label>
{if isset="$error_token"}
<span><i>{$error_token/}</i></span> <br>
{/if}
</div>
<div>
<input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label>
</div>
<div>
<input type="password" id="confirm_password" name="confirm_password" value="" required/>
<label for="password">Confirm Password</label>
</div>
<button type="submit">Confirm</button>
{if isset="$error_password"}
<span><i>{$error_password/}</i></span> <br>
{/if}
</fieldset>
</form>
</div>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="basic_auth" uuid="05216FE9-20F3-45FB-AB2E-8FCD75A8AD7E" library_target="basic_auth">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="basic_auth" uuid="26D7AD29-6E07-444D-A9CE-067D53029340" library_target="basic_auth">
<target name="basic_auth">
<root all_classes="true"/>
<file_rule>

View File

@@ -29,12 +29,15 @@ feature {NONE} -- Initialization
make
do
name := "basic auth"
version := "1.0"
description := "Service to manage basic authentication"
package := "core"
end
feature -- Access
name: STRING = "basic_auth"
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
@@ -64,7 +67,7 @@ feature {NONE} -- Implementation: routes
create l_bal_handler.make (api)
create l_methods
l_methods.enable_get
a_router.handle_with_request_methods ("/basic_auth_login", l_bal_handler, l_methods)
a_router.handle ("/basic_auth_login", l_bal_handler, l_methods)
end
configure_api_logoff (api: CMS_API; a_router: WSF_ROUTER)
@@ -75,7 +78,7 @@ feature {NONE} -- Implementation: routes
create l_bal_handler.make (api)
create l_methods
l_methods.enable_get
a_router.handle_with_request_methods ("/basic_auth_logoff", l_bal_handler, l_methods)
a_router.handle ("/basic_auth_logoff", l_bal_handler, l_methods)
end
feature -- Hooks configuration
@@ -106,7 +109,7 @@ feature -- Hooks
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
local
lnk: CMS_LOCAL_LINK
-- lnk: CMS_LOCAL_LINK
do
-- if attached a_response.current_user (a_response.request) as u then
-- create lnk.make (u.name + " (Logout)", "basic_auth_logoff?destination=" + a_response.request.request_uri)

View File

@@ -1,13 +0,0 @@
note
description: "Summary description for {CMS_AUTHENTICATION_CONSTANTS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_AUTHENTICATION_CONSTANTS
feature -- Access
oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_"
end

View File

@@ -29,7 +29,6 @@ feature {NONE} -- Initialization
make (a_setup: CMS_SETUP)
-- Create Current module, disabled by default.
do
name := "node"
version := "1.0"
description := "Service to manage content based on 'node'"
package := "core"
@@ -39,6 +38,10 @@ feature {NONE} -- Initialization
config: CMS_SETUP
-- Node configuration.
feature -- Access
name: STRING = "node"
feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API)
@@ -99,7 +102,8 @@ feature {CMS_API} -- Module management
is_installed (a_api: CMS_API): BOOLEAN
-- Is Current module installed?
do
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
Result := Precursor (a_api)
if Result and attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
Result := l_sql_storage.sql_table_exists ("nodes") and
l_sql_storage.sql_table_exists ("page_nodes")
end
@@ -109,8 +113,9 @@ feature {CMS_API} -- Module management
do
-- Schema
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (a_api.setup.environment.path.extended ("scripts").extended (name).appended_with_extension ("sql"))
l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended (name).appended_with_extension ("sql")), Void)
end
Precursor {CMS_MODULE}(a_api)
end
feature {CMS_API} -- Access: API
@@ -149,26 +154,26 @@ feature -- Access: router
-- TODO: for now, focused only on web interface, add REST api later. [2015-April-29]
create l_node_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/node", l_node_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get_post)
a_router.map (l_uri_mapping, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
a_router.handle ("/node/add/{type}", l_node_handler, a_router.methods_get_post)
a_router.handle ("/node/{id}/edit", l_node_handler, a_router.methods_get_post)
a_router.handle ("/node/{id}/delete", l_node_handler, a_router.methods_get_post)
a_router.handle ("/node/{id}/trash", l_node_handler, a_router.methods_get_post)
a_router.handle_with_request_methods ("/node/{id}", l_node_handler, a_router.methods_get)
a_router.handle ("/node/{id}", l_node_handler, a_router.methods_get)
-- For now: no REST API handling... a_router.methods_get_put_delete + a_router.methods_get_post)
-- Nodes
create l_nodes_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/nodes", l_nodes_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
a_router.map (l_uri_mapping, a_router.methods_get)
--Trash
create l_trash_handler.make (a_api, a_node_api)
create l_uri_mapping.make_trailing_slash_ignored ("/trash", l_trash_handler)
a_router.map_with_request_methods (l_uri_mapping, a_router.methods_get)
a_router.map (l_uri_mapping, a_router.methods_get)
end

View File

@@ -0,0 +1,22 @@
CREATE TABLE nodes (
`nid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
`revision` INTEGER,
`type` TEXT NOT NULL,
`title` VARCHAR(255) NOT NULL,
`summary` TEXT,
`content` TEXT,
`format` VARCHAR(128),
`author` INTEGER,
`publish` DATETIME,
`created` DATETIME NOT NULL,
`changed` DATETIME NOT NULL,
`status` INTEGER
);
CREATE TABLE page_nodes(
`nid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
`revision` INTEGER,
`parent` INTEGER
);

View File

@@ -13,7 +13,7 @@ inherit
REFACTORING_HELPER
create {CMS_AUTHENTICATION_MODULE}
create {CMS_OAUTH_20_MODULE}
make_with_storage
feature {NONE} -- Initialization
@@ -34,20 +34,20 @@ feature {CMS_MODULE} -- Access: User oauth storage.
feature -- Access: User Oauth20
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby.
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if any.
do
Result := oauth_20_storage.user_oauth2_by_id (a_uid, a_consumer)
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve a user by token `a_token' for the consumer `a_consumer'.
do
Result := oauth_20_storage.user_oauth2_by_token (a_token, a_consumer)
end
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve user by token `a_token' searching in all the registered consumers in the system.
do
Result := oauth_20_storage.user_oauth2_without_consumer_by_token (a_token)
end
@@ -75,7 +75,7 @@ feature -- Access: Consumers OAuth20
feature -- Change: User OAuth20
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32)
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL)
-- Add a new user with oauth20 using the consumer `a_consumer'.
require
has_id: a_user.has_id
@@ -84,7 +84,7 @@ feature -- Change: User OAuth20
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL)
-- Updaate user `a_user' with oauth2 for the consumer `a_consumer'.
require
has_id: a_user.has_id

View File

@@ -0,0 +1,21 @@
note
description: "Summary description for {CMS_OAUTH_20_CONSTANTS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_CONSTANTS
feature -- Access
oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_"
-- Name of Cookie used to keep the session info.
-- FIXME: make this configurable.
oauth_callback: STRING = "callback"
-- Callback parameter.
oauth_code: STRING = "code"
-- Code query parameter.
end

View File

@@ -8,17 +8,23 @@ class
CMS_OAUTH_20_CONSUMER
inherit
ANY
redefine
default_create
end
create
default_create
default_create,
make_with_id
feature {NONE} -- Initialization
make_with_id (a_id: like id)
do
id := a_id
default_create
end
default_create
do
set_endpoint ("")
@@ -34,29 +40,29 @@ feature {NONE} -- Initialization
feature -- Access
endpoint: READABLE_STRING_32
endpoint: READABLE_STRING_8
-- Url that receives the access token request.
authorize_url: READABLE_STRING_32
authorize_url: READABLE_STRING_8
--
extractor: READABLE_STRING_32
extractor: READABLE_STRING_8
-- text, json
callback_name: READABLE_STRING_32
callback_name: READABLE_STRING_8
-- consumer callback name
protected_resource_url: READABLE_STRING_32
protected_resource_url: READABLE_STRING_8
-- consumer resource url
scope: READABLE_STRING_32
scope: READABLE_STRING_8
-- consumer scope
api_key: READABLE_STRING_32
api_key: READABLE_STRING_8
-- consumer public key
api_secret: READABLE_STRING_32
api_secret: READABLE_STRING_8
-- consumer secret.
name: READABLE_STRING_32
@@ -65,8 +71,6 @@ feature -- Access
id: INTEGER_64
-- unique identifier.
feature -- Element change
set_extractor (a_extractor: like extractor)

View File

@@ -0,0 +1,63 @@
note
description: "Summary description for {CMS_OAUTH_20_EMAIL_SERVICE}."
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_EMAIL_SERVICE
inherit
EMAIL_SERVICE
redefine
initialize,
parameters
end
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
contact_email := parameters.contact_email
end
parameters: CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS
-- Associated parameters.
feature -- Access
contact_email: IMMUTABLE_STRING_8
-- contact email.
feature -- Basic Operations
send_contact_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_activation)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_register, l_message)
end
send_contact_welcome_email (a_to, a_content: READABLE_STRING_8)
-- Send successful contact message `a_token' to `a_to'.
require
attached_to: a_to /= Void
local
l_message: STRING
do
create l_message.make_from_string (parameters.account_welcome)
l_message.replace_substring_all ("$link", a_content)
send_message (contact_email, a_to, parameters.contact_subject_oauth, l_message)
end
end

View File

@@ -0,0 +1,260 @@
note
description: "Summary description for {CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS
inherit
EMAIL_SERVICE_PARAMETERS
create
make
feature {NONE} -- Initialization
make (a_cms_api: CMS_API)
local
utf: UTF_CONVERTER
l_site_name: READABLE_STRING_8
s: detachable READABLE_STRING_32
l_contact_email, l_subject_register, l_subject_activate, l_subject_password, l_subject_oauth: detachable READABLE_STRING_8
do
cms_api := a_cms_api
-- Use global smtp setting if any, otherwise "localhost"
smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost"))
l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name)
admin_email := a_cms_api.setup.site_email
if not admin_email.has ('<') then
admin_email := l_site_name + " <" + admin_email +">"
end
if attached {CONFIG_READER} a_cms_api.module_configuration_by_name ({CMS_AUTHENTICATION_MODULE}.name, Void) as cfg then
if attached cfg.text_item ("smtp") as l_smtp then
-- Overwrite global smtp setting if any.
smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp)
end
s := cfg.text_item ("email")
if s /= Void then
l_contact_email := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_register")
if s /= Void then
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_activate")
if s /= Void then
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_password")
if s /= Void then
l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s)
end
s := cfg.text_item ("subject_oauth")
if s /= Void then
l_subject_oauth := utf.utf_32_string_to_utf_8_string_8 (s)
end
end
if l_contact_email /= Void then
if not l_contact_email.has ('<') then
l_contact_email := l_site_name + " <" + l_contact_email + ">"
end
contact_email := l_contact_email
else
contact_email := admin_email
end
if l_subject_register /= Void then
contact_subject_register := l_subject_register
else
contact_subject_register := "Thank you for registering with us."
end
if l_subject_activate /= Void then
contact_subject_activate := l_subject_activate
else
contact_subject_activate := "New account activation token."
end
if l_subject_password /= Void then
contact_subject_password := l_subject_password
else
contact_subject_password := "Password Recovery."
end
if l_subject_oauth /= Void then
contact_subject_oauth := l_subject_oauth
else
contact_subject_oauth := "Welcome."
end
end
feature -- Access
smtp_server: IMMUTABLE_STRING_8
admin_email: IMMUTABLE_STRING_8
contact_email: IMMUTABLE_STRING_8
-- Contact email.
contact_subject_register: IMMUTABLE_STRING_8
contact_subject_activate: IMMUTABLE_STRING_8
contact_subject_password: IMMUTABLE_STRING_8
contact_subject_oauth: IMMUTABLE_STRING_8
account_activation: STRING
-- Account activation template email message.
do
Result := template_string ("account_activation.html", default_template_account_activation)
end
account_re_activation: STRING
-- Account re_activation template email message.
do
Result := template_string ("accunt_re_activation.html", default_template_account_re_activation)
end
account_password: STRING
-- Account password template email message.
do
Result := template_string ("account_new_password.html", default_template_account_new_password)
end
account_welcome: STRING
-- Account welcome template email message.
do
Result := template_string ("account_welcome.html", default_template_account_welcome)
end
feature {NONE} -- Implementation: Template
template_path (a_name: READABLE_STRING_GENERAL): PATH
-- Location of template named `a_name'.
do
Result := cms_api.module_location_by_name ({CMS_AUTHENTICATION_MODULE}.name).extended (a_name)
end
template_string (a_name: READABLE_STRING_GENERAL; a_default: STRING): STRING
-- Content of template named `a_name', or `a_default' if template is not found.
local
p: PATH
do
p := template_path ("account_activation.html")
if attached read_template_file (p) as l_content then
Result := l_content
else
create Result.make_from_string (a_default)
end
end
feature {NONE} -- Implementation
cms_api: CMS_API
read_template_file (a_path: PATH): detachable STRING
-- Read the content of the file at path `a_path'.
local
l_file: FILE
n: INTEGER
do
create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path)
if l_file.exists and then l_file.is_readable then
n := l_file.count
l_file.open_read
l_file.read_stream (n)
Result := l_file.last_string
l_file.close
else
-- Error
end
end
feature {NONE} -- Message email
default_template_account_activation: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Activation</title>
<meta name="description" content="Activation">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Thank you for registering at <a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on the following link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
default_template_account_re_activation: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>New Activation</title>
<meta name="description" content="New Activation token">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>You have requested a new activation token at <a href="...">ROC CMS</a></p>
<p>To complete your registration, please click on the following link to activate your account:<p>
<p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
default_template_account_new_password: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>New Password</title>
<meta name="description" content="New Password">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>You have required a new password at <a href="...">ROC CMS</a></p>
<p>To complete your request, please click on this link to generate a new password:<p>
<p><a href="$link">$link</a></p>
</body>
</html>
]"
default_template_account_welcome: STRING = "[
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Welcome</title>
<meta name="description" content="Welcome">
<meta name="author" content="ROC CMS">
</head>
<body>
<p>Welcome to<a href="...">ROC CMS</a></p>
<p>Thank you for joining us.</p>
</body>
</html>
]"
end

View File

@@ -0,0 +1,512 @@
note
description: "Generic OAuth Module supporting authentication using different providers."
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $"
revision: "$Revision: 97328 $"
class
CMS_OAUTH_20_MODULE
inherit
CMS_MODULE
rename
module_api as user_oauth_api
redefine
filters,
register_hooks,
initialize,
install,
user_oauth_api
end
CMS_HOOK_BLOCK
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
REFACTORING_HELPER
SHARED_LOGGER
CMS_REQUEST_UTIL
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "1.0"
description := "OAuth20 module"
package := "Oauth20"
create root_dir.make_current
cache_duration := 0
end
feature -- Access
name: STRING = "oauth20"
feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API)
-- <Precursor>
local
l_user_auth_api: like user_oauth_api
l_user_auth_storage: CMS_OAUTH_20_STORAGE_I
do
Precursor (a_api)
-- Storage initialization
if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql)
else
-- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage
end
-- API initialization
create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage)
user_oauth_api := l_user_auth_api
ensure then
user_oauth_api_set: user_oauth_api /= Void
end
feature {CMS_API} -- Module management
install (api: CMS_API)
local
l_consumers: LIST [STRING]
do
-- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("oauth2_consumers") then
--| Schema
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)
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)
end
-- 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)
else
from
l_sql_storage.sql_start
create {ARRAYED_LIST[STRING]} l_consumers.make (2)
until
l_sql_storage.sql_after
loop
if attached l_sql_storage.sql_read_string (1) as l_name then
l_consumers.force ("oauth2_" + l_name)
end
l_sql_storage.sql_forth
end
across l_consumers as ic loop
if not l_sql_storage.sql_table_exists (ic.item) then
if attached l_sql_storage.sql_script_content (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_table.sql.tpl"))) as sql then
-- FIXME: shouldn't we use a unique table for all oauth providers? or as it is .. one table per oauth provider?
sql.replace_substring_all ("$table_name", ic.item)
l_sql_storage.sql_execute_script (sql, Void)
end
end
end
end
Precursor {CMS_MODULE}(api)
end
end
feature {CMS_API} -- Access: API
user_oauth_api: detachable CMS_OAUTH_20_API
-- <Precursor>
feature -- Filters
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_oauth_api as l_user_oauth_api then
Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api))
end
end
feature -- Access: docs
root_dir: PATH
cache_duration: INTEGER
-- Caching duration
--| 0: disable
--| -1: cache always valie
--| nb: cache expires after nb seconds.
cache_disabled: BOOLEAN
do
Result := cache_duration = 0
end
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
if attached user_oauth_api as l_user_oauth_api then
configure_web (a_api, l_user_oauth_api, a_router)
end
end
configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER)
do
a_router.handle ("/account/roc-oauth-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/roc-oauth-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api,a_user_oauth_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post)
end
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_response)
a_response.subscribe_to_block_hook (Current)
a_response.subscribe_to_value_table_alter_hook (Current)
end
feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if attached current_user (a_response.request) as l_user then
a_value.force (l_user, "user")
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.current_user (a_response.request) as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_roc_auth_session_token
then
across
a_menu_system.primary_menu.items as ic
until
lnk2 /= Void
loop
if ic.item.title.has_substring ("(Logout)") then
lnk2 := ic.item
end
end
if lnk2 /= Void then
a_menu_system.primary_menu.remove (lnk2)
end
create lnk.make (u.name + " (Logout)", "account/roc-oauth-logout" )
a_menu_system.primary_menu.extend (lnk)
end
if a_response.location.starts_with ("account/roc-login") then
create lnk.make ("OAuth", "account/roc-oauth-login")
a_response.add_to_primary_tabs (lnk)
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-oauth-login")
then
get_block_view_login (a_block_id, a_response)
end
end
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.set_value ("Login", "optional_content_type")
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_OAUTH_20_CONSTANTS}.oauth_session) as l_cookie_token and then
attached {CMS_USER} current_user (req) as l_user
then
-- Logout OAuth
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, 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
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
if
attached user_oauth_api as l_auth_api and then
attached l_auth_api.oauth2_consumers as l_list
then
l_tpl_block.set_value (l_list, "oauth_consumers")
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
feature -- OAuth2 Login with Provider
handle_login_with_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_oauth: CMS_OAUTH_20_WORKFLOW
do
if
attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as p_consumer and then
attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer
then
create l_oauth.make (req.server_url, l_consumer)
if attached l_oauth.authorization_url as l_authorization_url then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (l_authorization_url)
r.execute
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Bad request")
r.execute
end
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Bad request")
r.execute
end
end
handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_auth: CMS_OAUTH_20_WORKFLOW
l_user_api: CMS_USER_API
l_user: CMS_USER
l_roles: LIST [CMS_USER_ROLE]
l_cookie: WSF_COOKIE
es: CMS_OAUTH_20_EMAIL_SERVICE
do
if attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as l_callback and then
attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then
attached {WSF_STRING} req.query_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_code) as l_code
then
create l_auth.make (req.server_url, l_consumer)
l_auth.sign_request (l_code.value)
if
attached l_auth.access_token as l_access_token and then
attached l_auth.user_profile as l_user_profile
then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- extract user email
-- check if the user exist
l_user_api := api.user_api
-- 1 if the user exit put it in the context
if
attached l_auth.user_email as l_email
then
if attached l_user_api.user_by_email (l_email) as p_user then
-- User with email exist
if attached a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then
-- Update oauth entry
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
else
-- create a oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
end
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
else
create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1)
l_roles.force (l_user_api.authenticated_user_role)
-- Create a new user and oauth entry
create l_user.make (l_email)
l_user.set_email (l_email)
l_user.set_password (new_token) -- generate a random password.
l_user.set_roles (l_roles)
l_user.mark_active
l_user_api.new_user (l_user)
-- Add oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name )
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
set_current_user (req, l_user)
-- Send Email
create es.make (create {CMS_OAUTH_20_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email")
es.send_contact_welcome_email (l_email, "")
end
end
r.set_redirection (r.front_page_url)
r.execute
end
end
end
feature {NONE} -- Token Generation
new_token: STRING
-- Generate a new token activation token
local
l_token: STRING
l_security: SECURITY_PROVIDER
l_encode: URL_ENCODER
do
create l_security
l_token := l_security.token
create l_encode
from until l_token.same_string (l_encode.encoded_string (l_token)) loop
-- Loop ensure that we have a security token that does not contain characters that need encoding.
-- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token
-- but the user will need to use an unencoded token if activation has to be done manually.
l_token := l_security.token
end
Result := l_token
end
feature {NONE} -- Implementation: date and time
http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME
local
d: HTTP_DATE
do
create d.make_from_string (s)
if not d.has_error then
Result := d.date_time
end
end
file_date (p: PATH): DATE_TIME
require
path_exists: (create {FILE_UTILITIES}).file_path_exists (p)
local
f: RAW_FILE
do
create f.make_with_path (p)
Result := timestamp_to_date (f.date)
end
timestamp_to_date (n: INTEGER): DATE_TIME
local
d: HTTP_DATE
do
create d.make_from_timestamp (n)
Result := d.date_time
end
note
copyright: "Copyright (c) 1984-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -1,5 +1,7 @@
note
description: "Summary description for {CMS_OAUTH_20_FILTER}."
description: "[
Extracts an OAuth2 token from the incoming request (cookie) and uses it to populate the user (or cms user context)
]"
date: "$Date$"
revision: "$Revision$"
@@ -32,27 +34,21 @@ feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter.
local
do
api.logger.put_debug (generator + ".execute ", Void)
-- if attached req.raw_header_data as l_raw_data then
-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
-- end
-- A valid user
if
attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_roc_auth_session_token
attached {WSF_STRING} req.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_roc_auth_session_token
then
if attached {CMS_USER} user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then
if attached user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then
set_current_user (req, l_user)
execute_next (req, res)
else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void)
execute_next (req, res)
end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
execute_next (req, res)
end
execute_next (req, res)
end
end

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="oauth_module" uuid="D64B990F-B51F-4E0D-AB2E-4AA5DDB783CE" library_target="oauth_module">
<target name="oauth_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option>
<library name="apis" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\apis\apis.ecf" readonly="false"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="cms_auth_module" location="..\auth\auth-safe.ecf" readonly="false"/>
<library name="cms_app_env" location="..\..\library\app_env\app_env-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="config" location="..\..\library\configuration\config-safe.ecf"/>
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer-safe.ecf" readonly="false"/>
<library name="email_service" location="..\..\library\email\email-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -20,18 +20,18 @@ feature -- Error Handling
feature -- Access: Users
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby.
deferred
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve a user by token `a_token' for the consumer `a_consumer'.
deferred
end
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve user by token `a_token' searching in all the registered consumers in the system.
deferred
end
@@ -53,12 +53,12 @@ feature -- Access: Consumers
feature -- Change: User Oauth2
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL)
-- Add a new user with oauth2 authentication.
deferred
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 )
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL )
-- Update user `a_user' with oauth2 authentication.
deferred
end

View File

@@ -22,17 +22,17 @@ feature -- Error handler
feature -- Access: Users
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
-- CMS User with Oauth credential by id if any.
do
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer_table: READABLE_STRING_GENERAL): detachable CMS_USER
-- -- CMS User with Oauth credential by access token `a_token' if any.
do
end
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL ): detachable CMS_USER
do
end
@@ -40,7 +40,7 @@ feature -- Access: Consumers
oauth2_consumers: LIST [STRING]
do
create {ARRAYED_LIST[STRING]} Result.make (0)
create {ARRAYED_LIST [STRING]} Result.make (0)
end
oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER
@@ -55,12 +55,12 @@ feature -- Access: Consumers
feature -- Change: User Oauth2
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32)
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL)
-- Add a new user with oauth2 authentication.
do
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 )
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_GENERAL )
-- Update user `a_user' with oauth2 authentication.
do
end

View File

@@ -22,10 +22,10 @@ create
feature -- Access User Outh
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER
-- Retrieve a user by token `a_token' searching in all the registered consumers in the system.
user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_GENERAL): detachable CMS_USER
-- Retrieve user by token `a_token' searching in all the registered consumers in the system.
local
l_list: LIST[STRING]
l_list: LIST [STRING]
do
error_handler.reset
write_information_log (generator + ".user_oauth2_without_consumer_by_token")
@@ -33,16 +33,14 @@ feature -- Access User Outh
from
l_list.start
until
l_list.after or attached Result
l_list.after or Result /= Void
loop
if attached {CMS_USER} user_oauth2_by_token (a_token, l_list.item) as l_user then
Result := l_user
end
Result := user_oauth2_by_token (a_token, l_list.item)
l_list.forth
end
end
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
@@ -53,7 +51,7 @@ feature -- Access User Outh
create l_parameters.make (1)
l_parameters.put (a_uid, "uid")
create l_string.make_from_string (select_user_oauth2_template_by_id)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_query (l_string, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
@@ -62,7 +60,7 @@ feature -- Access User Outh
end
end
user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
@@ -73,7 +71,7 @@ feature -- Access User Outh
create l_parameters.make (1)
l_parameters.put (a_token, "token")
create l_string.make_from_string (select_user_by_oauth2_template_token)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_query (l_string, l_parameters)
if sql_rows_count = 1 then
Result := fetch_user
@@ -85,11 +83,11 @@ feature -- Access User Outh
feature --Access: Consumers
oauth2_consumers: LIST[STRING]
oauth2_consumers: LIST [STRING]
-- Return a list of consumers, or empty
do
error_handler.reset
create {ARRAYED_LIST[STRING]}Result.make (0)
create {ARRAYED_LIST [STRING]} Result.make (0)
write_information_log (generator + ".user_by_oauth2_token")
sql_query (Sql_oauth_consumers, Void)
if not has_error then
@@ -142,7 +140,7 @@ feature --Access: Consumers
feature -- Change: User OAuth
new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32)
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL)
-- Add a new user with oauth2 authentication.
-- <Precursor>.
local
@@ -160,12 +158,12 @@ feature -- Change: User OAuth
l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date")
create l_string.make_from_string (sql_insert_oauth2_template)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_change (l_string, l_parameters)
sql_commit_transaction
end
update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32 )
update_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL )
-- Update user `a_user' with oauth2 authentication.
-- <Precursor>
local
@@ -182,7 +180,7 @@ feature -- Change: User OAuth
l_parameters.put (a_user_profile, "profile")
create l_string.make_from_string (sql_update_oauth2_template)
l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer))
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_change (l_string, l_parameters)
sql_commit_transaction
end
@@ -192,39 +190,38 @@ feature {NONE} -- Implementation OAuth Consumer
fetch_consumer: detachable CMS_OAUTH_20_CONSUMER
do
if attached sql_read_integer_64 (1) as l_id then
create Result
Result.set_id (l_id)
end
if Result /= Void then
if attached sql_read_string_32 (2) as l_name then
create Result.make_with_id (l_id)
if attached sql_read_string (2) as l_name then
Result.set_name (l_name)
end
if attached sql_read_string_32 (3) as l_api_secret then
if attached sql_read_string (3) as l_api_secret then
Result.set_api_secret (l_api_secret)
end
if attached sql_read_string_32 (4) as l_api_key then
if attached sql_read_string (4) as l_api_key then
Result.set_api_key (l_api_key)
end
if attached sql_read_string_32 (5) as l_scope then
if attached sql_read_string (5) as l_scope then
Result.set_scope (l_scope)
end
if attached sql_read_string_32 (6) as l_resource_url then
if attached sql_read_string (6) as l_resource_url then
Result.set_protected_resource_url (l_resource_url)
end
if attached sql_read_string_32 (7) as l_callback_name then
if attached sql_read_string (7) as l_callback_name then
Result.set_callback_name (l_callback_name)
end
if attached sql_read_string_32 (8) as l_extractor then
if attached sql_read_string (8) as l_extractor then
Result.set_extractor (l_extractor)
end
if attached sql_read_string_32 (9) as l_authorize_url then
if attached sql_read_string (9) as l_authorize_url then
Result.set_authorize_url (l_authorize_url)
end
if attached sql_read_string_32 (10) as l_endpoint then
if attached sql_read_string (10) as l_endpoint then
Result.set_endpoint (l_endpoint)
end
end
end
feature {NONE} -- Implementation: User
fetch_user: detachable CMS_USER
@@ -232,7 +229,7 @@ feature {NONE} -- Implementation: User
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_32 (1) as i then
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
@@ -264,15 +261,36 @@ feature {NONE} -- Implementation: User
end
end
feature -- {NONE} User OAuth2
feature {NONE} -- User OAuth2
sql_table_name (a_consumer: READABLE_STRING_8): STRING_8
oauth2_sql_table_name (a_consumer: READABLE_STRING_GENERAL): STRING_8
local
i,n: INTEGER
do
Result := Sql_table_prefix.twin
Result.append (a_consumer)
create Result.make_from_string (Sql_oauth2_table_prefix)
if a_consumer.is_valid_as_string_8 then
Result.append (a_consumer.to_string_8)
else
check only_ascii: False end
-- Replace non ascii char by '-'
from
i := 1
n := a_consumer.count
until
i > n
loop
if a_consumer [i].is_character_8 then
Result.append_code (a_consumer.code (i))
else
Result.append_character ('-')
end
i := i + 1
end
end
end
Select_user_by_oauth2_template_token: STRING = "SELECT u.* FROM users as u JOIN $table_name 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_oauth2_template_by_id: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.uid = :uid;"
@@ -282,12 +300,12 @@ feature -- {NONE} User OAuth2
Sql_oauth_consumers: STRING = "SELECT name FROM oauth2_consumers";
Sql_table_prefix: STRING = "oauth2_"
Sql_oauth2_table_prefix: STRING = "oauth2_"
feature -- {NONE} Consumer
feature {NONE} -- Consumer
Sql_oauth_consumer_callback: STRING ="SELECT * FROM oauth2_consumers where callback_name =:name;"
Sql_oauth_consumer_callback: STRING = "SELECT * FROM oauth2_consumers where callback_name =:name;"
Sql_oauth_consumer_name: STRING ="SELECT * FROM oauth2_consumers where name =:name;"
Sql_oauth_consumer_name: STRING = "SELECT * FROM oauth2_consumers where name =:name;"
end

View File

@@ -0,0 +1,18 @@
CREATE TABLE oauth2_consumers(
`cid` INTEGER PRIMARY KEY NOT NULL CHECK(`cid`>=0),
`name` VARCHAR(255) NOT NULL,
`api_secret` TEXT NOT NULL,
`api_key` TEXT NOT NULL,
`scope` VARCHAR (100) NOT NULL,
`protected_resource_url` VARCHAR (255) NOT NULL,
`callback_name` VARCHAR(255) NOT NULL,
`extractor` VARCHAR(50) NOT NULL,
`authorize_url` VARCHAR (255) NOT NULL,
`endpoint` VARCHAR (255) NOT NULL,
CONSTRAINT `cid`
UNIQUE(`cid`),
CONSTRAINT `name`
UNIQUE(`name`)
);

View File

@@ -0,0 +1,7 @@
-- Change the values TO_COMPLETE based on your API.
-- API SECTET KEY AND API PUBLIC KEY
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint)
VALUES ('google', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', 'callback_google', 'json','https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://accounts.google.com/o/oauth2/token');
INSERT INTO oauth2_consumers (name, api_secret, api_key, scope, protected_resource_url, callback_name, extractor, authorize_url, endpoint )
VALUES ('facebook', 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', 'callback_facebook','text','https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI','https://graph.facebook.com/oauth/access_token');

View File

@@ -0,0 +1,10 @@
CREATE TABLE $table_name (
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
`access_token` TEXT NOT NULL,
`created` DATETIME NOT NULL,
`details` TEXT NOT NULL,
CONSTRAINT `uid`
UNIQUE(`uid`)
);

View File

@@ -0,0 +1,7 @@
<div class="primary-tabs">
<div>
{foreach item="item" from="$oauth_consumers"}
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
{/foreach}
</div>
</div>

View File

@@ -42,6 +42,8 @@ feature {NONE} -- Initialization
local
l_url: like site_url
do
site_location := environment.path
--| Site id, used to identified a site, this could be set to a uuid, or else
site_id := text_item_or_default ("site.id", "_EWF_CMS_NO_ID_")
@@ -61,11 +63,18 @@ feature {NONE} -- Initialization
-- Can be also used to precise the "From:" value for email.
site_email := text_item_or_default ("site.email", "webmaster")
-- Location for theme folders.
-- Location for modules folders.
if attached text_item ("modules-dir") as s then
create modules_location.make_from_string (s)
else
modules_location := environment.modules_path
end
-- Location for themes folders.
if attached text_item ("themes-dir") as s then
create themes_location.make_from_string (s)
else
themes_location := environment.www_path.extended ("themes")
themes_location := environment.themes_path
end
-- Selected theme's name
@@ -76,7 +85,6 @@ feature {NONE} -- Initialization
end
compute_theme_location
compute_theme_assets_location
end
initialize_storages
@@ -139,10 +147,45 @@ feature -- Access
end
build_mailer
local
retried: BOOLEAN
f: FILE
do
to_implement ("Not implemented mailer")
if not retried then
if attached text_item ("mailer.smtp") as l_smtp then
create {NOTIFICATION_SMTP_MAILER} mailer.make (l_smtp)
elseif attached text_item ("mailer.sendmail") as l_sendmail then
create {NOTIFICATION_SENDMAIL_MAILER} mailer.make_with_location (l_sendmail)
elseif attached text_item ("mailer.output") as l_output then
if l_output.is_case_insensitive_equal ("@stderr") then
f := io.error
elseif l_output.is_case_insensitive_equal ("@stdout") then
f := io.output
else
create {RAW_FILE} f.make_with_name (l_output)
if not f.exists then
f.create_read_write
f.close
end
end
create {NOTIFICATION_STORAGE_MAILER} mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (f))
else
create {NOTIFICATION_STORAGE_MAILER} mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (io.error))
end
else
check valid_mailer: False end
-- FIXME: should we report persistent error message? If yes, see how.
create {NOTIFICATION_NULL_MAILER} mailer
end
rescue
retried := True
retry
end
feature -- Access
mailer: NOTIFICATION_MAILER
feature -- Access: storage
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
@@ -163,15 +206,7 @@ feature -- Theme: Compute location
theme_location := themes_location.extended (theme_name)
end
compute_theme_assets_location
-- assets (js, css, images, etc)
-- Not used at the moment.
do
debug ("refactor_fixme")
fixme ("Check if we really need it")
end
-- Check how to get this path from the CMS_THEME information.
theme_assets_location := theme_location.extended ("assets")
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -23,15 +23,35 @@ create
feature -- Access
theme_path: PATH
site_path: PATH
-- Directory containing the site.
--| For now, an alias for `path'.
do
Result := path
end
modules_path: PATH
-- Directory for templates (HTML, etc).
local
p: detachable PATH
do
p := internal_theme_path
p := internal_modules_path
if p = Void then
p := www_path.extended ("theme")
internal_theme_path := p
p := site_path.extended ("modules")
internal_modules_path := p
end
Result := p
end
themes_path: PATH
-- Directory for cms themes.
local
p: detachable PATH
do
p := internal_themes_path
if p = Void then
p := site_path.extended ("themes")
internal_themes_path := p
end
Result := p
end
@@ -58,7 +78,9 @@ feature -- Access
feature {NONE} -- Implementation
internal_theme_path: detachable like theme_path
internal_modules_path: detachable like modules_path
internal_themes_path: detachable like themes_path
internal_cms_config_ini_path: detachable like cms_config_ini_path

View File

@@ -90,7 +90,13 @@ feature -- Query
deferred
end
feature -- Access: Theme
feature -- Access: Theme
site_location: PATH
-- Path to CMS site root dir.
modules_location: PATH
-- Path to modules.
themes_location: PATH
-- Path to themes.
@@ -98,9 +104,6 @@ feature -- Access: Theme
theme_location: PATH
-- Path to a active theme.
theme_assets_location: PATH
-- Path to a active theme assets folder.
theme_information_location: PATH
-- Active theme informations.
do
@@ -110,6 +113,13 @@ feature -- Access: Theme
theme_name: READABLE_STRING_32
-- theme name.
feature -- Access
mailer: NOTIFICATION_MAILER
-- Email processor.
deferred
end
feature -- Access: storage
storage_drivers: STRING_TABLE [CMS_STORAGE_BUILDER]
@@ -186,4 +196,7 @@ feature -- Element change
module_registered: module_registered (m)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -28,12 +28,15 @@ feature {NONE} -- Initialization
make
do
name := "debug"
version := "1.0"
description := "Debug"
package := "cms"
end
feature -- Access
name: STRING = "debug"
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
@@ -134,7 +137,7 @@ feature -- Handler
end
note
copyright: "Copyright (c) 1984-2013, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -22,8 +22,8 @@ feature -- Initialization
l_roles: LIST [CMS_USER_ROLE]
do
--| Schema
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("core.sql"))
a_storage.sql_execute_file_script (a_setup.environment.path.extended ("scripts").extended ("user.sql"))
a_storage.sql_execute_file_script (a_setup.environment.site_path.extended ("scripts").extended ("core.sql"), Void)
a_storage.sql_execute_file_script (a_setup.environment.site_path.extended ("scripts").extended ("user.sql"), Void)
--| Roles
create l_anonymous_role.make ("anonymous")

View File

@@ -76,7 +76,13 @@ feature -- Operation
i := a_sql_statement.index_of (':', i)
if i = 0 then
i := n -- exit
elseif a_sql_statement.at (i-1).is_equal ('%'') or else a_sql_statement.at (i-1).is_equal ('%"') or else a_sql_statement.at (i-1).is_equal (' ') or else a_sql_statement.at (i-1).is_equal ('=') then
elseif
a_sql_statement [i-1] = '%''
or else a_sql_statement [i-1] = '%"'
or else a_sql_statement [i-1] = ' '
or else a_sql_statement [i-1] = '='
or else a_sql_statement [i-1] = '('
then
from
j := i + 1
until
@@ -124,15 +130,14 @@ feature -- Operation
feature -- Helper
sql_execute_file_script_with_params (a_path: PATH; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute SQL script from `a_path' and with params `a_params'.
sql_script_content (a_path: PATH): detachable STRING
-- Content of sql script located at `a_path'.
local
f: PLAIN_TEXT_FILE
sql: STRING
do
create f.make_with_path (a_path)
if f.exists and then f.is_access_readable then
create sql.make (f.count)
create Result.make (f.count)
f.open_read
from
f.start
@@ -140,43 +145,27 @@ feature -- Helper
f.exhausted or f.end_of_file
loop
f.read_stream_thread_aware (1_024)
sql.append (f.last_string)
Result.append (f.last_string)
end
f.close
sql_execute_script_with_params (sql, a_params)
end
end
sql_execute_file_script (a_path: PATH)
-- Execute SQL script from `a_path'.
local
f: PLAIN_TEXT_FILE
sql: STRING
sql_execute_file_script (a_path: PATH; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute SQL script from `a_path' and with optional parameters `a_params'.
do
create f.make_with_path (a_path)
if f.exists and then f.is_access_readable then
create sql.make (f.count)
f.open_read
from
f.start
until
f.exhausted or f.end_of_file
loop
f.read_stream_thread_aware (1_024)
sql.append (f.last_string)
end
f.close
sql_execute_script (sql)
if attached sql_script_content (a_path) as sql then
sql_execute_script (sql, a_params)
end
end
sql_execute_script (a_sql_script: STRING)
sql_execute_script (a_sql_script: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute SQL script.
-- i.e: multiple SQL statements.
local
i: INTEGER
err: BOOLEAN
cl: CELL [INTEGER]
do
reset_error
sql_begin_transaction
@@ -184,16 +173,17 @@ feature -- Helper
-- sql_change (a_sql_script, Void)
from
i := 1
create cl.put (0)
until
i > a_sql_script.count or err
loop
if attached next_sql_statement (a_sql_script, i) as s then
if attached next_sql_statement (a_sql_script, i, cl) as s then
if not s.is_whitespace then
sql_change (sql_statement (s), Void)
sql_change (sql_statement (s), a_params)
err := err or has_error
reset_error
end
i := i + s.count
i := i + cl.item
else
i := a_sql_script.count + 1
end
@@ -205,14 +195,6 @@ feature -- Helper
end
end
sql_execute_script_with_params (a_sql_script: STRING; a_params: detachable STRING_TABLE [detachable ANY])
-- Execute SQL script.
-- i.e: multiple SQL statements.
do
reset_error
sql_change (a_sql_script, a_params)
end
sql_table_exists (a_table_name: READABLE_STRING_8): BOOLEAN
-- Does table `a_table_name' exists?
do
@@ -382,11 +364,12 @@ feature -- Conversion
feature {NONE} -- Implementation
next_sql_statement (a_script: STRING; a_start_index: INTEGER): detachable STRING
next_sql_statement (a_script: STRING; a_start_index: INTEGER; a_offset: CELL [INTEGER]): detachable STRING
local
i,j,n: INTEGER
c: CHARACTER
l_end: INTEGER
l_removals: detachable ARRAYED_LIST [TUPLE [start_index,end_index: INTEGER]]
do
from
i := a_start_index
@@ -400,21 +383,32 @@ feature {NONE} -- Implementation
if i < n and then a_script[i + 1] = '-' then
-- Commented line "--" until New Line
j := a_script.index_of ('%N', i)
if j > 0 then
i := j
if j = 0 then
j := n
else
i := n
-- j := j
end
if l_removals = Void then
create l_removals.make (1)
end
l_removals.force ([i,j])
i := j
end
when '/' then
if i < n and then a_script[i + 1] = '*' then
-- Commented text "/*" until closing "*/"
j := a_script.substring_index ("*/", i)
if j > 0 then
i := j
if j = 0 then
j := n
else
i := n
j := j + 1 -- Include '/'
end
if l_removals = Void then
create l_removals.make (1)
end
l_removals.force ([i,j])
i := j
end
when '`', '"', '%'' then
from
@@ -428,6 +422,8 @@ feature {NONE} -- Implementation
if a_script [j - 1] /= '\' then
l_end := j
end
else
l_end := i
end
end
if l_end > 0 then
@@ -440,10 +436,23 @@ feature {NONE} -- Implementation
end
i := i + 1
end
i := a_script.index_of (';', a_start_index)
if i > a_start_index then
if i <= n and i > a_start_index then
Result := a_script.substring (a_start_index, i)
a_offset.replace (Result.count)
if l_removals /= Void then
j := 0
across
l_removals as ic
loop
Result.remove_substring (ic.item.start_index - j, ic.item.end_index - j)
j := j + ic.item.end_index - ic.item.start_index + 1
end
-- a_offset.replace (a_offset.item j)
end
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -137,6 +137,28 @@ feature -- Logging
end
end
feature -- Emails
process_email (e: CMS_EMAIL)
-- Process email `e'.
do
reset_error
setup.mailer.safe_process_email (e)
if setup.mailer.has_error then
error_handler.add_custom_error (0, "Mailer error", "Error occurred while processing email.")
end
end
process_emails (lst: ITERABLE [CMS_EMAIL])
-- Process collection of email `lst'.
do
reset_error
setup.mailer.process_emails (lst)
if setup.mailer.has_error then
error_handler.add_custom_error (0, "Mailer error", "Error occurred while processing emails.")
end
end
feature -- Permissions system
user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN
@@ -152,11 +174,8 @@ feature -- Query: module
-- Enabled module typed `a_type', if any.
--| usage: if attached module ({FOO_MODULE}) as mod then ...
local
-- t: STRING_8
l_type: TYPE [detachable CMS_MODULE]
do
-- t := type_name_without_annotation (a_type)
across
setup.modules as ic
until
@@ -173,8 +192,6 @@ feature -- Query: module
attached a_type.attempt (Result) and then attached l_type.generating_type.attempt (a_type)
then
-- Found
-- elseif t.same_string (type_name_without_annotation (l_type)) then
-- -- Found
else
Result := Void
end
@@ -322,55 +339,48 @@ feature {NONE}-- Implemenation
internal_user_api: detachable like user_api
-- Cached value for `user_api'.
type_name_without_annotation (a_type: TYPE [detachable ANY]): STRING
-- Type name for `a_type, without any annotation.
-- Used by `module' to search by type.
local
i,j,n: INTEGER
c: CHARACTER
feature -- Environment/ theme
site_location: PATH
-- CMS site location.
do
create Result.make_from_string (a_type.name)
from
i := 1
n := Result.count
until
i > n
loop
c := Result[i]
if c = '!' or c = '?' then
Result.remove (i)
n := n - 1
elseif c.is_lower then
j := Result.index_of (' ', i + 1)
if j > 0 then
Result.remove_substring (i, j)
n := n - (j - i)
end
else
i := i + 1
end
end
if Result.starts_with ("!") or Result.starts_with ("?") then
Result.remove_head (1)
elseif Result.starts_with ("detachable ") then
Result.remove_head (11)
end
Result := setup.site_location
end
feature -- Environment
theme_location: PATH
-- Active theme location.
do
Result := setup.theme_location
end
module_configuration (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
theme_assets_location: PATH
-- assets (js, css, images, etc).
do
debug ("refactor_fixme")
fixme ("Check if we really need it")
end
-- Check how to get this path from the CMS_THEME information.
Result := theme_location.extended ("assets")
end
feature -- Environment/ module
module_configuration_by_name (a_module_name: READABLE_STRING_GENERAL; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
-- Configuration reader for `a_module', and if `a_name' is set, using name `a_name'.
local
p, l_path: PATH
p, l_path: detachable PATH
l_name: READABLE_STRING_GENERAL
ut: FILE_UTILITIES
do
p := setup.environment.config_path.extended ("modules").extended (a_module_name)
if a_name = Void then
p := p.extended (a_module_name)
l_name := a_module_name
else
p := p.extended (a_name)
l_name := a_name
end
p := setup.environment.config_path
p := module_location_by_name (a_module_name).extended ("config").extended (l_name)
l_path := p.appended_with_extension ("json")
if ut.file_path_exists (l_path) then
create {JSON_CONFIG} Result.make_from_file (l_path)
@@ -382,13 +392,109 @@ feature -- Environment
end
if Result = Void and a_name /= Void then
-- Use sub config from default?
if attached {CONFIG_READER} module_configuration (a_module_name, Void) as cfg then
if attached {CONFIG_READER} module_configuration_by_name (a_module_name, Void) as cfg then
Result := cfg.sub_config (a_name)
else
-- Maybe try to use the global cms.ini ?
-- Maybe try to use the global cms.ini ?
end
end
end
modules_location: PATH
-- Directory containing cms modules.
do
Result := setup.modules_location
end
module_location (a_module: CMS_MODULE): PATH
-- Location associated with `a_module'.
do
Result := module_location_by_name (a_module.name)
end
module_location_by_name (a_module_name: READABLE_STRING_GENERAL): PATH
-- Location associated with `a_module_name'.
do
Result := modules_location.extended (a_module_name)
end
module_resource_location (a_module: CMS_MODULE; a_resource: PATH): PATH
-- Location of resource `a_resource' for `a_module'.
do
--| site/modules/$modname/$a_name.json
Result := module_resource_location_by_name (a_module.name, a_resource)
end
module_resource_location_by_name (a_module_name: READABLE_STRING_GENERAL; a_resource: PATH): PATH
-- Location of resource `a_resource' for `a_module'.
do
--| site/modules/$modname/$a_name.json
Result := module_location_by_name (a_module_name).extended_path (a_resource)
end
feature -- Environment/ modules and theme
module_theme_resource_location (a_module: CMS_MODULE; a_resource: PATH): detachable PATH
-- Theme resource location of `a_resource' for module `a_module', if exists.
-- By default, located under the module location folder, but could be overriden
-- from files located under modules subfolder of active `theme_location'.
--| First search in themes/$theme/modules/$a_module.name/$a_resource,
--| and if not found then search in
--| modules/$a_module_name/$a_resource.
local
ut: FILE_UTILITIES
do
-- Check first in selected theme folder.
Result := module_theme_location (a_module).extended_path (a_resource)
if not ut.file_path_exists (Result) then
-- And if not found, look into site/modules/$a_module.name/.... folders.
Result := module_resource_location (a_module, a_resource)
if not ut.file_path_exists (Result) then
Result := Void
end
end
end
module_theme_resource_location_by_name (a_module_name: READABLE_STRING_GENERAL; a_resource: PATH): detachable PATH
-- Theme resource location of `a_resource' for module named `a_module_name', if exists.
-- By default, located under the module location folder, but could be overriden
-- from files located under modules subfolder of active `theme_location'.
--| First search in themes/$theme/modules/$a_module.name/$a_resource,
--| and if not found then search in
--| modules/$a_module_name/$a_resource.
local
ut: FILE_UTILITIES
do
-- Check first in selected theme folder.
Result := module_theme_location_by_name (a_module_name).extended_path (a_resource)
if not ut.file_path_exists (Result) then
-- And if not found, look into site/modules/$a_module.name/.... folders.
Result := module_resource_location_by_name (a_module_name, a_resource)
if not ut.file_path_exists (Result) then
Result := Void
end
end
end
module_theme_location (a_module: CMS_MODULE): PATH
-- Location for overriden files associated with `a_module_name'.
do
Result := module_theme_location_by_name (a_module.name)
end
module_theme_location_by_name (a_module_name: READABLE_STRING_GENERAL): PATH
-- Location for overriden files associated with `a_module_name'.
do
Result := theme_location.extended ("modules").extended (a_module_name)
end
module_configuration (a_module: CMS_MODULE; a_name: detachable READABLE_STRING_GENERAL): detachable CONFIG_READER
do
Result := module_configuration_by_name (a_module.name, a_name)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

18
src/service/cms_email.e Normal file
View File

@@ -0,0 +1,18 @@
note
description: "CMS interface representing an email to be used with {CMS_API}.process_email (e: CMS_EMAIL)."
date: "$Date$"
revision: "$Revision$"
class
CMS_EMAIL
inherit
NOTIFICATION_EMAIL
create
make
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -156,7 +156,7 @@ feature -- Settings: router
do
api.logger.put_information (generator + ".configure_api_file_handler", Void)
create fhdl.make_hidden_with_path (setup.theme_assets_location)
create fhdl.make_hidden_with_path (api.theme_assets_location)
fhdl.disable_index
fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
do
@@ -253,7 +253,7 @@ feature -- Execution
r: NOT_FOUND_ERROR_CMS_RESPONSE
f: WSF_FILE_RESPONSE
do
p := api.setup.theme_assets_location.extended ("favicon.ico")
p := api.theme_assets_location.extended ("favicon.ico")
if ut.file_path_exists (p) then
create f.make_with_path (p)
res.send (f)

View File

@@ -16,6 +16,8 @@ feature -- Access
name: STRING
-- Name of the module.
deferred
end
description: STRING
-- Description of the module.
@@ -132,4 +134,7 @@ feature -- Hooks
create Result.make_empty
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -152,39 +152,6 @@ feature -- API
Result := api.formats
end
feature -- Module
module_resource_path (a_module: CMS_MODULE; a_resource: PATH): detachable PATH
-- Resource path of `a_resource' for module `a_module', if resource exists.
local
rp: PATH
ut: FILE_UTILITIES
do
-- Check first in selected theme folder.
rp := module_assets_theme_location (a_module)
Result := rp.extended_path (a_resource)
if not ut.file_path_exists (Result) then
-- And if not found, look into site/modules/$a_module.name/.... folders.
rp := module_assets_location (a_module)
Result := rp.extended_path (a_resource)
if not ut.file_path_exists (Result) then
Result := Void
end
end
end
module_assets_location (a_module: CMS_MODULE): PATH
-- Location for the assets associated with `a_module'.
do
Result := setup.environment.path.extended ("modules").extended (a_module.name)
end
module_assets_theme_location (a_module: CMS_MODULE): PATH
-- Location for the assets associated with `a_module'.
do
Result := setup.theme_location.extended ("modules").extended (a_module.name)
end
feature -- URL utilities
is_front: BOOLEAN
@@ -921,13 +888,19 @@ feature -- Generation
across
reg_ic.item.blocks as ic
loop
-- if attached {CMS_SMARTY_CONTENT_BLOCK} ic.item as l_tpl_block then
-- across
-- page.variables as var_ic
-- loop
-- l_tpl_block.set_value (var_ic.item, var_ic.key)
-- end
-- end
if attached {CMS_SMARTY_TEMPLATE_BLOCK} ic.item as l_tpl_block then
-- Apply page variables to smarty block.
-- FIXME: maybe add notion of values at the CMS_BLOCK level
-- or consider a CMS_BLOCK_WITH_VALUES ...
across
page.variables as var_ic
loop
if not l_tpl_block.values.has (var_ic.key) then
-- Do not overwrite if has key.
l_tpl_block.set_value (var_ic.item, var_ic.key)
end
end
end
page.add_to_region (theme.block_html (ic.item), reg_ic.item.name)
end
end

122
tools/roc/application.e Normal file
View File

@@ -0,0 +1,122 @@
note
description: "[
roc tool: install modules into an existing CMS Application.
roc install [--module|-m <MODULE_PATH>] [(--dir|-d <CMS_PATH>) | <MODULE_NAME>]
install: Install a given module to the corresponding cms application
--module|-m: module path or current directory if is not defined.
--dir|-d cms application path or current directory if is not defined
Running the command will copy to the CMS Application site/modules the following artifacts if the current module provide them.
config
scripts
themes
running
roc install blog
will look for a module blog in the modules directory starting at the current directory.
]"
date: "$Date$"
revision: "$Revision$"
class
APPLICATION
inherit
SHARED_EXECUTION_ENVIRONMENT
rename
print as ascii_print
end
ARGUMENTS_32
rename
print as ascii_print
end
LOCALIZED_PRINTER
rename
print as ascii_print,
localized_print as print
end
create
make
feature {NONE} -- Initialization
make
-- Initialize tool.
local
cmd_args: like command_arguments
do
-- TODO add support to other commands.
if argument_count = 0 then
print_usage
elseif attached commands.item (argument (1)) as cmd then
cmd_args := command_arguments
if cmd.is_valid (cmd_args) then
cmd.execute (cmd_args)
else
print_command_usage (cmd)
end
else
print ("Wrong command %"" + argument (1) + "%".%N")
print_usage
end
end
commands: STRING_TABLE [ROC_COMMAND]
local
cmd: ROC_COMMAND
once
create Result.make (1)
create {ROC_INSTALL_COMMAND} cmd.make ("install")
Result.force (cmd, cmd.name)
end
command_arguments: ARRAY [READABLE_STRING_32]
local
i,n: INTEGER
do
create Result.make_empty
Result.rebase (0)
Result.force (argument (0), 0)
from
i := 2 -- skip first arg which is command name
n := argument_count
until
i > n
loop
Result.force (argument (i), i - 1)
i := i + 1
end
end
feature -- Usage
print_usage
do
print ("Usage:%N")
across
commands as ic
loop
print_command_usage (ic.item)
end
end
print_command_usage (cmd: ROC_COMMAND)
do
print ("roc ")
print (cmd.name)
print (" ")
print (cmd.help)
print ("%N")
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

3
tools/roc/license.lic Normal file
View File

@@ -0,0 +1,3 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

19
tools/roc/roc.ecf Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="roc" uuid="10B0F9A7-B711-419B-A1B5-833EB61DF8A6">
<target name="roc">
<root class="APPLICATION" feature="make"/>
<option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="encoding" location="$ISE_LIBRARY\library\encoding\encoding-safe.ecf"/>
<cluster name="roc" location=".\" recursive="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
</cluster>
</target>
</system>

53
tools/roc/roc_command.e Normal file
View File

@@ -0,0 +1,53 @@
note
description: "Abstraction of roc command."
date: "$Date$"
revision: "$Revision$"
deferred class
ROC_COMMAND
inherit
SHARED_EXECUTION_ENVIRONMENT
rename
print as ascii_print
end
LOCALIZED_PRINTER
rename
print as ascii_print,
localized_print as print
end
feature {NONE} -- Initialization
make (a_name: READABLE_STRING_8)
do
create name.make_from_string (a_name)
end
feature -- Access
name: IMMUTABLE_STRING_8
help: STRING_32
deferred
end
feature -- Status report
is_valid (args: ARRAY [READABLE_STRING_32]): BOOLEAN
deferred
end
feature -- Execution
execute (args: ARRAY [READABLE_STRING_32])
require
args.lower = 0 -- Prog name at index 0, args at index 1, ...
deferred
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,285 @@
note
description: "Installation command."
date: "$Date$"
revision: "$Revision$"
class
ROC_INSTALL_COMMAND
inherit
ROC_COMMAND
create
make
feature -- Access
help: STRING_32
once
Result := "[--module|-m <MODULE_PATH>] [(--dir|-d <CMS_PATH>) | <MODULE_NAME>] [--site-dir <CMS_SITE_PATH>]"
end
feature -- Status report
is_valid (args: ARRAY [READABLE_STRING_32]): BOOLEAN
-- Is the submitted install command valid?
local
i, n: INTEGER
optional_module: BOOLEAN
cms_path: BOOLEAN
do
n := args.upper
-- TODO add error reporting.
if n >= 1 and then n <= 4 then
from
i := 1
until
i > n
loop
if args [i].same_string ("-m") then
optional_module := True
elseif args [i].same_string ("--module") then
optional_module := True
elseif args [i].same_string ("-d") then
cms_path := True
elseif args [i].same_string ("--dir") then
cms_path := True
end
i := i + 1
end
if n = 4 then
if (cms_path and optional_module) then
-- valid command
Result := True
else
print ("Error check the optional argument --module|-m and --dir|-d")
end
elseif n = 2 then
if (cms_path and not optional_module) then
Result := True
else
print ("Error missing value for dir")
Result := False
end
else
Result := True
end
else
Result := False
if n > 4 then
print ("Too many arguments")
end
if n < 1 then
print ("Too few argumetns")
end
end
end
feature -- Helpers
module_name (a_path: PATH): STRING_32
do
-- FIXME: better implementation needed. Either based on "a" new module.info file, or parsing the .ecf
if attached a_path.entry as e then
Result := e.name
else
Result := a_path.name
end
end
feature -- Execution
execute (args: ARRAY [READABLE_STRING_32])
-- Install a module into a cms application.
-- Pattern: module_src/site/* => cms/site/modules/$module_name/*
local
l_site_path, l_cms_path, l_module_source_path: detachable PATH
l_site_dir: DIRECTORY
l_modules_dir: DIRECTORY
l_dest_dir: DIRECTORY
i,n: INTEGER
do
from
i := 1
n := args.upper
until
i > n
loop
if attached args[i] as arg then
if
arg.same_string ("-d")
or arg.same_string ("--dir")
then
i := i + 1
if i <= n then
create l_cms_path.make_from_string (args[i])
end
elseif
arg.same_string ("--site-dir")
then
i := i + 1
if i <= n then
create l_site_path.make_from_string (args[i])
end
elseif
arg.same_string ("-m")
or arg.same_string ("--module")
then
i := i + 1
if i <= n then
create l_module_source_path.make_from_string (args[i])
end
end
end
i := i + 1
end
if l_module_source_path = Void then
l_module_source_path := Execution_environment.current_working_path
end
if l_cms_path = Void then
l_cms_path := Execution_environment.current_working_path.extended ("modules")
end
if l_cms_path /= Void and l_module_source_path /= Void then
-- If l_site_path is not set; initialize it to $cms_path/site.
if l_site_path = Void then
l_site_path := l_cms_path.extended ("site")
end
-- Install configuration files.
if attached module_name (l_module_source_path) as l_mod_name then
create l_site_dir.make_with_path (l_site_path)
if l_site_dir.exists then
create l_modules_dir.make_with_path (l_site_path.extended ("modules"))
if not l_modules_dir.exists then
l_modules_dir.create_dir
end
create l_dest_dir.make_with_path (l_modules_dir.path.extended (l_mod_name))
if not l_dest_dir.exists then
l_dest_dir.create_dir
end
install_module_elements (l_module_source_path, l_dest_dir.path, Void)
-- install_module_elements (l_module_source_path, l_dest_dir.path, Config_dir)
-- install_module_elements (l_module_source_path, l_dest_dir.path, Scripts_dir)
-- install_module_elements (l_module_source_path, l_dest_dir.path, Themes_dir)
print ("Module ")
print (l_mod_name)
print (" was successfuly installed to the CMS Application location ")
print (l_cms_path.name)
print ("%NCheck the module elements at ")
print (l_dest_dir.path.name)
print (".%N")
else
print ({STRING_32} "The CMS Application located at " + l_cms_path.name + "does not have the site or modules folders.%N")
end
else
print ("Error: not possible to retrieve module name.%N")
end
else
print ("Error: wrong path to CMS application.%N")
end
end
install_module_elements (a_module_source_path: PATH; a_cms_module_target_path: PATH; a_element: detachable READABLE_STRING_GENERAL)
-- Install module site files from `a_module_source_path' to cms application `a_cms_module_target_path' under expected modules folder.
-- If `a_element' is set, take into account only sub folder `a_element'.
local
l_path: PATH
l_dest_dir: DIRECTORY
l_src_dir: DIRECTORY
do
l_path := a_module_source_path.extended ("site")
if a_element /= Void then
-- Copy all files under "site/$a_element" into "site/modules/$module_name/$a_element" location.
create l_src_dir.make_with_path (l_path.extended (a_element))
create l_dest_dir.make_with_path (a_cms_module_target_path.extended (a_element))
else
-- Copy all files under "site" into "site/modules/$module_name/" location.
create l_src_dir.make_with_path (l_path)
create l_dest_dir.make_with_path (a_cms_module_target_path)
end
if not l_dest_dir.exists then
l_dest_dir.create_dir
end
copy_directory (l_src_dir, l_dest_dir, True)
end
feature {NONE} -- System/copy files
copy_directory (a_src: DIRECTORY; a_dest: DIRECTORY; is_recursive: BOOLEAN)
-- Copy all elements from `a_src' to `a_dest'.
local
l_dir: DIRECTORY
l_new_dir: DIRECTORY
entry: PATH
l_path: PATH
l_file: FILE
ut: FILE_UTILITIES
do
across
a_src.entries as ic
loop
entry := ic.item
if not (entry.is_current_symbol or else entry.is_parent_symbol) then
l_path := a_src.path.extended_path (entry)
create {RAW_FILE} l_file.make_with_path (l_path)
if not l_file.is_directory then
copy_file_in_directory (l_file, a_dest.path)
elseif is_recursive then
create l_dir.make_with_path (l_path)
create l_new_dir.make_with_path (a_dest.path.extended_path (entry))
ut.create_directory_path (l_new_dir.path)
if l_dir.exists then
copy_directory (l_dir, l_new_dir, is_recursive)
end
end
end
end
end
copy_file_in_directory (a_file: FILE; a_dir: PATH)
-- Copy file `a_file' to dir `a_dir'.
local
retried: BOOLEAN
l_dest: RAW_FILE
do
if not retried then
if attached a_file.path.entry as e then
create l_dest.make_with_path (a_dir.extended_path (e))
l_dest.create_read_write
a_file.open_read
-- Copy file source to destination
if
l_dest.exists and then
l_dest.is_writable and then
a_file.exists and then
a_file.is_readable
then
a_file.copy_to (l_dest)
a_file.close
l_dest.close
end
end
end
rescue
retried := True
retry
end
--feature -- Constants
-- Config_dir: STRING = "config"
-- -- Configuration dir.
-- Scripts_dir: STRING = "scripts"
-- -- Scripts dir.
-- Themes_dir: STRING = "themes"
-- -- Themes dir.
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end