We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 23214
    • 36 Posts
    I’m glad you’ve found it useful.

    Keep me posted if you make any changes, user group checking could be very useful.
      • 27672
      • 168 Posts
      okay, here’s what i came up with. the logic is:

      * a user only needs to be logged in to view files under /assets/filemanager/, which is controlled via resource access, on the page where the snippet is actually called.

      * to view files in /assets/filemanager/xyz/, the user must belong to a web user group named ’(wug) FileManager_xyz’, where ’xyz’ is the directory name.

      * the ’(wug) FileManager_’ piece is changeable, it’s a variable in the "settings" section of the snippet, but all permissions for this must use the same prefix, unless you change up the code.

      * this allows access to all files within /assets/filemanager/xyz/, including sub-directories (there is no second level of checking), so once you have access to level 2, you have access to level 3, level 4, etc

      <?php
      /* filemanager_retreiver snippet
       * Andrew Pettican November 2010
       *  - modified by Greg Titcomb 2010
       * 
       * This script retreives the requested file from assets/filemanager/ directory.
       * 
       * GET Parameters
       * - fd (File Descriptor)
       *   The path to the requested file. NB: this must be a file which exists in the assets/filemanager/ directory, but 
       *   it can be supplied in pretty much any format:
       *   Example 1. contacts/joe_bloggs.jpg
       *   Example 2. assets/filemanager/contacts/joe_bloggs.jpg
       *   Example 3. /home/server/public_html/informationcentre/assets/filemanager/contacts/joe_bloggs.jpg
       *   Example 4. http://localhost/assets/filemanager/contacts/joe_bloggs.jpg
       *
       * - dl (Download Flag, optional, default: false)
       *   Identifies if the file should be supplied to the browser as a download or inline, to the browser page.
       */
      
      mb_internal_encoding("UTF-8");
      $output = "";
      
      #########################
      ## Modify since $modx->getOption() is not available in MODx 1.0.4 (Evo)
      #########################
      $getOptionsEvo = array();
      $getOptionsEvo['fd'] = (isset($_REQUEST['fd'])) ? $_REQUEST['fd'] : false;
      $getOptionsEvo['dl'] = (isset($_REQUEST['dl'])) ? $_REQUEST['dl'] : false;
      
      ## FETCH PARAMETERS
      ## Leading '/' '\' '<' '>' '$' and '.' chars will be removed from the requested path
      //$file_path = ltrim($modx->getOption('fd',$_REQUEST,false), "/\\.<>$");
      $file_path = ltrim($getOptionsEvo['fd'], "/\\.<>$");
      
      ## FLAG INDICATING IF THIS FILE SHOULD LOAD AS A DOWNLOAD
      //$file_download = ($modx->getOption('dl',$_REQUEST,false) == "1");
      $file_download = ($getOptionsEvo['dl'] == "1");
      
      ## SETTINGS
      $filemanager_path = "assets/filemanager/";
      $base_path = $modx->config['base_path'].$filemanager_path;
      $base_url = $modx->config['site_url'].$filemanager_path;
      $webusergroup_prefix = '(wug) FileManager_';
      $debug_mode = FALSE;
      
      ## ACCEPTED FILE-TYPES AND THEIR MIME'S
      ## See: http://www.w3schools.com/media/media_mimeref.asp
      ## See: http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types
      $accepted_file_types = array();
      
      ## ALLOWED FILE-TYPES
      $accepted_file_types['zip'] = "application/zip";
      $accepted_file_types['pdf'] = "application/pdf";
      $accepted_file_types['doc'] = "application/msword";
      
      ## DEBUG
      if($debug_mode){
      	$output .= "<!-- Path/url requested : '$file_path' -->\n";
      }
      
      #########################
      ## VALIDATION - STAGE 1
      ## Cut $filemanager_path, $base_path or $base_url from the start of the file path if it exists
      #########################
      $filemanager_path_len = mb_strlen($filemanager_path);
      if(mb_strpos($file_path, $filemanager_path) === 0){
      	$file_path = mb_substr($file_path, mb_strlen($filemanager_path));
      }
      
      if(mb_strpos("/".$file_path, $base_path) === 0){
      	$file_path = mb_substr("/".$file_path, mb_strlen($base_path));
      }
      
      if(mb_strpos($file_path, $base_url) === 0){
      	$file_path = mb_substr($file_path, mb_strlen($base_url));
      }
      
      if($debug_mode){
      	$output .= "<!-- (V1) Local path requested : '$file_path' -->\n";
      }
      
      #########################
      ## VALIDATION - STAGE 2
      ## Determine the real path to the file, if the real path is different to the actual path, it could be v dangerous:
      ## e.g. assets/filemanager/../../../../restricted/dir/above/web/root.jpg
      #########################
      $file_path = $base_path.$file_path;
      $real_path = realpath($file_path);
      $validation_passed = ($real_path == $file_path);
      if($debug_mode){
      	$output .= "<!-- (V2) Real Path Check : '$real_path' == '".$file_path."' => ".($validation_passed ? "Y" : "N")." -->\n";
      }
      
      #########################
      ## VALIDATION - STAGE 3
      ## Sanity check, ensure the $real_path begins with $base_path
      #########################
      $validation_passed = ($validation_passed && ($base_path == mb_substr($real_path, 0, mb_strlen($base_path))));
      if($debug_mode){
      	$output .= "<!-- (V3) Sanity Path Check : '$base_path' == '".mb_substr($real_path, 0, mb_strlen($base_path))."' => ".($validation_passed ? "Y" : "N")." -->\n";
      }
      
      #########################
      ## FETCH SOME FILE INFO FROM THE PATH
      #########################
      $file_info = pathinfo($real_path);
      $file_name = $file_info['basename'];
      $file_ext = mb_strtolower($file_info['extension']);
      $file_mime = "application/octet-stream";
      
      #########################
      ## VALIDATION - STAGE 4
      ## Ensure the file exists and has an accepted file extension
      #########################
      $validation_passed = ($validation_passed && $real_path != "" && is_file($real_path) && array_key_exists($file_ext, $accepted_file_types));
      if($validation_passed){
      	$file_mime = $accepted_file_types[$file_ext];
      }
      
      #########################
      ## VALIDATION - STATE 5
      ## User Group Authentication
      #########################
      function get_string_between($string, $start, $end){
      	$string = " ".$string;
      	$ini = strpos($string,$start);
      	if ($ini == 0) return "";
      	$ini += strlen($start);
      	$len = strpos($string,$end,$ini) - $ini;
      	return substr($string,$ini,$len);
      }
      
      $file_sub_path = get_string_between($file_path, "/filemanager/", "/".$file_name);
      
      ## IF the there is a sub-path we need to check for user group, 
      ## ELSE we are just care that the user is logged in
      if(!empty($file_sub_path)){
      	## get the first directory under /assets/filemanaer/
      	$subPathExplodeArray = explode("/", $file_sub_path);
          $theSubPath = $subPathExplodeArray[0];
      	
      	## now look for a web user group named the same thing
          $isAuthorizedToViewFile = $modx->isMemberOfWebGroup(array($webusergroup_prefix.$theSubPath));
          $validation_passed = ($validation_passed && $isAuthorizedToViewFile);
      }
      
      #########################
      ## DEBUG
      #########################
      if($debug_mode){
      	$output .= "
      	<!--
      		file_path : '$file_path'
      		real_path : '$real_path'
      		file_name : '$file_name'
      		file_ext : '$file_ext'
      		file_mime : '$file_mime'
      		validation_passed : '".($validation_passed ? "Y" : "N")."'
      	-->
      	";
      }
      
      #########################
      ## CONTINUE?
      ## $validation_passed = FALSE; // Debug
      #########################
      if($validation_passed){
      	## STREAMING THE FILE MAY TAKE A WHILE
      	set_time_limit(0);
      	
      	## STREAM!
      	header('Content-Description: File Transfer');
      	header('Content-Type: '.$file_mime);
      	header('Content-Disposition: '.($file_download ? 'attachment' : 'inline').'; filename='.$file_name);
      	header('Content-Transfer-Encoding: binary');
      	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
      	header('Cache-Control: no-store, no-cache, must-revalidate');
      	header('Pragma: public');
      	header('Content-Length: ' . filesize($real_path));
      	ob_clean();
      	ob_end_flush();
      	readfile($real_path);
      	exit();
      }else{
      	$output .= "Cannot read file";
      }
      
      return $output;
      ?>


      i’m not sure if $modx->isMemberOfWebGroup() is available in Revo... it’s probably different because of the different authentication logic in revo, so that part would need to be changed. basically you’d just need to change this line:
      $isAuthorizedToViewFile = $modx->isMemberOfWebGroup(array($webusergroup_prefix.$theSubPath));

      to the Revo equivalent, which returns a true/false value.

      anyone see any holes in my added authentication stuff (my stuff is mostly in "VALIDATION - STATE 5") ??
        • 36648
        • 19 Posts
        Did anyone got it working with revolution ?
        I'm trying to use it but still gets "Cannot read file", debug mode doesnt output more ...

        This is a really usefull snippet !