One folder to be accessible by one user

One solution might be to directly restrict access to the file on the server, but utilize a url rewrite to display the content — only if the id matches in the request. Obviously this doesn’t answer every question in the scenario but it does provide a proof-of-concept to indirectly convert a url into a file.

When you upload a file you could store a reference in the user metadata instead of having to track custom folders of content.


This essentially provides access to this file:

http://example.dev/wp-content/uploads/2015/12/8-1200x675.jpg

If user ID 1 is requesting access and is logged in

http://example.dev/api/file/1/2015/12/8-1200x675.jpg

<?php

if ( ! class_exists( 'FileRequest' ) ):

    class FileRequest {
        const ENDPOINT_QUERY_NAME  = 'api/file';
        const ENDPOINT_QUERY_PARAM = '__api_file';

        // WordPress hooks

        public function init() {
            add_filter( 'query_vars', array ( $this, 'add_query_vars' ), 0 );
            add_action( 'parse_request', array ( $this, 'sniff_requests' ), 0 );
            add_action( 'init', array ( $this, 'add_endpoint' ), 0 );
        }

        // Add public query vars

        public function add_query_vars( $vars ) {

            // add all the things we know we'll use

            $vars[] = static::ENDPOINT_QUERY_PARAM;
            $vars[] = 'file';
            $vars[] = 'user';

            return $vars;
        }

        // Add API Endpoint

        public function add_endpoint() {
            add_rewrite_rule( '^' . static::ENDPOINT_QUERY_NAME . '/([^/]*)/(\S+)/?', 'index.php?' . static::ENDPOINT_QUERY_PARAM . '=1&user=$matches[1]&file=$matches[2]', 'top' );

            //////////////////////////////////
            flush_rewrite_rules( false ); //// <---------- REMOVE THIS WHEN DONE
            //////////////////////////////////
        }

        // Sniff Requests

        public function sniff_requests( $wp_query ) {
            global $wp;

            if ( isset(
                $wp->query_vars[ static::ENDPOINT_QUERY_PARAM ],
                $wp->query_vars[ 'file' ],
                $wp->query_vars[ 'user' ] ) ) {
                $this->handle_request(); // handle it
            }
        }

        // Handle Requests

        protected function handle_request() {
            global $wp;

            $file = $wp->query_vars[ 'file' ];
            $user = $wp->query_vars[ 'user' ];

            // make sure the request ID is the same as the logged in user

            if ( ''.get_current_user_id() !== $user ){
                wp_send_json_error( array (
                    'message' => 'Unauthorized',
                    'error' => '401',
                ) );
            }
            else {

                // add the file request to the uploads directory
                $upload_dir = wp_upload_dir();
                $path = $upload_dir['basedir']."https://wordpress.stackexchange.com/".$file;

                // make sure url is readable -- if not, stop!
                if ( ! is_readable( $path ) ) {
                    wp_send_json_error( array (
                        'message' => 'Bad Request',
                        'error' => '400',
                    ) );
                }

                // get files contents
                $file_content = file_get_contents( $path );

                // get the mime type for headers
                $type  = get_post_mime_type( $file_content );

                // output headers
                nocache_headers();
                header( "Content-type: $type;" );
                header( "Content-Length: " . strlen( $file_content ) );

                // output file data
                echo $file_content;
            }
            die();
        }
    }

    $wpFileRequest = new FileRequest();
    $wpFileRequest->init();

endif; // FileRequest