From 4e01d07da0b989dc16f305998b77c71cfc141b01 Mon Sep 17 00:00:00 2001 From: Antonin Date: Thu, 9 Apr 2015 12:34:48 +0200 Subject: [PATCH 01/22] Added column password in poll table. --- admin/migration.php | 4 +- .../AddColumn_hidden_In_poll_For_0_9.php | 2 +- .../AddColumn_receiveNewComments_For_0_9.php | 2 +- .../AddColumn_uniqId_In_vote_For_0_9.php | 2 +- ...sults_publicly_visible_In_poll_For_0_9.php | 78 +++++++++++++++++++ .../Migration/From_0_0_to_0_8_Migration.php | 2 +- .../Migration/From_0_8_to_0_9_Migration.php | 2 +- 7 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php diff --git a/admin/migration.php b/admin/migration.php index 5ed3c70..ad58d66 100644 --- a/admin/migration.php +++ b/admin/migration.php @@ -25,6 +25,7 @@ use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9; use Framadate\Migration\Alter_Comment_table_for_name_length; use Framadate\Migration\Alter_Comment_table_adding_date; use Framadate\Migration\Generate_uniqId_for_old_votes; +use Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9; use Framadate\Migration\Migration; use Framadate\Migration\RPadVotes_from_0_8; use Framadate\Utils; @@ -43,7 +44,8 @@ $migrations = [ new Generate_uniqId_for_old_votes(), new RPadVotes_from_0_8(), new Alter_Comment_table_for_name_length(), - new Alter_Comment_table_adding_date() + new Alter_Comment_table_adding_date(), + new AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9(), ]; // --------------------------------------- diff --git a/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php b/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php index dd335f9..f03356f 100644 --- a/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php +++ b/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php @@ -57,7 +57,7 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php b/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php index 865502a..850fc63 100644 --- a/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php +++ b/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php @@ -57,7 +57,7 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php b/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php index 440d2de..0b91985 100644 --- a/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php +++ b/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php @@ -57,7 +57,7 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php b/app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php new file mode 100644 index 0000000..9e6e5ca --- /dev/null +++ b/app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php @@ -0,0 +1,78 @@ +query('SHOW TABLES'); + $tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); + + // Check if tables of v0.9 are presents + $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); + return count($diff) === 0; + } + + /** + * This method is called only one time in the migration page. + * + * @param \PDO $pdo The connection to database + * @return bool true is the execution succeeded + */ + function execute(\PDO $pdo) { + $this->alterPollTable($pdo); + + return true; + } + + private function alterPollTable(\PDO $pdo) { + $pdo->exec(' + ALTER TABLE `' . Utils::table('poll') . '` + ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL , + ADD `results_publicly_visible` TINYINT(1) NULL DEFAULT NULL'); + } + +} diff --git a/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php b/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php index ac985f5..01ea838 100644 --- a/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php +++ b/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php @@ -57,7 +57,7 @@ class From_0_0_to_0_8_Migration implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php b/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php index 4598449..fa7f40c 100644 --- a/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php +++ b/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php @@ -57,7 +57,7 @@ class From_0_8_to_0_9_Migration implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded From 05f840ceb01bc74fd330151b2ccef51f66c7a450 Mon Sep 17 00:00:00 2001 From: Antonin Date: Thu, 9 Apr 2015 15:57:37 +0200 Subject: [PATCH 02/22] Safe way to hash password. --- .../Framadate/Security/PasswordHasher.php | 35 +++++++++++++++++++ composer.json | 3 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 app/classes/Framadate/Security/PasswordHasher.php diff --git a/app/classes/Framadate/Security/PasswordHasher.php b/app/classes/Framadate/Security/PasswordHasher.php new file mode 100644 index 0000000..44f4729 --- /dev/null +++ b/app/classes/Framadate/Security/PasswordHasher.php @@ -0,0 +1,35 @@ + Date: Thu, 9 Apr 2015 17:53:00 +0200 Subject: [PATCH 03/22] Password options are used with poll creation. - Just found that the "hidden" parameter wasn't... --- app/classes/Framadate/Form.php | 18 ++++++ .../Framadate/Repositories/PollRepository.php | 8 +-- .../Framadate/Security/PasswordHasher.php | 6 +- .../Framadate/Services/PollService.php | 15 ++++- create_poll.php | 46 +++++++++++++++- js/app/create_poll.js | 15 +++++ locale/de.json | 9 ++- locale/en.json | 14 +++-- locale/es.json | 9 ++- locale/fr.json | 5 +- tpl/create_poll.tpl | 55 +++++++++++++++++-- 11 files changed, 177 insertions(+), 23 deletions(-) diff --git a/app/classes/Framadate/Form.php b/app/classes/Framadate/Form.php index 339f9e3..cd50e6d 100644 --- a/app/classes/Framadate/Form.php +++ b/app/classes/Framadate/Form.php @@ -51,6 +51,24 @@ class Form */ public $hidden; + /** + * If true, a password will be needed to access the poll + * @var boolean + */ + public $use_password; + + /** + * The password needed to access the poll, if $use_password is set to true + * @var string + */ + public $password; + + /** + * If true, the polls results will be also visible for those without password + * @var boolean + */ + public $results_publicly_visible; + /** * List of available choices */ diff --git a/app/classes/Framadate/Repositories/PollRepository.php b/app/classes/Framadate/Repositories/PollRepository.php index 95debe4..7201d9b 100644 --- a/app/classes/Framadate/Repositories/PollRepository.php +++ b/app/classes/Framadate/Repositories/PollRepository.php @@ -11,12 +11,12 @@ class PollRepository extends AbstractRepository { parent::__construct($connect); } - public function insertPoll($poll_id, $admin_poll_id, $form) { + public function insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible) { $sql = 'INSERT INTO `' . Utils::table('poll') . '` - (id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments) - VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?)'; + (id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible) + VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?)'; $prepared = $this->prepare($sql); - $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments)); + $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments, $form->hidden, $password_hash, $results_publicly_visible)); } function findById($poll_id) { diff --git a/app/classes/Framadate/Security/PasswordHasher.php b/app/classes/Framadate/Security/PasswordHasher.php index 44f4729..4d1156f 100644 --- a/app/classes/Framadate/Security/PasswordHasher.php +++ b/app/classes/Framadate/Security/PasswordHasher.php @@ -15,7 +15,7 @@ class PasswordHasher { /** * Hash a password * - * @param $password the password to hash. + * @param string $password the password to hash. * @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash. */ public static function hash($password) { @@ -25,8 +25,8 @@ class PasswordHasher { /** * Verify a password with a hash * - * @param $password the password to verify - * @param $hash the hash to compare. + * @param string $password the password to verify + * @param string $hash the hash to compare. * @return bool */ public static function verify($password, $hash) { diff --git a/app/classes/Framadate/Services/PollService.php b/app/classes/Framadate/Services/PollService.php index 120ae97..1ae134c 100644 --- a/app/classes/Framadate/Services/PollService.php +++ b/app/classes/Framadate/Services/PollService.php @@ -20,8 +20,10 @@ namespace Framadate\Services; use Framadate\Form; use Framadate\FramaDB; -use Framadate\Repositories\RepositoryFactory; +use Framadate\Utils; use Framadate\Security\Token; +use Framadate\Security\PasswordHasher; +use Framadate\Repositories\RepositoryFactory; class PollService { @@ -110,9 +112,18 @@ class PollService { } while ($this->pollRepository->existsById($poll_id)); $admin_poll_id = $poll_id . $this->random(8); + // Password hash, if needed + if ($form->use_password) { + $password_hash = PasswordHasher::hash($form->password); + $results_publicly_visible = $form->results_publicly_visible; + } else { + $password_hash = null; + $results_publicly_visible = null; + } + // Insert poll + slots $this->pollRepository->beginTransaction(); - $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form); + $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible); $this->slotRepository->insertSlots($poll_id, $form->getChoices()); $this->pollRepository->commit(); diff --git a/create_poll.php b/create_poll.php index 43c5b4a..3faa240 100644 --- a/create_poll.php +++ b/create_poll.php @@ -19,6 +19,7 @@ use Framadate\Form; use Framadate\Services\InputService; +use Framadate\Editable; use Framadate\Utils; include_once __DIR__ . '/app/inc/init.php'; @@ -60,12 +61,18 @@ if ($goToStep2) { $receiveNewVotes = isset($_POST['receiveNewVotes']) ? $inputService->filterBoolean($_POST['receiveNewVotes']) : false; $receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false; $hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false; + $use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]); + $password = isset($_POST['password'])?$_POST['password']:null; + $password_repeat = isset($_POST['password_repeat'])?$_POST['password_repeat']:null; + $results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]); // On initialise également les autres variables $error_on_mail = false; $error_on_title = false; $error_on_name = false; $error_on_description = false; + $error_on_password = false; + $error_on_password_repeat = false; $_SESSION['form']->title = $title; $_SESSION['form']->admin_name = $name; @@ -75,6 +82,10 @@ if ($goToStep2) { $_SESSION['form']->receiveNewVotes = $receiveNewVotes; $_SESSION['form']->receiveNewComments = $receiveNewComments; $_SESSION['form']->hidden = $hidden; + $_SESSION['form']->use_password = ($use_password !== null); + $_SESSION['form']->password = $password; + $_SESSION['form']->results_publicly_visible = ($results_publicly_visible !== null); + if ($config['use_smtp'] == true) { if (empty($mail)) { @@ -101,7 +112,16 @@ if ($goToStep2) { $email_OK = true; } - if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name) { + if ($use_password) { + if (empty($password)) { + $error_on_password = true; + } else if ($password != $password_repeat) { + $error_on_password_repeat = true; + } + } + + if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name + && !$error_on_password && !$error_on_password_repeat) { if ($goToStep2 == 'date') { header('Location:create_date_poll.php'); @@ -143,6 +163,16 @@ $errors = array( 'msg' => '', 'aria' => '', 'class' => '' + ), + 'password' => array( + 'msg' => '', + 'aria' => '', + 'class' => '' + ), + 'password_repeat' => array( + 'msg' => '', + 'aria' => '', + 'class' => '' ) ); @@ -182,6 +212,17 @@ if (!empty($_POST[GO_TO_STEP_2])) { $errors['email']['class'] = ' has-error'; $errors['email']['msg'] = __('Error', 'The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.'); } + + if ($error_on_password) { + $errors['password']['aria'] = 'aria-describeby="poll_password_error" '; + $errors['password']['class'] = ' has-error'; + $errors['password']['msg'] = __('Error', 'Password is empty'); + } + if ($error_on_password_repeat) { + $errors['password_repeat']['aria'] = 'aria-describeby="poll_password_repeat_error" '; + $errors['password_repeat']['class'] = ' has-error'; + $errors['password_repeat']['msg'] = __('Error', 'Passwords do not match'); + } } $useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']); @@ -201,6 +242,9 @@ $smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $_SESSION[ $smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $_SESSION['form']->receiveNewVotes)); $smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $_SESSION['form']->receiveNewComments)); $smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $_SESSION['form']->hidden)); +$smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $_SESSION['form']->use_password)); +$smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $_SESSION['form']->results_publicly_visible)); +$smarty->assign('poll_password', Utils::fromPostOrDefault('password', $_SESSION['form']->password)); $smarty->assign('form', $_SESSION['form']); $smarty->display('create_poll.tpl'); diff --git a/js/app/create_poll.js b/js/app/create_poll.js index 66521dc..4a77ab5 100644 --- a/js/app/create_poll.js +++ b/js/app/create_poll.js @@ -18,6 +18,9 @@ $(document).ready(function () { + /** + * Error check when submitting form + */ $("#formulaire").submit(function (event) { var isHidden = $("#hidden").prop('checked'); var isOptionAllUserCanModifyEverything = $("#editableByAll").is(":checked"); @@ -30,6 +33,18 @@ $(document).ready(function () { } }); + /** + * Hide/Show password options + */ + var usePassword = $("#use_password"); + usePassword.change(function(){ + if (usePassword.prop("checked")) { + $("#password_options").removeClass("hidden"); + } else { + $("#password_options").addClass("hidden"); + } + }); + // Check cookies are enabled too var cookieEnabled = function () { var cookieEnabled = navigator.cookieEnabled; diff --git a/locale/de.json b/locale/de.json index 3147a0e..3390065 100644 --- a/locale/de.json +++ b/locale/de.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Bei jeder neuen Wertung eine E-Mail erhalten", "To receive an email for each new comment": "Bei jedem neuen Kommentar eine E-Mail erhalten", "Only the poll maker can see the poll's results": "Einzig der Autor der Abstimmung kann die Ergebnisse einsehen.", + "Use a password to restrict access.": "DE_Utiliser un mot de passe pour restreindre l'accès au sondage.", + "The results are publicly visible.": "DE_Les résultats sont visibles sans mot de passe.", + "Poll password": "DE_Mot de passe", + "Confirm password": "DE_Confirmer votre mot de passe.", "Go to step 2": "Weiter zum 2. Schritt" }, "Step 2": { @@ -338,6 +342,9 @@ "You can't create a poll with hidden results with the following edition option:": "Sie können mit der folgenden Editier-Option keine Umfrage mit versteckten Ergebnissen erzeugen:", "Failed to delete column": "Löschen der Spalte fehlgeschlagen", "MISSING_VALUES": "Fehlende Werte", - "CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden" + "CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden", + "Password is empty": "DE_Le mot de passe est vide.", + "Passwords do not match": "DE_Les mot de passes ne correspondent pas." + } } diff --git a/locale/en.json b/locale/en.json index 7cf3d2c..8b004d6 100644 --- a/locale/en.json +++ b/locale/en.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Receive an email for each new vote", "To receive an email for each new comment": "Receive an email for each new comment", "Only the poll maker can see the poll's results": "Only the poll maker can see the poll results", + "Use a password to restrict access.": "Use a password to restrict access.", + "The results are publicly visible.": "The results are publicly visible.", + "Poll password": "Password", + "Confirm password": "Confirmer votre mot de passe.", "Go to step 2": "Go to step 2" }, "Step 2": { @@ -272,9 +276,9 @@ "Expiration date": "Expiry date", "Votes": "Votes", "Actions": "Actions", - "See the poll": "Go to poll", - "Change the poll": "Change poll", - "Deleted the poll": "Delete the poll", + "See the poll": "See the poll", + "Change the poll": "Change the poll", + "Deleted the poll": "Deleted the poll", "Summary": "Summary", "Success": "Success", "Fail": "Fail", @@ -338,6 +342,8 @@ "You can't create a poll with hidden results with the following edition option:": "You can't create a poll with hidden results with the following option: ", "Failed to delete column": "Failed to delete column", "MISSING_VALUES": "Missing values", - "CANT_CONNECT_TO_DATABASE": "Unable to connect to database" + "CANT_CONNECT_TO_DATABASE": "Unable to connect to database", + "Password is empty": "Password is empty.", + "Passwords do not match": "Passwords do not match." } } diff --git a/locale/es.json b/locale/es.json index 37391f5..51a6991 100644 --- a/locale/es.json +++ b/locale/es.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Usted quiere recibir un correo electónico cada vez que alguien participe a la encuesta", "To receive an email for each new comment": "ES_Recevoir un courriel à chaque commentaire", "Only the poll maker can see the poll's results": "ES_Seul le créateur du sondage peut voir les résultats", + "Use a password to restrict access.": "ES_Utiliser un mot de passe pour restreindre l'accès au sondage.", + "The results are publicly visible.": "ES_Les résultats sont visibles sans mot de passe.", + "Poll password": "ES_Mot de passe", + "Confirm password": "ES_Confirmer votre mot de passe.", "Go to step 2": "ES_Aller à l'étape 2" }, "Step 2": { @@ -338,6 +342,9 @@ "You can't create a poll with hidden results with the following edition option:": "ES_Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ", "Failed to delete column": "Error al eliminar la columna", "MISSING_VALUES": "Los valores perdidos", - "CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos" + "CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos", + "Password is empty": "ES_Le mot de passe est vide.", + "Passwords do not match": "ES_Les mot de passes ne correspondent pas." + } } diff --git a/locale/fr.json b/locale/fr.json index af50990..4f3cc62 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Recevoir un courriel à chaque participation d'un sondé", "To receive an email for each new comment": "Recevoir un courriel à chaque commentaire", "Only the poll maker can see the poll's results": "Seul le créateur du sondage peut voir les résultats", + "Use a password to restrict access.": "Utiliser un mot de passe pour restreindre l'accès au sondage.", + "The results are publicly visible.": "Les résultats sont visibles sans mot de passe.", + "Poll password": "Mot de passe", + "Confirm password": "Confirmer votre mot de passe.", "Go to step 2": "Aller à l'étape 2" }, "Step 2": { @@ -263,7 +267,6 @@ "Migration": "Migration", "Purge": "Purge", "Logs": "Historique", - "Installation": "Installation", "Poll ID": "ID sondage", "Format": "Format", "Title": "Titre", diff --git a/tpl/create_poll.tpl b/tpl/create_poll.tpl index 8e98899..43b1fcb 100644 --- a/tpl/create_poll.tpl +++ b/tpl/create_poll.tpl @@ -49,7 +49,7 @@ {/if} -
+
@@ -69,7 +69,7 @@ {/if} {if $use_smtp} -
+
- {/if} + {/if}
@@ -144,18 +144,61 @@
- +
+
+
+ +
+
+ +
From 79133ef70e73ee3703a41b87bbe2894c39462edd Mon Sep 17 00:00:00 2001 From: Antonin Date: Thu, 9 Apr 2015 18:18:05 +0200 Subject: [PATCH 04/22] Hashing the password early --- app/classes/Framadate/Form.php | 4 ++-- .../Framadate/Repositories/PollRepository.php | 4 ++-- app/classes/Framadate/Services/PollService.php | 12 +----------- create_poll.php | 11 +++++++++-- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/app/classes/Framadate/Form.php b/app/classes/Framadate/Form.php index cd50e6d..07b7868 100644 --- a/app/classes/Framadate/Form.php +++ b/app/classes/Framadate/Form.php @@ -58,10 +58,10 @@ class Form public $use_password; /** - * The password needed to access the poll, if $use_password is set to true + * The password needed to access the poll, hashed. Only used if $use_password is set to true * @var string */ - public $password; + public $password_hash; /** * If true, the polls results will be also visible for those without password diff --git a/app/classes/Framadate/Repositories/PollRepository.php b/app/classes/Framadate/Repositories/PollRepository.php index 7201d9b..19f1ed9 100644 --- a/app/classes/Framadate/Repositories/PollRepository.php +++ b/app/classes/Framadate/Repositories/PollRepository.php @@ -11,12 +11,12 @@ class PollRepository extends AbstractRepository { parent::__construct($connect); } - public function insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible) { + public function insertPoll($poll_id, $admin_poll_id, $form) { $sql = 'INSERT INTO `' . Utils::table('poll') . '` (id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible) VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?)'; $prepared = $this->prepare($sql); - $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments, $form->hidden, $password_hash, $results_publicly_visible)); + $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments, $form->hidden, $form->password_hash, $form->results_publicly_visible)); } function findById($poll_id) { diff --git a/app/classes/Framadate/Services/PollService.php b/app/classes/Framadate/Services/PollService.php index 1ae134c..4031ac8 100644 --- a/app/classes/Framadate/Services/PollService.php +++ b/app/classes/Framadate/Services/PollService.php @@ -22,7 +22,6 @@ use Framadate\Form; use Framadate\FramaDB; use Framadate\Utils; use Framadate\Security\Token; -use Framadate\Security\PasswordHasher; use Framadate\Repositories\RepositoryFactory; class PollService { @@ -112,18 +111,9 @@ class PollService { } while ($this->pollRepository->existsById($poll_id)); $admin_poll_id = $poll_id . $this->random(8); - // Password hash, if needed - if ($form->use_password) { - $password_hash = PasswordHasher::hash($form->password); - $results_publicly_visible = $form->results_publicly_visible; - } else { - $password_hash = null; - $results_publicly_visible = null; - } - // Insert poll + slots $this->pollRepository->beginTransaction(); - $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible); + $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form); $this->slotRepository->insertSlots($poll_id, $form->getChoices()); $this->pollRepository->commit(); diff --git a/create_poll.php b/create_poll.php index 3faa240..06b4143 100644 --- a/create_poll.php +++ b/create_poll.php @@ -21,6 +21,7 @@ use Framadate\Form; use Framadate\Services\InputService; use Framadate\Editable; use Framadate\Utils; +use Framadate\Security\PasswordHasher; include_once __DIR__ . '/app/inc/init.php'; @@ -83,7 +84,6 @@ if ($goToStep2) { $_SESSION['form']->receiveNewComments = $receiveNewComments; $_SESSION['form']->hidden = $hidden; $_SESSION['form']->use_password = ($use_password !== null); - $_SESSION['form']->password = $password; $_SESSION['form']->results_publicly_visible = ($results_publicly_visible !== null); @@ -123,6 +123,14 @@ if ($goToStep2) { if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name && !$error_on_password && !$error_on_password_repeat) { + // If no errors, we hash the password if needed + if ($_SESSION['form']->use_password) { + $_SESSION['form']->password_hash = PasswordHasher::hash($password); + } else { + $_SESSION['form']->password_hash = null; + $_SESSION['form']->results_publicly_visible = null; + } + if ($goToStep2 == 'date') { header('Location:create_date_poll.php'); exit(); @@ -244,7 +252,6 @@ $smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewC $smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $_SESSION['form']->hidden)); $smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $_SESSION['form']->use_password)); $smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $_SESSION['form']->results_publicly_visible)); -$smarty->assign('poll_password', Utils::fromPostOrDefault('password', $_SESSION['form']->password)); $smarty->assign('form', $_SESSION['form']); $smarty->display('create_poll.tpl'); From 5955c0ece06008ad4c657646e4aae968f5884154 Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 13:43:58 +0100 Subject: [PATCH 05/22] Composer.lock update --- composer.lock | 142 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 48 deletions(-) diff --git a/composer.lock b/composer.lock index 4d7862e..1b2e6af 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,63 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "80025abeaccbfee148a75dd3ff34c19e", + "hash": "41953f68c6dc68f014c6072de985a81d", + "content-hash": "c85ba8e14cce189aef869998ce0e2430", "packages": [ + { + "name": "ircmaxell/password-compat", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "1ecb013b51756effed3a3c446a314084b54c9916" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/1ecb013b51756effed3a3c446a314084b54c9916", + "reference": "1ecb013b51756effed3a3c446a314084b54c9916", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2015-08-11 14:39:38" + }, { "name": "o80/i18n", "version": "dev-develop", "source": { "type": "git", "url": "https://github.com/olivierperez/o80-i18n.git", - "reference": "9b57197b395add3afc62fd674b03926230819b97" + "reference": "8f3fbc7c965559802ed4eda602528a24d641ea15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/9b57197b395add3afc62fd674b03926230819b97", - "reference": "9b57197b395add3afc62fd674b03926230819b97", + "url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/8f3fbc7c965559802ed4eda602528a24d641ea15", + "reference": "8f3fbc7c965559802ed4eda602528a24d641ea15", "shasum": "" }, "require": { @@ -50,20 +93,20 @@ "internationalization", "php" ], - "time": "2015-05-09 22:08:09" + "time": "2015-09-21 21:18:45" }, { "name": "phpmailer/phpmailer", - "version": "v5.2.10", + "version": "v5.2.13", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "07005ecbb80d11ec8c0f067bb37e8909cc7fcbb7" + "reference": "45df3a88f7f46071e10d0b600f228d19f95911b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/07005ecbb80d11ec8c0f067bb37e8909cc7fcbb7", - "reference": "07005ecbb80d11ec8c0f067bb37e8909cc7fcbb7", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/45df3a88f7f46071e10d0b600f228d19f95911b3", + "reference": "45df3a88f7f46071e10d0b600f228d19f95911b3", "shasum": "" }, "require": { @@ -71,12 +114,17 @@ }, "require-dev": { "phpdocumentor/phpdocumentor": "*", - "phpunit/phpunit": "4.3.*" + "phpunit/phpunit": "4.7.*" + }, + "suggest": { + "league/oauth2-client": "Needed for Gmail's XOAUTH2 authentication system" }, "type": "library", "autoload": { "classmap": [ "class.phpmailer.php", + "class.phpmaileroauth.php", + "class.phpmaileroauthgoogle.php", "class.smtp.php", "class.pop3.php", "extras/EasyPeasyICS.php", @@ -105,7 +153,7 @@ } ], "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "time": "2015-05-04 12:37:21" + "time": "2015-09-14 09:18:12" }, { "name": "smarty/smarty", @@ -137,7 +185,7 @@ "libs/sysplugins/smarty_security.php" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "LGPL-3.0" ], @@ -329,16 +377,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.2.2", + "version": "2.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c" + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c", - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", "shasum": "" }, "require": { @@ -387,7 +435,7 @@ "testing", "xunit" ], - "time": "2015-08-04 03:42:39" + "time": "2015-10-06 15:47:00" }, { "name": "phpunit/php-file-iterator", @@ -520,16 +568,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.6", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b" + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3ab72c62e550370a6cd5dc873e1a04ab57562f5b", - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", "shasum": "" }, "require": { @@ -565,20 +613,20 @@ "keywords": [ "tokenizer" ], - "time": "2015-08-16 08:51:00" + "time": "2015-09-15 10:49:45" }, { "name": "phpunit/phpunit", - "version": "4.8.4", + "version": "4.8.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7" + "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", - "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e", + "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e", "shasum": "" }, "require": { @@ -637,24 +685,24 @@ "testing", "xunit" ], - "time": "2015-08-15 04:21:23" + "time": "2015-10-23 06:48:33" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.6", + "version": "2.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42" + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/18dfbcb81d05e2296c0bcddd4db96cade75e6f42", - "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", "shasum": "" }, "require": { - "doctrine/instantiator": "~1.0,>=1.0.2", + "doctrine/instantiator": "^1.0.2", "php": ">=5.3.3", "phpunit/php-text-template": "~1.2", "sebastian/exporter": "~1.2" @@ -693,7 +741,7 @@ "mock", "xunit" ], - "time": "2015-07-10 06:54:24" + "time": "2015-10-02 06:51:40" }, { "name": "sebastian/comparator", @@ -929,16 +977,16 @@ }, { "name": "sebastian/global-state", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", "shasum": "" }, "require": { @@ -976,7 +1024,7 @@ "keywords": [ "global state" ], - "time": "2014-10-06 09:23:50" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", @@ -1068,24 +1116,21 @@ }, { "name": "symfony/yaml", - "version": "v2.7.3", + "version": "v2.7.6", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "71340e996171474a53f3d29111d046be4ad8a0ff" + "url": "https://github.com/symfony/yaml.git", + "reference": "eca9019c88fbe250164affd107bc8057771f3f4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff", - "reference": "71340e996171474a53f3d29111d046be4ad8a0ff", + "url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d", + "reference": "eca9019c88fbe250164affd107bc8057771f3f4d", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, "type": "library", "extra": { "branch-alias": { @@ -1113,13 +1158,14 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-07-28 14:07:07" + "time": "2015-10-11 09:39:48" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "o80/i18n": 20 + "o80/i18n": 20, + "ircmaxell/password-compat": 20 }, "prefer-stable": false, "prefer-lowest": false, From f6f04c69e0814c496757a888b74390b05a207e51 Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 15:08:54 +0100 Subject: [PATCH 06/22] Cleaning useless code. --- adminstuds.php | 24 ------------------------ studs.php | 23 ----------------------- 2 files changed, 47 deletions(-) diff --git a/adminstuds.php b/adminstuds.php index 422fc2b..f1b2dd9 100644 --- a/adminstuds.php +++ b/adminstuds.php @@ -233,30 +233,6 @@ if (isset($_POST['confirm_remove_all_votes'])) { } } -// ------------------------------- -// Add a comment -// ------------------------------- - -if (isset($_POST['add_comment'])) { - $name = $inputService->filterName($_POST['name']); - $comment = $inputService->filterComment($_POST['comment']); - - if ($name == null) { - $message = new Message('danger', __('Error', 'The name is invalid.')); - } - - if ($message == null) { - // Add comment - $result = $pollService->addComment($poll_id, $name, $comment); - if ($result) { - $message = new Message('success', __('Comments', 'Comment added')); - } else { - $message = new Message('danger', __('Error', 'Comment failed')); - } - } - -} - // ------------------------------- // Delete a comment // ------------------------------- diff --git a/studs.php b/studs.php index f4306f1..c6c4616 100644 --- a/studs.php +++ b/studs.php @@ -128,29 +128,6 @@ if (!empty($_POST['save'])) { // Save edition of an old vote } } } - -// ------------------------------- -// Add a comment -// ------------------------------- - -if (isset($_POST['add_comment'])) { - $name = $inputService->filterName($_POST['name']); - $comment = $inputService->filterComment($_POST['comment']); - - if ($name == null) { - $message = new Message('danger', __('Error', 'The name is invalid.')); - } - - if ($message == null) { - // Add comment - $result = $pollService->addComment($poll_id, $name, $comment); - if ($result) { - $message = new Message('success', __('Comments', 'Comment added')); - $notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name); - } else { - $message = new Message('danger', __('Error', 'Comment failed')); - } - } } // Retrieve data From 1ec9ea2dd5bc5f1cb7b5dbb3fff8a5ed435135b3 Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 16:36:54 +0100 Subject: [PATCH 07/22] Sugar for comment sending. - Do not send if comment empty - Disable submit button while submit is in progress --- js/app/studs.js | 71 ++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/js/app/studs.js b/js/app/studs.js index bb0c89b..6a4b300 100644 --- a/js/app/studs.js +++ b/js/app/studs.js @@ -49,40 +49,45 @@ $(document).ready(function () { form.submit(function(event) { event.preventDefault(); - $.ajax({ - type: 'POST', - url: form.attr('action'), - data: form.serialize(), - dataType: 'json', - success: function(data) - { - $('#comment').val(''); - - if (data.result) { - $('#comments_list') - .replaceWith(data.comments); - var lastComment = $('#comments_list') - .find('div.comment') - .last(); - lastComment.effect('highlight', {color: 'green'}, 401); - $('html, body').animate({ - scrollTop: lastComment.offset().top - }, 750); - } else { - var newMessage = $('#genericErrorTemplate').clone(); - newMessage - .find('.contents') - .text(data.message.message); - var commentsAlert = $('#comments_alerts'); - commentsAlert - .empty() - .append(newMessage); - $('html, body').animate({ - scrollTop: commentsAlert.offset().top - }, 750); + if ($('#comment').val()) { + $('#add_comment').attr("disabled", "disabled"); + $.ajax({ + type: 'POST', + url: form.attr('action'), + data: form.serialize(), + dataType: 'json', + success: function(data) + { + $('#comment').val(''); + if (data.result) { + $('#comments_list') + .replaceWith(data.comments); + var lastComment = $('#comments_list') + .find('div.comment') + .last(); + lastComment.effect('highlight', {color: 'green'}, 401); + $('html, body').animate({ + scrollTop: lastComment.offset().top + }, 750); + } else { + var newMessage = $('#genericErrorTemplate').clone(); + newMessage + .find('.contents') + .text(data.message.message); + var commentsAlert = $('#comments_alerts'); + commentsAlert + .empty() + .append(newMessage); + $('html, body').animate({ + scrollTop: commentsAlert.offset().top + }, 750); + } + }, + complete: function() { + $('#add_comment').removeAttr("disabled"); } - } - }); + }); + } return false; }); From c194d9a43f217d2687dba13ae96a2d950ee3bb5d Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 17:30:42 +0100 Subject: [PATCH 08/22] Applying the password restriction in studs. --- action/add_comment.php | 11 +- adminstuds.php | 2 + .../Framadate/Services/SecurityService.php | 42 +++++ css/style.css | 6 + locale/de.json | 7 + locale/en.json | 7 + locale/es.json | 7 + locale/fr.json | 7 + locale/it.json | 7 + studs.php | 161 +++++++++++------- tpl/part/comments.tpl | 15 +- tpl/part/password_request.tpl | 18 ++ tpl/part/vote_table_classic.tpl | 4 +- tpl/part/vote_table_date.tpl | 4 +- tpl/studs.tpl | 87 +++++----- 15 files changed, 271 insertions(+), 114 deletions(-) create mode 100644 tpl/part/password_request.tpl diff --git a/action/add_comment.php b/action/add_comment.php index 4bdd07c..9d537d2 100644 --- a/action/add_comment.php +++ b/action/add_comment.php @@ -21,6 +21,7 @@ use Framadate\Services\PollService; use Framadate\Services\InputService; use Framadate\Services\MailService; use Framadate\Services\NotificationService; +use Framadate\Services\SecurityService; use Framadate\Message; use Framadate\Utils; use Framadate\Editable; @@ -34,6 +35,7 @@ $poll_id = null; $poll = null; $message = null; $result = false; +$comments = array(); /* Services */ /*----------*/ @@ -43,7 +45,7 @@ $pollService = new PollService($connect, $logService); $inputService = new InputService(); $mailService = new MailService($config['use_smtp']); $notificationService = new NotificationService($mailService); - +$securityService = new SecurityService(); /* PAGE */ /* ---- */ @@ -57,7 +59,9 @@ if (!empty($_POST['poll'])) { if (!$poll) { $message = new Message('error', __('Error', 'This poll doesn\'t exist !')); -} else { +} else if ($poll && !$securityService->canAccessPoll($poll)) { + $message = new Message('error', __('Password', 'Wrong password')); +} else if ($poll) { $name = $inputService->filterName($_POST['name']); $comment = $inputService->filterComment($_POST['comment']); @@ -75,10 +79,9 @@ if (!$poll) { $message = new Message('danger', __('Error', 'Comment failed')); } } + $comments = $pollService->allCommentsByPollId($poll_id); } -$comments = $pollService->allCommentsByPollId($poll_id); - $smarty->error_reporting = E_ALL & ~E_NOTICE; $smarty->assign('comments', $comments); $comments_html = $smarty->fetch('part/comments_list.tpl'); diff --git a/adminstuds.php b/adminstuds.php index f1b2dd9..e8f13cb 100644 --- a/adminstuds.php +++ b/adminstuds.php @@ -370,5 +370,7 @@ $smarty->assign('editingVoteId', $editingVoteId); $smarty->assign('message', $message); $smarty->assign('admin', true); $smarty->assign('hidden', false); +$smarty->assign('accessGranted', true); +$smarty->assign('resultPubliclyVisible', true); $smarty->display('studs.tpl'); diff --git a/app/classes/Framadate/Services/SecurityService.php b/app/classes/Framadate/Services/SecurityService.php index fc67a72..a4b6f33 100644 --- a/app/classes/Framadate/Services/SecurityService.php +++ b/app/classes/Framadate/Services/SecurityService.php @@ -2,6 +2,7 @@ namespace Framadate\Services; use Framadate\Security\Token; +use Framadate\Security\PasswordHasher; class SecurityService { @@ -48,5 +49,46 @@ class SecurityService { return $checked; } + /** + * Verify if the current session allows to access given poll. + * + * @param $poll \stdClass The poll which we seek access + * @return bool true if the current session can access this poll + */ + public function canAccessPoll($poll) { + if (is_null($poll->password_hash)) { + return true; + } + + $this->ensureSessionPollSecurityIsCreated(); + + $currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null; + if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) { + return true; + } else { + unset($_SESSION['poll_security'][$poll->id]); + return false; + } + } + + /** + * Submit to the session a poll password + * + * @param $poll \stdClass The poll which we seek access + * @param $password string the password to compare + */ + public function submitPollAccess($poll, $password) { + if (!empty($password)) { + $this->ensureSessionPollSecurityIsCreated(); + $_SESSION['poll_security'][$poll->id] = $password; + } + } + + private function ensureSessionPollSecurityIsCreated() { + if (!isset($_SESSION['poll_security'])) { + $_SESSION['poll_security'] = array(); + } + } + } \ No newline at end of file diff --git a/css/style.css b/css/style.css index b88ae29..16e1865 100644 --- a/css/style.css +++ b/css/style.css @@ -488,4 +488,10 @@ table.results > tbody > tr:hover > td .glyphicon { /* Admin */ #poll_search { cursor: pointer; +} + +/* Studs */ +.password_request { + padding-top: 15px; + padding-bottom: 15px; } \ No newline at end of file diff --git a/locale/de.json b/locale/de.json index 3390065..4528f34 100644 --- a/locale/de.json +++ b/locale/de.json @@ -156,6 +156,13 @@ "anonyme": "anonym", "Comment added": "Kommentar hinzugefügt" }, + "Password": { + "Password": "DE_Mot de passe", + "Wrong password": "DE_Mot de passe incorrect.", + "Submit access": "DE_Accèder", + "You have to provide a password to access the poll.": "DE_Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "DE_Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Wenn Sie an dieser Umfrage teilnehmen möchten, müssen Sie ihren Namen angeben, die Auswahl treffen, die Ihnen am ehesten zusagt und mit dem Plus-Button am Ende der Zeile bestätigen.", "POLL_LOCKED_WARNING": "Die Abstimmung wurde vom Administrator gesperrt. Bewertungen und Kommentare werden eingefroren; es ist nicht mehr möglich, teilzunehmen", diff --git a/locale/en.json b/locale/en.json index 8b004d6..67af1dd 100644 --- a/locale/en.json +++ b/locale/en.json @@ -156,6 +156,13 @@ "anonyme": "anonyme", "Comment added": "Comment saved" }, + "Password": { + "Password": "Password", + "Wrong password": "Wrong password", + "Submit access": "Submit access", + "You have to provide a password to access the poll.": "You have to provide a password to access the poll.", + "You have to provide a password so you can participate to the poll.": "You have to provide a password so you can participate to the poll." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "If you want to vote in this poll, you have to give your name, make your choice, and submit it with the plus button at the end of the line.", "POLL_LOCKED_WARNING": "The administrator locked this poll. Votes and comments are frozen, it is no longer possible to participate", diff --git a/locale/es.json b/locale/es.json index 51a6991..5350b3f 100644 --- a/locale/es.json +++ b/locale/es.json @@ -156,6 +156,13 @@ "anonyme": "ES_anonyme", "Comment added": "ES_Commentaire ajouté" }, + "Password": { + "Password": "ES_Mot de passe", + "Wrong password": "ES_Mot de passe incorrect.", + "Submit access": "ES_Accèder", + "You have to provide a password to access the poll.": "ES_Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "ES_Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Para participar a esta encuesta, introduzca su nombre, elige todas las valores que son apriopriadas y validar su seleccion con el botón verde a la fin de línea.", "POLL_LOCKED_WARNING": "ES_L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer", diff --git a/locale/fr.json b/locale/fr.json index 4f3cc62..d76e66e 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -156,6 +156,13 @@ "anonyme": "anonyme", "Comment added": "Commentaire ajouté" }, + "Password": { + "Password": "Mot de passe", + "Wrong password": "Mot de passe incorrect.", + "Submit access": "Accèder", + "You have to provide a password to access the poll.": "Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Pour participer à ce sondage, veuillez entrer votre nom, choisir toutes les valeurs qui vous conviennent et valider votre choix avec le bouton en bout de ligne.", "POLL_LOCKED_WARNING": "L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer", diff --git a/locale/it.json b/locale/it.json index 0ca5008..81759f0 100644 --- a/locale/it.json +++ b/locale/it.json @@ -156,6 +156,13 @@ "anonyme": "anonimo", "Comment added": "Commento aggiunto" }, + "Password": { + "Password": "IT_Mot de passe", + "Wrong password": "IT_Mot de passe incorrect.", + "Submit access": "IT_Accèder", + "You have to provide a password to access the poll.": "IT_Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "IT_Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Per partecipare a questo sondaggio, è necessario inserire vostro nome, scegliere tutti i parametri che preferite e confermare la vostra scelta con il pulsante alla fine della riga.", "POLL_LOCKED_WARNING": "L'amministratore ha bloccato questa indagine. Voti e commenti sono congelati, non è più possibile partecipare", diff --git a/studs.php b/studs.php index c6c4616..d43ff0c 100644 --- a/studs.php +++ b/studs.php @@ -21,6 +21,7 @@ use Framadate\Services\PollService; use Framadate\Services\InputService; use Framadate\Services\MailService; use Framadate\Services\NotificationService; +use Framadate\Services\SecurityService; use Framadate\Message; use Framadate\Utils; use Framadate\Editable; @@ -34,6 +35,11 @@ $poll_id = null; $poll = null; $message = null; $editingVoteId = 0; +$accessGranted = true; +$resultPubliclyVisible = true; +$slots = array(); +$votes = array(); +$comments = array(); /* Services */ /*----------*/ @@ -43,6 +49,7 @@ $pollService = new PollService($connect, $logService); $inputService = new InputService(); $mailService = new MailService($config['use_smtp']); $notificationService = new NotificationService($mailService); +$securityService = new SecurityService(); /* PAGE */ @@ -62,78 +69,114 @@ if (!$poll) { } // ------------------------------- -// A vote is going to be edited +// Password verification // ------------------------------- -if (!empty($_GET['vote'])) { - $editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); +// TEMP, for testing purpose +if (isset($_GET['reset']) && $_GET['reset']) { + unset($_SESSION['poll_security']); } -// ------------------------------- -// Something to save (edit or add) -// ------------------------------- +if (!is_null($poll->password_hash)) { -if (!empty($_POST['save'])) { // Save edition of an old vote - $name = $inputService->filterName($_POST['name']); - $editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT); - $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); - - if (empty($editedVote)) { - $message = new Message('danger', __('Error', 'Something is going wrong...')); - } - if (count($choices) != count($_POST['choices'])) { - $message = new Message('danger', __('Error', 'There is a problem with your choices')); + // If we came from password submission + $password = isset($_POST['password']) ? $_POST['password'] : null; + if (!empty($password)) { + $securityService->submitPollAccess($poll, $password); } - if ($message == null) { - // Update vote - $result = $pollService->updateVote($poll_id, $editedVote, $name, $choices); - if ($result) { - if ($poll->editable == Editable::EDITABLE_BY_OWN) { - $editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); - $urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId); - $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); - } else { - $message = new Message('success', __('studs', 'Update vote succeeded')); - } - $notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name); - } else { - $message = new Message('danger', __('Error', 'Update vote failed')); - } + if (!$securityService->canAccessPoll($poll)) { + $accessGranted = false; } -} elseif (isset($_POST['save'])) { // Add a new vote - $name = $inputService->filterName($_POST['name']); - $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); + $resultPubliclyVisible = $poll->results_publicly_visible; - if ($name == null) { - $message = new Message('danger', __('Error', 'The name is invalid.')); - } - if (count($choices) != count($_POST['choices'])) { - $message = new Message('danger', __('Error', 'There is a problem with your choices')); - } - - if ($message == null) { - // Add vote - $result = $pollService->addVote($poll_id, $name, $choices); - if ($result) { - if ($poll->editable == Editable::EDITABLE_BY_OWN) { - $urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId); - $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); - } else { - $message = new Message('success', __('studs', 'Adding the vote succeeded')); - } - $notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name); - } else { - $message = new Message('danger', __('Error', 'Adding vote failed')); - } + if (!$accessGranted && !empty($password)) { + $message = new Message('danger', __('Password', 'Wrong password')); + } else if (!$accessGranted && !$resultPubliclyVisible) { + $message = new Message('danger', __('Password', 'You have to provide a password to access the poll.')); + } else if (!$accessGranted && $resultPubliclyVisible) { + $message = new Message('danger', __('Password', 'You have to provide a password so you can participate to the poll.')); } } + +// We allow actions only if access is granted +if ($accessGranted) { + + // ------------------------------- + // A vote is going to be edited + // ------------------------------- + + if (!empty($_GET['vote'])) { + $editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); + } + + // ------------------------------- + // Something to save (edit or add) + // ------------------------------- + + if (!empty($_POST['save'])) { // Save edition of an old vote + $name = $inputService->filterName($_POST['name']); + $editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT); + $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); + + if (empty($editedVote)) { + $message = new Message('danger', __('Error', 'Something is going wrong...')); + } + if (count($choices) != count($_POST['choices'])) { + $message = new Message('danger', __('Error', 'There is a problem with your choices')); + } + + if ($message == null) { + // Update vote + $result = $pollService->updateVote($poll_id, $editedVote, $name, $choices); + if ($result) { + if ($poll->editable == Editable::EDITABLE_BY_OWN) { + $editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); + $urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId); + $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); + } else { + $message = new Message('success', __('studs', 'Update vote succeeded')); + } + $notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name); + } else { + $message = new Message('danger', __('Error', 'Update vote failed')); + } + } + } elseif (isset($_POST['save'])) { // Add a new vote + $name = $inputService->filterName($_POST['name']); + $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); + + if ($name == null) { + $message = new Message('danger', __('Error', 'The name is invalid.')); + } + if (count($choices) != count($_POST['choices'])) { + $message = new Message('danger', __('Error', 'There is a problem with your choices')); + } + + if ($message == null) { + // Add vote + $result = $pollService->addVote($poll_id, $name, $choices); + if ($result) { + if ($poll->editable == Editable::EDITABLE_BY_OWN) { + $urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId); + $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); + } else { + $message = new Message('success', __('studs', 'Adding the vote succeeded')); + } + $notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name); + } else { + $message = new Message('danger', __('Error', 'Adding vote failed')); + } + } + } } // Retrieve data -$slots = $pollService->allSlotsByPoll($poll); -$votes = $pollService->allVotesByPollId($poll_id); -$comments = $pollService->allCommentsByPollId($poll_id); +if ($resultPubliclyVisible) { + $slots = $pollService->allSlotsByPoll($poll); + $votes = $pollService->allVotesByPollId($poll_id); + $comments = $pollService->allCommentsByPollId($poll_id); +} // Assign data to template $smarty->assign('poll_id', $poll_id); @@ -149,5 +192,7 @@ $smarty->assign('editingVoteId', $editingVoteId); $smarty->assign('message', $message); $smarty->assign('admin', false); $smarty->assign('hidden', $poll->hidden); +$smarty->assign('accessGranted', $accessGranted); +$smarty->assign('resultPubliclyVisible', $resultPubliclyVisible); $smarty->display('studs.tpl'); diff --git a/tpl/part/comments.tpl b/tpl/part/comments.tpl index 7a3ad4a..b9c0fb4 100644 --- a/tpl/part/comments.tpl +++ b/tpl/part/comments.tpl @@ -3,12 +3,11 @@ {* Comment list *} {include 'part/comments_list.tpl'} -
+{* Add comment form *} +{if $active && !$expired && $accessGranted} + - - - {* Add comment form *} - {if $active && !$expired} +
{__('Comments', 'Add a comment to the poll')} @@ -21,11 +20,11 @@
- +
- {/if} - \ No newline at end of file + +{/if} \ No newline at end of file diff --git a/tpl/part/password_request.tpl b/tpl/part/password_request.tpl new file mode 100644 index 0000000..aff6fe3 --- /dev/null +++ b/tpl/part/password_request.tpl @@ -0,0 +1,18 @@ +{if !$expired && ($active || $resultPubliclyVisible)} + + +
+
+
+ +
+ + + +
+
+
+
+
+{/if} + diff --git a/tpl/part/vote_table_classic.tpl b/tpl/part/vote_table_classic.tpl index bd092ef..e5e5070 100644 --- a/tpl/part/vote_table_classic.tpl +++ b/tpl/part/vote_table_classic.tpl @@ -97,7 +97,7 @@ {/foreach} - {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin)} + {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted} {__('Generic', 'Edit')} @@ -119,7 +119,7 @@ {* Line to add a new vote *} - {if $active && $editingVoteId === 0 && !$expired} + {if $active && $editingVoteId === 0 && !$expired && $accessGranted}
diff --git a/tpl/part/vote_table_date.tpl b/tpl/part/vote_table_date.tpl index a072a63..8ff1a04 100644 --- a/tpl/part/vote_table_date.tpl +++ b/tpl/part/vote_table_date.tpl @@ -148,7 +148,7 @@ {/foreach} - {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin)} + {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted} {__('Generic', 'Edit')} @@ -170,7 +170,7 @@ {* Line to add a new vote *} - {if $active && $editingVoteId === 0 && !$expired} + {if $active && $editingVoteId === 0 && !$expired && $accessGranted}
diff --git a/tpl/studs.tpl b/tpl/studs.tpl index e5d1113..7e873ab 100644 --- a/tpl/studs.tpl +++ b/tpl/studs.tpl @@ -12,6 +12,7 @@ {block name=main} + {* Messages *}
{if !empty($message)} @@ -20,49 +21,55 @@ + {if !$accessGranted && !$resultPubliclyVisible} - {* Global informations about the current poll *} + {include 'part/password_request.tpl' active=$poll->active} -{include 'part/poll_info.tpl' admin=$admin} - -{* Information about voting *} -{if $expired} -
-

{__('studs', 'The poll is expired, it will be deleted soon.')}

-

{__('studs', 'Deletion date:')} {$deletion_date|date_format:$date_format['txt_short']|html}

-
-{else} - {if $admin} - {include 'part/poll_hint_admin.tpl'} {else} - {include 'part/poll_hint.tpl' active=$poll->active} + + {* Global informations about the current poll *} + {include 'part/poll_info.tpl' admin=$admin} + + {* Information about voting *} + {if $expired} +
+

{__('studs', 'The poll is expired, it will be deleted soon.')}

+

{__('studs', 'Deletion date:')} {$deletion_date|date_format:$date_format['txt_short']|html}

+
+ {else} + {if $admin} + {include 'part/poll_hint_admin.tpl'} + {else} + {include 'part/poll_hint.tpl' active=$poll->active} + {/if} + {/if} + + {* Scroll left and right *} + + + {if !$accessGranted && $resultPubliclyVisible} + {include 'part/password_request.tpl' active=$poll->active} + {/if} + + {* Vote table *} + {if $poll->format === 'D'} + {include 'part/vote_table_date.tpl' active=$poll->active} + {else} + {include 'part/vote_table_classic.tpl' active=$poll->active} + {/if} + + {* Comments *} + {include 'part/comments.tpl' active=$poll->active comments=$comments} + {/if} -{/if} - -{* Scroll left and right *} - - - - -{* Vote table *} - -{if $poll->format === 'D'} - {include 'part/vote_table_date.tpl' active=$poll->active} -{else} - {include 'part/vote_table_classic.tpl' active=$poll->active} -{/if} - -{* Comments *} - -{include 'part/comments.tpl' active=$poll->active comments=$comments} {/block} From f888a643e06526b44ea38cf1ebc841916c88bb22 Mon Sep 17 00:00:00 2001 From: Antonin Date: Thu, 9 Apr 2015 12:34:48 +0200 Subject: [PATCH 09/22] Added column password in poll table. --- admin/migration.php | 4 +- .../AddColumn_hidden_In_poll_For_0_9.php | 2 +- .../AddColumn_receiveNewComments_For_0_9.php | 2 +- .../AddColumn_uniqId_In_vote_For_0_9.php | 2 +- ...sults_publicly_visible_In_poll_For_0_9.php | 78 +++++++++++++++++++ .../Migration/From_0_0_to_0_8_Migration.php | 2 +- .../Migration/From_0_8_to_0_9_Migration.php | 2 +- 7 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php diff --git a/admin/migration.php b/admin/migration.php index 5ed3c70..ad58d66 100644 --- a/admin/migration.php +++ b/admin/migration.php @@ -25,6 +25,7 @@ use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9; use Framadate\Migration\Alter_Comment_table_for_name_length; use Framadate\Migration\Alter_Comment_table_adding_date; use Framadate\Migration\Generate_uniqId_for_old_votes; +use Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9; use Framadate\Migration\Migration; use Framadate\Migration\RPadVotes_from_0_8; use Framadate\Utils; @@ -43,7 +44,8 @@ $migrations = [ new Generate_uniqId_for_old_votes(), new RPadVotes_from_0_8(), new Alter_Comment_table_for_name_length(), - new Alter_Comment_table_adding_date() + new Alter_Comment_table_adding_date(), + new AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9(), ]; // --------------------------------------- diff --git a/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php b/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php index dd335f9..f03356f 100644 --- a/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php +++ b/app/classes/Framadate/Migration/AddColumn_hidden_In_poll_For_0_9.php @@ -57,7 +57,7 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php b/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php index 865502a..850fc63 100644 --- a/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php +++ b/app/classes/Framadate/Migration/AddColumn_receiveNewComments_For_0_9.php @@ -57,7 +57,7 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php b/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php index 440d2de..0b91985 100644 --- a/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php +++ b/app/classes/Framadate/Migration/AddColumn_uniqId_In_vote_For_0_9.php @@ -57,7 +57,7 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php b/app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php new file mode 100644 index 0000000..9e6e5ca --- /dev/null +++ b/app/classes/Framadate/Migration/AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9.php @@ -0,0 +1,78 @@ +query('SHOW TABLES'); + $tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); + + // Check if tables of v0.9 are presents + $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); + return count($diff) === 0; + } + + /** + * This method is called only one time in the migration page. + * + * @param \PDO $pdo The connection to database + * @return bool true is the execution succeeded + */ + function execute(\PDO $pdo) { + $this->alterPollTable($pdo); + + return true; + } + + private function alterPollTable(\PDO $pdo) { + $pdo->exec(' + ALTER TABLE `' . Utils::table('poll') . '` + ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL , + ADD `results_publicly_visible` TINYINT(1) NULL DEFAULT NULL'); + } + +} diff --git a/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php b/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php index ac985f5..01ea838 100644 --- a/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php +++ b/app/classes/Framadate/Migration/From_0_0_to_0_8_Migration.php @@ -57,7 +57,7 @@ class From_0_0_to_0_8_Migration implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded diff --git a/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php b/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php index 4598449..fa7f40c 100644 --- a/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php +++ b/app/classes/Framadate/Migration/From_0_8_to_0_9_Migration.php @@ -57,7 +57,7 @@ class From_0_8_to_0_9_Migration implements Migration { } /** - * This methode is called only one time in the migration page. + * This method is called only one time in the migration page. * * @param \PDO $pdo The connection to database * @return bool true is the execution succeeded From cddbf3cb43b22777b03f2ddf05856e5edfa63d30 Mon Sep 17 00:00:00 2001 From: Antonin Date: Thu, 9 Apr 2015 15:57:37 +0200 Subject: [PATCH 10/22] Safe way to hash password. --- .../Framadate/Security/PasswordHasher.php | 35 +++++++++++++++++++ composer.json | 3 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 app/classes/Framadate/Security/PasswordHasher.php diff --git a/app/classes/Framadate/Security/PasswordHasher.php b/app/classes/Framadate/Security/PasswordHasher.php new file mode 100644 index 0000000..44f4729 --- /dev/null +++ b/app/classes/Framadate/Security/PasswordHasher.php @@ -0,0 +1,35 @@ + Date: Thu, 9 Apr 2015 17:53:00 +0200 Subject: [PATCH 11/22] Password options are used with poll creation. - Just found that the "hidden" parameter wasn't... --- app/classes/Framadate/Form.php | 18 ++++++ .../Framadate/Repositories/PollRepository.php | 8 +-- .../Framadate/Security/PasswordHasher.php | 6 +- .../Framadate/Services/PollService.php | 15 ++++- create_poll.php | 46 +++++++++++++++- js/app/create_poll.js | 15 +++++ locale/de.json | 9 ++- locale/en.json | 14 +++-- locale/es.json | 9 ++- locale/fr.json | 5 +- tpl/create_poll.tpl | 55 +++++++++++++++++-- 11 files changed, 177 insertions(+), 23 deletions(-) diff --git a/app/classes/Framadate/Form.php b/app/classes/Framadate/Form.php index 339f9e3..cd50e6d 100644 --- a/app/classes/Framadate/Form.php +++ b/app/classes/Framadate/Form.php @@ -51,6 +51,24 @@ class Form */ public $hidden; + /** + * If true, a password will be needed to access the poll + * @var boolean + */ + public $use_password; + + /** + * The password needed to access the poll, if $use_password is set to true + * @var string + */ + public $password; + + /** + * If true, the polls results will be also visible for those without password + * @var boolean + */ + public $results_publicly_visible; + /** * List of available choices */ diff --git a/app/classes/Framadate/Repositories/PollRepository.php b/app/classes/Framadate/Repositories/PollRepository.php index d0e3db9..3554d66 100644 --- a/app/classes/Framadate/Repositories/PollRepository.php +++ b/app/classes/Framadate/Repositories/PollRepository.php @@ -11,12 +11,12 @@ class PollRepository extends AbstractRepository { parent::__construct($connect); } - public function insertPoll($poll_id, $admin_poll_id, $form) { + public function insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible) { $sql = 'INSERT INTO `' . Utils::table('poll') . '` - (id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments) - VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?)'; + (id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible) + VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?)'; $prepared = $this->prepare($sql); - $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments)); + $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments, $form->hidden, $password_hash, $results_publicly_visible)); } function findById($poll_id) { diff --git a/app/classes/Framadate/Security/PasswordHasher.php b/app/classes/Framadate/Security/PasswordHasher.php index 44f4729..4d1156f 100644 --- a/app/classes/Framadate/Security/PasswordHasher.php +++ b/app/classes/Framadate/Security/PasswordHasher.php @@ -15,7 +15,7 @@ class PasswordHasher { /** * Hash a password * - * @param $password the password to hash. + * @param string $password the password to hash. * @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash. */ public static function hash($password) { @@ -25,8 +25,8 @@ class PasswordHasher { /** * Verify a password with a hash * - * @param $password the password to verify - * @param $hash the hash to compare. + * @param string $password the password to verify + * @param string $hash the hash to compare. * @return bool */ public static function verify($password, $hash) { diff --git a/app/classes/Framadate/Services/PollService.php b/app/classes/Framadate/Services/PollService.php index a666041..1be93cd 100644 --- a/app/classes/Framadate/Services/PollService.php +++ b/app/classes/Framadate/Services/PollService.php @@ -20,8 +20,10 @@ namespace Framadate\Services; use Framadate\Form; use Framadate\FramaDB; -use Framadate\Repositories\RepositoryFactory; +use Framadate\Utils; use Framadate\Security\Token; +use Framadate\Security\PasswordHasher; +use Framadate\Repositories\RepositoryFactory; class PollService { @@ -118,9 +120,18 @@ class PollService { } while ($this->pollRepository->existsById($poll_id)); $admin_poll_id = $poll_id . $this->random(8); + // Password hash, if needed + if ($form->use_password) { + $password_hash = PasswordHasher::hash($form->password); + $results_publicly_visible = $form->results_publicly_visible; + } else { + $password_hash = null; + $results_publicly_visible = null; + } + // Insert poll + slots $this->pollRepository->beginTransaction(); - $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form); + $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible); $this->slotRepository->insertSlots($poll_id, $form->getChoices()); $this->pollRepository->commit(); diff --git a/create_poll.php b/create_poll.php index 43c5b4a..3faa240 100644 --- a/create_poll.php +++ b/create_poll.php @@ -19,6 +19,7 @@ use Framadate\Form; use Framadate\Services\InputService; +use Framadate\Editable; use Framadate\Utils; include_once __DIR__ . '/app/inc/init.php'; @@ -60,12 +61,18 @@ if ($goToStep2) { $receiveNewVotes = isset($_POST['receiveNewVotes']) ? $inputService->filterBoolean($_POST['receiveNewVotes']) : false; $receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false; $hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false; + $use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]); + $password = isset($_POST['password'])?$_POST['password']:null; + $password_repeat = isset($_POST['password_repeat'])?$_POST['password_repeat']:null; + $results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]); // On initialise également les autres variables $error_on_mail = false; $error_on_title = false; $error_on_name = false; $error_on_description = false; + $error_on_password = false; + $error_on_password_repeat = false; $_SESSION['form']->title = $title; $_SESSION['form']->admin_name = $name; @@ -75,6 +82,10 @@ if ($goToStep2) { $_SESSION['form']->receiveNewVotes = $receiveNewVotes; $_SESSION['form']->receiveNewComments = $receiveNewComments; $_SESSION['form']->hidden = $hidden; + $_SESSION['form']->use_password = ($use_password !== null); + $_SESSION['form']->password = $password; + $_SESSION['form']->results_publicly_visible = ($results_publicly_visible !== null); + if ($config['use_smtp'] == true) { if (empty($mail)) { @@ -101,7 +112,16 @@ if ($goToStep2) { $email_OK = true; } - if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name) { + if ($use_password) { + if (empty($password)) { + $error_on_password = true; + } else if ($password != $password_repeat) { + $error_on_password_repeat = true; + } + } + + if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name + && !$error_on_password && !$error_on_password_repeat) { if ($goToStep2 == 'date') { header('Location:create_date_poll.php'); @@ -143,6 +163,16 @@ $errors = array( 'msg' => '', 'aria' => '', 'class' => '' + ), + 'password' => array( + 'msg' => '', + 'aria' => '', + 'class' => '' + ), + 'password_repeat' => array( + 'msg' => '', + 'aria' => '', + 'class' => '' ) ); @@ -182,6 +212,17 @@ if (!empty($_POST[GO_TO_STEP_2])) { $errors['email']['class'] = ' has-error'; $errors['email']['msg'] = __('Error', 'The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.'); } + + if ($error_on_password) { + $errors['password']['aria'] = 'aria-describeby="poll_password_error" '; + $errors['password']['class'] = ' has-error'; + $errors['password']['msg'] = __('Error', 'Password is empty'); + } + if ($error_on_password_repeat) { + $errors['password_repeat']['aria'] = 'aria-describeby="poll_password_repeat_error" '; + $errors['password_repeat']['class'] = ' has-error'; + $errors['password_repeat']['msg'] = __('Error', 'Passwords do not match'); + } } $useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']); @@ -201,6 +242,9 @@ $smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $_SESSION[ $smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $_SESSION['form']->receiveNewVotes)); $smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $_SESSION['form']->receiveNewComments)); $smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $_SESSION['form']->hidden)); +$smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $_SESSION['form']->use_password)); +$smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $_SESSION['form']->results_publicly_visible)); +$smarty->assign('poll_password', Utils::fromPostOrDefault('password', $_SESSION['form']->password)); $smarty->assign('form', $_SESSION['form']); $smarty->display('create_poll.tpl'); diff --git a/js/app/create_poll.js b/js/app/create_poll.js index 66521dc..4a77ab5 100644 --- a/js/app/create_poll.js +++ b/js/app/create_poll.js @@ -18,6 +18,9 @@ $(document).ready(function () { + /** + * Error check when submitting form + */ $("#formulaire").submit(function (event) { var isHidden = $("#hidden").prop('checked'); var isOptionAllUserCanModifyEverything = $("#editableByAll").is(":checked"); @@ -30,6 +33,18 @@ $(document).ready(function () { } }); + /** + * Hide/Show password options + */ + var usePassword = $("#use_password"); + usePassword.change(function(){ + if (usePassword.prop("checked")) { + $("#password_options").removeClass("hidden"); + } else { + $("#password_options").addClass("hidden"); + } + }); + // Check cookies are enabled too var cookieEnabled = function () { var cookieEnabled = navigator.cookieEnabled; diff --git a/locale/de.json b/locale/de.json index 3147a0e..3390065 100644 --- a/locale/de.json +++ b/locale/de.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Bei jeder neuen Wertung eine E-Mail erhalten", "To receive an email for each new comment": "Bei jedem neuen Kommentar eine E-Mail erhalten", "Only the poll maker can see the poll's results": "Einzig der Autor der Abstimmung kann die Ergebnisse einsehen.", + "Use a password to restrict access.": "DE_Utiliser un mot de passe pour restreindre l'accès au sondage.", + "The results are publicly visible.": "DE_Les résultats sont visibles sans mot de passe.", + "Poll password": "DE_Mot de passe", + "Confirm password": "DE_Confirmer votre mot de passe.", "Go to step 2": "Weiter zum 2. Schritt" }, "Step 2": { @@ -338,6 +342,9 @@ "You can't create a poll with hidden results with the following edition option:": "Sie können mit der folgenden Editier-Option keine Umfrage mit versteckten Ergebnissen erzeugen:", "Failed to delete column": "Löschen der Spalte fehlgeschlagen", "MISSING_VALUES": "Fehlende Werte", - "CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden" + "CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden", + "Password is empty": "DE_Le mot de passe est vide.", + "Passwords do not match": "DE_Les mot de passes ne correspondent pas." + } } diff --git a/locale/en.json b/locale/en.json index 7cf3d2c..8b004d6 100644 --- a/locale/en.json +++ b/locale/en.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Receive an email for each new vote", "To receive an email for each new comment": "Receive an email for each new comment", "Only the poll maker can see the poll's results": "Only the poll maker can see the poll results", + "Use a password to restrict access.": "Use a password to restrict access.", + "The results are publicly visible.": "The results are publicly visible.", + "Poll password": "Password", + "Confirm password": "Confirmer votre mot de passe.", "Go to step 2": "Go to step 2" }, "Step 2": { @@ -272,9 +276,9 @@ "Expiration date": "Expiry date", "Votes": "Votes", "Actions": "Actions", - "See the poll": "Go to poll", - "Change the poll": "Change poll", - "Deleted the poll": "Delete the poll", + "See the poll": "See the poll", + "Change the poll": "Change the poll", + "Deleted the poll": "Deleted the poll", "Summary": "Summary", "Success": "Success", "Fail": "Fail", @@ -338,6 +342,8 @@ "You can't create a poll with hidden results with the following edition option:": "You can't create a poll with hidden results with the following option: ", "Failed to delete column": "Failed to delete column", "MISSING_VALUES": "Missing values", - "CANT_CONNECT_TO_DATABASE": "Unable to connect to database" + "CANT_CONNECT_TO_DATABASE": "Unable to connect to database", + "Password is empty": "Password is empty.", + "Passwords do not match": "Passwords do not match." } } diff --git a/locale/es.json b/locale/es.json index 37391f5..51a6991 100644 --- a/locale/es.json +++ b/locale/es.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Usted quiere recibir un correo electónico cada vez que alguien participe a la encuesta", "To receive an email for each new comment": "ES_Recevoir un courriel à chaque commentaire", "Only the poll maker can see the poll's results": "ES_Seul le créateur du sondage peut voir les résultats", + "Use a password to restrict access.": "ES_Utiliser un mot de passe pour restreindre l'accès au sondage.", + "The results are publicly visible.": "ES_Les résultats sont visibles sans mot de passe.", + "Poll password": "ES_Mot de passe", + "Confirm password": "ES_Confirmer votre mot de passe.", "Go to step 2": "ES_Aller à l'étape 2" }, "Step 2": { @@ -338,6 +342,9 @@ "You can't create a poll with hidden results with the following edition option:": "ES_Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ", "Failed to delete column": "Error al eliminar la columna", "MISSING_VALUES": "Los valores perdidos", - "CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos" + "CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos", + "Password is empty": "ES_Le mot de passe est vide.", + "Passwords do not match": "ES_Les mot de passes ne correspondent pas." + } } diff --git a/locale/fr.json b/locale/fr.json index af50990..4f3cc62 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -211,6 +211,10 @@ "To receive an email for each new vote": "Recevoir un courriel à chaque participation d'un sondé", "To receive an email for each new comment": "Recevoir un courriel à chaque commentaire", "Only the poll maker can see the poll's results": "Seul le créateur du sondage peut voir les résultats", + "Use a password to restrict access.": "Utiliser un mot de passe pour restreindre l'accès au sondage.", + "The results are publicly visible.": "Les résultats sont visibles sans mot de passe.", + "Poll password": "Mot de passe", + "Confirm password": "Confirmer votre mot de passe.", "Go to step 2": "Aller à l'étape 2" }, "Step 2": { @@ -263,7 +267,6 @@ "Migration": "Migration", "Purge": "Purge", "Logs": "Historique", - "Installation": "Installation", "Poll ID": "ID sondage", "Format": "Format", "Title": "Titre", diff --git a/tpl/create_poll.tpl b/tpl/create_poll.tpl index 8e98899..43b1fcb 100644 --- a/tpl/create_poll.tpl +++ b/tpl/create_poll.tpl @@ -49,7 +49,7 @@
{/if} -
+
@@ -69,7 +69,7 @@ {/if} {if $use_smtp} -
+
- {/if} + {/if}
@@ -144,18 +144,61 @@
- +
+
+
+ +
+
+ +
From b462a6bbdb5af2667bad3cafe41dd77a22a8750d Mon Sep 17 00:00:00 2001 From: Antonin Date: Thu, 9 Apr 2015 18:18:05 +0200 Subject: [PATCH 12/22] Hashing the password early --- app/classes/Framadate/Form.php | 4 ++-- .../Framadate/Repositories/PollRepository.php | 4 ++-- app/classes/Framadate/Services/PollService.php | 12 +----------- create_poll.php | 11 +++++++++-- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/app/classes/Framadate/Form.php b/app/classes/Framadate/Form.php index cd50e6d..07b7868 100644 --- a/app/classes/Framadate/Form.php +++ b/app/classes/Framadate/Form.php @@ -58,10 +58,10 @@ class Form public $use_password; /** - * The password needed to access the poll, if $use_password is set to true + * The password needed to access the poll, hashed. Only used if $use_password is set to true * @var string */ - public $password; + public $password_hash; /** * If true, the polls results will be also visible for those without password diff --git a/app/classes/Framadate/Repositories/PollRepository.php b/app/classes/Framadate/Repositories/PollRepository.php index 3554d66..0f866cd 100644 --- a/app/classes/Framadate/Repositories/PollRepository.php +++ b/app/classes/Framadate/Repositories/PollRepository.php @@ -11,12 +11,12 @@ class PollRepository extends AbstractRepository { parent::__construct($connect); } - public function insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible) { + public function insertPoll($poll_id, $admin_poll_id, $form) { $sql = 'INSERT INTO `' . Utils::table('poll') . '` (id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible) VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?)'; $prepared = $this->prepare($sql); - $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments, $form->hidden, $password_hash, $results_publicly_visible)); + $prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments, $form->hidden, $form->password_hash, $form->results_publicly_visible)); } function findById($poll_id) { diff --git a/app/classes/Framadate/Services/PollService.php b/app/classes/Framadate/Services/PollService.php index 1be93cd..3897791 100644 --- a/app/classes/Framadate/Services/PollService.php +++ b/app/classes/Framadate/Services/PollService.php @@ -22,7 +22,6 @@ use Framadate\Form; use Framadate\FramaDB; use Framadate\Utils; use Framadate\Security\Token; -use Framadate\Security\PasswordHasher; use Framadate\Repositories\RepositoryFactory; class PollService { @@ -120,18 +119,9 @@ class PollService { } while ($this->pollRepository->existsById($poll_id)); $admin_poll_id = $poll_id . $this->random(8); - // Password hash, if needed - if ($form->use_password) { - $password_hash = PasswordHasher::hash($form->password); - $results_publicly_visible = $form->results_publicly_visible; - } else { - $password_hash = null; - $results_publicly_visible = null; - } - // Insert poll + slots $this->pollRepository->beginTransaction(); - $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form, $password_hash, $results_publicly_visible); + $this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form); $this->slotRepository->insertSlots($poll_id, $form->getChoices()); $this->pollRepository->commit(); diff --git a/create_poll.php b/create_poll.php index 3faa240..06b4143 100644 --- a/create_poll.php +++ b/create_poll.php @@ -21,6 +21,7 @@ use Framadate\Form; use Framadate\Services\InputService; use Framadate\Editable; use Framadate\Utils; +use Framadate\Security\PasswordHasher; include_once __DIR__ . '/app/inc/init.php'; @@ -83,7 +84,6 @@ if ($goToStep2) { $_SESSION['form']->receiveNewComments = $receiveNewComments; $_SESSION['form']->hidden = $hidden; $_SESSION['form']->use_password = ($use_password !== null); - $_SESSION['form']->password = $password; $_SESSION['form']->results_publicly_visible = ($results_publicly_visible !== null); @@ -123,6 +123,14 @@ if ($goToStep2) { if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name && !$error_on_password && !$error_on_password_repeat) { + // If no errors, we hash the password if needed + if ($_SESSION['form']->use_password) { + $_SESSION['form']->password_hash = PasswordHasher::hash($password); + } else { + $_SESSION['form']->password_hash = null; + $_SESSION['form']->results_publicly_visible = null; + } + if ($goToStep2 == 'date') { header('Location:create_date_poll.php'); exit(); @@ -244,7 +252,6 @@ $smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewC $smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $_SESSION['form']->hidden)); $smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $_SESSION['form']->use_password)); $smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $_SESSION['form']->results_publicly_visible)); -$smarty->assign('poll_password', Utils::fromPostOrDefault('password', $_SESSION['form']->password)); $smarty->assign('form', $_SESSION['form']); $smarty->display('create_poll.tpl'); From 6cc46b5bae811f73b682c267b3884d62b68bac56 Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 13:43:58 +0100 Subject: [PATCH 13/22] Composer.lock update --- composer.lock | 142 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 48 deletions(-) diff --git a/composer.lock b/composer.lock index 4d7862e..1b2e6af 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,63 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "80025abeaccbfee148a75dd3ff34c19e", + "hash": "41953f68c6dc68f014c6072de985a81d", + "content-hash": "c85ba8e14cce189aef869998ce0e2430", "packages": [ + { + "name": "ircmaxell/password-compat", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "1ecb013b51756effed3a3c446a314084b54c9916" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/1ecb013b51756effed3a3c446a314084b54c9916", + "reference": "1ecb013b51756effed3a3c446a314084b54c9916", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2015-08-11 14:39:38" + }, { "name": "o80/i18n", "version": "dev-develop", "source": { "type": "git", "url": "https://github.com/olivierperez/o80-i18n.git", - "reference": "9b57197b395add3afc62fd674b03926230819b97" + "reference": "8f3fbc7c965559802ed4eda602528a24d641ea15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/9b57197b395add3afc62fd674b03926230819b97", - "reference": "9b57197b395add3afc62fd674b03926230819b97", + "url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/8f3fbc7c965559802ed4eda602528a24d641ea15", + "reference": "8f3fbc7c965559802ed4eda602528a24d641ea15", "shasum": "" }, "require": { @@ -50,20 +93,20 @@ "internationalization", "php" ], - "time": "2015-05-09 22:08:09" + "time": "2015-09-21 21:18:45" }, { "name": "phpmailer/phpmailer", - "version": "v5.2.10", + "version": "v5.2.13", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "07005ecbb80d11ec8c0f067bb37e8909cc7fcbb7" + "reference": "45df3a88f7f46071e10d0b600f228d19f95911b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/07005ecbb80d11ec8c0f067bb37e8909cc7fcbb7", - "reference": "07005ecbb80d11ec8c0f067bb37e8909cc7fcbb7", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/45df3a88f7f46071e10d0b600f228d19f95911b3", + "reference": "45df3a88f7f46071e10d0b600f228d19f95911b3", "shasum": "" }, "require": { @@ -71,12 +114,17 @@ }, "require-dev": { "phpdocumentor/phpdocumentor": "*", - "phpunit/phpunit": "4.3.*" + "phpunit/phpunit": "4.7.*" + }, + "suggest": { + "league/oauth2-client": "Needed for Gmail's XOAUTH2 authentication system" }, "type": "library", "autoload": { "classmap": [ "class.phpmailer.php", + "class.phpmaileroauth.php", + "class.phpmaileroauthgoogle.php", "class.smtp.php", "class.pop3.php", "extras/EasyPeasyICS.php", @@ -105,7 +153,7 @@ } ], "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "time": "2015-05-04 12:37:21" + "time": "2015-09-14 09:18:12" }, { "name": "smarty/smarty", @@ -137,7 +185,7 @@ "libs/sysplugins/smarty_security.php" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "LGPL-3.0" ], @@ -329,16 +377,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.2.2", + "version": "2.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c" + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c", - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", "shasum": "" }, "require": { @@ -387,7 +435,7 @@ "testing", "xunit" ], - "time": "2015-08-04 03:42:39" + "time": "2015-10-06 15:47:00" }, { "name": "phpunit/php-file-iterator", @@ -520,16 +568,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.6", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b" + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3ab72c62e550370a6cd5dc873e1a04ab57562f5b", - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", "shasum": "" }, "require": { @@ -565,20 +613,20 @@ "keywords": [ "tokenizer" ], - "time": "2015-08-16 08:51:00" + "time": "2015-09-15 10:49:45" }, { "name": "phpunit/phpunit", - "version": "4.8.4", + "version": "4.8.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7" + "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", - "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e", + "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e", "shasum": "" }, "require": { @@ -637,24 +685,24 @@ "testing", "xunit" ], - "time": "2015-08-15 04:21:23" + "time": "2015-10-23 06:48:33" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.6", + "version": "2.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42" + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/18dfbcb81d05e2296c0bcddd4db96cade75e6f42", - "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", "shasum": "" }, "require": { - "doctrine/instantiator": "~1.0,>=1.0.2", + "doctrine/instantiator": "^1.0.2", "php": ">=5.3.3", "phpunit/php-text-template": "~1.2", "sebastian/exporter": "~1.2" @@ -693,7 +741,7 @@ "mock", "xunit" ], - "time": "2015-07-10 06:54:24" + "time": "2015-10-02 06:51:40" }, { "name": "sebastian/comparator", @@ -929,16 +977,16 @@ }, { "name": "sebastian/global-state", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", "shasum": "" }, "require": { @@ -976,7 +1024,7 @@ "keywords": [ "global state" ], - "time": "2014-10-06 09:23:50" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", @@ -1068,24 +1116,21 @@ }, { "name": "symfony/yaml", - "version": "v2.7.3", + "version": "v2.7.6", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "71340e996171474a53f3d29111d046be4ad8a0ff" + "url": "https://github.com/symfony/yaml.git", + "reference": "eca9019c88fbe250164affd107bc8057771f3f4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff", - "reference": "71340e996171474a53f3d29111d046be4ad8a0ff", + "url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d", + "reference": "eca9019c88fbe250164affd107bc8057771f3f4d", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, "type": "library", "extra": { "branch-alias": { @@ -1113,13 +1158,14 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-07-28 14:07:07" + "time": "2015-10-11 09:39:48" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "o80/i18n": 20 + "o80/i18n": 20, + "ircmaxell/password-compat": 20 }, "prefer-stable": false, "prefer-lowest": false, From 22253b4e687f6701c9c3cba858c9751f143156aa Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 15:08:54 +0100 Subject: [PATCH 14/22] Cleaning useless code. --- adminstuds.php | 24 ------------------------ studs.php | 23 ----------------------- 2 files changed, 47 deletions(-) diff --git a/adminstuds.php b/adminstuds.php index 7cb20d8..ceb0cdb 100644 --- a/adminstuds.php +++ b/adminstuds.php @@ -234,30 +234,6 @@ if (isset($_POST['confirm_remove_all_votes'])) { } } -// ------------------------------- -// Add a comment -// ------------------------------- - -if (isset($_POST['add_comment'])) { - $name = $inputService->filterName($_POST['name']); - $comment = $inputService->filterComment($_POST['comment']); - - if ($name == null) { - $message = new Message('danger', __('Error', 'The name is invalid.')); - } - - if ($message == null) { - // Add comment - $result = $pollService->addComment($poll_id, $name, $comment); - if ($result) { - $message = new Message('success', __('Comments', 'Comment added')); - } else { - $message = new Message('danger', __('Error', 'Comment failed')); - } - } - -} - // ------------------------------- // Delete a comment // ------------------------------- diff --git a/studs.php b/studs.php index f4306f1..c6c4616 100644 --- a/studs.php +++ b/studs.php @@ -128,29 +128,6 @@ if (!empty($_POST['save'])) { // Save edition of an old vote } } } - -// ------------------------------- -// Add a comment -// ------------------------------- - -if (isset($_POST['add_comment'])) { - $name = $inputService->filterName($_POST['name']); - $comment = $inputService->filterComment($_POST['comment']); - - if ($name == null) { - $message = new Message('danger', __('Error', 'The name is invalid.')); - } - - if ($message == null) { - // Add comment - $result = $pollService->addComment($poll_id, $name, $comment); - if ($result) { - $message = new Message('success', __('Comments', 'Comment added')); - $notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name); - } else { - $message = new Message('danger', __('Error', 'Comment failed')); - } - } } // Retrieve data From bb37d16dffa2f39fe6cebcf91f061c7f5ae80ceb Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 16:36:54 +0100 Subject: [PATCH 15/22] Sugar for comment sending. - Do not send if comment empty - Disable submit button while submit is in progress --- js/app/studs.js | 71 ++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/js/app/studs.js b/js/app/studs.js index bb0c89b..6a4b300 100644 --- a/js/app/studs.js +++ b/js/app/studs.js @@ -49,40 +49,45 @@ $(document).ready(function () { form.submit(function(event) { event.preventDefault(); - $.ajax({ - type: 'POST', - url: form.attr('action'), - data: form.serialize(), - dataType: 'json', - success: function(data) - { - $('#comment').val(''); - - if (data.result) { - $('#comments_list') - .replaceWith(data.comments); - var lastComment = $('#comments_list') - .find('div.comment') - .last(); - lastComment.effect('highlight', {color: 'green'}, 401); - $('html, body').animate({ - scrollTop: lastComment.offset().top - }, 750); - } else { - var newMessage = $('#genericErrorTemplate').clone(); - newMessage - .find('.contents') - .text(data.message.message); - var commentsAlert = $('#comments_alerts'); - commentsAlert - .empty() - .append(newMessage); - $('html, body').animate({ - scrollTop: commentsAlert.offset().top - }, 750); + if ($('#comment').val()) { + $('#add_comment').attr("disabled", "disabled"); + $.ajax({ + type: 'POST', + url: form.attr('action'), + data: form.serialize(), + dataType: 'json', + success: function(data) + { + $('#comment').val(''); + if (data.result) { + $('#comments_list') + .replaceWith(data.comments); + var lastComment = $('#comments_list') + .find('div.comment') + .last(); + lastComment.effect('highlight', {color: 'green'}, 401); + $('html, body').animate({ + scrollTop: lastComment.offset().top + }, 750); + } else { + var newMessage = $('#genericErrorTemplate').clone(); + newMessage + .find('.contents') + .text(data.message.message); + var commentsAlert = $('#comments_alerts'); + commentsAlert + .empty() + .append(newMessage); + $('html, body').animate({ + scrollTop: commentsAlert.offset().top + }, 750); + } + }, + complete: function() { + $('#add_comment').removeAttr("disabled"); } - } - }); + }); + } return false; }); From c6d6ad2393822afdcbfee61c0426ac9cf4d6af18 Mon Sep 17 00:00:00 2001 From: Antonin Date: Wed, 28 Oct 2015 17:30:42 +0100 Subject: [PATCH 16/22] Applying the password restriction in studs. --- action/add_comment.php | 11 +- adminstuds.php | 2 + .../Framadate/Services/SecurityService.php | 42 +++++ css/style.css | 6 + locale/de.json | 7 + locale/en.json | 7 + locale/es.json | 7 + locale/fr.json | 7 + locale/it.json | 7 + studs.php | 161 +++++++++++------- tpl/part/comments.tpl | 15 +- tpl/part/password_request.tpl | 18 ++ tpl/part/vote_table_classic.tpl | 4 +- tpl/part/vote_table_date.tpl | 4 +- tpl/studs.tpl | 87 +++++----- 15 files changed, 271 insertions(+), 114 deletions(-) create mode 100644 tpl/part/password_request.tpl diff --git a/action/add_comment.php b/action/add_comment.php index 4bdd07c..9d537d2 100644 --- a/action/add_comment.php +++ b/action/add_comment.php @@ -21,6 +21,7 @@ use Framadate\Services\PollService; use Framadate\Services\InputService; use Framadate\Services\MailService; use Framadate\Services\NotificationService; +use Framadate\Services\SecurityService; use Framadate\Message; use Framadate\Utils; use Framadate\Editable; @@ -34,6 +35,7 @@ $poll_id = null; $poll = null; $message = null; $result = false; +$comments = array(); /* Services */ /*----------*/ @@ -43,7 +45,7 @@ $pollService = new PollService($connect, $logService); $inputService = new InputService(); $mailService = new MailService($config['use_smtp']); $notificationService = new NotificationService($mailService); - +$securityService = new SecurityService(); /* PAGE */ /* ---- */ @@ -57,7 +59,9 @@ if (!empty($_POST['poll'])) { if (!$poll) { $message = new Message('error', __('Error', 'This poll doesn\'t exist !')); -} else { +} else if ($poll && !$securityService->canAccessPoll($poll)) { + $message = new Message('error', __('Password', 'Wrong password')); +} else if ($poll) { $name = $inputService->filterName($_POST['name']); $comment = $inputService->filterComment($_POST['comment']); @@ -75,10 +79,9 @@ if (!$poll) { $message = new Message('danger', __('Error', 'Comment failed')); } } + $comments = $pollService->allCommentsByPollId($poll_id); } -$comments = $pollService->allCommentsByPollId($poll_id); - $smarty->error_reporting = E_ALL & ~E_NOTICE; $smarty->assign('comments', $comments); $comments_html = $smarty->fetch('part/comments_list.tpl'); diff --git a/adminstuds.php b/adminstuds.php index ceb0cdb..6416bfb 100644 --- a/adminstuds.php +++ b/adminstuds.php @@ -371,5 +371,7 @@ $smarty->assign('editingVoteId', $editingVoteId); $smarty->assign('message', $message); $smarty->assign('admin', true); $smarty->assign('hidden', false); +$smarty->assign('accessGranted', true); +$smarty->assign('resultPubliclyVisible', true); $smarty->display('studs.tpl'); diff --git a/app/classes/Framadate/Services/SecurityService.php b/app/classes/Framadate/Services/SecurityService.php index fc67a72..a4b6f33 100644 --- a/app/classes/Framadate/Services/SecurityService.php +++ b/app/classes/Framadate/Services/SecurityService.php @@ -2,6 +2,7 @@ namespace Framadate\Services; use Framadate\Security\Token; +use Framadate\Security\PasswordHasher; class SecurityService { @@ -48,5 +49,46 @@ class SecurityService { return $checked; } + /** + * Verify if the current session allows to access given poll. + * + * @param $poll \stdClass The poll which we seek access + * @return bool true if the current session can access this poll + */ + public function canAccessPoll($poll) { + if (is_null($poll->password_hash)) { + return true; + } + + $this->ensureSessionPollSecurityIsCreated(); + + $currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null; + if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) { + return true; + } else { + unset($_SESSION['poll_security'][$poll->id]); + return false; + } + } + + /** + * Submit to the session a poll password + * + * @param $poll \stdClass The poll which we seek access + * @param $password string the password to compare + */ + public function submitPollAccess($poll, $password) { + if (!empty($password)) { + $this->ensureSessionPollSecurityIsCreated(); + $_SESSION['poll_security'][$poll->id] = $password; + } + } + + private function ensureSessionPollSecurityIsCreated() { + if (!isset($_SESSION['poll_security'])) { + $_SESSION['poll_security'] = array(); + } + } + } \ No newline at end of file diff --git a/css/style.css b/css/style.css index b88ae29..16e1865 100644 --- a/css/style.css +++ b/css/style.css @@ -488,4 +488,10 @@ table.results > tbody > tr:hover > td .glyphicon { /* Admin */ #poll_search { cursor: pointer; +} + +/* Studs */ +.password_request { + padding-top: 15px; + padding-bottom: 15px; } \ No newline at end of file diff --git a/locale/de.json b/locale/de.json index 3390065..4528f34 100644 --- a/locale/de.json +++ b/locale/de.json @@ -156,6 +156,13 @@ "anonyme": "anonym", "Comment added": "Kommentar hinzugefügt" }, + "Password": { + "Password": "DE_Mot de passe", + "Wrong password": "DE_Mot de passe incorrect.", + "Submit access": "DE_Accèder", + "You have to provide a password to access the poll.": "DE_Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "DE_Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Wenn Sie an dieser Umfrage teilnehmen möchten, müssen Sie ihren Namen angeben, die Auswahl treffen, die Ihnen am ehesten zusagt und mit dem Plus-Button am Ende der Zeile bestätigen.", "POLL_LOCKED_WARNING": "Die Abstimmung wurde vom Administrator gesperrt. Bewertungen und Kommentare werden eingefroren; es ist nicht mehr möglich, teilzunehmen", diff --git a/locale/en.json b/locale/en.json index 8b004d6..67af1dd 100644 --- a/locale/en.json +++ b/locale/en.json @@ -156,6 +156,13 @@ "anonyme": "anonyme", "Comment added": "Comment saved" }, + "Password": { + "Password": "Password", + "Wrong password": "Wrong password", + "Submit access": "Submit access", + "You have to provide a password to access the poll.": "You have to provide a password to access the poll.", + "You have to provide a password so you can participate to the poll.": "You have to provide a password so you can participate to the poll." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "If you want to vote in this poll, you have to give your name, make your choice, and submit it with the plus button at the end of the line.", "POLL_LOCKED_WARNING": "The administrator locked this poll. Votes and comments are frozen, it is no longer possible to participate", diff --git a/locale/es.json b/locale/es.json index 51a6991..5350b3f 100644 --- a/locale/es.json +++ b/locale/es.json @@ -156,6 +156,13 @@ "anonyme": "ES_anonyme", "Comment added": "ES_Commentaire ajouté" }, + "Password": { + "Password": "ES_Mot de passe", + "Wrong password": "ES_Mot de passe incorrect.", + "Submit access": "ES_Accèder", + "You have to provide a password to access the poll.": "ES_Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "ES_Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Para participar a esta encuesta, introduzca su nombre, elige todas las valores que son apriopriadas y validar su seleccion con el botón verde a la fin de línea.", "POLL_LOCKED_WARNING": "ES_L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer", diff --git a/locale/fr.json b/locale/fr.json index 4f3cc62..d76e66e 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -156,6 +156,13 @@ "anonyme": "anonyme", "Comment added": "Commentaire ajouté" }, + "Password": { + "Password": "Mot de passe", + "Wrong password": "Mot de passe incorrect.", + "Submit access": "Accèder", + "You have to provide a password to access the poll.": "Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Pour participer à ce sondage, veuillez entrer votre nom, choisir toutes les valeurs qui vous conviennent et valider votre choix avec le bouton en bout de ligne.", "POLL_LOCKED_WARNING": "L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer", diff --git a/locale/it.json b/locale/it.json index 0ca5008..81759f0 100644 --- a/locale/it.json +++ b/locale/it.json @@ -156,6 +156,13 @@ "anonyme": "anonimo", "Comment added": "Commento aggiunto" }, + "Password": { + "Password": "IT_Mot de passe", + "Wrong password": "IT_Mot de passe incorrect.", + "Submit access": "IT_Accèder", + "You have to provide a password to access the poll.": "IT_Vous devez donner le mot de passe pour avoir accès à ce sondage.", + "You have to provide a password so you can participate to the poll.": "IT_Vous devez donner le mot de passe pour pouvoir participer à ce sondage." + }, "studs": { "If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Per partecipare a questo sondaggio, è necessario inserire vostro nome, scegliere tutti i parametri che preferite e confermare la vostra scelta con il pulsante alla fine della riga.", "POLL_LOCKED_WARNING": "L'amministratore ha bloccato questa indagine. Voti e commenti sono congelati, non è più possibile partecipare", diff --git a/studs.php b/studs.php index c6c4616..d43ff0c 100644 --- a/studs.php +++ b/studs.php @@ -21,6 +21,7 @@ use Framadate\Services\PollService; use Framadate\Services\InputService; use Framadate\Services\MailService; use Framadate\Services\NotificationService; +use Framadate\Services\SecurityService; use Framadate\Message; use Framadate\Utils; use Framadate\Editable; @@ -34,6 +35,11 @@ $poll_id = null; $poll = null; $message = null; $editingVoteId = 0; +$accessGranted = true; +$resultPubliclyVisible = true; +$slots = array(); +$votes = array(); +$comments = array(); /* Services */ /*----------*/ @@ -43,6 +49,7 @@ $pollService = new PollService($connect, $logService); $inputService = new InputService(); $mailService = new MailService($config['use_smtp']); $notificationService = new NotificationService($mailService); +$securityService = new SecurityService(); /* PAGE */ @@ -62,78 +69,114 @@ if (!$poll) { } // ------------------------------- -// A vote is going to be edited +// Password verification // ------------------------------- -if (!empty($_GET['vote'])) { - $editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); +// TEMP, for testing purpose +if (isset($_GET['reset']) && $_GET['reset']) { + unset($_SESSION['poll_security']); } -// ------------------------------- -// Something to save (edit or add) -// ------------------------------- +if (!is_null($poll->password_hash)) { -if (!empty($_POST['save'])) { // Save edition of an old vote - $name = $inputService->filterName($_POST['name']); - $editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT); - $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); - - if (empty($editedVote)) { - $message = new Message('danger', __('Error', 'Something is going wrong...')); - } - if (count($choices) != count($_POST['choices'])) { - $message = new Message('danger', __('Error', 'There is a problem with your choices')); + // If we came from password submission + $password = isset($_POST['password']) ? $_POST['password'] : null; + if (!empty($password)) { + $securityService->submitPollAccess($poll, $password); } - if ($message == null) { - // Update vote - $result = $pollService->updateVote($poll_id, $editedVote, $name, $choices); - if ($result) { - if ($poll->editable == Editable::EDITABLE_BY_OWN) { - $editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); - $urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId); - $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); - } else { - $message = new Message('success', __('studs', 'Update vote succeeded')); - } - $notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name); - } else { - $message = new Message('danger', __('Error', 'Update vote failed')); - } + if (!$securityService->canAccessPoll($poll)) { + $accessGranted = false; } -} elseif (isset($_POST['save'])) { // Add a new vote - $name = $inputService->filterName($_POST['name']); - $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); + $resultPubliclyVisible = $poll->results_publicly_visible; - if ($name == null) { - $message = new Message('danger', __('Error', 'The name is invalid.')); - } - if (count($choices) != count($_POST['choices'])) { - $message = new Message('danger', __('Error', 'There is a problem with your choices')); - } - - if ($message == null) { - // Add vote - $result = $pollService->addVote($poll_id, $name, $choices); - if ($result) { - if ($poll->editable == Editable::EDITABLE_BY_OWN) { - $urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId); - $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); - } else { - $message = new Message('success', __('studs', 'Adding the vote succeeded')); - } - $notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name); - } else { - $message = new Message('danger', __('Error', 'Adding vote failed')); - } + if (!$accessGranted && !empty($password)) { + $message = new Message('danger', __('Password', 'Wrong password')); + } else if (!$accessGranted && !$resultPubliclyVisible) { + $message = new Message('danger', __('Password', 'You have to provide a password to access the poll.')); + } else if (!$accessGranted && $resultPubliclyVisible) { + $message = new Message('danger', __('Password', 'You have to provide a password so you can participate to the poll.')); } } + +// We allow actions only if access is granted +if ($accessGranted) { + + // ------------------------------- + // A vote is going to be edited + // ------------------------------- + + if (!empty($_GET['vote'])) { + $editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); + } + + // ------------------------------- + // Something to save (edit or add) + // ------------------------------- + + if (!empty($_POST['save'])) { // Save edition of an old vote + $name = $inputService->filterName($_POST['name']); + $editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT); + $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); + + if (empty($editedVote)) { + $message = new Message('danger', __('Error', 'Something is going wrong...')); + } + if (count($choices) != count($_POST['choices'])) { + $message = new Message('danger', __('Error', 'There is a problem with your choices')); + } + + if ($message == null) { + // Update vote + $result = $pollService->updateVote($poll_id, $editedVote, $name, $choices); + if ($result) { + if ($poll->editable == Editable::EDITABLE_BY_OWN) { + $editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); + $urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId); + $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); + } else { + $message = new Message('success', __('studs', 'Update vote succeeded')); + } + $notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name); + } else { + $message = new Message('danger', __('Error', 'Update vote failed')); + } + } + } elseif (isset($_POST['save'])) { // Add a new vote + $name = $inputService->filterName($_POST['name']); + $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); + + if ($name == null) { + $message = new Message('danger', __('Error', 'The name is invalid.')); + } + if (count($choices) != count($_POST['choices'])) { + $message = new Message('danger', __('Error', 'There is a problem with your choices')); + } + + if ($message == null) { + // Add vote + $result = $pollService->addVote($poll_id, $name, $choices); + if ($result) { + if ($poll->editable == Editable::EDITABLE_BY_OWN) { + $urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId); + $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); + } else { + $message = new Message('success', __('studs', 'Adding the vote succeeded')); + } + $notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name); + } else { + $message = new Message('danger', __('Error', 'Adding vote failed')); + } + } + } } // Retrieve data -$slots = $pollService->allSlotsByPoll($poll); -$votes = $pollService->allVotesByPollId($poll_id); -$comments = $pollService->allCommentsByPollId($poll_id); +if ($resultPubliclyVisible) { + $slots = $pollService->allSlotsByPoll($poll); + $votes = $pollService->allVotesByPollId($poll_id); + $comments = $pollService->allCommentsByPollId($poll_id); +} // Assign data to template $smarty->assign('poll_id', $poll_id); @@ -149,5 +192,7 @@ $smarty->assign('editingVoteId', $editingVoteId); $smarty->assign('message', $message); $smarty->assign('admin', false); $smarty->assign('hidden', $poll->hidden); +$smarty->assign('accessGranted', $accessGranted); +$smarty->assign('resultPubliclyVisible', $resultPubliclyVisible); $smarty->display('studs.tpl'); diff --git a/tpl/part/comments.tpl b/tpl/part/comments.tpl index 7a3ad4a..b9c0fb4 100644 --- a/tpl/part/comments.tpl +++ b/tpl/part/comments.tpl @@ -3,12 +3,11 @@ {* Comment list *} {include 'part/comments_list.tpl'} -
+{* Add comment form *} +{if $active && !$expired && $accessGranted} + - - - {* Add comment form *} - {if $active && !$expired} +
{__('Comments', 'Add a comment to the poll')} @@ -21,11 +20,11 @@
- +
- {/if} - \ No newline at end of file + +{/if} \ No newline at end of file diff --git a/tpl/part/password_request.tpl b/tpl/part/password_request.tpl new file mode 100644 index 0000000..aff6fe3 --- /dev/null +++ b/tpl/part/password_request.tpl @@ -0,0 +1,18 @@ +{if !$expired && ($active || $resultPubliclyVisible)} + + +
+
+
+ +
+ + + +
+
+
+
+
+{/if} + diff --git a/tpl/part/vote_table_classic.tpl b/tpl/part/vote_table_classic.tpl index bd092ef..e5e5070 100644 --- a/tpl/part/vote_table_classic.tpl +++ b/tpl/part/vote_table_classic.tpl @@ -97,7 +97,7 @@ {/foreach} - {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin)} + {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted} {__('Generic', 'Edit')} @@ -119,7 +119,7 @@ {* Line to add a new vote *} - {if $active && $editingVoteId === 0 && !$expired} + {if $active && $editingVoteId === 0 && !$expired && $accessGranted}
diff --git a/tpl/part/vote_table_date.tpl b/tpl/part/vote_table_date.tpl index a072a63..8ff1a04 100644 --- a/tpl/part/vote_table_date.tpl +++ b/tpl/part/vote_table_date.tpl @@ -148,7 +148,7 @@ {/foreach} - {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin)} + {if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted} {__('Generic', 'Edit')} @@ -170,7 +170,7 @@ {* Line to add a new vote *} - {if $active && $editingVoteId === 0 && !$expired} + {if $active && $editingVoteId === 0 && !$expired && $accessGranted}
diff --git a/tpl/studs.tpl b/tpl/studs.tpl index e5d1113..7e873ab 100644 --- a/tpl/studs.tpl +++ b/tpl/studs.tpl @@ -12,6 +12,7 @@ {block name=main} + {* Messages *}
{if !empty($message)} @@ -20,49 +21,55 @@ + {if !$accessGranted && !$resultPubliclyVisible} - {* Global informations about the current poll *} + {include 'part/password_request.tpl' active=$poll->active} -{include 'part/poll_info.tpl' admin=$admin} - -{* Information about voting *} -{if $expired} -
-

{__('studs', 'The poll is expired, it will be deleted soon.')}

-

{__('studs', 'Deletion date:')} {$deletion_date|date_format:$date_format['txt_short']|html}

-
-{else} - {if $admin} - {include 'part/poll_hint_admin.tpl'} {else} - {include 'part/poll_hint.tpl' active=$poll->active} + + {* Global informations about the current poll *} + {include 'part/poll_info.tpl' admin=$admin} + + {* Information about voting *} + {if $expired} +
+

{__('studs', 'The poll is expired, it will be deleted soon.')}

+

{__('studs', 'Deletion date:')} {$deletion_date|date_format:$date_format['txt_short']|html}

+
+ {else} + {if $admin} + {include 'part/poll_hint_admin.tpl'} + {else} + {include 'part/poll_hint.tpl' active=$poll->active} + {/if} + {/if} + + {* Scroll left and right *} + + + {if !$accessGranted && $resultPubliclyVisible} + {include 'part/password_request.tpl' active=$poll->active} + {/if} + + {* Vote table *} + {if $poll->format === 'D'} + {include 'part/vote_table_date.tpl' active=$poll->active} + {else} + {include 'part/vote_table_classic.tpl' active=$poll->active} + {/if} + + {* Comments *} + {include 'part/comments.tpl' active=$poll->active comments=$comments} + {/if} -{/if} - -{* Scroll left and right *} - - - - -{* Vote table *} - -{if $poll->format === 'D'} - {include 'part/vote_table_date.tpl' active=$poll->active} -{else} - {include 'part/vote_table_classic.tpl' active=$poll->active} -{/if} - -{* Comments *} - -{include 'part/comments.tpl' active=$poll->active comments=$comments} {/block} From 15c57eeb377e0d246b3fd4bf24edee8031503853 Mon Sep 17 00:00:00 2001 From: Antonin Date: Fri, 30 Oct 2015 20:45:18 +0100 Subject: [PATCH 17/22] Allows the admin to modify password related stuff. --- adminstuds.php | 23 +++++++++++- .../Framadate/Repositories/PollRepository.php | 4 +- css/style.css | 7 ++++ js/core.js | 29 +++++++++++++++ locale/de.json | 6 ++- locale/en.json | 6 ++- locale/es.json | 6 ++- locale/fr.json | 6 ++- locale/it.json | 6 ++- tpl/part/poll_info.tpl | 37 ++++++++++++++++++- tpl/part/vote_table_classic.tpl | 4 +- tpl/part/vote_table_date.tpl | 4 +- 12 files changed, 126 insertions(+), 12 deletions(-) diff --git a/adminstuds.php b/adminstuds.php index 6416bfb..ca920dd 100644 --- a/adminstuds.php +++ b/adminstuds.php @@ -24,7 +24,7 @@ use Framadate\Services\LogService; use Framadate\Services\MailService; use Framadate\Services\PollService; use Framadate\Services\NotificationService; -use Framadate\Utils; +use Framadate\Security\PasswordHasher; include_once __DIR__ . '/app/inc/init.php'; @@ -71,7 +71,8 @@ if ($poll) { if (isset($_POST['update_poll_info'])) { $updated = false; - $field = $inputService->filterAllowedValues($_POST['update_poll_info'], ['title', 'admin_mail', 'description', 'rules', 'expiration_date', 'name', 'hidden']); + $field = $inputService->filterAllowedValues($_POST['update_poll_info'], ['title', 'admin_mail', 'description', + 'rules', 'expiration_date', 'name', 'hidden', 'removePassword', 'password']); // Update the right poll field if ($field == 'title') { @@ -135,6 +136,24 @@ if (isset($_POST['update_poll_info'])) { $poll->hidden = $hidden; $updated = true; } + } elseif ($field == 'removePassword') { + $removePassword = isset($_POST['removePassword']) ? $inputService->filterBoolean($_POST['removePassword']) : false; + if ($removePassword) { + $poll->results_publicly_visible = false; + $poll->password_hash = null; + $updated = true; + } + } elseif ($field == 'password') { + $password = isset($_POST['password']) ? $_POST['password'] : null; + $resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) ? $inputService->filterBoolean($_POST['resultsPubliclyVisible']) : false; + if (!empty($password)) { + $poll->password_hash = PasswordHasher::hash($password); + $updated = true; + } + if ($resultsPubliclyVisible != $poll->results_publicly_visible) { + $poll->results_publicly_visible = $resultsPubliclyVisible; + $updated = true; + } } // Update poll in database diff --git a/app/classes/Framadate/Repositories/PollRepository.php b/app/classes/Framadate/Repositories/PollRepository.php index 0f866cd..fd62e55 100644 --- a/app/classes/Framadate/Repositories/PollRepository.php +++ b/app/classes/Framadate/Repositories/PollRepository.php @@ -48,9 +48,9 @@ class PollRepository extends AbstractRepository { } function update($poll) { - $prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=? WHERE id = ?'); + $prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=?, password_hash=?, results_publicly_visible=? WHERE id = ?'); - return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active, $poll->editable, $poll->hidden, $poll->id]); + return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active, $poll->editable, $poll->hidden, $poll->password_hash, $poll->results_publicly_visible, $poll->id]); } function deleteById($poll_id) { diff --git a/css/style.css b/css/style.css index 16e1865..cb2d524 100644 --- a/css/style.css +++ b/css/style.css @@ -167,6 +167,7 @@ caption { #poll-rules-form .btn-edit, #poll-hidden-form .btn-edit, #expiration-form .btn-edit, +#password-form .btn-edit, #name-form .btn-edit { position:absolute; left:-2000px; @@ -184,6 +185,8 @@ caption { #poll-hidden-form:hover .btn-edit, #expiration-form .btn-edit:focus, #expiration-form:hover .btn-edit, +#password-form .btn-edit:focus, +#password-form:hover .btn-edit, #name-form .btn-edit:focus, #name-form:hover .btn-edit { position:relative !important; @@ -494,4 +497,8 @@ table.results > tbody > tr:hover > td .glyphicon { .password_request { padding-top: 15px; padding-bottom: 15px; +} + +#password-form .btn-cancel { + float: right; } \ No newline at end of file diff --git a/js/core.js b/js/core.js index b05e962..70d9c84 100644 --- a/js/core.js +++ b/js/core.js @@ -104,6 +104,35 @@ $(document).ready(function() { return false; }); + + $('#password-form .btn-edit').on('click', function() { + $('#password-form p').hide(); + $('#password-form .js-password').removeClass('hidden'); + $('#password').focus(); + return false; + }); + + $('#password-form .btn-cancel').on('click', function() { + $('#password-form p').show(); + $('#password-form .js-password').addClass('hidden'); + $('.js-password .btn-edit').focus(); + return false; + }); + + // Hiding other field when the admin wants to remove the password protection + var removePassword = $('#removePassword'); + removePassword.on('click', function() { + var removeButton = removePassword.siblings('button'); + if (removePassword.is(":checked")) { + $('#password_information').addClass('hidden'); + removeButton.removeClass('hidden'); + } else { + $('#password_information').removeClass('hidden'); + removeButton.addClass('hidden'); + } + removeButton.focus(); + }); + // Horizontal scroll buttons if($('.results').width() > $('.container').width()) { $('.scroll-buttons').removeClass('hidden'); diff --git a/locale/de.json b/locale/de.json index 4528f34..682f839 100644 --- a/locale/de.json +++ b/locale/de.json @@ -125,7 +125,11 @@ "Save the new rules": "Neue Regeln speichern", "Cancel the rules edit": "Abbruch, Regeln nicht ändern", "Results are hidden.": "Ergebnisse werden ausgeblendet.", - "Results are visible.": "Ergebnisse sind sichtbar." + "Results are visible.": "Ergebnisse sind sichtbar.", + "Password protected.": "DE_Protégé par mot de passe.", + "Votes protected by password.": "DE_Votes protégés par mot de passe.", + "No password.": "DE_Pas de mot de passe", + "Remove password.": "DE_Supprimer le mot de passe." }, "Poll results": { "Votes of the poll": "Stimmabgaben zur Umfrage", diff --git a/locale/en.json b/locale/en.json index 67af1dd..90c103b 100644 --- a/locale/en.json +++ b/locale/en.json @@ -125,7 +125,11 @@ "Save the new rules": "Save the new rules", "Cancel the rules edit": "Cancel the rules edit", "Results are hidden.": "Results are hidden.", - "Results are visible.": "Results are visible." + "Results are visible.": "Results are visible.", + "Password protected.": "Password protected.", + "Votes protected by password.": "Votes protected by password.", + "No password.": "No password.", + "Remove password.": "Remove password." }, "Poll results": { "Votes of the poll": "Votes", diff --git a/locale/es.json b/locale/es.json index 5350b3f..08e06a3 100644 --- a/locale/es.json +++ b/locale/es.json @@ -125,7 +125,11 @@ "Save the new rules": "ES_Enregistrer les nouvelles permissions", "Cancel the rules edit": "ES_Annuler le changement de permissions", "Results are hidden.": "ES_Les résultats sont cachés.", - "Results are visible.": "ES_Les résultats sont visibles." + "Results are visible.": "ES_Les résultats sont visibles.", + "Password protected.": "ES_Protégé par mot de passe.", + "Votes protected by password.": "ES_Votes protégés par mot de passe.", + "No password.": "ES_Pas de mot de passe", + "Remove password.": "ES_Supprimer le mot de passe." }, "Poll results": { "Votes of the poll": "ES_Votes du sondage", diff --git a/locale/fr.json b/locale/fr.json index d76e66e..e66a8b9 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -125,7 +125,11 @@ "Save the new rules": "Enregistrer les nouvelles permissions", "Cancel the rules edit": "Annuler le changement de permissions", "Results are hidden.": "Les résultats sont cachés.", - "Results are visible.": "Les résultats sont visibles." + "Results are visible.": "Les résultats sont visibles.", + "Password protected.": "Protégé par mot de passe.", + "Votes protected by password.": "Votes protégés par mot de passe.", + "No password.": "Pas de mot de passe", + "Remove password.": "Supprimer le mot de passe." }, "Poll results": { "Votes of the poll": "Votes du sondage", diff --git a/locale/it.json b/locale/it.json index 81759f0..31db272 100644 --- a/locale/it.json +++ b/locale/it.json @@ -125,7 +125,11 @@ "Save the new rules": "Salvare i nuovi permessi", "Cancel the rules edit": "Annullare le modifica dei permessi", "Results are hidden.": "I risultati sono nascosti.", - "Results are visible.": "I risultati sono visibili." + "Results are visible.": "I risultati sono visibili.", + "Password protected.": "IT_Protégé par mot de passe.", + "Votes protected by password.": "IT_Votes protégés par mot de passe.", + "No password.": "IT_Pas de mot de passe", + "Remove password.": "IT_Supprimer le mot de passe." }, "Poll results": { "Votes of the poll": "Voti del sondaggio ", diff --git a/tpl/part/poll_info.tpl b/tpl/part/poll_info.tpl index 9c0f7f6..ec77e4e 100644 --- a/tpl/part/poll_info.tpl +++ b/tpl/part/poll_info.tpl @@ -120,7 +120,42 @@
{if $admin}
-
+
+
+ {if !empty($poll->password_hash) && !$poll->results_publicly_visible} + {$password_text = __('PollInfo', 'Password protected.')} + {elseif !empty($poll->password_hash) && $poll->results_publicly_visible} + {$password_text = __('PollInfo', 'Votes protected by password.')} + {else} + {$password_text = __('PollInfo', 'No password.')} + {/if} +

{$password_text}

+ +
+
+ +
{if $poll->hidden} {$hidden_icon = "glyphicon-eye-close"} diff --git a/tpl/part/vote_table_classic.tpl b/tpl/part/vote_table_classic.tpl index e5e5070..660dc10 100644 --- a/tpl/part/vote_table_classic.tpl +++ b/tpl/part/vote_table_classic.tpl @@ -4,7 +4,9 @@

{__('Poll results', 'Votes of the poll')} {if $hidden}({__('PollInfo', 'Results are hidden.')}){/if} - + {if $accessGranted} + + {/if}

diff --git a/tpl/part/vote_table_date.tpl b/tpl/part/vote_table_date.tpl index 8ff1a04..975d42b 100644 --- a/tpl/part/vote_table_date.tpl +++ b/tpl/part/vote_table_date.tpl @@ -4,7 +4,9 @@

{__('Poll results', 'Votes of the poll')} {if $hidden}({__('PollInfo', 'Results are hidden.')}){/if} - + {if $accessGranted} + + {/if}

From 3eae4d97d4e1ebfd63fd9850455f85a11b35d9ad Mon Sep 17 00:00:00 2001 From: Antonin Date: Fri, 30 Oct 2015 21:44:43 +0100 Subject: [PATCH 18/22] Local improvement (remove useless dots) - French errors messages were missing due to bad merge. --- locale/de.json | 8 ++++---- locale/en.json | 6 +++--- locale/es.json | 6 +++--- locale/fr.json | 10 ++++++---- locale/it.json | 4 ++++ tpl/create_poll.tpl | 4 ++-- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/locale/de.json b/locale/de.json index 682f839..bc2ba23 100644 --- a/locale/de.json +++ b/locale/de.json @@ -221,11 +221,11 @@ "Voters can modify their vote themselves": "Teilnehmer können ihre Wertungen verändern", "To receive an email for each new vote": "Bei jeder neuen Wertung eine E-Mail erhalten", "To receive an email for each new comment": "Bei jedem neuen Kommentar eine E-Mail erhalten", - "Only the poll maker can see the poll's results": "Einzig der Autor der Abstimmung kann die Ergebnisse einsehen.", - "Use a password to restrict access.": "DE_Utiliser un mot de passe pour restreindre l'accès au sondage.", - "The results are publicly visible.": "DE_Les résultats sont visibles sans mot de passe.", + "Only the poll maker can see the poll's results": "Einzig der Autor der Abstimmung kann die Ergebnisse einsehen", + "Use a password to restrict access": "DE_Utiliser un mot de passe pour restreindre l'accès au sondage", + "The results are publicly visible": "DE_Les résultats sont visibles sans mot de passe", "Poll password": "DE_Mot de passe", - "Confirm password": "DE_Confirmer votre mot de passe.", + "Confirm password": "DE_Confirmer votre mot de passe", "Go to step 2": "Weiter zum 2. Schritt" }, "Step 2": { diff --git a/locale/en.json b/locale/en.json index 90c103b..3ed4ad9 100644 --- a/locale/en.json +++ b/locale/en.json @@ -222,10 +222,10 @@ "To receive an email for each new vote": "Receive an email for each new vote", "To receive an email for each new comment": "Receive an email for each new comment", "Only the poll maker can see the poll's results": "Only the poll maker can see the poll results", - "Use a password to restrict access.": "Use a password to restrict access.", - "The results are publicly visible.": "The results are publicly visible.", + "Use a password to restrict access": "Use a password to restrict access", + "The results are publicly visible": "The results are publicly visible", "Poll password": "Password", - "Confirm password": "Confirmer votre mot de passe.", + "Confirm password": "Confirmer votre mot de passe", "Go to step 2": "Go to step 2" }, "Step 2": { diff --git a/locale/es.json b/locale/es.json index 08e06a3..a12a988 100644 --- a/locale/es.json +++ b/locale/es.json @@ -222,10 +222,10 @@ "To receive an email for each new vote": "Usted quiere recibir un correo electónico cada vez que alguien participe a la encuesta", "To receive an email for each new comment": "ES_Recevoir un courriel à chaque commentaire", "Only the poll maker can see the poll's results": "ES_Seul le créateur du sondage peut voir les résultats", - "Use a password to restrict access.": "ES_Utiliser un mot de passe pour restreindre l'accès au sondage.", - "The results are publicly visible.": "ES_Les résultats sont visibles sans mot de passe.", + "Use a password to restrict access": "ES_Utiliser un mot de passe pour restreindre l'accès au sondage", + "The results are publicly visible": "ES_Les résultats sont visibles sans mot de passe", "Poll password": "ES_Mot de passe", - "Confirm password": "ES_Confirmer votre mot de passe.", + "Confirm password": "ES_Confirmer votre mot de passe", "Go to step 2": "ES_Aller à l'étape 2" }, "Step 2": { diff --git a/locale/fr.json b/locale/fr.json index e66a8b9..40d9349 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -222,10 +222,10 @@ "To receive an email for each new vote": "Recevoir un courriel à chaque participation d'un sondé", "To receive an email for each new comment": "Recevoir un courriel à chaque commentaire", "Only the poll maker can see the poll's results": "Seul le créateur du sondage peut voir les résultats", - "Use a password to restrict access.": "Utiliser un mot de passe pour restreindre l'accès au sondage.", - "The results are publicly visible.": "Les résultats sont visibles sans mot de passe.", + "Use a password to restrict access": "Utiliser un mot de passe pour restreindre l'accès au sondage", + "The results are publicly visible": "Les résultats sont visibles sans mot de passe", "Poll password": "Mot de passe", - "Confirm password": "Confirmer votre mot de passe.", + "Confirm password": "Confirmer votre mot de passe ", "Go to step 2": "Aller à l'étape 2" }, "Step 2": { @@ -367,6 +367,8 @@ "You can't create a poll with hidden results with the following edition option:": "Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ", "Failed to delete column": "Échec de la suppression de colonne", "MISSING_VALUES": "Il manque des valeurs", - "CANT_CONNECT_TO_DATABASE": "Impossible de se connecter à la base de données" + "CANT_CONNECT_TO_DATABASE": "Impossible de se connecter à la base de données", + "Password is empty": "Le mot de passe est vide.", + "Passwords do not match": "Les mots de passe ne correspondent pas." } } diff --git a/locale/it.json b/locale/it.json index 31db272..d08a062 100644 --- a/locale/it.json +++ b/locale/it.json @@ -222,6 +222,10 @@ "To receive an email for each new vote": "Per ricevere un'email per ogni voto nuovo", "To receive an email for each new comment": "Ricevi una e-mail ogni commento", "Only the poll maker can see the poll's results": "Solo il creatore sondaggio possono vedere i risultati", + "Use a password to restrict access": "ES_Utiliser un mot de passe pour restreindre l'accès au sondage", + "The results are publicly visible": "ES_Les résultats sont visibles sans mot de passe", + "Poll password": "ES_Mot de passe", + "Confirm password": "ES_Confirmer votre mot de passe", "Go to step 2": "Andare al punto 2" }, "Step 2": { diff --git a/tpl/create_poll.tpl b/tpl/create_poll.tpl index 43b1fcb..c475317 100644 --- a/tpl/create_poll.tpl +++ b/tpl/create_poll.tpl @@ -161,7 +161,7 @@
@@ -193,7 +193,7 @@
From 1376426f27b352e355ccaf3e715fa9cb35e7e8d3 Mon Sep 17 00:00:00 2001 From: Antonin Date: Fri, 30 Oct 2015 21:45:52 +0100 Subject: [PATCH 19/22] Useless else if removed --- action/add_comment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action/add_comment.php b/action/add_comment.php index 9d537d2..85453e7 100644 --- a/action/add_comment.php +++ b/action/add_comment.php @@ -61,7 +61,7 @@ if (!$poll) { $message = new Message('error', __('Error', 'This poll doesn\'t exist !')); } else if ($poll && !$securityService->canAccessPoll($poll)) { $message = new Message('error', __('Password', 'Wrong password')); -} else if ($poll) { +} else { $name = $inputService->filterName($_POST['name']); $comment = $inputService->filterComment($_POST['comment']); From 7b05eeec44487b97245e8b03c6aef04ed61bc79f Mon Sep 17 00:00:00 2001 From: Antonin Date: Fri, 30 Oct 2015 21:46:52 +0100 Subject: [PATCH 20/22] Remove dev tests --- studs.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/studs.php b/studs.php index d43ff0c..793ff01 100644 --- a/studs.php +++ b/studs.php @@ -72,11 +72,6 @@ if (!$poll) { // Password verification // ------------------------------- -// TEMP, for testing purpose -if (isset($_GET['reset']) && $_GET['reset']) { - unset($_SESSION['poll_security']); -} - if (!is_null($poll->password_hash)) { // If we came from password submission From 91de1661bbdf9843b638d3a689df16cf5b507e04 Mon Sep 17 00:00:00 2001 From: Antonin Date: Fri, 30 Oct 2015 21:50:44 +0100 Subject: [PATCH 21/22] Inlining a change event --- js/app/create_poll.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/app/create_poll.js b/js/app/create_poll.js index 4a77ab5..ebd8744 100644 --- a/js/app/create_poll.js +++ b/js/app/create_poll.js @@ -36,9 +36,8 @@ $(document).ready(function () { /** * Hide/Show password options */ - var usePassword = $("#use_password"); - usePassword.change(function(){ - if (usePassword.prop("checked")) { + $("#use_password").change(function(){ + if ($(this).prop("checked")) { $("#password_options").removeClass("hidden"); } else { $("#password_options").addClass("hidden"); From d72c5295ce2d7d5b2419b89306aa64936dc934bd Mon Sep 17 00:00:00 2001 From: Antonin Date: Fri, 30 Oct 2015 21:53:04 +0100 Subject: [PATCH 22/22] useless spaces --- tpl/create_poll.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpl/create_poll.tpl b/tpl/create_poll.tpl index c475317..8ab82d1 100644 --- a/tpl/create_poll.tpl +++ b/tpl/create_poll.tpl @@ -136,7 +136,7 @@
- {/if} + {/if}