You are here

pfdp.module in Private files download permission 8.2

Same filename and directory in other branches
  1. 3.x pfdp.module

Implements the main module function and generic helper functions.

File

pfdp.module
View source
<?php

/**
 * @file
 * Implements the main module function and generic helper functions.
 */
use Drupal\pfdp\Entity\DirectoryEntity;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

/**
 * Returns a properly formatted user string to be used in a log line.
 */
function pfdp_get_user_log_details($user) {
  if (!$user) {
    return NULL;
  }
  return $user
    ->id() . ' (' . ($user
    ->getAccountName() ? $user
    ->getAccountName() : '-') . ', ' . ($user
    ->getDisplayName() ? $user
    ->getDisplayName() : '-') . ')';
}

/**
 * Returns an array of users with no '0' values, as '0' means 'unchecked'.
 */
function pfdp_get_proper_user_array($users) {
  while (in_array('0', $users)) {
    $k = array_search('0', $users);
    unset($users[$k]);
  }
  return $users;
}

/**
 * Returns a proper array to be used for downloads.
 */
function pfdp_get_download_headers($uri) {
  $settings = \Drupal::config('pfdp.settings');

  //
  if ($settings
    ->get('attachment_mode')) {
    return [
      'Content-Type' => \Drupal::service('file.mime_type.guesser')
        ->guess($uri),
      'Content-Disposition' => 'attachment; filename=' . \Drupal::service('file_system')
        ->basename($uri),
    ];
  }
  else {
    return [
      'Content-Type' => \Drupal::service('file.mime_type.guesser')
        ->guess($uri),
      'Content-Disposition' => 'inline',
    ];
  }
}

/**
 * Forces a file to be downloaded to the browser.
 */
function pfdp_force_download($uri, $uri_download_headers) {

  // Create a response here and send it to the browser.
  $file = new BinaryFileResponse($uri, 200, $uri_download_headers, false);
  $file
    ->send();

  // Exit to avoid duplication of HTTP transmission.
  exit;
}

/**
 * {@inheritdoc}
 */
function pfdp_file_download($uri) {
  $settings = \Drupal::config('pfdp.settings');
  $logger = \Drupal::logger('pfdp');
  $user = \Drupal::currentUser();

  // Check if $uri is valid.
  if ('://' === mb_substr($uri, -3, 3) || is_dir($uri)) {
    $logger
      ->warning('Invalid uri: "%uri".', [
      '%uri' => $uri,
    ]);
    return -1;
  }

  // Retrieve the download headers for $uri.
  $uri_download_headers = pfdp_get_download_headers($uri);

  // Check if the user may bypass permission restrictions.
  if ($user
    ->hasPermission('bypass pfdp')) {
    if ($settings
      ->get('debug_mode')) {
      $logger
        ->info('User %user granted permission to download uri "%uri".', [
        '%user' => pfdp_get_user_log_details($user),
        '%uri' => $uri,
      ]);
    }
    return $settings
      ->get('override_mode') ? pfdp_force_download($uri, $uri_download_headers) : $uri_download_headers;
  }
  elseif ($user
    ->hasPermission('bypass pfdp for temporary files') && 'temporary://' === mb_substr($uri, 0, 12)) {
    if ($settings
      ->get('debug_mode')) {
      $logger
        ->info('User %user granted permission to download uri "%uri".', [
        '%user' => pfdp_get_user_log_details($user),
        '%uri' => $uri,
      ]);
    }
    return $settings
      ->get('override_mode') ? pfdp_force_download($uri, $uri_download_headers) : $uri_download_headers;
  }
  else {

    // Extract the path from $uri, removing the protocol prefix and the file name.
    $uri_path = array_slice(explode('/', $uri), 2, -1);

    // Add a leading slash to $uri_path.
    $uri_path = '/' . implode('/', $uri_path);

    // Find the directory which best matches $uri_path.
    $best_matching_length = 0;
    $best_matching_directory = NULL;
    foreach (DirectoryEntity::loadMultiple() as $directory) {

      // Search for the best matching substring.
      $directory_path = $directory->path;
      if (0 === stripos($uri_path, $directory_path)) {
        if ($best_matching_length < mb_strlen($directory_path)) {
          $best_matching_length = mb_strlen($directory_path);
          $best_matching_directory = $directory;
        }
      }
    }
    if (NULL != $best_matching_directory) {

      // Check if this module should ignore the call.
      if ($best_matching_directory->bypass) {
        return NULL;
      }

      // Check if the file owner is allowed to access $uri.
      if ($best_matching_directory->grant_file_owners) {
        $files = \Drupal::entityTypeManager()
          ->getStorage('file')
          ->loadByProperties([
          'uri' => $uri,
        ]);
        if (!empty($files)) {
          $file = array_shift($files);
          if ($file
            ->getOwnerId() == $user
            ->id()) {
            if ($settings
              ->get('debug_mode')) {
              $logger
                ->info('User %user granted permission to download uri "%uri".', [
                '%user' => pfdp_get_user_log_details($user),
                '%uri' => $uri,
              ]);
            }
            return $settings
              ->get('override_mode') ? pfdp_force_download($uri, $uri_download_headers) : $uri_download_headers;
          }
        }
      }

      // Evaluate user and role permissions and optionally allow access to $uri.
      if ($settings
        ->get('by_user_checks')) {
        if (in_array($user
          ->id(), pfdp_get_proper_user_array($best_matching_directory->users))) {
          if ($settings
            ->get('debug_mode')) {
            $logger
              ->info('User %user granted permission to download uri "%uri".', [
              '%user' => pfdp_get_user_log_details($user),
              '%uri' => $uri,
            ]);
          }
          return $settings
            ->get('override_mode') ? pfdp_force_download($uri, $uri_download_headers) : $uri_download_headers;
        }
      }
      foreach ($user
        ->getRoles() as $rid) {
        if (in_array($rid, $best_matching_directory->roles)) {
          if ($settings
            ->get('debug_mode')) {
            $logger
              ->info('User %user granted permission to download uri "%uri".', [
              '%user' => pfdp_get_user_log_details($user),
              '%uri' => $uri,
            ]);
          }
          return $settings
            ->get('override_mode') ? pfdp_force_download($uri, $uri_download_headers) : $uri_download_headers;
        }
      }
    }
  }

  // By default, deny access.
  if ($settings
    ->get('debug_mode')) {
    $logger
      ->warning('User %user denied permission to download uri "%uri".', [
      '%user' => pfdp_get_user_log_details($user),
      '%uri' => $uri,
    ]);
  }
  return -1;
}

Functions

Namesort descending Description
pfdp_file_download
pfdp_force_download Forces a file to be downloaded to the browser.
pfdp_get_download_headers Returns a proper array to be used for downloads.
pfdp_get_proper_user_array Returns an array of users with no '0' values, as '0' means 'unchecked'.
pfdp_get_user_log_details Returns a properly formatted user string to be used in a log line.