Вопрос про xPDO

Здравствуй, сообщество!

Возникла необходимость записывать пароль пользователя как есть, не хэшируя. Предвосхищая возмущенные посты, скажу, что это необходимо для обмена с удаленной БД. Там УЖЕ пароли хранятся в хэшированном виде и, как вы понимаете, повторное хэширование кэшей ни к чему хорошему не приведет.
Покумекав и пошерстив исходники, нашел два варианта:

1. Расширить класс modHashing новым классом, в котором метод hash отдавал бы то же, что получил на входе, после этого временно сменить класс хэширования у пользователя на этот новый класс, установить пароль методом set, после чего сменить снова класс хэширования на тот, что используется на сайте постоянно.
2. У класса xPDOObject, наследником которого является и modUser, имеется метод _setRaw, который позволяет записывает в любое поле объекта информацию без предварительной обработки.

Первый метод мне не понравился: как-то сложно и не кошерно. У метода два есть ровно один, но критический недостаток — он объявлен с ключевым словом protected. Когда я пытаюсь вызвать этот метод из своего самописного класса, в который я передаю объект modUser, PHP плюется фатальной ошибкой.
Хотя в описании на php.net указано, что функции, объявленные protected, доступны только из самого объекта и его потомков. Я вызываю метод так:
modUser $user->_setRaw('password', $password);
То есть вроде бы, вызывается метод из объекта modUser, а ошибка возникает всё равно. Пока, временно (поскольку сайт уже в боевом состоянии) изменил protected на public. При этом, я прекрасно понимаю, что так делать нельзя и вообще это до ближайшего обновления.

В связи с этим, прошу уважаемое сообщество тыкнуть носом в способ, которым можно записать пароль в поле password, минуя процесс хэширования.

Заранее благодарю!
Антон Фомичёв
07 июля 2014, 09:29
modx.pro
2 443
0

Комментарии: 17

Василий Наумкин
07 июля 2014, 13:46
+1
Если нужно для обмена с удаленной БД — пиши пароли при создании юзера в отдельную таблицу плагином и не извращайся.
    Пётр Молчанов
    07 июля 2014, 13:53
    0
    В "_user_settings", например.
    $pass2 = $modx->newObject('modUserSetting');
      $pass2->set('user',$profile->get('internalKey'));
      $pass2->set('key','password2');
      $pass2->set('value',$password);
      $pass2->save();
      Антон Фомичёв
      07 июля 2014, 13:54
      0
      По-моему, это еще менее изящно, чем смена класса хэширования на лету:))
      Если я буду полученный пароль записывать в отдельную таблицу, мне придется всю авторизацию на сайте поменять, я не смогу использовать стандартные методы passwordMatches, changePassword и другие вкусности. Какая-то слишком дорогая цена для такой, в общем-то, простой задачи.
        Антон Фомичёв
        07 июля 2014, 13:57
        0
        А, мы, наверное, не поняли друг друга. Проблема не в том, что мне надо передать пароль удалённой БД (тут как раз всё просто), проблема в том, что я получаю хэш пароля из удалённой БД и мне его надо записать в БД сайта в обход хэширования.
          Василий Наумкин
          07 июля 2014, 14:03
          0
          В вопросе ты написал не совсем это.

          Ты можешь указать у юзера свой класс, вместо modUser и расширить protected методы. Посмотри исходники HybridAuth — там так и сделано.
            Антон Фомичёв
            07 июля 2014, 14:10
            0
            Вот это уже больше подходит, спасибо!
            Хотя всё равно придётся много где в моём коде внести изменения.

            Странно, конечно, что этот метод объявлен защищенным… Его использование в моей ситуации здорово бы всё упростило:))
              Василий Наумкин
              07 июля 2014, 14:12
              0
              Твой класс должен быть наследником modUser и тогда он сможет поменять логику работы _setRaw().
          Василий Наумкин
          07 июля 2014, 13:58
          0
          Тебе нужно для обмена с удаленной БД, а не для авторизации — или я неправильно понял?

          Есть нормальная работа с юзерами с одной стороны, а есть таблица id — открытый пароль, с другой. По моему, это гораздо красивее, чем вламываться в нутро движка и все там курочить.
            Антон Фомичёв
            07 июля 2014, 14:06
            0
            Тебе нужно для обмена с удаленной БД, а не для авторизации — или я неправильно понял?
            Не совсем так. Я получаю хэш пароля от удалённой БД и потом использую его для авторизации на сайте. Причины, по которым устроено всё именно так, сложны и многогранны и многими гранями упираются в требования заказчика:))

            Есть нормальная работа с юзерами с одной стороны, а есть таблица id — открытый пароль, с другой.
            Во-первых, не хотелось бы хранить пароль открытым, во-вторых, работа с юзерами и обмен с БД, как я написал выше, неразделимы. Я получаю хэш и он должен в итоге позволять пользователю авторизоваться во фронт-энде.

            По моему, это гораздо красивее, чем вламываться в нутро движка и все там курочить.
            Абсолютно согласен, я это сделал только как временное решение, чтобы сайт мог продолжать работать, пока я разбираюсь.
      Антон Фомичёв
      25 июля 2014, 01:04
      1
      +1
      В итоге проблема решилась, на мой взгляд, изящнее, чем расширение класса modUser.

      С версии 5.3 в php появились замыкания, а с версии 5.4 к ним добавили два метода: bind и bindTo. Подробнее о теории можно почитать тут.

      Эти методы позваляют связать ваше замыкание с конкретным объектом, после чего замыканию становится доступен $this. То есть, фактически, мы можем добавить на лету произвольный метод классу и он будет выполняться в контексте класса.

      В рассмотренном мной в заметке случае делается так:
      $user = $modx->user;
      $method = function($name, $value)
      	{
      		$this->_setRaw($name, $value);
      		return $this->save();
      	}
      
      $method = $method->bindTo($user, 'modUser');
      $method('password', '12345');
      Вуа-ля! Защищенный метод нам доступен из любого класса, поскольку выполняется как будто изнутри класса modUser.
        Fi1osof
        26 июля 2014, 18:35
        1
        0
        А что мешает напрямую устанавливать значение свойства?
        $user = & $modx->user;
        $user->password = 12345;
        $user->save();

        И будет у вас в таблице в чистом виде пароль 12345.

        Значения свойств не обязательно устанавливать через метод ->set(), где выполняются всякие модификации и т.п., их можно устанавливать напрямую, если само свойство у объекта уже есть. Это так же, как вы не установите $user->set('sudo', true);, так как там проверка, и если поле — sudo, то обламывается. Зато можно установить $user->sudo = true; $user->save();
          Антон Фомичёв
          26 июля 2014, 21:23
          0
          Ууу, класс! Вот это — то что надо! Спасибо большое за наводку.

          Даже как-то неудобно — нагородил я тут огород с замыканиями, а самого простого способа не увидел:))
            Fi1osof
            26 июля 2014, 21:33
            2
            +2
            Не за что.

            Только еще момент: таким образом нельзя установить свойства, которых еще нет у объекта. К примеру, если у объекта нет свойства test, то нельзя сделать так: $object->test = $value; Свойства test у объекта после этого просто не будет. Вот здесь как раз и придется использовать $object->set($key, $val), чтобы свойство было создано. За эту магию отвечают ООП-методы __set() и __get() у класса xPDOObject. И вот здесь как раз кроется самое интересное (для этого смотрите сорцы xPDO github.com/modxcms/xpdo/blob/master/xpdo/om/xpdoobject.class.php#L739 ). Дело в том, что в методах __get() и __set() сразу прописана работа со связями getOne()/setOne() и т.д. и т.п.
            К примеру, можно сделать так: $profile = $user->Profile, что будет равнозначно $profile = $user->getOne('Profile'). Таким образом можно получить свойство $user->Profile->email, даже если именно в этот момент еще не был получен связанный объект modProfile для этого пользователя. Или вот так:
            $user = $modx->newObject('modUser', array(
            «username» => «NewUser»,
            ));
            $profile = $modx->newObject(«modUserProfile», array(
            «email» => $someEmail,
            «password» => $somePass,
            ));
            $user->Profile = $profile;
            $user->save();
            И будут записаны в БД и данные пользователя (modUser), и данные его профиля (modUserProfile).
              Антон Фомичёв
              27 июля 2014, 12:30
              0
              Таким образом можно получить свойство $user->Profile->email, даже если именно в этот момент еще не был получен связанный объект modProfile для этого пользователя.
              Вот за эту наводку — отдельное огромное спасибо! Во мне вскипает очередная волна влюблённости в modx:))
                Fi1osof
                27 июля 2014, 13:19
                0
                Пожалуйста :)
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        17