diff --git a/classes/xorgsession.php b/classes/xorgsession.php index 5256c7995..939adbb55 100644 --- a/classes/xorgsession.php +++ b/classes/xorgsession.php @@ -208,6 +208,22 @@ protected function startSessionAs($user, $level) return true; } + /** + * Directly start a session as the given user, without authentication + * TODO describe "auth by email"... + */ + public function logAsUser(User $user) + { + // Kill any current session + $this->destroy(); + + // It is like the user has authenticated with a cookie + S::set('auth', AUTH_COOKIE); + + // Enter the session + return $this->startSessionAs($user, AUTH_COOKIE); + } + private function securityChecks() { $mail_subject = array(); diff --git a/modules/xnetevents.php b/modules/xnetevents.php index 6988ae722..277f7b401 100644 --- a/modules/xnetevents.php +++ b/modules/xnetevents.php @@ -21,13 +21,22 @@ define('NB_PER_PAGE', 25); +//enum describing who can access the event +abstract class AccessControl +{ + //use same values as the corresponding mysql enum + const Group = "group"; + const Registered = "registered"; + const All = "all"; +} + class XnetEventsModule extends PLModule { function handlers() { return array( '%grp/events' => $this->make_hook('events', AUTH_PASSWD, 'groups'), - '%grp/events/sub' => $this->make_hook('sub', AUTH_PASSWD, 'groups'), + '%grp/events/sub' => $this->make_hook('sub', AUTH_PUBLIC, 'groups'), '%grp/events/csv' => $this->make_hook('csv', AUTH_PASSWD, 'groups', NO_HTTPS), '%grp/events/ical' => $this->make_hook('ical', AUTH_PASSWD, 'groups', NO_HTTPS), '%grp/events/edit' => $this->make_hook('edit', AUTH_PASSWD, 'groupadmin'), @@ -160,7 +169,7 @@ function handler_events($page, $archive = null) $undisplayed_events = 0; foreach ($evts as $eid => &$e) { - if (!is_member() && !may_update() && !$e['accept_nonmembre']) { + if (!is_member() && !may_update() && $e['access_control']==AccessControl::Group) { $undisplayed_events ++; continue; } @@ -203,10 +212,13 @@ function handler_events($page, $archive = null) $page->assign('undisplayed_events', $undisplayed_events); } + function make_id_for_new_user($nom,$prenom,$email){ + return sha1("bfpPUBEfsfj".$nom."##".$prenom.";%".$email); + } + function handler_sub($page, $eid = null) { $this->load('xnetevents.inc.php'); - $page->changeTpl('xnetevents/subscribe.tpl'); $evt = get_event($eid); if (is_null($evt)) { @@ -218,7 +230,10 @@ function handler_sub($page, $eid = null) if (!$evt['inscr_open']) { $page->kill('Les inscriptions pour cet événement sont closes'); } - if (!$evt['accept_nonmembre'] && !is_member() && !may_update()) { + if ($evt['access_control']!=AccessControl::All && !S::logged()) { + $page->kill('Connecte-toi pour accéder à cet événement'); + } + if ($evt['access_control']==AccessControl::Group && !is_member() && !may_update()) { $url = $globals->asso('sub_url'); if (empty($url)) { $url = $platal->ns . $globals->asso('diminutif') . "/" . 'subscribe'; @@ -227,100 +242,193 @@ function handler_sub($page, $eid = null) '. Pour devenir membre, rends-toi sur la page de demande d\'inscripton.'); } - $res = XDB::query("SELECT stamp - FROM requests - WHERE type = 'paiements' AND data LIKE {?}", - PayReq::same_event($eid, $globals->asso('id'))); - $page->assign('validation', $res->numRows()); - - $page->assign('eid', $eid); - $page->assign('event', $evt); + //if we arrived here via a confirmation email, process it + if(Get::has('nom') && Get::has('prenom') && Get::has('email') && Get::has('token')){ + //check if the request is valid + //IMPORTANT: this page can log any existing user or new non x user, the security here must be at least as strong as the password recovery functionality (generate a random one time confirmation token, and send an url with is to the given email, the token expires after 6 hours) + XDB::execute('DELETE FROM unlogged_users_event_subscription + WHERE DATE_SUB(NOW(), INTERVAL 380 MINUTE) > created'); + $res = XDB::query('SELECT uid + FROM unlogged_users_event_subscription + WHERE certificat = {?}', Get::v('token')); + if($res->numRows()===0) { + $page->kill('Ce lien n\'est pas valide.'); + } + + $found_user_id = $res->fetchOneCell(); + $user_id=$this->make_id_for_new_user(Get::v('nom'),Get::v('prenom'),Get::v('email')); + + if($user_id!==$found_user_id){ + $page->kill('Ce lien n\'est pas valide.'); + } + + XDB::execute('DELETE FROM unlogged_users_event_subscription + WHERE certificat = {?}', Get::v('token')); + + //check if the email address is already registered + $user=User::getSilent(Get::v('email')); + + if($user){ + //the email address is already registered, use the registered account + $hruid=$user->hruid; + }else{ + //the email address is not registered, create a new user + require_once 'name.func.inc.php'; + list($local_part, $domain) = explode('@', strtolower(Get::v('email'))); + $firstname=Get::v('prenom'); + $lastname=Get::v('nom'); + $hruid = User::makeHrid($local_part, $domain, 'ext'); + $full_name = build_full_name($firstname, $lastname); + $directory_name = build_directory_name($firstname, $lastname); + $sort_name = build_sort_name($firstname, $lastname); + + //ensure that the hruid does not already exist + while(true){ + $res=XDB::query("SELECT hruid FROM accounts WHERE hruid={?}", $hruid); + if($res->numRows()===0) break; + $i=1; + $hruid = User::makeHrid($local_part, $domain.".$i", 'ext'); + ++$i; + } - $items = get_event_items($eid); - $subs = get_event_subscription($eid, S::v('uid')); + //insert the user in the database + XDB::execute('INSERT INTO accounts SET firstname={?}, lastname={?}, full_name={?}, directory_name={?}, sort_name={?}, hruid={?}, email={?}, type={?}, state={?}', $firstname, $lastname, $full_name, $directory_name, $sort_name, $hruid, Get::v('email'), "xnet", "active"); + $user=User::get($hruid); + } + if(!Platal::session()->logAsUser($user)){ + $page->kill("Authentication failed for $hruid."); + } + } - if (Post::has('submit')) { - S::assert_xsrf_token(); - $moments = Post::v('moment', array()); - $pers = Post::v('personnes', array()); - $old_subs = $subs; - $subs = array(); - - foreach ($moments as $j => $v) { - $subs[$j] = intval($v); - - // retrieve other field when more than one person - if ($subs[$j] == 2) { - if (!isset($pers[$j]) || !is_numeric($pers[$j]) || $pers[$j] < 0) { - $page->trigError("Tu dois choisir un nombre d'invités correct !"); - return; + if (S::logged()) { + $page->changeTpl('xnetevents/subscribe.tpl'); + $res = XDB::query("SELECT stamp + FROM requests + WHERE type = 'paiements' AND data LIKE {?}", + PayReq::same_event($eid, $globals->asso('id'))); + $page->assign('validation', $res->numRows()); + + $page->assign('eid', $eid); + $page->assign('event', $evt); + + $items = get_event_items($eid); + $subs = get_event_subscription($eid, S::v('uid')); + + if (Post::has('submit')) { + S::assert_xsrf_token(); + $moments = Post::v('moment', array()); + $pers = Post::v('personnes', array()); + $old_subs = $subs; + $subs = array(); + + foreach ($moments as $j => $v) { + $subs[$j] = intval($v); + + // retrieve other field when more than one person + if ($subs[$j] == 2) { + if (!isset($pers[$j]) || !is_numeric($pers[$j]) || $pers[$j] < 0) { + $page->trigError("Tu dois choisir un nombre d'invités correct !"); + return; + } + $subs[$j] = $pers[$j]; } - $subs[$j] = $pers[$j]; } - } - // count what the user must pay, and what he manually paid - $manual_paid = 0; - foreach ($items as $item_id => $item) { - if (array_key_exists($item_id, $old_subs)) { - $manual_paid += $old_subs[$item_id]['paid']; + // count what the user must pay, and what he manually paid + $manual_paid = 0; + foreach ($items as $item_id => $item) { + if (array_key_exists($item_id, $old_subs)) { + $manual_paid += $old_subs[$item_id]['paid']; + } } - } - // impossible to unsubscribe if you already paid sthing - if (!array_sum($subs) && $manual_paid != 0) { - $page->trigError("Impossible de te désinscrire complètement " . - "parce que tu as fait un paiement par " . - "chèque ou par liquide. Contacte un " . - "administrateur du groupe si tu es sûr de " . - "ne pas venir."); - $updated = false; - } else { - // update actual inscriptions - $updated = subscribe(S::v('uid'), $eid, $subs); - } - if ($updated) { - $evt = get_event_detail($eid); - if ($evt['topay'] > 0) { - $page->trigSuccess('Ton inscription à l\'événement a été mise à jour avec succès, tu peux payer ta participation en cliquant ci-dessous'); + // impossible to unsubscribe if you already paid sthing + if (!array_sum($subs) && $manual_paid != 0) { + $page->trigError("Impossible de te désinscrire complètement " . + "parce que tu as fait un paiement par " . + "chèque ou par liquide. Contacte un " . + "administrateur du groupe si tu es sûr de " . + "ne pas venir."); + $updated = false; } else { - $page->trigSuccess('Ton inscription à l\'événement a été mise à jour avec succès.'); + // update actual inscriptions + $updated = subscribe(S::v('uid'), $eid, $subs); } + if ($updated) { + $evt = get_event_detail($eid); + if ($evt['topay'] > 0) { + $page->trigSuccess('Ton inscription à l\'événement a été mise à jour avec succès, tu peux payer ta participation en cliquant ci-dessous'); + } else { + $page->trigSuccess('Ton inscription à l\'événement a été mise à jour avec succès.'); + } - if ($evt['subscription_notification'] != 'nobody') { - $mailer = new PlMailer('xnetevents/subscription-notif.mail.tpl'); - if ($evt['subscription_notification'] != 'creator') { - $admins = $globals->asso()->iterAdmins(); - while ($admin = $admins->next()) { - $mailer->addTo($admin); + if ($evt['subscription_notification'] != 'nobody') { + $mailer = new PlMailer('xnetevents/subscription-notif.mail.tpl'); + if ($evt['subscription_notification'] != 'creator') { + $admins = $globals->asso()->iterAdmins(); + while ($admin = $admins->next()) { + $mailer->addTo($admin); + } } + if ($evt['subscription_notification'] != 'animator') { + $mailer->addTo($evt['organizer']); + } + $mailer->assign('group', $globals->asso('nom')); + $mailer->assign('event', $evt['intitule']); + $mailer->assign('subs', $subs); + $mailer->assign('moments', $evt['moments']); + $mailer->assign('name', S::user()->fullName('promo')); + $mailer->send(); } - if ($evt['subscription_notification'] != 'animator') { - $mailer->addTo($evt['organizer']); - } - $mailer->assign('group', $globals->asso('nom')); - $mailer->assign('event', $evt['intitule']); - $mailer->assign('subs', $subs); - $mailer->assign('moments', $evt['moments']); - $mailer->assign('name', S::user()->fullName('promo')); - $mailer->send(); } } + $subs = get_event_subscription($eid, S::v('uid')); + // count what the user must pay + $topay = 0; + $manually_paid = 0; + foreach ($items as $item_id => $item) { + if (array_key_exists($item_id, $subs)) { + $topay += $item['montant']*$subs[$item_id]['nb']; + $manually_paid += $subs[$item_id]['paid']; + } + } + $paid = $manually_paid + get_event_telepaid($eid, S::v('uid')); + $page->assign('moments', $items); + $page->assign('subs', $subs); + $page->assign('topay', $topay); + $page->assign('paid', $paid); } - $subs = get_event_subscription($eid, S::v('uid')); - // count what the user must pay - $topay = 0; - $manually_paid = 0; - foreach ($items as $item_id => $item) { - if (array_key_exists($item_id, $subs)) { - $topay += $item['montant']*$subs[$item_id]['nb']; - $manually_paid += $subs[$item_id]['paid']; + else { + $page->changeTpl('xnetevents/subscribe_not_logged.tpl'); + $page->assign('eid', $eid); + $page->assign('event', $evt); + + if (Post::has('submit')) { + S::assert_xsrf_token(); + $page->assign('email', Post::v('email')); + $page->assign('form_sent', 1); + + $nom=Post::v('nom'); + $prenom=Post::v('prenom'); + $email=Post::v('email'); + $id=$this->make_id_for_new_user($nom,$prenom,$email); + $token=rand_url_id(); + $base = $globals->baseurl . '/' . $platal->ns . $globals->asso('diminutif'); + $url = "$base/events/sub/$eid?nom=".urlencode($nom)."&prenom=".urlencode($prenom)."&email=".urlencode($email)."&token=$token"; + + //remember our randomly generated id (valid 6 hours) + XDB::execute('INSERT INTO unlogged_users_event_subscription (certificat,uid,created) + VALUES ({?},{?},NOW())', $token, $id); + + $mailer = new PlMailer('xnetevents/subscribe_not_logged_confirm.mail.tpl'); + $mailer->assign('group', $globals->asso('nom')); + $mailer->assign('event', $evt['intitule']); + $mailer->assign('nom', $nom); + $mailer->assign('prenom', $prenom); + $mailer->assign('url', $url); + $mailer->addTo($email); + $mailer->send(); } } - $paid = $manually_paid + get_event_telepaid($eid, S::v('uid')); - $page->assign('moments', $items); - $page->assign('subs', $subs); - $page->assign('topay', $topay); - $page->assign('paid', $paid); } function handler_csv($page, $eid = null, $item_id = null) @@ -484,10 +592,11 @@ function handler_edit($page, $eid = null) ); $trivial = array('intitule', 'descriptif', 'noinvite', 'subscription_notification', - 'show_participants', 'accept_nonmembre', 'uid'); + 'show_participants', 'access_control', 'uid'); foreach ($trivial as $k) { $evt[$k] = Post::v($k); } + if (!$eid) { $evt['uid'] = S::v('uid'); } @@ -504,18 +613,18 @@ function handler_edit($page, $eid = null) XDB::execute('INSERT INTO group_events (eid, asso_id, uid, intitule, paiement_id, descriptif, debut, fin, show_participants, short_name, deadline_inscription, noinvite, - accept_nonmembre, subscription_notification) + access_control, subscription_notification) VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}) ON DUPLICATE KEY UPDATE asso_id = VALUES(asso_id), uid = VALUES(uid), intitule = VALUES(intitule), paiement_id = VALUES(paiement_id), descriptif = VALUES(descriptif), debut = VALUES(debut), fin = VALUES(fin), show_participants = VALUES(show_participants), short_name = VALUES(short_name), deadline_inscription = VALUES(deadline_inscription), noinvite = VALUES(noinvite), - accept_nonmembre = VALUES(accept_nonmembre), subscription_notification = VALUES(subscription_notification)', + access_control = VALUES(access_control), subscription_notification = VALUES(subscription_notification)', $evt['eid'], $evt['asso_id'], $evt['uid'], $evt['intitule'], $evt['paiement_id'], $evt['descriptif'], $evt['debut'], $evt['fin'], $evt['show_participants'], $evt['short_name'], $evt['deadline_inscription'], - $evt['noinvite'], $evt['accept_nonmembre'], $evt['subscription_notification']); + $evt['noinvite'], $evt['access_control'], $evt['subscription_notification']); // if new event, get its id if (!$eid) { @@ -581,7 +690,7 @@ function handler_edit($page, $eid = null) $res = XDB::query( "SELECT eid, intitule, descriptif, debut, fin, uid, show_participants, paiement_id, short_name, - deadline_inscription, noinvite, accept_nonmembre, subscription_notification + deadline_inscription, noinvite, access_control, subscription_notification FROM group_events WHERE eid = {?}", $eid); $evt = $res->fetchOneAssoc(); diff --git a/modules/xnetevents/xnetevents.inc.php b/modules/xnetevents/xnetevents.inc.php index 0e6fd6fec..c47d5452e 100644 --- a/modules/xnetevents/xnetevents.inc.php +++ b/modules/xnetevents/xnetevents.inc.php @@ -44,7 +44,7 @@ function get_events($asso_id, $order, $archive) if ($order != 'asc' && $order != 'desc') { $order = 'desc'; } - $evts = XDB::fetchAllAssoc('eid', "SELECT ge.eid, ge.uid, ge.intitule, ge.debut, ge.fin, ge.show_participants, ge.deadline_inscription, ge.accept_nonmembre, ge.paiement_id + $evts = XDB::fetchAllAssoc('eid', "SELECT ge.eid, ge.uid, ge.intitule, ge.debut, ge.fin, ge.show_participants, ge.deadline_inscription, ge.access_control, ge.paiement_id FROM group_events as ge WHERE asso_id = {?} and archive = {?} ORDER BY ge.debut $order", @@ -66,7 +66,7 @@ function get_event(&$eid) $eid); $eid = $id; } - $evt = XDB::fetchOneAssoc('SELECT ge.uid, ge.intitule, ge.descriptif, ge.debut, ge.fin, ge.deadline_inscription, ge.accept_nonmembre, ge.noinvite, ge.paiement_id + $evt = XDB::fetchOneAssoc('SELECT ge.uid, ge.intitule, ge.descriptif, ge.debut, ge.fin, ge.deadline_inscription, ge.access_control, ge.noinvite, ge.paiement_id FROM group_events as ge WHERE eid = {?}', $eid); @@ -167,7 +167,7 @@ function get_event_detail($eid, $item_id = false, $asso_id = null) if (!$evt) { return null; } - if ($GLOBALS['IS_XNET_SITE'] && $evt['accept_nonmembre'] == 0 && !is_member() && !may_update()) { + if ($GLOBALS['IS_XNET_SITE'] && $evt['access_control'] == AccessControl::Group && !is_member() && !may_update()) { return false; } diff --git a/templates/xnetevents/calendar.tpl b/templates/xnetevents/calendar.tpl index b65853e5c..974cc3923 100644 --- a/templates/xnetevents/calendar.tpl +++ b/templates/xnetevents/calendar.tpl @@ -37,7 +37,7 @@ UID:event-{$e.short_name}-{$e.eid}@{$asso->diminutif}.polytechnique.org ATTENDEE;CN="{$m.user->fullName('promo')}":MAILTO:{$m.user->bestEmail()} {/foreach} {/if} -{if $e.accept_nonmembre} +{if $e.access_control!=AccessControl::Group} CLASS:PUBLIC {else} CLASS:PRIVATE diff --git a/templates/xnetevents/edit.tpl b/templates/xnetevents/edit.tpl index 6f816f814..e00bc3d3c 100644 --- a/templates/xnetevents/edit.tpl +++ b/templates/xnetevents/edit.tpl @@ -147,11 +147,12 @@ function deadlineChange(box) non
- Autoriser les non-membres : - - + Accepter les : +
Autoriser les invités : diff --git a/templates/xnetevents/subscribe_not_logged.tpl b/templates/xnetevents/subscribe_not_logged.tpl new file mode 100644 index 000000000..ca87c5d6b --- /dev/null +++ b/templates/xnetevents/subscribe_not_logged.tpl @@ -0,0 +1,55 @@ +{**************************************************************************} +{* *} +{* Copyright (C) 2003-2016 Polytechnique.org *} +{* http://opensource.polytechnique.org/ *} +{* *} +{* This program is free software; you can redistribute it and/or modify *} +{* it under the terms of the GNU General Public License as published by *} +{* the Free Software Foundation; either version 2 of the License, or *} +{* (at your option) any later version. *} +{* *} +{* This program is distributed in the hope that it will be useful, *} +{* but WITHOUT ANY WARRANTY; without even the implied warranty of *} +{* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *} +{* GNU General Public License for more details. *} +{* *} +{* You should have received a copy of the GNU General Public License *} +{* along with this program; if not, write to the Free Software *} +{* Foundation, Inc., *} +{* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *} +{* *} +{**************************************************************************} + +

{$asso->nom} : Evénement {$event.intitule}

+ +

+ {assign var=profile value=$event.organizer->profile()} + Cet événement a lieu {$event.date} et a été proposé par + + {$event.organizer->fullName('promo')} + . +

+ +

+ {$event.descriptif|nl2br} +

+ +{if $form_sent} +Un email de confirmation vous à été envoyé à {$email}. +{else} +
+ {xsrf_token_field} +

+ Nom : + Prénom : + E-mail : +
+
+
+ +

+
+{/if} + +{* vim:set et sw=2 sts=2 sws=2 fenc=utf-8: *} + diff --git a/templates/xnetevents/subscribe_not_logged_confirm.mail.tpl b/templates/xnetevents/subscribe_not_logged_confirm.mail.tpl new file mode 100644 index 000000000..451771f9d --- /dev/null +++ b/templates/xnetevents/subscribe_not_logged_confirm.mail.tpl @@ -0,0 +1,39 @@ +{**************************************************************************} +{* *} +{* Copyright (C) 2003-2016 Polytechnique.org *} +{* http://opensource.polytechnique.org/ *} +{* *} +{* This program is free software; you can redistribute it and/or modify *} +{* it under the terms of the GNU General Public License as published by *} +{* the Free Software Foundation; either version 2 of the License, or *} +{* (at your option) any later version. *} +{* *} +{* This program is distributed in the hope that it will be useful, *} +{* but WITHOUT ANY WARRANTY; without even the implied warranty of *} +{* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *} +{* GNU General Public License for more details. *} +{* *} +{* You should have received a copy of the GNU General Public License *} +{* along with this program; if not, write to the Free Software *} +{* Foundation, Inc., *} +{* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *} +{* *} +{**************************************************************************} + +{config_load file="mails.conf" section="xnet_notification"} +{if $mail_part eq 'head'} +{from full=#from#} +{subject text="[`$group`] $event"} +{elseif $mail_part eq 'wiki'} +M. {$prenom} {$nom}, + +Pour valider votre inscription l'événement {$event}, veuillez suivre le lien de confirmation suivant qui expire dans six heures: + +{$url} + +Si vous n'arrivez pas à cliquer sur le lien, copiez intégralement l'adresse dans la barre de votre navigateur. Ce lien n'est valable qu'une fois. Si vous avez besoin d'un nouveau lien, vous pouvez tout simplement recommencer cette procédure. + +{include file="include/signature.mail.tpl"} +{/if} +{* vim:set et sw=2 sts=2 sws=2: *} + diff --git a/upgrade/1.1.21/01_add_group_events_access_control.sql b/upgrade/1.1.21/01_add_group_events_access_control.sql new file mode 100644 index 000000000..79b35e6ad --- /dev/null +++ b/upgrade/1.1.21/01_add_group_events_access_control.sql @@ -0,0 +1,5 @@ +alter table group_events add access_control enum('group','registered','all') default 'registered' after accept_nonmembre; +update group_events set access_control='group' where accept_nonmembre=0; +update group_events set access_control='registered' where accept_nonmembre=1; +create table unlogged_users_event_subscription (certificat char(32), uid char(40), created datetime); +