Changeset 84562 in spip-zone for _plugins_/acces_restreint
- Timestamp:
- Sep 10, 2014, 11:18:30 PM (7 years ago)
- Location:
- _plugins_/acces_restreint/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
_plugins_/acces_restreint/trunk/action/api_docrestreint.php
r84557 r84562 25 25 * docrestreint.api/id/cle/file 26 26 * 27 * @pipeline_appel accesrestreint_repertoires_toujours_autorises 28 * @pipeline_appel accesrestreint_pre_vue_document 29 * 27 30 * @param null $arg 28 31 */ … … 43 46 return; 44 47 } 48 45 49 46 50 // Séparer les 3 arguments … … 50 54 $f = implode("/", $arg); 51 55 52 / **53 * URL de test de fonctionnement54 * @see accesrestreint_gerer_htaccess() 55 */56 if ($ id_document==0 AND $cle==1 AND $f=="test/.test") {56 // Objet reprenant toutes les infos connues 57 $Document = new Accesrestreint_document($f, $id_document, $cle); 58 59 // Simple test de fonctionnement ? 60 if ($Document->est_un_test()) { 57 61 echo "OK"; 58 62 return; 59 63 } 60 64 61 include_spip('inc/documents'); 62 63 $file = get_spip_doc($f); 64 spip_log($file, 'dbg'); 65 66 // securite : on refuse tout ../ ou url absolue 67 if ((strpos($f, '../') !== false) OR (preg_match(',^\w+://,', $f))) { 68 accesrestreint_afficher_erreur_document(403); 69 return; 70 } 71 72 // inexistant ou illisible : 404 73 if (!file_exists($file) OR !is_readable($file)) { 74 accesrestreint_afficher_erreur_document(404); 75 return; 76 } 77 78 $status = $doc = false; 79 $dossiers_a_exclure = array('nl'); 80 81 // Si c'est dans un sous-dossier explicitement utilisé pour autre chose que les documents 82 // (exemple : les newsletters) 83 // et bien on ne teste pas l'accès 84 if (preg_match('%^(' . join('|', $dossiers_a_exclure) . ')/%', $f)){ 85 $status = 200; 86 } 87 else { 88 $where = "documents.fichier=".sql_quote(set_spip_doc($file)) 89 . ($id_document ? " AND documents.id_document=".intval($id_document): ''); 90 spip_log($where,'dbg'); 91 92 $doc = sql_fetsel("documents.id_document, documents.titre, documents.fichier, types.mime_type, types.inclus, documents.extension", "spip_documents AS documents LEFT JOIN spip_types_documents AS types ON documents.extension=types.extension",$where); 93 spip_log($doc,'dbg'); 94 if (!$doc) { 95 $status = 404; 96 } 97 else { 98 99 // ETag pour gerer le status 304 100 $ETag = md5($file . ': '. filemtime($file)); 101 if (isset($_SERVER['HTTP_IF_NONE_MATCH']) 102 AND $_SERVER['HTTP_IF_NONE_MATCH'] == $ETag) { 103 http_status(304); // Not modified 104 exit; 105 } 106 else { 107 header('ETag: '.$ETag); 108 } 109 110 // 111 // Verifier les droits de lecture du document 112 113 // en controlant la cle passee en argument si elle est dispo 114 // (perf issue : toutes les urls ont en principe cette cle fournie dans la page au moment du calcul de la page) 115 if ($cle){ 116 include_spip('inc/securiser_action'); 117 if (!verifier_cle_action($doc['id_document'].','.$f, $cle)) { 118 spip_log("acces interdit $cle erronee"); 119 $status = 403; 120 } 121 } 122 // en verifiant le droit explicitement sinon, plus lent ! 123 else { 124 if (!function_exists("autoriser")) 125 include_spip("inc/autoriser"); 126 if (!autoriser('voir', 'document', $doc['id_document'])) { 127 $status = 403; 128 spip_log("acces interdit $cle erronee"); 129 } 130 } 131 } 132 } 133 134 135 switch($status) { 136 137 case 403: 138 accesrestreint_afficher_erreur_document(403); 139 break; 140 141 case 404: 142 accesrestreint_afficher_erreur_document(404); 143 break; 144 145 default: 146 header("Content-Type: ". $doc['mime_type']); 147 // pour les images ne pas passer en attachment 148 // sinon, lorsqu'on pointe directement sur leur adresse, 149 // le navigateur les downloade au lieu de les afficher 150 151 if ($doc['inclus']=='non') { 152 153 $f = basename($file); 154 // ce content-type est necessaire pour eviter des corruptions de zip dans ie6 155 header('Content-Type: application/octet-stream'); 156 157 header("Content-Disposition: attachment; filename=\"$f\";"); 158 header("Content-Transfer-Encoding: binary"); 159 160 // fix for IE catching or PHP bug issue 161 header("Pragma: public"); 162 header("Expires: 0"); // set expiration time 163 header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); 164 165 } 166 else { 167 header("Expires: 3600"); // set expiration time 168 } 169 170 if ($cl = filesize($file)) 171 header("Content-Length: ". $cl); 172 173 readfile($file); 174 break; 175 } 176 65 // Insécurisé ( chemin ../ ou absolu ) : 403 66 if ($Document->est_insecurise()) { 67 $Document->status = 403; 68 accesrestreint_afficher_document_selon_status($Document); 69 return; 70 } 71 72 // inexistant ou non lisible : 404 73 if (!$Document->est_existant() OR !$Document->est_lisible()) { 74 $Document->status = 404; 75 accesrestreint_afficher_document_selon_status($Document); 76 return; 77 } 78 79 /** 80 * Liste les sous répertoires de IMG qui donnent accès aux fichiers 81 * systématiquement (sans autorisations particulières). 82 **/ 83 $repertoires_autorises = pipeline('accesrestreint_repertoires_toujours_autorises', array('nl')); 84 85 if ($Document->est_dans_repertoire($repertoires_autorises)) { 86 $Document->statut = 200; 87 accesrestreint_afficher_document_selon_status($Document); 88 return; 89 } 90 91 /** 92 * Propose à des plugins de modifier le Document 93 * 94 * Particulièrement pour donner le status http qui convient pour ce document. 95 * 96 * Si ce 'status' est renseigné par un plugin : on affiche le document (ou l'erreur) avec le statut trouvé 97 * 98 * Sinon, il sera calculé automatiquement ensuite : 99 * 100 * - si le document ne fait pas partie de spip_documents : 404 101 * - sinon, test d'autorisation de voir ce document (par clé d'action si indiquée, sinon par l'api autoriser) 102 **/ 103 $Document = pipeline('accesrestreint_pre_vue_document', $Document); 104 105 if ($Document->status) { 106 accesrestreint_afficher_document_selon_status($Document); 107 return; 108 } 109 110 // Un document sans 'status' doit être analysé 111 // Est-ce un document SPIP ? Non = 404 112 $doc = $Document->get_spip_document(); 113 114 if (!$doc) { 115 $Document->status = 404; 116 accesrestreint_afficher_document_selon_status($Document); 117 return; 118 } 119 120 121 // ETag pour gerer le status 304 122 $ETag = $Document->get_Etag(); 123 124 if (isset($_SERVER['HTTP_IF_NONE_MATCH']) 125 AND $_SERVER['HTTP_IF_NONE_MATCH'] == $ETag) { 126 $Document->status = 304; // Not modified 127 accesrestreint_afficher_document_selon_status($Document); 128 exit; 129 } 130 131 header('ETag: ' . $ETag); 132 133 // Verifier les droits de lecture du document 134 if (!$Document->est_autorise()) { 135 $Document->status = 403; 136 } 137 138 // envoyer le document 139 accesrestreint_afficher_document_selon_status($Document); 177 140 } 178 141 … … 196 159 197 160 /** 161 * Envoie le document au navigateur, en fonction de son status http 162 * 163 * @uses accesrestreint_afficher_erreur_document() 164 * @uses accesrestreint_afficher_document() 165 * 166 * @param Accesrestreint_document $Document 167 * @return void 168 **/ 169 function accesrestreint_afficher_document_selon_status(Accesrestreint_document $Document) { 170 switch ($Document->status) { 171 172 case 304: 173 case 403: 174 case 404: 175 accesrestreint_afficher_erreur_document($Document->status); 176 break; 177 178 default: 179 accesrestreint_afficher_document($Document); 180 break; 181 } 182 } 183 184 185 /** 198 186 * Affiche une page indiquant un document introuvable ou interdit 199 187 * … … 206 194 switch ($status) 207 195 { 196 case 304: 197 // not modified : sortir de suite ! 198 http_status(304); 199 exit; 200 208 201 case 403: 209 202 include_spip('inc/minipres'); … … 218 211 } 219 212 } 213 214 /** 215 * Envoie le document au navigateur 216 * 217 * Soit en document attaché, soit en direct 218 * 219 * @param Accesrestreint_document $Document 220 * @return void 221 **/ 222 function accesrestreint_afficher_document(Accesrestreint_document $Document) { 223 224 $chemin_fichier = $Document->get_chemin_complet_fichier(); 225 226 // document décrit dans la table spip_documents ? 227 if ($doc = $Document->get_spip_document()) { 228 if ($doc['mime_type']) { 229 header("Content-Type: " . $doc['mime_type']); 230 } 231 232 // pour les images ne pas passer en attachment 233 // sinon, lorsqu'on pointe directement sur leur adresse, 234 // le navigateur les downloade au lieu de les afficher 235 if ($doc['inclus'] == 'non') { 236 $f = basename($chemin_fichier); 237 // ce content-type est necessaire pour eviter des corruptions de zip dans ie6 238 header('Content-Type: application/octet-stream'); 239 240 header("Content-Disposition: attachment; filename=\"$f\";"); 241 header("Content-Transfer-Encoding: binary"); 242 243 // fix for IE catching or PHP bug issue 244 header("Pragma: public"); 245 header("Expires: 0"); // set expiration time 246 header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); 247 } else { 248 header("Expires: 3600"); // set expiration time 249 } 250 } else { 251 header("Expires: 3600"); // set expiration time 252 } 253 254 255 if ($size = filesize($chemin_fichier)) { 256 header("Content-Length: ". $size); 257 } 258 259 readfile($chemin_fichier); 260 } 261 262 263 /** 264 * Décrit un document qu'un utilisateur cherche à afficher. 265 * 266 * En aura t'il le droit ? 267 **/ 268 class Accesrestreint_document { 269 270 /** 271 * Identifiant du document demandé, si connu 272 * 273 * 0 si document inconnu. 274 * 275 * @var int 276 **/ 277 private $id_document = 0; 278 279 /** 280 * Clé d'action auteur, si connue 281 * 282 * 0 si inconnue 283 * 284 * @var int|string 285 **/ 286 private $cle_action = 0; 287 288 /** 289 * Chemin du fichier demandé, depuis IMG/ 290 * 291 * @var string 292 **/ 293 private $_fichier = ""; 294 295 /** 296 * Status HTTP à utiliser pour ce document 297 * 298 * @var string|int 299 **/ 300 public $status = ""; 301 302 303 /** 304 * Constructeur 305 * 306 * @param int $_fichier 307 * Chemin du fichier demandé, depuis IMG/ 308 * @param int $id_document 309 * Identifiant du document, si connu 310 * @param int|string $cle_action 311 * Clé d'action auteur, si connue 312 * @return 313 **/ 314 public function __construct($_fichier, $id_document = 0, $cle_action = 0) { 315 $this->_fichier = $_fichier; 316 $this->id_document = $id_document; 317 $this->cle_action = $cle_action; 318 } 319 320 /** 321 * Test si le document demandé vérifie simplement le fonctionnement correct du .htaccess dans IMG posé par Acces Restreint 322 * 323 * @see accesrestreint_gerer_htaccess() 324 * 325 * return bool 326 * True si document de test 327 */ 328 public function est_un_test() { 329 return $this->id_document == 0 330 AND $this->cle_action == 1 331 AND $this->_fichier == "test/.test"; 332 } 333 334 335 /** 336 * Test si le document demandé n'a pas un chemin d'accès sécurisé 337 * 338 * Par exemple avec ../ ou url absolue http:// 339 * 340 * @return bool 341 * True si le chemin est insécurisé 342 **/ 343 public function est_insecurise() { 344 return (strpos($this->_fichier, '../') !== false) 345 OR (preg_match(',^\w+://,', $this->_fichier)); 346 } 347 348 /** 349 * Test si le fichier existe 350 * 351 * @return bool True si existe 352 **/ 353 public function est_existant() { 354 $chemin_fichier = $this->get_chemin_complet_fichier(); 355 return file_exists($chemin_fichier); 356 } 357 358 /** 359 * Test si le fichier est accessible en lecture 360 * 361 * @return bool True si lisible 362 **/ 363 public function est_lisible() { 364 $chemin_fichier = $this->get_chemin_complet_fichier(); 365 return is_readable($chemin_fichier); 366 } 367 368 369 /** 370 * Test si un document est dans un sous-répertoire à la racine de IMG 371 * 372 * @param string|array $repertoires 373 * Nom d'un ou plusieurs répertoires 374 * @return bool 375 * True si le document est contenu dans un de ces répertoires 376 **/ 377 public function est_dans_repertoire($repertoires) { 378 if (!$repertoires) { 379 return false; 380 } 381 if (is_string($repertoires)) { 382 $repertoires = array($repertoires); 383 } 384 $repertoires = array_filter($repertoires); 385 386 return preg_match('%^(' . implode('|', $repertoires) . ')/%', $this->_fichier); 387 } 388 389 390 /** 391 * Test l'autorisation de voir ce document (spip) 392 * 393 * @return bool True si on peut voir le document 394 **/ 395 public function est_autorise() { 396 $doc = $this->get_spip_document(); 397 if (!$doc) { 398 spip_log("acces interdit, document hors de spip_documents"); 399 return false; 400 } 401 402 // en controlant la cle passee en argument si elle est dispo 403 // (perf issue : toutes les urls ont en principe cette cle fournie dans la page au moment du calcul de la page) 404 if ($this->cle_action){ 405 include_spip('inc/securiser_action'); 406 if (!verifier_cle_action($doc['id_document'].','.$this->_fichier, $this->cle_action)) { 407 spip_log("acces interdit $this->cle_action erronee"); 408 return false; 409 } 410 return true; 411 } 412 413 // en verifiant le droit explicitement sinon, plus lent ! 414 if (!function_exists("autoriser")) { 415 include_spip("inc/autoriser"); 416 } 417 418 if (!autoriser('voir', 'document', $doc['id_document'])) { 419 spip_log("acces interdit, pas autorise a voir le document " . $doc['id_document']); 420 return false; 421 } 422 423 return true; 424 } 425 426 427 /** 428 * Retourne la description du document dans la table spip_documents (et spip_types_documents) 429 * 430 * Seulement si ce fichier est un document dans SPIP 431 * 432 * @return array Description du document dans SPIP 433 **/ 434 public function get_spip_document() { 435 static $doc = null; 436 if (is_null($doc)) { 437 include_spip('inc/documents'); 438 $where = "documents.fichier = ".sql_quote(set_spip_doc($this->get_chemin_complet_fichier())) 439 . ($this->id_document ? " AND documents.id_document=".intval($this->id_document): ''); 440 spip_log($where, 'dbg'); 441 442 $doc = sql_fetsel( 443 "documents.id_document, documents.titre, documents.fichier, types.mime_type, types.inclus, documents.extension", 444 "spip_documents AS documents LEFT JOIN spip_types_documents AS types ON documents.extension=types.extension", 445 $where 446 ); 447 448 spip_log($doc, 'dbg'); 449 if (!$doc) { 450 $doc = array(); 451 } 452 } 453 return $doc; 454 } 455 456 /** 457 * Calcule et retourne le chemin complet du fichier (depuis la racine du site) 458 * 459 * @return string 460 **/ 461 public function get_chemin_complet_fichier() { 462 static $fichier = null; 463 if (is_null($fichier)) { 464 include_spip('inc/documents'); 465 $fichier = get_spip_doc($this->_fichier); 466 spip_log($fichier, 'dbg'); 467 } 468 return $fichier; 469 } 470 471 /** 472 * Calcule et retourne le hash Etag du fichier 473 * 474 * @return string 475 **/ 476 public function get_ETag() { 477 static $ETag = null; 478 if (is_null($ETag)) { 479 $ETag = md5($this->get_chemin_complet_fichier() . ': '. filemtime($this->get_chemin_complet_fichier())); 480 } 481 return $ETag; 482 } 483 484 } -
_plugins_/acces_restreint/trunk/paquet.xml
r84550 r84562 2 2 prefix="accesrestreint" 3 3 categorie="auteur" 4 version="3. 9.3"4 version="3.10.0" 5 5 etat="dev" 6 6 compatibilite="[3.0.0;3.0.*]" … … 32 32 <pipeline nom="autoriser" inclure="inc/accesrestreint_autoriser.php" /> 33 33 34 <!-- IMG/xx/... toujours accessibles en lecture --> 35 <pipeline nom="accesrestreint_repertoires_toujours_autorises" action="" /> 36 <!-- Avant d'analyser si un fichier demandé doit être envoyé au navigateur --> 37 <pipeline nom="accesrestreint_pre_vue_document" action="" /> 38 34 39 <menu nom="zones" titre="accesrestreint:icone_menu_config" parent="menu_publication" icone="images/zone-16.png" /> 35 40
Note: See TracChangeset
for help on using the changeset viewer.