Права доступа к default/files

Главные вкладки

Аватар пользователя gera8774 gera8774 3 июня в 13:52

Всем привет! Делаю полностью закрытый сайт, только для авторизованных пользователей. Для анонимов закрыто абсолютно всё. Но при этом доступно скачивание файлов из папки sites/default/files
$settings['file_private_path'] не помогает. Как закрыть полностью папку sites/default/files от анонимов?

Комментарии

Аватар пользователя gera8774 gera8774 3 июня в 19:09

Вроде бы с этим разобрались, если с нуля делать поле, то всё уходит в приватную папку. Всё работает, просто не так смотрела.
Но вот что делать например с сайтом, где уже достаточно много контента... С полями, у которых уже есть файлы в публичной папке, но которые надо бы скрыть?

Аватар пользователя OldWarrior OldWarrior 3 июня в 19:32

В идеале нужен кастомный модуль, например, реагирующий на хук hook_post_update и/или запускающий батч-процесс по всем сущностям файлов, во время которого бы выполнялся перенос файла в приватную папку + коррекция/обновление URI в таблице файлов.

Но можно попробовать начать с этого: filefield_paths. У него заявляется такая фича как:

Retroactive updates - rename and/or move previously uploaded files.

Это в теории добавляет настройки путей и чекбокс "Retroactive update" в форму настроек поля загрузки файла и при сохранении настроек должно осуществить перенос ранее загруженных файлов по указанному шаблону пути с фиксацией URI в БД.

Просто в тему: https://drupal.stackexchange.com/questions/264496/how-to-make-existing-p...

Аватар пользователя gera8774 gera8774 5 июня в 10:55

Теперь возник другой вопрос. Может сможете подсказать. Настроила приватную папку, всё ок. Настроила поле файл, теперь все загружаемые файлы уходят туда. Админу эти файлы доступны по ссылке, остальным нет.
Но в этой папке есть другая папка, в которую файлы грузятся не с полей, они там создаются программно. И админу ссылка на эти файлы выдает 403
Например сейчас есть папка 2025-06, в ней лежит файл, загруженный при создании материала. Он доступен по ссылке
Также рядом с этой папкой лежит папка mydir, в ней лежат программно сгенерированные файлы. Они по ссылке выдают 403
Права на папки и файлы такие же как у 2025-06

Аватар пользователя OldWarrior OldWarrior 5 июня в 13:36

gera8774 wrote: Например сейчас есть папка 2025-06, в ней лежит файл, загруженный при создании материала. Он доступен по ссылке
Также рядом с этой папкой лежит папка mydir, в ней лежат программно сгенерированные файлы. Они по ссылке выдают 403

Тут всё просто: папка private у вас находится за пределами веб-рут, т.е. недоступна для прямого скачивания из браузера, поскольку не обслуживается веб-сервером. Для этого она (приватная папка), собственно, и нужна. Друпал умеет по запросу "забирать" из этой папки файлы и отдавать как файл соответствующего MIME-типа. Но это потому что он знает, какому запросу (урлу) какой физический приватный файл сопоставлен.

Если вы самостоятельно кладёте в такую папку какой-либо файл, то он не обслуживается Друпалом.

Кстати, уточнение: вы же создали папку за пределами корня сайта, верно?

Аватар пользователя gera8774 gera8774 5 июня в 15:44

Да, верно, папка есть и она за пределами корня. И в нее у меня прекрасно уходят файлы из полей сущностей, и они мне доступны по ссылке. И туда также автоматически генерируются файлы из самописного модуля. И мне бы хотелось иметь к ним доступный для админа url, то есть, чтобы он всё-таки обслуживался друпалом)
У меня что-то типа такого:
function mymodule_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'mydocs') {
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$_doc = new \PhpOffice\PhpWord\TemplateProcessor('private://mydir/templates/template.docx');
$_doc->setValue('Title', $entity->getTitle() );
$PATH = 'private://mydir/file'. $entity->id().'.docx';
$_doc->saveAs($PATH);
}
}

И судя по всему, вот эту часть с сохранением мне надо делать как-то по-другому, чтобы друпал брал этот файл к себе "в подопечные" и заносил в базу
Вместо $_doc->saveAs($PATH); пробую такое:

$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData($_doc, 'private://mydir/file'. $entity->id().'.docx', FileSystemInterface::EXISTS_REPLACE);

Но это выдает ошибку:
TypeError: Drupal\file\FileRepository::writeData(): Argument #1 ($data) must be of type string
Вот и не понимаю, как быть

Аватар пользователя OldWarrior OldWarrior 5 июня в 18:40

gera8774 wrote: И судя по всему, вот эту часть с сохранением мне надо делать как-то по-другому, чтобы друпал брал этот файл к себе "в подопечные" и заносил в базу
Вместо $_doc->saveAs($PATH); пробую такое:

$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData($_doc, 'private://mydir/file'. $entity->id().'.docx', FileSystemInterface::EXISTS_REPLACE);

1. Честно говоря, мне этот выкрутас (применение методов 'file.repository' в вашей реализации) кажется каким-то сомнительным. Но нет времени разбирать-уточнять вопрос в документации. Тем более, что постановка файла под управление Друпала (а-ля managed files) несколько изменилась после 9-ки. Я навскидку уже не помню. Но если хотите - пробуйте как наметили и тогда см. п.2 ниже.

2. Ключевая ошибка здесь то, что вы пытаетесь передавать объект $_doc (который является частным экземпляром класса \PhpOffice\PhpWord\TemplateProcessor как аргумент в метод $fileRepository->writeData. Но этот метод ожидает строку, а не объект.

То есть, чтобы эта функция приняла сгенерированный Word - нужно передать его содержимое именно как строку. Вместе с тем, насколько я понял, в классах \PhpOffice\PhpWord нет готовых методов для экспорта в переменную, только в физический файл.

Поэтому можно попробовать применить какой-то хак, чтобы захватить вывод и перенаправить его в переменную. Например, используя буферизацию PHP и манипуляцию с врэпперами/потоками вывода (в данном случае 'php://output'):

<?php
...
ob_start();
$_doc->saveAs('php://output');
$generated_document ob_get_clean(); 

$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData(generated_document 'private://mydir/file'$entity->id().'.docx'FileSystemInterface::EXISTS_REPLACE);
...
?>
Аватар пользователя gera8774 gera8774 5 июня в 19:02

Собственно всё как обычно. Одно получилось, но дальше всё равно не идет
Сделала вот так:
$uri = $PATH;
$data = file_get_contents($uri);
$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData($data, $uri, FileSystemInterface::EXISTS_REPLACE);

Всё отлично, друпал берет мои файлы, они появиляются в базе
Но админ снова видит 403

Решила продолжить в новой теме.
https://drupal.ru/node/146438
Тут уже далеко ушло от изначального вопроса)

Аватар пользователя OldWarrior OldWarrior 5 июня в 19:11

Ну как бы я вам писал выше, что мне показались сомнительными манипуляции с 'file.repository'

По каким ссылкам админ вообще пытается запросить эти файлы? Какой вид/формат имеют URL'ы таких файлов?

Аватар пользователя gera8774 gera8774 5 июня в 19:28

Вот например ссылка на файл, который я гружу в ноду, и по этой ссылке файл открывается
mysite.ru/system/files/2025-06/1.jpg
А вот ссылка на мой генерируемый файл
mysite.ru/system/files/my-dir/myfile74.docx
Она выдает 403

Вообще было бы здорово обойтись php://output но проблема в том, что я генерирую файл на основе шаблона. И в этом случае есть возможность только сохранять новый полноценный файл. С этим тоже долго разбиралась, и как поняла при использовании PhpOffice\PhpWord\TemplateProcessor невозможно вывести файл через php://output

Аватар пользователя OldWarrior OldWarrior 5 июня в 19:51

1. Я сверился таки с документацией - у вас (вроде бы) верное использование службы 'file.repository', именно она создаёт одновременно и физический файл и его файловую сущность в Друпале. Т.е. тут всё вроде бы верно.

2.

gera8774 wrote: Вообще было бы здорово обойтись php://output но проблема в том, что я генерирую файл на основе шаблона.

Решение с врэппером 'php://output' - просто короткий путь без создания промежуточных файлов. Раз решили вопрос через запись с последующим чтением (а-ля file_get_contents) - ну, пусть так, главное - работает в вашем случае.

3.

gera8774 wrote: А вот ссылка на мой генерируемый файл
mysite.ru/system/files/my-dir/myfile74.docx
Она выдает 403

У меня тут ощущение какого-то подвоха. На всякий случай: откуда вы берёте этот URL? Я бы проверил, какой реально URL имеет файловая сущность с помощью какого-то такого кода:

<?php
// Во-первых, при сохранении получаем объект созданной файловой сущности в $file
$file $fileRepository->writeData($data$uriFileSystemInterface::EXISTS_REPLACE);

// И, например, записываем в журнал Друпала полученный канонический URL файла.
\Drupal::logger('my_module')->info($file->toUrl('canonical', ['absolute' => TRUE])->toString());
?>
Аватар пользователя gera8774 gera8774 6 июня в 11:25

В общем проблема была в том, что помимо самого файла и прав на него, надо еще иметь права на скачивание (для приватных файлов)
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21File%21fi...
Пока получается не очень, но уже лучше, чем было )

Аватар пользователя OldWarrior OldWarrior 6 июня в 11:35

1) У админа должны быть все права по умолчанию. Однако, вы писали, что у админа тоже нет возможности открыть файл по ссылке. Т.е. для админа никаких спец. манипуляций обычно не требуется.

Кстати, какой именно админ? Самый-самый толстый (uid=1)? Просто я когда-то сталкивался с тем, что админы с НЕ uid=1 порой обрабатывались иначе. Подозреваю, что баг каких-то модулей.

2) Что именно получается не очень? Вроде решаете же проблему.

Аватар пользователя gera8774 gera8774 6 июня в 11:51

Админ самый главный, первый. Он тоже получал 403
В общем сейчас всё разрешилось. Добавила вот:
mymodule_file_download(string $uri): ?array {
if (strpos($uri, 'private:') !== 0) {
return NULL;
}

if (!\Drupal::currentUser()->hasPermission('access private files')) {
return -1;
}

$file_repository = \Drupal::service('file.repository');
$file = $file_repository->loadByUri($uri);

$headers = file_get_content_headers($file);
return $headers;
}

Аватар пользователя gera8774 gera8774 3 июня в 19:30

а еще захожу в папку /sites/default/files/feeds и там тоже всё доступно для скачивания. Закрытые для анонимов материалы, импортируемые когда-то через csv лежат там, и их можно скачать неавторизованному пользователю. Поэтому очень хотелось бы как-то иметь возможность закрыть некоторые папки в публичной части