Compare commits

..

8 Commits

Author SHA1 Message Date
db697cec3e Fixed auth mail template text and code. 2016-02-03 23:33:52 +01:00
892f2331de Do not set destination query parameter to any account/auth url.
Set "site_sign_in_url" and "site_sign_out_url" as variables (so it could be used by template).
2016-02-03 23:16:05 +01:00
3496536751 Added CMS_API.request: WSF_REQUEST to ease dev of ROC CMS code.
- Removed CMS_REQUEST_UTIL
  - centralize a few request related code into CMS_API
Added CMS_API.user, CMS_API.set_user (CMS_USER), ... and user related routines.

Refactored Auth related code
  - added various abstractions to factorize implementation and harmonize solutions.
  - revisited the logout strategy.
  - updated the account info page, and remove info user should not care about.
  - simplified the process, and encourage auth module to follow same design.

Added CMS_LINK helper routines to modify the related query string.
Removed CMS_USER.profile (and related routines)
   - It was not used so far.
   - it will probably a specific module later, if needed.

Update various module to avoid fetching user from sql directly, and let this task to CMS_USER_API.

Removed CMS_NODE_API.node_author (a_node: CMS_NODE): detachable CMS_USER,
   - as the info is already in CMS_NODE.author

Added CMS_RESPONSE.redirection_delay, if ever one code want to redirect after a few seconds.
Added the request uri info to the not found cms response.
2016-01-29 21:58:49 +01:00
41ac45d07b Fixed various CMS_MODULE.install, by not marked module installed if an error occurred!
Improved Auth related module implementation by having a way to change settings like token, max age.
  - use CMS_SETUP.site_id and related "auth.$module.token" ... configuration values.
  - removed related CMS_..._CONSTANTS classes.

For auth session module, use auth_session as table name, and use VARCHAR(64).
Extracted sql from blog module, and store it under site/scripts/install.sql .
Renamed a few $modulename.sql as install.sql
2016-01-27 18:22:20 +01:00
d3b485f4d3 Removed unused local variable. 2016-01-22 22:19:24 +01:00
2b1d5f9693 Updated to new routine type. 2016-01-22 21:41:56 +01:00
59c03c5f4d Added CMS_STRING_EXPANDER.
For now with basic implementation.
  It will be improved later

Added SEO related attribute in CMS_RESPONSE.
Added improved Contact module.
Added basic SEO module.
2016-01-22 21:33:06 +01:00
39ab19d20e Eiffel code and ECFs update to support new agent notations.
Accepts /account and /account/ .
2016-01-19 16:15:13 +01:00
122 changed files with 4059 additions and 2203 deletions

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
<target name="cms"> <target name="cms">
<root all_classes="true"/> <root all_classes="true"/>
<file_rule> <file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude> <exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional"> <option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="cms" uuid="8CC0D052-57D1-4CAA-AFF1-448FA290734B" library_target="cms">
<description>ROC CMS library</description> <description>ROC CMS library</description>
<target name="cms"> <target name="cms">
<root all_classes="true"/> <root all_classes="true"/>
<file_rule> <file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude> <exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
<option warning="true" full_class_checking="false" void_safety="none" syntax="transitional"> <option warning="true" full_class_checking="false" void_safety="none" syntax="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>

View File

@@ -8,7 +8,7 @@
<exclude>/CVS$</exclude> <exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude> <exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional"> <option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/> <debug name="dbglog" enabled="true"/>
</option> </option>
<setting name="executable_name" value="demo"/> <setting name="executable_name" value="demo"/>
@@ -24,6 +24,7 @@
<library name="cms_auth_module" location="..\..\modules\auth\auth-safe.ecf" readonly="false"/> <library name="cms_auth_module" location="..\..\modules\auth\auth-safe.ecf" readonly="false"/>
<library name="cms_basic_auth_module" location="..\..\modules\basic_auth\basic_auth-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_blog_module" location="..\..\modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_contact_module" location="..\..\modules\contact\contact-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/> <library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/> <library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/>
<library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/> <library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
@@ -33,6 +34,7 @@
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/> <library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>
<library name="cms_openid_module" location="..\..\modules\openid\openid-safe.ecf" readonly="false"/> <library name="cms_openid_module" location="..\..\modules\openid\openid-safe.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes-safe.ecf" readonly="false"/> <library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes-safe.ecf" readonly="false"/>
<library name="cms_seo_module" location="..\..\modules\seo\seo-safe.ecf" readonly="false"/>
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/> <library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/> <library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false"> <library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false">
@@ -43,12 +45,12 @@
<!-- <!--
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf"/> <library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf"/>
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" /> <library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" />
--> -->
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-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_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
</target> </target>
<target name="demo_any" extends="common"> <target name="demo_any" extends="common">
<setting name="concurrency" value="thread"/> <setting name="concurrency" value="scoop"/>
<library name="any_launcher" location="..\..\launcher\any-safe.ecf" readonly="false"/> <library name="any_launcher" location="..\..\launcher\any-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true"/> <cluster name="src" location=".\src\" recursive="true"/>
</target> </target>

View File

@@ -6,10 +6,13 @@ set ROC_CMS_DIR=%~dp0
%ROC_CMD% install --module ..\..\modules\auth --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\contact --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\feed_aggregator --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\google_search --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\feed_aggregator --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\seo --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\google_search --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\session_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR%

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="cms_demo_module" uuid="4BB59A54-2544-4C10-BFA6-01D12E541A30" library_target="cms_demo_module"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="cms_demo_module" uuid="4BB59A54-2544-4C10-BFA6-01D12E541A30" library_target="cms_demo_module">
<target name="cms_demo_module"> <target name="cms_demo_module">
<root all_classes="true"/> <root all_classes="true"/>
<file_rule> <file_rule>
<exclude>/.git$</exclude> <exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude> <exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard"> <option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
</option> </option>
@@ -21,4 +21,3 @@
<cluster name="src" location=".\" recursive="true"/> <cluster name="src" location=".\" recursive="true"/>
</target> </target>
</system> </system>

View File

@@ -70,8 +70,9 @@ CREATE TABLE tb_demo(
api.logger.put_error ("Could not initialize database for demo module", generating_type) api.logger.put_error ("Could not initialize database for demo module", generating_type)
end end
end end
Precursor {CMS_MODULE}(api)
end end
-- For this demo, be flexible, and do not required sql.
Precursor {CMS_MODULE}(api)
end end
feature -- Access: router feature -- Access: router
@@ -151,7 +152,7 @@ feature -- Mapping helper: uri template
feature -- Mapping helper: uri template agent 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]]; rqst_methods: detachable WSF_REQUEST_METHODS) map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [WSF_REQUEST, WSF_RESPONSE]; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'. -- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'.
require require
a_tpl_attached: a_tpl /= Void a_tpl_attached: a_tpl /= Void

View File

@@ -4,9 +4,17 @@ root-dir=site/www
#modules-dir=site/modules #modules-dir=site/modules
[site] [site]
# General token that could be use for cookies, and related.
id=_EIFFEL_CMS_
# Name of the site, for the title, and eventual message. # Name of the site, for the title, and eventual message.
name=Eiffel CMS name=Eiffel CMS
# Properties used for SEO.
property[headline]=Eiffel CMS -- the demo
property[description]=Demo for Eiffel ROC CMS.
property[keywords]=eiffel,cms,demo
# Email used for notification # Email used for notification
email=noreply@example.com email=noreply@example.com
@@ -39,6 +47,14 @@ output=site\db\mailer.log
[blocks] [blocks]
@include=blocks.ini @include=blocks.ini
[auth]
# token, default is $site.id or built-in.
#token=_ROC_AUTH_TOKEN_
#session.token=
#session.max_age=86400
#openid.token=
#oauth.token=
[admin] [admin]
# CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none) # CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none)
installation_access=all installation_access=all

View File

@@ -0,0 +1 @@
{include file="block_account_info.tpl" /}

View File

@@ -1,62 +1,34 @@
<div class="primary-tabs"> <div class="primary-tabs">
{if isset="$user"} {if isset="$user"}
<h3>Account Information</h3> <h3>Account Information</h3>
<div> <ul class="user-information">
<div> <div>
<div> <label>Username:</label> {$user.name/}
<label>Username:</label> {$user.name/}
</div>
<div>
<label>Email:</label> {$user.email/}
</div>
<div>
<label>Creation Date:</label> {$user.creation_date/}
</div>
<div>
<label>Last login:</label> {$user.last_login_date/}
</div>
<div>
<form method="get" action="{$site_url/}{$auth_login_strategy/}">
<button type="submit">Logout</button>
</form>
</div>
</div> </div>
</div> <div>
<hr> <label>Email:</label> {$user.email/}
{include file="block_change_password.tpl" /} </div>
<hr> <div>
<h4>Roles</h4> <label>Creation Date:</label> {$user.creation_date/} (UTC)
<div> </div>
{foreach item="ic" from="$roles"} <div>
<div> <label>Last login:</label> {$user.last_login_date/} (UTC)
<ul> </div>
<li> <div>
<strong>{$ic.name/}</strong> <form method="get" action="{$site_url/}account/roc-logout">
<ul> <button type="submit">Logout</button>
<li> <i>permissions</i> </form>
<ul> </div>
{foreach item="ip" from="$ic.permissions"} </ul>
<li>{$ip/}</li>
{/foreach}
</ul>
</li>
</ul>
</li>
</ul>
</div>
{/foreach}
</div>
<hr> <hr>
<h4>Profile</h4> <h4>Profile</h4>
<div> <ul class="user-profile">
{foreach item="the_value" key="the_name" from="$user.profile"} {foreach item="the_value" key="the_name" from="$user.profile"}
<div> <li>
<label>{$the_name/}:</label> {$the_value/} <label>{$the_name/}:</label><div>{$the_value/}</div>
</div> </li>
{/foreach} {/foreach}
</div> </ul>
{/if} {/if}
{unless isset="$user"} {unless isset="$user"}
<div> <div>

View File

@@ -1,7 +1,7 @@
<div> <div>
<form action="{$site_url/}account/change-password" method="post"> <form action="{$site_url/}account/change-password" method="post">
<fieldset> <fieldset>
<legend>Change Password Form</legend> <legend>Change Password</legend>
<div> <div>
<input type="password" id="password" name="password" value="" required/> <input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label> <label for="password">Password</label>

View File

@@ -1,29 +0,0 @@
<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>
{/unless}
</div>

View File

@@ -1,307 +1,291 @@
var ROC_AUTH = ROC_AUTH || { }; var ROC_AUTH = ROC_AUTH || { };
var loginURL = "/basic_auth_login"; var loginURL = "/roc-basic-login";
var logoutURL = "/basic_auth_logoff"; var logoutURL = "/roc-basic-logoff";
var userAgent = navigator.userAgent.toLowerCase(); var userAgent = navigator.userAgent.toLowerCase();
var firstLogIn = true; var firstLogIn = true;
ROC_AUTH.login = function() { ROC_AUTH.login = function() {
var form = document.forms['cms_basic_auth']; var form = document.forms['cms_basic_auth'];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
//var host = form.host.value; //var host = form.host.value;
var origin = window.location.origin + window.location.pathname; var origin = window.location.origin + window.location.pathname;
var _login = function(){ var _login = function(){
if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') !== null ) { if (document.getElementById('myModalFormId') === null ) {
ROC_AUTH.remove ('myModalFormId'); var newdiv = document.createElement('div');
} newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
} else {
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
// .. ?
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}
}
}
}
}
if (username === "" || password === "") { var userAgent = navigator.userAgent.toLowerCase();
if (document.getElementById('myModalFormId') === null ) { if (userAgent.indexOf("firefox") != -1) { //TODO: check version number
var newdiv = document.createElement('div'); if (firstLogIn) {
newdiv.innerHTML = "<br>Invalid Credentials</br>"; _login();
newdiv.id = 'myModalFormId'; } else {
$(".primary-tabs").append(newdiv); ROC_AUTH.logoff(_login);
} }
}else{ } else {
_login();
//Instantiate HTTP Request }
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
}
} if (firstLogIn) {
} firstLogIn = false;
} }
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.login_with_redirect = function() { ROC_AUTH.login_with_redirect = function() {
var form = document.forms[2]; var form = document.forms[2];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
var host = form.host.value; var host = form.host.value;
var _login = function(){ var _login = function(){
var redirectURL = form.redirect && form.redirect.value || "";
$("#imgProgressRedirect").show();
var redirectURL = form.redirect && form.redirect.value || ""; if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
} else {
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host + loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host + "/";
} else {
window.location=host + redirectURL;
}
} else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
$("#imgProgressRedirect").show(); var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (document.getElementById('myModalFormId') !== null ) { if (firstLogIn) {
ROC_AUTH.remove ('myModalFormId'); _login();
} } else {
ROC_AUTH.logoff(_login);
}
if (username === "" || password === "") { } else{
if (document.getElementById('myModalFormId') === null ) { _login();
var newdiv = document.createElement('div'); }
newdiv.innerHTML = "<br>Invalid Credentials</br>"; if (firstLogIn) {
newdiv.id = 'myModalFormId'; firstLogIn = false;
$(".primary-tabs").append(newdiv); }
$("#imgProgressRedirect").hide();
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host.concat(loginURL), true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host.concat("/");
} else {
window.location=host.concat(redirectURL);
}
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.getQueryParameterByName = function (name) { ROC_AUTH.getQueryParameterByName = function (name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search);
results = regex.exec(location.search); return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
} }
ROC_AUTH.logoff = function(callback){ ROC_AUTH.logoff = function(callback){
var form = document.forms[0]; var form = document.forms[0];
var host = form.host.value; var host = form.host.value;
if (userAgent.indexOf("msie") != -1) { if (userAgent.indexOf("msie") != -1) {
document.execCommand("ClearAuthenticationCache"); document.execCommand("ClearAuthenticationCache");
} } else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest();
var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest(); //Logout. Tell the server not to return the "WWW-Authenticate" header
request1.open("GET", host + logoutURL + "?prompt=false", true);
//Logout. Tell the server not to return the "WWW-Authenticate" header request1.send("");
request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); request1.onreadystatechange = function(){
request1.send(""); if (request1.readyState == 4) {
request1.onreadystatechange = function(){ //Sign in with dummy credentials to clear the auth cache
if (request1.readyState == 4) { request2.open("GET", host + logoutURL, true, "logout", "logout");
request2.send("");
//Sign in with dummy credentials to clear the auth cache request2.onreadystatechange = function(){
request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); if (request2.readyState == 4) {
request2.send(""); if (callback!=null) {
callback.call();
request2.onreadystatechange = function(){ } else {
if (request2.readyState == 4) { window.location=host + logoutURL;
if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} }
} }
} }
}
} }
} } else {
} var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
else { request.open("GET", host + logoutURL, true, "logout", "logout");
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); request.send("");
request.open("GET", host.concat(logoutURL), true, "logout", "logout"); request.onreadystatechange = function(){
request.send(""); if (request.status==401 || request.status==403 ) {
request.onreadystatechange = function(){ window.location=host + logoutURL;
if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); }
} }
} }
}
}; };
ROC_AUTH.remove = function (id) ROC_AUTH.remove = function (id) {
{ var element = document.getElementById(id);
var element = document.getElementById(id); element.outerHTML = "";
element.outerHTML = ""; delete element;
delete element; return;
return;
}; };
$(document).ready(function() { $(document).ready(function() {
if (typeof String.prototype.contains != 'function') {
if (typeof String.prototype.contains != 'function') { String.prototype.contains = function (str){
String.prototype.contains = function (str){ return this.indexOf(str) != -1;
return this.indexOf(str) != -1; };
}; }
} ROC_AUTH.progressive_loging();
ROC_AUTH.progressive_loging();
}); });
ROC_AUTH.progressive_loging = function () { ROC_AUTH.progressive_loging = function () {
ROC_AUTH.login_href();
ROC_AUTH.login_href();
}; };
$(document).keypress(function(e) { $(document).keypress(function(e) {
if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) {
ROC_AUTH.login(); ROC_AUTH.login();
} }
}); });
ROC_AUTH.OnOneClick = function(event) { ROC_AUTH.OnOneClick = function(event) {
event.preventDefault(); event.preventDefault();
if ( document.forms[0] === undefined ) { if ( document.forms[0] === undefined ) {
ROC_AUTH.create_form(); ROC_AUTH.create_form();
} }
return false; return false;
}; };
ROC_AUTH.login_href = function() { ROC_AUTH.login_href = function() {
var els = document.getElementsByTagName("a"); var els = document.getElementsByTagName("a");
for (var i = 0, l = els.length; i < l; i++) { for (var i = 0, l = els.length; i < l; i++) {
var el = els[i]; var el = els[i];
if (el.href.contains("/basic_auth_login?destination")) { if (el.href.contains(loginURL + "?destination")) {
loginURL = el.href; // loginURL = el.href;
var OneClick = el; var OneClick = el;
OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false);
} }
} }
}; };
ROC_AUTH.create_form = function() { ROC_AUTH.create_form = function() {
// Fetching HTML Elements in Variables by ID. // Fetching HTML Elements in Variables by ID.
var createform = document.createElement('form'); // Create New Element Form var createform = document.createElement('form'); // Create New Element Form
createform.setAttribute("action", ""); // Setting Action Attribute on Form createform.setAttribute("action", ""); // Setting Action Attribute on Form
createform.setAttribute("method", "post"); // Setting Method Attribute on Form createform.setAttribute("method", "post"); // Setting Method Attribute on Form
$("body").append(createform); $("body").append(createform);
var heading = document.createElement('h2'); // Heading of Form var heading = document.createElement('h2'); // Heading of Form
heading.innerHTML = "Login Form "; heading.innerHTML = "Login Form ";
createform.appendChild(heading); createform.appendChild(heading);
var line = document.createElement('hr'); // Giving Horizontal Row After Heading var line = document.createElement('hr'); // Giving Horizontal Row After Heading
createform.appendChild(line); createform.appendChild(line);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var namelabel = document.createElement('label'); // Create Label for Name Field var namelabel = document.createElement('label'); // Create Label for Name Field
namelabel.innerHTML = "Username : "; // Set Field Labels namelabel.innerHTML = "Username : "; // Set Field Labels
createform.appendChild(namelabel); createform.appendChild(namelabel);
var inputelement = document.createElement('input'); // Create Input Field for UserName var inputelement = document.createElement('input'); // Create Input Field for UserName
inputelement.setAttribute("type", "text"); inputelement.setAttribute("type", "text");
inputelement.setAttribute("name", "username"); inputelement.setAttribute("name", "username");
inputelement.setAttribute("required","required"); inputelement.setAttribute("required","required");
createform.appendChild(inputelement); createform.appendChild(inputelement);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var passwordlabel = document.createElement('label'); // Create Label for Password Field var passwordlabel = document.createElement('label'); // Create Label for Password Field
passwordlabel.innerHTML = "Password : "; passwordlabel.innerHTML = "Password : ";
createform.appendChild(passwordlabel); createform.appendChild(passwordlabel);
var passwordelement = document.createElement('input'); // Create Input Field for Password. var passwordelement = document.createElement('input'); // Create Input Field for Password.
passwordelement.setAttribute("type", "password"); passwordelement.setAttribute("type", "password");
passwordelement.setAttribute("name", "password"); passwordelement.setAttribute("name", "password");
passwordelement.setAttribute("id", "password"); passwordelement.setAttribute("id", "password");
passwordelement.setAttribute("required","required"); passwordelement.setAttribute("required","required");
createform.appendChild(passwordelement); createform.appendChild(passwordelement);
var passwordbreak = document.createElement('br'); var passwordbreak = document.createElement('br');
createform.appendChild(passwordbreak); createform.appendChild(passwordbreak);
var submitelement = document.createElement('button'); // Append Submit Button var submitelement = document.createElement('button'); // Append Submit Button
submitelement.setAttribute("type", "button"); submitelement.setAttribute("type", "button");
submitelement.setAttribute("onclick", "ROC_AUTH.login();"); submitelement.setAttribute("onclick", "ROC_AUTH.login();");
submitelement.innerHTML = "Sign In "; submitelement.innerHTML = "Sign In ";
createform.appendChild(submitelement); createform.appendChild(submitelement);
}; };
@@ -310,16 +294,16 @@ var password = document.getElementById("password");
var confirm_password = document.getElementById("confirm_password"); var confirm_password = document.getElementById("confirm_password");
ROC_AUTH.validatePassword =function(){ ROC_AUTH.validatePassword =function(){
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
if(password.value != confirm_password.value) { if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match"); confirm_password.setCustomValidity("Passwords Don't Match");
} else { } else {
confirm_password.setCustomValidity(''); confirm_password.setCustomValidity('');
} }
} }
} }
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
password.onchange = ROC_AUTH.validatePassword(); password.onchange = ROC_AUTH.validatePassword();
confirm_password.onkeyup = ROC_AUTH.validatePassword; confirm_password.onkeyup = ROC_AUTH.validatePassword;
} }

View File

@@ -1,29 +1,23 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Basic Auth" relies on the HTTP basic acces authentication.<br/>(see also: <a href="https://en.wikipedia.org/wiki/Basic_access_authentication">https://en.wikipedia.org/wiki/Basic_access_authentication</a> )</div>
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div> <div>
<div> <form name="cms_basic_auth" action="{$site_url/}roc-basic-login" method="POST">
<form name="cms_basic_auth" action method="POST"> <input type="hidden" name="host" id="host" value="{$site_url/}">
<div> <div>
<input type="text" name="username" id="username" required> <input type="text" name="username" id="username" required>
<label>Username</label> <label>Username</label>
</div> </div>
<div>
<div> <input type="password" name="password" id="password" required>
<input type="password" name="password" id="password" required> <label>Password</label>
<label>Password</label> </div>
</div> <button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
</div>
</div> </div>
<div> <div>
<div> <a href="{$site_url/}account/new-password">Forgot password?</a>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div> </div>
{/unless}
</div> </div>
{/unless}

View File

@@ -0,0 +1,6 @@
CREATE TABLE blog_post_nodes(
`nid` INTEGER NOT NULL CHECK("nid">=0),
`revision` INTEGER NOT NULL,
`tags` VARCHAR(255),
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);

View File

@@ -0,0 +1,8 @@
{
"--email": "webmaster@example.com",
"subjet": "Thank you for contacting us",
"recaptcha": {
"site_key":"",
"secret_key":""
}
}

View File

@@ -0,0 +1,124 @@
.contact-box {
background-color: #F2F7F9;
width: 465px;
padding: 20px;
border: 6px solid #8FB5C1;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
border-radius: 15px;
position: relative;
/* Remove box shadow firefox, chrome and opera put around required fields.
* It looks rubbish.
*/
/* Normalize placeholder styles */
/* chrome, safari */
/* mozilla */
/* ie (faux placeholder) */
}
.contact-box h1 {
font-size: 42px;
}
.contact-box h2 {
margin-bottom: 15px;
font-style: italic;
font-weight: normal;
}
.contact-box label {
font-size: 15px;
margin-bottom: 2px;
display: block;
}
.contact-box input, .contact-box select, .contact-box textarea {
width: 100%;
font-size: 15px;
border: 1px solid #CEE1E8;
margin-bottom: 20px;
padding: 4px;
}
.contact-box input:focus, .contact-box select:focus, .contact-box textarea:focus {
border: 1px solid #AFCDD8;
background-color: #EBF2F4;
}
.contact-box textarea {
height: 150px;
resize: none;
}
.contact-box span.required {
font-weight: bold;
color: #F00;
}
.contact-box input[type=submit] {
width: 100px;
background-color: #333;
color: #FFF;
border: none;
display: block;
float: right;
margin-bottom: 0px;
margin-right: 6px;
background-color: #8FB5C1;
-moz-border-radius: 8px;
}
.contact-box input[type=submit]:hover {
background-color: #A6CFDD;
}
.contact-box input[type=submit]:active {
position: relative;
top: 1px;
}
.contact-box .message {
width: 95%;
margin: 25px 0px;
padding: 10px;
display: block;
border: solid 1px #ccc;
border-radius: 8px;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
}
.contact-box .message.hidden {
display: none;
}
.contact-box .message.error {
border-color: #E58E8E;
background-color: #FFE6E6;
}
.contact-box .message.error li {
padding: 2px;
list-style: none;
}
.contact-box .message.error li:before {
content: ' - ';
}
.contact-box .message.error #info {
font-weight: bold;
}
.contact-box .message.error #info:before {
content: '';
}
.contact-box .message.success {
border-color: #83D186;
padding-top: 25px;
background-color: #D3EDD3;
}
.contact-box .req-field-desc {
font-style: italic;
}
.contact-box input:required, .contact-box textarea:required {
-moz-box-shadow: none;
-webkit-box-shadow: none;
-o-box-shadow: none;
box-shadow: none;
}
.contact-box ::-webkit-input-placeholder {
color: #CCC;
font-style: italic;
}
.contact-box input:-moz-placeholder, .contact-box textarea:-moz-placeholder {
color: #CCC;
font-style: italic;
}
.contact-box input.placeholder-text, .contact-box textarea.placeholder-text {
color: #CCC;
font-style: italic;
}

View File

@@ -0,0 +1,140 @@
.contact-box {
background-color:#F2F7F9;
width:465px;
padding:20px;
border: 6px solid #8FB5C1;
-moz-border-radius:15px;
-webkit-border-radius:15px;
border-radius:15px;
position:relative;
h1 {
font-size:42px;
}
h2 {
margin-bottom:15px;
font-style:italic;
font-weight:normal;
}
label {
font-size:15px;
margin-bottom:2px;
display:block;
}
input, select, textarea {
width:100%;
font-size:15px;
border: 1px solid #CEE1E8;
margin-bottom:20px;
padding:4px;
&:focus {
border: 1px solid #AFCDD8;
background-color: #EBF2F4;
}
}
textarea {
height:150px;
resize: none;
}
span.required {
font-weight:bold;
color:#F00;
}
input[type=submit] {
width: 100px;
background-color:#333;
color:#FFF;
border:none;
display:block;
float:right;
margin-bottom:0px;
margin-right:6px;
background-color:#8FB5C1;
-moz-border-radius:8px;
&:hover {
background-color: #A6CFDD;
}
&:active {
position:relative;
top:1px;
}
}
.message {
width:95%;
margin:25px 0px;
padding:10px;
display:block;
border:solid 1px #ccc;
border-radius:8px;
-webkit-border-radius:8px;
-moz-border-radius:8px;
&.hidden {
display: none;
}
&.error {
border-color: #E58E8E;
background-color:#FFE6E6;
li {
padding:2px;
list-style:none;
&:before { content: ' - '; }
}
#info {
font-weight:bold;
&:before { content: ''; }
}
}
&.success {
border-color: #83D186;
padding-top: 25px;
background-color:#D3EDD3;
}
}
.req-field-desc {
font-style:italic;
}
/* Remove box shadow firefox, chrome and opera put around required fields.
* It looks rubbish.
*/
input:required, textarea:required {
-moz-box-shadow:none;
-webkit-box-shadow:none;
-o-box-shadow:none;
box-shadow:none;
}
/* Normalize placeholder styles */
/* chrome, safari */
::-webkit-input-placeholder {
color:#CCC;
font-style:italic;
}
/* mozilla */
input:-moz-placeholder, textarea:-moz-placeholder {
color:#CCC;
font-style:italic;
}
/* ie (faux placeholder) */
input.placeholder-text, textarea.placeholder-text {
color:#CCC;
font-style:italic;
}
}

View File

@@ -0,0 +1,25 @@
<div class="contact-box clearfix">
<h1>Contact us!</h1>
<form method="post" action="{$site_url/}contact" id="contact-form">
<label for="name">Name: <span class="required">*</span></label>
<input type="text" id="name" name="name" value="{$name/}" required="required" autofocus="autofocus" />
<label for="email">Email Address: <span class="required">*</span></label>
<input type="email" id="email" name="email" value="{$email/}" required="required" />
<label for="message">Message: <span class="required">*</span></label>
<textarea id="message" name="message" required="required" data-minlength="20" minlength="20" >{$message/}</textarea>
{unless isempty="$recaptcha_site_key"}
<div class="g-recaptcha" data-sitekey="{$recaptcha_site_key/}"></div>
<br/>
{/unless}
<input type="submit" value="Send" class="submit-button" />
<p class="req-field-desc"><span class="required">*</span> indicates a required field</p>
</form>
{unless isempty="$error_response"}
<ul class="message error">
{foreach item="item" from="$error_response"}<li class="info">{$item/}</li>{/foreach}
</ul>
<div class="notice"> Try again later </div>
{/unless}
</div>

View File

@@ -0,0 +1,15 @@
<div class="contact-box">
{if condition="$has_error"}
<div class="message error">
<strong>Internal Server Error <small>Error 500</small></strong>
<p>The page you requested could not be served because the server is down,
either contact the webmaster or try again.
Use your browser's <strong>Back</strong> button to navigate to the page you came from.</p>
<p><strong>Or you could just press this link:</strong> <a href="{$site_url/}" itemprop="home" rel="home">Take Me Home</a></p>
</div>
{/if}
{unless condition="$has_error"}
<p class="message success">Thank you for contacting the Eiffel Programming Language community.<br/>
We will get back to you promptly on your contact request.</p>
{/unless}
</div>

View File

@@ -0,0 +1,10 @@
<p>
Thank you for contacting {$sitename/}.<br/>
We will get back to you promptly about your contact message.
</p>
<h2>Your contact information:</h2>
<div>
<strong>Name<strong>: {$name/} <br/>
<strong>Email<strong>: {$email/} <br/>
<strong>Message<strong>: {$message/} <br/>
</div>

View File

@@ -0,0 +1,6 @@
<h2>Contact information:</h2>
<div>
<strong>Name<strong>: {$name/}<br/>
<strong>Email<strong>: {$email/} <br/>
<strong>Message<strong>: {$message/} <br/>
</div>

View File

@@ -3,22 +3,17 @@ ul.cms-nodes {
list-style-type: none; list-style-type: none;
padding: 3px 3px 3px 3px; padding: 3px 3px 3px 3px;
border: solid 1px #ccc; border: solid 1px #ccc;
li{ li{
border-top: dotted 1px #ccc; border-top: dotted 1px #ccc;
&:first-child { &:first-child {
border-top: none; border-top: none;
} }
} }
li.cms_type_page a::before { li.cms_type_page a::before {
content: "[page] "; content: "[page] ";
} }
li.cms_type_blog a::before { li.cms_type_blog a::before {
content: "[blog] "; content: "[blog] ";
} }
} }

View File

@@ -0,0 +1,9 @@
CREATE TABLE auth_session (
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
`access_token` VARCHAR(64) NOT NULL,
`created` DATETIME NOT NULL,
CONSTRAINT `uid` UNIQUE(`uid`),
CONSTRAINT `access_token` UNIQUE(`access_token`)
);

View File

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

View File

@@ -1,37 +1,23 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Session" is the standard authentication system. (based on cookie)</div>
<div> <h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div> <div>
<form name="cms_session_auth" action="{$site_url/}account/login-with-session" method="POST"> <form name="cms_session_auth" action="{$site_url/}account/auth/roc-session-login" method="POST">
<div>
<input type="text" name="username" id="username" required value="{$username/}">
<label>Username</label>
</div>
<div>
<input type="password" name="password" id="password" required >
<label>Password</label>
</div>
<button type="submit">Login</button>
</form>
</div>
</div>
<div>
<div> <div>
<p> <input type="text" name="username" id="username" required value="{$username/}">
<a href="{$site_url/}account/new-password">Forgot password?</a> <label>Username</label>
</p>
</div> </div>
</div>
{/unless}
{if isset=$error}
<div> <div>
<div> <input type="password" name="password" id="password" required >
<p> <label>Password</label>
<strong>{$error/}
</p>
</div> </div>
</div> <button type="submit">Login</button>
{/if} </form>
</div> </div>
<div>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</div>
{if isset="$error"}<div class="error">{$error/}</div>{/if}
</div>
{/unless}

View File

@@ -51,11 +51,18 @@ feature -- CMS modules
a_setup.register_module (create {CMS_BASIC_AUTH_MODULE}.make) a_setup.register_module (create {CMS_BASIC_AUTH_MODULE}.make)
a_setup.register_module (create {CMS_OAUTH_20_MODULE}.make) a_setup.register_module (create {CMS_OAUTH_20_MODULE}.make)
a_setup.register_module (create {CMS_OPENID_MODULE}.make) a_setup.register_module (create {CMS_OPENID_MODULE}.make)
a_setup.register_module (create {CMS_SESSION_AUTH_MODULE}.make)
-- Nodes -- Nodes
a_setup.register_module (create {CMS_NODE_MODULE}.make (a_setup)) a_setup.register_module (create {CMS_NODE_MODULE}.make (a_setup))
a_setup.register_module (create {CMS_BLOG_MODULE}.make) a_setup.register_module (create {CMS_BLOG_MODULE}.make)
-- Contact
a_setup.register_module (create {CMS_CONTACT_MODULE}.make)
-- Misc
a_setup.register_module (create {CMS_SEO_MODULE}.make)
-- Taxonomy -- Taxonomy
a_setup.register_module (create {CMS_TAXONOMY_MODULE}.make) a_setup.register_module (create {CMS_TAXONOMY_MODULE}.make)
@@ -66,10 +73,9 @@ feature -- CMS modules
a_setup.register_module (create {FEED_AGGREGATOR_MODULE}.make) a_setup.register_module (create {FEED_AGGREGATOR_MODULE}.make)
-- Miscellanious -- Miscellanious
a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE}.make)
a_setup.register_module (create {CMS_DEBUG_MODULE}.make) a_setup.register_module (create {CMS_DEBUG_MODULE}.make)
a_setup.register_module (create {CMS_DEMO_MODULE}.make) a_setup.register_module (create {CMS_DEMO_MODULE}.make)
a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE}.make)
a_setup.register_module (create {CMS_SESSION_AUTH_MODULE}.make)
end end
end end

View File

@@ -37,6 +37,80 @@ feature -- Access
weight: INTEGER weight: INTEGER
-- Optional weight used for order. -- Optional weight used for order.
query_string: detachable STRING
-- Query string from `location'.
local
i: INTEGER
loc: like location
do
loc := location
i := loc.index_of ('?', 1)
if i > 0 then
Result := loc.substring (i + 1, loc.count)
i := loc.last_index_of ('#', loc.count)
if i > 0 then
Result.keep_head (i - 1)
end
end
end
fragment_string: detachable STRING
-- Query string from `location'.
local
i: INTEGER
loc: like location
do
loc := location
i := loc.last_index_of ('#', loc.count)
if i > 0 then
Result := loc.substring (i + 1, loc.count)
end
end
feature -- Element change
add_query_parameter (a_encoded_name: READABLE_STRING_8; a_encoded_value: detachable READABLE_STRING_8)
-- Add query parameter "$a_encoded_name=$a_encoded_value" to `location'.
-- note: the argument must already be url encoded!
local
q: STRING_8
f: detachable READABLE_STRING_8
i,j: INTEGER
loc: STRING_8
do
create loc.make_from_string (location)
j := loc.last_index_of ('#', loc.count)
if j > 0 then
f := loc.substring (j, loc.count)
loc.keep_head (j - 1)
end
i := loc.index_of ('?', 1)
if i > 0 then
q := loc.substring (i + 1, loc.count)
loc.keep_head (i)
else
create q.make_empty
end
if not q.is_empty then
q.append_character ('&')
end
q.append (a_encoded_name)
if a_encoded_value /= Void then
q.append_character ('=')
q.append (a_encoded_value)
end
loc.append_character ('?')
loc.append (q)
if f /= Void then
loc.append (f)
end
location := loc
end
feature -- Comparison feature -- Comparison
is_less alias "<" (other: like Current): BOOLEAN is_less alias "<" (other: like Current): BOOLEAN
@@ -134,6 +208,6 @@ feature -- Status report
end end
note note
copyright: "2011-2015, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -65,9 +65,6 @@ feature -- Access
email: detachable READABLE_STRING_8 email: detachable READABLE_STRING_8
-- User email. -- User email.
profile: detachable CMS_USER_PROFILE
-- User profile.
creation_date: DATE_TIME creation_date: DATE_TIME
-- Creation date. -- Creation date.
@@ -189,26 +186,6 @@ feature -- Change element
email_set: email = a_email email_set: email = a_email
end end
set_profile (prof: like profile)
-- Set `profile' with `prof'.
do
profile := prof
ensure
profile_set: profile = prof
end
set_profile_item (k: READABLE_STRING_8; v: READABLE_STRING_8)
local
prof: like profile
do
prof := profile
if prof = Void then
create prof.make
profile := prof
end
prof.force (v, k)
end
set_last_login_date (dt: like last_login_date) set_last_login_date (dt: like last_login_date)
do do
last_login_date := dt last_login_date := dt
@@ -284,7 +261,6 @@ feature -- Status change
status_set: status = a_status status_set: status = a_status
end end
feature -- User status feature -- User status
not_active: INTEGER = 0 not_active: INTEGER = 0

View File

@@ -1,56 +0,0 @@
note
description: "[
User profile used to extend information associated with a {CMS_USER}.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_USER_PROFILE
inherit
TABLE_ITERABLE [READABLE_STRING_8, READABLE_STRING_GENERAL]
create
make
feature {NONE} -- Initialization
make
-- Create Current profile.
do
create items.make (0)
end
feature -- Access
item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
-- Profile item associated with key `k'.
do
Result := items.item (k)
end
feature -- Change
force (v: READABLE_STRING_8; k: READABLE_STRING_GENERAL)
-- Associated value `v' with key `k'.
do
items.force (v, k)
end
feature -- Access
new_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_GENERAL]
-- Fresh cursor associated with current structure
do
Result := items.new_cursor
end
feature {NONE} -- Implementation
items: STRING_TABLE [READABLE_STRING_8]
;note
copyright: "2011-2014, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -80,7 +80,7 @@ feature -- Cursor
feature -- Action feature -- Action
action: FUNCTION [ANY, detachable TUPLE, G] action: FUNCTION [DB_TUPLE, G]
-- Agent to create a new item of type G. -- Agent to create a new item of type G.
feature {NONE} -- Implementation feature {NONE} -- Implementation

View File

@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="store_mysql" uuid="DC757CBD-D8C4-44D6-A07F-C1148D8D233E" library_target="store_mysql"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="store_mysql" uuid="DC757CBD-D8C4-44D6-A07F-C1148D8D233E" library_target="store_mysql">
<description>CMS Eiffel Store MySQL persistence solution</description>
<target name="store_mysql"> <target name="store_mysql">
<root all_classes="true"/> <root all_classes="true"/>
<option warning="true" void_safety="all"> <option warning="true" is_obsolete_routine_type="false" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option> </option>
<setting name="console_application" value="true"/> <setting name="console_application" value="true"/>
@@ -26,9 +27,9 @@
</cluster> </cluster>
<cluster name="persistence_store_mysql" location=".\src\" recursive="true"> <cluster name="persistence_store_mysql" location=".\src\" recursive="true">
<file_rule> <file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude> <exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
</cluster> </cluster>
</target> </target>

View File

@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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="persistence_store_odbc" uuid="8FD9D3B3-5FC1-495F-A05D-0205EC966841" library_target="persistence_store_odbc"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="persistence_store_odbc" uuid="8FD9D3B3-5FC1-495F-A05D-0205EC966841" library_target="persistence_store_odbc">
<target name="persistence_store_odbc"> <target name="persistence_store_odbc">
<description>CMS Eiffel Store ODBC persistence solution</description>
<root all_classes="true"/> <root all_classes="true"/>
<option warning="true" void_safety="all"> <option warning="true" is_obsolete_routine_type="false" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option> </option>
<setting name="console_application" value="true"/> <setting name="console_application" value="true"/>
@@ -22,9 +23,9 @@
<cluster name="common" location="..\implementation\store\" recursive="true"/> <cluster name="common" location="..\implementation\store\" recursive="true"/>
<cluster name="persistence_store_odbc" location=".\src\" recursive="true"> <cluster name="persistence_store_odbc" location=".\src\" recursive="true">
<file_rule> <file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude> <exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule> </file_rule>
</cluster> </cluster>
</target> </target>

View File

@@ -166,7 +166,7 @@ feature -- Error
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor> -- <Precursor>
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then

View File

@@ -166,7 +166,7 @@ feature -- Error
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor> -- <Precursor>
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then

View File

@@ -86,12 +86,13 @@ feature -- Execution
not l_roles.is_empty not l_roles.is_empty
then then
s.append ("<h4>Role(s):</h4>") s.append ("<h4>Role(s):</h4>")
s.append ("<ul class=%"user-roles%">")
across l_roles as ic loop across l_roles as ic loop
l_role := ic.item l_role := ic.item
s.append ("<i>") s.append ("<li>")
s.append (link (l_role.name, "admin/role/" + l_role.id.out, Void)) s.append (link (l_role.name, "admin/role/" + l_role.id.out, Void))
s.append ("</i>") s.append ("</li>")
debug if request.query_parameter ("debug") /= Void then
s.append ("<h5>Permissions:</h5>") s.append ("<h5>Permissions:</h5>")
s.append ("<ul class=%"cms-permissions%">%N") s.append ("<ul class=%"cms-permissions%">%N")
across l_role.permissions as perms_ic loop across l_role.permissions as perms_ic loop
@@ -100,6 +101,7 @@ feature -- Execution
s.append ("</ul>%N") s.append ("</ul>%N")
end end
end end
s.append ("</ul>%N")
end end
s.append ("</div>") s.append ("</div>")

View File

@@ -0,0 +1,17 @@
note
description: "[
Common interface for Auth module API.
]"
date: "$Date$"
revision: "$Revision$"
class
CMS_AUTH_API_I
inherit
CMS_MODULE_API
create
make
end

View File

@@ -0,0 +1,44 @@
note
description: "[
Processes a HTTP request, and depending on header, authenticate a current user or not.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_AUTH_FILTER_I
inherit
WSF_FILTER
feature {NONE} -- Initialization
make (a_api: CMS_API)
-- Initialize Current handler with `a_api'.
do
api := a_api
end
feature -- API Service
api: CMS_API
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
deferred
end
auth_strategy: STRING
deferred
end
set_current_user (u: CMS_USER)
do
api.set_user (u)
-- Record auth strategy:
api.set_execution_variable ("auth_strategy", auth_strategy)
end
end

View File

@@ -0,0 +1,108 @@
note
description: "Common ancestor for Authentication modules."
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_AUTH_MODULE_I
inherit
CMS_MODULE
redefine
setup_hooks
end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_LOGGER
feature {NONE} -- Initialization
make
do
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
end
feature -- Access: auth strategy
login_title: READABLE_STRING_GENERAL
-- Module specific login title.
deferred
end
login_location: STRING
-- Login cms location for Current module.
deferred
end
logout_location: STRING
-- Logout cms location for Current module.
deferred
end
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- Is Current module strategy currently authenticating active user?
deferred
ensure
Result implies a_response.is_authenticated
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_menu_system_alter_hook (Current)
end
feature -- Hooks
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- <Precursor>
local
lnk: CMS_LOCAL_LINK
l_destination: READABLE_STRING_8
do
if attached {WSF_STRING} a_response.request.query_parameter ("destination") as p_destination then
l_destination := p_destination.value
else
l_destination := a_response.location
end
if is_authenticating (a_response) then
else
if a_response.location.starts_with ("account/auth/") then
create lnk.make (login_title, login_location)
if not l_destination.starts_with ("account/auth/") then
lnk.add_query_parameter ("destination", l_destination)
end
lnk.set_expandable (True)
a_response.add_to_primary_tabs (lnk)
end
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
end

View File

@@ -130,13 +130,13 @@ feature -- Access
account_re_activation: STRING account_re_activation: STRING
-- Account re_activation template email message. -- Account re_activation template email message.
do do
Result := template_string ("accunt_re_activation.html", default_template_account_re_activation) Result := template_string ("account_re_activation.html", default_template_account_re_activation)
end end
account_rejected: STRING account_rejected: STRING
-- Account rejected template email message. -- Account rejected template email message.
do do
Result := template_string ("accunt_rejected.html", default_template_account_rejected) Result := template_string ("account_rejected.html", default_template_account_rejected)
end end
account_password: STRING account_password: STRING
@@ -277,7 +277,7 @@ feature {NONE} -- Message email
</head> </head>
<body> <body>
<p>You requested has been rejected, your application does not conform our rules <a href="$host">$sitename</a></p> <p>Your account application is rejected, it does not conform our rules <a href="$host">$sitename</a></p>
</body> </body>
</html> </html>
]" ]"
@@ -316,7 +316,7 @@ feature {NONE} -- Message email
</head> </head>
<body> <body>
<p>You have required a new password at <a href="$host">$sitename</a></p> <p>You have requested a new password at <a href="$host">$sitename</a></p>
<p>To complete your request, please click on this link to generate a new password:<p> <p>To complete your request, please click on this link to generate a new password:<p>
@@ -337,7 +337,7 @@ feature {NONE} -- Message email
</head> </head>
<body> <body>
<p>Welcome to<a href="...">$sitename</a></p> <p>Welcome to <a href="$host">$sitename</a>.</p>
<p>Thank you for joining us.</p> <p>Thank you for joining us.</p>
</body> </body>
</html> </html>

View File

@@ -1,13 +1,12 @@
note note
description: "Module Auth" description: "Module Auth"
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $" date: "$Date$"
revision: "$Revision: 97328 $" revision: "$Revision$"
class class
CMS_AUTHENTICATION_MODULE CMS_AUTHENTICATION_MODULE
inherit inherit
CMS_MODULE CMS_MODULE
redefine redefine
setup_hooks, setup_hooks,
@@ -33,8 +32,6 @@ inherit
SHARED_LOGGER SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -82,6 +79,10 @@ feature -- Access: docs
feature -- Router feature -- Router
roc_login_location: STRING = "account/roc-login"
roc_logout_location: STRING = "account/roc-logout"
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor> -- <Precursor>
do do
@@ -90,18 +91,25 @@ feature -- Router
end end
configure_web (a_api: CMS_API; a_router: WSF_ROUTER) configure_web (a_api: CMS_API; a_router: WSF_ROUTER)
local
m: WSF_URI_MAPPING
do do
a_router.handle ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account(a_api, ?, ?)), a_router.methods_head_get) create m.make_trailing_slash_ignored ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account (a_api, ?, ?)))
a_router.handle ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get) a_router.map (m, a_router.methods_head_get)
a_router.handle ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout(a_api, ?, ?)), a_router.methods_head_get)
create m.make_trailing_slash_ignored ("/account/edit", create {WSF_URI_AGENT_HANDLER}.make (agent handle_edit_account (a_api, ?, ?)))
a_router.map (m, a_router.methods_head_get)
a_router.handle ("/" + roc_login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/" + roc_logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout(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/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/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation(a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/reject/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_reject(a_api, ?, ?)), a_router.methods_head_get) a_router.handle ("/account/reject/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_reject(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/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/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/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password(a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_change_password(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/change/{field}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_change_field (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/post-change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_change_password(a_api, ?, ?)), a_router.methods_get)
end end
@@ -124,8 +132,35 @@ feature -- Hooks configuration
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor> -- <Precursor>
local
l_destination: detachable READABLE_STRING_GENERAL
l_url: STRING
l_url_name: READABLE_STRING_GENERAL
do do
a_value.force (a_response.user, "user") if attached {WSF_STRING} a_response.request.query_parameter ("destination") as p_destination then
l_destination := p_destination.value
else
l_destination := a_response.location
end
if l_destination.starts_with ("account/auth/") then
l_destination := Void
end
if attached a_response.user as u then
a_value.force (u, "user")
l_url_name := "site_sign_out_url"
l_url := a_response.url (roc_logout_location, Void)
else
a_value.force (Void, "user")
l_url_name := "site_sign_in_url"
l_url := a_response.url (roc_login_location, Void)
end
if l_destination /= Void then
l_url.append ("?destination=" + percent_encoded (l_destination))
end
a_value.force (l_url, l_url_name)
end end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
@@ -138,14 +173,22 @@ feature -- Hooks configuration
create lnk.make (u.name, "account") create lnk.make (u.name, "account")
lnk.set_weight (97) lnk.set_weight (97)
a_menu_system.primary_menu.extend (lnk) a_menu_system.primary_menu.extend (lnk)
create lnk.make ("Logout", "account/roc-logout")
lnk.set_weight (98) create lnk.make ("Logout", roc_logout_location)
a_menu_system.primary_menu.extend (lnk)
else else
create lnk.make ("Login", "account/roc-login") create lnk.make ("Login", roc_login_location)
lnk.set_weight (98)
a_menu_system.primary_menu.extend (lnk)
end end
lnk.set_weight (98)
if
a_response.location.starts_with_general ("account/auth/")
or a_response.location.starts_with_general ("account/roc-log") -- in ou out
then
-- ignore destination
else
lnk.add_query_parameter ("destination", percent_encoded (a_response.location))
end
a_menu_system.primary_menu.extend (lnk)
-- Add the link to the taxonomy to the main menu -- Add the link to the taxonomy to the main menu
if a_response.has_permission ("admin registration") then if a_response.has_permission ("admin registration") then
create lnk.make ("Registration", "admin/pending-registrations/") create lnk.make ("Registration", "admin/pending-registrations/")
@@ -158,18 +201,78 @@ feature -- Handler
handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_user: detachable CMS_USER
b: STRING
lnk: CMS_LOCAL_LINK
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
create b.make_empty
l_user := r.user
if attached template_block ("account_info", r) as l_tpl_block then if attached template_block ("account_info", r) as l_tpl_block then
if attached r.user as l_user then l_tpl_block.set_weight (-10)
r.set_value (api.user_api.user_roles (l_user), "roles")
end
r.add_block (l_tpl_block, "content") r.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
r.add_warning_message ("Error with block [resources_page]") r.add_warning_message ("Error with block [resources_page]")
end end
end end
if r.is_authenticated then
create lnk.make ("View", "account/")
lnk.set_weight (1)
r.add_to_primary_tabs (lnk)
create lnk.make ("Edit", "account/edit")
lnk.set_weight (2)
r.add_to_primary_tabs (lnk)
end
r.set_main_content (b)
if l_user = Void then
r.set_redirection (roc_login_location)
end
r.execute
end
handle_edit_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
l_user: detachable CMS_USER
b: STRING
f: CMS_FORM
lnk: CMS_LOCAL_LINK
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
create b.make_empty
l_user := r.user
if attached template_block ("account_edit", r) as l_tpl_block then
l_tpl_block.set_weight (-10)
r.add_block (l_tpl_block, "content")
else
debug ("cms")
r.add_warning_message ("Error with block [resources_page]")
end
end
create lnk.make ("View", "account/")
lnk.set_weight (1)
r.add_to_primary_tabs (lnk)
create lnk.make ("Edit", "account/edit")
lnk.set_weight (2)
r.add_to_primary_tabs (lnk)
f := new_change_password_form (r)
f.append_to_html (r.wsf_theme, b)
f := new_change_email_form (r)
f.append_to_html (r.wsf_theme, b)
r.set_main_content (b)
if l_user = Void then
r.set_redirection ("account")
end
r.execute r.execute
end end
@@ -177,10 +280,30 @@ feature -- Handler
local local
r: CMS_RESPONSE r: CMS_RESPONSE
do do
if attached api.module_by_name ("basic_auth") then if api.user_is_authenticated then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection ("account")
r.execute
elseif attached api.module_by_name ("session_auth") then
-- FIXME: find better solution to support a default login system. -- FIXME: find better solution to support a default login system.
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (r.absolute_url ("/account/roc-basic-auth", Void)) if attached {WSF_STRING} req.query_parameter ("destination") as l_destination then
r.set_redirection ("account/auth/roc-session-login?destination=" + l_destination.url_encoded_value)
else
r.set_redirection ("account/auth/roc-session-login")
end
r.execute
elseif attached api.module_by_name ("basic_auth") then
-- FIXME: find better solution to support a default login system.
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.query_parameter ("destination") as l_destination then
r.set_redirection ("account/auth/roc-basic-login?destination=" + l_destination.url_encoded_value)
else
r.set_redirection ("account/auth/roc-basic-login")
end
r.execute r.execute
else else
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
@@ -191,9 +314,19 @@ feature -- Handler
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
loc: STRING
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (r.absolute_url ("", Void)) if attached {READABLE_STRING_8} api.execution_variable ("auth_strategy") as l_auth_strategy then
loc := l_auth_strategy
else
loc := ""
end
if attached {WSF_STRING} req.query_parameter ("destination") as l_destination then
loc.append ("?destination=" + l_destination.url_encoded_value)
end
r.set_redirection (loc)
r.execute r.execute
end end
@@ -228,7 +361,7 @@ feature -- Handler
l_exist := True l_exist := True
end end
if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then
-- Emails already exist. -- Email already exists.
r.set_value ("An account is already associated with that email address!", "error_email") r.set_value ("An account is already associated with that email address!", "error_email")
l_exist := True l_exist := True
end end
@@ -520,45 +653,91 @@ feature -- Handler
r.execute r.execute
end end
handle_change_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_change_field (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_user_api: CMS_USER_API l_user_api: CMS_USER_API
f: CMS_FORM
l_fieldname: detachable READABLE_STRING_8
b: STRING
lnk: CMS_LOCAL_LINK
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if attached {WSF_STRING} req.path_parameter ("field") as p_field then
l_user_api := api.user_api l_fieldname := p_field.url_encoded_value
if req.is_post_request_method then end
if attached r.user as l_user then if l_fieldname = Void then
r.set_value (api.user_api.user_roles (l_user), "roles") create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password and then l_password.value.same_string (l_confirm_password.value) then else
-- Does the passwords match? create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_user.set_password (l_password.value)
l_user_api.update_user (l_user) if r.is_authenticated then
r.set_redirection (req.absolute_script_url ("/account/post-change-password")) create lnk.make ("View", "account/")
else lnk.set_weight (1)
if attached template_block ("account_info", r) as l_tpl_block then r.add_to_primary_tabs (lnk)
-- r.set_value (l_user, "user")
r.set_value ("Passwords Don't Match", "error_password") create lnk.make ("Edit", "account/edit")
r.set_status_code ({HTTP_CONSTANTS}.bad_request) lnk.set_weight (2)
r.add_block (l_tpl_block, "content") r.add_to_primary_tabs (lnk)
end
l_user_api := api.user_api
if req.is_post_request_method then
if attached r.user as l_user then
if l_fieldname.is_case_insensitive_equal ("password") then
if
attached {WSF_STRING} req.form_parameter ("password") as l_password and then
attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password and then
l_password.value.same_string (l_confirm_password.value)
then
-- passwords matched?
l_user.set_password (l_password.value)
l_user_api.update_user (l_user)
r.add_success_message ("Password updated.")
r.set_redirection ("account/")
r.set_redirection_delay (3)
else
r.add_error_message ("Passwords do not match!")
f := new_change_password_form (r)
r.set_main_content (f.to_html (r.wsf_theme))
end
elseif l_fieldname.is_case_insensitive_equal ("email") then
-- FIXME: find a safer workflow .. allow multiple emails, and have a primary email?
if
attached {WSF_STRING} req.form_parameter ("email") as l_email and then
attached {WSF_STRING} req.form_parameter ("confirm_email") as l_confirm_email and then
l_email.value.same_string (l_confirm_email.value) and then
l_email.value.is_valid_as_string_8
then
-- emails matched?
l_user.set_email (l_email.value.to_string_8)
l_user_api.update_user (l_user)
r.add_success_message ("Email updated.")
r.set_redirection ("account/")
r.set_redirection_delay (3)
else
r.add_error_message ("Emails do not match!")
f := new_change_email_form (r)
r.set_main_content (f.to_html (r.wsf_theme))
end
else
r.add_error_message ("You can not change %"" + l_fieldname + "%" information!")
end end
end end
else
create b.make_empty
if l_fieldname.is_case_insensitive_equal_general ("password") then
f := new_change_password_form (r)
f.append_to_html (r.wsf_theme, b)
elseif l_fieldname.is_case_insensitive_equal_general ("email") then
f := new_change_email_form (r)
f.append_to_html (r.wsf_theme, b)
end
r.set_main_content (b)
end end
end end
r.execute r.execute
end end
handle_post_change_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached template_block ("post_change", r) as l_tpl_block then
r.add_block (l_tpl_block, "content")
end
r.execute
end
handle_admin_pending_registrations (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API) handle_admin_pending_registrations (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local local
l_response: CMS_RESPONSE l_response: CMS_RESPONSE
@@ -651,37 +830,75 @@ feature -- Handler
end end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"register", "reactivate", "new_password", "reset_password", "registration">> Result := <<"register", "reactivate", "new_password", "reset_password", "registration">>
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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
local
loc: READABLE_STRING_8
do do
if a_block_id.is_case_insensitive_equal_general ("register") and then a_response.location.starts_with ("account/roc-register") then loc := a_response.location
if a_block_id.is_case_insensitive_equal_general ("register") and then loc.starts_with ("account/roc-register") then
get_block_view_register (a_block_id, a_response) get_block_view_register (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then a_response.location.starts_with ("account/reactivate") then elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then loc.starts_with ("account/reactivate") then
get_block_view_reactivate (a_block_id, a_response) get_block_view_reactivate (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then a_response.location.starts_with ("account/new-password") then elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then loc.starts_with ("account/new-password") then
get_block_view_new_password (a_block_id, a_response) 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.location.starts_with ("account/reset-password") then elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then loc.starts_with ("account/reset-password") then
get_block_view_reset_password (a_block_id, a_response) get_block_view_reset_password (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("registration") and then a_response.location.starts_with ("admin/pending-registrations") then elseif a_block_id.is_case_insensitive_equal_general ("registration") and then loc.starts_with ("admin/pending-registrations") then
get_block_view_registration (a_block_id, a_response) get_block_view_registration (a_block_id, a_response)
end end
end end
new_change_password_form (a_response: CMS_RESPONSE): CMS_FORM
local
fs: WSF_FORM_FIELD_SET
pwd: WSF_FORM_PASSWORD_INPUT
do
create Result.make (a_response.url ("account/change/password", Void), "change-password-form")
create fs.make
fs.set_legend ("Change password")
Result.extend (fs)
create pwd.make ("password")
pwd.set_label ("Password")
pwd.enable_required
fs.extend (pwd)
create pwd.make ("confirm_password")
pwd.set_label ("Confirm password")
pwd.enable_required
fs.extend (pwd)
-- create but.make_with_text ("op", "Confirm")
-- fs.extend (but)
fs.extend_html_text ("<button type=%"submit%">Confirm</button>")
end
new_change_email_form (a_response: CMS_RESPONSE): CMS_FORM
local
fs: WSF_FORM_FIELD_SET
tf: WSF_FORM_EMAIL_INPUT
do
create Result.make (a_response.url ("account/change/email", Void), "change-email-form")
create fs.make
fs.set_legend ("Change email")
Result.extend (fs)
create tf.make ("email")
tf.set_label ("Email")
tf.enable_required
fs.extend (tf)
create tf.make ("confirm_email")
tf.set_label ("Confirm email")
tf.enable_required
fs.extend (tf)
fs.extend_html_text ("<button type=%"submit%">Confirm</button>")
end
feature {NONE} -- Token Generation feature {NONE} -- Token Generation
new_token: STRING new_token: STRING

View File

@@ -0,0 +1 @@
{include file="block_account_info.tpl" /}

View File

@@ -1,62 +1,34 @@
<div class="primary-tabs"> <div class="primary-tabs">
{if isset="$user"} {if isset="$user"}
<h3>Account Information</h3> <h3>Account Information</h3>
<div> <ul class="user-information">
<div> <div>
<div> <label>Username:</label> {$user.name/}
<label>Username:</label> {$user.name/}
</div>
<div>
<label>Email:</label> {$user.email/}
</div>
<div>
<label>Creation Date:</label> {$user.creation_date/}
</div>
<div>
<label>Last login:</label> {$user.last_login_date/}
</div>
<div>
<form method="get" action="{$site_url/}{$auth_login_strategy/}">
<button type="submit">Logout</button>
</form>
</div>
</div> </div>
</div> <div>
<hr> <label>Email:</label> {$user.email/}
{include file="block_change_password.tpl" /} </div>
<hr> <div>
<h4>Roles</h4> <label>Creation Date:</label> {$user.creation_date/} (UTC)
<div> </div>
{foreach item="ic" from="$roles"} <div>
<div> <label>Last login:</label> {$user.last_login_date/} (UTC)
<ul> </div>
<li> <div>
<strong>{$ic.name/}</strong> <form method="get" action="{$site_url/}account/roc-logout">
<ul> <button type="submit">Logout</button>
<li> <i>permissions</i> </form>
<ul> </div>
{foreach item="ip" from="$ic.permissions"} </ul>
<li>{$ip/}</li>
{/foreach}
</ul>
</li>
</ul>
</li>
</ul>
</div>
{/foreach}
</div>
<hr> <hr>
<h4>Profile</h4> <h4>Profile</h4>
<div> <ul class="user-profile">
{foreach item="the_value" key="the_name" from="$user.profile"} {foreach item="the_value" key="the_name" from="$user.profile"}
<div> <li>
<label>{$the_name/}:</label> {$the_value/} <label>{$the_name/}:</label><div>{$the_value/}</div>
</div> </li>
{/foreach} {/foreach}
</div> </ul>
{/if} {/if}
{unless isset="$user"} {unless isset="$user"}
<div> <div>

View File

@@ -1,7 +1,7 @@
<div> <div>
<form action="{$site_url/}account/change-password" method="post"> <form action="{$site_url/}account/change-password" method="post">
<fieldset> <fieldset>
<legend>Change Password Form</legend> <legend>Change Password</legend>
<div> <div>
<input type="password" id="password" name="password" value="" required/> <input type="password" id="password" name="password" value="" required/>
<label for="password">Password</label> <label for="password">Password</label>

View File

@@ -1,29 +0,0 @@
<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>
{/unless}
</div>

View File

@@ -10,24 +10,17 @@ class
CMS_BASIC_AUTH_MODULE CMS_BASIC_AUTH_MODULE
inherit inherit
CMS_MODULE CMS_AUTH_MODULE_I
rename
module_api as basic_auth_api
redefine redefine
make,
filters, filters,
setup_hooks setup_hooks
end end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -35,26 +28,42 @@ feature {NONE} -- Initialization
make make
do do
Precursor
version := "1.0" version := "1.0"
description := "Service to manage basic authentication" description := "Service to manage basic authentication"
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
end end
feature -- Access feature -- Access
name: STRING = "basic_auth" name: STRING = "basic_auth"
feature -- Access: router feature -- Access: auth strategy
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) login_title: STRING = "Basic Auth"
-- Module specific login title.
login_location: STRING = "account/auth/roc-basic-login"
do_login_location: STRING = "roc-basic-login" -- IMPORTANT: it has to be at the root !
logout_location: STRING = "roc-basic-logoff" -- IMPORTANT: it has to be at the root !
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- <Precursor> -- <Precursor>
do do
configure_api_login (a_api, a_router) if
configure_api_logoff (a_api, a_router) a_response.is_authenticated and then
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_response.request.http_authorization /= Void
then
Result := True
end
end end
feature {CMS_API} -- Access: API
oauth20_api: detachable CMS_AUTH_API_I
-- <Precursor>
feature -- Access: filter feature -- Access: filter
filters (a_api: CMS_API): detachable LIST [WSF_FILTER] filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
@@ -65,6 +74,16 @@ feature -- Access: filter
Result.extend (create {CMS_BASIC_AUTH_FILTER}.make (a_api)) Result.extend (create {CMS_BASIC_AUTH_FILTER}.make (a_api))
end end
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
configure_api_login (a_api, a_router)
configure_api_logoff (a_api, a_router)
a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_basic_auth (a_api, ?, ?)), a_router.methods_head_get)
end
feature {NONE} -- Implementation: routes feature {NONE} -- Implementation: routes
configure_api_login (api: CMS_API; a_router: WSF_ROUTER) configure_api_login (api: CMS_API; a_router: WSF_ROUTER)
@@ -75,7 +94,7 @@ feature {NONE} -- Implementation: routes
create l_bal_handler.make (api) create l_bal_handler.make (api)
create l_methods create l_methods
l_methods.enable_get l_methods.enable_get
a_router.handle ("/basic_auth_login", l_bal_handler, l_methods) a_router.handle ("/" + do_login_location, l_bal_handler, l_methods)
end end
configure_api_logoff (api: CMS_API; a_router: WSF_ROUTER) configure_api_logoff (api: CMS_API; a_router: WSF_ROUTER)
@@ -86,16 +105,38 @@ feature {NONE} -- Implementation: routes
create l_bal_handler.make (api) create l_bal_handler.make (api)
create l_methods create l_methods
l_methods.enable_get l_methods.enable_get
a_router.handle ("/basic_auth_logoff", l_bal_handler, l_methods) a_router.handle ("/" + logout_location, l_bal_handler, l_methods)
end end
handle_login_basic_auth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_login_basic_auth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
vals: CMS_VALUE_TABLE
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_value ("Basic Auth", "optional_content_type") if api.user_is_authenticated then
r.add_error_message ("You are already signed in!")
r.set_main_content (r.link ("Logout", "account/roc-logout", Void))
else
if attached template_block ("login", r) as l_tpl_block then
r.add_javascript_url (r.url ("module/" + name + "/files/js/roc_basic_auth.js", Void))
create vals.make (1)
-- add the variable to the block
api.hooks.invoke_value_table_alter (vals, r)
across
vals as ic
loop
l_tpl_block.set_value (ic.item, ic.key)
end
r.add_block (l_tpl_block, "content")
else
debug ("cms")
r.add_warning_message ("Error with block [login]")
end
end
r.set_value ("Basic Auth", "optional_content_type")
end
r.execute r.execute
end end
@@ -104,103 +145,25 @@ feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration. -- Module hooks configuration.
do do
auto_subscribe_to_hooks (a_hooks) Precursor (a_hooks)
a_hooks.subscribe_to_block_hook (Current) a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end end
feature -- Hooks feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if a_response.is_authenticated then
a_value.force ("basic_auth_logoff", "auth_login_strategy")
end
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
local
lnk: CMS_LOCAL_LINK
lnk2: detachable CMS_LINK
do
if attached a_response.user as u then
across
a_menu_system.primary_menu.items as ic
until
lnk2 /= Void
loop
if ic.item.location.same_string ("account/roc-logout") then
lnk2 := ic.item
end
end
if lnk2 /= Void then
a_menu_system.primary_menu.remove (lnk2)
end
create lnk.make ("Logout", "basic_auth_logoff")
lnk.set_weight (98)
a_menu_system.primary_menu.extend (lnk)
else
if a_response.location.starts_with ("account/") then
create lnk.make ("Basic Auth", "account/roc-basic-auth")
lnk.set_expandable (True)
a_response.add_to_primary_tabs (lnk)
end
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"login">> 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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if if a_block_id.is_case_insensitive_equal_general ("login") then
a_block_id.is_case_insensitive_equal_general ("login") and then
a_response.location.starts_with ("account/roc-basic-auth")
then
a_response.add_javascript_url (a_response.url ("module/" + name + "/files/js/roc_basic_auth.js", Void)) a_response.add_javascript_url (a_response.url ("module/" + name + "/files/js/roc_basic_auth.js", Void))
get_block_view_login (a_block_id, a_response) get_block_view_login (a_block_id, a_response)
end end
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 feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -210,7 +173,7 @@ feature {NONE} -- Block views
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached template_block (a_block_id, a_response) as l_tpl_block then
create vals.make (1) create vals.make (1)
-- add the variable to the block -- add the variable to the block
value_table_alter (vals, a_response) a_response.api.hooks.invoke_value_table_alter (vals, a_response)
across across
vals as ic vals as ic
loop loop

View File

@@ -9,54 +9,46 @@ class
CMS_BASIC_AUTH_FILTER CMS_BASIC_AUTH_FILTER
inherit inherit
WSF_URI_TEMPLATE_HANDLER CMS_AUTH_FILTER_I
CMS_HANDLER
WSF_FILTER REFACTORING_HELPER
create create
make make
feature -- Basic operations feature -- Basic operations
auth_strategy: STRING
do
Result := {CMS_BASIC_AUTH_MODULE}.logout_location
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE) execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter. -- Execute the filter.
local local
l_auth: HTTP_AUTHORIZATION l_auth: HTTP_AUTHORIZATION
do do
api.logger.put_debug (generator + ".execute ", Void)
create l_auth.make (req.http_authorization) create l_auth.make (req.http_authorization)
debug
if attached req.raw_header_data as l_raw_data then
api.logger.put_debug (generator + ".execute " + (create {UTF_CONVERTER}).escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void)
end
end
-- A valid user
if if
(attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal_general ("basic")) and then l_auth.is_basic and then
attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password attached l_auth.login as l_auth_login and then
attached l_auth.password as l_auth_password
then then
if api.user_api.is_valid_credential (l_auth_login, l_auth_password) then if
if attached api.user_api.user_by_name (l_auth_login) as l_user then api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then
debug ("refactor_fixme") attached api.user_api.user_by_name (l_auth_login) as l_user
fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT") then
-- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user)) debug ("refactor_fixme")
-- other authentication filters (OpenID, etc) should implement the same approach. fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT")
end -- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user))
set_current_user (req, l_user) -- other authentication filters (OpenID, etc) should implement the same approach.
execute_next (req, res)
else
debug ("refactor_fixme")
to_implement ("Internal server error")
end
end end
set_current_user (l_user)
else else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void) api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void)
execute_next (req, res)
end end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
execute_next (req, res)
end end
execute_next (req, res)
end end
end end

View File

@@ -49,9 +49,9 @@ feature -- HTTP Methods
-- <Precursor> -- <Precursor>
do do
api.logger.put_information (generator + ".do_get Processing basic auth login", Void) api.logger.put_information (generator + ".do_get Processing basic auth login", Void)
if attached {STRING_32} current_user_name (req) as l_user then if api.user_is_authenticated then
if attached {WSF_STRING} req.query_parameter ("destination") as l_uri then if attached {WSF_STRING} req.query_parameter ("destination") as l_uri then
redirect_to (req.absolute_script_url (l_uri.url_encoded_value), res) redirect_to (req.absolute_script_url (l_uri.url_encoded_value), res)
else else
redirect_to (req.absolute_script_url ("/"), res) redirect_to (req.absolute_script_url ("/"), res)
end end

View File

@@ -51,11 +51,11 @@ feature -- HTTP Methods
do do
api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void)
if attached req.query_parameter ("prompt") as l_prompt then if attached req.query_parameter ("prompt") as l_prompt then
unset_current_user (req) api.unset_current_user (req)
send_access_denied_message (res) send_access_denied_message (res)
else else
create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api)
unset_current_user (req) api.unset_current_user (req)
l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection
l_url := req.absolute_script_url ("") l_url := req.absolute_script_url ("")
i := l_url.substring_index ("://", 1) i := l_url.substring_index ("://", 1)

View File

@@ -1,307 +1,291 @@
var ROC_AUTH = ROC_AUTH || { }; var ROC_AUTH = ROC_AUTH || { };
var loginURL = "/basic_auth_login"; var loginURL = "/roc-basic-login";
var logoutURL = "/basic_auth_logoff"; var logoutURL = "/roc-basic-logoff";
var userAgent = navigator.userAgent.toLowerCase(); var userAgent = navigator.userAgent.toLowerCase();
var firstLogIn = true; var firstLogIn = true;
ROC_AUTH.login = function() { ROC_AUTH.login = function() {
var form = document.forms['cms_basic_auth']; var form = document.forms['cms_basic_auth'];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
//var host = form.host.value; //var host = form.host.value;
var origin = window.location.origin + window.location.pathname; var origin = window.location.origin + window.location.pathname;
var _login = function(){ var _login = function(){
if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') !== null ) { if (document.getElementById('myModalFormId') === null ) {
ROC_AUTH.remove ('myModalFormId'); var newdiv = document.createElement('div');
} newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
} else {
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
// .. ?
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
}
}
}
}
}
}
if (username === "" || password === "") { var userAgent = navigator.userAgent.toLowerCase();
if (document.getElementById('myModalFormId') === null ) { if (userAgent.indexOf("firefox") != -1) { //TODO: check version number
var newdiv = document.createElement('div'); if (firstLogIn) {
newdiv.innerHTML = "<br>Invalid Credentials</br>"; _login();
newdiv.id = 'myModalFormId'; } else {
$(".primary-tabs").append(newdiv); ROC_AUTH.logoff(_login);
} }
}else{ } else {
_login();
//Instantiate HTTP Request }
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
delete form;
window.location=window.location.origin;
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
}
} if (firstLogIn) {
} firstLogIn = false;
} }
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.login_with_redirect = function() { ROC_AUTH.login_with_redirect = function() {
var form = document.forms[2]; var form = document.forms[2];
var username = form.username.value; var username = form.username.value;
var password = form.password.value; var password = form.password.value;
var host = form.host.value; var host = form.host.value;
var _login = function(){ var _login = function(){
var redirectURL = form.redirect && form.redirect.value || "";
$("#imgProgressRedirect").show();
var redirectURL = form.redirect && form.redirect.value || ""; if (document.getElementById('myModalFormId') !== null ) {
ROC_AUTH.remove ('myModalFormId');
}
if (username === "" || password === "") {
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
} else {
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host + loginURL, true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host + "/";
} else {
window.location=host + redirectURL;
}
} else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".login-box").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
$("#imgProgressRedirect").show(); var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (document.getElementById('myModalFormId') !== null ) { if (firstLogIn) {
ROC_AUTH.remove ('myModalFormId'); _login();
} } else {
ROC_AUTH.logoff(_login);
}
if (username === "" || password === "") { } else{
if (document.getElementById('myModalFormId') === null ) { _login();
var newdiv = document.createElement('div'); }
newdiv.innerHTML = "<br>Invalid Credentials</br>"; if (firstLogIn) {
newdiv.id = 'myModalFormId'; firstLogIn = false;
$(".primary-tabs").append(newdiv); }
$("#imgProgressRedirect").hide();
}
}else{
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("GET", host.concat(loginURL), true, username, password);
request.send(null);
//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==200) {
if (redirectURL === "") {
window.location=host.concat("/");
} else {
window.location=host.concat(redirectURL);
}
}
else{
if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){
}
if (document.getElementById('myModalFormId') === null ) {
var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId';
$(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide();
}
}
}
}
}
}
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
if (firstLogIn) _login();
else logoff(_login);
}
else{
_login();
}
if (firstLogIn) firstLogIn = false;
}; };
ROC_AUTH.getQueryParameterByName = function (name) { ROC_AUTH.getQueryParameterByName = function (name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search);
results = regex.exec(location.search); return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " "));
} }
ROC_AUTH.logoff = function(callback){ ROC_AUTH.logoff = function(callback){
var form = document.forms[0]; var form = document.forms[0];
var host = form.host.value; var host = form.host.value;
if (userAgent.indexOf("msie") != -1) { if (userAgent.indexOf("msie") != -1) {
document.execCommand("ClearAuthenticationCache"); document.execCommand("ClearAuthenticationCache");
} } else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number
else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest();
var request1 = new XMLHttpRequest();
var request2 = new XMLHttpRequest(); //Logout. Tell the server not to return the "WWW-Authenticate" header
request1.open("GET", host + logoutURL + "?prompt=false", true);
//Logout. Tell the server not to return the "WWW-Authenticate" header request1.send("");
request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); request1.onreadystatechange = function(){
request1.send(""); if (request1.readyState == 4) {
request1.onreadystatechange = function(){ //Sign in with dummy credentials to clear the auth cache
if (request1.readyState == 4) { request2.open("GET", host + logoutURL, true, "logout", "logout");
request2.send("");
//Sign in with dummy credentials to clear the auth cache request2.onreadystatechange = function(){
request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); if (request2.readyState == 4) {
request2.send(""); if (callback!=null) {
callback.call();
request2.onreadystatechange = function(){ } else {
if (request2.readyState == 4) { window.location=host + logoutURL;
if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} }
} }
} }
}
} }
} } else {
} var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
else { request.open("GET", host + logoutURL, true, "logout", "logout");
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); request.send("");
request.open("GET", host.concat(logoutURL), true, "logout", "logout"); request.onreadystatechange = function(){
request.send(""); if (request.status==401 || request.status==403 ) {
request.onreadystatechange = function(){ window.location=host + logoutURL;
if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); }
} }
} }
}
}; };
ROC_AUTH.remove = function (id) ROC_AUTH.remove = function (id) {
{ var element = document.getElementById(id);
var element = document.getElementById(id); element.outerHTML = "";
element.outerHTML = ""; delete element;
delete element; return;
return;
}; };
$(document).ready(function() { $(document).ready(function() {
if (typeof String.prototype.contains != 'function') {
if (typeof String.prototype.contains != 'function') { String.prototype.contains = function (str){
String.prototype.contains = function (str){ return this.indexOf(str) != -1;
return this.indexOf(str) != -1; };
}; }
} ROC_AUTH.progressive_loging();
ROC_AUTH.progressive_loging();
}); });
ROC_AUTH.progressive_loging = function () { ROC_AUTH.progressive_loging = function () {
ROC_AUTH.login_href();
ROC_AUTH.login_href();
}; };
$(document).keypress(function(e) { $(document).keypress(function(e) {
if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) {
ROC_AUTH.login(); ROC_AUTH.login();
} }
}); });
ROC_AUTH.OnOneClick = function(event) { ROC_AUTH.OnOneClick = function(event) {
event.preventDefault(); event.preventDefault();
if ( document.forms[0] === undefined ) { if ( document.forms[0] === undefined ) {
ROC_AUTH.create_form(); ROC_AUTH.create_form();
} }
return false; return false;
}; };
ROC_AUTH.login_href = function() { ROC_AUTH.login_href = function() {
var els = document.getElementsByTagName("a"); var els = document.getElementsByTagName("a");
for (var i = 0, l = els.length; i < l; i++) { for (var i = 0, l = els.length; i < l; i++) {
var el = els[i]; var el = els[i];
if (el.href.contains("/basic_auth_login?destination")) { if (el.href.contains(loginURL + "?destination")) {
loginURL = el.href; // loginURL = el.href;
var OneClick = el; var OneClick = el;
OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false);
} }
} }
}; };
ROC_AUTH.create_form = function() { ROC_AUTH.create_form = function() {
// Fetching HTML Elements in Variables by ID. // Fetching HTML Elements in Variables by ID.
var createform = document.createElement('form'); // Create New Element Form var createform = document.createElement('form'); // Create New Element Form
createform.setAttribute("action", ""); // Setting Action Attribute on Form createform.setAttribute("action", ""); // Setting Action Attribute on Form
createform.setAttribute("method", "post"); // Setting Method Attribute on Form createform.setAttribute("method", "post"); // Setting Method Attribute on Form
$("body").append(createform); $("body").append(createform);
var heading = document.createElement('h2'); // Heading of Form var heading = document.createElement('h2'); // Heading of Form
heading.innerHTML = "Login Form "; heading.innerHTML = "Login Form ";
createform.appendChild(heading); createform.appendChild(heading);
var line = document.createElement('hr'); // Giving Horizontal Row After Heading var line = document.createElement('hr'); // Giving Horizontal Row After Heading
createform.appendChild(line); createform.appendChild(line);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var namelabel = document.createElement('label'); // Create Label for Name Field var namelabel = document.createElement('label'); // Create Label for Name Field
namelabel.innerHTML = "Username : "; // Set Field Labels namelabel.innerHTML = "Username : "; // Set Field Labels
createform.appendChild(namelabel); createform.appendChild(namelabel);
var inputelement = document.createElement('input'); // Create Input Field for UserName var inputelement = document.createElement('input'); // Create Input Field for UserName
inputelement.setAttribute("type", "text"); inputelement.setAttribute("type", "text");
inputelement.setAttribute("name", "username"); inputelement.setAttribute("name", "username");
inputelement.setAttribute("required","required"); inputelement.setAttribute("required","required");
createform.appendChild(inputelement); createform.appendChild(inputelement);
var linebreak = document.createElement('br'); var linebreak = document.createElement('br');
createform.appendChild(linebreak); createform.appendChild(linebreak);
var passwordlabel = document.createElement('label'); // Create Label for Password Field var passwordlabel = document.createElement('label'); // Create Label for Password Field
passwordlabel.innerHTML = "Password : "; passwordlabel.innerHTML = "Password : ";
createform.appendChild(passwordlabel); createform.appendChild(passwordlabel);
var passwordelement = document.createElement('input'); // Create Input Field for Password. var passwordelement = document.createElement('input'); // Create Input Field for Password.
passwordelement.setAttribute("type", "password"); passwordelement.setAttribute("type", "password");
passwordelement.setAttribute("name", "password"); passwordelement.setAttribute("name", "password");
passwordelement.setAttribute("id", "password"); passwordelement.setAttribute("id", "password");
passwordelement.setAttribute("required","required"); passwordelement.setAttribute("required","required");
createform.appendChild(passwordelement); createform.appendChild(passwordelement);
var passwordbreak = document.createElement('br'); var passwordbreak = document.createElement('br');
createform.appendChild(passwordbreak); createform.appendChild(passwordbreak);
var submitelement = document.createElement('button'); // Append Submit Button var submitelement = document.createElement('button'); // Append Submit Button
submitelement.setAttribute("type", "button"); submitelement.setAttribute("type", "button");
submitelement.setAttribute("onclick", "ROC_AUTH.login();"); submitelement.setAttribute("onclick", "ROC_AUTH.login();");
submitelement.innerHTML = "Sign In "; submitelement.innerHTML = "Sign In ";
createform.appendChild(submitelement); createform.appendChild(submitelement);
}; };
@@ -310,16 +294,16 @@ var password = document.getElementById("password");
var confirm_password = document.getElementById("confirm_password"); var confirm_password = document.getElementById("confirm_password");
ROC_AUTH.validatePassword =function(){ ROC_AUTH.validatePassword =function(){
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
if(password.value != confirm_password.value) { if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match"); confirm_password.setCustomValidity("Passwords Don't Match");
} else { } else {
confirm_password.setCustomValidity(''); confirm_password.setCustomValidity('');
} }
} }
} }
if ((password != null) && (confirm_password != null)) { if ((password != null) && (confirm_password != null)) {
password.onchange = ROC_AUTH.validatePassword(); password.onchange = ROC_AUTH.validatePassword();
confirm_password.onkeyup = ROC_AUTH.validatePassword; confirm_password.onkeyup = ROC_AUTH.validatePassword;
} }

View File

@@ -1,29 +1,23 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Basic Auth" relies on the HTTP basic acces authentication.<br/>(see also: <a href="https://en.wikipedia.org/wiki/Basic_access_authentication">https://en.wikipedia.org/wiki/Basic_access_authentication</a> )</div>
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div> <div>
<div> <form name="cms_basic_auth" action="{$site_url/}roc-basic-login" method="POST">
<form name="cms_basic_auth" action method="POST"> <input type="hidden" name="host" id="host" value="{$site_url/}">
<div> <div>
<input type="text" name="username" id="username" required> <input type="text" name="username" id="username" required>
<label>Username</label> <label>Username</label>
</div> </div>
<div>
<div> <input type="password" name="password" id="password" required>
<input type="password" name="password" id="password" required> <label>Password</label>
<label>Password</label> </div>
</div> <button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
<button type="button" onclick="ROC_AUTH.login();">Login</button>
</form>
</div>
</div> </div>
<div> <div>
<div> <a href="{$site_url/}account/new-password">Forgot password?</a>
<p>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</p>
</div>
</div> </div>
{/unless}
</div> </div>
{/unless}

View File

@@ -78,27 +78,17 @@ feature {CMS_API} -- Module Initialization
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
install (api: CMS_API) install (a_api: CMS_API)
local
sql: STRING
do do
-- Schema -- Schema
if attached api.storage.as_sql_storage as l_sql_storage then if attached a_api.storage.as_sql_storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("blog_post_nodes") then l_sql_storage.sql_execute_file_script (a_api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install.sql")), Void)
sql := "[
CREATE TABLE blog_post_nodes( if l_sql_storage.has_error then
`nid` INTEGER NOT NULL CHECK("nid">=0), a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
`revision` INTEGER NOT NULL, else
`tags` VARCHAR(255), Precursor {CMS_MODULE} (a_api)
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);
]"
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
end end
Precursor (api)
end end
end end

View File

@@ -0,0 +1,6 @@
CREATE TABLE blog_post_nodes(
`nid` INTEGER NOT NULL CHECK("nid">=0),
`revision` INTEGER NOT NULL,
`tags` VARCHAR(255),
CONSTRAINT PK_nid_revision PRIMARY KEY (nid,revision)
);

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="contact" uuid="5F9BB4AA-FB62-4550-B314-DED374843DC0" library_target="contact">
<target name="contact">
<root all_classes="true"/>
<option is_obsolete_routine_type="true">
</option>
<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_config" location="..\..\library\configuration\config-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="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="recaptcha" location="..\..\library\recaptcha\recaptcha-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_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,8 @@
{
"--email": "webmaster@example.com",
"subjet": "Thank you for contacting us",
"recaptcha": {
"site_key":"",
"secret_key":""
}
}

View File

@@ -0,0 +1,124 @@
.contact-box {
background-color: #F2F7F9;
width: 465px;
padding: 20px;
border: 6px solid #8FB5C1;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
border-radius: 15px;
position: relative;
/* Remove box shadow firefox, chrome and opera put around required fields.
* It looks rubbish.
*/
/* Normalize placeholder styles */
/* chrome, safari */
/* mozilla */
/* ie (faux placeholder) */
}
.contact-box h1 {
font-size: 42px;
}
.contact-box h2 {
margin-bottom: 15px;
font-style: italic;
font-weight: normal;
}
.contact-box label {
font-size: 15px;
margin-bottom: 2px;
display: block;
}
.contact-box input, .contact-box select, .contact-box textarea {
width: 100%;
font-size: 15px;
border: 1px solid #CEE1E8;
margin-bottom: 20px;
padding: 4px;
}
.contact-box input:focus, .contact-box select:focus, .contact-box textarea:focus {
border: 1px solid #AFCDD8;
background-color: #EBF2F4;
}
.contact-box textarea {
height: 150px;
resize: none;
}
.contact-box span.required {
font-weight: bold;
color: #F00;
}
.contact-box input[type=submit] {
width: 100px;
background-color: #333;
color: #FFF;
border: none;
display: block;
float: right;
margin-bottom: 0px;
margin-right: 6px;
background-color: #8FB5C1;
-moz-border-radius: 8px;
}
.contact-box input[type=submit]:hover {
background-color: #A6CFDD;
}
.contact-box input[type=submit]:active {
position: relative;
top: 1px;
}
.contact-box .message {
width: 95%;
margin: 25px 0px;
padding: 10px;
display: block;
border: solid 1px #ccc;
border-radius: 8px;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
}
.contact-box .message.hidden {
display: none;
}
.contact-box .message.error {
border-color: #E58E8E;
background-color: #FFE6E6;
}
.contact-box .message.error li {
padding: 2px;
list-style: none;
}
.contact-box .message.error li:before {
content: ' - ';
}
.contact-box .message.error #info {
font-weight: bold;
}
.contact-box .message.error #info:before {
content: '';
}
.contact-box .message.success {
border-color: #83D186;
padding-top: 25px;
background-color: #D3EDD3;
}
.contact-box .req-field-desc {
font-style: italic;
}
.contact-box input:required, .contact-box textarea:required {
-moz-box-shadow: none;
-webkit-box-shadow: none;
-o-box-shadow: none;
box-shadow: none;
}
.contact-box ::-webkit-input-placeholder {
color: #CCC;
font-style: italic;
}
.contact-box input:-moz-placeholder, .contact-box textarea:-moz-placeholder {
color: #CCC;
font-style: italic;
}
.contact-box input.placeholder-text, .contact-box textarea.placeholder-text {
color: #CCC;
font-style: italic;
}

View File

@@ -0,0 +1,140 @@
.contact-box {
background-color:#F2F7F9;
width:465px;
padding:20px;
border: 6px solid #8FB5C1;
-moz-border-radius:15px;
-webkit-border-radius:15px;
border-radius:15px;
position:relative;
h1 {
font-size:42px;
}
h2 {
margin-bottom:15px;
font-style:italic;
font-weight:normal;
}
label {
font-size:15px;
margin-bottom:2px;
display:block;
}
input, select, textarea {
width:100%;
font-size:15px;
border: 1px solid #CEE1E8;
margin-bottom:20px;
padding:4px;
&:focus {
border: 1px solid #AFCDD8;
background-color: #EBF2F4;
}
}
textarea {
height:150px;
resize: none;
}
span.required {
font-weight:bold;
color:#F00;
}
input[type=submit] {
width: 100px;
background-color:#333;
color:#FFF;
border:none;
display:block;
float:right;
margin-bottom:0px;
margin-right:6px;
background-color:#8FB5C1;
-moz-border-radius:8px;
&:hover {
background-color: #A6CFDD;
}
&:active {
position:relative;
top:1px;
}
}
.message {
width:95%;
margin:25px 0px;
padding:10px;
display:block;
border:solid 1px #ccc;
border-radius:8px;
-webkit-border-radius:8px;
-moz-border-radius:8px;
&.hidden {
display: none;
}
&.error {
border-color: #E58E8E;
background-color:#FFE6E6;
li {
padding:2px;
list-style:none;
&:before { content: ' - '; }
}
#info {
font-weight:bold;
&:before { content: ''; }
}
}
&.success {
border-color: #83D186;
padding-top: 25px;
background-color:#D3EDD3;
}
}
.req-field-desc {
font-style:italic;
}
/* Remove box shadow firefox, chrome and opera put around required fields.
* It looks rubbish.
*/
input:required, textarea:required {
-moz-box-shadow:none;
-webkit-box-shadow:none;
-o-box-shadow:none;
box-shadow:none;
}
/* Normalize placeholder styles */
/* chrome, safari */
::-webkit-input-placeholder {
color:#CCC;
font-style:italic;
}
/* mozilla */
input:-moz-placeholder, textarea:-moz-placeholder {
color:#CCC;
font-style:italic;
}
/* ie (faux placeholder) */
input.placeholder-text, textarea.placeholder-text {
color:#CCC;
font-style:italic;
}
}

View File

@@ -0,0 +1,25 @@
<div class="contact-box clearfix">
<h1>Contact us!</h1>
<form method="post" action="{$site_url/}contact" id="contact-form">
<label for="name">Name: <span class="required">*</span></label>
<input type="text" id="name" name="name" value="{$name/}" required="required" autofocus="autofocus" />
<label for="email">Email Address: <span class="required">*</span></label>
<input type="email" id="email" name="email" value="{$email/}" required="required" />
<label for="message">Message: <span class="required">*</span></label>
<textarea id="message" name="message" required="required" data-minlength="20" minlength="20" >{$message/}</textarea>
{unless isempty="$recaptcha_site_key"}
<div class="g-recaptcha" data-sitekey="{$recaptcha_site_key/}"></div>
<br/>
{/unless}
<input type="submit" value="Send" class="submit-button" />
<p class="req-field-desc"><span class="required">*</span> indicates a required field</p>
</form>
{unless isempty="$error_response"}
<ul class="message error">
{foreach item="item" from="$error_response"}<li class="info">{$item/}</li>{/foreach}
</ul>
<div class="notice"> Try again later </div>
{/unless}
</div>

View File

@@ -0,0 +1,15 @@
<div class="contact-box">
{if condition="$has_error"}
<div class="message error">
<strong>Internal Server Error <small>Error 500</small></strong>
<p>The page you requested could not be served because the server is down,
either contact the webmaster or try again.
Use your browser's <strong>Back</strong> button to navigate to the page you came from.</p>
<p><strong>Or you could just press this link:</strong> <a href="{$site_url/}" itemprop="home" rel="home">Take Me Home</a></p>
</div>
{/if}
{unless condition="$has_error"}
<p class="message success">Thank you for contacting the Eiffel Programming Language community.<br/>
We will get back to you promptly on your contact request.</p>
{/unless}
</div>

View File

@@ -0,0 +1,10 @@
<p>
Thank you for contacting {$sitename/}.<br/>
We will get back to you promptly about your contact message.
</p>
<h2>Your contact information:</h2>
<div>
<strong>Name<strong>: {$name/} <br/>
<strong>Email<strong>: {$email/} <br/>
<strong>Message<strong>: {$message/} <br/>
</div>

View File

@@ -0,0 +1,6 @@
<h2>Contact information:</h2>
<div>
<strong>Name<strong>: {$name/}<br/>
<strong>Email<strong>: {$email/} <br/>
<strong>Message<strong>: {$message/} <br/>
</div>

View File

@@ -0,0 +1,533 @@
note
description: "[
Module that provide contact us web form functionality.
]"
author: "$Author: jfiat $"
date: "$Date: 2016-01-08 22:43:12 +0100 (ven., 08 janv. 2016) $"
revision: "$Revision: 98369 $"
class
CMS_CONTACT_MODULE
inherit
CMS_MODULE
rename
module_api as contact_api
redefine
setup_hooks,
install,
initialize,
contact_api
end
CMS_HOOK_BLOCK
CMS_HOOK_BLOCK_HELPER
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
REFACTORING_HELPER
SHARED_LOGGER
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "1.0"
description := "Contact form module"
package := "messaging"
end
feature -- Access
name: STRING = "contact"
-- <Precursor>
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
local
l_contact_api: like contact_api
ut: FILE_UTILITIES
p: PATH
contact_storage: CONTACT_STORAGE_I
do
Precursor (api)
-- if attached api.storage.as_sql_storage as l_storage_sql then
-- create {CONTACT_STORAGE_SQL} contact_storage.make (l_storage_sql)
-- else
p := file_system_storage_path (api)
if ut.directory_path_exists (p) then
create {CONTACT_STORAGE_FS} contact_storage.make (p, api)
else
create {CONTACT_STORAGE_NULL} contact_storage.make
end
create l_contact_api.make (api, contact_storage)
contact_api := l_contact_api
end
feature {CMS_API} -- Module management
install (api: CMS_API)
local
retried: BOOLEAN
d: DIRECTORY
do
if not retried then
create d.make_with_path (file_system_storage_path (api))
d.recursive_create_dir
Precursor {CMS_MODULE}(api) -- Marked installed
end
rescue
retried := True
retry
end
file_system_storage_path (api: CMS_API): PATH
-- Location of eventual file system based storage for contact messages.
do
Result := api.site_location.extended ("db").extended (name).extended ("messages")
end
feature {CMS_API} -- Access: API
contact_api: detachable CONTACT_API
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration.
local
m: WSF_URI_MAPPING
do
create m.make_trailing_slash_ignored ("/contact", create {WSF_URI_AGENT_HANDLER}.make (agent handle_contact (a_api, ?, ?)))
a_router.map (m, a_router.methods_head_get)
a_router.handle ("/contact", create {WSF_URI_AGENT_HANDLER}.make (agent handle_post_contact (a_api, ?, ?)), a_router.methods_put_post)
end
feature -- Recaptcha
recaptcha_secret_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if
attached cfg.text_item ("recaptcha.secret_key") as l_recaptcha_key and then
not l_recaptcha_key.is_empty
then
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
end
end
end
recaptcha_site_key (api: CMS_API): detachable READABLE_STRING_8
-- Get recaptcha security key.
local
utf: UTF_CONVERTER
do
if attached api.module_configuration (Current, Void) as cfg then
if
attached cfg.text_item ("recaptcha.site_key") as l_recaptcha_key and then
not l_recaptcha_key.is_empty
then
Result := utf.utf_32_string_to_utf_8_string_8 (l_recaptcha_key)
end
end
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_block_hook (Current)
end
feature -- Hooks
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'.
do
debug ("refactor_fixme")
fixme ("add contact to menu")
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name]
do
Result := <<"?contact">>
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do
if a_block_id.is_case_insensitive_equal_general ("contact") then
-- "contact", "post_contact"
if a_response.request.is_get_request_method then
if attached template_block (Current, a_block_id, a_response) as l_tpl_block then
if attached recaptcha_site_key (a_response.api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end
a_response.add_block (l_tpl_block, "content")
a_response.add_style (a_response.url ("/module/" + name + "/files/css/contact.css", Void), Void)
else
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
end
end
new_html_contact_form (a_response: CMS_RESPONSE; api: CMS_API): STRING
local
f: CMS_FORM
do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/contact.css", Void), Void)
if attached template_block (Current, "contact", a_response) as l_tpl_block then
if attached recaptcha_site_key (api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
end
across
a_response.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
Result := l_tpl_block.to_html (a_response.theme)
else
f := new_contact_form (a_response, api)
api.hooks.invoke_form_alter (f, f.last_data, a_response)
Result := "<div class=%"contact-box%"><h1>Contact us!</h1>" + f.to_html (a_response.wsf_theme) + "<br/></div>"
end
end
new_contact_form (a_response: CMS_RESPONSE; api: CMS_API): CMS_FORM
local
f: CMS_FORM
f_name: WSF_FORM_TEXT_INPUT
f_email: WSF_FORM_EMAIL_INPUT
f_msg: WSF_FORM_TEXTAREA
f_submit: WSF_FORM_SUBMIT_INPUT
do
create f.make (a_response.url ("contact", Void), "contact-form")
create f_name.make ("name")
f_name.set_label ("Name")
f_name.set_is_required (True)
f.extend (f_name)
create f_email.make ("email")
f_email.set_label ("Email Address")
f_email.set_is_required (True)
f.extend (f_email)
create f_msg.make ("message")
f_msg.set_label ("Message")
f_msg.set_rows (5)
f_msg.set_is_required (True)
f.extend (f_msg)
if attached recaptcha_site_key (api) as l_recaptcha_site_key then
f.extend_html_text ("<div class=%"g-recaptcha%" data-sitekey=%"" + l_recaptcha_site_key + "%"></div><br/>")
end
create f_submit.make_with_text ("submit-op", "Send")
f.extend (f_submit)
-- f.extend_html_text ("[
-- <p class="req-field-desc"><span class="required">*</span> indicates a required field</p>
-- ]")
Result := f
end
handle_contact (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
do
-- FIXME: we should use WSF_FORM, and integrate the recaptcha using the form alter hook.
write_debug_log (generator + ".handle_contact")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.values.force ("contact", "contact")
r.set_main_content (new_html_contact_form (r, api))
r.execute
end
handle_post_contact (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
msg: CONTACT_MESSAGE
l_params: CONTACT_EMAIL_SERVICE_PARAMETERS
e: CMS_EMAIL
vars: STRING_TABLE [READABLE_STRING_8]
l_contact_email_address: READABLE_STRING_8
do
write_information_log (generator + ".handle_post_contact")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.add_style (r.url ("/module/" + name + "/files/css/contact.css", Void), Void)
r.values.force (False, "has_error")
create vars.make_caseless (5)
vars.put (safe_html_encoded (api.setup.site_url), "siteurl")
vars.put (safe_html_encoded (api.setup.site_name), "sitename")
write_debug_log (generator + ".handle_post_contact {Form Parameters:" + form_parameters_as_string (req) + "}")
if
attached {WSF_STRING} req.form_parameter ("name") as l_name and then
attached {WSF_STRING} req.form_parameter ("email") as l_email and then
attached {WSF_STRING} req.form_parameter ("message") as l_message
then
if
is_form_captcha_verified (req, "g-recaptcha-response", api) and then
l_email.value.is_valid_as_string_8
then
l_contact_email_address := l_email.value.to_string_8
if attached contact_api as l_contact_api then
create msg.make (l_name.value, l_message.value)
msg.set_email (l_contact_email_address)
l_contact_api.save_contact_message (msg)
end
create l_params.make (api, Current)
-- Send internal email to admin.
vars.put (html_encoded (l_name.value), "name")
vars.put (html_encoded (l_contact_email_address), "email")
vars.put (html_encoded (l_message.value), "message")
write_debug_log (generator + ".handle_post_contact: send notification email")
e := api.new_email (l_params.admin_email, "Notification Contact", email_html_message ("notification", r, vars))
e.set_from_address (l_params.admin_email)
e.add_header_line ("MIME-Version:1.0")
e.add_header_line ("Content-Type: text/html; charset=utf-8")
api.process_email (e)
if not api.has_error then
-- Send Contact email to the user
write_information_log (generator + ".handle_post_contact: preparing the message.")
e := api.new_email (l_contact_email_address, l_params.contact_subject_text, email_html_message ("message", r, vars))
e.set_from_address (l_params.admin_email)
e.add_header_line ("MIME-Version:1.0")
e.add_header_line ("Content-Type: text/html; charset=utf-8")
write_debug_log (generator + ".handle_post_contact: send_contact_email")
api.process_email (e)
end
if api.has_error then
write_error_log (generator + ".handle_post_contact: error message:["+ api.string_representation_of_errors +"]")
r.set_status_code ({HTTP_CONSTANTS}.internal_server_error)
r.values.force (True, "has_error")
vars.put ("True", "has_error")
end
if attached template_block_with_values (Current, "post_contact", r, vars) as l_tpl_block then
across
r.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
r.set_main_content (l_tpl_block.to_html (r.theme))
else
r.set_main_content ("Thank you for your message.")
end
r.execute
else
-- send a bad request status code and redisplay the form with the previous data loaded.
r.set_value (False, "error")
r.set_status_code ({HTTP_STATUS_CODE}.bad_request)
if attached template_block_with_values (Current, "contact", r, vars) as l_tpl_block then
across
r.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
if attached recaptcha_site_key (api) as l_recaptcha_site_key then
l_tpl_block.set_value (l_recaptcha_site_key, "recaptcha_site_key")
l_tpl_block.set_value (<<"Missing Captcha", "Internal Server Error">>, "error_response")
end
r.set_main_content (l_tpl_block.to_html (r.theme))
else
debug ("cms")
r.add_warning_message ("Error with block [contact]")
end
end
r.execute
end
else
-- Internal server error
write_error_log (generator + ".handle_post_contact: Internal Server error")
r.values.force (True, "has_error")
r.set_status_code ({HTTP_CONSTANTS}.internal_server_error)
if attached template_block_with_values (Current, "post_contact", r, vars) as l_tpl_block then
across
r.values as tb
loop
l_tpl_block.set_value (tb.item, tb.key)
end
r.set_main_content (l_tpl_block.to_html (r.theme))
end
r.execute
end
end
is_form_captcha_verified (req: WSF_REQUEST; a_form_field_id: READABLE_STRING_GENERAL; api: CMS_API): BOOLEAN
do
if attached recaptcha_secret_key (api) as l_recaptcha_key then
if
attached {WSF_STRING} req.form_parameter (a_form_field_id) as l_recaptcha_response and then
is_captcha_verified (l_recaptcha_key, l_recaptcha_response.value)
then
Result := True
else
--| Bad or missing captcha
Result := False
end
else
--| reCaptcha is not setup, so no verification
Result := True
end
end
feature {NONE} -- Helpers
form_parameters_as_string (req: WSF_REQUEST): STRING
do
create Result.make_empty
across req.form_parameters as ic loop
Result.append (ic.item.key)
Result.append_character ('=')
Result.append_string (ic.item.string_representation)
Result.append_character ('%N')
end
end
feature {NONE} -- Contact Message
template_block_with_values (a_module: CMS_MODULE; a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_values: STRING_TABLE [ANY]): like template_block
do
Result := template_block (a_module, a_block_id, a_response)
if Result /= Void then
across
a_values as ic
loop
Result.set_value (ic.item, ic.key)
end
end
end
email_html_message (a_message_id: READABLE_STRING_8; a_response: CMS_RESPONSE; a_html_encoded_values: STRING_TABLE [READABLE_STRING_8]): STRING
-- html message related to `a_message_id'.
local
res: PATH
p: detachable PATH
tpl: CMS_SMARTY_TEMPLATE_BLOCK
exp: CMS_STRING_EXPANDER [STRING_8]
do
write_debug_log (generator + ".email_html_message for [" + a_message_id + " ]")
create res.make_from_string ("templates")
res := res.extended ("email_").appended (a_message_id).appended_with_extension ("tpl")
p := a_response.api.module_theme_resource_location (Current, res)
if p /= Void then
if attached p.entry as e then
create tpl.make (a_message_id, Void, p.parent, e)
write_debug_log (generator + ".email_html_message from smarty template:" + tpl.out)
else
create tpl.make (a_message_id, Void, p.parent, p)
write_debug_log (generator + ".email_html_message from smarty template:" + tpl.out)
end
across
a_html_encoded_values as ic
loop
tpl.set_value (ic.item, ic.key)
end
Result := tpl.to_html (a_response.theme)
else
if a_message_id.is_case_insensitive_equal_general ("message") then
create Result.make_from_string (contact_message_template)
elseif a_message_id.is_case_insensitive_equal_general ("notification") then
create Result.make_from_string (contact_notification_message_template)
else
create Result.make_from_string (a_message_id)
across
a_html_encoded_values as ic
loop
Result.append ("<li>")
Result.append (html_encoded (ic.key))
Result.append (": ")
Result.append (ic.item) -- Already html encoded.
Result.append ("</li>%N")
end
end
create exp.make
across
a_html_encoded_values as ic
loop
exp.put (ic.item, ic.key)
end
exp.expand_string (Result)
write_debug_log (generator + ".email_html_message using built-in message:" + Result)
end
end
contact_message_template: STRING
do
Result := "[
<p>Thank you for contacting $sitename.<br/>
We will get back to you promptly on your contact request.
</p>
]"
+ contact_notification_message_template
end
contact_notification_message_template: STRING = "[
<h2>Contact information:</h2>
<div>
<strong>Name<strong>: $name <br/>
<strong>Email<strong>: $email <br/>
<strong>Message<strong>: $message <br/>
</div>
]"
feature {NONE} -- Google recaptcha uri template
is_captcha_verified (a_secret, a_response: READABLE_STRING_8): BOOLEAN
local
api: RECAPTCHA_API
l_errors: STRING
do
write_debug_log (generator + ".is_captcha_verified with response: [" + a_response + "]")
create api.make (a_secret, a_response)
Result := api.verify
if not Result and then attached api.errors as l_api_errors then
create l_errors.make_empty
l_errors.append_character ('%N')
across l_api_errors as ic loop
l_errors.append ( ic.item )
l_errors.append_character ('%N')
end
write_error_log (generator + ".is_captcha_verified api_errors [" + l_errors + "]")
end
end
end

View File

@@ -0,0 +1,40 @@
note
description: "API for the contact module."
date: "$Date: 2015-05-22 23:00:09 +0200 (ven., 22 mai 2015) $"
revision: "$Revision: 97349 $"
class
CONTACT_API
inherit
CMS_MODULE_API
rename
make as make_api
end
REFACTORING_HELPER
create
make
feature {NONE} -- Initialization
make (a_api: CMS_API; a_contact_storage: like contact_storage)
-- <Precursor>.
do
make_api (a_api)
contact_storage := a_contact_storage
end
feature {CMS_MODULE} -- Access nodes storage.
contact_storage: CONTACT_STORAGE_I
feature -- Basic operation
save_contact_message (msg: CONTACT_MESSAGE)
do
contact_storage.save_contact_message (msg)
end
end

View File

@@ -0,0 +1,63 @@
note
description: "Summary description for {CONTACT_EMAIL_SERVICE_PARAMETERS}."
date: "$Date: 2015-07-03 19:04:52 +0200 (ven., 03 juil. 2015) $"
revision: "$Revision: 97646 $"
class
CONTACT_EMAIL_SERVICE_PARAMETERS
create
make
feature {NONE} -- Initialization
make (a_cms_api: CMS_API; a_contact_module: CMS_CONTACT_MODULE)
local
utf: UTF_CONVERTER
l_site_name: READABLE_STRING_8
s: detachable READABLE_STRING_32
l_contact_email, l_contact_subject: detachable READABLE_STRING_8
do
-- Use global smtp setting if any, otherwise "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 (a_contact_module, Void) as cfg then
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")
if s /= Void then
l_contact_subject := 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_contact_subject /= Void then
contact_subject_text := l_contact_subject
else
contact_subject_text := "Thank you for contacting us"
end
end
feature -- Access
admin_email: IMMUTABLE_STRING_8
contact_email: IMMUTABLE_STRING_8
-- Contact email.
contact_subject_text: IMMUTABLE_STRING_8
end

View File

@@ -0,0 +1,43 @@
note
description: "Interface {CONTACT_MESSAGE} representing the contact's message."
date: "$Date: 2015-07-03 19:04:52 +0200 (ven., 03 juil. 2015) $"
revision: "$Revision: 97646 $"
class
CONTACT_MESSAGE
create
make
feature {NONE} -- Initialization
make (a_name: like username; a_message: like message)
do
username := a_name
message := a_message
create date.make_now_utc
end
feature -- Access
username: READABLE_STRING_32
email: detachable READABLE_STRING_8
message: READABLE_STRING_32
date: DATE_TIME
feature -- Change
set_email (e: like email)
do
email := e
end
set_date (d: like date)
do
date := d
end
end

View File

@@ -0,0 +1,56 @@
note
description: "[
Contact message storage based on SQL statements.
]"
date: "$Date: 2015-07-03 19:04:52 +0200 (ven., 03 juil. 2015) $"
revision: "$Revision: 97646 $"
class
CONTACT_STORAGE_FS
inherit
CONTACT_STORAGE_I
CMS_STORAGE_FS_I
REFACTORING_HELPER
create
make
feature -- Access
feature -- Change
save_contact_message (m: CONTACT_MESSAGE)
local
s: STRING
utf: UTF_CONVERTER
now: DATE_TIME
do
error_handler.reset
create now.make_now_utc
write_information_log (generator + ".save_contact_message")
create s.make_empty
s.append ("date=")
s.append (m.date.out)
s.append_character ('%N')
s.append ("name=")
s.append (utf.utf_32_string_to_utf_8_string_8 (m.username))
s.append_character ('%N')
if attached m.email as l_email then
s.append ("email=")
s.append (l_email)
s.append_character ('%N')
end
s.append ("message=%N")
s.append (utf.utf_32_string_to_utf_8_string_8 (m.message))
s.append_character ('%N')
save_to_file (s, date_to_yyyymmdd_hhmmss_string (now))
end
end

View File

@@ -0,0 +1,27 @@
note
description: "[
Persistence interface for CONTACT_MODULE.
]"
author: "$Author: jfiat $"
date: "$Date: 2015-05-22 23:00:09 +0200 (ven., 22 mai 2015) $"
revision: "$Revision: 97349 $"
deferred class
CONTACT_STORAGE_I
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
deferred
end
feature -- Access
feature -- Change
save_contact_message (m: CONTACT_MESSAGE)
deferred
end
end

View File

@@ -0,0 +1,39 @@
note
description: "[
Objects that ...
]"
author: "$Author: jfiat $"
date: "$Date: 2015-05-22 23:00:09 +0200 (ven., 22 mai 2015) $"
revision: "$Revision: 97349 $"
class
CONTACT_STORAGE_NULL
inherit
CONTACT_STORAGE_I
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
create error_handler.make
end
feature -- Error Handling
error_handler: ERROR_HANDLER
-- Error handler.
feature -- Access
feature -- Change
save_contact_message (m: CONTACT_MESSAGE)
do
end
end

View File

@@ -0,0 +1,49 @@
note
description: "[
Contact message storage based on SQL statements.
]"
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
CONTACT_STORAGE_SQL
inherit
CMS_PROXY_STORAGE_SQL
CONTACT_STORAGE_I
CMS_STORAGE_SQL_I
REFACTORING_HELPER
create
make
feature -- Access
feature -- Change
save_contact_message (m: CONTACT_MESSAGE)
local
l_parameters: STRING_TABLE [detachable ANY]
now: DATE_TIME
do
create now.make_now_utc
error_handler.reset
write_information_log (generator + ".save_contact_message")
create l_parameters.make (9)
l_parameters.put (m, "message")
l_parameters.put (now, "changed")
sql_begin_transaction
sql_modify (sql_insert_contact_message, l_parameters)
sql_commit_transaction
end
feature {NONE} -- Queries
sql_insert_contact_message: STRING = "INSERT INTO contact_messages (name, email, date, message) VALUES (:name, :email, :date, :message);"
-- SQL Insert to add a new contact message.
end

View File

@@ -290,9 +290,7 @@ feature -- Access: Node
is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN
-- Is the user `u' owner of the node `n'. -- Is the user `u' owner of the node `n'.
do do
if attached node_storage.node_author (a_node) as l_author then Result := u.same_as (a_node.author)
Result := u.same_as (l_author)
end
end end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE] nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]

View File

@@ -127,8 +127,13 @@ feature {CMS_API} -- Module management
-- Schema -- Schema
if attached a_api.storage.as_sql_storage as l_sql_storage then if attached a_api.storage.as_sql_storage as l_sql_storage then
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) 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)
if l_sql_storage.has_error then
a_api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
else
Precursor {CMS_MODULE} (a_api)
end
end end
Precursor {CMS_MODULE}(a_api)
end end
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API

View File

@@ -122,7 +122,7 @@ feature -- HTTP Methods
if if
l_node /= Void and then l_node /= Void and then
l_rev > 0 and then l_rev > 0 and then
node_api.has_permission_for_action_on_node ("view revisions", l_node, current_user (req)) node_api.has_permission_for_action_on_node ("view revisions", l_node, api.user)
then then
l_node := node_api.revision_node (l_nid, l_rev) l_node := node_api.revision_node (l_nid, l_rev)
end end
@@ -137,9 +137,9 @@ feature -- HTTP Methods
view_response.set_revision (l_rev) view_response.set_revision (l_rev)
view_response.execute view_response.execute
elseif elseif
attached current_user (req) as l_user and then attached api.user as l_user and then
( node_api.is_author_of_node (l_user, l_node) ( node_api.is_author_of_node (l_user, l_node)
or else api.user_api.user_has_permission (l_user, "view unpublished " + l_node.content_type) or else api.user_has_permission (l_user, "view unpublished " + l_node.content_type)
) )
then then
create view_response.make (req, res, api, node_api) create view_response.make (req, res, api, node_api)
@@ -208,13 +208,13 @@ feature -- HTTP Methods
do_trash (req: WSF_REQUEST; res: WSF_RESPONSE) do_trash (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Trash a node, soft delete. -- Trash a node, soft delete.
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("trash", l_node, l_user) then
node_api.trash_node (l_node) node_api.trash_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
@@ -245,13 +245,13 @@ feature {NONE} -- Trash:Restore
local local
l_source: STRING l_source: STRING
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then
attached {CMS_NODE} node_api.node (l_id.integer_value) as l_node attached {CMS_NODE} node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("delete", l_node, l_user) then
node_api.delete_node (l_node) node_api.delete_node (l_node)
l_source := node_api.node_path (l_node) l_source := node_api.node_path (l_node)
api.unset_path_alias (l_source, api.location_alias (l_source)) api.unset_path_alias (l_source, api.location_alias (l_source))
@@ -274,13 +274,13 @@ feature {NONE} -- Trash:Restore
do_restore (req: WSF_REQUEST; res: WSF_RESPONSE) do_restore (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Restore a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published. -- Restore a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published.
do do
if attached current_user (req) as l_user then if attached api.user as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
if if
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("restore", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("restore", l_node, l_user) then
node_api.restore_node (l_node) node_api.restore_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
@@ -310,7 +310,7 @@ feature {NONE} -- Trash:Restore
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("view revisions", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("view revisions", l_node, api.user) then
create r.make (req, res, api) create r.make (req, res, api)
create b.make_empty create b.make_empty
b.append ("<ul>") b.append ("<ul>")

View File

@@ -53,6 +53,7 @@ feature -- Execution
then then
l_manager.append_content_as_html_to_page (l_node, Current) l_manager.append_content_as_html_to_page (l_node, Current)
end end
set_modification_date (l_node.modification_date)
elseif revision > 0 then elseif revision > 0 then
set_main_content ("Missing revision node!") set_main_content ("Missing revision node!")
else else
@@ -64,8 +65,6 @@ feature -- Execution
if l_node /= Void and revision > 0 then if l_node /= Void and revision > 0 then
set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title)) set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title))
end end
end end
end end

View File

@@ -120,13 +120,6 @@ feature -- Access
deferred deferred
end end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author. if any.
require
valid_node: a_node.has_id
deferred
end
nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE] nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE]
-- List of nodes of type `a_node_type'. -- List of nodes of type `a_node_type'.
--| Redefine to optimize! --| Redefine to optimize!

View File

@@ -75,17 +75,6 @@ feature -- Access: node
do do
end end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author. if any.
do
end
node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER]
-- Possible list of node's collaborator.
do
create {ARRAYED_LIST [CMS_USER]} Result.make (0)
end
feature -- Access: outline feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE] children (a_node: CMS_NODE): detachable LIST [CMS_NODE]

View File

@@ -206,23 +206,6 @@ feature -- Access
sql_finalize sql_finalize
end end
node_author (a_node: CMS_NODE): detachable CMS_USER
-- Node's author for the given node id.
local
l_parameters: STRING_TABLE [ANY]
do
error_handler.reset
write_information_log (generator + ".node_author")
create l_parameters.make (2)
l_parameters.put (a_node.id, "nid")
l_parameters.put (a_node.revision, "revision")
sql_query (Select_user_author, l_parameters)
if not has_error and not sql_after then
Result := fetch_author
end
sql_finalize
end
last_inserted_node_id: INTEGER_64 last_inserted_node_id: INTEGER_64
-- Last insert node id. -- Last insert node id.
do do
@@ -589,11 +572,6 @@ feature {NONE} -- Queries
sql_delete_node_revisions: STRING = "DELETE FROM node_revisions WHERE nid=:nid;" sql_delete_node_revisions: STRING = "DELETE FROM node_revisions WHERE nid=:nid;"
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author
Select_user_author: STRING = "SELECT uid, name, password, salt, email, users.status, users.created, signed FROM nodes INNER JOIN users ON nodes.author=users.uid AND nodes.nid = :nid AND nodes.revision = :revision;"
-- Select_node_author: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed FROM users INNER JOIN nodes ON nodes.author=users.uid AND nodes.nid =:nid;"
feature {NONE} -- Implementation feature {NONE} -- Implementation
@@ -638,23 +616,4 @@ feature {NONE} -- Implementation
end end
end end
fetch_author: detachable CMS_USER
do
if attached sql_read_string_32 (2) as l_name and then not l_name.is_whitespace then
create Result.make (l_name)
if attached sql_read_integer_32 (1) as l_id then
Result.set_id (l_id)
end
if attached sql_read_string (3) as l_password then
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
else
check expected_valid_user: False end
end
end
end end

View File

@@ -3,22 +3,17 @@ ul.cms-nodes {
list-style-type: none; list-style-type: none;
padding: 3px 3px 3px 3px; padding: 3px 3px 3px 3px;
border: solid 1px #ccc; border: solid 1px #ccc;
li{ li{
border-top: dotted 1px #ccc; border-top: dotted 1px #ccc;
&:first-child { &:first-child {
border-top: none; border-top: none;
} }
} }
li.cms_type_page a::before { li.cms_type_page a::before {
content: "[page] "; content: "[page] ";
} }
li.cms_type_blog a::before { li.cms_type_blog a::before {
content: "[blog] "; content: "[blog] ";
} }
} }

View File

@@ -1,7 +1,7 @@
note note
description: "[ description: "[
API to manage CMS User OAuth authentication. API to manage CMS User OAuth authentication.
]" ]"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
@@ -9,7 +9,7 @@ class
CMS_OAUTH_20_API CMS_OAUTH_20_API
inherit inherit
CMS_MODULE_API CMS_AUTH_API_I
REFACTORING_HELPER REFACTORING_HELPER
@@ -20,19 +20,36 @@ feature {NONE} -- Initialization
make_with_storage (a_api: CMS_API; a_oauth_storage: CMS_OAUTH_20_STORAGE_I) make_with_storage (a_api: CMS_API; a_oauth_storage: CMS_OAUTH_20_STORAGE_I)
-- Create an object with api `a_api' and storage `a_oauth_storage'. -- Create an object with api `a_api' and storage `a_oauth_storage'.
local
s: detachable READABLE_STRING_8
do do
oauth_20_storage := a_oauth_storage oauth_20_storage := a_oauth_storage
make (a_api) make (a_api)
-- Initialize session related settings.
s := a_api.setup.string_8_item ("auth.oauth.token")
if s = Void then
s := a_api.setup.site_id + default_session_token_suffix
end
create session_token.make_from_string (s)
ensure ensure
oauht_20_storage_set: oauth_20_storage = a_oauth_storage oauth_20_storage_set: oauth_20_storage = a_oauth_storage
end end
feature {CMS_MODULE} -- Access: User oauth storage. feature {CMS_MODULE} -- Access: Oauth storage.
oauth_20_storage: CMS_OAUTH_20_STORAGE_I oauth_20_storage: CMS_OAUTH_20_STORAGE_I
-- storage interface. -- storage interface.
feature -- Access: User Oauth20 feature -- Access: tokens
default_session_token_suffix: STRING = "_OAUTH_TOKEN_"
-- Default value for `session_auth_token'.
session_token: IMMUTABLE_STRING_8
-- Name of Cookie used to keep the session info.
feature -- Access: Oauth20
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER 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. -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if any.
@@ -80,7 +97,6 @@ feature -- Access: Consumers OAuth20
feature -- Change: User OAuth20 feature -- Change: User OAuth20
new_user_oauth2 (a_token: READABLE_STRING_GENERAL; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_GENERAL) 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'. -- Add a new user with oauth20 using the consumer `a_consumer'.
require require
@@ -107,6 +123,4 @@ feature -- Change: User OAuth20
oauth_20_storage.remove_user_oauth2 (a_user, a_consumer_table) oauth_20_storage.remove_user_oauth2 (a_user, a_consumer_table)
end end
end end

View File

@@ -1,21 +0,0 @@
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

@@ -1,5 +1,7 @@
note note
description: "Generic OAuth Module supporting authentication using different providers." description: "[
Generic OAuth Module supporting authentication using different providers.
]"
date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $" date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $"
revision: "$Revision: 97328 $" revision: "$Revision: 97328 $"
@@ -7,26 +9,20 @@ class
CMS_OAUTH_20_MODULE CMS_OAUTH_20_MODULE
inherit inherit
CMS_MODULE CMS_AUTH_MODULE_I
rename rename
module_api as user_oauth_api module_api as oauth20_api
redefine redefine
make,
filters, filters,
setup_hooks, setup_hooks,
initialize, initialize,
install, install,
user_oauth_api oauth20_api
end end
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_EXECUTION_ENVIRONMENT SHARED_EXECUTION_ENVIRONMENT
export export
{NONE} all {NONE} all
@@ -34,11 +30,6 @@ inherit
REFACTORING_HELPER REFACTORING_HELPER
SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -47,11 +38,9 @@ feature {NONE} -- Initialization
make make
-- Create current module -- Create current module
do do
Precursor
version := "1.0" version := "1.0"
description := "OAuth20 module" description := "OAuth20 module"
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
create root_dir.make_current create root_dir.make_current
cache_duration := 0 cache_duration := 0
@@ -66,24 +55,24 @@ feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API) initialize (a_api: CMS_API)
-- <Precursor> -- <Precursor>
local local
l_user_auth_api: like user_oauth_api l_oauth20_api: like oauth20_api
l_user_auth_storage: CMS_OAUTH_20_STORAGE_I l_auth_storage: CMS_OAUTH_20_STORAGE_I
do do
Precursor (a_api) Precursor (a_api)
-- Storage initialization -- Storage initialization
if attached a_api.storage.as_sql_storage as l_storage_sql then if attached a_api.storage.as_sql_storage as l_storage_sql then
create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) create {CMS_OAUTH_20_STORAGE_SQL} l_auth_storage.make (l_storage_sql)
else else
-- FIXME: in case of NULL storage, should Current be disabled? -- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage create {CMS_OAUTH_20_STORAGE_NULL} l_auth_storage
end end
-- API initialization -- API initialization
create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage) create l_oauth20_api.make_with_storage (a_api, l_auth_storage)
user_oauth_api := l_user_auth_api oauth20_api := l_oauth20_api
ensure then ensure then
user_oauth_api_set: user_oauth_api /= Void user_oauth_api_set: oauth20_api /= Void
end end
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
@@ -94,62 +83,66 @@ feature {CMS_API} -- Module management
do do
-- Schema -- Schema
if attached api.storage.as_sql_storage as l_sql_storage then if attached api.storage.as_sql_storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("oauth2_consumers") then
--| Schema --| Schema
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("oauth2_consumers.sql")), Void) 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 if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for oauth_20 module", generating_type) api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
end else
-- TODO workaround. -- 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) 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 if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize oauth2_consumers for module [" + name + "]", generating_type)
else
-- 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 different consumers", 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
l_sql_storage.sql_finalize
-- TODO workaround, until we have an admin module across l_consumers as ic loop
l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void) if not l_sql_storage.sql_table_exists (ic.item) then
if l_sql_storage.has_error 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
api.logger.put_error ("Could not initialize database for differnent consumers", generating_type) -- FIXME: shouldn't we use a unique table for all oauth providers? or as it is .. one table per oauth provider?
else sql.replace_substring_all ("$table_name", ic.item)
from l_sql_storage.sql_execute_script (sql, Void)
l_sql_storage.sql_start end
create {ARRAYED_LIST [STRING]} l_consumers.make (2) end
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
l_sql_storage.sql_finalize
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
l_sql_storage.sql_finalize
Precursor {CMS_AUTH_MODULE_I}(api) -- Marked as installed.
end end
end end
l_sql_storage.sql_finalize
Precursor {CMS_MODULE}(api)
end end
end end
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API
user_oauth_api: detachable CMS_OAUTH_20_API oauth20_api: detachable CMS_OAUTH_20_API
-- <Precursor> -- <Precursor>
feature -- Filters feature -- Filters
filters (a_api: CMS_API): detachable LIST [WSF_FILTER] filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
-- Possibly list of Filter's module. -- Possibly list of Filter's module.
do do
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached oauth20_api as l_oauth_api then
if attached user_oauth_api as l_user_oauth_api then create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api)) Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_oauth_api))
end end
end end
@@ -168,107 +161,82 @@ feature -- Access: docs
Result := cache_duration = 0 Result := cache_duration = 0
end end
feature -- Access: auth strategy
login_title: STRING = "OAuth"
-- Module specific login title.
login_location: STRING = "account/auth/roc-oauth-login"
logout_location: STRING = "account/auth/roc-oauth-logout"
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- <Precursor>
do
if
a_response.is_authenticated and then
attached oauth20_api as l_oauth20_api and then
attached a_response.request.cookie (l_oauth20_api.session_token)
then
Result := True
end
end
feature -- Router feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor> -- <Precursor>
do do
if attached user_oauth_api as l_user_oauth_api then if attached oauth20_api as l_oauth_api then
configure_web (a_api, l_user_oauth_api, a_router) a_router.handle ("/" + login_location,
create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/" + logout_location,
create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_oauth_api, ?, ?)),
a_router.methods_get_post)
a_router.handle ("/account/auth/login-with-oauth/{" + oauth_callback_path_parameter + "}",
create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api, l_oauth_api, ?, ?)),
a_router.methods_get_post)
a_router.handle ("/account/auth/oauth-callback/{" + oauth_callback_path_parameter + "}",
create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, l_oauth_api, ?, ?)),
a_router.methods_get_post)
a_router.handle ("/account/auth/oauth-associate",
create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_associate (a_api, l_oauth_api, ?, ?)),
a_router.methods_post)
a_router.handle ("/account/auth/oauth-un-associate",
create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_un_associate (a_api, l_oauth_api, ?, ?)),
a_router.methods_post)
end end
end end
configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER) oauth_callback_path_parameter: STRING = "callback"
do -- Callback path parameter.
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) oauth_code_query_parameter: STRING = "code"
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) -- Code query parameter, specific to OAuth protocol.
a_router.handle ("/account/oauth-callback/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) -- FIXME: should we have a way to change this value?
a_router.handle ("/account/oauth-associate", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_associate (a_api, a_user_oauth_api, ?, ?)), a_router.methods_post) -- : if a OAuth provider is not using "code" query name.
a_router.handle ("/account/oauth-un-associate", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_un_associate (a_api, a_user_oauth_api, ?, ?)), a_router.methods_post)
end
feature -- Hooks configuration feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration. -- Module hooks configuration.
do do
auto_subscribe_to_hooks (a_hooks) Precursor (a_hooks)
a_hooks.subscribe_to_block_hook (Current) a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end end
feature -- Hooks feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if
attached a_response.user as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session)
then
a_value.force ("account/roc-oauth-logout", "auth_login_strategy")
end
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
local
lnk: CMS_LOCAL_LINK
lnk2: detachable CMS_LINK
do
if
attached a_response.user as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_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.location.same_string ("account/roc-logout") or else
ic.item.location.same_string ("basic_auth_logoff")
then
lnk2 := ic.item
end
end
if lnk2 /= Void then
a_menu_system.primary_menu.remove (lnk2)
end
create lnk.make ("Logout", "account/roc-oauth-logout" )
a_menu_system.primary_menu.extend (lnk)
else
if a_response.location.starts_with ("account/") then
create lnk.make ("OAuth", "account/roc-oauth-login")
a_response.add_to_primary_tabs (lnk)
end
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"login", "account">> Result := <<"login", "account">>
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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if if
a_block_id.is_case_insensitive_equal_general ("login") and then a_block_id.is_case_insensitive_equal_general ("login") and then
a_response.location.starts_with ("account/roc-oauth-login") a_response.location.starts_with (login_location)
then then
get_block_view_login (a_block_id, a_response) get_block_view_login (a_block_id, a_response)
elseif a_block_id.is_case_insensitive_equal_general ("account") and then elseif a_block_id.is_case_insensitive_equal_general ("account") and then
@@ -279,6 +247,7 @@ feature -- Hooks
attached a_response.user as l_user attached a_response.user as l_user
then then
associate_account (l_user, a_response.values) associate_account (l_user, a_response.values)
l_tpl_block.set_weight (5)
a_response.add_block (l_tpl_block, "content") a_response.add_block (l_tpl_block, "content")
else else
debug ("cms") debug ("cms")
@@ -293,25 +262,29 @@ feature -- Hooks
r: CMS_RESPONSE r: CMS_RESPONSE
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_value ("Login", "optional_content_type") if r.is_authenticated then
r.add_error_message ("You are already signed in!")
else
r.set_value ("Login", "optional_content_type")
end
r.execute r.execute
end end
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_logout (api: CMS_API; a_oauth20_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_cookie: WSF_COOKIE l_cookie: WSF_COOKIE
do do
if if
attached {WSF_STRING} req.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_cookie_token and then attached api.user as l_user and then
attached {CMS_USER} current_user (req) as l_user attached {WSF_STRING} req.cookie (a_oauth20_api.session_token) as l_cookie_token
then then
-- Logout OAuth -- Logout OAuth
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_cookie_token.value) create l_cookie.make (a_oauth20_api.session_token, l_cookie_token.value)
l_cookie.set_path ("/") l_cookie.set_path ("/")
l_cookie.set_max_age (-1) l_cookie.set_max_age (-1)
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
unset_current_user (req) api.unset_current_user (req)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found) r.set_status_code ({HTTP_CONSTANTS}.found)
@@ -329,7 +302,7 @@ feature {NONE} -- Associate
l_associated: LIST [STRING] l_associated: LIST [STRING]
l_not_associated: LIST [STRING] l_not_associated: LIST [STRING]
do do
if attached user_oauth_api as l_oauth_api then if attached oauth20_api as l_oauth_api then
create {ARRAYED_LIST [STRING]} l_associated.make (1) create {ARRAYED_LIST [STRING]} l_associated.make (1)
create {ARRAYED_LIST [STRING]} l_not_associated.make (1) create {ARRAYED_LIST [STRING]} l_not_associated.make (1)
across l_oauth_api.oauth2_consumers as ic loop across l_oauth_api.oauth2_consumers as ic loop
@@ -344,25 +317,6 @@ feature {NONE} -- Associate
end end
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 feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -372,14 +326,14 @@ feature {NONE} -- Block views
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached template_block (a_block_id, a_response) as l_tpl_block then
create vals.make (1) create vals.make (1)
-- add the variable to the block -- add the variable to the block
value_table_alter (vals, a_response) a_response.api.hooks.invoke_value_table_alter (vals, a_response)
across across
vals as ic vals as ic
loop loop
l_tpl_block.set_value (ic.item, ic.key) l_tpl_block.set_value (ic.item, ic.key)
end end
if if
attached user_oauth_api as l_auth_api and then attached oauth20_api as l_auth_api and then
attached l_auth_api.oauth2_consumers as l_list attached l_auth_api.oauth2_consumers as l_list
then then
l_tpl_block.set_value (l_list, "oauth_consumers") l_tpl_block.set_value (l_list, "oauth_consumers")
@@ -402,7 +356,7 @@ feature -- OAuth2 Login with Provider
l_oauth: CMS_OAUTH_20_WORKFLOW l_oauth: CMS_OAUTH_20_WORKFLOW
do do
if if
attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as p_consumer and then attached {WSF_STRING} req.path_parameter (oauth_callback_path_parameter) as p_consumer and then
attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer
then then
create l_oauth.make (req.server_url, l_consumer) create l_oauth.make (req.server_url, l_consumer)
@@ -422,7 +376,7 @@ feature -- OAuth2 Login with Provider
end end
end end
handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_callback_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_auth: CMS_OAUTH_20_WORKFLOW l_auth: CMS_OAUTH_20_WORKFLOW
@@ -432,9 +386,9 @@ feature -- OAuth2 Login with Provider
l_cookie: WSF_COOKIE l_cookie: WSF_COOKIE
es: CMS_AUTHENTICATION_EMAIL_SERVICE es: CMS_AUTHENTICATION_EMAIL_SERVICE
do do
if attached {WSF_STRING} req.path_parameter ({CMS_OAUTH_20_CONSTANTS}.oauth_callback) as l_callback and then if attached {WSF_STRING} req.path_parameter (oauth_callback_path_parameter) 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 {CMS_OAUTH_20_CONSUMER} a_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 attached {WSF_STRING} req.query_parameter (oauth_code_query_parameter) as l_code
then then
create l_auth.make (req.server_url, l_consumer) create l_auth.make (req.server_url, l_consumer)
l_auth.sign_request (l_code.value) l_auth.sign_request (l_code.value)
@@ -452,20 +406,20 @@ feature -- OAuth2 Login with Provider
then then
if attached l_user_api.user_by_email (l_email) as p_user then if attached l_user_api.user_by_email (l_email) as p_user then
-- User with email exist -- User with email exist
if attached a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then if attached a_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then
-- Update oauth entry -- Update oauth entry
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name ) a_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
else else
-- create a oauth entry -- create a oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name ) a_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
end end
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_access_token.token) create l_cookie.make (a_oauth_api.session_token, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/") l_cookie.set_path ("/")
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
elseif attached a_user_oauth_api.user_oauth2_by_email (l_email, l_consumer.name) as p_user then elseif attached a_oauth_api.user_oauth2_by_email (l_email, l_consumer.name) as p_user then
a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name ) a_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name )
create l_cookie.make ({CMS_OAUTH_20_CONSTANTS}.oauth_session, l_access_token.token) create l_cookie.make (a_oauth_api.session_token, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/") l_cookie.set_path ("/")
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
@@ -482,15 +436,15 @@ feature -- OAuth2 Login with Provider
l_user_api.new_user (l_user) l_user_api.new_user (l_user)
-- Add oauth entry -- Add oauth entry
a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name ) a_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) create l_cookie.make (a_oauth_api.session_token, l_access_token.token)
l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_max_age (l_access_token.expires_in)
l_cookie.set_path ("/") l_cookie.set_path ("/")
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
set_current_user (req, l_user) api.set_user (l_user)
api.record_user_login (l_user)
-- Send Email
-- Send Email
create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api))
write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email") write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email")
es.send_contact_welcome_email (l_email, l_user, req.absolute_script_url ("")) es.send_contact_welcome_email (l_email, l_user, req.absolute_script_url (""))
@@ -499,9 +453,7 @@ feature -- OAuth2 Login with Provider
r.set_redirection (r.front_page_url) r.set_redirection (r.front_page_url)
r.execute r.execute
end end
end end
end end
handle_associate (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_associate (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
@@ -518,14 +470,13 @@ feature -- OAuth2 Login with Provider
then then
l_user.set_email (l_email.value) l_user.set_email (l_email.value)
a_oauth_api.new_user_oauth2 ("none", "none", l_user, l_consumer.value ) a_oauth_api.new_user_oauth2 ("none", "none", l_user, l_consumer.value )
-- TODO send email? -- TODO send email?
end end
end end
r.set_redirection (req.absolute_script_url ("/account")) r.set_redirection (req.absolute_script_url ("/account"))
r.execute r.execute
end end
handle_un_associate (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_un_associate (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE

View File

@@ -1,6 +1,7 @@
note note
description: "[ description: "[
Extracts an OAuth2 token from the incoming request (cookie) and uses it to populate the user (or cms user context) Extracts an OAuth2 token from the incoming request (cookie) and
uses it to populate the user (or cms user context).
]" ]"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
@@ -9,14 +10,11 @@ class
CMS_OAUTH_20_FILTER CMS_OAUTH_20_FILTER
inherit inherit
WSF_URI_TEMPLATE_HANDLER CMS_AUTH_FILTER_I
CMS_HANDLER
rename rename
make as make_handler make as make_filter
end end
WSF_FILTER
create create
make make
@@ -24,29 +22,30 @@ feature {NONE} -- Initialization
make (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API) make (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API)
do do
make_handler (a_api) make_filter (a_api)
user_oauth_api := a_user_oauth_api oauth_api := a_user_oauth_api
end end
user_oauth_api: CMS_OAUTH_20_API oauth_api: CMS_OAUTH_20_API
feature -- Basic operations feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE) auth_strategy: STRING
-- Execute the filter. do
Result := {CMS_OAUTH_20_MODULE}.logout_location
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>.
do do
api.logger.put_debug (generator + ".execute ", Void)
-- A valid user
if if
attached {WSF_STRING} req.cookie ({CMS_OAUTH_20_CONSTANTS}.oauth_session) as l_roc_auth_session_token attached {WSF_STRING} req.cookie (oauth_api.session_token) as l_roc_auth_session_token
then then
if attached user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then if attached oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then
set_current_user (req, l_user) set_current_user (l_user)
else else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void)
end end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
end end
execute_next (req, res) execute_next (req, res)
end end

View File

@@ -21,7 +21,7 @@ feature -- Error Handling
feature -- Access: Users feature -- Access: Users
user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_GENERAL): 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. -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if any.
deferred deferred
end end

View File

@@ -45,23 +45,27 @@ feature -- Access User Outh
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING l_string: STRING
l_uid: INTEGER_64
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".user_oauth2_by_id") write_information_log (generator + ".user_oauth2_by_id")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_uid, "uid") l_parameters.put (a_uid, "uid")
create l_string.make_from_string (select_user_oauth2_template_by_id) create l_string.make_from_string (select_user_id_oauth2_template_by_id)
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer)) l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_query (l_string, l_parameters) sql_query (l_string, l_parameters)
if not has_error and not sql_after then if not has_error and not sql_after then
Result := fetch_user l_uid := sql_read_integer_64 (1)
sql_forth sql_forth
if not sql_after then if not sql_after then
check no_more_than_one: False end check no_more_than_one: False end
Result := Void l_uid := 0
end end
end end
sql_finalize sql_finalize
if l_uid > 0 and attached api as l_cms_api then
Result := l_cms_api.user_api.user_by_id (l_uid)
end
end end
user_oauth2_by_email (a_email: like {CMS_USER}.email; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER user_oauth2_by_email (a_email: like {CMS_USER}.email; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
@@ -69,23 +73,27 @@ feature -- Access User Outh
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING l_string: STRING
l_uid: INTEGER_64
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".user_oauth2_by_email") write_information_log (generator + ".user_oauth2_by_email")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_email, "email") l_parameters.put (a_email, "email")
create l_string.make_from_string (select_user_oauth2_template_by_email) create l_string.make_from_string (select_user_id_oauth2_template_by_email)
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer)) l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_query (l_string, l_parameters) sql_query (l_string, l_parameters)
if not has_error and not sql_after then if not has_error and not sql_after then
Result := fetch_user l_uid := sql_read_integer_64 (1)
sql_forth sql_forth
if not sql_after then if not sql_after then
check no_more_than_one: False end check no_more_than_one: False end
Result := Void l_uid := 0
end end
end end
sql_finalize sql_finalize
if l_uid > 0 and attached api as l_cms_api then
Result := l_cms_api.user_api.user_by_id (l_uid)
end
end end
user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER user_oauth2_by_token (a_token: READABLE_STRING_GENERAL; a_consumer: READABLE_STRING_GENERAL): detachable CMS_USER
@@ -93,26 +101,29 @@ feature -- Access User Outh
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
l_string: STRING l_string: STRING
l_uid: INTEGER_64
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".user_by_oauth2_token") write_information_log (generator + ".user_by_oauth2_token")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_token, "token") l_parameters.put (a_token, "token")
create l_string.make_from_string (select_user_by_oauth2_template_token) create l_string.make_from_string (select_user_id_by_oauth2_template_token)
l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer)) l_string.replace_substring_all ("$table_name", oauth2_sql_table_name (a_consumer))
sql_query (l_string, l_parameters) sql_query (l_string, l_parameters)
if not has_error and not sql_after then if not has_error and not sql_after then
Result := fetch_user l_uid := sql_read_integer_64 (1)
sql_forth sql_forth
if not sql_after then if not sql_after then
check no_more_than_one: False end check no_more_than_one: False end
Result := Void l_uid := 0
end end
end end
sql_finalize sql_finalize
if l_uid > 0 and attached api as l_cms_api then
Result := l_cms_api.user_api.user_by_id (l_uid)
end
end end
feature --Access: Consumers feature --Access: Consumers
oauth2_consumers: LIST [STRING] oauth2_consumers: LIST [STRING]
@@ -286,45 +297,6 @@ feature {NONE} -- Implementation OAuth Consumer
end end
end end
feature {NONE} -- Implementation: User
fetch_user: detachable CMS_USER
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_64 (1) as i then
l_id := i
end
if attached sql_read_string_32 (2) as s and then not s.is_whitespace then
l_name := s
end
if l_name /= Void then
create Result.make (l_name)
if l_id > 0 then
Result.set_id (l_id)
end
elseif l_id > 0 then
create Result.make_with_id (l_id)
end
if Result /= Void then
if attached sql_read_string (3) as l_password then
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
if attached sql_read_integer_32 (6) as l_status then
Result.set_status (l_status)
end
else
check expected_valid_user: False end
end
end
feature {NONE} -- User OAuth2 feature {NONE} -- User OAuth2
oauth2_sql_table_name (a_consumer: READABLE_STRING_GENERAL): STRING_8 oauth2_sql_table_name (a_consumer: READABLE_STRING_GENERAL): STRING_8
@@ -353,12 +325,20 @@ feature {NONE} -- User OAuth2
end 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;" Select_user_id_by_oauth2_template_token: STRING = "SELECT uid FROM $table_name WHERE access_token = :token;"
--| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. --| User id for access token :token
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;" Select_user_id_oauth2_template_by_id: STRING = "SELECT uid FROM $table_name WHERE uid = :uid;"
Select_user_oauth2_template_by_email: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.email = :email;" Select_user_id_oauth2_template_by_email: STRING = "SELECT uid FROM $table_name WHERE email = :email;"
-- 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;"
-- Select_user_oauth2_template_by_email: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.email = :email;"
Sql_insert_oauth2_template: STRING = "INSERT INTO $table_name (uid, access_token, details, created, email) VALUES (:uid, :token, :profile, :utc_date, :email);" Sql_insert_oauth2_template: STRING = "INSERT INTO $table_name (uid, access_token, details, created, email) VALUES (:uid, :token, :profile, :utc_date, :email);"

View File

@@ -7,8 +7,9 @@ note
class class
CMS_OPENID_API CMS_OPENID_API
inherit inherit
CMS_MODULE_API CMS_AUTH_API_I
REFACTORING_HELPER REFACTORING_HELPER
@@ -19,11 +20,27 @@ feature {NONE} -- Initialization
make_with_storage (a_api: CMS_API; a_openid_storage: CMS_OPENID_STORAGE_I) make_with_storage (a_api: CMS_API; a_openid_storage: CMS_OPENID_STORAGE_I)
-- Create an object with api `a_api' and storage `a_openid_storage'. -- Create an object with api `a_api' and storage `a_openid_storage'.
local
s: detachable READABLE_STRING_8
do do
openid_storage := a_openid_storage openid_storage := a_openid_storage
make (a_api) make (a_api)
-- Initialize openid related settings.
s := a_api.setup.string_8_item ("auth." + {CMS_OPENID_MODULE}.name + ".token")
if s = Void then
s := a_api.setup.site_id + default_session_token_suffix
end
create session_token.make_from_string (s)
s := a_api.setup.string_8_item ("auth.openid.max_age")
if s /= Void and then s.is_integer then
session_max_age := s.to_integer
else
session_max_age := 3600 --| one hour: *60(min) *60(sec)
end
ensure ensure
openid_storage_set: openid_storage = a_openid_storage openid_storage_set: openid_storage = a_openid_storage
end end
feature {CMS_MODULE} -- Access: User openid storage. feature {CMS_MODULE} -- Access: User openid storage.
@@ -31,6 +48,16 @@ feature {CMS_MODULE} -- Access: User openid storage.
openid_storage: CMS_OPENID_STORAGE_I openid_storage: CMS_OPENID_STORAGE_I
-- storage interface. -- storage interface.
feature -- Access: token
default_session_token_suffix: STRING = "_OPENID_TOKEN_"
session_token: IMMUTABLE_STRING_8
-- Name of Cookie used to keep the session info.
session_max_age: INTEGER
-- Max age.
feature -- Access: User Openid feature -- Access: User Openid
user_openid_by_userid_identity (a_uid: like {CMS_USER}.id; a_identity: READABLE_STRING_GENERAL): detachable CMS_USER user_openid_by_userid_identity (a_uid: like {CMS_USER}.id; a_identity: READABLE_STRING_GENERAL): detachable CMS_USER
@@ -60,7 +87,6 @@ feature -- Access: Consumers OAuth20
feature -- Change: User Openid feature -- Change: User Openid
new_user_openid (a_identity: READABLE_STRING_GENERAL; a_user: CMS_USER) new_user_openid (a_identity: READABLE_STRING_GENERAL; a_user: CMS_USER)
-- Add a new user with openid using the identity `a_identity'. -- Add a new user with openid using the identity `a_identity'.
require require

View File

@@ -1,16 +0,0 @@
note
description: "Summary description for {CMS_OPENID_CONSTANTS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_OPENID_CONSTANTS
feature -- Access
openid_session: STRING = "EWF_ROC_OPENID_TOKEN_"
-- Name of Cookie used to keep the session info.
-- FIXME: make this configurable.
consumer: STRING = "consumer"
end

View File

@@ -9,26 +9,20 @@ class
CMS_OPENID_MODULE CMS_OPENID_MODULE
inherit inherit
CMS_MODULE CMS_AUTH_MODULE_I
rename rename
module_api as user_openid_api module_api as openid_api
redefine redefine
make,
filters, filters,
setup_hooks, setup_hooks,
initialize, initialize,
install, install,
user_openid_api openid_api
end end
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_EXECUTION_ENVIRONMENT SHARED_EXECUTION_ENVIRONMENT
export export
{NONE} all {NONE} all
@@ -36,11 +30,6 @@ inherit
REFACTORING_HELPER REFACTORING_HELPER
SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -49,10 +38,9 @@ feature {NONE} -- Initialization
make make
-- Create current module -- Create current module
do do
Precursor
version := "1.0" version := "1.0"
description := "Openid module" description := "Openid module"
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
create root_dir.make_current create root_dir.make_current
cache_duration := 0 cache_duration := 0
@@ -68,7 +56,7 @@ feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API) initialize (a_api: CMS_API)
-- <Precursor> -- <Precursor>
local local
l_openid_api: like user_openid_api l_openid_api: like openid_api
l_openid_storage: CMS_OPENID_STORAGE_I l_openid_storage: CMS_OPENID_STORAGE_I
do do
Precursor (a_api) Precursor (a_api)
@@ -83,9 +71,9 @@ feature {CMS_API} -- Module Initialization
-- API initialization -- API initialization
create l_openid_api.make_with_storage (a_api, l_openid_storage) create l_openid_api.make_with_storage (a_api, l_openid_storage)
user_openid_api := l_openid_api openid_api := l_openid_api
ensure then ensure then
user_opend_api_set: user_openid_api /= Void user_opend_api_set: openid_api /= Void
end end
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
@@ -94,30 +82,33 @@ feature {CMS_API} -- Module management
do do
-- Schema -- Schema
if attached api.storage.as_sql_storage as l_sql_storage then if attached api.storage.as_sql_storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("openid_consumers") then
--| Schema --| Schema
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_consumers.sql")), Void) l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_consumers.sql")), Void)
if l_sql_storage.has_error then if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for openid module", generating_type) api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
end else
-- TODO workaround. -- TODO workaround.
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_consumers_initialize.sql")), Void) l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_consumers_initialize.sql")), Void)
end
-- TODO workaround, until we have an admin module -- TODO workaround, until we have an admin module
if l_sql_storage.has_error then if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for different consumers", generating_type) api.logger.put_error ("Could not initialize openid consumer table for module [" + name + "]", generating_type)
else else
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_items.sql")),Void) l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("openid_items.sql")),Void)
if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize openid items for module [" + name + "]", generating_type)
else
Precursor {CMS_AUTH_MODULE_I}(api) -- Mark it installed.
end
end
end end
Precursor {CMS_MODULE}(api)
end end
end end
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API
user_openid_api: detachable CMS_OPENID_API openid_api: detachable CMS_OPENID_API
-- <Precursor> -- <Precursor>
feature -- Filters feature -- Filters
@@ -125,9 +116,9 @@ feature -- Filters
filters (a_api: CMS_API): detachable LIST [WSF_FILTER] filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
-- Possibly list of Filter's module. -- Possibly list of Filter's module.
do do
if attached user_openid_api as l_user_openid_api then if attached openid_api as l_openid_api then
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
Result.extend (create {CMS_OPENID_FILTER}.make (a_api, l_user_openid_api)) Result.extend (create {CMS_OPENID_FILTER}.make (a_api, l_openid_api))
end end
end end
@@ -146,126 +137,95 @@ feature -- Access: docs
Result := cache_duration = 0 Result := cache_duration = 0
end end
feature -- Access: auth strategy
login_title: STRING = "OpenID"
-- Module specific login title.
login_location: STRING = "account/auth/roc-openid-login"
logout_location: STRING = "account/auth/roc-openid-logout"
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- <Precursor>
do
if
a_response.is_authenticated and then
attached openid_api as l_openid_api and then
attached {WSF_STRING} a_response.request.cookie (l_openid_api.session_token)
then
Result := True
end
end
feature -- Router feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor> -- <Precursor>
do do
if attached user_openid_api as l_user_openid_api then if attached openid_api as l_openid_api then
configure_web (a_api, l_user_openid_api, a_router) a_router.handle ("/" + login_location,
create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)),
a_router.methods_get_post)
a_router.handle ("/" + logout_location,
create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_openid_api, ?, ?)),
a_router.methods_get_post)
a_router.handle ("/account/auth/login-with-openid/{" + openid_consumer_path_parameter + "}",
create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_openid (a_api, l_openid_api, ?, ?)),
a_router.methods_get_post)
a_router.handle ("/account/auth/openid-callback",
create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_openid (a_api, l_openid_api, ?, ?)),
a_router.methods_get_post)
end end
end end
configure_web (a_api: CMS_API; a_user_openid_api: CMS_OPENID_API; a_router: WSF_ROUTER) openid_consumer_path_parameter: STRING = "consumer"
do -- Consumer path parameter name.
a_router.handle ("/account/roc-openid-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_openid_login (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/roc-openid-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/login-with-openid/{consumer}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_openid (a_api,a_user_openid_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/openid-callback", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_openid (a_api, a_user_openid_api, ?, ?)), a_router.methods_get_post)
end
feature -- Hooks configuration feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration. -- Module hooks configuration.
do do
auto_subscribe_to_hooks (a_hooks) Precursor (a_hooks)
a_hooks.subscribe_to_block_hook (Current) a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end end
feature -- Hooks feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if
attached a_response.user as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_OPENID_CONSTANTS}.openid_session)
then
a_value.force ("account/roc-openid-logout", "auth_login_strategy")
end
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
local
lnk: CMS_LOCAL_LINK
lnk2: detachable CMS_LINK
do
if
attached a_response.user as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_OPENID_CONSTANTS}.openid_session) as l_roc_auth_session_token
then
across
a_menu_system.primary_menu.items as ic
until
lnk2 /= Void
loop
if
ic.item.location.same_string ("account/roc-logout") or else
ic.item.location.same_string ("basic_auth_logoff")
then
lnk2 := ic.item
end
end
if lnk2 /= Void then
a_menu_system.primary_menu.remove (lnk2)
end
create lnk.make ("Logout", "account/roc-openid-logout" )
a_menu_system.primary_menu.extend (lnk)
else
if a_response.location.starts_with ("account/") then
create lnk.make ("Openid", "account/roc-openid-login")
a_response.add_to_primary_tabs (lnk)
end
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"login">> 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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if if
a_block_id.is_case_insensitive_equal_general ("login") and then a_block_id.is_case_insensitive_equal_general ("login") and then
a_response.location.starts_with ("account/roc-openid-login") a_response.location.starts_with (login_location)
then then
get_block_view_login (a_block_id, a_response) get_block_view_login (a_block_id, a_response)
end end
end end
handle_openid_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
o: OPENID_CONSUMER o: OPENID_CONSUMER
s: STRING s: STRING
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if req.is_get_request_method then if api.user_is_authenticated then
r.add_error_message ("You are already signed in!")
elseif req.is_get_request_method then
r.set_value ("Login", "optional_content_type") r.set_value ("Login", "optional_content_type")
r.execute
elseif req.is_post_request_method then elseif req.is_post_request_method then
create s.make_empty create s.make_empty
if attached req.string_item ("openid") as p_openid then if attached req.string_item ("openid") as p_openid then
s.append ("Check openID: " + p_openid) s.append ("Check openID: " + p_openid)
create o.make (req.absolute_script_url ("/account/login-with-openid")) create o.make (req.absolute_script_url ("/account/auth/login-with-openid"))
o.ask_email (True) o.ask_email (True)
o.ask_all_info (False) o.ask_all_info (False)
if attached o.auth_url (p_openid) as l_url then if attached o.auth_url (p_openid) as l_url then
@@ -274,27 +234,27 @@ feature -- Hooks
s.append (" Failure") s.append (" Failure")
r.set_status_code ({HTTP_CONSTANTS}.bad_request) r.set_status_code ({HTTP_CONSTANTS}.bad_request)
r.values.force (s, "error") r.values.force (s, "error")
r.execute
end end
end end
end end
r.execute
end end
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_logout (api: CMS_API; a_openid_api: CMS_OPENID_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_cookie: WSF_COOKIE l_cookie: WSF_COOKIE
do do
if if
attached {WSF_STRING} req.cookie ({CMS_OPENID_CONSTANTS}.openid_session) as l_cookie_token and then attached {CMS_USER} api.user as l_user and then
attached {CMS_USER} current_user (req) as l_user attached {WSF_STRING} req.cookie (a_openid_api.session_token) as l_cookie_token
then then
-- Logout OAuth -- Logout OAuth
create l_cookie.make ({CMS_OPENID_CONSTANTS}.openid_session, l_cookie_token.value) create l_cookie.make (a_openid_api.session_token, l_cookie_token.value)
l_cookie.set_path ("/") l_cookie.set_path ("/")
l_cookie.set_max_age (-1) l_cookie.set_max_age (-1)
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
unset_current_user (req) api.unset_current_user (req)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found) r.set_status_code ({HTTP_CONSTANTS}.found)
r.set_redirection (req.absolute_script_url ("")) r.set_redirection (req.absolute_script_url (""))
@@ -304,25 +264,6 @@ feature -- Hooks
end end
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 feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -332,14 +273,14 @@ feature {NONE} -- Block views
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached template_block (a_block_id, a_response) as l_tpl_block then
create vals.make (1) create vals.make (1)
-- add the variable to the block -- add the variable to the block
value_table_alter (vals, a_response) a_response.api.hooks.invoke_value_table_alter (vals, a_response)
across across
vals as ic vals as ic
loop loop
l_tpl_block.set_value (ic.item, ic.key) l_tpl_block.set_value (ic.item, ic.key)
end end
if if
attached user_openid_api as l_openid_api and then attached openid_api as l_openid_api and then
attached l_openid_api.openid_consumers as l_list attached l_openid_api.openid_consumers as l_list
then then
l_tpl_block.set_value (l_list, "openid_consumers") l_tpl_block.set_value (l_list, "openid_consumers")
@@ -356,18 +297,20 @@ feature {NONE} -- Block views
feature -- Openid Login feature -- Openid Login
handle_login_with_openid (api: CMS_API; a_oauth_api: CMS_OPENID_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_login_with_openid (api: CMS_API; a_openid_api: CMS_OPENID_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
b: STRING b: STRING
o: OPENID_CONSUMER o: OPENID_CONSUMER
do do
if attached {WSF_STRING} req.path_parameter ({CMS_OPENID_CONSTANTS}.consumer) as p_openid and then if
attached {CMS_OPENID_CONSUMER} a_oauth_api.openid_consumer_by_name (p_openid.value) as l_oc then attached {WSF_STRING} req.path_parameter (openid_consumer_path_parameter) as p_openid and then
attached {CMS_OPENID_CONSUMER} a_openid_api.openid_consumer_by_name (p_openid.value) as l_oc
then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
create b.make_empty create b.make_empty
b.append ("Check openID: " + p_openid.value) b.append ("Check openID: " + p_openid.value)
create o.make (req.absolute_script_url ("/account/openid-callback")) create o.make (req.absolute_script_url ("/account/auth/openid-callback"))
o.ask_email (True) o.ask_email (True)
o.ask_all_info (False) o.ask_all_info (False)
if attached o.auth_url (l_oc.endpoint) as l_url then if attached o.auth_url (l_oc.endpoint) as l_url then
@@ -383,7 +326,7 @@ feature -- Openid Login
end end
end end
handle_callback_openid (api: CMS_API; a_user_openid_api: CMS_OPENID_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_callback_openid (api: CMS_API; a_openid_api: CMS_OPENID_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_user_api: CMS_USER_API l_user_api: CMS_USER_API
@@ -410,16 +353,17 @@ feature -- Openid Login
l_user_api := api.user_api l_user_api := api.user_api
if attached l_user_api.user_by_email (l_email) as p_user then if attached l_user_api.user_by_email (l_email) as p_user then
-- User with email exist -- User with email exist
if attached a_user_openid_api.user_openid_by_userid_identity (p_user.id, l_identity) then if attached a_openid_api.user_openid_by_userid_identity (p_user.id, l_identity) then
-- Update openid entry? -- Update openid entry?
else else
-- create a oauth entry -- create a oauth entry
a_user_openid_api.new_user_openid (l_identity,p_user) a_openid_api.new_user_openid (l_identity,p_user)
end end
create l_cookie.make ({CMS_OPENID_CONSTANTS}.openid_session, l_identity) create l_cookie.make (a_openid_api.session_token, l_identity)
l_cookie.set_max_age (3600) l_cookie.set_max_age (a_openid_api.session_max_age)
l_cookie.set_path ("/") l_cookie.set_path ("/")
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
api.record_user_login (p_user)
else else
create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1) create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1)
@@ -434,9 +378,9 @@ feature -- Openid Login
l_user_api.new_user (l_user) l_user_api.new_user (l_user)
-- Add oauth entry -- Add oauth entry
a_user_openid_api.new_user_openid (l_identity, l_user ) a_openid_api.new_user_openid (l_identity, l_user )
create l_cookie.make ({CMS_OPENID_CONSTANTS}.openid_session, l_identity) create l_cookie.make (a_openid_api.session_token, l_identity)
l_cookie.set_max_age (3600) l_cookie.set_max_age (a_openid_api.session_max_age)
l_cookie.set_path ("/") l_cookie.set_path ("/")
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
@@ -475,37 +419,6 @@ feature {NONE} -- Token Generation
Result := l_token Result := l_token
end 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 note
copyright: "Copyright (c) 1984-2013, Eiffel Software and others" copyright: "Copyright (c) 1984-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -9,45 +9,42 @@ class
CMS_OPENID_FILTER CMS_OPENID_FILTER
inherit inherit
WSF_URI_TEMPLATE_HANDLER CMS_AUTH_FILTER_I
CMS_HANDLER
rename rename
make as make_handler make as make_filter
end end
WSF_FILTER
create create
make make
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_api: CMS_API; a_user_openid_api: CMS_OPENID_API) make (a_api: CMS_API; a_openid_api: CMS_OPENID_API)
do do
make_handler (a_api) make_filter (a_api)
user_openid_api := a_user_openid_api openid_api := a_openid_api
end end
user_openid_api: CMS_OPENID_API openid_api: CMS_OPENID_API
feature -- Basic operations feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE) auth_strategy: STRING
-- Execute the filter. do
Result := {CMS_OPENID_MODULE}.logout_location
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>.
do do
api.logger.put_debug (generator + ".execute ", Void)
-- A valid user
if if
attached {WSF_STRING} req.cookie ({CMS_OPENID_CONSTANTS}.openid_session) as l_roc_openid_session_token attached {WSF_STRING} req.cookie (openid_api.session_token) as l_roc_openid_session_token
then then
if attached user_openid_api.user_openid_by_identity (l_roc_openid_session_token.value) as l_user then if attached openid_api.user_openid_by_identity (l_roc_openid_session_token.value) as l_user then
set_current_user (req, l_user) set_current_user (l_user)
else else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_openid_session_token.value , Void) api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_openid_session_token.value , Void)
end end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
end end
execute_next (req, res) execute_next (req, res)
end end

View File

@@ -26,45 +26,53 @@ feature -- Access User Outh
-- <Precursor> -- <Precursor>
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
l_uid: INTEGER_64
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".user_openid_by_userid_identity") write_information_log (generator + ".user_openid_by_userid_identity")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_uid, "uid") l_parameters.put (a_uid, "uid")
l_parameters.put (a_identity, "identity") l_parameters.put (a_identity, "identity")
sql_query (Select_user_openid_by_id, l_parameters) sql_query (Select_user_id_openid_by_id, l_parameters)
if not has_error and not sql_after then if not has_error and not sql_after then
Result := fetch_user l_uid := sql_read_integer_64 (1)
sql_forth sql_forth
if not sql_after then if not sql_after then
check no_more_than_one: False end check no_more_than_one: False end
Result := Void l_uid := 0
end end
end end
sql_finalize sql_finalize
if l_uid > 0 and attached api as l_cms_api then
Result := l_cms_api.user_api.user_by_id (l_uid)
end
end end
user_openid_by_identity (a_identity: READABLE_STRING_GENERAL): detachable CMS_USER user_openid_by_identity (a_identity: READABLE_STRING_GENERAL): detachable CMS_USER
-- <Precursor> -- <Precursor>
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
l_uid: INTEGER_64
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".user_openid_by_identity") write_information_log (generator + ".user_openid_by_identity")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_identity, "identity") l_parameters.put (a_identity, "identity")
sql_query (Select_user_by_openid_identity, l_parameters) sql_query (Select_user_id_by_openid_identity, l_parameters)
if not has_error and not sql_after then if not has_error and not sql_after then
Result := fetch_user l_uid := sql_read_integer_64 (1)
sql_forth sql_forth
if not sql_after then if not sql_after then
check no_more_than_one: False end check no_more_than_one: False end
Result := Void l_uid := 0
end end
else else
check no_more_than_one: False end check no_more_than_one: False end
end end
sql_finalize sql_finalize
if l_uid > 0 and attached api as l_cms_api then
Result := l_cms_api.user_api.user_by_id (l_uid)
end
end end
feature --Access: Consumers feature --Access: Consumers
@@ -148,52 +156,11 @@ feature {NONE} -- Implementation OAuth Consumer
end end
end end
feature {NONE} -- Implementation: User
fetch_user: detachable CMS_USER
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_64 (1) as i then
l_id := i
end
if attached sql_read_string_32 (2) as s and then not s.is_whitespace then
l_name := s
end
if l_name /= Void then
create Result.make (l_name)
if l_id > 0 then
Result.set_id (l_id)
end
elseif l_id > 0 then
create Result.make_with_id (l_id)
end
if Result /= Void then
if attached sql_read_string (3) as l_password then
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
if attached sql_read_integer_32 (6) as l_status then
Result.set_status (l_status)
end
else
check expected_valid_user: False end
end
end
feature {NONE} -- User OpenID feature {NONE} -- User OpenID
Select_user_id_by_openid_identity: STRING = "SELECT uid FROM openid_items WHERE identity = :identity;"
Select_user_by_openid_identity: STRING = "SELECT u.* FROM users as u JOIN openid_items as og ON og.uid = u.uid and og.identity = :identity;" Select_user_id_openid_by_id: STRING = "SELECT uid FROM openid_items WHERE uid = :uid and identity = :identity;"
--| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped.
Select_user_openid_by_id: STRING = "SELECT u.* FROM users as u JOIN openid_items as og ON og.uid = u.uid and og.uid = :uid and og.identity = :identity;"
Sql_insert_openid: STRING = "INSERT INTO openid_items (uid, identity, created) VALUES (:uid, :identity, :utc_date);" Sql_insert_openid: STRING = "INSERT INTO openid_items (uid, identity, created) VALUES (:uid, :identity, :utc_date);"

14
modules/seo/seo-safe.ecf Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="seo" uuid="D53D622D-1F5A-4079-82B9-3E9A1605832B" library_target="seo">
<target name="seo">
<root all_classes="true"/>
<option is_obsolete_routine_type="false">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-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"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,154 @@
note
description: "[
Module that provides Search Engine Optimization.
]"
date: "$Date: 2016-01-08 22:43:12 +0100 (ven., 08 janv. 2016) $"
revision: "$Revision: 98369 $"
class
CMS_SEO_MODULE
inherit
CMS_MODULE
redefine
setup_hooks
end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_RESPONSE_ALTER
REFACTORING_HELPER
create
make
feature {NONE} -- Initialization
make
-- Create current module
do
version := "1.0"
description := "Search Engine Optimization"
package := "misc"
end
feature -- Access
name: STRING = "seo"
-- <Precursor>
feature -- Router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- Router configuration.
do
end
feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration.
do
auto_subscribe_to_hooks (a_hooks)
a_hooks.subscribe_to_response_alter_hook (Current)
end
feature -- Hook
response_alter (a_response: CMS_RESPONSE)
local
l_siteurl: STRING
l_title, l_desc, l_keywords: detachable READABLE_STRING_32
l_now: DATE_TIME
dt: detachable DATE_TIME
l_props: STRING_TABLE [READABLE_STRING_32]
api: CMS_API
setup: CMS_SETUP
do
api := a_response.api
setup := api.setup
l_siteurl := a_response.absolute_url (a_response.request.percent_encoded_path_info, Void)
create l_props.make_equal_caseless (5)
l_props.put (l_siteurl, "url")
l_props.put (api.setup.site_name, "site_name")
if attached api.setup.site_properties as tb then
across
tb as ic
loop
if ic.key.same_string ("headline") then
l_props.force (ic.item, "site_name")
else
l_props.put (ic.item, ic.key)
end
end
end
l_title := a_response.title
if l_title /= Void then
l_props.put (l_title, "title")
end
l_desc := a_response.description
if l_desc = Void then
l_desc := api.setup.site_description
else
l_props.put (l_desc, "description")
end
l_keywords := a_response.keywords
if l_keywords = Void then
l_keywords := api.setup.site_keywords
else
l_props.put (l_keywords, "keywords")
end
create l_now.make_now_utc
dt := a_response.publication_date
if dt = Void then
dt := l_now
end
l_props.put (date_to_yyyy_mm_dd_string (dt), "published_time")
dt := a_response.modification_date
if dt = Void then
dt := l_now
end
l_props.put (date_to_yyyy_mm_dd_string (dt), "modified_time")
a_response.add_additional_head_line ("<link rel=%"profile%" href=%"http://gmpg.org/xfn/11%" />", False)
if l_desc /= Void then
a_response.add_additional_head_line ("<meta name=%"description%" content=%"" + api.html_encoded (l_desc) + "%" />", False)
end
if l_keywords /= Void then
a_response.add_additional_head_line ("<meta name=%"keywords%" content=%"" + api.html_encoded (l_keywords) + "%" />", False)
end
-- Meta properties
a_response.add_additional_head_line ("<meta property=%"og:type%" content=%"article%" />", False)
across
l_props as ic
loop
a_response.add_additional_head_line ("<meta property=%"og:" + ic.key + "%" content=%"" + api.html_encoded (ic.item) + "%" />", False)
end
a_response.add_additional_head_line ("<link rel='canonical' href='" + l_siteurl + "' />", False)
a_response.add_additional_head_line ("<link rel='shortlink' href='" + l_siteurl + "' />", False)
end
feature -- Helpers: date
date_to_yyyy_mm_dd_string (dt: DATE_TIME): STRING
-- Date to YYYY-mm-dd format.
do
create Result.make (10)
Result.append_integer (dt.year)
Result.append_character ('-')
if dt.month < 10 then
Result.append_character ('0')
end
Result.append_integer (dt.month)
Result.append_character ('-')
if dt.day < 10 then
Result.append_character ('0')
end
Result.append_integer (dt.day)
end
end

View File

@@ -6,9 +6,8 @@ note
class class
CMS_SESSION_API CMS_SESSION_API
inherit inherit
CMS_MODULE_API CMS_AUTH_API_I
REFACTORING_HELPER REFACTORING_HELPER
@@ -19,9 +18,25 @@ feature {NONE} -- Initialization
make_with_storage (a_api: CMS_API; a_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I) make_with_storage (a_api: CMS_API; a_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I)
-- Create an object with api `a_api' and storage `a_session_auth_storage'. -- Create an object with api `a_api' and storage `a_session_auth_storage'.
local
s: detachable READABLE_STRING_8
do do
session_auth_storage := a_session_auth_storage session_auth_storage := a_session_auth_storage
make (a_api) make (a_api)
-- Initialize session related settings.
s := a_api.setup.string_8_item ("auth.session.token")
if s = Void then
s := a_api.setup.site_id + default_session_token_suffix
end
create session_token.make_from_string (s)
s := a_api.setup.string_8_item ("auth.session.max_age")
if s /= Void and then s.is_integer then
session_max_age := s.to_integer
else
session_max_age := 86400 --| one day: 24(h) *60(min) *60(sec)
end
ensure ensure
session_auth_storage_set: session_auth_storage = a_session_auth_storage session_auth_storage_set: session_auth_storage = a_session_auth_storage
end end
@@ -31,6 +46,17 @@ feature {CMS_MODULE} -- Access: User session storage.
session_auth_storage: CMS_SESSION_AUTH_STORAGE_I session_auth_storage: CMS_SESSION_AUTH_STORAGE_I
-- storage interface. -- storage interface.
feature -- Settings
default_session_token_suffix: STRING = "_SESSION_TOKEN_"
-- Default value for `session_token'.
session_token: IMMUTABLE_STRING_8
-- Token used for the session related cookies.
session_max_age: INTEGER
-- Value of the Max-Age, before the cookie expires.
feature -- Access feature -- Access
user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER user_by_session_token (a_token: READABLE_STRING_32): detachable CMS_USER

View File

@@ -10,30 +10,20 @@ class
CMS_SESSION_AUTH_MODULE CMS_SESSION_AUTH_MODULE
inherit inherit
CMS_MODULE CMS_AUTH_MODULE_I
rename rename
module_api as user_session_api module_api as session_api
redefine redefine
make,
filters, filters,
setup_hooks, setup_hooks,
initialize, initialize,
install, install,
user_session_api session_api
end end
CMS_HOOK_AUTO_REGISTER
CMS_HOOK_BLOCK CMS_HOOK_BLOCK
CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_VALUE_TABLE_ALTER
SHARED_LOGGER
CMS_REQUEST_UTIL
create create
make make
@@ -41,41 +31,38 @@ feature {NONE} -- Initialization
make make
do do
Precursor
version := "1.0" version := "1.0"
description := "Service to manage cookie based authentication" description := "Service to manage cookie based authentication"
package := "authentication"
add_dependency ({CMS_AUTHENTICATION_MODULE})
end end
feature -- Access feature -- Access
name: STRING = "session_auth" name: STRING = "session_auth"
feature {CMS_API} -- Module Initialization feature {CMS_API} -- Module Initialization
initialize (a_api: CMS_API) initialize (a_api: CMS_API)
-- <Precursor> -- <Precursor>
local local
l_session_auth_api: like user_session_api l_session_auth_api: like session_api
l_user_auth_storage: CMS_SESSION_AUTH_STORAGE_I l_session_auth_storage: CMS_SESSION_AUTH_STORAGE_I
do do
Precursor (a_api) Precursor (a_api)
-- Storage initialization -- Storage initialization
if attached a_api.storage.as_sql_storage as l_storage_sql then if attached a_api.storage.as_sql_storage as l_storage_sql then
create {CMS_SESSION_AUTH_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) create {CMS_SESSION_AUTH_STORAGE_SQL} l_session_auth_storage.make (l_storage_sql)
else else
-- FIXME: in case of NULL storage, should Current be disabled? -- FIXME: in case of NULL storage, should Current be disabled?
create {CMS_SESSION_AUTH_STORAGE_NULL} l_user_auth_storage create {CMS_SESSION_AUTH_STORAGE_NULL} l_session_auth_storage
end end
-- API initialization -- API initialization
create l_session_auth_api.make_with_storage (a_api, l_user_auth_storage) create l_session_auth_api.make_with_storage (a_api, l_session_auth_storage)
user_session_api := l_session_auth_api session_api := l_session_auth_api
ensure then ensure then
session_auth_api_set: user_session_api /= Void session_auth_api_set: session_api /= Void
end end
feature {CMS_API} -- Module management feature {CMS_API} -- Module management
@@ -84,32 +71,52 @@ feature {CMS_API} -- Module management
do do
-- Schema -- Schema
if attached api.storage.as_sql_storage as l_sql_storage then if attached api.storage.as_sql_storage as l_sql_storage then
if not l_sql_storage.sql_table_exists ("session_auth") then l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install.sql")), Void)
--| Schema
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("session_auth_table.sql")), Void)
if l_sql_storage.has_error then if l_sql_storage.has_error then
api.logger.put_error ("Could not initialize database for session auth module", generating_type) api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type)
end else
Precursor {CMS_AUTH_MODULE_I} (api) -- Mark it as installed.
end end
l_sql_storage.sql_finalize
Precursor {CMS_MODULE}(api)
end end
end end
feature {CMS_API} -- Access: API feature {CMS_API} -- Access: API
user_session_api: detachable CMS_SESSION_API session_api: detachable CMS_SESSION_API
-- <Precursor> -- <Precursor>
feature -- Access: auth strategy
login_title: STRING = "Session"
-- Module specific login title.
login_location: STRING = "account/auth/roc-session-login"
logout_location: STRING = "account/auth/roc-session-logout"
is_authenticating (a_response: CMS_RESPONSE): BOOLEAN
-- <Precursor>
do
if
a_response.is_authenticated and then
attached session_api as l_session_api and then
attached a_response.request.cookie (l_session_api.session_token)
then
Result := True
end
end
feature -- Access: router feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor> -- <Precursor>
do do
a_router.handle ("/account/roc-session-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), a_router.methods_head_get) if attached session_api as l_session_api then
a_router.handle ("/account/roc-session-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/" + login_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/account/login-with-session", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,user_session_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/" + logout_location, create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_session_api, ?, ?)), a_router.methods_get_post)
a_router.handle ("/account/auth/roc-session-login", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,session_api, ?, ?)), a_router.methods_get_post)
end
end end
feature -- Access: filter feature -- Access: filter
@@ -117,8 +124,8 @@ feature -- Access: filter
filters (a_api: CMS_API): detachable LIST [WSF_FILTER] filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
-- Possibly list of Filter's module. -- Possibly list of Filter's module.
do do
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached session_api as l_session_api then
if attached user_session_api as l_session_api then create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
Result.extend (create {CMS_SESSION_AUTH_FILTER}.make (a_api, l_session_api)) Result.extend (create {CMS_SESSION_AUTH_FILTER}.make (a_api, l_session_api))
end end
end end
@@ -127,27 +134,49 @@ feature {NONE} -- Implementation: routes
handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
vals: CMS_VALUE_TABLE
r: CMS_RESPONSE r: CMS_RESPONSE
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if api.user_is_authenticated then
r.add_error_message ("You are already signed in!")
else
if attached template_block ("login", r) as l_tpl_block then
create vals.make (1)
-- add the variable to the block
l_tpl_block.set_value (api.user, "user")
l_tpl_block.set_value (r.url ("", Void), "site_url")
api.hooks.invoke_value_table_alter (vals, r)
across
vals as ic
loop
l_tpl_block.set_value (ic.item, ic.key)
end
r.add_block (l_tpl_block, "content")
else
r.add_error_message ("Error: missing block [login]")
end
end
r.execute r.execute
end end
handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_logout (api: CMS_API; a_session_api: CMS_SESSION_API ; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_cookie: WSF_COOKIE l_cookie: WSF_COOKIE
tok: STRING
do do
tok := a_session_api.session_token
if if
attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_cookie_token and then attached {WSF_STRING} req.cookie (tok) as l_cookie_token and then
attached {CMS_USER} current_user (req) as l_user attached api.user as l_user
then then
-- Logout Session -- Logout Session
create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_cookie_token.value) create l_cookie.make (tok, l_cookie_token.value) -- FIXME: unicode issue?
l_cookie.set_path ("/") l_cookie.set_path ("/")
l_cookie.set_max_age (-1) l_cookie.set_max_age (-1)
res.add_cookie (l_cookie) res.add_cookie (l_cookie)
unset_current_user (req) api.unset_user
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_status_code ({HTTP_CONSTANTS}.found) r.set_status_code ({HTTP_CONSTANTS}.found)
@@ -167,25 +196,39 @@ feature {NONE} -- Implementation: routes
if if
attached a_session_api as l_session_api and then attached a_session_api as l_session_api and then
attached {WSF_STRING} req.form_parameter ("username") as l_username and then attached {WSF_STRING} req.form_parameter ("username") as l_username and then
attached {WSF_STRING} req.form_parameter ("password") as l_password and then attached {WSF_STRING} req.form_parameter ("password") as l_password
api.user_api.is_valid_credential (l_username.value, l_password.value) and then
attached api.user_api.user_by_name (l_username.value) as l_user
then then
l_token := generate_token
if if
a_session_api.has_user_token (l_user) api.user_api.is_valid_credential (l_username.value, l_password.value) and then
attached api.user_api.user_by_name (l_username.value) as l_user
then then
l_session_api.update_user_session_auth (l_token, l_user) l_token := generate_token
if a_session_api.has_user_token (l_user) then
l_session_api.update_user_session_auth (l_token, l_user)
else
l_session_api.new_user_session_auth (l_token, l_user)
end
create l_cookie.make (a_session_api.session_token, l_token)
l_cookie.set_max_age (a_session_api.session_max_age)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
api.set_user (l_user)
api.record_user_login (l_user)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached {WSF_STRING} req.query_parameter ("destination") as p_destination then
r.set_redirection (p_destination.url_encoded_value)
else
r.set_redirection ("")
end
else else
l_session_api.new_user_session_auth (l_token, l_user) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if attached template_block ("login", r) as l_tpl_block then
l_tpl_block.set_value (l_username.value, "username")
l_tpl_block.set_value ("Wrong: Username or password ", "error")
r.add_block (l_tpl_block, "content")
end
end end
create l_cookie.make ({CMS_SESSION_CONSTANTS}.session_auth_token, l_token)
l_cookie.set_max_age ({CMS_SESSION_CONSTANTS}.session_max_age)
l_cookie.set_path ("/")
res.add_cookie (l_cookie)
set_current_user (req, l_user)
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
r.set_redirection (req.absolute_script_url (""))
r.execute r.execute
else else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
@@ -205,105 +248,24 @@ feature -- Hooks configuration
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Module hooks configuration. -- Module hooks configuration.
do do
auto_subscribe_to_hooks (a_hooks) Precursor (a_hooks)
a_hooks.subscribe_to_block_hook (Current) a_hooks.subscribe_to_block_hook (Current)
a_hooks.subscribe_to_value_table_alter_hook (Current)
end end
feature -- Hooks feature -- Hooks
value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE)
-- <Precursor>
do
if
attached a_response.user as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token)
then
a_value.force ("account/roc-session-logout", "auth_login_strategy")
end
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
local
lnk: CMS_LOCAL_LINK
lnk2: detachable CMS_LINK
do
if
attached a_response.user as u and then
attached {WSF_STRING} a_response.request.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token)
then
across
a_menu_system.primary_menu.items as ic
until
lnk2 /= Void
loop
if ic.item.location.same_string ("account/roc-logout") or else ic.item.location.same_string ("basic_auth_logoff") then
lnk2 := ic.item
end
end
if lnk2 /= Void then
a_menu_system.primary_menu.remove (lnk2)
end
create lnk.make ("Logout", "account/roc-session-logout" )
a_menu_system.primary_menu.extend (lnk)
else
if a_response.location.starts_with ("account/") then
create lnk.make ("Session", "account/roc-session-login")
a_response.add_to_primary_tabs (lnk)
end
end
end
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
local
l_string: STRING
do do
Result := <<"login">> 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 end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if if a_block_id.is_case_insensitive_equal_general ("login") then
a_block_id.is_case_insensitive_equal_general ("login") and then
a_response.location.starts_with ("account/roc-session-login")
then
get_block_view_login (a_block_id, a_response) get_block_view_login (a_block_id, a_response)
end end
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 feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -313,7 +275,7 @@ feature {NONE} -- Block views
if attached template_block (a_block_id, a_response) as l_tpl_block then if attached template_block (a_block_id, a_response) as l_tpl_block then
create vals.make (1) create vals.make (1)
-- add the variable to the block -- add the variable to the block
value_table_alter (vals, a_response) a_response.api.hooks.invoke_value_table_alter (vals, a_response)
across across
vals as ic vals as ic
loop loop
@@ -327,7 +289,6 @@ feature {NONE} -- Block views
end end
end end
generate_token: STRING generate_token: STRING
-- Generate token to use in a Session. -- Generate token to use in a Session.
local local
@@ -346,4 +307,5 @@ feature {NONE} -- Block views
end end
Result := l_token Result := l_token
end end
end end

View File

@@ -1,19 +0,0 @@
note
description: "Summary description for {CMS_SESSION_CONSTANTS}."
date: "$Date$"
revision: "$Revision$"
class
CMS_SESSION_CONSTANTS
feature
session_auth_token: STRING = "EWF_ROC_SESSION_AUTH_TOKEN_"
-- Name of Cookie used to keep the session info.
-- TODO add a config file to be able to customize this value via coniguration file.
session_max_age: INTEGER = 86400
-- Value of the Max-Age, before the cookie expires.
-- TODO add a config file to be able to customize this value via coniguration file.
end

View File

@@ -37,7 +37,7 @@ feature -- Access
password_hash (a_password, a_salt: STRING): STRING password_hash (a_password, a_salt: STRING): STRING
-- Password hash based on password `a_password' and salt value `a_salt'. -- Password hash based on password `a_password' and salt value `a_salt'.
do do
Result := sha1_string (a_password + a_salt ) Result := sha1_string (a_password + a_salt)
end end
feature {NONE} -- Implementation feature {NONE} -- Implementation
@@ -77,7 +77,6 @@ feature {NONE} -- Implementation
feature -- Encoding feature -- Encoding
base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8
-- Encodes a byte array into a STRING doing base64 encoding. -- Encodes a byte array into a STRING doing base64 encoding.
local local

View File

@@ -9,45 +9,42 @@ class
CMS_SESSION_AUTH_FILTER CMS_SESSION_AUTH_FILTER
inherit inherit
WSF_URI_TEMPLATE_HANDLER CMS_AUTH_FILTER_I
CMS_HANDLER
rename rename
make as make_handler make as make_filter
end end
WSF_FILTER
create create
make make
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_api: CMS_API; a_session_oauth_api: CMS_SESSION_API) make (a_api: CMS_API; a_session_api: CMS_SESSION_API)
do do
make_handler (a_api) make_filter (a_api)
session_oauth_api := a_session_oauth_api session_api := a_session_api
end end
session_oauth_api: CMS_SESSION_API session_api: CMS_SESSION_API
feature -- Basic operations feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE) auth_strategy: STRING
-- Execute the filter. do
Result := {CMS_SESSION_AUTH_MODULE}.logout_location
end
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>.
do do
api.logger.put_debug (generator + ".execute ", Void)
-- A valid user
if if
attached {WSF_STRING} req.cookie ({CMS_SESSION_CONSTANTS}.session_auth_token) as l_roc_auth_session_token attached {WSF_STRING} req.cookie (session_api.session_token) as l_roc_auth_session_token
then then
if attached session_oauth_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then if attached session_api.user_by_session_token (l_roc_auth_session_token.value) as l_user then
set_current_user (req, l_user) set_current_user (l_user)
else else
api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void)
end end
else
api.logger.put_debug (generator + ".execute without authentication", Void)
end end
execute_next (req, res) execute_next (req, res)
end end

View File

@@ -7,7 +7,6 @@ class
CMS_SESSION_AUTH_STORAGE_SQL CMS_SESSION_AUTH_STORAGE_SQL
inherit inherit
CMS_SESSION_AUTH_STORAGE_I CMS_SESSION_AUTH_STORAGE_I
CMS_PROXY_STORAGE_SQL CMS_PROXY_STORAGE_SQL
@@ -27,23 +26,27 @@ feature -- Access User
-- Retrieve user by token `a_token', if any. -- Retrieve user by token `a_token', if any.
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
l_uid: INTEGER_64
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".user_by_session_token") write_information_log (generator + ".user_by_session_token")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_token, "token") l_parameters.put (a_token, "token")
sql_query (Select_user_by_token, l_parameters) sql_query (Select_user_id_by_token, l_parameters)
if not has_error and not sql_after then if not has_error and not sql_after then
Result := fetch_user l_uid := sql_read_integer_64 (1)
sql_forth sql_forth
if not sql_after then if not sql_after then
check check
no_more_than_one: False no_more_than_one: False
end end
Result := Void l_uid := 0
end end
end end
sql_finalize sql_finalize
if l_uid > 0 and attached api as l_cms_api then
Result := l_cms_api.user_api.user_by_id (l_uid)
end
end end
has_user_token (a_user: CMS_USER): BOOLEAN has_user_token (a_user: CMS_USER): BOOLEAN
@@ -68,7 +71,7 @@ feature -- Access User
feature -- Change User token feature -- Change User token
new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER;) new_user_session_auth (a_token: READABLE_STRING_GENERAL; a_user: CMS_USER)
-- <Precursor>. -- <Precursor>.
local local
l_parameters: STRING_TABLE [detachable ANY] l_parameters: STRING_TABLE [detachable ANY]
@@ -102,54 +105,14 @@ feature -- Change User token
sql_finalize sql_finalize
end end
feature {NONE} -- Implementation
fetch_user: detachable CMS_USER
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
do
if attached sql_read_integer_64 (1) as i then
l_id := i
end
if attached sql_read_string_32 (2) as s and then not s.is_whitespace then
l_name := s
end
if l_name /= Void then
create Result.make (l_name)
if l_id > 0 then
Result.set_id (l_id)
end
elseif l_id > 0 then
create Result.make_with_id (l_id)
end
if Result /= Void then
if attached sql_read_string (3) as l_password then
-- FIXME: should we return the password here ???
Result.set_hashed_password (l_password)
end
if attached sql_read_string (5) as l_email then
Result.set_email (l_email)
end
if attached sql_read_integer_32 (6) as l_status then
Result.set_status (l_status)
end
else
check
expected_valid_user: False
end
end
end
feature {NONE} -- SQL statements feature {NONE} -- SQL statements
Select_user_by_token: STRING = "SELECT u.* FROM users as u JOIN session_auth as og ON og.uid = u.uid and og.access_token = :token;" Select_user_id_by_token: STRING = "SELECT uid FROM auth_session WHERE access_token = :token;"
--| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped.
Sql_insert_session_auth: STRING = "INSERT INTO session_auth (uid, access_token, created) VALUES (:uid, :token, :utc_date);" sql_insert_session_auth: STRING = "INSERT INTO auth_session (uid, access_token, created) VALUES (:uid, :token, :utc_date);"
Sql_update_session_auth: STRING = "UPDATE session_auth SET access_token = :token, created = :utc_date WHERE uid =:uid;" sql_update_session_auth: STRING = "UPDATE auth_session SET access_token = :token, created = :utc_date WHERE uid =:uid;"
Select_user_token: STRING = "SELECT COUNT(*) FROM session_auth where uid = :uid;" select_user_token: STRING = "SELECT COUNT(*) FROM auth_session where uid = :uid;"
end end

View File

@@ -0,0 +1,9 @@
CREATE TABLE auth_session (
`uid` INTEGER PRIMARY KEY NOT NULL CHECK(`uid`>=0),
`access_token` VARCHAR(64) NOT NULL,
`created` DATETIME NOT NULL,
CONSTRAINT `uid` UNIQUE(`uid`),
CONSTRAINT `access_token` UNIQUE(`access_token`)
);

View File

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

View File

@@ -1,37 +1,23 @@
<div class="primary-tabs"> {unless isset="$user"}
{unless isset="$user"} <div class="login-box">
<h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3> <div class="description">The "Session" is the standard authentication system. (based on cookie)</div>
<div> <h3>Login or <a href="{$site_url/}account/roc-register">Register</a></h3>
<div> <div>
<form name="cms_session_auth" action="{$site_url/}account/login-with-session" method="POST"> <form name="cms_session_auth" action="{$site_url/}account/auth/roc-session-login" method="POST">
<div>
<input type="text" name="username" id="username" required value="{$username/}">
<label>Username</label>
</div>
<div>
<input type="password" name="password" id="password" required >
<label>Password</label>
</div>
<button type="submit">Login</button>
</form>
</div>
</div>
<div>
<div> <div>
<p> <input type="text" name="username" id="username" required value="{$username/}">
<a href="{$site_url/}account/new-password">Forgot password?</a> <label>Username</label>
</p>
</div> </div>
</div>
{/unless}
{if isset=$error}
<div> <div>
<div> <input type="password" name="password" id="password" required >
<p> <label>Password</label>
<strong>{$error/}
</p>
</div> </div>
</div> <button type="submit">Login</button>
{/if} </form>
</div> </div>
<div>
<a href="{$site_url/}account/new-password">Forgot password?</a>
</div>
{if isset="$error"}<div class="error">{$error/}</div>{/if}
</div>
{/unless}

View File

@@ -70,18 +70,20 @@ feature {CMS_API} -- Module management
do do
-- Schema -- Schema
if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then
l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install").appended_with_extension ("sql")), Void) l_sql_storage.sql_execute_file_script (api.module_resource_location (Current, (create {PATH}.make_from_string ("scripts")).extended ("install.sql")), Void)
if l_sql_storage.has_error then if l_sql_storage.has_error then
api.logger.put_error ("Could not install database for taxonomy module", generating_type) api.logger.put_error ("Could not install database for taxonomy module", generating_type)
end else
Precursor (api) Precursor (api)
create l_taxonomy_api.make (api) -- Populate
create voc.make ("Tags") create l_taxonomy_api.make (api)
voc.set_description ("Enter comma separated tags.") create voc.make ("Tags")
l_taxonomy_api.save_vocabulary (voc) voc.set_description ("Enter comma separated tags.")
voc.set_is_tags (True) l_taxonomy_api.save_vocabulary (voc)
l_taxonomy_api.associate_vocabulary_with_type (voc, "page") voc.set_is_tags (True)
l_taxonomy_api.associate_vocabulary_with_type (voc, "page")
end
end end
end end

Some files were not shown because too many files have changed in this diff Show More