Fi1osof 02 сентября 2013 1 3
А вот этот баг — один из самых сложных и злостных, с которым я сталкивался в MODX-е… На официальном форуме вопрос задали уже более двух месяцев назад (хотя я столкнулся гораздо раньше), и до сих пор нет четкого ответа на него. Но вот вчера у меня опять вылезла эта бяка на сайте, и я тогда сел основательно ее копать. Суть проблемы заключается в том, что как бы админка вся работает и все клева, но при сохранении документа получаем сообщение Access denied (или Доступ запрещен, в зависимости от языка админки). При чем самое интересное в том, что при сохранении документа через быстрое редактирование документ сохраняется, все ОК, а при редактировании в полном редакторе у нас ошибка доступа.

Собственно, за 3 часа я получил боле менее вменяемый ответ на природу этого феномена, но самого решения у меня сейчас нет. Тем не менее под катом вас ждет перевод этого ответа и описание самых интересных моментов.

Итак, когда я начал ковырять все это дело, я обратил внимание на то, что на сервер передается HTTP_MODAUTH='0', то есть ключ нулевой (именно нулевой, а не отсутствующий). То есть это и не полный ключ сессии, но и не отсутствие его. То есть с натяжкой можно считать, что ключ сессии есть. НО: при запросе на update документа из быстрого редактора документ нормально сохраняется, а при запросе на update из полного редактора у нас Access denied. При этом в обоих запросах присутствует HTTP_MODAUTH='0' (здесь и далее мы будем рассматривать только эти два запроса (быстрый и полные редактор), так как их принцип примерно общий, а результат разный). Так в чем же тогда фокус? На этот вопрос я получил ответ, когда более внимательно изучил modConnectorResponse. Вот самый интересный кусочек кода:
if (!$isLogin && !isset($_SERVER['HTTP_MODAUTH']) && (!isset($_REQUEST['HTTP_MODAUTH']) || empty($_REQUEST['HTTP_MODAUTH']))) {
    $this->responseCode = 401;
    $this->body = $modx->error->failure($modx->lexicon('access_denied'),array('code' => 401));
}

И вот самое интересное мне не сразу в глаза кинулось, а именно переменные $_SERVER['HTTP_MODAUTH'] и $_REQUEST['HTTP_MODAUTH']. То есть ключи одинаковые HTTP_MODAUTH, а массивы разные $_SERVER и $_REQUEST. Думаю, многие понимают что это за массивы и в чем их отличие, поэтому не буду их объяснять. Но вот что интересней всего: $_SERVER['HTTP_MODAUTH'] нигде не объявляется. То есть во всем ядре MODX-а нет где-либо присвоения значения этой переменной. Но ведь она не может просто так появиться… Тогда я полез ковырять JS-файлы, и вот такой интересный кусочек нашел в core/modx.layout.js:
Ext.Ajax.defaultHeaders = {
        'modAuth': config.auth
    };
    Ext.Ajax.extraParams = {
        'HTTP_MODAUTH': config.auth
    };


Ext.Ajax.extraParams — это параметры, передаваемые в запросе (по умолчанию для всех запросов). А вот Ext.Ajax.defaultHeaders — это заголовки запроса. И вот как раз их-то если специально искать не будешь, не увидишь. А они есть.



А увидеть этот параметр мы можем в коде страницы админки. К примеру:
<script type="text/javascript">Ext.onReady(function() {
    ......
    MODx.load({xtype: "modx-layout",accordionPanels: MODx.accordionPanels || [],
        auth: "modx51b7a546bbe9a4.05164626_25223640a1da1e4.42369214"});
});</script>


Формируется он в modManagerController.
$siteId = $this->modx->user->getUserToken('mgr');
........
MODx.load({xtype: "modx-layout",accordionPanels: MODx.accordionPanels || [],auth: "'.$siteId.'"});


modUser::getUserToken():
public function getUserToken($ctx = '') {
    if (empty($ctx)) $ctx = $this->xpdo->context->get('key');
    return isset($_SESSION['modx.'.$ctx.'.user.token']) ? $_SESSION['modx.'.$ctx.'.user.token'] : '';
}


Вот он методом modUser::getUserToken() и получает токен контекста, который и участвует во всей этой цепочке. И что самое интересное, токен может быть и нулевым, лишь бы он был хоть каким-то, и везде имел одинаковое значение (и в $_SERVER['HTTP_MODAUTH'], и в $_REQUEST['HTTP_MODAUTH']). Тогда будет считаться, что пользователь корректно авторизован. И в большинстве случаев так и происходит. НО, в случае отправки запроса из полного редактора страницы этого не происходит. Просто потому что его механизм отправки данных отличается, и он не отправляет modAuth-заголовок на сервер. В результате чего переменная $_SERVER['HTTP_MODAUTH'] пустая, и мы имеем Action denied.

Каждый может проверить этот баг очень просто (на MODX Revolution 2.2.8 и выше). Просто выполните в Console (или другим способом) этот простой php-код:
$_SESSION['modx.mgr.user.token'] = '0';

То есть просто обнулите сессию в контексте mgr, после чего обновите страницу админки. Админка работать будет, но попробуйте обновить документ в полном редакторе, и скорее всего получите Access denied.

Резюме: в какой-то момент токен сессии обнуляется (по какой причине — я не знаю. У меня есть подозрение, что такое случается, когда ставишь галочку Запомнить, но в админку потом не заходишь несколько дней). Потом через несколько дней заходишь, и как бы авторизован, но вот с таким обнуленным токеном. И в итоге получается, что как бы авторизован, но не полноценно. И при этом ты и не не авторизован, то есть нет требования авторизоваться.
3 комментария
w
web-ramos 04 ноября 2013г в 23:42 #
Такая же проблема решилась у меня последовательностью в админке:
1) Безопасность -> перезагрузить права доступа
2) Безопасность -> завершить все сеансы

Выбрасывает на страницу логина, заходим заново, проблема ушла
Fi1osof1
Fi1osof 04 ноября 2013г в 23:54 #
Это не решение проблемы в целом. Я описывал, что здесь проблема как раз в пустом ключе сессии. Конечно, завершив все сеансы (удалив записи сессий из БД) и авторизовавшись снова, у вас будет новая сессия с нормальным ключом. Но вообще не исключается вариант, что через некоторое время у вас опять будет пустой ключ в сессии, и опять возникнет эта же проблема.

Вот здесь я писал хоть и хардкорный, но все таки действенный фикс этой проблемы: forums.modx.com/thread/85173/after-upgrade-to-2-2-8-problem-access-denied-on-save?page=6#dis-post-476634
w
web-ramos 05 ноября 2013г в 00:03 #
согласен!
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.