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") ??