Drupal 7 и Приватные файлы

Аватар пользователя vitok vitok 5 февраля 2011 в 0:33

С чего все началось? С темы которая всех на этом форме мягко говоря удивила: "Прикрепить файлы к статье Joomla". Несмотря на название, само содержимое топика (опуская слово Joomla) было вполне адекватным! А именно поставлена следующая задача.

Задача: Прикрепить к статье файл.

Основные требования:
1. Файл должен загружаться с компьютера автора, в процессе ввода текста. *Хороший пример - этот форум. Тут можно прикрепить файл непосредственно при вводе сообщения.
2. Файл должен быть защищён от скачивания НЕ зарегистрированными пользователями. *не просто скрыта ссылка, а именно защита от скачивания и хотлинков.

Дополнительные (не обязательные, но крайне жалетельные) требования:
1. Если пользователь не загистрирован, то ссылка на файл не исчезает, он просто не может его скачать
2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации
3. Счётчик скачивания файла где-нибудь недалеко от ссылки "скачать"
4. Статика в админке, "кто, когда и что" скачал (это было бы вообе шикарно!)

Подопытным для всего этого как не странно стала не Joomla, а Drupal 7.

Сама задача для друпала не хитрая "Прикрепить к статье (материалу) файл" нам позволяет замечательная фишка семерки "Field API" т.е. по сути первый пункт основных требований выполнили. Идем дальше:

Quote:

Файл должен быть защищён от скачивания НЕ зарегистрированными пользователями. *не просто скрыта ссылка, а именно защита от скачивания и хотлинков.

Это для меня был самый интересный вопрос, поэтому на нем я и заострю внимание.

Для начала нам надо настроить "файловую систему" первым делом надо создать приватную папку, где будут хранится все наши приватные файлы. Я сделал это следующим образом:

cd /путь/к/папке/с/установленным/друпалом/sites/default
chmod 755 .
mkdir files-private
chmod 555 .

Для тех кто не в теме, расшифровываю. Сначала папке /sites/default задал права на запись, создал в ней папку, затем вернул права на "только доступ". поскольку папку /sites/default/files по умолчанию отдана апачу, я решил не создавать приватную папку в ней, чтоб еще не писать правила для запрета апача гулять туда (хотя подозреваю что оповестить его о запрете все равно придется).
После этой процедуры вернемся в админку друпала, а именно идем по адресу admin/config/media/file-system и в настройках "Приватный путь файловой системы" вписываем только что созданную нами папку sites/default/files-private после чего сохраняем настройки.
Далее идем по направлению Структура » Типы содержимого » наш тестовый тип » Управлять полями создаем поле типа "файл" (для изображений аналогично) и в настройках указываем Хранилище Личные файлы сохраняем и смотрим что получилось.

Я минут 5 втыкал что за фигня получилась, вроде как файлы приватные загружаются в одну структуру, отдаются из другой, но отдаются всем подряд. После чего я начал гуглить... и первым делом нагуглил следующее:

Quote:

Setting the download method to "Private" doesn't have any effect by itself...
what the private download setting does do, is fire off an extra event, hook_download()

Тут кстати автор опЯчататься. На самом деле хук называется hook_file_download
А вообщем это все можно перевести как "Сама по себе приватная файловая система ничего не делает т.е. по сути это api и которая дает один (может и больше, не интересовался) хук с помощью которого мы можем управлять скачиванием этих файлов. После чего я набросал модуль из двух хуков, один создает новое право для ролей hook_permission, а второй тот самый hook_file_download в котором указано что если у пользователя нет права на просмотр приватных файлов слать его лесом! Получившийся модуль искать в прикрепленных файлах.

После активации модуля идем на страницу прав пользователей admin/people/permissions и выдаем новое правило "View private files" кому положено качать приватные файлы т.е. зарегистрированным пользователям. На этом собственно второй пункт выполнен, все анонимы идут лесом.

Теперь идем по дополнительным пунктам:

Quote:

1. Если пользователь не загистрирован, то ссылка на файл не исчезает, он просто не может его скачать

Уже сделано.

Quote:

2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации

В нашем модуле надо просто $uri прировать не к -1 а к странице регистрации.

3 и 4 пункты решает модуль Download Count вот только портировать под 7-ку еще надо его. А вот это Download Tools для семерки, но сам его не тестил.

Ну в общем-то тема раскрыта, комментируем.

0 Thanks

Комментарии

Аватар пользователя vitok vitok 5 февраля 2011 в 1:24

Я наверно это все дольше описывал чем делал. Кстати после всего этого у меня возник вопрос, а почему этот функционал в ядро не запихнули (я про код из модуля)? Я б даже сказал в идеале для каждого нового созданного поля с приватным хранилищем ядро должно было создаваться право доступа, чтоб можно было тонко разграничивать приватные файлы между ролями и группами (OG)

Аватар пользователя vitok vitok 5 февраля 2011 в 2:02
"RxB" wrote:

Приватными файлами почти не пользуются.

Я вот тоже не представляю где бы мне это пригодилось, но сама идея понравилась, поэтому делал из спортивного интереса =)

Аватар пользователя xxandeadxx xxandeadxx 5 февраля 2011 в 10:21
"vitok" wrote:

Я вот тоже не представляю где бы мне это пригодилось

платный контент например, заплатил - получил возможность скачивать. статья интересная, спасибо

Аватар пользователя vitok vitok 5 февраля 2011 в 11:11
"xxandeadxx" wrote:
"vitok" wrote:

Я вот тоже не представляю где бы мне это пригодилось

платный контент например, заплатил - получил возможность скачивать.

Это да, или даже к примеру на не малом корпаративном сайте приватные файлы не помешали бы, да или для той-же социалки и.т.д. и.т.п. Я просто имел введу что в ТЗ еще не видел пункта о приватных файлах.

Аватар пользователя vitok vitok 14 февраля 2011 в 12:33

Хе-хе) я попал на главную)
Кстати заметил баг, что при включенном кешировании анонимов не редиректит на страницу "доступ запрещен" в место этого рендрится какая-то страница с иероглифами.

Аватар пользователя mak-vardugin mak-vardugin 16 февраля 2011 в 4:01

Ну вот, а старуха Шапокляк говорила что хорошими делами прославиться нельзя, выходит врала корга старая.

Аватар пользователя Siniy Siniy 5 мая 2011 в 21:24

«После чего я набросал модуль из двух хуков»

А где модуль можно посмотреть, тыкните носом, пожалуйста.

Аватар пользователя Siniy Siniy 11 мая 2011 в 7:44
vitok wrote:
Quote:

2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации

В нашем модуле надо просто $uri прировать не к -1 а к странице регистрации.

Можете привести код для примера?

<?php
  
if (!user_access('view private files')) {
    
$uri = -1;
  }
?>

Что здесь надо поставить?

Аватар пользователя Siniy Siniy 11 мая 2011 в 23:08

Решил этот вопрос созданием страницы с инструкцией и ссылкой на регистрацию и указанием ея, как страницу ошибки 403 в admin/config/system/site-information.
Ибо, если править код модуля, то файл становится доступным.

Аватар пользователя vitok vitok 13 мая 2011 в 18:54
"Siniy" wrote:

Угу, я в топике описывал эту часть чисто теоретически, когда сам попробовал, заметил тоже самое.

Аватар пользователя Siniy Siniy 16 мая 2011 в 11:23

А чем всё решилось в деле счётчика, логирования загрузок?
Download Count до семёрки ещё не дорос.

Аватар пользователя vitok vitok 20 мая 2011 в 23:49

Siniy ничем, передо мной выше указанные задачи не стояли, я чисто из "спортивного" интереса занялся этими вопросами. Счетчик портировать еще надо, а это не ко мне)

Аватар пользователя andruculla andruculla 7 июля 2011 в 1:44

Спасибо тебе, друг.
А как можно использовать данный способ при загрузке файла через фтп вручную? Хочу получить на сайте аналогичную ссылку на файл. (ограничения хостинга)

Разобрался сам.

Аватар пользователя sistemanipel sistemanipel 10 ноября 2011 в 1:34

vitok
Спасибо за помощь, закрыл доступ к скачиванию анонимами, кликает по ссылке попадает на user/register но в строке браузера висит путь system/files/price_list.xls а вверху, над формой регистрации ошибка:

Notice: Undefined index: type в функции metatags_quick_field_access() (строка 119 в файле /home/sozida/public_html/sites/all/modules/metatags_quick/metatags_quick.module).

Может подскажет кто, как устранить? Я почти "0" в этом деле, сижу сам леплю из того что нахожу в сети.
Спасибо

Аватар пользователя GUN1A GUN1A 9 февраля 2013 в 22:15

Цитата:

2. Если не зарегистрированный пользователь пытается скачать файл - система его уведомляет о необходимости регистрации

Народ так я и не понял как эту проблему решить

Аватар пользователя Dusty79 Dusty79 8 ноября в 3:00

Да не сочтётся за некропостинг, ибо плодить темы-близнецы также неразумно.
Я уж было обрадовался, когда на этот топик набрёл, думал полезного чего найду, да не тут-то было. :-( Аттача след простыл.
Прошу помощи, поскольку нуб.
Дело такое. Необходимо регулировать доступ к приватным файлам не по ролям, а индивидуально по пользователям. Файлы типа "Image", множественно добавляемые в материал.
Для такого дела добавил типу файла поле "field_access" с сылкой на сущность пользователя, со множественным значением. При редактировании материала могу отметить тех пользователей, кому будет доступно скачивание изображения. Администраторам и хозяину файла тоже можно. Остальных надо слать.
Копал интернеты и по своему скудоумию состряпал нечто. Возникла засада. Не умею обратиться и прочитать значение добавленного мною к изображению поля. Знающие люди, подскажите, пожалуйста.
Получившийся код привожу ниже.


<?php

function user_file_access_reference_file_download($uri) {
    global 
$user;
    foreach (
$user->roles as $role) {
        if ((
$role == 'administrator') || ($role == 'editor')) {
            return array(
'Content-Type' => file_get_mimetype($uri));
        } elseif (isset( 
А вот тут засада ->field_access['und'])) {
            
$qnty_users countА вот тут засада ->field_access['und']);
            for ( 
$i 0$i $qnty_users$i++ ) {
                if ( 
А вот тут засада ->field_access['und'][$i]['target_id'] == $user->uid) {
                    return array(
'Content-Type' => file_get_mimetype($uri));
                }
            }
        } else {
            return -
1;
        }
    }
}

?>
Аватар пользователя Dusty79 Dusty79 8 ноября в 8:23

Есть, но он не подходит. Во-первых нельзя назначить права на каждый отдельный файл в поле и модуль сыплет ошибками, если значений поля больше одного. Во-вторых, при использовании uLogin вместо имён он отображает идентификаторы, а в-третьих, в моём случае галочки на разрешение скачивания должен ставить автор материала/хозяин файла.

Аватар пользователя Dusty79 Dusty79 8 ноября в 11:25

Дополню, чтоб нагляднее было, что-ли...
При редактировании файла:

В профиле пользователя живёт такая вкладка со списком изображений, доступных ему для скачивания:

По прямым ссылкам должен быть отлуп. Вот чего я хочу добиться.

Аватар пользователя Dusty79 Dusty79 9 ноября в 2:03

Я почти добился своего.


<?php

function user_file_access_reference_file_download($uri) {
    
$file file_load_multiple(array(), array('uri' => $uri));
    
$field_values field_get_items('image'$file'field_access');
    
$access_enabled false;
    global 
$user;
    foreach (
$user->roles as $role) {
        if (
$role == 'administrator') {
            
$access_enabled true;
            break;
        }
    }
//    if ($field_values['uid'] == $user->uid) {
//        $access_enabled = true;
//    }
    
if ($access_enabled == false) {
        
$qnty_users count($field_values['field_access']['und']);
        for ( 
$i 0$i $qnty_users$i++ ) {
            if (
$field_values['field_access']['und'][$i]['target_id'] == $user->uid) {
                
$access_enabled true;
                break;
            }
        }
    }
    if (
$access_enabled) {
        
// Access to the file is success.
        
return array('Content-Type' => file_get_mimetype($uri));
    } else {
        
// Access to the file is denied.
        
return -1;
    }
}

?>

Работает, но я не догоняю один момент. Стоит раскомментить строки

<?php
    
if ($field_values['uid'] == $user->uid) {
        
$access_enabled true;
    }
?>

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

Аватар пользователя Dusty79 Dusty79 10 ноября в 2:18

Продолжаю свою борьбу, успехи так себе.
Текущий тестовый вариант:

<?php

/* Implements hook_file_download()
 * administrator - roles have access
 * field_access - entityreference field contains users that have access
*/

function check_accsess($uri) {
    
$access_enabled false;
    
$files file_load_multiple(array(), array('uri' => $uri));
    
// watchdog('MyModule', '<pre>'. print_r($files, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
    
reset($files);
    
$file current($files);
    unset(
$files);
    
// watchdog('MyModule', '<pre>'. print_r($file, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
    
if (isset($file->field_access)) {
        
$access_values $file->field_access;
    } else {
        
watchdog('MyModule''<pre>'print_r('Не файл!'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
        
$access_enabled true;
        return 
$access_enabled;
    }
    
// watchdog('MyModule', '<pre>'. print_r($access_values, TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
    
global $user;
    foreach (
$user->roles as $role) {
        if (
$role == 'administrator') {
            
watchdog('MyModule''<pre>'print_r('Права доступа как Администратору'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
            
$access_enabled true;
            return 
true;
            break;
        }
    }
    
// watchdog('MyModule', '<pre>'. print_r('$file->uid', TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
    
if ($access_enabled == false) {
        if (
$file->uid == $user->uid) {
            
watchdog('MyModule''<pre>'print_r('Права доступа как владельцу'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
            
$access_enabled true;
            return 
true;
        }
    }
    if (
$access_enabled == false) {
        
watchdog('MyModule''<pre>'print_r('Проверка прав доступа по списку пользователей'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
        foreach(
$access_values['und'] as $key_arr => $val_arr){
            
// watchdog('MyModule', '<pre>'. print_r($val_arr['target_id'], TRUE) .'</pre>', array(), WATCHDOG_INFO, NULL);
            
if ($val_arr['target_id'] == $user->uid) {
                
watchdog('MyModule''<pre>'print_r('Права доступа предоставлены по списку пользователей'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
                
$access_enabled true;
                return 
true;
                break;
            }
        }
    }
    return 
$access_enabled;
}
function 
user_file_access_reference_file_download($uri) {
    if (
check_accsess($uri)) {
        
// Access to the file is success.
        
return array('Content-Type' => file_get_mimetype($uri));
    } else {
        
// Access to the file is denied.
        
watchdog('MyModule''<pre>'print_r('Доступ запрещён'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
        return -
1;
    }
}
?>

Текущие проблемы:

  • Нахрен закрывается отображение картинки в любом виде и стиле всем, кто не админ, не хозяин файла и не отмечен в поле "field_access". А превьюшки-то нужны.
  • Частенько вываливается ошибка

    Warning: Invalid argument supplied for foreach() в функции check_accsess() (строка 43 в файле

    несмотря на предварительную проверку. Да и проверку вставлял только из-за того, что массив $files периодически оказывается пустым.

Мозги вскипают. Может, подскажет кто, как быть?

Аватар пользователя Dusty79 Dusty79 10 ноября в 21:11

Немного подпилил код:


<?php

/* Implements hook_file_download()
 * administrator - roles have access
 * field_access - entityreference field contains users that have access
*/

function check_accsess($uri) {
    
$access_enabled false;
    
watchdog('MyModule''<pre>'print_r($uriTRUE) .'</pre>', array(), WATCHDOG_INFONULL);
    
$uri_check explode('/', (string)$uri);
    if (
array_search('styles'$uri_check)) {
        
watchdog('MyModule''<pre>'print_r('Просмотр превьюшки разрешен'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
        
$access_enabled true// Просмотр превьюшки разрешен
        
return $access_enabled;
    }
    if (
$access_enabled == false) {
        global 
$user;
        foreach (
$user->roles as $role) {
            if (
$role == 'administrator') {
                
watchdog('MyModule''<pre>'print_r('Разрешить для роли администратора'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
                
$access_enabled true// Разрешить для роли администратора
                
return $access_enabled;
                break;
            }
        }
    }
    if (
$access_enabled == false) {
        
$files file_load_multiple(array(), array('uri' => $uri));
        
reset($files);
        
$file current($files);
        unset(
$files);
    }
        if (isset(
$file->uid)) {
            if (
$file->uid == $user->uid) {
                
watchdog('MyModule''<pre>'print_r('Права доступа как владельцу'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
                
$access_enabled true// Права доступа предоставлены как владельцу
                
return $access_enabled;
            }
        }
        if (
$access_enabled == false) {
            if (isset(
$file->field_access)) {
                
watchdog('MyModule''<pre>'print_r('Проверка прав доступа по списку пользователей'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
                
$access_values $file->field_access;
                foreach(
$access_values['und'] as $key_arr => $val_arr){
                    if (
$val_arr['target_id'] == $user->uid) {
                        
watchdog('MyModule''<pre>'print_r('Права доступа предоставлены по списку пользователей'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
                        
$access_enabled true// Права доступа предоставлены по списку пользователей
                        
return $access_enabled;
                        break;
                    }
                }
            }
        }
    return 
$access_enabled;
}
function 
user_file_access_reference_file_download($uri) {
    if (
check_accsess($uri)) {
        
// Access to the file is success.
        
return array('Content-Type' => file_get_mimetype($uri));
    } else {
        
// Access to the file is denied.
        
watchdog('MyModule''<pre>'print_r('Доступ запрещён'TRUE) .'</pre>', array(), WATCHDOG_INFONULL);
        return -
1;
    }
}
?>

Выяснилось, что при отображении картинки не в оригинале, а в каком-либо стиле, хук дёргается дважды. Посмотреть превьюшку я как бы разрешаю, но при этом на сам оригинальный файл мне приходится возвращать запрет. Ну пришёл он в uri. По каким признакам мне его проигнорировать? И этот запрет не даёт посмотреть и превьюшку.
Как решить такую завихруху? В отчётах журнала чётко видно, что пользователь тянет картинку из стилей, но на хук цепляется один раз uri превьюшки и один раз uri оригинальной картинки.
Помогите разобраться, пожалуйста.

Аватар пользователя Dusty79 Dusty79 12 ноября в 19:11

Решение для отдачи превьюшек нашёл. Оказалось просто, сложным был путь к простому решению.