Skip to content

Instantly share code, notes, and snippets.

@nullvariable
Created September 29, 2020 18:43
Show Gist options
  • Save nullvariable/2bad7687be2cf2897d86aebc82003d3f to your computer and use it in GitHub Desktop.
Save nullvariable/2bad7687be2cf2897d86aebc82003d3f to your computer and use it in GitHub Desktop.
Private Files Example for Pantheon
<?php
/**
* Plugin Name: Example Private Files
* Author: Doug Cone
* Description: Auth for Pantheon Private files
* Version: 0.1.0
*/
/**
* This doesn't protect direct access, you must move your files to a folder not accessible to the public, but accessible to this script.
* On Pantheon, that location is: `/wp-content/uploads/private` (See docs: https://pantheon.io/docs/private-paths)
*
* You will need to move any files uploaded prior to adding this plugin, or fallback to checking the old upload path.
*
* This plugin is all or nothing, you could likely add a per file option leveraging different filters/hooks and the file meta data.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// location that private files will live in.
define( 'PF_UPLOADS', WP_CONTENT_DIR . '/uploads/private' );
// Modify the upload folder to ensure that files cannot be accessed directly.
add_filter( 'upload_dir', 'pf_upload_dir' );
add_action(
'init',
function () {
add_feed( 'priv_files', 'pf_handle' );
}
);
/**
* Filter the upload directory to our private files location.
*
* @param array $uploads the uploads array, see web/wp-includes/functions.php.
* @return array
*/
function pf_upload_dir( $uploads ) {
$path = str_replace( $uploads['basedir'], '', $uploads['path'] );
$uploads['basedir'] = PF_UPLOADS;
$uploads['path'] = PF_UPLOADS . $path;
$uploads['url'] = site_url( '/priv_files?file=' . $path );
$uploads['baseurl'] = site_url( '/priv_files?file=' );
return $uploads;
}
/**
* Add feed handler. Checks auth status and responds accordingly.
*
* @return void
*/
function pf_handle() {
// Something like `current_user_can( 'manage_options' )` here would restrict all file access to admins. `is_user_logged_in()` would just require a user to be logged in.
if ( true === true ) {
$file_to_check = isset( $_GET['file'] ) ? PF_UPLOADS . sanitize_text_field( $_GET[ 'file' ] ) : '';
if ( file_exists( $file_to_check ) ) {
header( 'Content-Type: ' . mime_content_type( $file_to_check ), true, 200 );
// you could/should add a caching header here to encourage the browser to hold on to this file longer.
readfile( $file_to_check ); // phpcs:ignore
exit;
} else {
global $wp_query;
status_header( 404 );
$wp_query->set_404();
nocache_headers();
$template = get_query_template( '404' );
if ( $template ) {
header( 'Content-Type: text/html' );
include $template;
}
exit;
}
} else {
status_header( 403 );
nocache_headers();
// or maybe `wp_safe_redirect( wp_login_url(), 302, 'Private Files' );` instead.
}
}
@nullvariable
Copy link
Author

Note that on Pantheon static assets don't count towards your traffic, but requests to php scripts like this do.

@twfahey1
Copy link

twfahey1 commented Nov 3, 2020

Awesome, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment