3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
13 if (!defined('_ECRIRE_INC_VERSION')) return;
15 include_spip('base/abstract_sql');
17 // Fonctions de gestion de l'acces restreint aux rubriques
20 // http://doc.spip.org/@acces_restreint_rubrique
21 function acces_restreint_rubrique($id_rubrique) {
22 global $connect_id_rubrique;
24 return (isset($connect_id_rubrique[$id_rubrique]));
27 // http://doc.spip.org/@auteurs_article
28 function auteurs_article($id_article, $cond='')
30 return sql_allfetsel("id_auteur", "spip_auteurs_articles", "id_article=$id_article". ($cond ?
" AND $cond" : ''));
33 // http://doc.spip.org/@auteurs_autorises
34 function auteurs_autorises($in, $cond='')
36 return sql_in("statut", array('0minirezo','1comite'))
37 . (!$cond ?
'' : " AND $cond")
38 . (!$in ?
'' : (" AND ". sql_in("id_auteur", $in, 'NOT')));
41 // Un nouvel inscrit prend son statut definitif a la 1ere connexion.
42 // Le statut a ete memorise dans bio (cf formulaire_inscription).
43 // On le verifie, car la config a peut-etre change depuis,
44 // et pour compatibilite avec les anciennes versions n'utilisait pas "bio".
46 // http://doc.spip.org/@acces_statut
47 function acces_statut($id_auteur, $statut, $bio)
49 if ($statut != 'nouveau') return $statut;
50 include_spip('inc/filtres');
51 include_spip('inc/autoriser');
52 if (!autoriser('inscrireauteur', $bio)) return $statut; //i.e. "nouveau"
53 include_spip('action/editer_auteur');
54 instituer_auteur($id_auteur,array('statut'=> $bio));
55 include_spip('inc/modifier');
56 revision_auteur($id_auteur, array('bio'=>''));
57 include_spip('inc/session');
58 session_set('statut',$bio);
62 // Fonction d'authentification. Retourne:
63 // - URL de connexion si on ne sait rien (pas de cookie, pas Auth_user);
64 // - un tableau si visiteur sans droit (tableau = sa ligne SQL)
65 // - code numerique d'erreur SQL
66 // - une chaine vide si autorisation a penetrer dans l'espace prive.
68 // http://doc.spip.org/@inc_auth_dist
69 function inc_auth_dist() {
71 global $connect_login ;
75 if ($row) return auth_init_droits($row);
77 if (!$connect_login) return auth_a_loger();
79 // Cas ou l'auteur a ete identifie mais on n'a pas d'info sur lui
80 // C'est soit parce que la base est inutilisable,
81 // soit parce que la table des auteurs a changee (restauration etc)
82 // Pas la peine d'insister.
83 // Renvoyer le nom fautif et une URL de remise a zero
86 return array('login' => $connect_login,
87 'site' => generer_url_public('', "action=logout&logout=prive"));
89 $n = intval(sql_errno());
90 spip_log("Erreur base de donnees $n " . sql_error());
94 // fonction appliquee par ecrire/index sur le resultat de la precedente
95 // en cas de refus de connexion.
96 // Retourne un message a afficher ou redirige illico.
98 function auth_echec($raison)
100 include_spip('inc/minipres');
101 include_spip('inc/headers');
102 // pas authentifie. Pourquoi ?
103 if (is_string($raison)) {
104 // redirection vers une page d'authentification
105 // on ne revient pas de cette fonction
106 // sauf si pb de header
107 $raison = redirige_formulaire($raison);
108 } elseif (is_int($raison)) {
109 // erreur SQL a afficher
110 $raison = minipres(_T('info_travaux_titre'), _T('titre_probleme_technique'). "<p><tt>".sql_errno()." ".sql_error()."</tt></p>");
111 } elseif (@$raison['statut']) {
112 // un simple visiteur n'a pas acces a l'espace prive
113 spip_log("connexion refusee a " . @$raison['id_auteur']);
114 $raison = minipres(_T('avis_erreur_connexion'),_T('avis_erreur_visiteur'));
116 // auteur en fin de droits ...
117 $h = $raison['site'];
118 $raison = minipres(_T('avis_erreur_connexion'),
120 . _T('texte_inc_auth_1',
121 array('auth_login' => $raison['login']))
123 . _T('texte_inc_auth_2')
125 . _T('texte_inc_auth_3'));
130 // Retourne la description d'un authentifie par cookie ou http_auth
131 // Et affecte la globale $connect_login
135 global $auth_can_disconnect, $ignore_auth_http, $ignore_remote_user;
136 global $connect_login ;
139 // Initialiser variables (eviter hacks par URL)
144 $auth_can_disconnect = false;
147 // Recuperer les donnees d'identification
150 // Session valide en cours ?
151 if (isset($_COOKIE['spip_session'])) {
152 $session = charger_fonction('session', 'inc');
153 if ($id_auteur = $session()
154 OR $id_auteur===0 // reprise sur restauration
156 $auth_can_disconnect = true;
157 $connect_login = $GLOBALS['visiteur_session']['login'];
158 } else unset($_COOKIE['spip_session']);
161 // Essayer auth http si significatif
162 // (ignorer les login d'intranet independants de spip)
163 if (!$ignore_auth_http) {
165 (isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])
166 AND $r = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))
168 // Si auth http differtente de basic, PHP_AUTH_PW
169 // est indisponible mais tentons quand meme pour
170 // autocreation via LDAP
171 (isset($_SERVER['REMOTE_USER'])
172 AND $r = lire_php_auth($_SERVER['PHP_AUTH_USER'] = $_SERVER['REMOTE_USER'], ''))
175 $_SERVER['PHP_AUTH_PW'] = '';
176 $auth_can_disconnect = true;
177 $GLOBALS['visiteur_session'] = $r;
178 $connect_login = $GLOBALS['visiteur_session']['login'];
180 // cas de la session en plus de PHP_AUTH
181 /* if ($id_auteur != $r['id_auteur']){
182 spip_log("vol de session $id_auteur" . join(', ', $r));
183 unset($_COOKIE['spip_session']);
188 // Authentification .htaccess old style, car .htaccess semble
189 // souvent definir *aussi* PHP_AUTH_USER et PHP_AUTH_PW
190 else if (isset($_SERVER['REMOTE_USER']))
191 $connect_login = $_SERVER['REMOTE_USER'];
194 $where = (is_numeric($id_auteur)
195 /*AND $id_auteur>0*/ // reprise lors des restaurations
197 "id_auteur=$id_auteur" :
198 (!strlen($connect_login) ?
'' : "login=" . sql_quote($connect_login));
200 if (!$where) return '';
202 // Trouver les autres infos dans la table auteurs.
203 // le champ 'quand' est utilise par l'agenda
205 return sql_fetsel("*, en_ligne AS quand", "spip_auteurs", "$where AND statut!='5poubelle'");
209 // Init des globales pour tout l'espace prive si visiteur connu
210 // Le tableau global visiteur_session contient toutes les infos pertinentes et
211 // a jour (tandis que $visiteur_session peut avoir des valeurs un peu datees
212 // s'il est pris dans le fichier de session)
213 // Les plus utiles sont aussi dans les variables simples ci-dessus
214 // si la globale est vide ce n'est pas un tableau, on la force pour empecher un warning
216 function auth_init_droits($row)
218 global $connect_statut, $connect_toutes_rubriques, $connect_id_rubrique, $connect_login, $connect_id_auteur;
220 $connect_id_auteur = $row['id_auteur'];
221 $connect_login = $row['login'];
222 $connect_statut = acces_statut($connect_id_auteur, $row['statut'], $row['bio']);
224 // on force l'Ă©criture de cette info dans le fichier de session
225 // pour pouvoir récupérer #SESSION{en_ligne} dans les squelettes
226 session_set('en_ligne', $row['en_ligne']);
228 $GLOBALS['visiteur_session'] = array_merge((array)$GLOBALS['visiteur_session'], $row);
229 $r = @unserialize
($row['prefs']);
230 $GLOBALS['visiteur_session']['prefs'] =
231 (@isset
($r['couleur'])) ?
$r : array('couleur' =>1, 'display'=>0);
233 // au cas ou : ne pas memoriser les champs sensibles
234 unset($GLOBALS['visiteur_session']['pass']);
235 unset($GLOBALS['visiteur_session']['htpass']);
236 unset($GLOBALS['visiteur_session']['alea_actuel']);
237 unset($GLOBALS['visiteur_session']['alea_futur']);
239 // rajouter les sessions meme en mode auth_http
240 // pour permettre les connexions multiples et identifier les visiteurs
241 if (!isset($_COOKIE['spip_session'])) {
242 $session = charger_fonction('session', 'inc');
243 if ($spip_session = $session($row)) {
244 include_spip('inc/cookie');
247 $_COOKIE['spip_session'] = $spip_session,
248 time() +
3600 * 24 * 14
253 // Etablir les droits selon le codage attendu
254 // dans ecrire/index.php ecrire/prive.php
256 // Pas autorise a acceder a ecrire ? renvoyer le tableau
257 // A noter : le premier appel a autoriser() a le bon gout
258 // d'initialiser $GLOBALS['visiteur_session']['restreint'],
259 // qui ne figure pas dans le fichier de session
260 include_spip('inc/autoriser');
262 if (!autoriser('ecrire'))
265 // autoriser('ecrire') ne laisse passer que les Admin et les Redac
270 if ($connect_statut == '0minirezo') {
271 if (is_array($GLOBALS['visiteur_session']['restreint']))
272 $connect_id_rubrique = $GLOBALS['visiteur_session']['restreint'];
273 $connect_toutes_rubriques = !$connect_id_rubrique;
275 // Pour les redacteurs, inc_version a fait l'initialisation minimale
277 return ''; // i.e. pas de pb.
280 function auth_a_loger()
282 $redirect = generer_url_public('login',
283 "url=" . rawurlencode(self('&',true)), '&');
285 // un echec au "bonjour" (login initial) quand le statut est
286 // inconnu signale sans doute un probleme de cookies
287 if (isset($_GET['bonjour']))
288 $redirect = parametre_url($redirect,
290 (!isset($GLOBALS['visiteur_session']['statut'])
299 // http://doc.spip.org/@auth_trace
300 function auth_trace($row, $date=null)
302 // Indiquer la connexion. A la minute pres ca suffit.
303 if (!is_numeric($connect_quand = $row['quand']))
304 $connect_quand = strtotime($connect_quand);
307 $date = date('Y-m-d H:i:s');
309 if (abs(strtotime($date) - $connect_quand) >= 60) {
310 sql_updateq("spip_auteurs", array("en_ligne" => $date), "id_auteur=" .$row['id_auteur']);
315 /** ----------------------------------------------------------------------------
316 * API Authentification, gestion des identites centralisees
320 * Fonction aiguillage, privee
321 * @param string $fonction
323 * @param mixed $defaut
326 function auth_administrer($fonction,$args,$defaut=false){
327 $auth_methode = array_shift($args);
328 $auth_methode = $auth_methode ?
$auth_methode : 'spip'; // valeur par defaut au cas ou
329 if ($auth = charger_fonction($auth_methode,'auth',true)
330 AND function_exists($f="auth_{$auth_methode}_$fonction")
332 return call_user_func_array($f, $args);
338 * Pipeline pour inserer du contenu dans le formulaire de login
343 function auth_formulaire_login($flux){
344 foreach ($GLOBALS['liste_des_authentifications'] as $methode)
345 $flux = auth_administrer('formulaire_login',array($methode,$flux),$flux);
352 * Retrouver le login interne lie a une info login saisie
353 * la saisie peut correspondre a un login delegue
354 * qui sera alors converti en login interne apres verification
356 * @param string $login
357 * @param string $serveur
358 * @return string/bool
360 function auth_retrouver_login($login, $serveur=''){
361 if (!spip_connect($serveur)) {
362 include_spip('inc/minipres');
363 echo minipres(_T('info_travaux_titre'),
364 _T('titre_probleme_technique'));
368 foreach ($GLOBALS['liste_des_authentifications'] as $methode) {
369 if ($auteur = auth_administrer('retrouver_login',array($methode, $login, $serveur))) {
378 * informer sur un login
379 * Ce dernier transmet le tableau ci-dessous a la fonction JS informer_auteur
380 * Il est invoque par la fonction JS actualise_auteur via la globale JS
381 * page_auteur=#URL_PAGE{informer_auteur} dans le squelette login
382 * N'y aurait-il pas plus simple ?
384 * @param string $login
385 * @param string $serveur
388 function auth_informer_login($login, $serveur=''){
390 OR !$login = auth_retrouver_login($login, $serveur)
391 OR !$row = sql_fetsel('*','spip_auteurs','login='.sql_quote($login),'','','','',$serveur)
395 $prefs = unserialize($row['prefs']);
397 'id_auteur'=>$row['id_auteur'],
398 'login'=>$row['login'],
399 'cnx' => ($prefs['cnx'] == 'perma') ?
'1' : '0',
400 'logo' => recuperer_fond('formulaires/inc-logo_auteur', $row),
403 // desactiver le hash md5 si pas auteur spip ?
404 if ($row['source']!=='spip'){
405 $row['alea_actuel']= '';
406 $row['alea_futur']= '';
410 return auth_administrer('informer_login',array($row['source'],$infos, $row, $serveur),$infos);
415 * Essayer les differentes sources d'authenfication dans l'ordre specifie.
416 * S'en souvenir dans visiteur_session['auth']
418 * @param string $login
419 * @param string $password
420 * @param string $serveur
423 function auth_identifier_login($login, $password, $serveur=''){
425 foreach ($GLOBALS['liste_des_authentifications'] as $methode) {
426 if ($auth = charger_fonction($methode, 'auth',true)){
427 $auteur = $auth($login, $password, $serveur);
428 if (is_array($auteur) AND count($auteur)) {
429 spip_log("connexion de $login par methode $methode");
430 $auteur['auth'] = $methode;
433 elseif (is_string($auteur))
434 $erreur .= "$auteur ";
441 * Fournir une url de retour apres login par un SSO
442 * pour finir l'authentification
444 * @param string $auth_methode
445 * @param string $login
446 * @param string $serveur
449 function auth_url_retour_login($auth_methode, $login, $redirect='', $serveur=''){
450 $securiser_action = charger_fonction('securiser_action','inc');
451 return $securiser_action('auth', "$auth_methode/$login", $redirect, true);
454 function auth_terminer_identifier_login($auth_methode, $login, $serveur=''){
455 $args = func_get_args();
456 $auteur = auth_administrer('terminer_identifier_login',$args);
461 * Loger un auteur suite a son identification
463 * @param array $auteur
465 function auth_loger($auteur, $refuse_cookie_admin=false){
466 if (!is_array($auteur) OR !count($auteur))
469 $session = charger_fonction('session', 'inc');
471 $p = ($auteur['prefs']) ?
unserialize($auteur['prefs']) : array();
472 $p['cnx'] = ($auteur['cookie'] == 'oui') ?
'perma' : '';
473 $p = array('prefs' => serialize($p));
474 sql_updateq('spip_auteurs', $p, "id_auteur=" . $auteur['id_auteur']);
476 if ($auteur['statut'] == 'nouveau') {
477 $session(); // charger la session car on va la modifier
478 $auteur['statut'] = acces_statut($auteur['id_auteur'], $auteur['statut'], $auteur['bio']);
481 // Si on est admin, poser le cookie de correspondance
482 include_spip('inc/cookie');
483 if (!$refuse_cookie_admin AND $auteur['statut'] == '0minirezo') {
484 spip_setcookie('spip_admin', '@'.$auteur['login'],
485 time() +
7 * 24 * 3600);
487 // sinon le supprimer ...
489 spip_setcookie('spip_admin', '',1);
492 // bloquer ici le visiteur qui tente d'abuser de ses droits
498 function auth_deloger(){
499 $logout = charger_fonction('logout','action');
504 * Tester la possibilite de modifier le login d'authentification
505 * pour la methode donnee
507 * @param string $auth_methode
508 * @param string $serveur
511 function auth_autoriser_modifier_login($auth_methode, $serveur=''){
512 $args = func_get_args();
513 return auth_administrer('autoriser_modifier_login',$args);
517 * Verifier la validite d'un nouveau login pour modification
518 * pour la methode donnee
520 * @param string $auth_methode
521 * @param string $new_login
522 * @param int $id_auteur
523 * @param string $serveur
525 * message d'erreur ou chaine vide si pas d'erreur
527 function auth_verifier_login($auth_methode, $new_login, $id_auteur=0, $serveur=''){
528 $args = func_get_args();
529 return auth_administrer('verifier_login',$args,'');
533 * Modifier le login d'un auteur pour la methode donnee
535 * @param string $auth_methode
536 * @param string $new_login
537 * @param int $id_auteur
538 * @param string $serveur
541 function auth_modifier_login($auth_methode, $new_login, $id_auteur, $serveur=''){
542 $args = func_get_args();
543 return auth_administrer('modifier_login',$args);
547 * Tester la possibilite de modifier le pass
548 * pour la methode donnee
550 * @param string $auth_methode
551 * @param string $serveur
555 function auth_autoriser_modifier_pass($auth_methode, $serveur=''){
556 $args = func_get_args();
557 return auth_administrer('autoriser_modifier_pass',$args);
561 * Verifier la validite d'un pass propose pour modification
562 * pour la methode donnee
564 * @param string $auth_methode
565 * @param string $login
566 * @param string $new_pass
567 * @param int $id_auteur
568 * @param string $serveur
570 * message d'erreur ou chaine vide si pas d'erreur
572 function auth_verifier_pass($auth_methode, $login, $new_pass, $id_auteur=0, $serveur=''){
573 $args = func_get_args();
574 return auth_administrer('verifier_pass',$args,'');
578 * Modifier le mot de passe d'un auteur
579 * pour la methode donnee
581 * @param string $auth_methode
582 * @param string $login
583 * @param string $new_pass
584 * @param int $id_auteur
585 * @param string $serveur
589 function auth_modifier_pass($auth_methode, $login, $new_pass, $id_auteur, $serveur=''){
590 $args = func_get_args();
591 return auth_administrer('modifier_pass',$args);
595 * Synchroniser un compte sur une base distante pour la methode
596 * donnee lorsque des modifications sont faites dans la base auteur
598 * @param string $auth_methode
599 * ici true permet de forcer la synchronisation de tous les acces pour toutes les methodes
600 * @param int $id_auteur
601 * @param array $champs
602 * @param array $options
603 * @param string $serveur
606 function auth_synchroniser_distant($auth_methode=true, $id_auteur=0, $champs=array(), $options = array(), $serveur=''){
607 $args = func_get_args();
608 if ($auth_methode===true OR (isset($options['all']) AND $options['all']==true)){
609 $options['all'] = true; // ajouter une option all=>true pour chaque auth
610 $args = array(true, $id_auteur, $champs, $options, $serveur);
611 foreach ($GLOBALS['liste_des_authentifications'] as $methode) {
613 array_unshift($args,$methode);
614 auth_administrer('synchroniser_distant',$args);
618 auth_administrer('synchroniser_distant',$args);
624 * @param string $login
626 * @param string $serveur
629 function lire_php_auth($login, $pw, $serveur=''){
631 $row = sql_fetsel('*', 'spip_auteurs', 'login=' . sql_quote($login),'','','','',$serveur);
634 if (spip_connect_ldap($serveur)
635 AND $auth_ldap = charger_fonction('ldap', 'auth', true))
636 return $auth_ldap($login, $pw, $serveur);
639 // su pas de source definie
640 // ou auth/xxx introuvable, utiliser 'spip'
641 if (!$auth_methode = $row['source']
642 OR !$auth = charger_fonction($auth_methode, 'auth', true))
643 $auth = charger_fonction('spip', 'auth', true);
647 $auteur = $auth($login, $pw, $serveur);
648 // verifier que ce n'est pas un message d'erreur
649 if (is_array($auteur) AND count($auteur))
655 * entete php_auth (est-encore utilise ?)
658 * @param <type> $raison
659 * @param <type> $retour
662 * @param <type> $lien
664 function ask_php_auth($pb, $raison, $retour='', $url='', $re='', $lien='') {
665 @Header
("WWW-Authenticate: Basic realm=\"espace prive\"");
666 @Header
("HTTP/1.0 401 Unauthorized");
667 $ici = generer_url_ecrire();
668 $retour = $retour?
$retour:_T('icone_retour');
669 echo "<b>$pb</b><p>$raison</p>[<a href='$ici'>$retour</a>] ";
671 echo "[<a href='", generer_url_action('cookie',"essai_auth_http=oui&$url"), "'>$re</a>]";
675 echo " [<a href='$ici'>"._T('login_espace_prive')."</a>]";