From 3496536751c23820a89b85f8aa38f6a94f754854 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 29 Jan 2016 21:58:49 +0100 Subject: [PATCH] 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. --- .../auth/templates/block_account_edit.tpl | 1 + .../auth/templates/block_account_info.tpl | 74 +-- .../auth/templates/block_change_password.tpl | 2 +- .../modules/auth/templates/block_login.tpl | 29 -- .../basic_auth/files/js/roc_basic_auth.js | 482 +++++++++--------- .../basic_auth/templates/block_login.tpl | 42 +- .../session_auth/templates/block_login.tpl | 50 +- library/model/src/link/cms_link.e | 76 ++- library/model/src/user/cms_user.e | 24 - library/model/src/user/cms_user_profile.e | 56 -- modules/admin/handler/role/cms_role_handler.e | 2 +- modules/admin/handler/user/cms_user_handler.e | 2 +- .../handler/user/cms_user_view_response.e | 8 +- modules/auth/cms_auth_api_i.e | 17 + modules/auth/cms_auth_filter_i.e | 44 ++ modules/auth/cms_auth_module_i.e | 108 ++++ modules/auth/cms_authentication_module.e | 293 ++++++++--- .../site/templates/block_account_edit.tpl | 1 + .../site/templates/block_account_info.tpl | 74 +-- .../site/templates/block_change_password.tpl | 2 +- modules/auth/site/templates/block_login.tpl | 29 -- modules/basic_auth/cms_basic_auth_module.e | 171 +++---- .../basic_auth/filter/cms_basic_auth_filter.e | 50 +- .../handler/cms_basic_auth_login_handler.e | 4 +- .../handler/cms_basic_auth_logoff_handler.e | 4 +- .../site/files/js/roc_basic_auth.js | 482 +++++++++--------- .../basic_auth/site/templates/block_login.tpl | 42 +- modules/contact/src/cms_contact_module.e | 17 +- modules/node/cms_node_api.e | 4 +- modules/node/handler/node_handler.e | 20 +- modules/node/handler/node_view_response.e | 3 +- modules/node/persistence/cms_node_storage_i.e | 7 - .../node/persistence/cms_node_storage_null.e | 11 - .../node/persistence/cms_node_storage_sql.e | 41 -- modules/oauth20/cms_oauth_20_api.e | 5 +- modules/oauth20/cms_oauth_20_module.e | 158 ++---- modules/oauth20/filter/cms_oauth_20_filter.e | 25 +- .../persistence/cms_oauth_20_storage_i.e | 2 +- .../persistence/cms_oauth_20_storage_sql.e | 86 ++-- modules/openid/cms_openid_api.e | 7 +- modules/openid/cms_openid_module.e | 159 ++---- modules/openid/filter/cms_openid_filter.e | 22 +- .../persitence/cms_openid_storage_sql.e | 65 +-- modules/session_auth/cms_session_api.e | 2 +- .../session_auth/cms_session_auth_module.e | 209 +++----- .../filter/cms_session_auth_filter.e | 22 +- .../cms_session_auth_storage_sql.e | 2 +- .../site/templates/block_login.tpl | 50 +- .../taxonomy_vocabulary_admin_handler.e | 4 +- src/kernel/content/cms_alias_block.e | 11 +- src/kernel/content/cms_block.e | 8 +- src/kernel/content/cms_cache_block.e | 2 +- src/kernel/content/cms_encoders.e | 13 + .../content/cms_smarty_template_block.e | 2 +- src/persistence/user/cms_user_storage_i.e | 25 - src/persistence/user/cms_user_storage_null.e | 1 - src/persistence/user/cms_user_storage_sql_i.e | 10 +- src/service/cms_api.e | 130 ++++- src/service/cms_execution.e | 4 +- src/service/cms_module.e | 4 +- src/service/handler/cms_handler.e | 7 +- src/service/misc/cms_request_util.e | 77 --- src/service/misc/cms_url_utilities.e | 12 +- src/service/response/cms_response.e | 82 ++- .../error/not_found_error_cms_response.e | 4 +- src/service/user/cms_user_api.e | 79 ++- tpl/site/scripts/user.sql | 1 - 67 files changed, 1742 insertions(+), 1820 deletions(-) create mode 100644 examples/demo/site/modules/auth/templates/block_account_edit.tpl delete mode 100644 examples/demo/site/modules/auth/templates/block_login.tpl delete mode 100644 library/model/src/user/cms_user_profile.e create mode 100644 modules/auth/cms_auth_api_i.e create mode 100644 modules/auth/cms_auth_filter_i.e create mode 100644 modules/auth/cms_auth_module_i.e create mode 100644 modules/auth/site/templates/block_account_edit.tpl delete mode 100644 modules/auth/site/templates/block_login.tpl delete mode 100644 src/service/misc/cms_request_util.e diff --git a/examples/demo/site/modules/auth/templates/block_account_edit.tpl b/examples/demo/site/modules/auth/templates/block_account_edit.tpl new file mode 100644 index 0000000..4f4b0f5 --- /dev/null +++ b/examples/demo/site/modules/auth/templates/block_account_edit.tpl @@ -0,0 +1 @@ +{include file="block_account_info.tpl" /} diff --git a/examples/demo/site/modules/auth/templates/block_account_info.tpl b/examples/demo/site/modules/auth/templates/block_account_info.tpl index 0d206bb..4f96ae8 100644 --- a/examples/demo/site/modules/auth/templates/block_account_info.tpl +++ b/examples/demo/site/modules/auth/templates/block_account_info.tpl @@ -1,62 +1,34 @@
{if isset="$user"}

Account Information

-
-
-
- {$user.name/} -
-
- {$user.email/} -
-
- {$user.creation_date/} -
-
- {$user.last_login_date/} -
-
-
- -
-
+
    +
    + {$user.name/}
    -
-
- {include file="block_change_password.tpl" /} -
-

Roles

-
- {foreach item="ic" from="$roles"} -
-
    -
  • - {$ic.name/} -
      -
    • permissions -
        - {foreach item="ip" from="$ic.permissions"} -
      • {$ip/}
      • - {/foreach} -
      -
    • -
    -
  • -
-
- {/foreach} -
- - +
+ {$user.email/} +
+
+ {$user.creation_date/} (UTC) +
+
+ {$user.last_login_date/} (UTC) +
+
+
+ +
+
+

Profile

-
+
+ {/if} {unless isset="$user"}
diff --git a/examples/demo/site/modules/auth/templates/block_change_password.tpl b/examples/demo/site/modules/auth/templates/block_change_password.tpl index 047508c..6822433 100644 --- a/examples/demo/site/modules/auth/templates/block_change_password.tpl +++ b/examples/demo/site/modules/auth/templates/block_change_password.tpl @@ -1,7 +1,7 @@
- Change Password Form + Change Password
diff --git a/examples/demo/site/modules/auth/templates/block_login.tpl b/examples/demo/site/modules/auth/templates/block_login.tpl deleted file mode 100644 index fd24464..0000000 --- a/examples/demo/site/modules/auth/templates/block_login.tpl +++ /dev/null @@ -1,29 +0,0 @@ -
- {unless isset="$user"} -

Login or Register

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

Login or Register

+{unless isset="$user"} + +{/unless} diff --git a/examples/demo/site/modules/session_auth/templates/block_login.tpl b/examples/demo/site/modules/session_auth/templates/block_login.tpl index a344568..4243ff8 100644 --- a/examples/demo/site/modules/session_auth/templates/block_login.tpl +++ b/examples/demo/site/modules/session_auth/templates/block_login.tpl @@ -1,37 +1,23 @@ -
- {unless isset="$user"} -

Login or Register

-
-
-
-
- - -
- -
- - -
- -
-
-
-
+{unless isset="$user"} + + + {if isset="$error"}
{$error/}
{/if} +
+{/unless} diff --git a/library/model/src/link/cms_link.e b/library/model/src/link/cms_link.e index 4f981a8..10a4361 100644 --- a/library/model/src/link/cms_link.e +++ b/library/model/src/link/cms_link.e @@ -37,6 +37,80 @@ feature -- Access weight: INTEGER -- 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 is_less alias "<" (other: like Current): BOOLEAN @@ -134,6 +208,6 @@ feature -- Status report end 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)" end diff --git a/library/model/src/user/cms_user.e b/library/model/src/user/cms_user.e index 761abe0..64dd443 100644 --- a/library/model/src/user/cms_user.e +++ b/library/model/src/user/cms_user.e @@ -65,9 +65,6 @@ feature -- Access email: detachable READABLE_STRING_8 -- User email. - profile: detachable CMS_USER_PROFILE - -- User profile. - creation_date: DATE_TIME -- Creation date. @@ -189,26 +186,6 @@ feature -- Change element email_set: email = a_email 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) do last_login_date := dt @@ -284,7 +261,6 @@ feature -- Status change status_set: status = a_status end - feature -- User status not_active: INTEGER = 0 diff --git a/library/model/src/user/cms_user_profile.e b/library/model/src/user/cms_user_profile.e deleted file mode 100644 index 1f0a407..0000000 --- a/library/model/src/user/cms_user_profile.e +++ /dev/null @@ -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 diff --git a/modules/admin/handler/role/cms_role_handler.e b/modules/admin/handler/role/cms_role_handler.e index 0b9a852..e145643 100644 --- a/modules/admin/handler/role/cms_role_handler.e +++ b/modules/admin/handler/role/cms_role_handler.e @@ -166,7 +166,7 @@ feature -- Error do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) -- 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 l_id.is_integer and then diff --git a/modules/admin/handler/user/cms_user_handler.e b/modules/admin/handler/user/cms_user_handler.e index 4bfd764..28656ff 100644 --- a/modules/admin/handler/user/cms_user_handler.e +++ b/modules/admin/handler/user/cms_user_handler.e @@ -166,7 +166,7 @@ feature -- Error do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) -- 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 l_id.is_integer and then diff --git a/modules/admin/handler/user/cms_user_view_response.e b/modules/admin/handler/user/cms_user_view_response.e index 930cf20..64883b8 100644 --- a/modules/admin/handler/user/cms_user_view_response.e +++ b/modules/admin/handler/user/cms_user_view_response.e @@ -86,12 +86,13 @@ feature -- Execution not l_roles.is_empty then s.append ("

Role(s):

") + s.append ("
    ") across l_roles as ic loop l_role := ic.item - s.append ("") + s.append ("
  • ") s.append (link (l_role.name, "admin/role/" + l_role.id.out, Void)) - s.append ("") - debug + s.append ("
  • ") + if request.query_parameter ("debug") /= Void then s.append ("
    Permissions:
    ") s.append ("
      %N") across l_role.permissions as perms_ic loop @@ -100,6 +101,7 @@ feature -- Execution s.append ("
    %N") end end + s.append ("
%N") end s.append ("
") diff --git a/modules/auth/cms_auth_api_i.e b/modules/auth/cms_auth_api_i.e new file mode 100644 index 0000000..c500584 --- /dev/null +++ b/modules/auth/cms_auth_api_i.e @@ -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 diff --git a/modules/auth/cms_auth_filter_i.e b/modules/auth/cms_auth_filter_i.e new file mode 100644 index 0000000..21f566e --- /dev/null +++ b/modules/auth/cms_auth_filter_i.e @@ -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) + -- + 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 diff --git a/modules/auth/cms_auth_module_i.e b/modules/auth/cms_auth_module_i.e new file mode 100644 index 0000000..8ca01ba --- /dev/null +++ b/modules/auth/cms_auth_module_i.e @@ -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) + -- + 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.url_encoded_value + else + l_destination := percent_encoded (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 diff --git a/modules/auth/cms_authentication_module.e b/modules/auth/cms_authentication_module.e index 3bb64a1..41b1591 100644 --- a/modules/auth/cms_authentication_module.e +++ b/modules/auth/cms_authentication_module.e @@ -7,7 +7,6 @@ class CMS_AUTHENTICATION_MODULE inherit - CMS_MODULE redefine setup_hooks, @@ -33,8 +32,6 @@ inherit SHARED_LOGGER - CMS_REQUEST_UTIL - create make @@ -93,9 +90,13 @@ feature -- Router local m: WSF_URI_MAPPING do - create m.make_trailing_slash_ignored ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account(a_api, ?, ?))) + create m.make_trailing_slash_ignored ("/account", create {WSF_URI_AGENT_HANDLER}.make (agent handle_account (a_api, ?, ?))) a_router.map (m, 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 ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login(a_api, ?, ?)), 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) a_router.handle ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register(a_api, ?, ?)), a_router.methods_get_post) @@ -104,8 +105,7 @@ feature -- Router a_router.handle ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password(a_api, ?, ?)), a_router.methods_get_post) a_router.handle ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password(a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/change-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_change_password(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) + a_router.handle ("/account/change/{field}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_change_field (a_api, ?, ?)), a_router.methods_get_post) end @@ -142,14 +142,22 @@ feature -- Hooks configuration create lnk.make (u.name, "account") lnk.set_weight (97) a_menu_system.primary_menu.extend (lnk) + create lnk.make ("Logout", "account/roc-logout") - lnk.set_weight (98) - a_menu_system.primary_menu.extend (lnk) else create lnk.make ("Login", "account/roc-login") - lnk.set_weight (98) - a_menu_system.primary_menu.extend (lnk) end + lnk.set_weight (98) + if + a_response.location.starts_with_general ("account/auth/") + or a_response.location.starts_with_general ("account/roc-log") + 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 if a_response.has_permission ("admin registration") then create lnk.make ("Registration", "admin/pending-registrations/") @@ -162,18 +170,78 @@ feature -- Handler handle_account (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE + l_user: detachable CMS_USER + b: STRING + 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_info", r) as l_tpl_block then - if attached r.user as l_user then - r.set_value (api.user_api.user_roles (l_user), "roles") - end + 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 + + 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 ("account/roc-login") + 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 end @@ -181,10 +249,30 @@ feature -- Handler local r: CMS_RESPONSE 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. 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 else create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) @@ -195,9 +283,19 @@ feature -- Handler handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE + loc: STRING do 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 end @@ -232,7 +330,7 @@ feature -- Handler l_exist := True end if attached l_user_api.user_by_email (l_email) or else attached l_user_api.temp_user_by_email (l_email) then - -- Emails already exist. + -- Email already exists. r.set_value ("An account is already associated with that email address!", "error_email") l_exist := True end @@ -524,45 +622,91 @@ feature -- Handler r.execute 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 r: CMS_RESPONSE l_user_api: CMS_USER_API + f: CMS_FORM + l_fieldname: detachable READABLE_STRING_8 + b: STRING + lnk: CMS_LOCAL_LINK do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - l_user_api := api.user_api - if req.is_post_request_method then - if attached r.user as l_user then - r.set_value (api.user_api.user_roles (l_user), "roles") - 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 - -- Does the passwords match? - l_user.set_password (l_password.value) - l_user_api.update_user (l_user) - r.set_redirection (req.absolute_script_url ("/account/post-change-password")) - else - if attached template_block ("account_info", r) as l_tpl_block then - -- r.set_value (l_user, "user") - r.set_value ("Passwords Don't Match", "error_password") - r.set_status_code ({HTTP_CONSTANTS}.bad_request) - r.add_block (l_tpl_block, "content") + if attached {WSF_STRING} req.path_parameter ("field") as p_field then + l_fieldname := p_field.url_encoded_value + end + if l_fieldname = Void then + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + else + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + + 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 + + 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 + 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 r.execute 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) local l_response: CMS_RESPONSE @@ -655,20 +799,8 @@ feature -- Handler end block_list: ITERABLE [like {CMS_BLOCK}.name] - local - l_string: STRING do 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 get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) @@ -689,6 +821,53 @@ feature -- Handler 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 ("") + 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 ("") + end + feature {NONE} -- Token Generation new_token: STRING diff --git a/modules/auth/site/templates/block_account_edit.tpl b/modules/auth/site/templates/block_account_edit.tpl new file mode 100644 index 0000000..4f4b0f5 --- /dev/null +++ b/modules/auth/site/templates/block_account_edit.tpl @@ -0,0 +1 @@ +{include file="block_account_info.tpl" /} diff --git a/modules/auth/site/templates/block_account_info.tpl b/modules/auth/site/templates/block_account_info.tpl index 0d206bb..4f96ae8 100644 --- a/modules/auth/site/templates/block_account_info.tpl +++ b/modules/auth/site/templates/block_account_info.tpl @@ -1,62 +1,34 @@
{if isset="$user"}

Account Information

-
-
-
- {$user.name/} -
-
- {$user.email/} -
-
- {$user.creation_date/} -
-
- {$user.last_login_date/} -
-
-
- -
-
+
    +
    + {$user.name/}
    -
-
- {include file="block_change_password.tpl" /} -
-

Roles

-
- {foreach item="ic" from="$roles"} -
-
    -
  • - {$ic.name/} -
      -
    • permissions -
        - {foreach item="ip" from="$ic.permissions"} -
      • {$ip/}
      • - {/foreach} -
      -
    • -
    -
  • -
-
- {/foreach} -
- - +
+ {$user.email/} +
+
+ {$user.creation_date/} (UTC) +
+
+ {$user.last_login_date/} (UTC) +
+
+
+ +
+
+

Profile

-
+
+ {/if} {unless isset="$user"}
diff --git a/modules/auth/site/templates/block_change_password.tpl b/modules/auth/site/templates/block_change_password.tpl index 047508c..6822433 100644 --- a/modules/auth/site/templates/block_change_password.tpl +++ b/modules/auth/site/templates/block_change_password.tpl @@ -1,7 +1,7 @@
- Change Password Form + Change Password
diff --git a/modules/auth/site/templates/block_login.tpl b/modules/auth/site/templates/block_login.tpl deleted file mode 100644 index fd24464..0000000 --- a/modules/auth/site/templates/block_login.tpl +++ /dev/null @@ -1,29 +0,0 @@ -
- {unless isset="$user"} -

Login or Register

-
-
- -
- - -
- -
- - -
- - - -
-
-
- -
- {/unless} -
diff --git a/modules/basic_auth/cms_basic_auth_module.e b/modules/basic_auth/cms_basic_auth_module.e index 43c0bf7..ce35643 100644 --- a/modules/basic_auth/cms_basic_auth_module.e +++ b/modules/basic_auth/cms_basic_auth_module.e @@ -10,24 +10,17 @@ class CMS_BASIC_AUTH_MODULE inherit - CMS_MODULE + CMS_AUTH_MODULE_I + rename + module_api as basic_auth_api redefine + make, filters, setup_hooks end - CMS_HOOK_AUTO_REGISTER - CMS_HOOK_BLOCK - CMS_HOOK_MENU_SYSTEM_ALTER - - CMS_HOOK_VALUE_TABLE_ALTER - - SHARED_LOGGER - - CMS_REQUEST_UTIL - create make @@ -35,26 +28,42 @@ feature {NONE} -- Initialization make do + Precursor version := "1.0" description := "Service to manage basic authentication" - package := "authentication" - add_dependency ({CMS_AUTHENTICATION_MODULE}) end feature -- Access 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 -- do - configure_api_login (a_api, a_router) - configure_api_logoff (a_api, a_router) - 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) + if + a_response.is_authenticated and then + a_response.request.http_authorization /= Void + then + Result := True + end end +feature {CMS_API} -- Access: API + + oauth20_api: detachable CMS_AUTH_API_I + -- + feature -- Access: 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)) end +feature -- Access: router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- + 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 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_methods 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 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_methods 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 - handle_login_basic_auth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE + vals: CMS_VALUE_TABLE do 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 end @@ -104,103 +145,25 @@ feature -- Hooks configuration setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) -- Module hooks configuration. do - auto_subscribe_to_hooks (a_hooks) + Precursor (a_hooks) a_hooks.subscribe_to_block_hook (Current) - a_hooks.subscribe_to_value_table_alter_hook (Current) end feature -- Hooks - value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) - -- - do - if 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] - local - l_string: STRING do - Result := <<"login">> - debug ("roc") - create l_string.make_empty - across - Result as ic - loop - l_string.append (ic.item) - l_string.append_character (' ') - end - write_debug_log (generator + ".block_list:" + l_string ) - end + Result := <<"?login">> end get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) do - if - a_block_id.is_case_insensitive_equal_general ("login") and then - a_response.location.starts_with ("account/roc-basic-auth") - then + if a_block_id.is_case_insensitive_equal_general ("login") then 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) end end -feature {NONE} -- Helpers - - template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK - -- Smarty content block for `a_block_id' - local - p: detachable PATH - do - create p.make_from_string ("templates") - p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") - - p := a_response.api.module_theme_resource_location (Current, p) - if p /= Void then - if attached p.entry as e then - create Result.make (a_block_id, Void, p.parent, e) - else - create Result.make (a_block_id, Void, p.parent, p) - end - end - end - feature {NONE} -- Block views get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) @@ -210,7 +173,7 @@ feature {NONE} -- Block views if attached template_block (a_block_id, a_response) as l_tpl_block then create vals.make (1) -- add the variable to the block - value_table_alter (vals, a_response) + a_response.api.hooks.invoke_value_table_alter (vals, a_response) across vals as ic loop diff --git a/modules/basic_auth/filter/cms_basic_auth_filter.e b/modules/basic_auth/filter/cms_basic_auth_filter.e index 2b866ff..7aca72a 100644 --- a/modules/basic_auth/filter/cms_basic_auth_filter.e +++ b/modules/basic_auth/filter/cms_basic_auth_filter.e @@ -9,54 +9,46 @@ class CMS_BASIC_AUTH_FILTER inherit - WSF_URI_TEMPLATE_HANDLER - CMS_HANDLER - WSF_FILTER + CMS_AUTH_FILTER_I + + REFACTORING_HELPER create make feature -- Basic operations + auth_strategy: STRING + do + Result := {CMS_BASIC_AUTH_MODULE}.logout_location + end + execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute the filter. local l_auth: HTTP_AUTHORIZATION do - api.logger.put_debug (generator + ".execute ", Void) 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 - (attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal_general ("basic")) and then - attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password + l_auth.is_basic and then + attached l_auth.login as l_auth_login and then + attached l_auth.password as l_auth_password then - if api.user_api.is_valid_credential (l_auth_login, l_auth_password) then - if attached api.user_api.user_by_name (l_auth_login) as l_user then - debug ("refactor_fixme") - fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT") - -- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user)) - -- other authentication filters (OpenID, etc) should implement the same approach. - end - set_current_user (req, l_user) - execute_next (req, res) - else - debug ("refactor_fixme") - to_implement ("Internal server error") - end + if + api.user_api.is_valid_credential (l_auth_login, l_auth_password) and then + attached api.user_api.user_by_name (l_auth_login) as l_user + then + debug ("refactor_fixme") + fixme ("Maybe we need to store in the credentials in a shared context SECURITY_CONTEXT") + -- req.set_execution_variable ("security_content", create SECURITY_CONTEXT.make (l_user)) + -- other authentication filters (OpenID, etc) should implement the same approach. end + set_current_user (l_user) else api.logger.put_error (generator + ".execute login_valid failed for: " + l_auth_login, Void) - execute_next (req, res) end - else - api.logger.put_debug (generator + ".execute without authentication", Void) - execute_next (req, res) end + execute_next (req, res) end end diff --git a/modules/basic_auth/handler/cms_basic_auth_login_handler.e b/modules/basic_auth/handler/cms_basic_auth_login_handler.e index 00a4ba4..05a9785 100644 --- a/modules/basic_auth/handler/cms_basic_auth_login_handler.e +++ b/modules/basic_auth/handler/cms_basic_auth_login_handler.e @@ -49,9 +49,9 @@ feature -- HTTP Methods -- do 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 - 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 redirect_to (req.absolute_script_url ("/"), res) end diff --git a/modules/basic_auth/handler/cms_basic_auth_logoff_handler.e b/modules/basic_auth/handler/cms_basic_auth_logoff_handler.e index 3c4dce2..8d3176f 100644 --- a/modules/basic_auth/handler/cms_basic_auth_logoff_handler.e +++ b/modules/basic_auth/handler/cms_basic_auth_logoff_handler.e @@ -51,11 +51,11 @@ feature -- HTTP Methods do api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) if attached req.query_parameter ("prompt") as l_prompt then - unset_current_user (req) + api.unset_current_user (req) send_access_denied_message (res) else create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) - unset_current_user (req) + api.unset_current_user (req) l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection l_url := req.absolute_script_url ("") i := l_url.substring_index ("://", 1) diff --git a/modules/basic_auth/site/files/js/roc_basic_auth.js b/modules/basic_auth/site/files/js/roc_basic_auth.js index 467bcd4..d932692 100644 --- a/modules/basic_auth/site/files/js/roc_basic_auth.js +++ b/modules/basic_auth/site/files/js/roc_basic_auth.js @@ -1,307 +1,291 @@ var ROC_AUTH = ROC_AUTH || { }; -var loginURL = "/basic_auth_login"; -var logoutURL = "/basic_auth_logoff"; +var loginURL = "/roc-basic-login"; +var logoutURL = "/roc-basic-logoff"; var userAgent = navigator.userAgent.toLowerCase(); var firstLogIn = true; ROC_AUTH.login = function() { - var form = document.forms['cms_basic_auth']; - var username = form.username.value; - var password = form.password.value; - //var host = form.host.value; + var form = document.forms['cms_basic_auth']; + var username = form.username.value; + var password = form.password.value; + //var host = form.host.value; var origin = window.location.origin + window.location.pathname; - var _login = function(){ + var _login = function(){ + if (document.getElementById('myModalFormId') !== null ) { + ROC_AUTH.remove ('myModalFormId'); + } - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } + if (username === "" || password === "") { + if (document.getElementById('myModalFormId') === null ) { + var newdiv = document.createElement('div'); + newdiv.innerHTML = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".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 = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".login-box").append(newdiv); + } + } + } + } + } + } - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", loginURL, true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - delete form; - window.location=window.location.origin; - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - } + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1) { //TODO: check version number + if (firstLogIn) { + _login(); + } else { + ROC_AUTH.logoff(_login); + } + } else { + _login(); + } - } - } - } - } - } - - 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; + if (firstLogIn) { + firstLogIn = false; + } }; ROC_AUTH.login_with_redirect = function() { - var form = document.forms[2]; - var username = form.username.value; - var password = form.password.value; - var host = form.host.value; - var _login = function(){ + var form = document.forms[2]; + var username = form.username.value; + var password = form.password.value; + var host = form.host.value; + var _login = function(){ + var redirectURL = form.redirect && form.redirect.value || ""; + $("#imgProgressRedirect").show(); - 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 = "
Invalid Credentials
"; + 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 = "
Invalid Credentials
"; + newdiv.id = 'myModalFormId'; + $(".login-box").append(newdiv); + $("#imgProgressRedirect").hide(); + } + } + } + } + } + } - $("#imgProgressRedirect").show(); - - if (document.getElementById('myModalFormId') !== null ) { - ROC_AUTH.remove ('myModalFormId'); - } - - - if (username === "" || password === "") { - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - }else{ - - //Instantiate HTTP Request - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(loginURL), true, username, password); - request.send(null); - - //Process Response - request.onreadystatechange = function(){ - if (request.readyState == 4) { - if (request.status==200) { - if (redirectURL === "") { - window.location=host.concat("/"); - } else { - window.location=host.concat(redirectURL); - } - - } - else{ - if (navigator.userAgent.toLowerCase().indexOf("firefox") != -1){ - } - - if (document.getElementById('myModalFormId') === null ) { - var newdiv = document.createElement('div'); - newdiv.innerHTML = "
Invalid Credentials
"; - newdiv.id = 'myModalFormId'; - $(".primary-tabs").append(newdiv); - $("#imgProgressRedirect").hide(); - } - - } - } - } - } - } - - var userAgent = navigator.userAgent.toLowerCase(); - if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - if (firstLogIn) _login(); - else logoff(_login); - } - else{ - _login(); - } - - if (firstLogIn) firstLogIn = false; + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + if (firstLogIn) { + _login(); + } else { + ROC_AUTH.logoff(_login); + } + } else{ + _login(); + } + if (firstLogIn) { + firstLogIn = false; + } }; - ROC_AUTH.getQueryParameterByName = function (name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); + return results === null ? " " : decodeURIComponent(results[1].replace(/\+/g, " ")); } ROC_AUTH.logoff = function(callback){ - var form = document.forms[0]; - var host = form.host.value; - - if (userAgent.indexOf("msie") != -1) { - document.execCommand("ClearAuthenticationCache"); - } - else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number - - var request1 = new XMLHttpRequest(); - var request2 = new XMLHttpRequest(); - - //Logout. Tell the server not to return the "WWW-Authenticate" header - request1.open("GET", host.concat(logoutURL) + "?prompt=false", true); - request1.send(""); - request1.onreadystatechange = function(){ - if (request1.readyState == 4) { - - //Sign in with dummy credentials to clear the auth cache - request2.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request2.send(""); - - request2.onreadystatechange = function(){ - if (request2.readyState == 4) { - if (callback!=null) { callback.call(); } else { window.location=host.concat(logoutURL);} - } - } - - } - } - } - else { - var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); - request.open("GET", host.concat(logoutURL), true, "logout", "logout"); - request.send(""); - request.onreadystatechange = function(){ - if (request.status==401 || request.status==403 ) { window.location=host.concat(logoutURL); - } - } - } + var form = document.forms[0]; + var host = form.host.value; + + if (userAgent.indexOf("msie") != -1) { + document.execCommand("ClearAuthenticationCache"); + } else if (userAgent.indexOf("firefox") != -1){ //TODO: check version number + var request1 = new XMLHttpRequest(); + var request2 = new XMLHttpRequest(); + + //Logout. Tell the server not to return the "WWW-Authenticate" header + request1.open("GET", host + logoutURL + "?prompt=false", true); + request1.send(""); + request1.onreadystatechange = function(){ + if (request1.readyState == 4) { + //Sign in with dummy credentials to clear the auth cache + request2.open("GET", host + logoutURL, true, "logout", "logout"); + request2.send(""); + request2.onreadystatechange = function(){ + if (request2.readyState == 4) { + if (callback!=null) { + callback.call(); + } else { + window.location=host + logoutURL; + } + } + } + } + } + } else { + var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")); + request.open("GET", host + logoutURL, true, "logout", "logout"); + request.send(""); + request.onreadystatechange = function(){ + if (request.status==401 || request.status==403 ) { + window.location=host + logoutURL; + } + } + } }; -ROC_AUTH.remove = function (id) -{ - var element = document.getElementById(id); - element.outerHTML = ""; - delete element; - return; +ROC_AUTH.remove = function (id) { + var element = document.getElementById(id); + element.outerHTML = ""; + delete element; + return; }; $(document).ready(function() { - - if (typeof String.prototype.contains != 'function') { - String.prototype.contains = function (str){ - return this.indexOf(str) != -1; - }; - } - ROC_AUTH.progressive_loging(); - + if (typeof String.prototype.contains != 'function') { + String.prototype.contains = function (str){ + return this.indexOf(str) != -1; + }; + } + ROC_AUTH.progressive_loging(); }); ROC_AUTH.progressive_loging = function () { - - ROC_AUTH.login_href(); + ROC_AUTH.login_href(); }; $(document).keypress(function(e) { - if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { - ROC_AUTH.login(); - } + if ((e.which === 13) && (e.target.localName === 'input' && e.target.id === 'password')) { + ROC_AUTH.login(); + } }); ROC_AUTH.OnOneClick = function(event) { - event.preventDefault(); - if ( document.forms[0] === undefined ) { - ROC_AUTH.create_form(); - } - return false; + event.preventDefault(); + if ( document.forms[0] === undefined ) { + ROC_AUTH.create_form(); + } + return false; }; ROC_AUTH.login_href = function() { - var els = document.getElementsByTagName("a"); - for (var i = 0, l = els.length; i < l; i++) { - var el = els[i]; - if (el.href.contains("/basic_auth_login?destination")) { - loginURL = el.href; - var OneClick = el; - OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); - } - } + var els = document.getElementsByTagName("a"); + for (var i = 0, l = els.length; i < l; i++) { + var el = els[i]; + if (el.href.contains(loginURL + "?destination")) { +// loginURL = el.href; + var OneClick = el; + OneClick.addEventListener('click', ROC_AUTH.OnOneClick, false); + } + } }; ROC_AUTH.create_form = function() { - // Fetching HTML Elements in Variables by ID. - var createform = document.createElement('form'); // Create New Element Form - createform.setAttribute("action", ""); // Setting Action Attribute on Form - createform.setAttribute("method", "post"); // Setting Method Attribute on Form - $("body").append(createform); + // Fetching HTML Elements in Variables by ID. + var createform = document.createElement('form'); // Create New Element Form + createform.setAttribute("action", ""); // Setting Action Attribute on Form + createform.setAttribute("method", "post"); // Setting Method Attribute on Form + $("body").append(createform); - var heading = document.createElement('h2'); // Heading of Form - heading.innerHTML = "Login Form "; - createform.appendChild(heading); + var heading = document.createElement('h2'); // Heading of Form + heading.innerHTML = "Login Form "; + createform.appendChild(heading); - var line = document.createElement('hr'); // Giving Horizontal Row After Heading - createform.appendChild(line); + var line = document.createElement('hr'); // Giving Horizontal Row After Heading + createform.appendChild(line); - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); - var namelabel = document.createElement('label'); // Create Label for Name Field - namelabel.innerHTML = "Username : "; // Set Field Labels - createform.appendChild(namelabel); + var namelabel = document.createElement('label'); // Create Label for Name Field + namelabel.innerHTML = "Username : "; // Set Field Labels + createform.appendChild(namelabel); - var inputelement = document.createElement('input'); // Create Input Field for UserName - inputelement.setAttribute("type", "text"); - inputelement.setAttribute("name", "username"); - inputelement.setAttribute("required","required"); - createform.appendChild(inputelement); + var inputelement = document.createElement('input'); // Create Input Field for UserName + inputelement.setAttribute("type", "text"); + inputelement.setAttribute("name", "username"); + inputelement.setAttribute("required","required"); + createform.appendChild(inputelement); - var linebreak = document.createElement('br'); - createform.appendChild(linebreak); + var linebreak = document.createElement('br'); + createform.appendChild(linebreak); - var passwordlabel = document.createElement('label'); // Create Label for Password Field - passwordlabel.innerHTML = "Password : "; - createform.appendChild(passwordlabel); + var passwordlabel = document.createElement('label'); // Create Label for Password Field + passwordlabel.innerHTML = "Password : "; + createform.appendChild(passwordlabel); - var passwordelement = document.createElement('input'); // Create Input Field for Password. - passwordelement.setAttribute("type", "password"); - passwordelement.setAttribute("name", "password"); - passwordelement.setAttribute("id", "password"); - passwordelement.setAttribute("required","required"); - createform.appendChild(passwordelement); + var passwordelement = document.createElement('input'); // Create Input Field for Password. + passwordelement.setAttribute("type", "password"); + passwordelement.setAttribute("name", "password"); + passwordelement.setAttribute("id", "password"); + passwordelement.setAttribute("required","required"); + createform.appendChild(passwordelement); - var passwordbreak = document.createElement('br'); - createform.appendChild(passwordbreak); + var passwordbreak = document.createElement('br'); + createform.appendChild(passwordbreak); - var submitelement = document.createElement('button'); // Append Submit Button - submitelement.setAttribute("type", "button"); - submitelement.setAttribute("onclick", "ROC_AUTH.login();"); - submitelement.innerHTML = "Sign In "; - createform.appendChild(submitelement); + var submitelement = document.createElement('button'); // Append Submit Button + submitelement.setAttribute("type", "button"); + submitelement.setAttribute("onclick", "ROC_AUTH.login();"); + submitelement.innerHTML = "Sign In "; + createform.appendChild(submitelement); }; @@ -310,16 +294,16 @@ var password = document.getElementById("password"); var confirm_password = document.getElementById("confirm_password"); ROC_AUTH.validatePassword =function(){ - if ((password != null) && (confirm_password != null)) { - if(password.value != confirm_password.value) { - confirm_password.setCustomValidity("Passwords Don't Match"); - } else { - confirm_password.setCustomValidity(''); - } - } + if ((password != null) && (confirm_password != null)) { + if(password.value != confirm_password.value) { + confirm_password.setCustomValidity("Passwords Don't Match"); + } else { + confirm_password.setCustomValidity(''); + } + } } if ((password != null) && (confirm_password != null)) { - password.onchange = ROC_AUTH.validatePassword(); - confirm_password.onkeyup = ROC_AUTH.validatePassword; + password.onchange = ROC_AUTH.validatePassword(); + confirm_password.onkeyup = ROC_AUTH.validatePassword; } diff --git a/modules/basic_auth/site/templates/block_login.tpl b/modules/basic_auth/site/templates/block_login.tpl index c49cf2c..f5b3eb6 100644 --- a/modules/basic_auth/site/templates/block_login.tpl +++ b/modules/basic_auth/site/templates/block_login.tpl @@ -1,29 +1,23 @@ -
- {unless isset="$user"} -

Login or Register

+{unless isset="$user"} + +{/unless} diff --git a/modules/contact/src/cms_contact_module.e b/modules/contact/src/cms_contact_module.e index 09d6225..8e2cbf8 100644 --- a/modules/contact/src/cms_contact_module.e +++ b/modules/contact/src/cms_contact_module.e @@ -20,8 +20,6 @@ inherit contact_api end - SHARED_HTML_ENCODER - CMS_HOOK_BLOCK CMS_HOOK_BLOCK_HELPER @@ -287,8 +285,8 @@ feature -- Hooks r.values.force (False, "has_error") create vars.make_caseless (5) - vars.put (html_encoded (api.setup.site_url), "siteurl") - vars.put (html_encoded (api.setup.site_name), "sitename") + 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 @@ -422,17 +420,6 @@ feature {NONE} -- Helpers end end -feature {NONE} -- HTML ENCODING. - - html_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 - do - if s /= Void then - Result := html_encoder.general_encoded_string (s) - else - create Result.make_empty - 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 diff --git a/modules/node/cms_node_api.e b/modules/node/cms_node_api.e index bc4523e..4345a8f 100644 --- a/modules/node/cms_node_api.e +++ b/modules/node/cms_node_api.e @@ -290,9 +290,7 @@ feature -- Access: Node is_author_of_node (u: CMS_USER; a_node: CMS_NODE): BOOLEAN -- Is the user `u' owner of the node `n'. do - if attached node_storage.node_author (a_node) as l_author then - Result := u.same_as (l_author) - end + Result := u.same_as (a_node.author) end nodes_of_type (a_node_type: CMS_CONTENT_TYPE): LIST [CMS_NODE] diff --git a/modules/node/handler/node_handler.e b/modules/node/handler/node_handler.e index 516aef2..36a19db 100644 --- a/modules/node/handler/node_handler.e +++ b/modules/node/handler/node_handler.e @@ -122,7 +122,7 @@ feature -- HTTP Methods if l_node /= Void 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 l_node := node_api.revision_node (l_nid, l_rev) end @@ -137,9 +137,9 @@ feature -- HTTP Methods view_response.set_revision (l_rev) view_response.execute 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) - 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 create view_response.make (req, res, api, node_api) @@ -208,13 +208,13 @@ feature -- HTTP Methods do_trash (req: WSF_REQUEST; res: WSF_RESPONSE) -- Trash a node, soft delete. 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 l_id.is_integer and then attached node_api.node (l_id.integer_value) as l_node 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) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) else @@ -245,13 +245,13 @@ feature {NONE} -- Trash:Restore local l_source: STRING 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 l_id.is_integer and then attached {CMS_NODE} node_api.node (l_id.integer_value) as l_node 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) l_source := node_api.node_path (l_node) 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) -- Restore a node: From {CMS_NODE_API}.trashed to {CMS_NODE_API}.not_published. 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 l_id.is_integer and then attached node_api.node (l_id.integer_value) as l_node 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) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) else @@ -310,7 +310,7 @@ feature {NONE} -- Trash:Restore l_id.is_integer and then attached node_api.node (l_id.integer_value) as l_node 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 b.make_empty b.append ("
    ") diff --git a/modules/node/handler/node_view_response.e b/modules/node/handler/node_view_response.e index 64b800b..0105605 100644 --- a/modules/node/handler/node_view_response.e +++ b/modules/node/handler/node_view_response.e @@ -53,6 +53,7 @@ feature -- Execution then l_manager.append_content_as_html_to_page (l_node, Current) end + set_modification_date (l_node.modification_date) elseif revision > 0 then set_main_content ("Missing revision node!") else @@ -64,8 +65,6 @@ feature -- Execution if l_node /= Void and revision > 0 then set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title)) end - - end end diff --git a/modules/node/persistence/cms_node_storage_i.e b/modules/node/persistence/cms_node_storage_i.e index dc941c2..bea4166 100644 --- a/modules/node/persistence/cms_node_storage_i.e +++ b/modules/node/persistence/cms_node_storage_i.e @@ -120,13 +120,6 @@ feature -- Access deferred 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] -- List of nodes of type `a_node_type'. --| Redefine to optimize! diff --git a/modules/node/persistence/cms_node_storage_null.e b/modules/node/persistence/cms_node_storage_null.e index 34cf6a3..9d7bf45 100644 --- a/modules/node/persistence/cms_node_storage_null.e +++ b/modules/node/persistence/cms_node_storage_null.e @@ -75,17 +75,6 @@ feature -- Access: node do 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 children (a_node: CMS_NODE): detachable LIST [CMS_NODE] diff --git a/modules/node/persistence/cms_node_storage_sql.e b/modules/node/persistence/cms_node_storage_sql.e index 3047652..13640a3 100644 --- a/modules/node/persistence/cms_node_storage_sql.e +++ b/modules/node/persistence/cms_node_storage_sql.e @@ -206,23 +206,6 @@ feature -- Access sql_finalize 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 insert node id. do @@ -589,11 +572,6 @@ feature {NONE} -- Queries 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 @@ -638,23 +616,4 @@ feature {NONE} -- Implementation 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 diff --git a/modules/oauth20/cms_oauth_20_api.e b/modules/oauth20/cms_oauth_20_api.e index 44ecce8..cb5ef6a 100644 --- a/modules/oauth20/cms_oauth_20_api.e +++ b/modules/oauth20/cms_oauth_20_api.e @@ -9,7 +9,7 @@ class CMS_OAUTH_20_API inherit - CMS_MODULE_API + CMS_AUTH_API_I REFACTORING_HELPER @@ -97,7 +97,6 @@ feature -- Access: Consumers 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) -- Add a new user with oauth20 using the consumer `a_consumer'. require @@ -124,6 +123,4 @@ feature -- Change: User OAuth20 oauth_20_storage.remove_user_oauth2 (a_user, a_consumer_table) end - - end diff --git a/modules/oauth20/cms_oauth_20_module.e b/modules/oauth20/cms_oauth_20_module.e index f390df7..653e2a5 100644 --- a/modules/oauth20/cms_oauth_20_module.e +++ b/modules/oauth20/cms_oauth_20_module.e @@ -9,10 +9,11 @@ class CMS_OAUTH_20_MODULE inherit - CMS_MODULE + CMS_AUTH_MODULE_I rename module_api as oauth20_api redefine + make, filters, setup_hooks, initialize, @@ -22,12 +23,6 @@ inherit CMS_HOOK_BLOCK - CMS_HOOK_AUTO_REGISTER - - CMS_HOOK_MENU_SYSTEM_ALTER - - CMS_HOOK_VALUE_TABLE_ALTER - SHARED_EXECUTION_ENVIRONMENT export {NONE} all @@ -35,10 +30,6 @@ inherit REFACTORING_HELPER - SHARED_LOGGER - - CMS_REQUEST_UTIL - create make @@ -47,11 +38,9 @@ feature {NONE} -- Initialization make -- Create current module do + Precursor version := "1.0" description := "OAuth20 module" - package := "authentication" - - add_dependency ({CMS_AUTHENTICATION_MODULE}) create root_dir.make_current cache_duration := 0 @@ -135,7 +124,7 @@ feature {CMS_API} -- Module management end l_sql_storage.sql_finalize - Precursor {CMS_MODULE}(api) -- Marked as installed. + Precursor {CMS_AUTH_MODULE_I}(api) -- Marked as installed. end end end @@ -151,8 +140,8 @@ feature -- Filters filters (a_api: CMS_API): detachable LIST [WSF_FILTER] -- Possibly list of Filter's module. do - create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached oauth20_api as l_oauth_api then + create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_oauth_api)) end end @@ -172,27 +161,48 @@ feature -- Access: docs Result := cache_duration = 0 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 + -- + 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 setup_router (a_router: WSF_ROUTER; a_api: CMS_API) -- do if attached oauth20_api as l_oauth_api then - a_router.handle ("/account/roc-oauth-login", + 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/roc-oauth-logout", + 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/login-with-oauth/{" + oauth_callback_path_parameter + "}", + 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/oauth-callback/{" + oauth_callback_path_parameter + "}", + 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/oauth-associate", + 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/oauth-un-associate", + 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 @@ -211,84 +221,22 @@ feature -- Hooks configuration setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) -- Module hooks configuration. do - auto_subscribe_to_hooks (a_hooks) + Precursor (a_hooks) a_hooks.subscribe_to_block_hook (Current) - a_hooks.subscribe_to_value_table_alter_hook (Current) end feature -- Hooks - value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) - -- - do - if - attached a_response.user as u and then - attached oauth20_api as l_oauth20_api and then - attached a_response.request.cookie (l_oauth20_api.session_token) - 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 oauth20_api as l_oauth20_api and then - attached {WSF_STRING} a_response.request.cookie (l_oauth20_api.session_token) 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] - local - l_string: STRING do 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 get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) do if a_block_id.is_case_insensitive_equal_general ("login") and then - a_response.location.starts_with ("account/roc-oauth-login") + a_response.location.starts_with (login_location) then get_block_view_login (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("account") and then @@ -299,6 +247,7 @@ feature -- Hooks attached a_response.user as l_user then associate_account (l_user, a_response.values) + l_tpl_block.set_weight (5) a_response.add_block (l_tpl_block, "content") else debug ("cms") @@ -313,7 +262,11 @@ feature -- Hooks r: CMS_RESPONSE do 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 end @@ -323,7 +276,7 @@ feature -- Hooks l_cookie: WSF_COOKIE do if - attached {CMS_USER} current_user (req) as l_user and then + attached api.user as l_user and then attached {WSF_STRING} req.cookie (a_oauth20_api.session_token) as l_cookie_token then -- Logout OAuth @@ -331,7 +284,7 @@ feature -- Hooks l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) - unset_current_user (req) + api.unset_current_user (req) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) r.set_status_code ({HTTP_CONSTANTS}.found) @@ -364,25 +317,6 @@ feature {NONE} -- Associate end end -feature {NONE} -- Helpers - - template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK - -- Smarty content block for `a_block_id' - local - p: detachable PATH - do - create p.make_from_string ("templates") - p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") - p := a_response.api.module_theme_resource_location (Current, p) - if p /= Void then - if attached p.entry as e then - create Result.make (a_block_id, Void, p.parent, e) - else - create Result.make (a_block_id, Void, p.parent, p) - end - end - end - feature {NONE} -- Block views get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) @@ -392,7 +326,7 @@ feature {NONE} -- Block views if attached template_block (a_block_id, a_response) as l_tpl_block then create vals.make (1) -- add the variable to the block - value_table_alter (vals, a_response) + a_response.api.hooks.invoke_value_table_alter (vals, a_response) across vals as ic loop @@ -507,10 +441,10 @@ feature -- OAuth2 Login with Provider l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) - set_current_user (req, l_user) + 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)) write_debug_log (generator + ".handle_callback_oauth: send_contact_welcome_email") es.send_contact_welcome_email (l_email, l_user, req.absolute_script_url ("")) diff --git a/modules/oauth20/filter/cms_oauth_20_filter.e b/modules/oauth20/filter/cms_oauth_20_filter.e index e04630d..420fe33 100644 --- a/modules/oauth20/filter/cms_oauth_20_filter.e +++ b/modules/oauth20/filter/cms_oauth_20_filter.e @@ -10,15 +10,11 @@ class CMS_OAUTH_20_FILTER inherit - WSF_URI_TEMPLATE_HANDLER - - CMS_HANDLER + CMS_AUTH_FILTER_I rename - make as make_handler + make as make_filter end - WSF_FILTER - create make @@ -26,7 +22,7 @@ feature {NONE} -- Initialization make (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API) do - make_handler (a_api) + make_filter (a_api) oauth_api := a_user_oauth_api end @@ -34,21 +30,22 @@ feature {NONE} -- Initialization feature -- Basic operations - execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute the filter. + auth_strategy: STRING + do + Result := {CMS_OAUTH_20_MODULE}.logout_location + end + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- . do - api.logger.put_debug (generator + ".execute ", Void) - -- A valid user if attached {WSF_STRING} req.cookie (oauth_api.session_token) as l_roc_auth_session_token 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 api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) end - else - api.logger.put_debug (generator + ".execute without authentication", Void) end execute_next (req, res) end diff --git a/modules/oauth20/persistence/cms_oauth_20_storage_i.e b/modules/oauth20/persistence/cms_oauth_20_storage_i.e index d9fcf5e..bd16eb2 100644 --- a/modules/oauth20/persistence/cms_oauth_20_storage_i.e +++ b/modules/oauth20/persistence/cms_oauth_20_storage_i.e @@ -21,7 +21,7 @@ feature -- Error Handling feature -- Access: Users 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 end diff --git a/modules/oauth20/persistence/cms_oauth_20_storage_sql.e b/modules/oauth20/persistence/cms_oauth_20_storage_sql.e index 6b6f0b5..e0b3abf 100644 --- a/modules/oauth20/persistence/cms_oauth_20_storage_sql.e +++ b/modules/oauth20/persistence/cms_oauth_20_storage_sql.e @@ -45,23 +45,27 @@ feature -- Access User Outh local l_parameters: STRING_TABLE [detachable ANY] l_string: STRING + l_uid: INTEGER_64 do error_handler.reset write_information_log (generator + ".user_oauth2_by_id") create l_parameters.make (1) 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)) sql_query (l_string, l_parameters) if not has_error and not sql_after then - Result := fetch_user + l_uid := sql_read_integer_64 (1) sql_forth if not sql_after then check no_more_than_one: False end - Result := Void + l_uid := 0 end end 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 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 l_parameters: STRING_TABLE [detachable ANY] l_string: STRING + l_uid: INTEGER_64 do error_handler.reset write_information_log (generator + ".user_oauth2_by_email") create l_parameters.make (1) 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)) sql_query (l_string, l_parameters) if not has_error and not sql_after then - Result := fetch_user + l_uid := sql_read_integer_64 (1) sql_forth if not sql_after then check no_more_than_one: False end - Result := Void + l_uid := 0 end end 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 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 l_parameters: STRING_TABLE [detachable ANY] l_string: STRING + l_uid: INTEGER_64 do error_handler.reset write_information_log (generator + ".user_by_oauth2_token") create l_parameters.make (1) 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)) sql_query (l_string, l_parameters) if not has_error and not sql_after then - Result := fetch_user + l_uid := sql_read_integer_64 (1) sql_forth if not sql_after then check no_more_than_one: False end - Result := Void + l_uid := 0 end end 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 - feature --Access: Consumers oauth2_consumers: LIST [STRING] @@ -286,45 +297,6 @@ feature {NONE} -- Implementation OAuth Consumer 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 oauth2_sql_table_name (a_consumer: READABLE_STRING_GENERAL): STRING_8 @@ -353,12 +325,20 @@ feature {NONE} -- User OAuth2 end end - Select_user_by_oauth2_template_token: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.access_token = :token;" - --| FIXME: replace the u.* by a list of field names, to avoid breaking `featch_user' if two fieds are swiped. + Select_user_id_by_oauth2_template_token: STRING = "SELECT uid FROM $table_name WHERE access_token = :token;" + --| 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);" diff --git a/modules/openid/cms_openid_api.e b/modules/openid/cms_openid_api.e index 8009883..d0543fd 100644 --- a/modules/openid/cms_openid_api.e +++ b/modules/openid/cms_openid_api.e @@ -9,7 +9,7 @@ class CMS_OPENID_API inherit - CMS_MODULE_API + CMS_AUTH_API_I REFACTORING_HELPER @@ -27,7 +27,7 @@ feature {NONE} -- Initialization make (a_api) -- Initialize openid related settings. - s := a_api.setup.string_8_item ("auth.openid.token") + 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 @@ -40,7 +40,7 @@ feature {NONE} -- Initialization session_max_age := 3600 --| one hour: *60(min) *60(sec) end ensure - openid_storage_set: openid_storage = a_openid_storage + openid_storage_set: openid_storage = a_openid_storage end feature {CMS_MODULE} -- Access: User openid storage. @@ -87,7 +87,6 @@ feature -- Access: Consumers OAuth20 feature -- Change: User Openid - new_user_openid (a_identity: READABLE_STRING_GENERAL; a_user: CMS_USER) -- Add a new user with openid using the identity `a_identity'. require diff --git a/modules/openid/cms_openid_module.e b/modules/openid/cms_openid_module.e index 7f9cba0..2cc779f 100644 --- a/modules/openid/cms_openid_module.e +++ b/modules/openid/cms_openid_module.e @@ -9,10 +9,11 @@ class CMS_OPENID_MODULE inherit - CMS_MODULE + CMS_AUTH_MODULE_I rename module_api as openid_api redefine + make, filters, setup_hooks, initialize, @@ -20,15 +21,8 @@ inherit openid_api end - CMS_HOOK_BLOCK - CMS_HOOK_AUTO_REGISTER - - CMS_HOOK_MENU_SYSTEM_ALTER - - CMS_HOOK_VALUE_TABLE_ALTER - SHARED_EXECUTION_ENVIRONMENT export {NONE} all @@ -36,11 +30,6 @@ inherit REFACTORING_HELPER - SHARED_LOGGER - - CMS_REQUEST_UTIL - - create make @@ -49,10 +38,9 @@ feature {NONE} -- Initialization make -- Create current module do + Precursor version := "1.0" description := "Openid module" - package := "authentication" - add_dependency ({CMS_AUTHENTICATION_MODULE}) create root_dir.make_current cache_duration := 0 @@ -111,7 +99,7 @@ feature {CMS_API} -- Module management if l_sql_storage.has_error then api.logger.put_error ("Could not initialize openid items for module [" + name + "]", generating_type) else - Precursor {CMS_MODULE}(api) -- Mark it installed. + Precursor {CMS_AUTH_MODULE_I}(api) -- Mark it installed. end end end @@ -149,22 +137,43 @@ feature -- Access: docs Result := cache_duration = 0 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 + -- + 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 setup_router (a_router: WSF_ROUTER; a_api: CMS_API) -- do if attached openid_api as l_openid_api then - a_router.handle ("/account/roc-openid-login", - create {WSF_URI_AGENT_HANDLER}.make (agent handle_openid_login (a_api, ?, ?)), + a_router.handle ("/" + login_location, + create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/roc-openid-logout", + 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/login-with-openid/{" + openid_consumer_path_parameter + "}", + 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/openid-callback", + 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 @@ -178,105 +187,45 @@ feature -- Hooks configuration setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) -- Module hooks configuration. do - auto_subscribe_to_hooks (a_hooks) + Precursor (a_hooks) a_hooks.subscribe_to_block_hook (Current) - a_hooks.subscribe_to_value_table_alter_hook (Current) end feature -- Hooks - value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) - -- - do - if - attached a_response.user as u and then - attached openid_api as l_openid_api and then - attached {WSF_STRING} a_response.request.cookie (l_openid_api.session_token) - 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 openid_api as l_openid_api and then - attached {WSF_STRING} a_response.request.cookie (l_openid_api.session_token) 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] - local - l_string: STRING do Result := <<"login">> - debug ("roc") - create l_string.make_empty - across - Result as ic - loop - l_string.append (ic.item) - l_string.append_character (' ') - end - write_debug_log (generator + ".block_list:" + l_string ) - end end get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) do if a_block_id.is_case_insensitive_equal_general ("login") and then - a_response.location.starts_with ("account/roc-openid-login") + a_response.location.starts_with (login_location) then get_block_view_login (a_block_id, a_response) 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 r: CMS_RESPONSE o: OPENID_CONSUMER s: STRING do 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.execute + + elseif req.is_post_request_method then create s.make_empty if attached req.string_item ("openid") as p_openid then 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_all_info (False) if attached o.auth_url (p_openid) as l_url then @@ -285,10 +234,10 @@ feature -- Hooks s.append (" Failure") r.set_status_code ({HTTP_CONSTANTS}.bad_request) r.values.force (s, "error") - r.execute end end end + r.execute end handle_logout (api: CMS_API; a_openid_api: CMS_OPENID_API; req: WSF_REQUEST; res: WSF_RESPONSE) @@ -297,7 +246,7 @@ feature -- Hooks l_cookie: WSF_COOKIE do if - attached {CMS_USER} current_user (req) as l_user and then + attached {CMS_USER} api.user as l_user and then attached {WSF_STRING} req.cookie (a_openid_api.session_token) as l_cookie_token then -- Logout OAuth @@ -305,7 +254,7 @@ feature -- Hooks l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) - unset_current_user (req) + api.unset_current_user (req) create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) r.set_status_code ({HTTP_CONSTANTS}.found) r.set_redirection (req.absolute_script_url ("")) @@ -315,25 +264,6 @@ feature -- Hooks end end -feature {NONE} -- Helpers - - template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK - -- Smarty content block for `a_block_id' - local - p: detachable PATH - do - create p.make_from_string ("templates") - p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") - p := a_response.api.module_theme_resource_location (Current, p) - if p /= Void then - if attached p.entry as e then - create Result.make (a_block_id, Void, p.parent, e) - else - create Result.make (a_block_id, Void, p.parent, p) - end - end - end - feature {NONE} -- Block views get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) @@ -343,7 +273,7 @@ feature {NONE} -- Block views if attached template_block (a_block_id, a_response) as l_tpl_block then create vals.make (1) -- add the variable to the block - value_table_alter (vals, a_response) + a_response.api.hooks.invoke_value_table_alter (vals, a_response) across vals as ic loop @@ -380,7 +310,7 @@ feature -- Openid Login create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) create b.make_empty 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_all_info (False) if attached o.auth_url (l_oc.endpoint) as l_url then @@ -433,6 +363,7 @@ feature -- Openid Login l_cookie.set_max_age (a_openid_api.session_max_age) l_cookie.set_path ("/") res.add_cookie (l_cookie) + api.record_user_login (p_user) else create {ARRAYED_LIST [CMS_USER_ROLE]} l_roles.make (1) diff --git a/modules/openid/filter/cms_openid_filter.e b/modules/openid/filter/cms_openid_filter.e index a2968bd..bb44619 100644 --- a/modules/openid/filter/cms_openid_filter.e +++ b/modules/openid/filter/cms_openid_filter.e @@ -9,15 +9,11 @@ class CMS_OPENID_FILTER inherit - WSF_URI_TEMPLATE_HANDLER - - CMS_HANDLER + CMS_AUTH_FILTER_I rename - make as make_handler + make as make_filter end - WSF_FILTER - create make @@ -25,7 +21,7 @@ feature {NONE} -- Initialization make (a_api: CMS_API; a_openid_api: CMS_OPENID_API) do - make_handler (a_api) + make_filter (a_api) openid_api := a_openid_api end @@ -33,15 +29,19 @@ feature {NONE} -- Initialization feature -- Basic operations - execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute the filter. + auth_strategy: STRING + do + Result := {CMS_OPENID_MODULE}.logout_location + end + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- . do - -- A valid user if attached {WSF_STRING} req.cookie (openid_api.session_token) as l_roc_openid_session_token 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 api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_openid_session_token.value , Void) end diff --git a/modules/openid/persitence/cms_openid_storage_sql.e b/modules/openid/persitence/cms_openid_storage_sql.e index bb2f0d7..460b35c 100644 --- a/modules/openid/persitence/cms_openid_storage_sql.e +++ b/modules/openid/persitence/cms_openid_storage_sql.e @@ -26,45 +26,53 @@ feature -- Access User Outh -- local l_parameters: STRING_TABLE [detachable ANY] + l_uid: INTEGER_64 do error_handler.reset write_information_log (generator + ".user_openid_by_userid_identity") create l_parameters.make (1) l_parameters.put (a_uid, "uid") 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 - Result := fetch_user + l_uid := sql_read_integer_64 (1) sql_forth if not sql_after then check no_more_than_one: False end - Result := Void + l_uid := 0 end end 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 user_openid_by_identity (a_identity: READABLE_STRING_GENERAL): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] + l_uid: INTEGER_64 do error_handler.reset write_information_log (generator + ".user_openid_by_identity") create l_parameters.make (1) 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 - Result := fetch_user + l_uid := sql_read_integer_64 (1) sql_forth if not sql_after then check no_more_than_one: False end - Result := Void + l_uid := 0 end else check no_more_than_one: False end end 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 feature --Access: Consumers @@ -148,52 +156,11 @@ feature {NONE} -- Implementation OAuth Consumer 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 + 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;" - --| 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;" + Select_user_id_openid_by_id: STRING = "SELECT uid FROM openid_items WHERE uid = :uid and identity = :identity;" Sql_insert_openid: STRING = "INSERT INTO openid_items (uid, identity, created) VALUES (:uid, :identity, :utc_date);" diff --git a/modules/session_auth/cms_session_api.e b/modules/session_auth/cms_session_api.e index 438bff5..ac02417 100644 --- a/modules/session_auth/cms_session_api.e +++ b/modules/session_auth/cms_session_api.e @@ -7,7 +7,7 @@ class CMS_SESSION_API inherit - CMS_MODULE_API + CMS_AUTH_API_I REFACTORING_HELPER diff --git a/modules/session_auth/cms_session_auth_module.e b/modules/session_auth/cms_session_auth_module.e index 60d5756..2192498 100644 --- a/modules/session_auth/cms_session_auth_module.e +++ b/modules/session_auth/cms_session_auth_module.e @@ -10,10 +10,11 @@ class CMS_SESSION_AUTH_MODULE inherit - CMS_MODULE + CMS_AUTH_MODULE_I rename module_api as session_api redefine + make, filters, setup_hooks, initialize, @@ -21,19 +22,8 @@ inherit session_api end - - CMS_HOOK_AUTO_REGISTER - CMS_HOOK_BLOCK - CMS_HOOK_MENU_SYSTEM_ALTER - - CMS_HOOK_VALUE_TABLE_ALTER - - SHARED_LOGGER - - CMS_REQUEST_UTIL - create make @@ -41,10 +31,9 @@ feature {NONE} -- Initialization make do + Precursor version := "1.0" description := "Service to manage cookie based authentication" - package := "authentication" - add_dependency ({CMS_AUTHENTICATION_MODULE}) end feature -- Access @@ -87,7 +76,7 @@ feature {CMS_API} -- Module management if l_sql_storage.has_error then api.logger.put_error ("Could not initialize database for module [" + name + "]", generating_type) else - Precursor {CMS_MODULE} (api) -- Mark it as installed. + Precursor {CMS_AUTH_MODULE_I} (api) -- Mark it as installed. end end end @@ -95,7 +84,28 @@ feature {CMS_API} -- Module management feature {CMS_API} -- Access: API session_api: detachable CMS_SESSION_API - -- + -- + +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 + -- + 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 @@ -103,9 +113,9 @@ feature -- Access: router -- do if attached session_api as l_session_api then - a_router.handle ("/account/roc-session-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle ("/account/roc-session-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, l_session_api, ?, ?)), a_router.methods_get_post) - a_router.handle ("/account/login-with-session", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_session (a_api,session_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 ("/" + 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 @@ -114,8 +124,8 @@ feature -- Access: filter filters (a_api: CMS_API): detachable LIST [WSF_FILTER] -- Possibly list of Filter's module. do - create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached 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)) end end @@ -124,9 +134,29 @@ feature {NONE} -- Implementation: routes handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local + vals: CMS_VALUE_TABLE r: CMS_RESPONSE do 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 end @@ -139,14 +169,14 @@ feature {NONE} -- Implementation: routes tok := a_session_api.session_token if 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 -- Logout Session create l_cookie.make (tok, l_cookie_token.value) -- FIXME: unicode issue? l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) - unset_current_user (req) + api.unset_user create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) r.set_status_code ({HTTP_CONSTANTS}.found) @@ -166,25 +196,39 @@ feature {NONE} -- Implementation: routes if attached a_session_api as l_session_api and then attached {WSF_STRING} req.form_parameter ("username") as l_username and then - attached {WSF_STRING} req.form_parameter ("password") as l_password and then - api.user_api.is_valid_credential (l_username.value, l_password.value) and then - attached api.user_api.user_by_name (l_username.value) as l_user + attached {WSF_STRING} req.form_parameter ("password") as l_password then - l_token := generate_token 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 - 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 - 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 - 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) - set_current_user (req, l_user) - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - r.set_redirection (req.absolute_script_url ("")) r.execute else create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) @@ -204,108 +248,24 @@ feature -- Hooks configuration setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER) -- Module hooks configuration. do - auto_subscribe_to_hooks (a_hooks) + Precursor (a_hooks) a_hooks.subscribe_to_block_hook (Current) - a_hooks.subscribe_to_value_table_alter_hook (Current) end feature -- Hooks - value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) - -- - do - if - attached a_response.user as u and then - attached session_api as l_session_api and then - attached a_response.request.cookie (l_session_api.session_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 then - if - attached session_api as l_session_api and then - attached a_response.request.cookie (l_session_api.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-session-logout" ) - a_menu_system.primary_menu.extend (lnk) - end - elseif a_response.location.starts_with ("account/") then - create lnk.make ("Session", "account/roc-session-login") - a_response.add_to_primary_tabs (lnk) - end - end - block_list: ITERABLE [like {CMS_BLOCK}.name] - local - l_string: STRING do - Result := <<"login">> - debug ("roc") - create l_string.make_empty - across - Result as ic - loop - l_string.append (ic.item) - l_string.append_character (' ') - end - write_debug_log (generator + ".block_list:" + l_string ) - end + Result := <<"?login">> end get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) do - if - a_block_id.is_case_insensitive_equal_general ("login") and then - a_response.location.starts_with ("account/roc-session-login") - then + if a_block_id.is_case_insensitive_equal_general ("login") then get_block_view_login (a_block_id, a_response) end end -feature {NONE} -- Helpers - - template_block (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE): detachable CMS_SMARTY_TEMPLATE_BLOCK - -- Smarty content block for `a_block_id' - local - p: detachable PATH - do - create p.make_from_string ("templates") - p := p.extended ("block_").appended (a_block_id).appended_with_extension ("tpl") - - p := a_response.api.module_theme_resource_location (Current, p) - if p /= Void then - if attached p.entry as e then - create Result.make (a_block_id, Void, p.parent, e) - else - create Result.make (a_block_id, Void, p.parent, p) - end - end - end - feature {NONE} -- Block views get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) @@ -315,7 +275,7 @@ feature {NONE} -- Block views if attached template_block (a_block_id, a_response) as l_tpl_block then create vals.make (1) -- add the variable to the block - value_table_alter (vals, a_response) + a_response.api.hooks.invoke_value_table_alter (vals, a_response) across vals as ic loop @@ -347,4 +307,5 @@ feature {NONE} -- Block views end Result := l_token end + end diff --git a/modules/session_auth/filter/cms_session_auth_filter.e b/modules/session_auth/filter/cms_session_auth_filter.e index 3e1b338..aedc0a2 100644 --- a/modules/session_auth/filter/cms_session_auth_filter.e +++ b/modules/session_auth/filter/cms_session_auth_filter.e @@ -9,15 +9,11 @@ class CMS_SESSION_AUTH_FILTER inherit - WSF_URI_TEMPLATE_HANDLER - - CMS_HANDLER + CMS_AUTH_FILTER_I rename - make as make_handler + make as make_filter end - WSF_FILTER - create make @@ -25,7 +21,7 @@ feature {NONE} -- Initialization make (a_api: CMS_API; a_session_api: CMS_SESSION_API) do - make_handler (a_api) + make_filter (a_api) session_api := a_session_api end @@ -33,15 +29,19 @@ feature {NONE} -- Initialization feature -- Basic operations - execute (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Execute the filter. + auth_strategy: STRING + do + Result := {CMS_SESSION_AUTH_MODULE}.logout_location + end + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- . do - -- A valid user if attached {WSF_STRING} req.cookie (session_api.session_token) as l_roc_auth_session_token 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 api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) end diff --git a/modules/session_auth/persistence/cms_session_auth_storage_sql.e b/modules/session_auth/persistence/cms_session_auth_storage_sql.e index e802b29..466b0dd 100644 --- a/modules/session_auth/persistence/cms_session_auth_storage_sql.e +++ b/modules/session_auth/persistence/cms_session_auth_storage_sql.e @@ -107,7 +107,7 @@ feature -- Change User token feature {NONE} -- SQL statements - Select_user_id_by_token: STRING = "SELECT u.uid FROM users as u JOIN auth_session 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;" sql_insert_session_auth: STRING = "INSERT INTO auth_session (uid, access_token, created) VALUES (:uid, :token, :utc_date);" diff --git a/modules/session_auth/site/templates/block_login.tpl b/modules/session_auth/site/templates/block_login.tpl index a344568..4243ff8 100644 --- a/modules/session_auth/site/templates/block_login.tpl +++ b/modules/session_auth/site/templates/block_login.tpl @@ -1,37 +1,23 @@ -
    - {unless isset="$user"} -

    Login or Register

    -
    -
    -
    -
    - - -
    - -
    - - -
    - -
    -
    -
    -
    +{unless isset="$user"} + + + {if isset="$error"}
    {$error/}
    {/if} +
    +{/unless} diff --git a/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e b/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e index c3ba3ac..9bdcb61 100644 --- a/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e +++ b/modules/taxonomy/handler/taxonomy_vocabulary_admin_handler.e @@ -72,7 +72,7 @@ feature -- HTTP Methods l_typename: READABLE_STRING_GENERAL s: STRING do - if not api.user_has_permission (current_user (req), "admin taxonomy") then + if not api.has_permission ("admin taxonomy") then send_access_denied (req, res) else if attached {WSF_STRING} req.form_parameter ("op") as p_op then @@ -161,7 +161,7 @@ feature -- HTTP Methods local tid: INTEGER_64 do - if not api.user_has_permission (current_user (req), "admin taxonomy") then + if not api.has_permission ("admin taxonomy") then send_access_denied (req, res) else if attached {WSF_STRING} req.path_parameter ("vocid") as p_vocid then diff --git a/src/kernel/content/cms_alias_block.e b/src/kernel/content/cms_alias_block.e index 60cc9fa..85dc8af 100644 --- a/src/kernel/content/cms_alias_block.e +++ b/src/kernel/content/cms_alias_block.e @@ -12,6 +12,9 @@ class inherit CMS_BLOCK + redefine + append_to_html + end create make_with_block @@ -47,6 +50,12 @@ feature -- Status report feature -- Conversion + append_to_html (a_theme: CMS_THEME; a_output: STRING_8) + -- Append HTML representation of Current block to `a_output'. + do + origin.append_to_html (a_theme, a_output) + end + to_html (a_theme: CMS_THEME): STRING_8 -- HTML representation of Current block. do @@ -54,6 +63,6 @@ feature -- Conversion end ;note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/kernel/content/cms_block.e b/src/kernel/content/cms_block.e index 74568fa..f80df8a 100644 --- a/src/kernel/content/cms_block.e +++ b/src/kernel/content/cms_block.e @@ -82,6 +82,12 @@ feature -- Element change feature -- Conversion + append_to_html (a_theme: CMS_THEME; a_output: STRING_8) + -- Append HTML representation of Current block to `a_output'. + do + a_output.append (to_html (a_theme)) + end + to_html (a_theme: CMS_THEME): STRING_8 -- HTML representation of Current block. deferred @@ -112,6 +118,6 @@ feature -- Status report end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/kernel/content/cms_cache_block.e b/src/kernel/content/cms_cache_block.e index 56d8aed..eb2fe39 100644 --- a/src/kernel/content/cms_cache_block.e +++ b/src/kernel/content/cms_cache_block.e @@ -78,7 +78,7 @@ feature -- Conversion end end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/src/kernel/content/cms_encoders.e b/src/kernel/content/cms_encoders.e index 5729d40..9085483 100644 --- a/src/kernel/content/cms_encoders.e +++ b/src/kernel/content/cms_encoders.e @@ -27,6 +27,16 @@ feature -- Encoders Result := html_encoder.general_encoded_string (a_string) end + safe_html_encoded (a_string: detachable READABLE_STRING_GENERAL): STRING_8 + -- `a_string' encoded for html output or empty string. + do + if a_string /= Void then + Result := html_encoded (a_string) + else + Result := "" + end + end + url_encoded, percent_encoded (a_string: READABLE_STRING_GENERAL): STRING_8 -- `a_string' encoded with percent encoding, mainly used for url. @@ -34,4 +44,7 @@ feature -- Encoders Result := percent_encoder.percent_encoded_string (a_string) end +note + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/kernel/content/cms_smarty_template_block.e b/src/kernel/content/cms_smarty_template_block.e index 7e53d9b..6abad17 100644 --- a/src/kernel/content/cms_smarty_template_block.e +++ b/src/kernel/content/cms_smarty_template_block.e @@ -190,6 +190,6 @@ feature -- Debug Result.append ("%N}") end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/persistence/user/cms_user_storage_i.e b/src/persistence/user/cms_user_storage_i.e index 0e28a43..29fb00e 100644 --- a/src/persistence/user/cms_user_storage_i.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -118,33 +118,8 @@ feature -- Change: user deferred end - feature -- Access: roles and permissions --- user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN --- -- Anonymous or user `u' has permission for `s' ? --- --| `s' could be "create page", --- do ----- if s = Void then ----- Result := True ----- elseif u = Void then ------- Result := user_role_has_permission (anonymous_user_role, s) ----- else ----- Result := user_role_has_permission (authenticated_user_role, s) ----- if not Result and attached u.roles as l_roles then ----- across ----- l_roles as r ----- until ----- Result ----- loop ----- if attached user_role_by_id (r.item) as ur then ----- Result := user_role_has_permission (ur, s) ----- end ----- end ----- end ----- end --- end - user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN do Result := a_role.has_permission (s) diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e index 2399479..0415325 100644 --- a/src/persistence/user/cms_user_storage_null.e +++ b/src/persistence/user/cms_user_storage_null.e @@ -76,7 +76,6 @@ feature -- Change: user do end - feature -- Access: roles and permissions user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 8273748..b93399e 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -267,13 +267,14 @@ feature -- Change: user sql_begin_transaction write_information_log (generator + ".update_user") - create l_parameters.make (6) + create l_parameters.make (7) l_parameters.put (a_user.id, "uid") l_parameters.put (a_user.name, "name") l_parameters.put (l_password_hash, "password") l_parameters.put (l_password_salt, "salt") l_parameters.put (l_email, "email") l_parameters.put (a_user.status, "status") + l_parameters.put (a_user.last_login_date, "signed") sql_modify (sql_update_user, l_parameters) sql_finalize @@ -307,6 +308,8 @@ feature -- Change: user sql_finalize end +feature -- Change: roles + update_user_roles (a_user: CMS_USER) -- Update roles of `a_user' require @@ -847,6 +850,9 @@ feature {NONE} -- Implementation: User if attached sql_read_integer_32 (6) as l_status then Result.set_status (l_status) end + if attached sql_read_date_time (8) as l_signed_date then + Result.set_last_login_date (l_signed_date) + end else check expected_valid_user: False end end @@ -903,7 +909,7 @@ feature {NONE} -- Sql Queries: USER sql_insert_user: STRING = "INSERT INTO users (name, password, salt, email, created, status) VALUES (:name, :password, :salt, :email, :created, :status);" -- SQL Insert to add a new user. - sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email, status=:status WHERE uid=:uid;" + sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email, status=:status, signed=:signed WHERE uid=:uid;" -- SQL update to update an existing user. sql_delete_user: STRING = "DELETE FROM users WHERE uid=:uid;" diff --git a/src/service/cms_api.e b/src/service/cms_api.e index cce86f2..66b9c14 100644 --- a/src/service/cms_api.e +++ b/src/service/cms_api.e @@ -9,22 +9,24 @@ class inherit ANY + CMS_ENCODERS + CMS_HOOK_EXPORT CMS_EXPORT_JSON_UTILITIES REFACTORING_HELPER - CMS_REQUEST_UTIL - create make feature {NONE} -- Initialize - make (a_setup: CMS_SETUP) - -- Create the API service with a setup `a_setup' + make (a_setup: CMS_SETUP; req: WSF_REQUEST) + -- Create the API service with a setup `a_setup' + -- and request `req'. do + request := req setup := a_setup create error_handler.make create {CMS_ENV_LOGGER} logger.make @@ -168,6 +170,12 @@ feature -- Access storage: CMS_STORAGE -- Default persistence storage. +feature {NONE} -- Access: request + + request: WSF_REQUEST + -- Associated http request. + --| note: here for the sole purpose of CMS_API. + feature -- Content content_types: ARRAYED_LIST [CMS_CONTENT_TYPE] @@ -375,6 +383,13 @@ feature {NONE} -- Emails implementation feature -- Permissions system + has_permission (a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN + -- Anonymous or user `user' has permission for `a_permission'? + --| `a_permission' could be for instance "create page". + do + Result := user_api.user_has_permission (user, a_permission) + end + user_has_permission (a_user: detachable CMS_USER; a_permission: detachable READABLE_STRING_GENERAL): BOOLEAN -- Anonymous or user `a_user' has permission for `a_permission'? --| `a_permission' could be for instance "create page". @@ -866,6 +881,113 @@ feature -- Hook end end +feature -- Access: active user + + user_is_authenticated: BOOLEAN + -- Is user authenticated? + do + Result := user /= Void + ensure + Result implies user /= Void + end + + user: detachable CMS_USER + -- Current user or Void in case of visitor. + note + EIS: "eiffel:?class=CMS_BASIC_AUTH_FILTER&feature=execute" + do + Result := current_user (request) + end + + set_user (a_user: CMS_USER) + -- Set `a_user' as current `user'. + require + a_user_attached: a_user /= Void + do + set_current_user (request, a_user) + end + + unset_user + -- Unset `user'. + do + unset_current_user (request) + end + + record_user_login (a_user: CMS_USER) + -- Record login event for `a_user'. + require + user_has_id: a_user.has_id + do + a_user.set_last_login_date_now + user_api.update_user (a_user) + end + +feature -- Request utilities + + execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY + -- Execution variable related to `a_name' + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + Result := request.execution_variable (a_name) + end + + set_execution_variable (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY) + do + request.set_execution_variable (a_name, a_value) + ensure + param_set: execution_variable (a_name) = a_value + end + + unset_execution_variable (a_name: READABLE_STRING_GENERAL) + do + request.unset_execution_variable (a_name) + ensure + param_unset: execution_variable (a_name) = Void + end + + +feature {CMS_API_ACCESS, CMS_RESPONSE, CMS_MODULE} -- Request utilities + + current_user (req: WSF_REQUEST): detachable CMS_USER + -- Current user or Void in case of Guest user. + do + check req = request end + if attached {CMS_USER} execution_variable (cms_execution_variable_name ("user")) as l_user then + Result := l_user + end + end + + set_current_user (req: WSF_REQUEST; a_user: CMS_USER) + -- Set `a_user' as `current_user'. + do + check req = request end + set_execution_variable (cms_execution_variable_name ("user"), a_user) + ensure + user_set: current_user (req) ~ a_user + end + + unset_current_user (req: WSF_REQUEST) + -- Unset current user. + do + check req = request end + req.unset_execution_variable (cms_execution_variable_name ("user")) + ensure + user_unset: current_user (req) = Void + end + +feature {NONE} -- Implementation: current user + + cms_execution_variable_name (a_name: READABLE_STRING_GENERAL): READABLE_STRING_GENERAL + -- Execution variable name for `a_name'. + local + s32: STRING_32 + do + create s32.make_from_string_general (once "_roccms_.") + s32.append_string_general (a_name) + Result := s32 + end + note copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/src/service/cms_execution.e b/src/service/cms_execution.e index bf54521..518bb28 100644 --- a/src/service/cms_execution.e +++ b/src/service/cms_execution.e @@ -43,7 +43,7 @@ feature {NONE} -- Initialization l_setup := initial_cms_setup setup_storage (l_setup) setup_modules (l_setup) - create api.make (l_setup) + create api.make (l_setup, request) modules := api.enabled_modules initialize_cms @@ -314,7 +314,7 @@ feature -- Execution end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/src/service/cms_module.e b/src/service/cms_module.e index dee526f..e0789be 100644 --- a/src/service/cms_module.e +++ b/src/service/cms_module.e @@ -9,6 +9,8 @@ deferred class inherit REFACTORING_HELPER + CMS_ENCODERS + feature -- Access is_enabled: BOOLEAN @@ -193,6 +195,6 @@ invariant version_set: not version.is_whitespace note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/handler/cms_handler.e b/src/service/handler/cms_handler.e index ae2f3b9..df6973d 100644 --- a/src/service/handler/cms_handler.e +++ b/src/service/handler/cms_handler.e @@ -11,7 +11,9 @@ deferred class inherit WSF_HANDLER - CMS_REQUEST_UTIL + CMS_API_ACCESS + + CMS_ENCODERS REFACTORING_HELPER @@ -93,4 +95,7 @@ feature -- Response helpers r.execute end +note + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/misc/cms_request_util.e b/src/service/misc/cms_request_util.e deleted file mode 100644 index bbf222d..0000000 --- a/src/service/misc/cms_request_util.e +++ /dev/null @@ -1,77 +0,0 @@ -note - description: "Set of helper features related to CMS Request needs." - date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" - revision: "$Revision: 96616 $" - -deferred class - CMS_REQUEST_UTIL - -inherit - CMS_ENCODERS - - REFACTORING_HELPER - -feature -- User - - current_user_name (req: WSF_REQUEST): detachable READABLE_STRING_32 - -- Current user name or Void in case of Guest users. - note - EIS: "src=eiffel:?class=AUTHENTICATION_FILTER&feature=execute" - do - if attached {CMS_USER} current_user (req) as l_user then - Result := l_user.name - end - end - - current_user (req: WSF_REQUEST): detachable CMS_USER - -- Current user or Void in case of Guest user. - -- note: if a CMS_RESPONSE is available, always prefer {CMS_RESPONSE}.user if relevant. - note - EIS: "eiffel:?class=AUTHENTICATION_FILTER&feature=execute" - do - if attached {CMS_USER} req.execution_variable (current_user_execution_variable_name) as l_user then - Result := l_user - end - end - -feature -- Change - - set_current_user (req: WSF_REQUEST; a_user: detachable CMS_USER) - -- Set `a_user' as `current_user'. - do - if a_user = Void then - req.unset_execution_variable (current_user_execution_variable_name) - else - req.set_execution_variable (current_user_execution_variable_name, a_user) - end - ensure - user_set: current_user (req) ~ a_user - end - - unset_current_user (req: WSF_REQUEST) - -- Unset current user. - do - req.unset_execution_variable (current_user_execution_variable_name) - ensure - user_unset: current_user (req) = Void - end - -feature {NONE} -- Implementation: current user - - current_user_execution_variable_name: STRING = "_cms_active_user_" - -- Execution variable name used to keep current user data. - -feature -- Media Type - - current_media_type (req: WSF_REQUEST): detachable READABLE_STRING_32 - -- Current media type or Void if it's not acceptable. - do - if attached {STRING} req.execution_variable ("media_type") as l_type then - Result := l_type - end - end - -note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" -end diff --git a/src/service/misc/cms_url_utilities.e b/src/service/misc/cms_url_utilities.e index 7f50911..467b33e 100644 --- a/src/service/misc/cms_url_utilities.e +++ b/src/service/misc/cms_url_utilities.e @@ -1,6 +1,5 @@ note - description: "Summary description for {CMS_URL_UTILITIES}." - author: "" + description: "Collection of helper routines to manipulate URL for CMS." date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" revision: "$Revision: 96616 $" @@ -8,7 +7,7 @@ deferred class CMS_URL_UTILITIES inherit - CMS_REQUEST_UTIL + CMS_ENCODERS feature -- Core @@ -43,12 +42,16 @@ feature -- Core feature -- Link link (a_text: detachable READABLE_STRING_GENERAL; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING + -- HTML link with title `a_text' and href `a_path'. + -- `opts' is used for additional settings. do create Result.make (32) append_link_to_html (a_text, a_path, opts, Result) end link_with_raw_text (a_raw_text: detachable READABLE_STRING_8; a_path: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING + -- HTML link with title the html code `a_raw_text' and href `a_path'. + -- `opts' is used for additional settings. do create Result.make (32) append_link_with_raw_text_to_html (a_raw_text, a_path, opts, Result) @@ -180,6 +183,7 @@ feature -- Url checked_url (a_url: READABLE_STRING_8): READABLE_STRING_8 do + -- FIXME: implement a way to check if `a_url' is safe, and does not reveal security issue. Result := a_url end @@ -189,6 +193,6 @@ feature -- Url end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/response/cms_response.e b/src/service/response/cms_response.e index 3eaafa5..cd05e1e 100644 --- a/src/service/response/cms_response.e +++ b/src/service/response/cms_response.e @@ -116,6 +116,9 @@ feature -- Access: metadata redirection: detachable READABLE_STRING_8 -- Location for eventual redirection. + redirection_delay: NATURAL + -- Optional redirection delay in seconds. + feature -- Access: query location: STRING_8 @@ -196,8 +199,23 @@ feature -- User access end user: detachable CMS_USER + -- Active user if authenticated. do - Result := current_user (request) + Result := api.user + end + + set_user (u: CMS_USER) + -- Set active user to `u'. + require + attached_u: u /= Void + do + api.set_user (u) + end + + unset_user + -- Unset active user. + do + api.unset_user end feature -- Permission @@ -356,6 +374,11 @@ feature -- Element change redirection := a_location end + set_redirection_delay (nb_secs: NATURAL) + do + redirection_delay := nb_secs + end + feature -- Logging log (a_category: READABLE_STRING_8; a_message: READABLE_STRING_8; a_level: INTEGER; a_link: detachable CMS_LINK) @@ -1212,8 +1235,8 @@ feature -- Generation page.register_variable (absolute_url ("", Void), "site_url") page.register_variable (absolute_url ("", Void), "host") -- Same as `site_url'. page.register_variable (request.is_https, "is_https") - if attached current_user_name (request) as l_user then - page.register_variable (l_user, "user") + if attached user as l_user then + page.register_variable (l_user.name, "user") end page.register_variable (title, "site_title") page.set_is_front (is_front) @@ -1326,16 +1349,40 @@ feature -- Helpers: cms link end end - user_html_link (u: CMS_USER): like link +feature -- Helpers: html links + + user_html_link (u: CMS_USER): STRING do Result := link (u.name, "user/" + u.id.out, Void) end +feature -- Helpers: URLs + + location_absolute_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING + -- Absolute URL for `a_location'. + --| Options `opts' could be + --| - absolute: True|False => return absolute url + --| - query: string => append "?query" + --| - fragment: string => append "#fragment" + do + Result := absolute_url (a_location, opts) + end + + location_url (a_location: READABLE_STRING_8; opts: detachable CMS_API_OPTIONS): STRING + -- URL for `a_location'. + --| Options `opts' could be + --| - absolute: True|False => return absolute url + --| - query: string => append "?query" + --| - fragment: string => append "#fragment" + do + Result := url (a_location, opts) + end + user_url (u: CMS_USER): like url require u_with_id: u.has_id do - Result := url ("user/" + u.id.out, Void) + Result := location_url ("user/" + u.id.out, Void) end feature -- Execution @@ -1363,8 +1410,22 @@ feature {NONE} -- Execution page: CMS_HTML_PAGE_RESPONSE utf: UTF_CONVERTER h: HTTP_HEADER - l_new_location: READABLE_STRING_8 + l_new_location: detachable READABLE_STRING_8 + l_redirection_delay: like redirection_delay do + if attached redirection as l_location then + -- FIXME: find out if this is safe or not. + if l_location.has_substring ("://") then + l_new_location := l_location + else + l_new_location := location_absolute_url (l_location, Void) + end + l_redirection_delay := redirection_delay + if l_redirection_delay > 0 then + add_additional_head_line ("", True) + end + end + if attached {READABLE_STRING_GENERAL} values.item ("optional_content_type") as l_type then create cms_page.make_typed (utf.utf_32_string_to_utf_8_string_8 (l_type)) else @@ -1376,14 +1437,7 @@ feature {NONE} -- Execution h := page.header h.put_content_length (page.html.count) h.put_current_date - if attached redirection as l_location then - -- FIXME: find out if this is safe or not. - if l_location.has_substring ("://") then - l_new_location := l_location - else - l_new_location := absolute_url (l_location, Void) - end --- h.put_location (l_new_location) + if l_new_location /= Void and l_redirection_delay = 0 then response.redirect_now (l_new_location) else h.put_header_object (header) diff --git a/src/service/response/error/not_found_error_cms_response.e b/src/service/response/error/not_found_error_cms_response.e index 8f3ab3e..c8badeb 100644 --- a/src/service/response/error/not_found_error_cms_response.e +++ b/src/service/response/error/not_found_error_cms_response.e @@ -33,10 +33,10 @@ feature -- Execution do set_title ("Not Found") set_page_title ("Not Found") - set_main_content ("The requested page could not be found.") + set_main_content ("The requested page %"" + request.request_uri + "%"could not be found.") end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/src/service/user/cms_user_api.e b/src/service/user/cms_user_api.e index 7bd9b94..2a5ebdc 100644 --- a/src/service/user/cms_user_api.e +++ b/src/service/user/cms_user_api.e @@ -14,7 +14,7 @@ inherit create make -feature -- Access +feature -- Access: user user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER -- User by id `a_id', if any. @@ -58,6 +58,44 @@ feature -- Access Result := storage.recent_users (params.offset.to_integer_32, params.size.to_integer_32) end +feature -- Change User + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + require + no_id: not a_user.has_id + no_hashed_password: a_user.hashed_password = Void + do + reset_error + if + attached a_user.email as l_email + then + storage.new_user (a_user) + error_handler.append (storage.error_handler) + else + error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!") + end + end + + update_user (a_user: CMS_USER) + -- Update user `a_user'. + require + has_id: a_user.has_id + do + reset_error + storage.update_user (a_user) + error_handler.append (storage.error_handler) + end + + delete_user (a_user: CMS_USER) + -- Delete user `a_user'. + require + has_id: a_user.has_id + do + reset_error + storage.delete_user (a_user) + error_handler.append (storage.error_handler) + end feature -- Status report @@ -241,45 +279,6 @@ feature -- Change User role error_handler.append (storage.error_handler) end -feature -- Change User - - new_user (a_user: CMS_USER) - -- Add a new user `a_user'. - require - no_id: not a_user.has_id - no_hashed_password: a_user.hashed_password = Void - do - reset_error - if - attached a_user.email as l_email - then - storage.new_user (a_user) - error_handler.append (storage.error_handler) - else - error_handler.add_custom_error (0, "bad new user request", "Missing password or email to create new user!") - end - end - - update_user (a_user: CMS_USER) - -- Update user `a_user'. - require - has_id: a_user.has_id - do - reset_error - storage.update_user (a_user) - error_handler.append (storage.error_handler) - end - - delete_user (a_user: CMS_USER) - -- Delete user `a_user'. - require - has_id: a_user.has_id - do - reset_error - storage.delete_user (a_user) - error_handler.append (storage.error_handler) - end - feature -- User Activation new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) diff --git a/tpl/site/scripts/user.sql b/tpl/site/scripts/user.sql index 161642d..e9efdc1 100644 --- a/tpl/site/scripts/user.sql +++ b/tpl/site/scripts/user.sql @@ -46,7 +46,6 @@ CREATE TABLE `users_password_recovery` ( CONSTRAINT `token` UNIQUE (`token`) ); - CREATE TABLE `auth_temp_users` ( `uid` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, `name` VARCHAR(100) NOT NULL,