From 032cc5bdcb4b4f1ab83ea5dfcf099e57d6aae371 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 5 Jun 2015 18:39:27 -0300 Subject: [PATCH 1/8] Updated CMS with Login Module. -- The module handle basic_auth (at the moment). -- Handle login, logout, register user, activate/reactivate an account, password recovery. -- Send notification emails. CMS Updates -- Added a new service: email. -- Updated Basic Auth Module to handle logout based on the browser type. -- Updated persistence layer to save and remove and query activation token and password token. -- Updated CMS_USER to handle status {active, not_active, trashed}. -- Updated MySQL scripts to be in sync with SQLite scripts --- cms-safe.ecf | 1 + cms.ecf | 1 + examples/demo/demo-safe.ecf | 1 + examples/demo/site/config/login/login.json | 5 + examples/demo/site/scripts/user.sql | 16 + .../themes/bootstrap/assets/js/roc_auth.js | 18 +- .../modules/login/templates/block_login.tpl | 32 + .../login/templates/block_new_password.tpl | 16 + .../login/templates/block_post_password.tpl | 3 + .../login/templates/block_post_reactivate.tpl | 3 + .../login/templates/block_post_register.tpl | 3 + .../login/templates/block_post_reset.tpl | 3 + .../login/templates/block_reactivate.tpl | 19 + .../login/templates/block_register.tpl | 28 + .../login/templates/block_reset_password.tpl | 28 + examples/demo/src/ewf_roc_server.e | 5 + library/model/src/user/cms_user.e | 62 ++ library/persistence/mysql/scripts/core.sql | 30 + .../mysql/scripts/create_database.sql | 163 ----- library/persistence/mysql/scripts/node.sql | 24 + library/persistence/mysql/scripts/schema.sql | 72 -- library/persistence/mysql/scripts/tables.sql | 14 - .../persistence/mysql/scripts/triggers.sql | 8 - library/persistence/mysql/scripts/user.sql | 66 ++ modules/basic_auth/basic_auth_module.e | 14 +- .../handler/basic_auth_logoff_handler.e | 55 +- modules/login/login-safe.ecf | 23 + modules/login/login_email_service.e | 45 ++ .../login/login_email_service_parameters.e | 73 ++ modules/login/login_module.e | 675 ++++++++++++++++++ src/persistence/sql/cms_storage_sql_builder.e | 4 + src/persistence/user/cms_user_storage_i.e | 37 + src/persistence/user/cms_user_storage_null.e | 31 + src/persistence/user/cms_user_storage_sql_i.e | 180 ++++- src/service/email/email_service.e | 102 +++ src/service/email/email_service_parameters.e | 20 + src/service/user/cms_user_api.e | 57 ++ 37 files changed, 1660 insertions(+), 277 deletions(-) create mode 100644 examples/demo/site/config/login/login.json create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_post_password.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reactivate.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_post_register.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reset.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl create mode 100644 examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl create mode 100644 library/persistence/mysql/scripts/core.sql delete mode 100644 library/persistence/mysql/scripts/create_database.sql create mode 100644 library/persistence/mysql/scripts/node.sql delete mode 100644 library/persistence/mysql/scripts/schema.sql delete mode 100644 library/persistence/mysql/scripts/tables.sql delete mode 100644 library/persistence/mysql/scripts/triggers.sql create mode 100644 library/persistence/mysql/scripts/user.sql create mode 100644 modules/login/login-safe.ecf create mode 100644 modules/login/login_email_service.e create mode 100644 modules/login/login_email_service_parameters.e create mode 100644 modules/login/login_module.e create mode 100644 src/service/email/email_service.e create mode 100644 src/service/email/email_service_parameters.e diff --git a/cms-safe.ecf b/cms-safe.ecf index d1c2b3c..1fa90e4 100644 --- a/cms-safe.ecf +++ b/cms-safe.ecf @@ -28,6 +28,7 @@ + diff --git a/cms.ecf b/cms.ecf index 4b9f7d6..a4bc8d3 100644 --- a/cms.ecf +++ b/cms.ecf @@ -28,6 +28,7 @@ + diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index c760a4f..a5c8f59 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -15,6 +15,7 @@ + diff --git a/examples/demo/site/config/login/login.json b/examples/demo/site/config/login/login.json new file mode 100644 index 0000000..5b44da3 --- /dev/null +++ b/examples/demo/site/config/login/login.json @@ -0,0 +1,5 @@ +{ + "email": "webmaster@example.com", + "subjet": "Thank you for regitering with us", + "smtp": "127.0.0.1" +} diff --git a/examples/demo/site/scripts/user.sql b/examples/demo/site/scripts/user.sql index 707dbc2..3868949 100644 --- a/examples/demo/site/scripts/user.sql +++ b/examples/demo/site/scripts/user.sql @@ -31,4 +31,20 @@ CREATE TABLE "role_permissions"( "module" VARCHAR(255) ); +CREATE TABLE "users_activations" ( + "aid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("aid" >= 0), + "token" VARCHAR(255) NOT NULL, + "uid" INTEGER NOT NULL CHECK ("uid" >= 0), + "created" DATETIME NOT NULL, + CONSTRAINT "token" UNIQUE ("token") +); + +CREATE TABLE "users_password_recovery" ( + "aid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("aid" >= 0), + "token" VARCHAR(255) NOT NULL, + "uid" INTEGER NOT NULL CHECK ("uid" >= 0), + "created" DATETIME NOT NULL, + CONSTRAINT "token" UNIQUE ("token") +); + COMMIT; diff --git a/examples/demo/site/themes/bootstrap/assets/js/roc_auth.js b/examples/demo/site/themes/bootstrap/assets/js/roc_auth.js index 06f0731..75382ef 100644 --- a/examples/demo/site/themes/bootstrap/assets/js/roc_auth.js +++ b/examples/demo/site/themes/bootstrap/assets/js/roc_auth.js @@ -1,7 +1,8 @@ var ROC_AUTH = ROC_AUTH || { }; -var loginURL = "/login"; -var logoutURL = "/logoff"; +var loginURL = "/basic_auth_login"; +var logoutURL = "/basic_auth_logoff"; + var userAgent = navigator.userAgent.toLowerCase(); var firstLogIn = true; @@ -305,3 +306,16 @@ ROC_AUTH.create_form = function() { }; +var password = document.getElementById("password") + , confirm_password = document.getElementById("confirm_password"); + +ROC_AUTH.validatePassword =function(){ + if(password.value != confirm_password.value) { + confirm_password.setCustomValidity("Passwords Don't Match"); + } else { + confirm_password.setCustomValidity(''); + } +} + +password.onchange = ROC_AUTH.validatePassword(); +confirm_password.onkeyup = ROC_AUTH.validatePassword; \ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl new file mode 100644 index 0000000..495fdd8 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl @@ -0,0 +1,32 @@ +
+ {if isset="$user"} +

Logout

+ {/if} + {unless isset="$user"} +

Login or Register

+
+
+
+
+ + +
+ +
+ + +
+ + +
+
+
+
+ +
+ {/unless} +
\ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl new file mode 100644 index 0000000..84b5de0 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl @@ -0,0 +1,16 @@ +
+
+
+ Require new password +
+ + + {if isset="$error_email"} + {$error_email/}
+ {/if} +
+
+ +
+
+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_password.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_password.tpl new file mode 100644 index 0000000..0ec7a7c --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_password.tpl @@ -0,0 +1,3 @@ +
+

We have send you a new token code, check your email to generate a new password

+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reactivate.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reactivate.tpl new file mode 100644 index 0000000..09e7206 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reactivate.tpl @@ -0,0 +1,3 @@ +
+

We have send you a new activation code, check your email to activate your account.

+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_register.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_register.tpl new file mode 100644 index 0000000..d59f75a --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_register.tpl @@ -0,0 +1,3 @@ +
+

Thanks for register, check your email to activate your account.

+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reset.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reset.tpl new file mode 100644 index 0000000..9ccecfb --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_post_reset.tpl @@ -0,0 +1,3 @@ +
+

You new password has been saved!

+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl new file mode 100644 index 0000000..01a7960 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl @@ -0,0 +1,19 @@ +
+
+
+ Reactivate Form +
+ + + {if isset="$error_email"} + {$error_email/}
+ {/if} +
+ {if isset="$is_active"} + {$is_active/}
+ {/if} +
+ +
+
+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl new file mode 100644 index 0000000..522bde9 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl @@ -0,0 +1,28 @@ +
+
+
+ Register Form +
+ + + {if isset="$error_name"} + {$error_name/}
+ {/if} +
+
+ + +
+
+ + + {if isset="$error_email"} + {$error_email/}
+ {/if} +
+ + + +
+
+
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl new file mode 100644 index 0000000..aa2ee81 --- /dev/null +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl @@ -0,0 +1,28 @@ +
+
+
+ Generate New Password Form +
+ + + {if isset="$error_token"} + {$error_token/}
+ {/if} +
+
+ + +
+
+ + +
+ + + {if isset="$error_password"} + {$error_password/}
+ {/if} + +
+
+
diff --git a/examples/demo/src/ewf_roc_server.e b/examples/demo/src/ewf_roc_server.e index d284825..86a7dd4 100644 --- a/examples/demo/src/ewf_roc_server.e +++ b/examples/demo/src/ewf_roc_server.e @@ -135,6 +135,11 @@ feature -- CMS setup m.enable a_setup.register_module (m) + create {LOGIN_MODULE} m.make + m.enable + a_setup.register_module (m) + + create {BASIC_AUTH_MODULE} m.make if not a_setup.module_with_same_type_registered (m) then m.enable diff --git a/library/model/src/user/cms_user.e b/library/model/src/user/cms_user.e index 5abb436..713ca65 100644 --- a/library/model/src/user/cms_user.e +++ b/library/model/src/user/cms_user.e @@ -27,6 +27,7 @@ feature {NONE} -- Initialization initialize ensure name_set: name = a_name + status_not_active: status = not_active end make_with_id (a_id: INTEGER_64) @@ -38,11 +39,13 @@ feature {NONE} -- Initialization initialize ensure id_set: id = a_id + status_not_active: status = not_active end initialize do create creation_date.make_now_utc + mark_not_active end feature -- Access @@ -71,6 +74,13 @@ feature -- Access last_login_date: detachable DATE_TIME -- User last login. + status: INTEGER + -- Associated status for the current user. + -- default: not_active + -- active + -- trashed + + feature -- Roles roles: detachable LIST [CMS_USER_ROLE] @@ -118,6 +128,12 @@ feature -- Status report Result := other /= Void and then id = other.id end + is_active: BOOLEAN + -- is the current user active? + do + Result := status = {CMS_USER}.active + end + feature -- Change element set_id (a_id: like id) @@ -225,6 +241,52 @@ feature -- Change element: data end end +feature -- Status change + + mark_not_active + -- Set status to not_active + do + set_status (not_active) + ensure + status_not_active: status = not_active + end + + mark_active + -- Set status to active. + do + set_status (active) + ensure + status_active: status = active + end + + mark_trashed + -- Set status to trashed. + do + set_status (trashed) + ensure + status_trash: status = trashed + end + + set_status (a_status: like status) + -- Assign `status' with `a_status'. + do + status := a_status + ensure + status_set: status = a_status + end + + +feature -- User status + + not_active: INTEGER = 0 + -- The user is not active. + + active: INTEGER = 1 + -- The user is active + + Trashed: INTEGER = -1 + -- The user is trashed (soft delete), ready to be deleted/destroyed from storage. + invariant id_or_name_set: id > 0 or else not name.is_whitespace diff --git a/library/persistence/mysql/scripts/core.sql b/library/persistence/mysql/scripts/core.sql new file mode 100644 index 0000000..366ccbd --- /dev/null +++ b/library/persistence/mysql/scripts/core.sql @@ -0,0 +1,30 @@ +BEGIN; + +CREATE TABLE `logs` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `category` VARCHAR(255) NOT NULL, + `level` int(11) NOT NULL, + `uid` int(11) DEFAULT NULL, + `message` text NOT NULL, + `info` text, + `link` text, + `date` datetime NOT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE `custom_values` ( + `type` VARCHAR(255) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `value` VARCHAR(255) NOT NULL +); + +CREATE TABLE `path_aliases` ( + `pid` int(11) NOT NULL AUTO_INCREMENT, + `source` varchar(255) NOT NULL, + `alias` varchar(255) NOT NULL, + `lang` varchar(12) DEFAULT NULL, + PRIMARY KEY (`pid`) +); + +COMMIT; + diff --git a/library/persistence/mysql/scripts/create_database.sql b/library/persistence/mysql/scripts/create_database.sql deleted file mode 100644 index a78dd4e..0000000 --- a/library/persistence/mysql/scripts/create_database.sql +++ /dev/null @@ -1,163 +0,0 @@ -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; -SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; - --- ----------------------------------------------------- --- Schema mydb --- ----------------------------------------------------- --- ----------------------------------------------------- --- Schema cms_dev --- ----------------------------------------------------- -CREATE SCHEMA IF NOT EXISTS `cms_dev` DEFAULT CHARACTER SET latin1 ; -USE `cms_dev` ; - --- ----------------------------------------------------- --- Table `cms_dev`.`users` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`users` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `username` VARCHAR(100) NOT NULL, - `password` VARCHAR(100) NOT NULL, - `salt` VARCHAR(100) NOT NULL, - `email` VARCHAR(250) NOT NULL, - `creation_date` DATETIME NULL DEFAULT NULL, - `last_login_date` DATETIME NULL DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `username` (`username` ASC)) -ENGINE = InnoDB -AUTO_INCREMENT = 2 -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `cms_dev`.`nodes` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `publication_date` DATE NOT NULL, - `creation_date` DATE NOT NULL, - `modification_date` DATE NOT NULL, - `title` VARCHAR(255) NOT NULL, - `summary` TEXT NOT NULL, - `content` MEDIUMTEXT NOT NULL, - `author_id` INT(10) UNSIGNED NULL DEFAULT NULL, - `version` INT(10) UNSIGNED ZEROFILL NULL DEFAULT NULL, - `editor_id` INT(10) UNSIGNED NULL DEFAULT NULL, - PRIMARY KEY (`id`), - INDEX `fk_nodes_users1_idx` (`author_id` ASC), - INDEX `fk_nodes_users2_idx` (`editor_id` ASC), - CONSTRAINT `fk_nodes_users1` - FOREIGN KEY (`author_id`) - REFERENCES `cms_dev`.`users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_nodes_users2` - FOREIGN KEY (`editor_id`) - REFERENCES `cms_dev`.`users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -AUTO_INCREMENT = 11 -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `cms_dev`.`roles` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`roles` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `role` VARCHAR(100) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `role` (`role` ASC)) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `cms_dev`.`permissions` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`permissions` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `name` VARCHAR(45) NOT NULL, - `roles_id` INT(10) UNSIGNED NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `name_UNIQUE` (`name` ASC), - INDEX `fk_permissions_roles1_idx` (`roles_id` ASC), - CONSTRAINT `fk_permissions_roles1` - FOREIGN KEY (`roles_id`) - REFERENCES `cms_dev`.`roles` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `cms_dev`.`profiles` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`profiles` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `key` VARCHAR(45) NOT NULL, - `value` VARCHAR(100) NULL DEFAULT NULL, - `users_id` INT(10) UNSIGNED NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `key_UNIQUE` (`key` ASC), - INDEX `fk_profiles_users1_idx` (`users_id` ASC), - CONSTRAINT `fk_profiles_users1` - FOREIGN KEY (`users_id`) - REFERENCES `cms_dev`.`users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `cms_dev`.`users_nodes` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`users_nodes` ( - `users_id` INT(10) UNSIGNED NOT NULL, - `nodes_id` INT(10) UNSIGNED NOT NULL, - PRIMARY KEY (`users_id`, `nodes_id`), - INDEX `fk_users_has_nodes_nodes1_idx` (`nodes_id` ASC), - INDEX `fk_users_has_nodes_users_idx` (`users_id` ASC), - CONSTRAINT `fk_users_has_nodes_nodes1` - FOREIGN KEY (`nodes_id`) - REFERENCES `cms_dev`.`nodes` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_users_has_nodes_users` - FOREIGN KEY (`users_id`) - REFERENCES `cms_dev`.`users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `cms_dev`.`users_roles` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`users_roles` ( - `users_id` INT(10) UNSIGNED NOT NULL, - `roles_id` INT(10) UNSIGNED NOT NULL, - PRIMARY KEY (`users_id`, `roles_id`), - INDEX `fk_users_has_roles_roles1_idx` (`roles_id` ASC), - INDEX `fk_users_has_roles_users1_idx` (`users_id` ASC), - CONSTRAINT `fk_users_has_roles_roles1` - FOREIGN KEY (`roles_id`) - REFERENCES `cms_dev`.`roles` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_users_has_roles_users1` - FOREIGN KEY (`users_id`) - REFERENCES `cms_dev`.`users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - -SET SQL_MODE=@OLD_SQL_MODE; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/library/persistence/mysql/scripts/node.sql b/library/persistence/mysql/scripts/node.sql new file mode 100644 index 0000000..a4a053e --- /dev/null +++ b/library/persistence/mysql/scripts/node.sql @@ -0,0 +1,24 @@ +BEGIN; + +CREATE TABLE nodes ( + nid INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK( nid >=0), + revision INTEGER, + type TEXT NOT NULL, + title VARCHAR(255) NOT NULL, + summary TEXT, + content MEDIUMTEXT NOT NULL, + format VARCHAR(255), + author INTEGER, + publish DATETIME, + created DATETIME NOT NULL, + changed DATETIME NOT NULL, + status INTEGER +); + +CREATE TABLE page_nodes( + nid INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL CHECK( nid >=0), + revision INTEGER, + parent INTEGER +); + +COMMIT; diff --git a/library/persistence/mysql/scripts/schema.sql b/library/persistence/mysql/scripts/schema.sql deleted file mode 100644 index 382707e..0000000 --- a/library/persistence/mysql/scripts/schema.sql +++ /dev/null @@ -1,72 +0,0 @@ -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; -SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; - --- ----------------------------------------------------- --- Schema mydb --- ----------------------------------------------------- --- ----------------------------------------------------- --- Schema roc_cms --- ----------------------------------------------------- -DROP SCHEMA IF EXISTS `roc_cms` ; -CREATE SCHEMA IF NOT EXISTS `roc_cms` DEFAULT CHARACTER SET latin1 ; -USE `roc_cms` ; - --- ----------------------------------------------------- --- Table `roc_cms`.`nodes` --- ----------------------------------------------------- -DROP TABLE IF EXISTS `roc_cms`.`nodes` ; - -CREATE TABLE IF NOT EXISTS `roc_cms`.`nodes` ( - `nid` INT(11) NOT NULL AUTO_INCREMENT, - `version` INT(11) NULL DEFAULT NULL, - `type` INT(11) NULL DEFAULT NULL, - `title` VARCHAR(255) NOT NULL, - `summary` TEXT NOT NULL, - `content` MEDIUMTEXT NOT NULL, - `author` INT(11) NULL DEFAULT NULL, - `publish` DATETIME NULL DEFAULT NULL, - `created` DATETIME NOT NULL, - `changed` DATETIME NOT NULL, - PRIMARY KEY (`nid`)) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `roc_cms`.`users` --- ----------------------------------------------------- -DROP TABLE IF EXISTS `roc_cms`.`users` ; - -CREATE TABLE IF NOT EXISTS `roc_cms`.`users` ( - `uid` INT(11) NOT NULL AUTO_INCREMENT, - `name` VARCHAR(100) NOT NULL, - `password` VARCHAR(100) NOT NULL, - `salt` VARCHAR(100) NOT NULL, - `email` VARCHAR(250) NOT NULL, - `status` INT(11) NULL DEFAULT NULL, - `created` DATETIME NOT NULL, - `signed` DATETIME NULL DEFAULT NULL, - PRIMARY KEY (`uid`), - UNIQUE INDEX `name` (`name` ASC)) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `roc_cms`.`users_roles` --- ----------------------------------------------------- -DROP TABLE IF EXISTS `roc_cms`.`users_roles` ; - -CREATE TABLE IF NOT EXISTS `roc_cms`.`users_roles` ( - `rid` INT(11) NOT NULL AUTO_INCREMENT, - `role` VARCHAR(100) NOT NULL, - PRIMARY KEY (`rid`), - UNIQUE INDEX `role` (`role` ASC)) -ENGINE = InnoDB -DEFAULT CHARACTER SET = latin1; - - -SET SQL_MODE=@OLD_SQL_MODE; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/library/persistence/mysql/scripts/tables.sql b/library/persistence/mysql/scripts/tables.sql deleted file mode 100644 index 4b1a4ba..0000000 --- a/library/persistence/mysql/scripts/tables.sql +++ /dev/null @@ -1,14 +0,0 @@ -DROP TABLE IF EXISTS nodes; - -CREATE TABLE nodes -( - id smallint unsigned NOT NULL auto_increment, - publication_date date NOT NULL, #When the article was published - creation_date date NOT NULL, #When the article was created - modification_date date NOT NULL, #When the article was updated - title varchar(255) NOT NULL, #Full title of the article - summary text NOT NULL, #A short summary of the articule - content mediumtext NOT NULL, #The HTML content of the article - - PRIMARY KEY (ID) -); \ No newline at end of file diff --git a/library/persistence/mysql/scripts/triggers.sql b/library/persistence/mysql/scripts/triggers.sql deleted file mode 100644 index b3e10a4..0000000 --- a/library/persistence/mysql/scripts/triggers.sql +++ /dev/null @@ -1,8 +0,0 @@ -DELIMITER $$ -CREATE TRIGGER update_editor -AFTER INSERT ON `users_nodes` FOR EACH ROW - UPDATE Nodes - SET editor_id = NEW.users_id - WHERE id = NEW.nodes_id; -$$ -DELIMITER ; \ No newline at end of file diff --git a/library/persistence/mysql/scripts/user.sql b/library/persistence/mysql/scripts/user.sql new file mode 100644 index 0000000..e99072a --- /dev/null +++ b/library/persistence/mysql/scripts/user.sql @@ -0,0 +1,66 @@ +BEGIN; + +CREATE TABLE `users` ( + `uid` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `password` varchar(100) NOT NULL, + `salt` varchar(100) NOT NULL, + `email` varchar(250) NOT NULL, + `status` int(11) DEFAULT NULL, + `created` datetime NOT NULL, + `signed` datetime DEFAULT NULL, + CHECK (`uid` >= 0), + PRIMARY KEY (`uid`), + UNIQUE KEY `name` (`name`) +); + +CREATE TABLE `roles` ( + `rid` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + CHECK (`rid` >= 0), + PRIMARY KEY (`rid`), + UNIQUE KEY `name` (`name`) +); + + +CREATE TABLE `users_roles` ( + `uid` int(11) NOT NULL, + `rid` int(11) NOT NULL, + CHECK (`uid` >= 0), + CHECK (`rid` >= 0) +); + +CREATE TABLE `role_permissions` ( + `rid` int(11) NOT NULL, + `permission` varchar(255) NOT NULL, + `module` varchar(255) DEFAULT NULL, + CHECK (`rid` >= 0) +); + + +CREATE TABLE `users_activations` ( + `aid` int(11) NOT NULL AUTO_INCREMENT, + `token` varchar(255) NOT NULL, + `uid` int(11) NOT NULL, + `created` datetime NOT NULL, + CHECK (`aid` >= 0), + CHECK (`uid` >= 0), + PRIMARY KEY (`aid`), + UNIQUE KEY `token` (`token`) +); + + +CREATE TABLE `users_password_recovery` ( + `aid` int(11) NOT NULL AUTO_INCREMENT, + `token` varchar(255) NOT NULL, + `uid` int(11) NOT NULL, + `created` datetime NOT NULL, + CHECK (`aid` >= 0), + CHECK (`uid` >= 0), + PRIMARY KEY (`aid`), + UNIQUE KEY `token` (`token`) +); + + + +COMMIT; \ No newline at end of file diff --git a/modules/basic_auth/basic_auth_module.e b/modules/basic_auth/basic_auth_module.e index 515cac7..0d9d649 100644 --- a/modules/basic_auth/basic_auth_module.e +++ b/modules/basic_auth/basic_auth_module.e @@ -108,14 +108,14 @@ feature -- Hooks local lnk: CMS_LOCAL_LINK do - if attached a_response.current_user (a_response.request) as u then - create lnk.make (u.name + " (Logout)", "basic_auth_logoff?destination=" + a_response.request.request_uri) - else - create lnk.make ("Login", "basic_auth_login?destination=" + a_response.request.request_uri) - end +-- if attached a_response.current_user (a_response.request) as u then +-- create lnk.make (u.name + " (Logout)", "basic_auth_logoff?destination=" + a_response.request.request_uri) +-- else +-- create lnk.make ("Login", "basic_auth_login?destination=" + a_response.request.request_uri) +-- end -- if not a_menu_system.primary_menu.has (lnk) then - lnk.set_weight (99) - a_menu_system.primary_menu.extend (lnk) +-- lnk.set_weight (99) +-- a_menu_system.primary_menu.extend (lnk) -- end end diff --git a/modules/basic_auth/handler/basic_auth_logoff_handler.e b/modules/basic_auth/handler/basic_auth_logoff_handler.e index 40ca330..5e8a667 100644 --- a/modules/basic_auth/handler/basic_auth_logoff_handler.e +++ b/modules/basic_auth/handler/basic_auth_logoff_handler.e @@ -55,21 +55,62 @@ feature -- HTTP Methods else create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, api) unset_current_user (req) - l_page.set_status_code ({HTTP_STATUS_CODE}.found) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection - if attached {WSF_STRING} req.query_parameter ("destination") as l_uri then - l_url := req.absolute_script_url (l_uri.url_encoded_value) - else - l_url := req.absolute_script_url ("") - end + l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) -- Note: can not use {HTTP_STATUS_CODE}.unauthorized for redirection + l_url := req.absolute_script_url ("") i := l_url.substring_index ("://", 1) if i > 0 then -- Note: this is a hack to have the logout effective on various browser -- (firefox requires this). l_url.replace_substring ("://_logout_basic_auth_@", i, i + 2) end - l_page.set_redirection (l_url) + if + attached req.http_user_agent as l_user_agent and then + browser_name (l_user_agent).is_case_insensitive_equal_general ("Firefox") + then + -- Set status to refirect + -- and redirect to the host page. + l_page.set_status_code ({HTTP_STATUS_CODE}.found) + l_page.set_redirection (l_url) + end l_page.execute end end + + browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32 + -- Browser name. + -- Must contain Must not contain + -- Firefox Firefox/xyz Seamonkey/xyz + -- Seamonkey Seamonkey/xyz + -- Chrome Chrome/xyz Chromium/xyz + -- Chromium Chromium/xyz + -- Safari Safari/xyz Chrome/xyz + -- Chromium/xyz + -- Opera OPR/xyz [1] + -- Opera/xyz [2] + -- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format + + do + if + a_user_agent.has_substring ("Firefox") and then + not a_user_agent.has_substring ("Seamonkey") + then + Result := "Firefox" + elseif a_user_agent.has_substring ("Seamonkey") then + Result := "Seamonkey" + elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then + Result := "Chrome" + elseif a_user_agent.has_substring ("Chromium") then + Result := "Chromiun" + elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then + Result := "Safari" + elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then + Result := "Opera" + elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then + Result := "Internet Explorer" + else + Result := "Unknown" + end + end + end diff --git a/modules/login/login-safe.ecf b/modules/login/login-safe.ecf new file mode 100644 index 0000000..eaba1d4 --- /dev/null +++ b/modules/login/login-safe.ecf @@ -0,0 +1,23 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + diff --git a/modules/login/login_email_service.e b/modules/login/login_email_service.e new file mode 100644 index 0000000..2ada59e --- /dev/null +++ b/modules/login/login_email_service.e @@ -0,0 +1,45 @@ +note + description: "Summary description for {LOGIN_EMAIL_SERVICE}." + date: "$Date$" + revision: "$Revision$" + +class + LOGIN_EMAIL_SERVICE + +inherit + EMAIL_SERVICE + redefine + initialize, + parameters + end + +create + make + +feature {NONE} -- Initialization + + initialize + do + Precursor + contact_email := parameters.contact_email + end + + parameters: LOGIN_EMAIL_SERVICE_PARAMETERS + -- Associated parameters. + +feature -- Access + + contact_email: IMMUTABLE_STRING_8 + -- contact email. + +feature -- Basic Operations + + send_contact_email (a_to, a_content: READABLE_STRING_8) + -- Send successful contact message `a_token' to `a_to'. + require + attached_to: a_to /= Void + do + send_message (contact_email, a_to, parameters.contact_subject_text, a_content) + end + +end diff --git a/modules/login/login_email_service_parameters.e b/modules/login/login_email_service_parameters.e new file mode 100644 index 0000000..d356af0 --- /dev/null +++ b/modules/login/login_email_service_parameters.e @@ -0,0 +1,73 @@ +note + description: "Summary description for {LOGIN_EMAIL_SERVICE_PARAMETERS}." + date: "$Date$" + revision: "$Revision$" + +class + LOGIN_EMAIL_SERVICE_PARAMETERS + +inherit + EMAIL_SERVICE_PARAMETERS + +create + make + +feature {NONE} -- Initialization + + make (a_cms_api: CMS_API) + local + utf: UTF_CONVERTER + l_site_name: READABLE_STRING_8 + s: detachable READABLE_STRING_32 + l_contact_email, l_contact_subject: detachable READABLE_STRING_8 + do + -- Use global smtp setting if any, otherwise "localhost" + smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost")) + l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name) + admin_email := a_cms_api.setup.site_email + + if not admin_email.has ('<') then + admin_email := l_site_name + " <" + admin_email +">" + end + + if attached {CONFIG_READER} a_cms_api.module_configuration ("login", Void) as cfg then + if attached cfg.text_item ("smtp") as l_smtp then + -- Overwrite global smtp setting if any. + smtp_server := utf.utf_32_string_to_utf_8_string_8 (l_smtp) + end + s := cfg.text_item ("email") + if s /= Void then + l_contact_email := utf.utf_32_string_to_utf_8_string_8 (s) + end + s := cfg.text_item ("subject") + if s /= Void then + l_contact_subject := utf.utf_32_string_to_utf_8_string_8 (s) + end + end + if l_contact_email /= Void then + if not l_contact_email.has ('<') then + l_contact_email := l_site_name + " <" + l_contact_email + ">" + end + contact_email := l_contact_email + else + contact_email := admin_email + end + if l_contact_subject /= Void then + contact_subject_text := l_contact_subject + else + contact_subject_text := "Thank you for registering with us" + end + end + +feature -- Access + + smtp_server: IMMUTABLE_STRING_8 + + admin_email: IMMUTABLE_STRING_8 + + contact_email: IMMUTABLE_STRING_8 + -- Contact email. + + contact_subject_text: IMMUTABLE_STRING_8 + +end diff --git a/modules/login/login_module.e b/modules/login/login_module.e new file mode 100644 index 0000000..32f7249 --- /dev/null +++ b/modules/login/login_module.e @@ -0,0 +1,675 @@ +note + description: "Module Logging supporting different authentication strategies" + date: "$Date: 2015-05-20 06:50:50 -0300 (mi. 20 de may. de 2015) $" + revision: "$Revision: 97328 $" + +class + LOGIN_MODULE + +inherit + CMS_MODULE + redefine + register_hooks + end + + CMS_HOOK_BLOCK + + CMS_HOOK_AUTO_REGISTER + + CMS_HOOK_MENU_SYSTEM_ALTER + + CMS_HOOK_VALUE_TABLE_ALTER + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + + REFACTORING_HELPER + + SHARED_LOGGER + + CMS_REQUEST_UTIL + +create + make + +feature {NONE} -- Initialization + + make + -- Create current module + do + name := "login" + version := "1.0" + description := "Eiffel login module" + package := "login" + + create root_dir.make_current + cache_duration := 0 + end + +feature -- Access: docs + + root_dir: PATH + + cache_duration: INTEGER + -- Caching duration + --| 0: disable + --| -1: cache always valie + --| nb: cache expires after nb seconds. + + cache_disabled: BOOLEAN + do + Result := cache_duration = 0 + end + +feature -- Router + + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) + -- Router configuration. + do + a_router.handle_with_request_methods ("/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) + a_router.handle_with_request_methods ("/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (a_api, ?, ?)), a_router.methods_head_get) + a_router.handle_with_request_methods ("/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) + end + +feature -- Hooks configuration + + register_hooks (a_response: CMS_RESPONSE) + -- Module hooks configuration. + do + auto_subscribe_to_hooks (a_response) + a_response.subscribe_to_block_hook (Current) + a_response.subscribe_to_value_table_alter_hook (Current) + end + +feature -- Hooks + + value_table_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) + -- + do + if attached current_user (a_response.request) as l_user then + a_value.force (l_user, "user") + end + end + + menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + -- Hook execution on collection of menu contained by `a_menu_system' + -- for related response `a_response'. + local + lnk: CMS_LOCAL_LINK + do + if attached a_response.current_user (a_response.request) as u then + create lnk.make (u.name + " (Logout)", "roc-logout" ) + else + create lnk.make ("Login", "roc-login") + end + a_menu_system.primary_menu.extend (lnk) + lnk.set_weight (98) + end + + block_list: ITERABLE [like {CMS_BLOCK}.name] + local + l_string: STRING + do + Result := <<"login","register","reactivate","new_password", "reset_password">> + create l_string.make_empty + across Result as ic loop + l_string.append (ic.item) + l_string.append_character (' ') + end + write_debug_log (generator + ".block_list:" + l_string ) + end + + get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if + a_block_id.is_case_insensitive_equal_general ("login") and then + a_response.request.path_info.starts_with ("/roc-login") + then + if attached template_block (a_block_id, a_response) as l_tpl_block then + create vals.make (1) + -- add the variable to the block + value_table_alter (vals, a_response) + across + vals as ic + loop + l_tpl_block.set_value (ic.item, ic.key) + end + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif + a_block_id.is_case_insensitive_equal_general ("register") and then + a_response.request.path_info.starts_with ("/roc-register") + then + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_name") or else a_response.values.has ("error_email") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name") + l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + l_tpl_block.set_value (a_response.values.item ("email"), "email") + l_tpl_block.set_value (a_response.values.item ("name"), "name") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_register", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + elseif + a_block_id.is_case_insensitive_equal_general ("reactivate") and then + a_response.request.path_info.starts_with ("/reactivate") + then + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + l_tpl_block.set_value (a_response.values.item ("email"), "email") + l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_reactivate", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + elseif + a_block_id.is_case_insensitive_equal_general ("new_password") and then + a_response.request.path_info.starts_with ("/new-password") + then + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_email") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + l_tpl_block.set_value (a_response.values.item ("email"), "email") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_password", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + elseif + a_block_id.is_case_insensitive_equal_general ("reset_password") and then + a_response.request.path_info.starts_with ("/reset-password") + then + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("token"), "token") + l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") + l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password") + l_tpl_block.set_value (a_response.values.item ("token"), "token") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_reset", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + end + end + + handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + br: BAD_REQUEST_ERROR_CMS_RESPONSE + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_value ("Login", "optional_content_type") + r.execute + end + + + handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + br: BAD_REQUEST_ERROR_CMS_RESPONSE + l_url: STRING + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + l_url := req.absolute_script_url ("") + l_url.append ("/basic_auth_logoff") + r.set_redirection (l_url) + r.execute + end + + handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_user_api: CMS_USER_API + u: CMS_USER + l_roles: LIST [CMS_USER_ROLE] + l_exist: BOOLEAN + es: LOGIN_EMAIL_SERVICE + l_link: STRING + l_token: STRING + l_message: STRING + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_value ("Register", "optional_content_type") + if req.is_post_request_method then + if + attached {WSF_STRING} req.form_parameter ("name") as l_name and then + attached {WSF_STRING} req.form_parameter ("password") as l_password and then + attached {WSF_STRING} req.form_parameter ("email") as l_email + then + l_user_api := api.user_api + + if attached l_user_api.user_by_name (l_name.value) then + -- Username already exist. + r.values.force ("The user name exist!", "error_name") + l_exist := True + end + if attached l_user_api.user_by_email (l_email.value) then + -- Emails already exist. + r.values.force ("The email exist!", "error_email") + l_exist := True + end + + if not l_exist then + -- New user + create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1) + l_roles.force (l_user_api.authenticated_user_role) + + create u.make (l_name.value) + u.set_email (l_email.value) + u.set_password (l_password.value) + u.set_roles (l_roles) + l_user_api.new_user (u) + + -- Create activation token + l_token := new_token + l_user_api.new_activation (l_token, u.id) + create l_link.make_from_string (req.server_url) + l_link.append ("/activate/") + l_link.append (l_token) + + create l_message.make_from_string (account_activation) + l_message.replace_substring_all ("$link", l_link) + + -- Send Email + create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_contact_email") + es.send_contact_email (l_email.value, l_message) + + else + r.values.force (l_name.value, "name") + r.values.force (l_email.value, "email") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end + end + end + + r.execute + end + + handle_activation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_user_api: CMS_USER_API + l_id: INTEGER_64 + l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE + l_link: CMS_LOCAL_LINK + do + l_user_api := api.user_api + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if attached {WSF_STRING} req.path_parameter ("token") as l_token then + + if attached {CMS_USER} l_user_api.user_by_activation_token (l_token.value) as l_user then + -- Valid user_id + l_user.mark_active + l_user_api.update_user (l_user) + l_user_api.remove_activation (l_token.value) + r.set_value ("Account activated", "optional_content_type") + r.set_main_content ("

Your account "+ l_user.name +" has been activated

") + else + -- the token does not exist, or it was already used. + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + r.set_value ("Account not activated", "optional_content_type") + r.set_main_content ("

The token "+ l_token.value +" is not valid Reactivate Account

" ) + + end + r.execute + else + create l_ir.make (req, res, api) + l_ir.execute + end + end + + + handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + br: BAD_REQUEST_ERROR_CMS_RESPONSE + es: LOGIN_EMAIL_SERVICE + l_user_api: CMS_USER_API + l_token: STRING + l_link: STRING + l_message: STRING + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if req.is_post_request_method then + if + attached {WSF_STRING} req.form_parameter ("email") as l_email + then + l_user_api := api.user_api + if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then + -- User exist create a new token and send a new email. + if l_user.is_active then + r.values.force ("The asociated user to the given email " + l_email.value + " , is already active", "is_active") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + else + l_token := new_token + l_user_api.new_activation (l_token, l_user.id) + create l_link.make_from_string (req.server_url) + l_link.append ("/activate/") + l_link.append (l_token) + + create l_message.make_from_string (account_activation) + l_message.replace_substring_all ("$link", l_link) + + -- Send Email + create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_contact_email") + es.send_contact_email (l_email.value, l_message) + end + else + r.values.force ("The email does not exist or !", "error_email") + r.values.force (l_email.value, "email") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end + end + end + + r.execute + end + + handle_new_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + br: BAD_REQUEST_ERROR_CMS_RESPONSE + es: LOGIN_EMAIL_SERVICE + l_user_api: CMS_USER_API + l_token: STRING + l_link: STRING + l_message: STRING + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + if req.is_post_request_method then + l_user_api := api.user_api + if attached {WSF_STRING} req.form_parameter ("email") as l_email then + if attached {CMS_USER} l_user_api.user_by_email (l_email.value) as l_user then + -- User exist create a new token and send a new email. + l_token := new_token + l_user_api.new_password (l_token, l_user.id) + create l_link.make_from_string (req.server_url) + l_link.append ("/reset-password?token=") + l_link.append (l_token) + + create l_message.make_from_string (account_new_password) + l_message.replace_substring_all ("$link", l_link) + + -- Send Email + create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_contact_email") + es.send_contact_email (l_email.value, l_message) + else + r.values.force ("The email does not exist !", "error_email") + r.values.force (l_email.value, "email") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end + end + end + r.execute + end + + + handle_reset_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + br: BAD_REQUEST_ERROR_CMS_RESPONSE + es: LOGIN_EMAIL_SERVICE + l_user_api: CMS_USER_API + l_link: STRING + l_message: STRING + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + l_user_api := api.user_api + if attached {WSF_STRING} req.query_parameter ("token") as l_token then + r.values.force (l_token.value, "token") + if l_user_api.user_by_password_token (l_token.value) = Void then + r.values.force ("The token " + l_token.value + " is not valid, click here to generate a new token.", "error_token") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end + end + + if req.is_post_request_method then + + if + attached {WSF_STRING} req.form_parameter ("token") as l_token and then + attached {WSF_STRING} req.form_parameter ("password") as l_password and then + attached {WSF_STRING} req.form_parameter ("confirm_password") as l_confirm_password + then + -- Does the passwords match? + if l_password.value.same_string (l_confirm_password.value) then + -- is the token valid? + if attached {CMS_USER} l_user_api.user_by_password_token (l_token.value) as l_user then + l_user.set_password (l_password.value) + l_user_api.update_user (l_user) + l_user_api.remove_password (l_token.value) + end + else + r.values.force ("Passwords Don't Match", "error_password") + r.values.force (l_token.value, "token") + r.set_status_code ({HTTP_CONSTANTS}.bad_request) + end + end + end + r.execute + 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.module_resource_path (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} -- Token Generation + + new_token: STRING + -- Generate a new token activation token + local + l_token: STRING + l_security: SECURITY_PROVIDER + l_encode: URL_ENCODER + do + create l_security + l_token := l_security.token + create l_encode + from until l_token.same_string (l_encode.encoded_string (l_token)) loop + -- Loop ensure that we have a security token that does not contain characters that need encoding. + -- We cannot simply to an encode-decode because the email sent to the user will contain an encoded token + -- but the user will need to use an unencoded token if activation has to be done manually. + l_token := l_security.token + end + Result := l_token + end + +feature --{NONE} -- Message email + + account_activation: STRING= "[ + + + + + Eiffel.org Activation + + + + + +

Thank you for registering at Eiffel.org

+ +

To complete your registration, please click on this link to activate your account:

+ +

$link

+

Thank you for joining us.

+ + + ]" + + account_new_password: STRING= "[ + + + + + Eiffel.org New Password + + + + + +

You have required a new password at Eiffel.org

+ +

To complete your request, please click on this link to genereate a new password:

+ +

$link

+ + + ]" + +feature {NONE} -- Implementation: date and time + + http_date_format_to_date (s: READABLE_STRING_8): detachable DATE_TIME + local + d: HTTP_DATE + do + create d.make_from_string (s) + if not d.has_error then + Result := d.date_time + end + end + + file_date (p: PATH): DATE_TIME + require + path_exists: (create {FILE_UTILITIES}).file_path_exists (p) + local + f: RAW_FILE + do + create f.make_with_path (p) + Result := timestamp_to_date (f.date) + end + + timestamp_to_date (n: INTEGER): DATE_TIME + local + d: HTTP_DATE + do + create d.make_from_timestamp (n) + Result := d.date_time + end + +note + copyright: "Copyright (c) 1984-2013, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/src/persistence/sql/cms_storage_sql_builder.e b/src/persistence/sql/cms_storage_sql_builder.e index a1bec3b..6323b86 100644 --- a/src/persistence/sql/cms_storage_sql_builder.e +++ b/src/persistence/sql/cms_storage_sql_builder.e @@ -36,6 +36,7 @@ feature -- Initialization create u.make ("admin") u.set_password ("istrator#") u.set_email (a_setup.site_email) + u.set_status ({CMS_USER}.active) a_storage.new_user (u) --| Node @@ -74,16 +75,19 @@ feature -- Initialization create u.make ("auth") u.set_password ("enticated#") u.set_email (a_setup.site_email) + u.set_status ({CMS_USER}.active) a_storage.new_user (u) create u.make ("test") u.set_password ("test#") u.set_email (a_setup.site_email) + u.set_status ({CMS_USER}.active) a_storage.new_user (u) create u.make ("view") u.set_password ("only#") u.set_email (a_setup.site_email) + u.set_status ({CMS_USER}.active) u.set_roles (l_roles) a_storage.new_user (u) end diff --git a/src/persistence/user/cms_user_storage_i.e b/src/persistence/user/cms_user_storage_i.e index e31fd14..44b794a 100644 --- a/src/persistence/user/cms_user_storage_i.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -56,6 +56,20 @@ feature -- Access password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) end + user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User with activation token `a_token', if any. + deferred + ensure + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) + end + + user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User with password token `a_token', if any. + deferred + ensure + password: Result /= Void implies (Result.hashed_password /= Void and Result.password = Void) + end + is_valid_credential (a_u, a_p: READABLE_STRING_32): BOOLEAN -- Does account with username `a_username' and password `a_password' exist? deferred @@ -141,4 +155,27 @@ feature -- Change: roles and permissions deferred end +feature -- Change: User activation + + save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- . + deferred + end + + remove_activation (a_token: READABLE_STRING_32) + -- . + deferred + end + +feature -- Change: User password recovery + + save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- . + deferred + end + + remove_password (a_token: READABLE_STRING_32) + -- . + deferred + end end diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e index 7422618..8142699 100644 --- a/src/persistence/user/cms_user_storage_null.e +++ b/src/persistence/user/cms_user_storage_null.e @@ -34,6 +34,14 @@ feature -- Access: user do end + user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + end + + user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + end + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN do end @@ -76,4 +84,27 @@ feature -- Change: roles and permissions do end +feature -- Change: User activation + + save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- . + do + end + + remove_activation (a_token: READABLE_STRING_32) + -- . + do + end + +feature -- Change: User password recovery + + save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- . + do + end + + remove_password (a_token: READABLE_STRING_32) + -- . + do + end end diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 54c26a0..1950668 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -62,7 +62,7 @@ feature -- Access: user l_parameters: STRING_TABLE [detachable ANY] do error_handler.reset - write_information_log (generator + ".user") + write_information_log (generator + ".user_by_id") create l_parameters.make (1) l_parameters.put (a_id, "uid") sql_query (select_user_by_id, l_parameters) @@ -107,6 +107,40 @@ feature -- Access: user end end + user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User for the given activation token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_by_activation_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (select_user_by_activation_token, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + + user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User for the given password token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_by_password_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (select_user_by_password_token, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN local l_security: SECURITY_PROVIDER @@ -155,6 +189,7 @@ feature -- Change: user l_parameters.put (l_password_salt, "salt") l_parameters.put (l_email, "email") l_parameters.put (create {DATE_TIME}.make_now_utc, "created") + l_parameters.put (a_user.status, "status") sql_change (sql_insert_user, l_parameters) if not error_handler.has_error then @@ -197,6 +232,7 @@ feature -- Change: user l_parameters.put (l_password_salt, "salt") l_parameters.put (l_email, "email") l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") + l_parameters.put (a_user.status, "status") sql_change (sql_update_user, l_parameters) else @@ -441,6 +477,108 @@ feature -- Change: roles and permissions end end + +feature -- Access: User activation + + activation_elapsed_time (a_token: READABLE_STRING_32): INTEGER_32 + -- amount of time that has passed in days since the token `a_token' was saved. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".activation_elapsed_time") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (sql_select_activation_expiration, l_parameters) + if sql_rows_count = 1 then + Result := sql_read_integer_32 (1) + end + end + + user_id_by_activation (a_token: READABLE_STRING_32): INTEGER_64 + -- User id associatied with a token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_id_by_actication") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (sql_select_userid_activation, l_parameters) + if sql_rows_count = 1 then + Result := sql_read_integer_32 (1) + end + end + +feature -- Change: User activation + + save_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_utc_date: DATE_TIME + do + error_handler.reset + sql_begin_transaction + write_information_log (generator + ".save_activation") + create l_utc_date.make_now_utc + create l_parameters.make (2) + l_parameters.put (a_token, "token") + l_parameters.put (a_id, "uid") + l_parameters.put (l_utc_date, "utc_date") + sql_change (sql_insert_activation, l_parameters) + sql_commit_transaction + end + + remove_activation (a_token: READABLE_STRING_32) + -- . + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + write_information_log (generator + ".remove_activation") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_change (sql_remove_activation, l_parameters) + sql_commit_transaction + end + +feature -- Change: User password recovery + + save_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + l_utc_date: DATE_TIME + do + error_handler.reset + sql_begin_transaction + write_information_log (generator + ".save_password") + create l_utc_date.make_now_utc + create l_parameters.make (2) + l_parameters.put (a_token, "token") + l_parameters.put (a_id, "uid") + l_parameters.put (l_utc_date, "utc_date") + sql_change (sql_insert_password, l_parameters) + sql_commit_transaction + end + + remove_password (a_token: READABLE_STRING_32) + -- . + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + write_information_log (generator + ".remove_password") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_change (sql_remove_password, l_parameters) + sql_commit_transaction + end + + feature {NONE} -- Implementation: User user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_8 @@ -489,6 +627,9 @@ feature {NONE} -- Implementation: User 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 @@ -551,10 +692,11 @@ feature {NONE} -- Sql Queries: USER Select_salt_by_username: STRING = "SELECT salt FROM Users WHERE name =:name;" -- Retrieve salt by username if exists. - sql_insert_user: STRING = "INSERT INTO users (name, password, salt, email, created) VALUES (:name, :password, :salt, :email, :created);" - -- SQL Insert to add a new node. + 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 WHERE uid=:uid;" + sql_update_user: STRING = "UPDATE users SET name=:name, password=:password, salt=:salt, email=:email, status=:status WHERE uid=:uid;" + -- SQL update to update an existing user. feature {NONE} -- Sql Queries: USER ROLE @@ -584,4 +726,34 @@ feature {NONE} -- Sql Queries: USER ROLE select_role_permissions_by_role_id: STRING = "SELECT permission, module FROM role_permissions WHERE rid=:rid;" -- User role permissions for role id :rid; +feature {NONE} -- Sql Queries: USER ACTIVATION + + sql_insert_activation: STRING = "INSERT INTO users_activations (token, uid, created) VALUES (:token, :uid, :utc_date);" + -- SQL insert a new activation :token. + + sql_select_activation_expiration: STRING = "SELECT DATEDIFF(day,created,UTC_DATE()) FROM users_activations where token = :token;" + -- elapsed time that has passed in days since the token `a_token' was saved. + + sql_select_userid_activation: STRING = "SELECT uid FROM users_activations where token = :token;" + -- Retrieve userid given the activation token. + + Select_user_by_activation_token: STRING = "SELECT u.* FROM users as u JOIN users_activations as ua ON ua.uid = u.uid and ua.token = :token;" + -- Retrieve user by activation token if exist. + + Sql_remove_activation: STRING = "DELETE FROM users_activations WHERE token = :token;" + -- Remove activation token. + +feature {NONE} + + sql_insert_password: STRING = "INSERT INTO users_password_recovery (token, uid, created) VALUES (:token, :uid, :utc_date);" + -- SQL insert a new password recovery :token. + + Sql_remove_password: STRING = "DELETE FROM users_password_recovery WHERE token = :token;" + -- Retrieve password if exist. + + Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;" + -- Retrieve user by password token if exist. + + + end diff --git a/src/service/email/email_service.e b/src/service/email/email_service.e new file mode 100644 index 0000000..833aeb6 --- /dev/null +++ b/src/service/email/email_service.e @@ -0,0 +1,102 @@ +note + description: "Basic Email Service" + date: "$Date: 2015-04-30 05:45:25 -0300 (ju. 30 de abr. de 2015) $" + revision: "$Revision: 97218 $" + +class + EMAIL_SERVICE + +inherit + + SHARED_ERROR + SHARED_LOGGER + +create + make + +feature {NONE} -- Initialization + + make (a_params: like parameters) + -- Create instance of {EMAIL_SERVICE} with smtp_server `a_params.smtp_server'. + -- Using `a_params.admin_email' as admin email. + do + parameters := a_params + initialize + end + + initialize + -- Initialize service. + local + l_address_factory: INET_ADDRESS_FACTORY + do + admin_email := parameters.admin_email + + -- Get local host name needed in creation of SMTP_PROTOCOL. + create l_address_factory + create smtp_protocol.make (parameters.smtp_server, l_address_factory.create_localhost.host_name) + set_successful + end + + parameters: EMAIL_SERVICE_PARAMETERS + -- Associated parameters. + + admin_email: IMMUTABLE_STRING_8 + -- Site admin's email. + + smtp_protocol: SMTP_PROTOCOL + -- SMTP protocol. + +feature -- Basic Operations + + send_internal_email (a_content: READABLE_STRING_GENERAL) + do + send_message (admin_email, admin_email, "Notification Contact", a_content) + end + + send_email_internal_server_error (a_content: READABLE_STRING_GENERAL) + do + send_message (admin_email, admin_email, "Internal Server Error", a_content) + end + + send_message (a_from_address, a_to_address: READABLE_STRING_8; a_subjet: READABLE_STRING_GENERAL; a_content: READABLE_STRING_GENERAL) + local + l_email: EMAIL + utf: UTF_CONVERTER + do + write_debug_log (generator + ".send_message: [from:" + a_from_address + ", to:" + a_to_address + ", subject:" + a_subjet + ", content:" + a_content) + create l_email.make_with_entry (a_from_address, a_to_address) + l_email.set_message (utf.escaped_utf_32_string_to_utf_8_string_8 (a_content)) + l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, utf.escaped_utf_32_string_to_utf_8_string_8 (a_subjet)) + l_email.add_header_entry ("MIME-Version:", "1.0") + l_email.add_header_entry ("Content-Type", "text/html; charset=utf-8") + send_email (l_email) + end + +feature {NONE} -- Implementation + + send_email (a_email: EMAIL) + -- Send the email represented by `a_email'. + local + l_retried: BOOLEAN + do + if not l_retried then + write_information_log (generator + ".send_email Process send email.") + smtp_protocol.initiate_protocol + smtp_protocol.transfer (a_email) + smtp_protocol.close_protocol + write_information_log (generator + ".send_email Email sent.") + if smtp_protocol.error then + set_last_error ("smtp_protocol reported an error", generator + ".send_email") + else + set_successful + end + else + write_error_log (generator + ".send_email Email not send " + last_error_message ) + end + rescue + set_last_error_from_exception (generator + ".send_email") + l_retried := True + retry + end + +end diff --git a/src/service/email/email_service_parameters.e b/src/service/email/email_service_parameters.e new file mode 100644 index 0000000..c7d18e1 --- /dev/null +++ b/src/service/email/email_service_parameters.e @@ -0,0 +1,20 @@ +note + description: "Basic Email Service customized for cms site" + author: "" + date: "$Date: 2015-01-16 07:17:14 -0300 (vi. 16 de ene. de 2015) $" + revision: "$Revision: 96467 $" + +deferred class + EMAIL_SERVICE_PARAMETERS + +feature -- Access + + smtp_server: IMMUTABLE_STRING_8 + deferred + end + + admin_email: IMMUTABLE_STRING_8 + deferred + end + +end diff --git a/src/service/user/cms_user_api.e b/src/service/user/cms_user_api.e index 5f374df..8465f91 100644 --- a/src/service/user/cms_user_api.e +++ b/src/service/user/cms_user_api.e @@ -29,6 +29,24 @@ feature -- Access Result := storage.user_by_name (a_username) end + user_by_email (a_email: READABLE_STRING_32): detachable CMS_USER + -- User by email `a_email', if any. + do + Result := storage.user_by_email (a_email) + end + + user_by_activation_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User by activation token `a_token'. + do + Result := storage.user_by_activation_token (a_token) + end + + user_by_password_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User by password token `a_token'. + do + Result := storage.user_by_password_token (a_token) + end + feature -- Status report is_valid_credential (a_auth_login, a_auth_password: READABLE_STRING_32): BOOLEAN @@ -133,4 +151,43 @@ feature -- Change User storage.update_user (a_user) end +feature -- User Activation + + new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- Save activation token `a_token', for the user with the id `a_id'. + do + storage.save_activation (a_token, a_id) + end + + remove_activation (a_token: READABLE_STRING_32) + -- Remove activation token `a_token', from the storage. + do + storage.remove_activation (a_token) + end + +feature -- User Password Recovery + + new_password (a_token: READABLE_STRING_32; a_id: INTEGER_64) + -- Save password token `a_token', for the user with the id `a_id'. + do + storage.save_password (a_token, a_id) + end + + remove_password (a_token: READABLE_STRING_32) + -- Remove password token `a_token', from the storage. + do + storage.remove_password (a_token) + end + +feature -- User status + + not_active: INTEGER = 0 + -- The user is not active. + + active: INTEGER = 1 + -- The user is active + + Trashed: INTEGER = -1 + -- The user is trashed (soft delete), ready to be deleted/destroyed from storage. + end From 181c32a895b6dcdf96ecdc8b1a5f885af22465f0 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 5 Jun 2015 19:12:10 -0300 Subject: [PATCH 2/8] Update: refactor get_block_view. --- modules/login/login_module.e | 312 ++++++++++++++++++++--------------- 1 file changed, 175 insertions(+), 137 deletions(-) diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 32f7249..3faceeb 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -133,159 +133,27 @@ feature -- Hooks a_block_id.is_case_insensitive_equal_general ("login") and then a_response.request.path_info.starts_with ("/roc-login") then - if attached template_block (a_block_id, a_response) as l_tpl_block then - create vals.make (1) - -- add the variable to the block - value_table_alter (vals, a_response) - across - vals as ic - loop - l_tpl_block.set_value (ic.item, ic.key) - end - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end + get_block_view_login (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("register") and then a_response.request.path_info.starts_with ("/roc-register") then - if a_response.request.is_get_request_method then - if attached template_block (a_block_id, a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - elseif a_response.request.is_post_request_method then - if a_response.values.has ("error_name") or else a_response.values.has ("error_email") then - if attached template_block (a_block_id, a_response) as l_tpl_block then - l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name") - l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") - l_tpl_block.set_value (a_response.values.item ("email"), "email") - l_tpl_block.set_value (a_response.values.item ("name"), "name") - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - else - if attached template_block ("post_register", a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - end - end + get_block_view_register (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then a_response.request.path_info.starts_with ("/reactivate") then - if a_response.request.is_get_request_method then - if attached template_block (a_block_id, a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - elseif a_response.request.is_post_request_method then - if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then - if attached template_block (a_block_id, a_response) as l_tpl_block then - l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") - l_tpl_block.set_value (a_response.values.item ("email"), "email") - l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active") - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - else - if attached template_block ("post_reactivate", a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - end - end + get_block_view_reactivate (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then a_response.request.path_info.starts_with ("/new-password") then - if a_response.request.is_get_request_method then - if attached template_block (a_block_id, a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - elseif a_response.request.is_post_request_method then - if a_response.values.has ("error_email") then - if attached template_block (a_block_id, a_response) as l_tpl_block then - l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") - l_tpl_block.set_value (a_response.values.item ("email"), "email") - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - else - if attached template_block ("post_password", a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - end - end + get_block_view_new_password (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then a_response.request.path_info.starts_with ("/reset-password") then - if a_response.request.is_get_request_method then - if attached template_block (a_block_id, a_response) as l_tpl_block then - l_tpl_block.set_value (a_response.values.item ("token"), "token") - l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - elseif a_response.request.is_post_request_method then - if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then - if attached template_block (a_block_id, a_response) as l_tpl_block then - l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") - l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password") - l_tpl_block.set_value (a_response.values.item ("token"), "token") - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - else - if attached template_block ("post_reset", a_response) as l_tpl_block then - a_response.add_block (l_tpl_block, "content") - else - debug ("cms") - a_response.add_warning_message ("Error with block [" + a_block_id + "]") - end - end - end - end + get_block_view_reset_password (a_block_id, a_response) end end @@ -568,6 +436,176 @@ feature {NONE} -- Helpers end end +feature {NONE} -- Block views + + get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if attached template_block (a_block_id, a_response) as l_tpl_block then + create vals.make (1) + -- add the variable to the block + value_table_alter (vals, a_response) + across + vals as ic + loop + l_tpl_block.set_value (ic.item, ic.key) + end + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + + get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_name") or else a_response.values.has ("error_email") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_name"), "error_name") + l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + l_tpl_block.set_value (a_response.values.item ("email"), "email") + l_tpl_block.set_value (a_response.values.item ("name"), "name") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_register", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + end + + + get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_email") or else a_response.values.has ("is_active") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + l_tpl_block.set_value (a_response.values.item ("email"), "email") + l_tpl_block.set_value (a_response.values.item ("is_active"), "is_active") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_reactivate", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + end + + get_block_view_new_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_email") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_email"), "error_email") + l_tpl_block.set_value (a_response.values.item ("email"), "email") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_password", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + end + + get_block_view_reset_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) + local + vals: CMS_VALUE_TABLE + do + if a_response.request.is_get_request_method then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("token"), "token") + l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + elseif a_response.request.is_post_request_method then + if a_response.values.has ("error_token") or else a_response.values.has ("error_password") then + if attached template_block (a_block_id, a_response) as l_tpl_block then + l_tpl_block.set_value (a_response.values.item ("error_token"), "error_token") + l_tpl_block.set_value (a_response.values.item ("error_password"), "error_password") + l_tpl_block.set_value (a_response.values.item ("token"), "token") + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + else + if attached template_block ("post_reset", a_response) as l_tpl_block then + a_response.add_block (l_tpl_block, "content") + else + debug ("cms") + a_response.add_warning_message ("Error with block [" + a_block_id + "]") + end + end + end + end + end + feature {NONE} -- Token Generation new_token: STRING From 96ba3c35a26d2bbd75927102a9177df0f2e2eb45 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 8 Jun 2015 12:58:33 -0300 Subject: [PATCH 3/8] OAuth2 Gmail Added OAuth2 GMAIL loggin/logout support. Added OAuth2 Gmail filter. LoginModule Updated LoginModule with OAuth2 Gmail support. Persitence Extended user persitance api with OAuth2 gmail features. (TODO refactor persistance as an user extention) --- .../config/{ => modules}/login/login.json | 0 .../config/modules/login/oauth2_gmail.json | 7 + examples/demo/site/scripts/oauth_gmail.sql | 14 ++ .../modules/login/templates/block_login.tpl | 7 +- modules/login/filter/oauth_gmail_filter.e | 46 +++++ modules/login/login-safe.ecf | 6 + modules/login/login_module.e | 131 ++++++++++++- modules/login/oauth_login_gmail.e | 179 ++++++++++++++++++ src/persistence/user/cms_user_storage_i.e | 21 ++ src/persistence/user/cms_user_storage_null.e | 20 ++ src/persistence/user/cms_user_storage_sql_i.e | 86 ++++++++- src/service/user/cms_user_api.e | 29 +++ 12 files changed, 536 insertions(+), 10 deletions(-) rename examples/demo/site/config/{ => modules}/login/login.json (100%) create mode 100644 examples/demo/site/config/modules/login/oauth2_gmail.json create mode 100644 examples/demo/site/scripts/oauth_gmail.sql create mode 100644 modules/login/filter/oauth_gmail_filter.e create mode 100644 modules/login/oauth_login_gmail.e diff --git a/examples/demo/site/config/login/login.json b/examples/demo/site/config/modules/login/login.json similarity index 100% rename from examples/demo/site/config/login/login.json rename to examples/demo/site/config/modules/login/login.json diff --git a/examples/demo/site/config/modules/login/oauth2_gmail.json b/examples/demo/site/config/modules/login/oauth2_gmail.json new file mode 100644 index 0000000..8cd35ef --- /dev/null +++ b/examples/demo/site/config/modules/login/oauth2_gmail.json @@ -0,0 +1,7 @@ +{ + "api_secret":"ADD_YOUR_SECRET_KEY", + "api_key":"ADD_YOUR_PUBLIC_KEY", + "scope": "email", + "api_revoke":"https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN", + "protected_resource_url":"https://www.googleapis.com/plus/v1/people/me" +} diff --git a/examples/demo/site/scripts/oauth_gmail.sql b/examples/demo/site/scripts/oauth_gmail.sql new file mode 100644 index 0000000..3efbe55 --- /dev/null +++ b/examples/demo/site/scripts/oauth_gmail.sql @@ -0,0 +1,14 @@ +BEGIN; + + +CREATE TABLE "oauth2_gmail"( + "uid" INTEGER PRIMARY KEY NOT NULL CHECK("uid">=0), + "access_token" VARCHAR(255) NOT NULL, + "created" DATETIME NOT NULL, + "details" TEXT NOT NULL, + CONSTRAINT "uid" + UNIQUE("uid") + ); + + +COMMIT; \ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl index 495fdd8..18301e6 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl @@ -1,6 +1,6 @@
{if isset="$user"} -

Logout

+

Logout

{/if} {unless isset="$user"}

Login or Register

@@ -28,5 +28,8 @@

- {/unless} + {/unless} +
+ +
\ No newline at end of file diff --git a/modules/login/filter/oauth_gmail_filter.e b/modules/login/filter/oauth_gmail_filter.e new file mode 100644 index 0000000..cd9de8b --- /dev/null +++ b/modules/login/filter/oauth_gmail_filter.e @@ -0,0 +1,46 @@ +note + description: "Summary description for {OAUTH_GMAIL_FILTER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + OAUTH_GMAIL_FILTER + +inherit + WSF_URI_TEMPLATE_HANDLER + CMS_HANDLER + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + local + utf: UTF_CONVERTER + do + api.logger.put_debug (generator + ".execute ", Void) +-- if attached req.raw_header_data as l_raw_data then +-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) +-- end + -- A valid user + if + attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_roc_auth_session_token + then + if attached {CMS_USER} api.user_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then + set_current_user (req, l_user) + execute_next (req, res) + else + api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) + execute_next (req, res) + end + else + api.logger.put_debug (generator + ".execute without authentication", Void) + execute_next (req, res) + end + end + +end diff --git a/modules/login/login-safe.ecf b/modules/login/login-safe.ecf index eaba1d4..38f4ea5 100644 --- a/modules/login/login-safe.ecf +++ b/modules/login/login-safe.ecf @@ -18,6 +18,12 @@ + + + + + + diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 3faceeb..6a95bd2 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -9,6 +9,7 @@ class inherit CMS_MODULE redefine + filters, register_hooks end @@ -48,6 +49,16 @@ feature {NONE} -- Initialization cache_duration := 0 end + +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) + Result.extend (create {OAUTH_GMAIL_FILTER}.make (a_api)) + end + feature -- Access: docs root_dir: PATH @@ -75,6 +86,8 @@ feature -- Router a_router.handle_with_request_methods ("/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, ?, ?)), a_router.methods_get_post) end feature -- Hooks configuration @@ -171,15 +184,33 @@ feature -- Hooks handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE l_url: STRING + l_oauth_gmail: OAUTH_LOGIN_GMAIL + l_cookie: WSF_COOKIE do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - r.set_status_code ({HTTP_CONSTANTS}.found) - l_url := req.absolute_script_url ("") - l_url.append ("/basic_auth_logoff") - r.set_redirection (l_url) - r.execute + if + attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_cookie_token and then + attached {CMS_USER} current_user (req) as l_user + then + -- Logout gmail + create l_oauth_gmail.make (api, req.absolute_script_url ("")) + l_oauth_gmail.sign_out (l_cookie_token.value) + create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_cookie_token.value) + l_cookie.set_max_age (-1) + res.add_cookie (l_cookie) + unset_current_user (req) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + r.set_redirection (req.absolute_script_url ("")) + r.execute + else + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + l_url := req.absolute_script_url ("") + l_url.append ("/basic_auth_logoff") + r.set_redirection (l_url) + r.execute + end end handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) @@ -606,6 +637,92 @@ feature {NONE} -- Block views end end +feature -- OAuth2 Login with google. + + handle_login_with_google (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_oauth_gmail: OAUTH_LOGIN_GMAIL + do + create l_oauth_gmail.make (api, req.absolute_script_url ("")) + if attached l_oauth_gmail.authorization_url as l_authorization then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_redirection (l_authorization) + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("Bad request") + r.execute + end + end + + handle_callback_gmail (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_auth_gmail: OAUTH_LOGIN_GMAIL + l_user_api: CMS_USER_API + l_user: CMS_USER + l_roles: LIST [CMS_USER_ROLE] + l_cookie: WSF_COOKIE + do + if attached {WSF_STRING} req.query_parameter ("code") as l_code then + create l_auth_gmail.make (api, req.server_url) + l_auth_gmail.sign_request (l_code.value) + if + attached l_auth_gmail.access_token as l_access_token and then + attached l_auth_gmail.user_profile as l_user_profile + then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + -- extract user email + -- check if the user exist + l_user_api := api.user_api + -- 1 if the user exit put it in the context + if + attached l_auth_gmail.user_email as l_email + then + if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then + -- User with email exist + if attached {CMS_USER} l_user_api.user_oauth2_gmail_by_id (p_user.id) then + -- Update oauth entry + l_user_api.update_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + else + -- create a oauth entry + l_user_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + end + create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) + l_cookie.set_max_age (l_access_token.expires_in) + res.add_cookie (l_cookie) + else + + create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1) + l_roles.force (l_user_api.authenticated_user_role) + + -- Create a new user and oauth entry + create l_user.make (l_email) + l_user.set_email (l_email) + l_user.set_password (new_token) -- generate a random password. + l_user.set_roles (l_roles) + l_user.mark_active + l_user_api.new_user (l_user) + + -- Add oauth entry + l_user_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) + create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) + l_cookie.set_max_age (l_access_token.expires_in) + res.add_cookie (l_cookie) + end + else + + + end + r.set_redirection (req.absolute_script_url ("")) + r.execute + end + + end + + end + feature {NONE} -- Token Generation new_token: STRING diff --git a/modules/login/oauth_login_gmail.e b/modules/login/oauth_login_gmail.e new file mode 100644 index 0000000..1c484c9 --- /dev/null +++ b/modules/login/oauth_login_gmail.e @@ -0,0 +1,179 @@ +note + description: "OAuth workflow for Gmails." + date: "$Date$" + revision: "$Revision$" + +class + OAUTH_LOGIN_GMAIL + +inherit + + SHARED_LOGGER + +create + make + +feature {NONE} -- Initialization + + make (a_cms_api:CMS_API a_host: READABLE_STRING_32) + -- Create an object with the host `a_host'. + do + cms_api := a_cms_api + initilize + create config.make_default (api_key, api_secret) + config.set_callback (a_host + "/oauthgmail") + config.set_scope (scope) + create goauth + api_service := goauth.create_service (config) + ensure + cms_api_set: cms_api = a_cms_api + end + + initilize + local + utf: UTF_CONVERTER + do + --Use configuration values if any if not defaul + api_key := "KEY" + api_secret := "SECRET" + scope := "email" + + api_revoke := "[https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN]" + protected_resource_url := "https://www.googleapis.com/plus/v1/people/me" + + + if attached {CONFIG_READER} cms_api.module_configuration ("login", "oauth2_gmail") as cfg then + if attached cfg.text_item ("api_secret") as l_api_secret then + api_secret := utf.utf_32_string_to_utf_8_string_8 (l_api_secret) + end + if attached cfg.text_item ("api_key") as l_api_key then + api_key := utf.utf_32_string_to_utf_8_string_8 (l_api_key) + end + if attached cfg.text_item ("scope") as l_scope then + scope := utf.utf_32_string_to_utf_8_string_8 (l_scope) + end + if attached cfg.text_item ("api_revoke") as l_api_revoke then + api_revoke := utf.utf_32_string_to_utf_8_string_8 (l_api_revoke) + end + if attached cfg.text_item ("protected_resource_url") as l_resource_url then + protected_resource_url := utf.utf_32_string_to_utf_8_string_8 (l_resource_url) + end + end + + end + +feature -- Access + + authorization_url: detachable READABLE_STRING_32 + -- Obtain the Authorization URL. + do + -- Obtain the Authorization URL + write_debug_log (generator + ".authorization_url Fetching the Authorization URL..!") + if attached api_service.authorization_url (empty_token) as l_authorization_url then + write_debug_log (generator + ".authorization_url: Got the Authorization URL!") + write_debug_log (generator + ".authorization_url:" + l_authorization_url) + Result := l_authorization_url.as_string_32 + end + end + + sign_request (a_code: READABLE_STRING_32) + -- Sign request with code `a_code'. + --! To get the code `a_code' you need to do a request + --! using the authorization_url + local + request: OAUTH_REQUEST + do + -- Get the access token. + write_debug_log (generator + ".sign_request Fetching the access token with code [" + a_code + "]") + access_token := api_service.access_token_post (empty_token, create {OAUTH_VERIFIER}.make (a_code)) + if attached access_token as l_access_token then + write_debug_log (generator + ".sign_request Got the Access Token [" + l_access_token.debug_output + "]") + -- Get the user email + --! at the moment the scope is mail, but we can change it to get more information. + create request.make ("GET", protected_resource_url) + request.add_header ("Authorization", "Bearer " + l_access_token.token) + api_service.sign_request (l_access_token, request) + if attached {OAUTH_RESPONSE} request.execute as l_response then + write_debug_log (generator + ".sign_request Sign_request response [" + l_response.status.out + "]") + if attached l_response.body as l_body then + user_profile := l_body + write_debug_log (generator + ".sign_request User profile [" + l_body + "]") + end + end + end + end + + sign_out (a_code: READABLE_STRING_32) + -- Invalidate the current OAuth access token `a_code'. + local + l_revoke: STRING + request: OAUTH_REQUEST + do + create l_revoke.make_from_string (api_revoke) + l_revoke.replace_substring_all ("$ACCESS_TOKEN", a_code) + create request.make ("POST", l_revoke) + if attached {OAUTH_RESPONSE} request.execute as l_response then + -- do nothing + write_debug_log (generator + ".sign_out response [" + l_response.status.out + "]") + check invalidate_ok: l_response.status = {HTTP_CONSTANTS}.ok end + end + end + + user_email: detachable READABLE_STRING_32 + -- Retrieve user email if any. + local + l_json: JSON_CONFIG + do + if attached user_profile as l_profile then + create l_json.make_from_string (l_profile) + if + attached {JSON_ARRAY} l_json.item ("emails") as l_array and then + attached {JSON_OBJECT} l_array.i_th (1) as l_object and then + attached {JSON_STRING} l_object.item ("value") as l_email + then + Result := l_email.item + end + end + end + +feature -- Access + + access_token: detachable OAUTH_TOKEN + -- JSON representing the access token. + + user_profile: detachable READABLE_STRING_32 + -- JSON representing the user profiles. + +feature {NONE} -- Implementation + + goauth: OAUTH_20_GOOGLE_API + -- OAuth 2.0 Google API. + + config: OAUTH_CONFIG + -- configuration. + + api_service: OAUTH_SERVICE_I + -- Service. + + api_key: STRING + -- public key. + + api_secret: STRING + -- secret key. + + scope: STRING + -- api scope to access protected resources. + + api_revoke: STRING + -- Revoke url + + protected_resource_url: STRING + -- Resource url. + + empty_token: detachable OAUTH_TOKEN + -- fake token. + + cms_api: CMS_API + -- CMS API. + +end diff --git a/src/persistence/user/cms_user_storage_i.e b/src/persistence/user/cms_user_storage_i.e index 44b794a..777767f 100644 --- a/src/persistence/user/cms_user_storage_i.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -178,4 +178,25 @@ feature -- Change: User password recovery -- . deferred end + +feature -- Change: User Oauth2 + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + deferred + end + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Update user `a_user' with oauth2 gmail authentication. + deferred + end + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + deferred + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + deferred + end + end diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e index 8142699..cb6c12b 100644 --- a/src/persistence/user/cms_user_storage_null.e +++ b/src/persistence/user/cms_user_storage_null.e @@ -107,4 +107,24 @@ feature -- Change: User password recovery -- . do end + +feature -- Change User Oauth + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + do + end + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Update user `a_user' with oauth2 gmail authentication. + do + end + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + do + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + end end diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 1950668..2f100d7 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -579,6 +579,82 @@ feature -- Change: User password recovery end +feature -- User Oauth2 + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_oauth2_gmail") + create l_parameters.make (4) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (a_user_profile, "profile") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + sql_change (sql_insert_oauth2_gmail, l_parameters) + sql_commit_transaction + end + + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_oauth2_gmail") + create l_parameters.make (4) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (a_user_profile, "profile") + + sql_change (sql_update_oauth2_gmail, l_parameters) + sql_commit_transaction + end + + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User for the given password token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_by_oauth2_gmail_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (select_user_by_oauth2_gmail_token, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + -- User for the given password token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_oauth2_gmail_by_id") + create l_parameters.make (1) + l_parameters.put (a_uid, "uid") + sql_query (select_user_oauth2_gmail_by_id, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + + feature {NONE} -- Implementation: User user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_8 @@ -743,7 +819,7 @@ feature {NONE} -- Sql Queries: USER ACTIVATION Sql_remove_activation: STRING = "DELETE FROM users_activations WHERE token = :token;" -- Remove activation token. -feature {NONE} +feature {NONE} -- User Password Recovery sql_insert_password: STRING = "INSERT INTO users_password_recovery (token, uid, created) VALUES (:token, :uid, :utc_date);" -- SQL insert a new password recovery :token. @@ -754,6 +830,14 @@ feature {NONE} Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;" -- Retrieve user by password token if exist. +feature {NONE}-- User Oauth2 Gmail. + Sql_insert_oauth2_gmail: STRING = "INSERT INTO oauth2_gmail (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + + Sql_update_oauth2_gmail: STRING = "UPDATE oauth2_gmail SET access_token = :token, details = :profile WHERE uid =:uid;" + + Select_user_by_oauth2_gmail_token: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.access_token = :token;" + + Select_user_oauth2_gmail_by_id: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.uid = :uid;" end diff --git a/src/service/user/cms_user_api.e b/src/service/user/cms_user_api.e index 8465f91..87236e7 100644 --- a/src/service/user/cms_user_api.e +++ b/src/service/user/cms_user_api.e @@ -151,6 +151,35 @@ feature -- Change User storage.update_user (a_user) end + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + require + has_id: a_user.has_id + do + storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) + end + + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Updaate user `a_user' with oauth2 gmail authentication. + require + has_id: a_user.has_id + do + storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) + end + + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + do + Result := storage.user_oauth2_gmail_by_id (a_uid) + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + Result := storage.user_by_oauth2_gmail_token (a_token) + end + feature -- User Activation new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) From e188625c43a568ff3095a3b42c4b0bd8640b0ff7 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 8 Jun 2015 18:32:34 -0300 Subject: [PATCH 4/8] Update Login Module. - Added an API to mange user OAuth authentication. - Updated the Filter to use the new API. - Updated the Module to initialize if it needed the storages needed by the login module. - Updated gmail callback to use the new API. - Added a Persistence Layer CMS_USER_API - clean api and related persistence code. --- modules/login/cms_user_oauth_api.e | 64 ++++++++ modules/login/filter/oauth_gmail_filter.e | 22 ++- modules/login/login-safe.ecf | 3 +- modules/login/login_module.e | 97 ++++++++++-- .../persistence/cms_user_oauth_storage_i.e | 44 ++++++ .../persistence/cms_user_oauth_storage_null.e | 48 ++++++ .../persistence/cms_user_oauth_storage_sql.e | 149 ++++++++++++++++++ src/persistence/user/cms_user_storage_i.e | 20 --- src/persistence/user/cms_user_storage_null.e | 19 --- src/persistence/user/cms_user_storage_sql_i.e | 87 ---------- src/service/user/cms_user_api.e | 29 ---- 11 files changed, 413 insertions(+), 169 deletions(-) create mode 100644 modules/login/cms_user_oauth_api.e create mode 100644 modules/login/persistence/cms_user_oauth_storage_i.e create mode 100644 modules/login/persistence/cms_user_oauth_storage_null.e create mode 100644 modules/login/persistence/cms_user_oauth_storage_sql.e diff --git a/modules/login/cms_user_oauth_api.e b/modules/login/cms_user_oauth_api.e new file mode 100644 index 0000000..a9a7bb6 --- /dev/null +++ b/modules/login/cms_user_oauth_api.e @@ -0,0 +1,64 @@ +note + description: "[ + API to manage CMS User OAuth authentication. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER_OAUTH_API + +inherit + CMS_MODULE_API + + REFACTORING_HELPER + +create {LOGIN_MODULE} + make_with_storage + +feature {NONE} -- Initialization + + make_with_storage (a_api: CMS_API; a_user_oauth_storage: CMS_USER_OAUTH_STORAGE_I) + do + user_oauth_storage := a_user_oauth_storage + make (a_api) + end + +feature {CMS_MODULE} -- Access user oauth storage. + + user_oauth_storage: CMS_USER_OAUTH_STORAGE_I + + +feature -- Access: OAuth2 Gmail + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + do + Result := user_oauth_storage.user_oauth2_gmail_by_id (a_uid) + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + Result := user_oauth_storage.user_by_oauth2_gmail_token (a_token) + end + + +feature -- Change: OAuth2 Gmail + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + require + has_id: a_user.has_id + do + user_oauth_storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) + end + + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Updaate user `a_user' with oauth2 gmail authentication. + require + has_id: a_user.has_id + do + user_oauth_storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) + end + +end diff --git a/modules/login/filter/oauth_gmail_filter.e b/modules/login/filter/oauth_gmail_filter.e index cd9de8b..95c4d62 100644 --- a/modules/login/filter/oauth_gmail_filter.e +++ b/modules/login/filter/oauth_gmail_filter.e @@ -10,11 +10,25 @@ class inherit WSF_URI_TEMPLATE_HANDLER CMS_HANDLER + rename + make as make_handler + end + WSF_FILTER create make +feature {NONE} -- Initialization + + make (a_api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API) + do + make_handler (a_api) + user_oauth_api := a_user_oauth_api + end + + user_oauth_api: CMS_USER_OAUTH_API + feature -- Basic operations execute (req: WSF_REQUEST; res: WSF_RESPONSE) @@ -23,14 +37,14 @@ feature -- Basic operations utf: UTF_CONVERTER do api.logger.put_debug (generator + ".execute ", Void) --- if attached req.raw_header_data as l_raw_data then --- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) --- end + if attached req.raw_header_data as l_raw_data then + api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) + end -- A valid user if attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_roc_auth_session_token then - if attached {CMS_USER} api.user_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then + if attached {CMS_USER} user_oauth_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) execute_next (req, res) else diff --git a/modules/login/login-safe.ecf b/modules/login/login-safe.ecf index 38f4ea5..0032064 100644 --- a/modules/login/login-safe.ecf +++ b/modules/login/login-safe.ecf @@ -18,8 +18,9 @@ + - + diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 6a95bd2..3c336cd 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -8,11 +8,18 @@ class inherit CMS_MODULE + rename + module_api as user_oauth_api redefine filters, - register_hooks + register_hooks, + initialize, + is_installed, + install, + user_oauth_api end + CMS_HOOK_BLOCK CMS_HOOK_AUTO_REGISTER @@ -49,6 +56,65 @@ feature {NONE} -- Initialization cache_duration := 0 end +feature {CMS_API} -- Module Initialization + + initialize (a_api: CMS_API) + -- + local + l_user_auth_api: like user_oauth_api + l_user_auth_storage: CMS_USER_OAUTH_STORAGE_I + do + Precursor (a_api) + + -- Storage initialization + if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then + create {CMS_USER_OAUTH_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) + else + -- FIXME: in case of NULL storage, should Current be disabled? + create {CMS_USER_OAUTH_STORAGE_NULL} l_user_auth_storage + end + + -- Node API initialization + create l_user_auth_api.make_with_storage (a_api, l_user_auth_storage) + user_oauth_api := l_user_auth_api + ensure then + user_oauth_api_set: user_oauth_api /= Void + end + +feature {CMS_API} -- Module management + + is_installed (api: CMS_API): BOOLEAN + -- Is Current module installed? + do + Result := attached api.storage.custom_value ("is_initialized", "module-" + name) as v and then v.is_case_insensitive_equal_general ("yes") + end + + install (api: CMS_API) + local + sql: STRING + l_setup: CMS_SETUP + do + l_setup := api.setup + + -- Schema + if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then + if not l_sql_storage.sql_table_exists ("oauth2_gmail") then + --| Schema + l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("core.sql")) + + if l_sql_storage.has_error then + api.logger.put_error ("Could not initialize database for blog module", generating_type) + end + end + api.storage.set_custom_value ("is_initialized", "module-" + name, "yes") + end + end + +feature {CMS_API} -- Access: API + + user_oauth_api: detachable CMS_USER_OAUTH_API + -- + feature -- Filters @@ -56,7 +122,9 @@ feature -- Filters -- Possibly list of Filter's module. do create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) - Result.extend (create {OAUTH_GMAIL_FILTER}.make (a_api)) + if attached user_oauth_api as l_user_oauth_api then + Result.extend (create {OAUTH_GMAIL_FILTER}.make (a_api, l_user_oauth_api)) + end end feature -- Access: docs @@ -76,8 +144,17 @@ feature -- Access: docs feature -- Router + setup_router (a_router: WSF_ROUTER; a_api: CMS_API) - -- Router configuration. + -- + do + if attached user_oauth_api as l_user_oauth_api then + configure_web (a_api, l_user_oauth_api, a_router) + end + end + + + configure_web (a_api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; a_router: WSF_ROUTER) do a_router.handle_with_request_methods ("/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) a_router.handle_with_request_methods ("/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post) @@ -87,9 +164,11 @@ feature -- Router a_router.handle_with_request_methods ("/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) + end + feature -- Hooks configuration register_hooks (a_response: CMS_RESPONSE) @@ -656,7 +735,7 @@ feature -- OAuth2 Login with google. end end - handle_callback_gmail (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_callback_gmail (api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_auth_gmail: OAUTH_LOGIN_GMAIL @@ -682,12 +761,12 @@ feature -- OAuth2 Login with google. then if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then -- User with email exist - if attached {CMS_USER} l_user_api.user_oauth2_gmail_by_id (p_user.id) then + if attached {CMS_USER} a_user_oauth_api.user_oauth2_gmail_by_id (p_user.id) then -- Update oauth entry - l_user_api.update_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + a_user_oauth_api.update_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) else -- create a oauth entry - l_user_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) end create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) @@ -706,7 +785,7 @@ feature -- OAuth2 Login with google. l_user_api.new_user (l_user) -- Add oauth entry - l_user_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) + a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) res.add_cookie (l_cookie) diff --git a/modules/login/persistence/cms_user_oauth_storage_i.e b/modules/login/persistence/cms_user_oauth_storage_i.e new file mode 100644 index 0000000..8932faf --- /dev/null +++ b/modules/login/persistence/cms_user_oauth_storage_i.e @@ -0,0 +1,44 @@ +note + description: "Summary description for {CMS_USER_OAUTH_STORAGE_I}." + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_USER_OAUTH_STORAGE_I + +inherit + SHARED_LOGGER + +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + deferred + end + +feature -- Access + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + -- CMS User with Oauth gmail credential by id if any. + deferred + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- -- CMS User with Oauth gmail credential by access token `a_token' if any. + deferred + end + +feature -- Change: User Oauth2 + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + deferred + end + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Update user `a_user' with oauth2 gmail authentication. + deferred + end + + +end diff --git a/modules/login/persistence/cms_user_oauth_storage_null.e b/modules/login/persistence/cms_user_oauth_storage_null.e new file mode 100644 index 0000000..e20e07f --- /dev/null +++ b/modules/login/persistence/cms_user_oauth_storage_null.e @@ -0,0 +1,48 @@ +note + description: "Summary description for {CMS_USER_OAUTH_STORAGE_NULL}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER_OAUTH_STORAGE_NULL + +inherit + + CMS_USER_OAUTH_STORAGE_I + + +feature -- Error handler + + error_handler: ERROR_HANDLER + -- Error handler. + do + create Result.make + end + +feature -- Access + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + -- CMS User with Oauth gmail credential by id if any. + do + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- -- CMS User with Oauth gmail credential by access token `a_token' if any. + do + end + +feature -- Change: User Oauth2 + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + do + end + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Update user `a_user' with oauth2 gmail authentication. + do + end + + +end diff --git a/modules/login/persistence/cms_user_oauth_storage_sql.e b/modules/login/persistence/cms_user_oauth_storage_sql.e new file mode 100644 index 0000000..9a9a692 --- /dev/null +++ b/modules/login/persistence/cms_user_oauth_storage_sql.e @@ -0,0 +1,149 @@ +note + description: "Summary description for {CMS_USER_OAUTH_STORAGE_SQL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER_OAUTH_STORAGE_SQL + +inherit + CMS_USER_OAUTH_STORAGE_I + + CMS_PROXY_STORAGE_SQL + + CMS_USER_OAUTH_STORAGE_I + + CMS_STORAGE_SQL_I + + REFACTORING_HELPER + +create + make + +feature -- Access User Outh Gmail + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_oauth2_gmail_by_id") + create l_parameters.make (1) + l_parameters.put (a_uid, "uid") + sql_query (select_user_oauth2_gmail_by_id, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_by_oauth2_gmail_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (select_user_by_oauth2_gmail_token, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + +feature -- Change: User Oauth2 Gmail + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- . + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_oauth2_gmail") + create l_parameters.make (4) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (a_user_profile, "profile") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + sql_change (sql_insert_oauth2_gmail, l_parameters) + sql_commit_transaction + end + + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_oauth2_gmail") + create l_parameters.make (4) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (a_user_profile, "profile") + + sql_change (sql_update_oauth2_gmail, l_parameters) + sql_commit_transaction + 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_32 (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 Gmail. + + Sql_insert_oauth2_gmail: STRING = "INSERT INTO oauth2_gmail (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + + Sql_update_oauth2_gmail: STRING = "UPDATE oauth2_gmail SET access_token = :token, details = :profile WHERE uid =:uid;" + + Select_user_by_oauth2_gmail_token: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.access_token = :token;" + + Select_user_oauth2_gmail_by_id: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.uid = :uid;" + + +end diff --git a/src/persistence/user/cms_user_storage_i.e b/src/persistence/user/cms_user_storage_i.e index 777767f..bd82eda 100644 --- a/src/persistence/user/cms_user_storage_i.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -179,24 +179,4 @@ feature -- Change: User password recovery deferred end -feature -- Change: User Oauth2 - - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. - deferred - end - - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Update user `a_user' with oauth2 gmail authentication. - deferred - end - - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - deferred - end - - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - deferred - end - end diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e index cb6c12b..283fe5b 100644 --- a/src/persistence/user/cms_user_storage_null.e +++ b/src/persistence/user/cms_user_storage_null.e @@ -108,23 +108,4 @@ feature -- Change: User password recovery do end -feature -- Change User Oauth - - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. - do - end - - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Update user `a_user' with oauth2 gmail authentication. - do - end - - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - do - end - - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - do - end end diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 2f100d7..58e4723 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -578,83 +578,6 @@ feature -- Change: User password recovery sql_commit_transaction end - -feature -- User Oauth2 - - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. - local - l_parameters: STRING_TABLE [detachable ANY] - do - error_handler.reset - sql_begin_transaction - - write_information_log (generator + ".new_user_oauth2_gmail") - create l_parameters.make (4) - l_parameters.put (a_user.id, "uid") - l_parameters.put (a_token, "token") - l_parameters.put (a_user_profile, "profile") - l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - - sql_change (sql_insert_oauth2_gmail, l_parameters) - sql_commit_transaction - end - - - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. - local - l_parameters: STRING_TABLE [detachable ANY] - do - error_handler.reset - sql_begin_transaction - - write_information_log (generator + ".new_user_oauth2_gmail") - create l_parameters.make (4) - l_parameters.put (a_user.id, "uid") - l_parameters.put (a_token, "token") - l_parameters.put (a_user_profile, "profile") - - sql_change (sql_update_oauth2_gmail, l_parameters) - sql_commit_transaction - end - - - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - -- User for the given password token `a_token', if any. - local - l_parameters: STRING_TABLE [detachable ANY] - do - error_handler.reset - write_information_log (generator + ".user_by_oauth2_gmail_token") - create l_parameters.make (1) - l_parameters.put (a_token, "token") - sql_query (select_user_by_oauth2_gmail_token, l_parameters) - if sql_rows_count = 1 then - Result := fetch_user - else - check no_more_than_one: sql_rows_count = 0 end - end - end - - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - -- User for the given password token `a_token', if any. - local - l_parameters: STRING_TABLE [detachable ANY] - do - error_handler.reset - write_information_log (generator + ".user_oauth2_gmail_by_id") - create l_parameters.make (1) - l_parameters.put (a_uid, "uid") - sql_query (select_user_oauth2_gmail_by_id, l_parameters) - if sql_rows_count = 1 then - Result := fetch_user - else - check no_more_than_one: sql_rows_count = 0 end - end - end - - feature {NONE} -- Implementation: User user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_8 @@ -830,14 +753,4 @@ feature {NONE} -- User Password Recovery Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;" -- Retrieve user by password token if exist. -feature {NONE}-- User Oauth2 Gmail. - - Sql_insert_oauth2_gmail: STRING = "INSERT INTO oauth2_gmail (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" - - Sql_update_oauth2_gmail: STRING = "UPDATE oauth2_gmail SET access_token = :token, details = :profile WHERE uid =:uid;" - - Select_user_by_oauth2_gmail_token: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.access_token = :token;" - - Select_user_oauth2_gmail_by_id: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.uid = :uid;" - end diff --git a/src/service/user/cms_user_api.e b/src/service/user/cms_user_api.e index 87236e7..8465f91 100644 --- a/src/service/user/cms_user_api.e +++ b/src/service/user/cms_user_api.e @@ -151,35 +151,6 @@ feature -- Change User storage.update_user (a_user) end - - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. - require - has_id: a_user.has_id - do - storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) - end - - - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Updaate user `a_user' with oauth2 gmail authentication. - require - has_id: a_user.has_id - do - storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) - end - - - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - do - Result := storage.user_oauth2_gmail_by_id (a_uid) - end - - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - do - Result := storage.user_by_oauth2_gmail_token (a_token) - end - feature -- User Activation new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64) From f652aa8a155ec7ddb18ceee8ac0e04cc6659a82d Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 9 Jun 2015 19:44:52 -0300 Subject: [PATCH 5/8] Update Login Module. - Updated routes relative to /account/ - Updated emails with template support. - Updated cookie to support the new route. - Updated smarty block templates to use the new path. --- .../modules/login/account_activation.html | 18 ++ .../modules/login/account_new_password.html | 17 ++ .../modules/login/account_re_activation.html | 18 ++ .../config/modules/login/account_welcome.html | 13 ++ .../demo/site/config/modules/login/login.json | 5 +- .../{oauth_gmail.sql => oauth2_gmail.sql} | 0 .../modules/login/templates/block_login.tpl | 13 +- .../login/templates/block_new_password.tpl | 2 +- .../login/templates/block_reactivate.tpl | 2 +- .../login/templates/block_register.tpl | 2 +- .../login/templates/block_reset_password.tpl | 2 +- modules/login/filter/oauth_gmail_filter.e | 3 +- modules/login/login_constants.e | 13 ++ modules/login/login_email_service.e | 45 +++- .../login/login_email_service_parameters.e | 210 +++++++++++++++++- modules/login/login_module.e | 135 +++++------ modules/login/oauth_login_gmail.e | 2 +- 17 files changed, 393 insertions(+), 107 deletions(-) create mode 100644 examples/demo/site/config/modules/login/account_activation.html create mode 100644 examples/demo/site/config/modules/login/account_new_password.html create mode 100644 examples/demo/site/config/modules/login/account_re_activation.html create mode 100644 examples/demo/site/config/modules/login/account_welcome.html rename examples/demo/site/scripts/{oauth_gmail.sql => oauth2_gmail.sql} (100%) create mode 100644 modules/login/login_constants.e diff --git a/examples/demo/site/config/modules/login/account_activation.html b/examples/demo/site/config/modules/login/account_activation.html new file mode 100644 index 0000000..0ab4c4f --- /dev/null +++ b/examples/demo/site/config/modules/login/account_activation.html @@ -0,0 +1,18 @@ + + + + + Activation + + + + + +

Thank you for registering at ROC CMS

+ +

To complete your registration, please click on this link to activate your account:

+ +

$link

+

Thank you for joining us.

+ + diff --git a/examples/demo/site/config/modules/login/account_new_password.html b/examples/demo/site/config/modules/login/account_new_password.html new file mode 100644 index 0000000..dfcd355 --- /dev/null +++ b/examples/demo/site/config/modules/login/account_new_password.html @@ -0,0 +1,17 @@ + + + + + New Password + + + + + +

You have required a new password at ROC CMS

+ +

To complete your request, please click on this link to genereate a new password:

+ +

$link

+ + \ No newline at end of file diff --git a/examples/demo/site/config/modules/login/account_re_activation.html b/examples/demo/site/config/modules/login/account_re_activation.html new file mode 100644 index 0000000..95aebb2 --- /dev/null +++ b/examples/demo/site/config/modules/login/account_re_activation.html @@ -0,0 +1,18 @@ + + + + + New Activation + + + + + +

You have request a new activation token atROC CMS

+ +

To complete your registration, please click on this link to activate your account:

+ +

$link

+

Thank you for joining us.

+ + \ No newline at end of file diff --git a/examples/demo/site/config/modules/login/account_welcome.html b/examples/demo/site/config/modules/login/account_welcome.html new file mode 100644 index 0000000..0ea987c --- /dev/null +++ b/examples/demo/site/config/modules/login/account_welcome.html @@ -0,0 +1,13 @@ + + + + + Welcome + + + + +

Welcome toROC CMS

+

Thank you for joining us.

+ + \ No newline at end of file diff --git a/examples/demo/site/config/modules/login/login.json b/examples/demo/site/config/modules/login/login.json index 5b44da3..2f3e155 100644 --- a/examples/demo/site/config/modules/login/login.json +++ b/examples/demo/site/config/modules/login/login.json @@ -1,5 +1,8 @@ { "email": "webmaster@example.com", - "subjet": "Thank you for regitering with us", + "subjet_register": "Thank you for regitering with us, activate account", + "subjet_activate": "New account ativation token", + "subjet_password": "Password Recovery!!!", + "subjet_oauth": "Welcome", "smtp": "127.0.0.1" } diff --git a/examples/demo/site/scripts/oauth_gmail.sql b/examples/demo/site/scripts/oauth2_gmail.sql similarity index 100% rename from examples/demo/site/scripts/oauth_gmail.sql rename to examples/demo/site/scripts/oauth2_gmail.sql diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl index 18301e6..130aa76 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl @@ -1,9 +1,6 @@
- {if isset="$user"} -

Logout

- {/if} - {unless isset="$user"} -

Login or Register

+ {unless isset="$user"} +

Login or Register

@@ -24,12 +21,12 @@ - {/unless}
- +
+ {/unless}
\ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl index 84b5de0..ef5407f 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_new_password.tpl @@ -1,5 +1,5 @@
- +
Require new password
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl index 01a7960..bef65c0 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reactivate.tpl @@ -1,5 +1,5 @@
- +
Reactivate Form
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl index 522bde9..58a4872 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_register.tpl @@ -1,5 +1,5 @@
- +
Register Form
diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl index aa2ee81..b99289d 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_reset_password.tpl @@ -1,5 +1,5 @@
- +
Generate New Password Form
diff --git a/modules/login/filter/oauth_gmail_filter.e b/modules/login/filter/oauth_gmail_filter.e index 95c4d62..c4f6310 100644 --- a/modules/login/filter/oauth_gmail_filter.e +++ b/modules/login/filter/oauth_gmail_filter.e @@ -1,6 +1,5 @@ note description: "Summary description for {OAUTH_GMAIL_FILTER}." - author: "" date: "$Date$" revision: "$Revision$" @@ -42,7 +41,7 @@ feature -- Basic operations end -- A valid user if - attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_roc_auth_session_token + attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_gmail_session) as l_roc_auth_session_token then if attached {CMS_USER} user_oauth_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) diff --git a/modules/login/login_constants.e b/modules/login/login_constants.e new file mode 100644 index 0000000..39886dc --- /dev/null +++ b/modules/login/login_constants.e @@ -0,0 +1,13 @@ +note + description: "Summary description for {LOGIN_CONSTANTS}." + date: "$Date$" + revision: "$Revision$" + +class + LOGIN_CONSTANTS + +feature -- Access + + oauth_gmail_session: STRING = "EWF_ROC_OAUTH_GMAIL_SESSION_" + +end diff --git a/modules/login/login_email_service.e b/modules/login/login_email_service.e index 2ada59e..9d885b4 100644 --- a/modules/login/login_email_service.e +++ b/modules/login/login_email_service.e @@ -38,8 +38,51 @@ feature -- Basic Operations -- Send successful contact message `a_token' to `a_to'. require attached_to: a_to /= Void + local + l_message: STRING do - send_message (contact_email, a_to, parameters.contact_subject_text, a_content) + create l_message.make_from_string (parameters.account_activation) + l_message.replace_substring_all ("$link", a_content) + send_message (contact_email, a_to, parameters.contact_subject_register, l_message) end + + send_contact_activation_email (a_to, a_content: READABLE_STRING_8) + -- Send successful contact message `a_token' to `a_to'. + require + attached_to: a_to /= Void + local + l_message: STRING + do + create l_message.make_from_string (parameters.account_re_activation) + l_message.replace_substring_all ("$link", a_content) + send_message (contact_email, a_to, parameters.contact_subject_activate, l_message) + end + + + send_contact_password_email (a_to, a_content: READABLE_STRING_8) + -- Send successful contact message `a_token' to `a_to'. + require + attached_to: a_to /= Void + local + l_message: STRING + do + create l_message.make_from_string (parameters.account_password) + l_message.replace_substring_all ("$link", a_content) + send_message (contact_email, a_to, parameters.contact_subject_password, l_message) + end + + send_contact_welcome_email (a_to, a_content: READABLE_STRING_8) + -- Send successful contact message `a_token' to `a_to'. + require + attached_to: a_to /= Void + local + l_message: STRING + do + create l_message.make_from_string (parameters.account_welcome) + l_message.replace_substring_all ("$link", a_content) + send_message (contact_email, a_to, parameters.contact_subject_oauth, l_message) + end + + end diff --git a/modules/login/login_email_service_parameters.e b/modules/login/login_email_service_parameters.e index d356af0..b3e8feb 100644 --- a/modules/login/login_email_service_parameters.e +++ b/modules/login/login_email_service_parameters.e @@ -19,8 +19,9 @@ feature {NONE} -- Initialization utf: UTF_CONVERTER l_site_name: READABLE_STRING_8 s: detachable READABLE_STRING_32 - l_contact_email, l_contact_subject: detachable READABLE_STRING_8 + l_contact_email, l_subject_register, l_subject_activate, l_subject_password, l_subject_oauth: detachable READABLE_STRING_8 do + setup := a_cms_api.setup -- Use global smtp setting if any, otherwise "localhost" smtp_server := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.text_item_or_default ("smtp", "localhost")) l_site_name := utf.escaped_utf_32_string_to_utf_8_string_8 (a_cms_api.setup.site_name) @@ -39,10 +40,23 @@ feature {NONE} -- Initialization if s /= Void then l_contact_email := utf.utf_32_string_to_utf_8_string_8 (s) end - s := cfg.text_item ("subject") + s := cfg.text_item ("subject_register") if s /= Void then - l_contact_subject := utf.utf_32_string_to_utf_8_string_8 (s) + l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s) end + s := cfg.text_item ("subject_activate") + if s /= Void then + l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s) + end + s := cfg.text_item ("subject_password") + if s /= Void then + l_subject_register := utf.utf_32_string_to_utf_8_string_8 (s) + end + s := cfg.text_item ("subject_oauth") + if s /= Void then + l_subject_oauth := utf.utf_32_string_to_utf_8_string_8 (s) + end + end if l_contact_email /= Void then if not l_contact_email.has ('<') then @@ -52,11 +66,28 @@ feature {NONE} -- Initialization else contact_email := admin_email end - if l_contact_subject /= Void then - contact_subject_text := l_contact_subject + if l_subject_register /= Void then + contact_subject_register := l_subject_register else - contact_subject_text := "Thank you for registering with us" + contact_subject_register := "Thank you for registering with us." end + + if l_subject_activate /= Void then + contact_subject_activate := l_subject_activate + else + contact_subject_activate := "New account activation token." + end + if l_subject_password /= Void then + contact_subject_password := l_subject_password + else + contact_subject_password := "Password Recovery." + end + if l_subject_oauth /= Void then + contact_subject_oauth := l_subject_oauth + else + contact_subject_oauth := "Welcome." + end + end feature -- Access @@ -68,6 +99,171 @@ feature -- Access contact_email: IMMUTABLE_STRING_8 -- Contact email. - contact_subject_text: IMMUTABLE_STRING_8 + contact_subject_register: IMMUTABLE_STRING_8 + contact_subject_activate: IMMUTABLE_STRING_8 + contact_subject_password: IMMUTABLE_STRING_8 + contact_subject_oauth: IMMUTABLE_STRING_8 + + + + account_activation: STRING + -- Account activation template email message. + local + p: PATH + do + p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_activation.html") + if attached read_template_file (p) as l_content then + Result := l_content + else + create Result.make_from_string (template_account_activation) + end + end + + account_re_activation: STRING + -- Account re_activation template email message. + local + p: PATH + do + p := setup.environment.config_path.extended ("modules").extended ("login").extended("accunt_re_activation.html") + if attached read_template_file (p) as l_content then + Result := l_content + else + create Result.make_from_string (template_account_re_activation) + end + end + + account_password: STRING + -- Account password template email message. + local + p: PATH + do + p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_new_password.html") + if attached read_template_file (p) as l_content then + Result := l_content + else + create Result.make_from_string (template_account_new_password) + end + end + + account_welcome: STRING + -- Account welcome template email message. + local + p: PATH + do + p := setup.environment.config_path.extended ("modules").extended ("login").extended("account_welcome.html") + if attached read_template_file (p) as l_content then + Result := l_content + else + create Result.make_from_string (template_account_welcome) + end + end + +feature {NONE} -- Implementation + + setup: CMS_SETUP + + + read_template_file (a_path: PATH): detachable STRING + -- Read the content of the file at path `a_path'. + local + l_file: FILE + l_content: STRING + do + create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path) + if l_file.exists and then l_file.is_readable then + l_file.open_read + l_file.read_stream (l_file.count) + Result := l_file.last_string + l_file.close + else + -- Error + end + end + + +feature {NONE} -- Message email + + template_account_activation: STRING= "[ + + + + + Activation + + + + + +

Thank you for registering at ROC CMS

+ +

To complete your registration, please click on this link to activate your account:

+ +

$link

+

Thank you for joining us.

+ + + ]" + + + template_account_re_activation: STRING= "[ + + + + + New Activation + + + + + +

You have request a new activation token atROC CMS

+ +

To complete your registration, please click on this link to activate your account:

+ +

$link

+

Thank you for joining us.

+ + + ]" + + + + template_account_new_password: STRING= "[ + + + + + New Password + + + + + +

You have required a new password at ROC CMS

+ +

To complete your request, please click on this link to genereate a new password:

+ +

$link

+ + + ]" + + + template_account_welcome: STRING= "[ + + + + + Welcome + + + + + +

Welcome toROC CMS

+

Thank you for joining us.

+ + + ]" end diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 3c336cd..4db73b7 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -39,6 +39,7 @@ inherit CMS_REQUEST_UTIL + create make @@ -100,7 +101,7 @@ feature {CMS_API} -- Module management if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then if not l_sql_storage.sql_table_exists ("oauth2_gmail") then --| Schema - l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("core.sql")) + l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_gmail.sql")) if l_sql_storage.has_error then api.logger.put_error ("Could not initialize database for blog module", generating_type) @@ -156,16 +157,15 @@ feature -- Router configure_web (a_api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; a_router: WSF_ROUTER) do - a_router.handle_with_request_methods ("/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle_with_request_methods ("/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (a_api, ?, ?)), a_router.methods_head_get) - a_router.handle_with_request_methods ("/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) - + a_router.handle_with_request_methods ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) + a_router.handle_with_request_methods ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/activate/{token}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_activation (a_api, ?, ?)), a_router.methods_head_get) + a_router.handle_with_request_methods ("/account/reactivate", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reactivation (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) end @@ -196,9 +196,9 @@ feature -- Hooks lnk: CMS_LOCAL_LINK do if attached a_response.current_user (a_response.request) as u then - create lnk.make (u.name + " (Logout)", "roc-logout" ) + create lnk.make (u.name + " (Logout)", "account/roc-logout" ) else - create lnk.make ("Login", "roc-login") + create lnk.make ("Login", "account/roc-login") end a_menu_system.primary_menu.extend (lnk) lnk.set_weight (98) @@ -223,27 +223,27 @@ feature -- Hooks do if a_block_id.is_case_insensitive_equal_general ("login") and then - a_response.request.path_info.starts_with ("/roc-login") + a_response.request.path_info.starts_with ("/account/roc-login") then get_block_view_login (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("register") and then - a_response.request.path_info.starts_with ("/roc-register") + a_response.request.path_info.starts_with ("/account/roc-register") then get_block_view_register (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("reactivate") and then - a_response.request.path_info.starts_with ("/reactivate") + a_response.request.path_info.starts_with ("/account/reactivate") then get_block_view_reactivate (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("new_password") and then - a_response.request.path_info.starts_with ("/new-password") + a_response.request.path_info.starts_with ("/account/new-password") then get_block_view_new_password (a_block_id, a_response) elseif a_block_id.is_case_insensitive_equal_general ("reset_password") and then - a_response.request.path_info.starts_with ("/reset-password") + a_response.request.path_info.starts_with ("/account/reset-password") then get_block_view_reset_password (a_block_id, a_response) end @@ -259,6 +259,15 @@ feature -- Hooks r.execute end + handle_workaround_filter (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + br: BAD_REQUEST_ERROR_CMS_RESPONSE + do + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.execute + end + handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local @@ -268,13 +277,14 @@ feature -- Hooks l_cookie: WSF_COOKIE do if - attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_cookie_token and then + attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_gmail_session) as l_cookie_token and then attached {CMS_USER} current_user (req) as l_user then -- Logout gmail create l_oauth_gmail.make (api, req.absolute_script_url ("")) l_oauth_gmail.sign_out (l_cookie_token.value) - create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_cookie_token.value) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_cookie_token.value) + l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) unset_current_user (req) @@ -340,16 +350,14 @@ feature -- Hooks l_token := new_token l_user_api.new_activation (l_token, u.id) create l_link.make_from_string (req.server_url) - l_link.append ("/activate/") + l_link.append ("/account/activate/") l_link.append (l_token) - create l_message.make_from_string (account_activation) - l_message.replace_substring_all ("$link", l_link) -- Send Email create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_email") - es.send_contact_email (l_email.value, l_message) + es.send_contact_email (l_email.value, l_link) else r.values.force (l_name.value, "name") @@ -385,7 +393,7 @@ feature -- Hooks -- the token does not exist, or it was already used. r.set_status_code ({HTTP_CONSTANTS}.bad_request) r.set_value ("Account not activated", "optional_content_type") - r.set_main_content ("

The token "+ l_token.value +" is not valid Reactivate Account

" ) + r.set_main_content ("

The token "+ l_token.value +" is not valid Reactivate Account

" ) end r.execute @@ -421,16 +429,13 @@ feature -- Hooks l_token := new_token l_user_api.new_activation (l_token, l_user.id) create l_link.make_from_string (req.server_url) - l_link.append ("/activate/") + l_link.append ("/account/activate/") l_link.append (l_token) - create l_message.make_from_string (account_activation) - l_message.replace_substring_all ("$link", l_link) - -- Send Email create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) - write_debug_log (generator + ".handle register: send_contact_email") - es.send_contact_email (l_email.value, l_message) + write_debug_log (generator + ".handle register: send_contact_activation_email") + es.send_contact_activation_email (l_email.value, l_link) end else r.values.force ("The email does not exist or !", "error_email") @@ -462,16 +467,13 @@ feature -- Hooks l_token := new_token l_user_api.new_password (l_token, l_user.id) create l_link.make_from_string (req.server_url) - l_link.append ("/reset-password?token=") + l_link.append ("/account/reset-password?token=") l_link.append (l_token) - create l_message.make_from_string (account_new_password) - l_message.replace_substring_all ("$link", l_link) - -- Send Email create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) - write_debug_log (generator + ".handle register: send_contact_email") - es.send_contact_email (l_email.value, l_message) + write_debug_log (generator + ".handle register: send_contact_password_email") + es.send_contact_password_email (l_email.value, l_link) else r.values.force ("The email does not exist !", "error_email") r.values.force (l_email.value, "email") @@ -497,7 +499,7 @@ feature -- Hooks if attached {WSF_STRING} req.query_parameter ("token") as l_token then r.values.force (l_token.value, "token") if l_user_api.user_by_password_token (l_token.value) = Void then - r.values.force ("The token " + l_token.value + " is not valid, click here to generate a new token.", "error_token") + r.values.force ("The token " + l_token.value + " is not valid, click here to generate a new token.", "error_token") r.set_status_code ({HTTP_CONSTANTS}.bad_request) end end @@ -743,6 +745,7 @@ feature -- OAuth2 Login with google. l_user: CMS_USER l_roles: LIST [CMS_USER_ROLE] l_cookie: WSF_COOKIE + es: LOGIN_EMAIL_SERVICE do if attached {WSF_STRING} req.query_parameter ("code") as l_code then create l_auth_gmail.make (api, req.server_url) @@ -768,8 +771,9 @@ feature -- OAuth2 Login with google. -- create a oauth entry a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) end - create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) + l_cookie.set_path ("/") res.add_cookie (l_cookie) else @@ -786,13 +790,19 @@ feature -- OAuth2 Login with google. -- Add oauth entry a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) - create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) + l_cookie.set_path ("/") res.add_cookie (l_cookie) + set_current_user (req, l_user) + + + -- Send Email + create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + write_debug_log (generator + ".handle register: send_contact_welcome_email") + es.send_contact_welcome_email (l_email, "") end else - - end r.set_redirection (req.absolute_script_url ("")) r.execute @@ -823,48 +833,7 @@ feature {NONE} -- Token Generation Result := l_token end -feature --{NONE} -- Message email - account_activation: STRING= "[ - - - - - Eiffel.org Activation - - - - - -

Thank you for registering at Eiffel.org

- -

To complete your registration, please click on this link to activate your account:

- -

$link

-

Thank you for joining us.

- - - ]" - - account_new_password: STRING= "[ - - - - - Eiffel.org New Password - - - - - -

You have required a new password at Eiffel.org

- -

To complete your request, please click on this link to genereate a new password:

- -

$link

- - - ]" feature {NONE} -- Implementation: date and time diff --git a/modules/login/oauth_login_gmail.e b/modules/login/oauth_login_gmail.e index 1c484c9..f84c622 100644 --- a/modules/login/oauth_login_gmail.e +++ b/modules/login/oauth_login_gmail.e @@ -21,7 +21,7 @@ feature {NONE} -- Initialization cms_api := a_cms_api initilize create config.make_default (api_key, api_secret) - config.set_callback (a_host + "/oauthgmail") + config.set_callback (a_host + "/account/oauthgmail") config.set_scope (scope) create goauth api_service := goauth.create_service (config) From 18732a95327a2c88d0aaa182d66c231ace591f84 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 11 Jun 2015 10:01:36 -0300 Subject: [PATCH 6/8] Updated Login Module. - OAUTH LOGIN: is generic based on a new OAUTH_20_GENERIC_API - Storage (at the moment only SQL) for OAUTH_CONSUMER configuration. - OAUTH login and callback are generic. - Added a OAUTH_20_GENERIC_API. - Added scripts and templates to build the new OAUTH tables. - Fixed CMS_STORAGE_SQL_I.check_sql_query_validity issue. - Extended CMS_STORAGE_SQL_I, to execute scripts with paramerters. - Updated filter, now it's generic for every OAUTH consumer. --- .../demo/site/scripts/oauth2_consumers.sql | 20 ++ .../scripts/oauth2_consumers_initialize.sql | 11 + .../{oauth2_gmail.sql => oauth2_template.sql} | 4 +- .../modules/login/templates/block_login.tpl | 6 +- examples/demo/site/themes/bootstrap/page.tpl | 2 +- modules/login/cms_oauth_consumer.e | 152 ++++++++++++ modules/login/cms_user_oauth_api.e | 52 +++- .../{oauth_gmail_filter.e => oauth_filter.e} | 14 +- modules/login/login_constants.e | 4 +- modules/login/login_module.e | 225 +++++++++++++++--- .../{oauth_login_gmail.e => oauth_login.e} | 79 ++---- .../persistence/cms_oauth_20_generic_api.e | 94 ++++++++ .../persistence/cms_user_oauth_storage_i.e | 26 +- .../persistence/cms_user_oauth_storage_null.e | 25 +- .../persistence/cms_user_oauth_storage_sql.e | 94 ++++++-- src/persistence/sql/cms_storage_sql_i.e | 34 ++- 16 files changed, 688 insertions(+), 154 deletions(-) create mode 100644 examples/demo/site/scripts/oauth2_consumers.sql create mode 100644 examples/demo/site/scripts/oauth2_consumers_initialize.sql rename examples/demo/site/scripts/{oauth2_gmail.sql => oauth2_template.sql} (71%) create mode 100644 modules/login/cms_oauth_consumer.e rename modules/login/filter/{oauth_gmail_filter.e => oauth_filter.e} (66%) rename modules/login/{oauth_login_gmail.e => oauth_login.e} (61%) create mode 100644 modules/login/persistence/cms_oauth_20_generic_api.e diff --git a/examples/demo/site/scripts/oauth2_consumers.sql b/examples/demo/site/scripts/oauth2_consumers.sql new file mode 100644 index 0000000..6bf79d6 --- /dev/null +++ b/examples/demo/site/scripts/oauth2_consumers.sql @@ -0,0 +1,20 @@ +BEGIN; + +CREATE TABLE "oauth2_consumers"( + "cid" INTEGER PRIMARY KEY NOT NULL CHECK("cid">=0), + "name" VARCHAR(255) NOT NULL, + "api_secret" TEXT NOT NULL, + "api_key" TEXT NOT NULL, + "scope" VARCHAR (100) NOT NULL, + "protected_resource_url" VARCHAR (255) NOT NULL, + "callback_name" VARCHAR(255) NOT NULL, + "extractor" VARCHAR(50) NOT NULL, + "authorize_url" VARCHAR (255) NOT NULL, + "endpoint" VARCHAR (255) NOT NULL, + CONSTRAINT "cid" + UNIQUE("cid"), + CONSTRAINT "name" + UNIQUE("name") + ); + +COMMIT; \ No newline at end of file diff --git a/examples/demo/site/scripts/oauth2_consumers_initialize.sql b/examples/demo/site/scripts/oauth2_consumers_initialize.sql new file mode 100644 index 0000000..16766a7 --- /dev/null +++ b/examples/demo/site/scripts/oauth2_consumers_initialize.sql @@ -0,0 +1,11 @@ +BEGIN; + + -- Change the values `TO_COMPLETE' based on your API. + -- API SECTET KEY AND API PUBLIC KEY + +INSERT INTO "oauth2_consumers" ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint") +VALUES ("google", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://www.googleapis.com/plus/v1/people/me', "callback_google", "json","https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://accounts.google.com/o/oauth2/token"); +INSERT INTO "oauth2_consumers" ("name", "api_secret", "api_key", "scope", "protected_resource_url", "callback_name", "extractor", "authorize_url", "endpoint" ) +VALUES ("facebook", 'TO-COMPLETE', 'TO-COMPLETE', 'email', 'https://graph.facebook.com/me', "callback_facebook","text","https://www.facebook.com/dialog/oauth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI","https://graph.facebook.com/oauth/access_token"); + +COMMIT; \ No newline at end of file diff --git a/examples/demo/site/scripts/oauth2_gmail.sql b/examples/demo/site/scripts/oauth2_template.sql similarity index 71% rename from examples/demo/site/scripts/oauth2_gmail.sql rename to examples/demo/site/scripts/oauth2_template.sql index 3efbe55..6c1fa68 100644 --- a/examples/demo/site/scripts/oauth2_gmail.sql +++ b/examples/demo/site/scripts/oauth2_template.sql @@ -1,9 +1,9 @@ BEGIN; -CREATE TABLE "oauth2_gmail"( +CREATE TABLE :table_name ( "uid" INTEGER PRIMARY KEY NOT NULL CHECK("uid">=0), - "access_token" VARCHAR(255) NOT NULL, + "access_token" TEXT NOT NULL, "created" DATETIME NOT NULL, "details" TEXT NOT NULL, CONSTRAINT "uid" diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl index 130aa76..dda41b5 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl @@ -1,4 +1,4 @@ -
+
{unless isset="$user"}

Login or Register

@@ -26,7 +26,9 @@
- + {foreach item="item" from="$oauth_consumers"} + Login with {$item/}
+ {/foreach}
{/unless}
\ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/page.tpl b/examples/demo/site/themes/bootstrap/page.tpl index 9bf0155..a587c0f 100644 --- a/examples/demo/site/themes/bootstrap/page.tpl +++ b/examples/demo/site/themes/bootstrap/page.tpl @@ -57,7 +57,7 @@ {unless isempty="$page_title"}

{$page_title/}

{/unless} {$page.region_content/} -
+
diff --git a/modules/login/cms_oauth_consumer.e b/modules/login/cms_oauth_consumer.e new file mode 100644 index 0000000..fda8210 --- /dev/null +++ b/modules/login/cms_oauth_consumer.e @@ -0,0 +1,152 @@ +note + description: "Summary description for {CMS_OAUTH_CONSUMER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_OAUTH_CONSUMER + +inherit + + ANY + redefine + default_create + end + +create + default_create + +feature {NONE} -- Initialization + + default_create + do + set_endpoint ("") + set_authorize_url ("") + set_extractor ("") + set_callback_name ("") + set_protected_resource_url ("") + set_scope ("") + set_api_key ("") + set_api_secret ("") + set_name ("") + end + +feature -- Access + + endpoint: READABLE_STRING_32 + -- Url that receives the access token request. + + authorize_url: READABLE_STRING_32 + -- + + extractor: READABLE_STRING_32 + -- text, json + + + callback_name: READABLE_STRING_32 + -- consumer callback name + + protected_resource_url: READABLE_STRING_32 + -- consumer resource url + + scope: READABLE_STRING_32 + -- consumer scope + + api_key: READABLE_STRING_32 + -- consumer public key + + api_secret: READABLE_STRING_32 + -- consumer secret. + + name: READABLE_STRING_32 + -- consumer name. + + id: INTEGER_64 + -- unique identifier. + + + +feature -- Element change + + set_extractor (a_extractor: like extractor) + -- Assign `extractor' with `a_extractor'. + do + extractor := a_extractor + ensure + extractor_assigned: extractor = a_extractor + end + + set_authorize_url (a_authorize_url: like authorize_url) + -- Assign `authorize_url' with `a_authorize_url'. + do + authorize_url := a_authorize_url + ensure + authorize_url_assigned: authorize_url = a_authorize_url + end + + set_endpoint (a_endpoint: like endpoint) + -- Assign `endpoint' with `a_endpoint'. + do + endpoint := a_endpoint + ensure + endpoint_assigned: endpoint = a_endpoint + end + + set_callback_name (a_callback_name: like callback_name) + -- Assign `callback_name' with `a_callback_name'. + do + callback_name := a_callback_name + ensure + callback_name_assigned: callback_name = a_callback_name + end + + set_protected_resource_url (a_protected_resource_url: like protected_resource_url) + -- Assign `protected_resource_url' with `a_protected_resource_url'. + do + protected_resource_url := a_protected_resource_url + ensure + protected_resource_url_assigned: protected_resource_url = a_protected_resource_url + end + + set_scope (a_scope: like scope) + -- Assign `scope' with `a_scope'. + do + scope := a_scope + ensure + scope_assigned: scope = a_scope + end + + set_api_key (an_api_key: like api_key) + -- Assign `api_key' with `an_api_key'. + do + api_key := an_api_key + ensure + api_key_assigned: api_key = an_api_key + end + + set_api_secret (an_api_secret: like api_secret) + -- Assign `api_secret' with `an_api_secret'. + do + api_secret := an_api_secret + ensure + api_secret_assigned: api_secret = an_api_secret + end + + set_name (a_name: like name) + -- Assign `name' with `a_name'. + do + name := a_name + ensure + name_assigned: name = a_name + end + + set_id (an_id: like id) + -- Assign `id' with `an_id'. + do + id := an_id + ensure + id_assigned: id = an_id + end + +end diff --git a/modules/login/cms_user_oauth_api.e b/modules/login/cms_user_oauth_api.e index a9a7bb6..4e2ed38 100644 --- a/modules/login/cms_user_oauth_api.e +++ b/modules/login/cms_user_oauth_api.e @@ -31,34 +31,70 @@ feature {CMS_MODULE} -- Access user oauth storage. feature -- Access: OAuth2 Gmail - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER +-- user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER +-- do +-- Result := user_oauth_storage.user_oauth2_gmail_by_id (a_uid) +-- end + +-- user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER +-- do +-- Result := user_oauth_storage.user_by_oauth2_gmail_token (a_token) +-- end + + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER do - Result := user_oauth_storage.user_oauth2_gmail_by_id (a_uid) + Result := user_oauth_storage.user_oauth2_by_id (a_uid, a_consumer_table) end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER do - Result := user_oauth_storage.user_by_oauth2_gmail_token (a_token) + Result := user_oauth_storage.user_by_oauth2_token (a_token, a_consumer_table) end + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + do + Result := user_oauth_storage.user_by_oauth2_global_token (a_token) + end + + oauth2_consumers: LIST [STRING] + do + Result := user_oauth_storage.oauth2_consumers + end feature -- Change: OAuth2 Gmail - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) +-- new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) +-- -- Add a new user with oauth2 gmail authentication. +-- require +-- has_id: a_user.has_id +-- do +-- user_oauth_storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) +-- end + + +-- update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) +-- -- Updaate user `a_user' with oauth2 gmail authentication. +-- require +-- has_id: a_user.has_id +-- do +-- user_oauth_storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) +-- end + + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) -- Add a new user with oauth2 gmail authentication. require has_id: a_user.has_id do - user_oauth_storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) + user_oauth_storage.new_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) end - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) -- Updaate user `a_user' with oauth2 gmail authentication. require has_id: a_user.has_id do - user_oauth_storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) + user_oauth_storage.update_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) end end diff --git a/modules/login/filter/oauth_gmail_filter.e b/modules/login/filter/oauth_filter.e similarity index 66% rename from modules/login/filter/oauth_gmail_filter.e rename to modules/login/filter/oauth_filter.e index c4f6310..d7c1f45 100644 --- a/modules/login/filter/oauth_gmail_filter.e +++ b/modules/login/filter/oauth_filter.e @@ -1,10 +1,10 @@ note - description: "Summary description for {OAUTH_GMAIL_FILTER}." + description: "Summary description for {OAUTH_FILTER}." date: "$Date$" revision: "$Revision$" class - OAUTH_GMAIL_FILTER + OAUTH_FILTER inherit WSF_URI_TEMPLATE_HANDLER @@ -36,14 +36,14 @@ feature -- Basic operations utf: UTF_CONVERTER do api.logger.put_debug (generator + ".execute ", Void) - if attached req.raw_header_data as l_raw_data then - api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) - end +-- if attached req.raw_header_data as l_raw_data then +-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) +-- end -- A valid user if - attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_gmail_session) as l_roc_auth_session_token + attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_session) as l_roc_auth_session_token then - if attached {CMS_USER} user_oauth_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then + if attached {CMS_USER} user_oauth_api.user_by_oauth2_global_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) execute_next (req, res) else diff --git a/modules/login/login_constants.e b/modules/login/login_constants.e index 39886dc..ffae849 100644 --- a/modules/login/login_constants.e +++ b/modules/login/login_constants.e @@ -8,6 +8,6 @@ class feature -- Access - oauth_gmail_session: STRING = "EWF_ROC_OAUTH_GMAIL_SESSION_" - + oauth_session: STRING = "EWF_ROC_OAUTH_TOKEN_" + end diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 4db73b7..e649c95 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -94,18 +94,47 @@ feature {CMS_API} -- Module management local sql: STRING l_setup: CMS_SETUP + l_params: detachable STRING_TABLE [detachable ANY] + l_consumers: LIST [STRING] do l_setup := api.setup -- Schema if attached {CMS_STORAGE_SQL_I} api.storage as l_sql_storage then - if not l_sql_storage.sql_table_exists ("oauth2_gmail") then + if not l_sql_storage.sql_table_exists ("oauth2_consumers") then --| Schema - l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_gmail.sql")) + l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers.sql")) if l_sql_storage.has_error then api.logger.put_error ("Could not initialize database for blog module", generating_type) end + -- TODO workaround. + l_sql_storage.sql_execute_file_script (l_setup.environment.path.extended ("scripts").extended ("oauth2_consumers_initialize.sql")) + end + + -- TODO workaround, until we have an admin module + l_sql_storage.sql_query ("SELECT name FROM oauth2_consumers;", Void) + if l_sql_storage.has_error then + api.logger.put_error ("Could not initialize database for differnent consumerns", generating_type) + else + from + l_sql_storage.sql_start + create {ARRAYED_LIST[STRING]} l_consumers.make (2) + until + l_sql_storage.sql_after + loop + if attached l_sql_storage.sql_read_string (1) as l_name then + l_consumers.force ("oauth2_"+l_name) + end + l_sql_storage.sql_forth + end + across l_consumers as ic loop + if not l_sql_storage.sql_table_exists (ic.item) then + create l_params.make (1) + l_params.force (ic.item, "table_name") + l_sql_storage.sql_execute_file_script_with_params (l_setup.environment.path.extended ("scripts").extended ("oauth2_template.sql"), l_params) + end + end end api.storage.set_custom_value ("is_initialized", "module-" + name, "yes") end @@ -116,7 +145,6 @@ feature {CMS_API} -- Access: API user_oauth_api: detachable CMS_USER_OAUTH_API -- - feature -- Filters filters (a_api: CMS_API): detachable LIST [WSF_FILTER] @@ -124,7 +152,7 @@ feature -- Filters do create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached user_oauth_api as l_user_oauth_api then - Result.extend (create {OAUTH_GMAIL_FILTER}.make (a_api, l_user_oauth_api)) + Result.extend (create {OAUTH_FILTER}.make (a_api, l_user_oauth_api)) end end @@ -164,8 +192,8 @@ feature -- Router a_router.handle_with_request_methods ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/account/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/account/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) end @@ -273,17 +301,15 @@ feature -- Hooks local r: CMS_RESPONSE l_url: STRING - l_oauth_gmail: OAUTH_LOGIN_GMAIL + l_oauth_gmail: OAUTH_LOGIN l_cookie: WSF_COOKIE do if - attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_gmail_session) as l_cookie_token and then + attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_session) as l_cookie_token and then attached {CMS_USER} current_user (req) as l_user then -- Logout gmail - create l_oauth_gmail.make (api, req.absolute_script_url ("")) - l_oauth_gmail.sign_out (l_cookie_token.value) - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_cookie_token.value) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_cookie_token.value) l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) @@ -563,7 +589,14 @@ feature {NONE} -- Block views loop l_tpl_block.set_value (ic.item, ic.key) end - a_response.add_block (l_tpl_block, "content") + if + attached user_oauth_api as l_auth_api and then + attached l_auth_api.oauth2_consumers as l_list + then + l_tpl_block.set_value (l_list, "oauth_consumers") + end + + a_response.add_block (l_tpl_block, "content") else debug ("cms") a_response.add_warning_message ("Error with block [" + a_block_id + "]") @@ -720,16 +753,25 @@ feature {NONE} -- Block views feature -- OAuth2 Login with google. - handle_login_with_google (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_login_with_oauth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_oauth_gmail: OAUTH_LOGIN_GMAIL + l_oauth: OAUTH_LOGIN do - create l_oauth_gmail.make (api, req.absolute_script_url ("")) - if attached l_oauth_gmail.authorization_url as l_authorization then - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - r.set_redirection (l_authorization) - r.execute + if + attached {WSF_STRING} req.path_parameter ("callback") as p_consumer and then + attached {CMS_OAUTH_CONSUMER} oauth_consumer_by_name (api, p_consumer.value) as l_consumer + then + create l_oauth.make (req.server_url, l_consumer) + if attached l_oauth.authorization_url as l_authorization_url then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_redirection (l_authorization_url) + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("Bad request") + r.execute + end else create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) r.set_main_content ("Bad request") @@ -737,22 +779,25 @@ feature -- OAuth2 Login with google. end end - handle_callback_gmail (api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_auth_gmail: OAUTH_LOGIN_GMAIL + l_auth: OAUTH_LOGIN l_user_api: CMS_USER_API l_user: CMS_USER l_roles: LIST [CMS_USER_ROLE] l_cookie: WSF_COOKIE es: LOGIN_EMAIL_SERVICE do - if attached {WSF_STRING} req.query_parameter ("code") as l_code then - create l_auth_gmail.make (api, req.server_url) - l_auth_gmail.sign_request (l_code.value) + if attached {WSF_STRING} req.path_parameter ("callback") as l_callback and then + attached {CMS_OAUTH_CONSUMER} oauth_consumer_by_callback (api, l_callback.value) as l_consumer and then + attached {WSF_STRING} req.query_parameter ("code") as l_code + then + create l_auth.make (req.server_url, l_consumer) + l_auth.sign_request (l_code.value) if - attached l_auth_gmail.access_token as l_access_token and then - attached l_auth_gmail.user_profile as l_user_profile + attached l_auth.access_token as l_access_token and then + attached l_auth.user_profile as l_user_profile then create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) -- extract user email @@ -760,18 +805,18 @@ feature -- OAuth2 Login with google. l_user_api := api.user_api -- 1 if the user exit put it in the context if - attached l_auth_gmail.user_email as l_email + attached l_auth.user_email as l_email then if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then -- User with email exist - if attached {CMS_USER} a_user_oauth_api.user_oauth2_gmail_by_id (p_user.id) then + if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, "oauth2_" + l_consumer.name) then -- Update oauth entry - a_user_oauth_api.update_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, "oauth2_" + l_consumer.name ) else -- create a oauth entry - a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, "oauth2_" + l_consumer.name ) end - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_access_token.token) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) @@ -789,8 +834,8 @@ feature -- OAuth2 Login with google. l_user_api.new_user (l_user) -- Add oauth entry - a_user_oauth_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_gmail_session, l_access_token.token) + a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, "oauth_" + l_consumer.name ) + create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) @@ -865,6 +910,120 @@ feature {NONE} -- Implementation: date and time Result := d.date_time end +feature --{NONE} -- Helper OAUTH Consumers. + + + oauth_consumer_by_name (a_api: CMS_API; a_name: READABLE_STRING_8): detachable CMS_OAUTH_CONSUMER + local + l_params: detachable STRING_TABLE [detachable ANY] + l_setup: CMS_SETUP + do + -- TODO workaround!!, move to the persistence layer + l_setup := a_api.setup + + -- Schema + if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then + + -- Todo workaround, move this to his own database layer. + create l_params.make (1) + l_params.force (a_name, "name") + l_sql_storage.sql_query ("SELECT * FROM oauth2_consumers where name =:name;", l_params) + if l_sql_storage.has_error then + a_api.logger.put_error ("Could not retrieve a consumer from the database", generating_type) + else + -- Fetch a Consumer + create Result + if attached l_sql_storage.sql_read_integer_64 (1) as l_id then + Result.set_id (l_id) + end + if attached l_sql_storage.sql_read_string_32 (2) as l_name then + Result.set_name (l_name) + end + if attached l_sql_storage.sql_read_string_32 (3) as l_api_secret then + Result.set_api_secret (l_api_secret) + end + if attached l_sql_storage.sql_read_string_32 (4) as l_api_key then + Result.set_api_key (l_api_key) + end + if attached l_sql_storage.sql_read_string_32 (5) as l_scope then + Result.set_scope (l_scope) + end + if attached l_sql_storage.sql_read_string_32 (6) as l_resource_url then + Result.set_protected_resource_url (l_resource_url) + end + if attached l_sql_storage.sql_read_string_32 (7) as l_callback_name then + Result.set_callback_name (l_callback_name) + end + if attached l_sql_storage.sql_read_string_32 (8) as l_extractor then + Result.set_extractor (l_extractor) + end + if attached l_sql_storage.sql_read_string_32 (9) as l_authorize_url then + Result.set_authorize_url (l_authorize_url) + end + if attached l_sql_storage.sql_read_string_32 (10) as l_endpoint then + Result.set_endpoint (l_endpoint) + end + end + end + end + + + oauth_consumer_by_callback (a_api: CMS_API; a_name: READABLE_STRING_8): detachable CMS_OAUTH_CONSUMER + local + l_params: detachable STRING_TABLE [detachable ANY] + l_setup: CMS_SETUP + do + -- TODO workaround !!! move to the persistence layer. + l_setup := a_api.setup + + + -- Schema + if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then + + -- Todo workaround, move this to his own database layer. + create l_params.make (1) + l_params.force (a_name, "name") + l_sql_storage.sql_query ("SELECT * FROM oauth2_consumers where callback_name =:name;", l_params) + if l_sql_storage.has_error then + a_api.logger.put_error ("Could not retrieve a consumer from the database", generating_type) + else + -- Fetch a Consumer + create Result + if attached l_sql_storage.sql_read_integer_64 (1) as l_id then + Result.set_id (l_id) + end + if attached l_sql_storage.sql_read_string_32 (2) as l_name then + Result.set_name (l_name) + end + if attached l_sql_storage.sql_read_string_32 (3) as l_api_secret then + Result.set_api_secret (l_api_secret) + end + if attached l_sql_storage.sql_read_string_32 (4) as l_api_key then + Result.set_api_key (l_api_key) + end + if attached l_sql_storage.sql_read_string_32 (5) as l_scope then + Result.set_scope (l_scope) + end + if attached l_sql_storage.sql_read_string_32 (6) as l_resource_url then + Result.set_protected_resource_url (l_resource_url) + end + if attached l_sql_storage.sql_read_string_32 (7) as l_callback_name then + Result.set_callback_name (l_callback_name) + end + if attached l_sql_storage.sql_read_string_32 (8) as l_extractor then + Result.set_extractor (l_extractor) + end + if attached l_sql_storage.sql_read_string_32 (9) as l_authorize_url then + Result.set_authorize_url (l_authorize_url) + end + if attached l_sql_storage.sql_read_string_32 (10) as l_endpoint then + Result.set_endpoint (l_endpoint) + end + end + end + end + + note copyright: "Copyright (c) 1984-2013, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/modules/login/oauth_login_gmail.e b/modules/login/oauth_login.e similarity index 61% rename from modules/login/oauth_login_gmail.e rename to modules/login/oauth_login.e index f84c622..6c100da 100644 --- a/modules/login/oauth_login_gmail.e +++ b/modules/login/oauth_login.e @@ -1,10 +1,10 @@ note - description: "OAuth workflow for Gmails." + description: "OAuth workflow" date: "$Date$" revision: "$Revision$" class - OAUTH_LOGIN_GMAIL + OAUTH_LOGIN inherit @@ -15,51 +15,25 @@ create feature {NONE} -- Initialization - make (a_cms_api:CMS_API a_host: READABLE_STRING_32) + make (a_host: READABLE_STRING_32; a_consumer: CMS_OAUTH_CONSUMER) -- Create an object with the host `a_host'. do - cms_api := a_cms_api - initilize + initilize (a_consumer) create config.make_default (api_key, api_secret) - config.set_callback (a_host + "/account/oauthgmail") + config.set_callback (a_host + "/account/"+ a_consumer.callback_name) config.set_scope (scope) - create goauth - api_service := goauth.create_service (config) - ensure - cms_api_set: cms_api = a_cms_api + --Todo create a generic OAUTH_20_GENERIC_API + create oauth_api.make (a_consumer.endpoint, a_consumer.authorize_url, a_consumer.extractor) + api_service := oauth_api.create_service (config) end - initilize - local - utf: UTF_CONVERTER + initilize (a_consumer: CMS_OAUTH_CONSUMER) do --Use configuration values if any if not defaul - api_key := "KEY" - api_secret := "SECRET" - scope := "email" - - api_revoke := "[https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN]" - protected_resource_url := "https://www.googleapis.com/plus/v1/people/me" - - - if attached {CONFIG_READER} cms_api.module_configuration ("login", "oauth2_gmail") as cfg then - if attached cfg.text_item ("api_secret") as l_api_secret then - api_secret := utf.utf_32_string_to_utf_8_string_8 (l_api_secret) - end - if attached cfg.text_item ("api_key") as l_api_key then - api_key := utf.utf_32_string_to_utf_8_string_8 (l_api_key) - end - if attached cfg.text_item ("scope") as l_scope then - scope := utf.utf_32_string_to_utf_8_string_8 (l_scope) - end - if attached cfg.text_item ("api_revoke") as l_api_revoke then - api_revoke := utf.utf_32_string_to_utf_8_string_8 (l_api_revoke) - end - if attached cfg.text_item ("protected_resource_url") as l_resource_url then - protected_resource_url := utf.utf_32_string_to_utf_8_string_8 (l_resource_url) - end - end - + api_key := a_consumer.api_key + api_secret := a_consumer.api_secret + scope := a_consumer.scope + protected_resource_url := a_consumer.protected_resource_url end feature -- Access @@ -103,26 +77,11 @@ feature -- Access end end - sign_out (a_code: READABLE_STRING_32) - -- Invalidate the current OAuth access token `a_code'. - local - l_revoke: STRING - request: OAUTH_REQUEST - do - create l_revoke.make_from_string (api_revoke) - l_revoke.replace_substring_all ("$ACCESS_TOKEN", a_code) - create request.make ("POST", l_revoke) - if attached {OAUTH_RESPONSE} request.execute as l_response then - -- do nothing - write_debug_log (generator + ".sign_out response [" + l_response.status.out + "]") - check invalidate_ok: l_response.status = {HTTP_CONSTANTS}.ok end - end - end - user_email: detachable READABLE_STRING_32 -- Retrieve user email if any. local l_json: JSON_CONFIG + utf: UTF_CONVERTER do if attached user_profile as l_profile then create l_json.make_from_string (l_profile) @@ -132,6 +91,8 @@ feature -- Access attached {JSON_STRING} l_object.item ("value") as l_email then Result := l_email.item + elseif attached {JSON_STRING} l_json.item ("email") as l_email then + Result := l_email.unescaped_string_32 end end end @@ -146,7 +107,7 @@ feature -- Access feature {NONE} -- Implementation - goauth: OAUTH_20_GOOGLE_API + oauth_api: CMS_OAUTH_20_GENERIC_API -- OAuth 2.0 Google API. config: OAUTH_CONFIG @@ -164,16 +125,10 @@ feature {NONE} -- Implementation scope: STRING -- api scope to access protected resources. - api_revoke: STRING - -- Revoke url - protected_resource_url: STRING -- Resource url. empty_token: detachable OAUTH_TOKEN -- fake token. - cms_api: CMS_API - -- CMS API. - end diff --git a/modules/login/persistence/cms_oauth_20_generic_api.e b/modules/login/persistence/cms_oauth_20_generic_api.e new file mode 100644 index 0000000..149c340 --- /dev/null +++ b/modules/login/persistence/cms_oauth_20_generic_api.e @@ -0,0 +1,94 @@ +note + description: "Generic OAUTH2 API" + date: "$Date$" + revision: "$Revision$" + +class + CMS_OAUTH_20_GENERIC_API + +inherit + + OAUTH_20_API + redefine + access_token_extractor, + access_token_verb + end + +create + make + +feature {NONE} -- Initialize + + make (a_endpoint: READABLE_STRING_32; a_authorize_url: READABLE_STRING_32; a_extractor: READABLE_STRING_32) + do + endpoint := a_endpoint + authorize_url := a_authorize_url + extractor := a_extractor + ensure + endpoint_set: endpoint = a_endpoint + authorize_url_set: authorize_url = a_authorize_url + extractor_set: extractor = a_authorize_url + end + + endpoint: READABLE_STRING_32 + -- Url that receives the access token request. + + authorize_url: READABLE_STRING_32 + -- + + extractor: READABLE_STRING_32 + -- text, json + +feature -- Access + + access_token_extractor: ACCESS_TOKEN_EXTRACTOR + -- Return token extractor, by default TOKEN_EXTRACTOR_20. + do + if extractor.is_case_insensitive_equal_general ("json") then + create {JSON_TOKEN_EXTRACTOR} Result + else + create {TOKEN_EXTRACTOR_20} Result + end + end + + access_token_verb: READABLE_STRING_GENERAL + do + Result := "POST" + end + + access_token_endpoint: READABLE_STRING_GENERAL + -- Url that receives the access token request + do + create {STRING_32} Result.make_from_string (endpoint) + end + + authorization_url (config: OAUTH_CONFIG): detachable READABLE_STRING_GENERAL + -- Url where you should redirect your users to authneticate + local + l_result: STRING_32 + do + if attached config.scope as l_scope then + create {STRING_32} l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) + l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8) + if attached config.callback as l_callback then + l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_32)) + end + if attached config.callback as l_callback then + l_result.replace_substring_all ("$SCOPE", (create {OAUTH_ENCODER}).encoded_string (l_scope.as_string_32)) + Result := l_result + end + else + create {STRING_32} l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) + l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8) + if attached config.callback as l_callback then + l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_32)) + end + end + end + +feature -- Implementation + + Scoped_authorize_url: STRING = "&scope=$SCOPE"; + + +end diff --git a/modules/login/persistence/cms_user_oauth_storage_i.e b/modules/login/persistence/cms_user_oauth_storage_i.e index 8932faf..6011ee7 100644 --- a/modules/login/persistence/cms_user_oauth_storage_i.e +++ b/modules/login/persistence/cms_user_oauth_storage_i.e @@ -18,27 +18,35 @@ feature -- Error Handling feature -- Access - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - -- CMS User with Oauth gmail credential by id if any. + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- CMS User with Oauth credential by id if any. deferred end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - -- -- CMS User with Oauth gmail credential by access token `a_token' if any. + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- -- CMS User with Oauth credential by access token `a_token' if any. + deferred + end + + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + -- + deferred + end + + oauth2_consumers: LIST [STRING] deferred end feature -- Change: User Oauth2 - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Add a new user with oauth2 authentication. deferred end - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Update user `a_user' with oauth2 gmail authentication. + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + -- Update user `a_user' with oauth2 authentication. deferred end - end diff --git a/modules/login/persistence/cms_user_oauth_storage_null.e b/modules/login/persistence/cms_user_oauth_storage_null.e index e20e07f..a907093 100644 --- a/modules/login/persistence/cms_user_oauth_storage_null.e +++ b/modules/login/persistence/cms_user_oauth_storage_null.e @@ -22,25 +22,34 @@ feature -- Error handler feature -- Access - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER - -- CMS User with Oauth gmail credential by id if any. + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- CMS User with Oauth credential by id if any. do end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER - -- -- CMS User with Oauth gmail credential by access token `a_token' if any. + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- -- CMS User with Oauth credential by access token `a_token' if any. do end + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + do + end + + oauth2_consumers: LIST [STRING] + do + create {ARRAYED_LIST[STRING]} Result.make (0) + end + feature -- Change: User Oauth2 - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Add a new user with oauth2 gmail authentication. + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Add a new user with oauth2 authentication. do end - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- Update user `a_user' with oauth2 gmail authentication. + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + -- Update user `a_user' with oauth2 authentication. do end diff --git a/modules/login/persistence/cms_user_oauth_storage_sql.e b/modules/login/persistence/cms_user_oauth_storage_sql.e index 9a9a692..c0585f6 100644 --- a/modules/login/persistence/cms_user_oauth_storage_sql.e +++ b/modules/login/persistence/cms_user_oauth_storage_sql.e @@ -22,16 +22,39 @@ create feature -- Access User Outh Gmail - user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + + user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + local + l_list: LIST[STRING] + do + error_handler.reset + write_information_log (generator + ".user_by_oauth2_global_token") + l_list := oauth2_consumers + from + l_list.start + until + l_list.after or attached Result + loop + if attached {CMS_USER} user_by_oauth2_token (a_token, "oauth2_"+l_list.item) as l_user then + Result := l_user + end + l_list.forth + end + end + + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset - write_information_log (generator + ".user_oauth2_gmail_by_id") + write_information_log (generator + ".user_oauth2_by_id") create l_parameters.make (1) l_parameters.put (a_uid, "uid") - sql_query (select_user_oauth2_gmail_by_id, l_parameters) + create l_string.make_from_string (select_user_oauth2_template_by_id) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_query (l_string, l_parameters) if sql_rows_count = 1 then Result := fetch_user else @@ -39,16 +62,19 @@ feature -- Access User Outh Gmail end end - user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset - write_information_log (generator + ".user_by_oauth2_gmail_token") + write_information_log (generator + ".user_by_oauth2_token") create l_parameters.make (1) l_parameters.put (a_token, "token") - sql_query (select_user_by_oauth2_gmail_token, l_parameters) + create l_string.make_from_string (select_user_by_oauth2_template_token) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_query (l_string, l_parameters) if sql_rows_count = 1 then Result := fetch_user else @@ -56,43 +82,71 @@ feature -- Access User Outh Gmail end end + oauth2_consumers: LIST[STRING] + -- Return a list of consumers, or empty + do + error_handler.reset + create {ARRAYED_LIST[STRING]}Result.make (0) + write_information_log (generator + ".user_by_oauth2_token") + sql_query (Sql_oauth_consumers, Void) + if not has_error then + from + sql_start + until + sql_after + loop + if attached sql_read_string (1) as l_name then + Result.force (l_name) + end + sql_forth + end + end + end + feature -- Change: User Oauth2 Gmail - new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) - -- . + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Add a new user with oauth2 authentication. + -- . local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset sql_begin_transaction - write_information_log (generator + ".new_user_oauth2_gmail") + write_information_log (generator + ".new_user_oauth2") create l_parameters.make (4) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (a_user_profile, "profile") l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") - sql_change (sql_insert_oauth2_gmail, l_parameters) + create l_string.make_from_string (sql_insert_oauth2_template) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_change (l_string, l_parameters) sql_commit_transaction end - - update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + -- Update user `a_user' with oauth2 authentication. -- local l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING do error_handler.reset sql_begin_transaction - write_information_log (generator + ".new_user_oauth2_gmail") + write_information_log (generator + ".new_user_oauth2") create l_parameters.make (4) l_parameters.put (a_user.id, "uid") l_parameters.put (a_token, "token") l_parameters.put (a_user_profile, "profile") - sql_change (sql_update_oauth2_gmail, l_parameters) + create l_string.make_from_string (sql_update_oauth2_template) + l_string.replace_substring_all ("$table_name", a_consumer_table) + sql_change (l_string, l_parameters) sql_commit_transaction end @@ -135,15 +189,17 @@ feature {NONE} -- Implementation: User end end -feature {NONE}-- User Oauth2 Gmail. +feature -- {NONE} User OAuth2 - Sql_insert_oauth2_gmail: STRING = "INSERT INTO oauth2_gmail (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + 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;" - Sql_update_oauth2_gmail: STRING = "UPDATE oauth2_gmail SET access_token = :token, details = :profile WHERE uid =:uid;" + 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_by_oauth2_gmail_token: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.access_token = :token;" - Select_user_oauth2_gmail_by_id: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.uid = :uid;" + Sql_insert_oauth2_template: STRING = "INSERT INTO $table_name (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + Sql_update_oauth2_template: STRING = "UPDATE $table_name SET access_token = :token, details = :profile WHERE uid =:uid;" + + Sql_oauth_consumers: STRING = "SELECT name FROM oauth2_consumers"; end diff --git a/src/persistence/sql/cms_storage_sql_i.e b/src/persistence/sql/cms_storage_sql_i.e index 8f38ab0..fa9f91b 100644 --- a/src/persistence/sql/cms_storage_sql_i.e +++ b/src/persistence/sql/cms_storage_sql_i.e @@ -76,7 +76,7 @@ feature -- Operation i := a_sql_statement.index_of (':', i) if i = 0 then i := n -- exit - else + elseif a_sql_statement.at (i-1).is_equal ('%'') or else a_sql_statement.at (i-1).is_equal ('%"') or else a_sql_statement.at (i-1).is_equal (' ') or else a_sql_statement.at (i-1).is_equal ('=') then from j := i + 1 until @@ -124,6 +124,30 @@ feature -- Operation feature -- Helper + sql_execute_file_script_with_params (a_path: PATH; a_params: detachable STRING_TABLE [detachable ANY]) + -- Execute SQL script from `a_path' and with params `a_params'. + local + f: PLAIN_TEXT_FILE + sql: STRING + do + create f.make_with_path (a_path) + if f.exists and then f.is_access_readable then + create sql.make (f.count) + f.open_read + from + f.start + until + f.exhausted or f.end_of_file + loop + f.read_stream_thread_aware (1_024) + sql.append (f.last_string) + end + f.close + sql_execute_script_with_params (sql, a_params) + end + end + + sql_execute_file_script (a_path: PATH) -- Execute SQL script from `a_path'. local @@ -157,6 +181,14 @@ feature -- Helper -- sql_commit_transaction end + sql_execute_script_with_params (a_sql_script: STRING; a_params: detachable STRING_TABLE [detachable ANY]) + -- Execute SQL script. + -- i.e: multiple SQL statements. + do + reset_error + sql_change (a_sql_script, a_params) + end + sql_table_exists (a_table_name: READABLE_STRING_8): BOOLEAN -- Does table `a_table_name' exists? local From afced59b0c8c9e645108d85e58761a8161fb88d9 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 11 Jun 2015 21:50:27 -0300 Subject: [PATCH 7/8] Updated Login Module. - Refactor raname classes and features. - Clean code. --- examples/demo/demo-safe.ecf | 4 +- examples/demo/src/ewf_roc_server.e | 2 +- ...tants.e => cms_authentication_constants.e} | 4 +- ...authentication_email_service_parameters.e} | 4 +- ...n_module.e => cms_authentication_module.e} | 207 +++--------------- ...ce.e => cms_authenticaton_email_service.e} | 6 +- modules/login/cms_oauth_20_api.e | 95 ++++++++ ...uth_consumer.e => cms_oauth_20_consumer.e} | 2 +- ...{oauth_login.e => cms_oauth_20_workflow.e} | 10 +- modules/login/cms_user_oauth_api.e | 100 --------- .../{oauth_filter.e => cms_oauth_20_filter.e} | 12 +- .../persistence/cms_oauth_20_generic_api.e | 6 +- ...h_storage_i.e => cms_oauth_20_storage_i.e} | 30 ++- ...age_null.e => cms_oauth_20_storage_null.e} | 24 +- ...orage_sql.e => cms_oauth_20_storage_sql.e} | 128 +++++++++-- 15 files changed, 298 insertions(+), 336 deletions(-) rename modules/login/{login_constants.e => cms_authentication_constants.e} (55%) rename modules/login/{login_email_service_parameters.e => cms_authentication_email_service_parameters.e} (98%) rename modules/login/{login_module.e => cms_authentication_module.e} (80%) rename modules/login/{login_email_service.e => cms_authenticaton_email_service.e} (92%) create mode 100644 modules/login/cms_oauth_20_api.e rename modules/login/{cms_oauth_consumer.e => cms_oauth_20_consumer.e} (99%) rename modules/login/{oauth_login.e => cms_oauth_20_workflow.e} (93%) delete mode 100644 modules/login/cms_user_oauth_api.e rename modules/login/filter/{oauth_filter.e => cms_oauth_20_filter.e} (71%) rename modules/login/persistence/{cms_user_oauth_storage_i.e => cms_oauth_20_storage_i.e} (51%) rename modules/login/persistence/{cms_user_oauth_storage_null.e => cms_oauth_20_storage_null.e} (61%) rename modules/login/persistence/{cms_user_oauth_storage_sql.e => cms_oauth_20_storage_sql.e} (54%) diff --git a/examples/demo/demo-safe.ecf b/examples/demo/demo-safe.ecf index a5c8f59..0542388 100644 --- a/examples/demo/demo-safe.ecf +++ b/examples/demo/demo-safe.ecf @@ -20,12 +20,10 @@ - + diff --git a/examples/demo/src/ewf_roc_server.e b/examples/demo/src/ewf_roc_server.e index 86a7dd4..c2acb14 100644 --- a/examples/demo/src/ewf_roc_server.e +++ b/examples/demo/src/ewf_roc_server.e @@ -135,7 +135,7 @@ feature -- CMS setup m.enable a_setup.register_module (m) - create {LOGIN_MODULE} m.make + create {CMS_AUTHENTICATION_MODULE} m.make m.enable a_setup.register_module (m) diff --git a/modules/login/login_constants.e b/modules/login/cms_authentication_constants.e similarity index 55% rename from modules/login/login_constants.e rename to modules/login/cms_authentication_constants.e index ffae849..6278ed8 100644 --- a/modules/login/login_constants.e +++ b/modules/login/cms_authentication_constants.e @@ -1,10 +1,10 @@ note - description: "Summary description for {LOGIN_CONSTANTS}." + description: "Summary description for {CMS_AUTHENTICATION_CONSTANTS}." date: "$Date$" revision: "$Revision$" class - LOGIN_CONSTANTS + CMS_AUTHENTICATION_CONSTANTS feature -- Access diff --git a/modules/login/login_email_service_parameters.e b/modules/login/cms_authentication_email_service_parameters.e similarity index 98% rename from modules/login/login_email_service_parameters.e rename to modules/login/cms_authentication_email_service_parameters.e index b3e8feb..f6be1fc 100644 --- a/modules/login/login_email_service_parameters.e +++ b/modules/login/cms_authentication_email_service_parameters.e @@ -1,10 +1,10 @@ note - description: "Summary description for {LOGIN_EMAIL_SERVICE_PARAMETERS}." + description: "Summary description for {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}." date: "$Date$" revision: "$Revision$" class - LOGIN_EMAIL_SERVICE_PARAMETERS + CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS inherit EMAIL_SERVICE_PARAMETERS diff --git a/modules/login/login_module.e b/modules/login/cms_authentication_module.e similarity index 80% rename from modules/login/login_module.e rename to modules/login/cms_authentication_module.e index e649c95..ce75ca7 100644 --- a/modules/login/login_module.e +++ b/modules/login/cms_authentication_module.e @@ -4,7 +4,7 @@ revision: "$Revision: 97328 $" class - LOGIN_MODULE + CMS_AUTHENTICATION_MODULE inherit CMS_MODULE @@ -63,16 +63,16 @@ feature {CMS_API} -- Module Initialization -- local l_user_auth_api: like user_oauth_api - l_user_auth_storage: CMS_USER_OAUTH_STORAGE_I + l_user_auth_storage: CMS_OAUTH_20_STORAGE_I do Precursor (a_api) -- Storage initialization if attached {CMS_STORAGE_SQL_I} a_api.storage as l_storage_sql then - create {CMS_USER_OAUTH_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) + create {CMS_OAUTH_20_STORAGE_SQL} l_user_auth_storage.make (l_storage_sql) else -- FIXME: in case of NULL storage, should Current be disabled? - create {CMS_USER_OAUTH_STORAGE_NULL} l_user_auth_storage + create {CMS_OAUTH_20_STORAGE_NULL} l_user_auth_storage end -- Node API initialization @@ -92,7 +92,6 @@ feature {CMS_API} -- Module management install (api: CMS_API) local - sql: STRING l_setup: CMS_SETUP l_params: detachable STRING_TABLE [detachable ANY] l_consumers: LIST [STRING] @@ -142,7 +141,7 @@ feature {CMS_API} -- Module management feature {CMS_API} -- Access: API - user_oauth_api: detachable CMS_USER_OAUTH_API + user_oauth_api: detachable CMS_OAUTH_20_API -- feature -- Filters @@ -152,7 +151,7 @@ feature -- Filters do create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) if attached user_oauth_api as l_user_oauth_api then - Result.extend (create {OAUTH_FILTER}.make (a_api, l_user_oauth_api)) + Result.extend (create {CMS_OAUTH_20_FILTER}.make (a_api, l_user_oauth_api)) end end @@ -183,7 +182,7 @@ feature -- Router end - configure_web (a_api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; a_router: WSF_ROUTER) + configure_web (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; a_router: WSF_ROUTER) do a_router.handle_with_request_methods ("/account/roc-login", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login (a_api, ?, ?)), a_router.methods_head_get) a_router.handle_with_request_methods ("/account/roc-register", create {WSF_URI_AGENT_HANDLER}.make (agent handle_register (a_api, ?, ?)), a_router.methods_get_post) @@ -192,7 +191,7 @@ feature -- Router a_router.handle_with_request_methods ("/account/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) - a_router.handle_with_request_methods ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/account/login-with-oauth/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_login_with_oauth (a_api,a_user_oauth_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/account/{callback}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_callback_oauth (a_api, a_user_oauth_api, ?, ?)), a_router.methods_get_post) end @@ -246,8 +245,6 @@ feature -- Hooks end get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) - local - vals: CMS_VALUE_TABLE do if a_block_id.is_case_insensitive_equal_general ("login") and then @@ -280,36 +277,24 @@ feature -- Hooks handle_login (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) r.set_value ("Login", "optional_content_type") r.execute end - handle_workaround_filter (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) - local - r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE - do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - r.execute - end - - handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE l_url: STRING - l_oauth_gmail: OAUTH_LOGIN l_cookie: WSF_COOKIE do if - attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_session) as l_cookie_token and then + attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_cookie_token and then attached {CMS_USER} current_user (req) as l_user then -- Logout gmail - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_cookie_token.value) + create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_cookie_token.value) l_cookie.set_path ("/") l_cookie.set_max_age (-1) res.add_cookie (l_cookie) @@ -335,10 +320,9 @@ feature -- Hooks u: CMS_USER l_roles: LIST [CMS_USER_ROLE] l_exist: BOOLEAN - es: LOGIN_EMAIL_SERVICE + es: CMS_AUTHENTICATON_EMAIL_SERVICE l_link: STRING l_token: STRING - l_message: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) r.set_value ("Register", "optional_content_type") @@ -381,7 +365,7 @@ feature -- Hooks -- Send Email - create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_email") es.send_contact_email (l_email.value, l_link) @@ -400,9 +384,7 @@ feature -- Hooks local r: CMS_RESPONSE l_user_api: CMS_USER_API - l_id: INTEGER_64 l_ir: INTERNAL_SERVER_ERROR_CMS_RESPONSE - l_link: CMS_LOCAL_LINK do l_user_api := api.user_api create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) @@ -433,12 +415,10 @@ feature -- Hooks handle_reactivation (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE - es: LOGIN_EMAIL_SERVICE + es: CMS_AUTHENTICATON_EMAIL_SERVICE l_user_api: CMS_USER_API l_token: STRING l_link: STRING - l_message: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if req.is_post_request_method then @@ -459,7 +439,7 @@ feature -- Hooks l_link.append (l_token) -- Send Email - create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_activation_email") es.send_contact_activation_email (l_email.value, l_link) end @@ -477,12 +457,10 @@ feature -- Hooks handle_new_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE - es: LOGIN_EMAIL_SERVICE + es: CMS_AUTHENTICATON_EMAIL_SERVICE l_user_api: CMS_USER_API l_token: STRING l_link: STRING - l_message: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if req.is_post_request_method then @@ -497,7 +475,7 @@ feature -- Hooks l_link.append (l_token) -- Send Email - create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_password_email") es.send_contact_password_email (l_email.value, l_link) else @@ -514,11 +492,7 @@ feature -- Hooks handle_reset_password (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE - es: LOGIN_EMAIL_SERVICE l_user_api: CMS_USER_API - l_link: STRING - l_message: STRING do create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) l_user_api := api.user_api @@ -605,8 +579,6 @@ feature {NONE} -- Block views end get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) - local - vals: CMS_VALUE_TABLE do if a_response.request.is_get_request_method then if attached template_block (a_block_id, a_response) as l_tpl_block then @@ -643,8 +615,6 @@ feature {NONE} -- Block views get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) - local - vals: CMS_VALUE_TABLE do if a_response.request.is_get_request_method then if attached template_block (a_block_id, a_response) as l_tpl_block then @@ -679,8 +649,6 @@ feature {NONE} -- Block views end get_block_view_new_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) - local - vals: CMS_VALUE_TABLE do if a_response.request.is_get_request_method then if attached template_block (a_block_id, a_response) as l_tpl_block then @@ -714,8 +682,6 @@ feature {NONE} -- Block views end get_block_view_reset_password (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) - local - vals: CMS_VALUE_TABLE do if a_response.request.is_get_request_method then if attached template_block (a_block_id, a_response) as l_tpl_block then @@ -753,14 +719,14 @@ feature {NONE} -- Block views feature -- OAuth2 Login with google. - handle_login_with_oauth (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_login_with_oauth (api: CMS_API; a_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_oauth: OAUTH_LOGIN + l_oauth: CMS_OAUTH_20_WORKFLOW do if attached {WSF_STRING} req.path_parameter ("callback") as p_consumer and then - attached {CMS_OAUTH_CONSUMER} oauth_consumer_by_name (api, p_consumer.value) as l_consumer + attached {CMS_OAUTH_20_CONSUMER} a_oauth_api.oauth_consumer_by_name (p_consumer.value) as l_consumer then create l_oauth.make (req.server_url, l_consumer) if attached l_oauth.authorization_url as l_authorization_url then @@ -779,18 +745,18 @@ feature -- OAuth2 Login with google. end end - handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_callback_oauth (api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - l_auth: OAUTH_LOGIN + l_auth: CMS_OAUTH_20_WORKFLOW l_user_api: CMS_USER_API l_user: CMS_USER l_roles: LIST [CMS_USER_ROLE] l_cookie: WSF_COOKIE - es: LOGIN_EMAIL_SERVICE + es: CMS_AUTHENTICATON_EMAIL_SERVICE do if attached {WSF_STRING} req.path_parameter ("callback") as l_callback and then - attached {CMS_OAUTH_CONSUMER} oauth_consumer_by_callback (api, l_callback.value) as l_consumer and then + attached {CMS_OAUTH_20_CONSUMER} a_user_oauth_api.oauth_consumer_by_callback (l_callback.value) as l_consumer and then attached {WSF_STRING} req.query_parameter ("code") as l_code then create l_auth.make (req.server_url, l_consumer) @@ -809,14 +775,14 @@ feature -- OAuth2 Login with google. then if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then -- User with email exist - if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, "oauth2_" + l_consumer.name) then + if attached {CMS_USER} a_user_oauth_api.user_oauth2_by_id (p_user.id, l_consumer.name) then -- Update oauth entry - a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, "oauth2_" + l_consumer.name ) + a_user_oauth_api.update_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name ) else -- create a oauth entry - a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, "oauth2_" + l_consumer.name ) + a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, p_user, l_consumer.name ) end - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_access_token.token) + create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) @@ -834,8 +800,8 @@ feature -- OAuth2 Login with google. l_user_api.new_user (l_user) -- Add oauth entry - a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, "oauth_" + l_consumer.name ) - create l_cookie.make ({LOGIN_CONSTANTS}.oauth_session, l_access_token.token) + a_user_oauth_api.new_user_oauth2 (l_access_token.token, l_user_profile, l_user, l_consumer.name ) + create l_cookie.make ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session, l_access_token.token) l_cookie.set_max_age (l_access_token.expires_in) l_cookie.set_path ("/") res.add_cookie (l_cookie) @@ -843,7 +809,7 @@ feature -- OAuth2 Login with google. -- Send Email - create es.make (create {LOGIN_EMAIL_SERVICE_PARAMETERS}.make (api)) + create es.make (create {CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS}.make (api)) write_debug_log (generator + ".handle register: send_contact_welcome_email") es.send_contact_welcome_email (l_email, "") end @@ -910,119 +876,6 @@ feature {NONE} -- Implementation: date and time Result := d.date_time end -feature --{NONE} -- Helper OAUTH Consumers. - - - oauth_consumer_by_name (a_api: CMS_API; a_name: READABLE_STRING_8): detachable CMS_OAUTH_CONSUMER - local - l_params: detachable STRING_TABLE [detachable ANY] - l_setup: CMS_SETUP - do - -- TODO workaround!!, move to the persistence layer - l_setup := a_api.setup - - -- Schema - if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then - - -- Todo workaround, move this to his own database layer. - create l_params.make (1) - l_params.force (a_name, "name") - l_sql_storage.sql_query ("SELECT * FROM oauth2_consumers where name =:name;", l_params) - if l_sql_storage.has_error then - a_api.logger.put_error ("Could not retrieve a consumer from the database", generating_type) - else - -- Fetch a Consumer - create Result - if attached l_sql_storage.sql_read_integer_64 (1) as l_id then - Result.set_id (l_id) - end - if attached l_sql_storage.sql_read_string_32 (2) as l_name then - Result.set_name (l_name) - end - if attached l_sql_storage.sql_read_string_32 (3) as l_api_secret then - Result.set_api_secret (l_api_secret) - end - if attached l_sql_storage.sql_read_string_32 (4) as l_api_key then - Result.set_api_key (l_api_key) - end - if attached l_sql_storage.sql_read_string_32 (5) as l_scope then - Result.set_scope (l_scope) - end - if attached l_sql_storage.sql_read_string_32 (6) as l_resource_url then - Result.set_protected_resource_url (l_resource_url) - end - if attached l_sql_storage.sql_read_string_32 (7) as l_callback_name then - Result.set_callback_name (l_callback_name) - end - if attached l_sql_storage.sql_read_string_32 (8) as l_extractor then - Result.set_extractor (l_extractor) - end - if attached l_sql_storage.sql_read_string_32 (9) as l_authorize_url then - Result.set_authorize_url (l_authorize_url) - end - if attached l_sql_storage.sql_read_string_32 (10) as l_endpoint then - Result.set_endpoint (l_endpoint) - end - end - end - end - - - oauth_consumer_by_callback (a_api: CMS_API; a_name: READABLE_STRING_8): detachable CMS_OAUTH_CONSUMER - local - l_params: detachable STRING_TABLE [detachable ANY] - l_setup: CMS_SETUP - do - -- TODO workaround !!! move to the persistence layer. - l_setup := a_api.setup - - - -- Schema - if attached {CMS_STORAGE_SQL_I} a_api.storage as l_sql_storage then - - -- Todo workaround, move this to his own database layer. - create l_params.make (1) - l_params.force (a_name, "name") - l_sql_storage.sql_query ("SELECT * FROM oauth2_consumers where callback_name =:name;", l_params) - if l_sql_storage.has_error then - a_api.logger.put_error ("Could not retrieve a consumer from the database", generating_type) - else - -- Fetch a Consumer - create Result - if attached l_sql_storage.sql_read_integer_64 (1) as l_id then - Result.set_id (l_id) - end - if attached l_sql_storage.sql_read_string_32 (2) as l_name then - Result.set_name (l_name) - end - if attached l_sql_storage.sql_read_string_32 (3) as l_api_secret then - Result.set_api_secret (l_api_secret) - end - if attached l_sql_storage.sql_read_string_32 (4) as l_api_key then - Result.set_api_key (l_api_key) - end - if attached l_sql_storage.sql_read_string_32 (5) as l_scope then - Result.set_scope (l_scope) - end - if attached l_sql_storage.sql_read_string_32 (6) as l_resource_url then - Result.set_protected_resource_url (l_resource_url) - end - if attached l_sql_storage.sql_read_string_32 (7) as l_callback_name then - Result.set_callback_name (l_callback_name) - end - if attached l_sql_storage.sql_read_string_32 (8) as l_extractor then - Result.set_extractor (l_extractor) - end - if attached l_sql_storage.sql_read_string_32 (9) as l_authorize_url then - Result.set_authorize_url (l_authorize_url) - end - if attached l_sql_storage.sql_read_string_32 (10) as l_endpoint then - Result.set_endpoint (l_endpoint) - end - end - end - end - note copyright: "Copyright (c) 1984-2013, Eiffel Software and others" diff --git a/modules/login/login_email_service.e b/modules/login/cms_authenticaton_email_service.e similarity index 92% rename from modules/login/login_email_service.e rename to modules/login/cms_authenticaton_email_service.e index 9d885b4..d4a1984 100644 --- a/modules/login/login_email_service.e +++ b/modules/login/cms_authenticaton_email_service.e @@ -1,10 +1,10 @@ note - description: "Summary description for {LOGIN_EMAIL_SERVICE}." + description: "Summary description for {CMS_AUTHENTICATON_EMAIL_SERVICE}." date: "$Date$" revision: "$Revision$" class - LOGIN_EMAIL_SERVICE + CMS_AUTHENTICATON_EMAIL_SERVICE inherit EMAIL_SERVICE @@ -24,7 +24,7 @@ feature {NONE} -- Initialization contact_email := parameters.contact_email end - parameters: LOGIN_EMAIL_SERVICE_PARAMETERS + parameters: CMS_AUTHENTICATION_EMAIL_SERVICE_PARAMETERS -- Associated parameters. feature -- Access diff --git a/modules/login/cms_oauth_20_api.e b/modules/login/cms_oauth_20_api.e new file mode 100644 index 0000000..27739b2 --- /dev/null +++ b/modules/login/cms_oauth_20_api.e @@ -0,0 +1,95 @@ +note + description: "[ + API to manage CMS User OAuth authentication. + ]" + date: "$Date$" + revision: "$Revision$" + +class + CMS_OAUTH_20_API + +inherit + CMS_MODULE_API + + REFACTORING_HELPER + +create {CMS_AUTHENTICATION_MODULE} + make_with_storage + +feature {NONE} -- Initialization + + make_with_storage (a_api: CMS_API; a_oauth_storage: CMS_OAUTH_20_STORAGE_I) + -- Create an object with api `a_api' and storage `a_oauth_storage'. + do + oauth_20_storage := a_oauth_storage + make (a_api) + ensure + oauht_20_storage_set: oauth_20_storage = a_oauth_storage + end + +feature {CMS_MODULE} -- Access: User oauth storage. + + oauth_20_storage: CMS_OAUTH_20_STORAGE_I + -- storage interface. + +feature -- Access: User Oauth20 + + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER + -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby. + do + Result := oauth_20_storage.user_oauth2_by_id (a_uid, a_consumer) + end + + user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER + -- Retrieve a user by token `a_token' for the consumer `a_consumer'. + do + Result := oauth_20_storage.user_oauth2_by_token (a_token, a_consumer) + end + + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + -- Retrieve a user by token `a_token' searching in all the registered consumers in the system. + do + Result := oauth_20_storage.user_oauth2_without_consumer_by_token (a_token) + end + +feature -- Access: Consumers OAuth20 + + oauth2_consumers: LIST [STRING] + -- List of Oauth_20 consumers, if any, empty in other case. + do + Result := oauth_20_storage.oauth2_consumers + end + + oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by name `a_name', if any. + do + Result := oauth_20_storage.oauth_consumer_by_name (a_name) + end + + oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by callback `a_callback', if any. + do + Result := oauth_20_storage.oauth_consumer_by_callback (a_callback) + end + +feature -- Change: User OAuth20 + + + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32) + -- Add a new user with oauth20 using the consumer `a_consumer'. + require + has_id: a_user.has_id + do + oauth_20_storage.new_user_oauth2 (a_token, a_user_profile, a_user, a_consumer) + end + + + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + -- Updaate user `a_user' with oauth2 for the consumer `a_consumer'. + require + has_id: a_user.has_id + do + oauth_20_storage.update_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) + end + +end diff --git a/modules/login/cms_oauth_consumer.e b/modules/login/cms_oauth_20_consumer.e similarity index 99% rename from modules/login/cms_oauth_consumer.e rename to modules/login/cms_oauth_20_consumer.e index fda8210..79f3421 100644 --- a/modules/login/cms_oauth_consumer.e +++ b/modules/login/cms_oauth_20_consumer.e @@ -5,7 +5,7 @@ note revision: "$Revision$" class - CMS_OAUTH_CONSUMER + CMS_OAUTH_20_CONSUMER inherit diff --git a/modules/login/oauth_login.e b/modules/login/cms_oauth_20_workflow.e similarity index 93% rename from modules/login/oauth_login.e rename to modules/login/cms_oauth_20_workflow.e index 6c100da..527fc6c 100644 --- a/modules/login/oauth_login.e +++ b/modules/login/cms_oauth_20_workflow.e @@ -4,7 +4,7 @@ note revision: "$Revision$" class - OAUTH_LOGIN + CMS_OAUTH_20_WORKFLOW inherit @@ -15,19 +15,19 @@ create feature {NONE} -- Initialization - make (a_host: READABLE_STRING_32; a_consumer: CMS_OAUTH_CONSUMER) + make (a_host: READABLE_STRING_32; a_consumer: CMS_OAUTH_20_CONSUMER) -- Create an object with the host `a_host'. do initilize (a_consumer) - create config.make_default (api_key, api_secret) + create config.make_default (a_consumer.api_key, a_consumer.api_secret) config.set_callback (a_host + "/account/"+ a_consumer.callback_name) - config.set_scope (scope) + config.set_scope (a_consumer.scope) --Todo create a generic OAUTH_20_GENERIC_API create oauth_api.make (a_consumer.endpoint, a_consumer.authorize_url, a_consumer.extractor) api_service := oauth_api.create_service (config) end - initilize (a_consumer: CMS_OAUTH_CONSUMER) + initilize (a_consumer: CMS_OAUTH_20_CONSUMER) do --Use configuration values if any if not defaul api_key := a_consumer.api_key diff --git a/modules/login/cms_user_oauth_api.e b/modules/login/cms_user_oauth_api.e deleted file mode 100644 index 4e2ed38..0000000 --- a/modules/login/cms_user_oauth_api.e +++ /dev/null @@ -1,100 +0,0 @@ -note - description: "[ - API to manage CMS User OAuth authentication. - ]" - date: "$Date$" - revision: "$Revision$" - -class - CMS_USER_OAUTH_API - -inherit - CMS_MODULE_API - - REFACTORING_HELPER - -create {LOGIN_MODULE} - make_with_storage - -feature {NONE} -- Initialization - - make_with_storage (a_api: CMS_API; a_user_oauth_storage: CMS_USER_OAUTH_STORAGE_I) - do - user_oauth_storage := a_user_oauth_storage - make (a_api) - end - -feature {CMS_MODULE} -- Access user oauth storage. - - user_oauth_storage: CMS_USER_OAUTH_STORAGE_I - - -feature -- Access: OAuth2 Gmail - --- user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER --- do --- Result := user_oauth_storage.user_oauth2_gmail_by_id (a_uid) --- end - --- user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER --- do --- Result := user_oauth_storage.user_by_oauth2_gmail_token (a_token) --- end - - user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER - do - Result := user_oauth_storage.user_oauth2_by_id (a_uid, a_consumer_table) - end - - user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER - do - Result := user_oauth_storage.user_by_oauth2_token (a_token, a_consumer_table) - end - - user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER - do - Result := user_oauth_storage.user_by_oauth2_global_token (a_token) - end - - oauth2_consumers: LIST [STRING] - do - Result := user_oauth_storage.oauth2_consumers - end - -feature -- Change: OAuth2 Gmail - --- new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) --- -- Add a new user with oauth2 gmail authentication. --- require --- has_id: a_user.has_id --- do --- user_oauth_storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) --- end - - --- update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) --- -- Updaate user `a_user' with oauth2 gmail authentication. --- require --- has_id: a_user.has_id --- do --- user_oauth_storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) --- end - - new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) - -- Add a new user with oauth2 gmail authentication. - require - has_id: a_user.has_id - do - user_oauth_storage.new_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) - end - - - update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) - -- Updaate user `a_user' with oauth2 gmail authentication. - require - has_id: a_user.has_id - do - user_oauth_storage.update_user_oauth2 (a_token, a_user_profile, a_user, a_consumer_table) - end - -end diff --git a/modules/login/filter/oauth_filter.e b/modules/login/filter/cms_oauth_20_filter.e similarity index 71% rename from modules/login/filter/oauth_filter.e rename to modules/login/filter/cms_oauth_20_filter.e index d7c1f45..2750615 100644 --- a/modules/login/filter/oauth_filter.e +++ b/modules/login/filter/cms_oauth_20_filter.e @@ -1,10 +1,10 @@ note - description: "Summary description for {OAUTH_FILTER}." + description: "Summary description for {CMS_OAUTH_20_FILTER}." date: "$Date$" revision: "$Revision$" class - OAUTH_FILTER + CMS_OAUTH_20_FILTER inherit WSF_URI_TEMPLATE_HANDLER @@ -20,13 +20,13 @@ create feature {NONE} -- Initialization - make (a_api: CMS_API; a_user_oauth_api: CMS_USER_OAUTH_API) + make (a_api: CMS_API; a_user_oauth_api: CMS_OAUTH_20_API) do make_handler (a_api) user_oauth_api := a_user_oauth_api end - user_oauth_api: CMS_USER_OAUTH_API + user_oauth_api: CMS_OAUTH_20_API feature -- Basic operations @@ -41,9 +41,9 @@ feature -- Basic operations -- end -- A valid user if - attached {WSF_STRING} req.cookie ({LOGIN_CONSTANTS}.oauth_session) as l_roc_auth_session_token + attached {WSF_STRING} req.cookie ({CMS_AUTHENTICATION_CONSTANTS}.oauth_session) as l_roc_auth_session_token then - if attached {CMS_USER} user_oauth_api.user_by_oauth2_global_token (l_roc_auth_session_token.value) as l_user then + if attached {CMS_USER} user_oauth_api.user_oauth2_without_consumer_by_token (l_roc_auth_session_token.value) as l_user then set_current_user (req, l_user) execute_next (req, res) else diff --git a/modules/login/persistence/cms_oauth_20_generic_api.e b/modules/login/persistence/cms_oauth_20_generic_api.e index 149c340..9de5716 100644 --- a/modules/login/persistence/cms_oauth_20_generic_api.e +++ b/modules/login/persistence/cms_oauth_20_generic_api.e @@ -51,18 +51,18 @@ feature -- Access end end - access_token_verb: READABLE_STRING_GENERAL + access_token_verb: STRING_32 do Result := "POST" end - access_token_endpoint: READABLE_STRING_GENERAL + access_token_endpoint: STRING_32 -- Url that receives the access token request do create {STRING_32} Result.make_from_string (endpoint) end - authorization_url (config: OAUTH_CONFIG): detachable READABLE_STRING_GENERAL + authorization_url (config: OAUTH_CONFIG): detachable STRING_32 -- Url where you should redirect your users to authneticate local l_result: STRING_32 diff --git a/modules/login/persistence/cms_user_oauth_storage_i.e b/modules/login/persistence/cms_oauth_20_storage_i.e similarity index 51% rename from modules/login/persistence/cms_user_oauth_storage_i.e rename to modules/login/persistence/cms_oauth_20_storage_i.e index 6011ee7..2357707 100644 --- a/modules/login/persistence/cms_user_oauth_storage_i.e +++ b/modules/login/persistence/cms_oauth_20_storage_i.e @@ -1,10 +1,12 @@ note - description: "Summary description for {CMS_USER_OAUTH_STORAGE_I}." + description: "[ + API to handle OAUTH storage + ]" date: "$Date$" revision: "$Revision$" deferred class - CMS_USER_OAUTH_STORAGE_I + CMS_OAUTH_20_STORAGE_I inherit SHARED_LOGGER @@ -16,27 +18,39 @@ feature -- Error Handling deferred end -feature -- Access +feature -- Access: Users user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER - -- CMS User with Oauth credential by id if any. + -- Retrieve a user by id `a_uid' for the consumer `a_consumer', if aby. deferred end - user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER - -- -- CMS User with Oauth credential by access token `a_token' if any. + user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + -- Retrieve a user by token `a_token' for the consumer `a_consumer'. deferred end - user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER - -- + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + -- Retrieve a user by token `a_token' searching in all the registered consumers in the system. deferred end +feature -- Access: Consumers + oauth2_consumers: LIST [STRING] deferred end + oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by name `a_name', if any. + deferred + end + + oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by callback `a_callback', if any. + deferred + end + feature -- Change: User Oauth2 new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) diff --git a/modules/login/persistence/cms_user_oauth_storage_null.e b/modules/login/persistence/cms_oauth_20_storage_null.e similarity index 61% rename from modules/login/persistence/cms_user_oauth_storage_null.e rename to modules/login/persistence/cms_oauth_20_storage_null.e index a907093..d15d092 100644 --- a/modules/login/persistence/cms_user_oauth_storage_null.e +++ b/modules/login/persistence/cms_oauth_20_storage_null.e @@ -1,15 +1,15 @@ note - description: "Summary description for {CMS_USER_OAUTH_STORAGE_NULL}." + description: "Summary description for {CMS_OAUTH_20_STORAGE_NULL}." author: "" date: "$Date$" revision: "$Revision$" class - CMS_USER_OAUTH_STORAGE_NULL + CMS_OAUTH_20_STORAGE_NULL inherit - CMS_USER_OAUTH_STORAGE_I + CMS_OAUTH_20_STORAGE_I feature -- Error handler @@ -20,27 +20,39 @@ feature -- Error handler create Result.make end -feature -- Access +feature -- Access: Users user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER -- CMS User with Oauth credential by id if any. do end - user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER -- -- CMS User with Oauth credential by access token `a_token' if any. do end - user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER do end +feature -- Access: Consumers + oauth2_consumers: LIST [STRING] do create {ARRAYED_LIST[STRING]} Result.make (0) end + oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by name `a_name', if any. + do + end + + oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by callback `a_callback', if any. + do + end + feature -- Change: User Oauth2 new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) diff --git a/modules/login/persistence/cms_user_oauth_storage_sql.e b/modules/login/persistence/cms_oauth_20_storage_sql.e similarity index 54% rename from modules/login/persistence/cms_user_oauth_storage_sql.e rename to modules/login/persistence/cms_oauth_20_storage_sql.e index c0585f6..ee92e32 100644 --- a/modules/login/persistence/cms_user_oauth_storage_sql.e +++ b/modules/login/persistence/cms_oauth_20_storage_sql.e @@ -1,17 +1,17 @@ note - description: "Summary description for {CMS_USER_OAUTH_STORAGE_SQL}." + description: "Summary description for {CMS_OAUTH_20_STORAGE_SQL}." date: "$Date$" revision: "$Revision$" class - CMS_USER_OAUTH_STORAGE_SQL + CMS_OAUTH_20_STORAGE_SQL inherit - CMS_USER_OAUTH_STORAGE_I + CMS_OAUTH_20_STORAGE_I CMS_PROXY_STORAGE_SQL - CMS_USER_OAUTH_STORAGE_I + CMS_OAUTH_20_STORAGE_I CMS_STORAGE_SQL_I @@ -20,29 +20,29 @@ inherit create make -feature -- Access User Outh Gmail +feature -- Access User Outh - - user_by_oauth2_global_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + user_oauth2_without_consumer_by_token (a_token: READABLE_STRING_32 ): detachable CMS_USER + -- Retrieve a user by token `a_token' searching in all the registered consumers in the system. local l_list: LIST[STRING] do error_handler.reset - write_information_log (generator + ".user_by_oauth2_global_token") + write_information_log (generator + ".user_oauth2_without_consumer_by_token") l_list := oauth2_consumers from l_list.start until l_list.after or attached Result loop - if attached {CMS_USER} user_by_oauth2_token (a_token, "oauth2_"+l_list.item) as l_user then + if attached {CMS_USER} user_oauth2_by_token (a_token, l_list.item) as l_user then Result := l_user end l_list.forth end end - user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_id (a_uid: like {CMS_USER}.id; a_consumer: READABLE_STRING_32): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] @@ -53,7 +53,7 @@ feature -- Access User Outh Gmail create l_parameters.make (1) l_parameters.put (a_uid, "uid") create l_string.make_from_string (select_user_oauth2_template_by_id) - l_string.replace_substring_all ("$table_name", a_consumer_table) + l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer)) sql_query (l_string, l_parameters) if sql_rows_count = 1 then Result := fetch_user @@ -62,7 +62,7 @@ feature -- Access User Outh Gmail end end - user_by_oauth2_token (a_token: READABLE_STRING_32; a_consumer_table: READABLE_STRING_32): detachable CMS_USER + user_oauth2_by_token (a_token: READABLE_STRING_32; a_consumer: READABLE_STRING_32): detachable CMS_USER -- local l_parameters: STRING_TABLE [detachable ANY] @@ -73,7 +73,7 @@ feature -- Access User Outh Gmail create l_parameters.make (1) l_parameters.put (a_token, "token") create l_string.make_from_string (select_user_by_oauth2_template_token) - l_string.replace_substring_all ("$table_name", a_consumer_table) + l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer)) sql_query (l_string, l_parameters) if sql_rows_count = 1 then Result := fetch_user @@ -82,6 +82,9 @@ feature -- Access User Outh Gmail end end + +feature --Access: Consumers + oauth2_consumers: LIST[STRING] -- Return a list of consumers, or empty do @@ -103,9 +106,45 @@ feature -- Access User Outh Gmail end end -feature -- Change: User Oauth2 Gmail + oauth_consumer_by_name (a_name: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by name `a_name', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".oauth_consumer_by_name") + create l_parameters.make (1) + l_parameters.put (a_name, "name") + sql_query (sql_oauth_consumer_name, l_parameters) + if sql_rows_count = 1 then + Result := fetch_consumer + else + check no_more_than_one: sql_rows_count = 0 end + end + end - new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32) + oauth_consumer_by_callback (a_callback: READABLE_STRING_8): detachable CMS_OAUTH_20_CONSUMER + -- Retrieve a consumer by callback `a_callback', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + l_string: STRING + do + error_handler.reset + write_information_log (generator + ".oauth_consumer_by_callback") + create l_parameters.make (1) + l_parameters.put (a_callback, "name") + sql_query (sql_oauth_consumer_callback, l_parameters) + if sql_rows_count = 1 then + Result := fetch_consumer + else + check no_more_than_one: sql_rows_count = 0 end + end + end + +feature -- Change: User OAuth + + new_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32) -- Add a new user with oauth2 authentication. -- . local @@ -123,12 +162,12 @@ feature -- Change: User Oauth2 Gmail l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") create l_string.make_from_string (sql_insert_oauth2_template) - l_string.replace_substring_all ("$table_name", a_consumer_table) + l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer)) sql_change (l_string, l_parameters) sql_commit_transaction end - update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer_table: READABLE_STRING_32 ) + update_user_oauth2 (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER; a_consumer: READABLE_STRING_32 ) -- Update user `a_user' with oauth2 authentication. -- local @@ -145,11 +184,49 @@ feature -- Change: User Oauth2 Gmail l_parameters.put (a_user_profile, "profile") create l_string.make_from_string (sql_update_oauth2_template) - l_string.replace_substring_all ("$table_name", a_consumer_table) + l_string.replace_substring_all ("$table_name", sql_table_name (a_consumer)) sql_change (l_string, l_parameters) sql_commit_transaction end +feature {NONE} -- Implementation OAuth Consumer + + fetch_consumer: detachable CMS_OAUTH_20_CONSUMER + do + if attached sql_read_integer_64 (1) as l_id then + create Result + Result.set_id (l_id) + end + if Result /= Void then + if attached sql_read_string_32 (2) as l_name then + Result.set_name (l_name) + end + if attached sql_read_string_32 (3) as l_api_secret then + Result.set_api_secret (l_api_secret) + end + if attached sql_read_string_32 (4) as l_api_key then + Result.set_api_key (l_api_key) + end + if attached sql_read_string_32 (5) as l_scope then + Result.set_scope (l_scope) + end + if attached sql_read_string_32 (6) as l_resource_url then + Result.set_protected_resource_url (l_resource_url) + end + if attached sql_read_string_32 (7) as l_callback_name then + Result.set_callback_name (l_callback_name) + end + if attached sql_read_string_32 (8) as l_extractor then + Result.set_extractor (l_extractor) + end + if attached sql_read_string_32 (9) as l_authorize_url then + Result.set_authorize_url (l_authorize_url) + end + if attached sql_read_string_32 (10) as l_endpoint then + Result.set_endpoint (l_endpoint) + end + end + end feature {NONE} -- Implementation: User fetch_user: detachable CMS_USER @@ -191,15 +268,28 @@ feature {NONE} -- Implementation: User feature -- {NONE} User OAuth2 + sql_table_name (a_consumer: READABLE_STRING_8): STRING_8 + do + Result := Sql_table_prefix.twin + Result.append (a_consumer) + end + Select_user_by_oauth2_template_token: STRING = "SELECT u.* FROM users as u JOIN $table_name as og ON og.uid = u.uid and og.access_token = :token;" Select_user_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;" - Sql_insert_oauth2_template: STRING = "INSERT INTO $table_name (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" Sql_update_oauth2_template: STRING = "UPDATE $table_name SET access_token = :token, details = :profile WHERE uid =:uid;" Sql_oauth_consumers: STRING = "SELECT name FROM oauth2_consumers"; + Sql_table_prefix: STRING = "oauth2_" + +feature -- {NONE} Consumer + + Sql_oauth_consumer_callback: STRING ="SELECT * FROM oauth2_consumers where callback_name =:name;" + + Sql_oauth_consumer_name: STRING ="SELECT * FROM oauth2_consumers where name =:name;" + end From 9e51df1e01e47b4b9678c69a9d3adae7555de1c9 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 18 Jun 2015 12:35:56 -0300 Subject: [PATCH 8/8] Updated OAUTH consumer using STRING_8 instead of STRING_32 --- .../persistence/cms_oauth_20_generic_api.e | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/login/persistence/cms_oauth_20_generic_api.e b/modules/login/persistence/cms_oauth_20_generic_api.e index 9de5716..552d2ea 100644 --- a/modules/login/persistence/cms_oauth_20_generic_api.e +++ b/modules/login/persistence/cms_oauth_20_generic_api.e @@ -19,7 +19,7 @@ create feature {NONE} -- Initialize - make (a_endpoint: READABLE_STRING_32; a_authorize_url: READABLE_STRING_32; a_extractor: READABLE_STRING_32) + make (a_endpoint: READABLE_STRING_8; a_authorize_url: READABLE_STRING_8; a_extractor: READABLE_STRING_8) do endpoint := a_endpoint authorize_url := a_authorize_url @@ -30,13 +30,13 @@ feature {NONE} -- Initialize extractor_set: extractor = a_authorize_url end - endpoint: READABLE_STRING_32 + endpoint: READABLE_STRING_8 -- Url that receives the access token request. - authorize_url: READABLE_STRING_32 + authorize_url: READABLE_STRING_8 -- - extractor: READABLE_STRING_32 + extractor: READABLE_STRING_8 -- text, json feature -- Access @@ -51,37 +51,37 @@ feature -- Access end end - access_token_verb: STRING_32 + access_token_verb: STRING_8 do Result := "POST" end - access_token_endpoint: STRING_32 + access_token_endpoint: STRING_8 -- Url that receives the access token request do - create {STRING_32} Result.make_from_string (endpoint) + create Result.make_from_string (endpoint) end - authorization_url (config: OAUTH_CONFIG): detachable STRING_32 + authorization_url (config: OAUTH_CONFIG): detachable STRING_8 -- Url where you should redirect your users to authneticate local - l_result: STRING_32 + l_result: STRING_8 do if attached config.scope as l_scope then - create {STRING_32} l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) + create l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8) if attached config.callback as l_callback then - l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_32)) + l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_8)) end if attached config.callback as l_callback then - l_result.replace_substring_all ("$SCOPE", (create {OAUTH_ENCODER}).encoded_string (l_scope.as_string_32)) + l_result.replace_substring_all ("$SCOPE", (create {OAUTH_ENCODER}).encoded_string (l_scope.as_STRING_8)) Result := l_result end else - create {STRING_32} l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) + create l_result.make_from_string (authorize_url + SCOPED_AUTHORIZE_URL) l_result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8) if attached config.callback as l_callback then - l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_32)) + l_result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_8)) end end end