Аутентификация с использованием Jelly и Jelly-Auth -- часть 2

Четверг, 22 апреля 2010 г.
Рубрика: Web frameworks
Метки: |
Просмотров: 3058
Подписаться на комментарии по RSS

Briefly in English
Next part of tutorial on using Kohana 3 Jelly and Jelly-Auth (autologin, built-in role system application). Will be translated soon.


Часть 1


В этой части добавятся автологин и использование встроенной ролевой системы. Однако сначала проведу работу над ошибками

1. вместо
$this->auth = Jelly_Auth::instance();

лучше
$this->auth = Auth::instance();

Thanks SpadXIII


2. модуль ORM здесь действительно не нужен и его можно спокойно отключить (спасибо Sezarin).



3. Для удобства можно ввести свойство Controller_admin::$user и определять его в before():
$this->user = $this->auth->get_user();



3. В описании поля email модели стоит только проверка на уникальность, но нет проверки на пустое поле. Поэтому если кто-то не указал электронную почту, а потом это же делает другой, то выбрасывается Database Exception на неуникальность полей под индексом UNIQUE (которое не ловится, поскольку стоит catch на Validation_Exception). Я не нашел лучшего способа исправить ситуацию, кроме как наследовать User, переопределив метод initialize() таким образом (если нет желания переписать в нем все поля полностью):

public static function initialize(Jelly_Meta $meta)
{
    $meta->fields = array(
        'email' => new Field_Email(array(
            'unique' => TRUE,
            'rules' => array(
                'not_empty' => array(TRUE),
            )
        )),
    );
        
    parent::initialize($meta);
}



Кроме того, был малость исправлен и дополнен вывод сообщений об ошибках при валидации.


Сделаем автологин.
Для этого нужно
а) изменить форму, добавив чекбокс


б) добавить на страницу логина обработку чекбокса, переписав проверку $_POST таким образом

// try to login
if ($_POST)
{
    $username = $_POST['username'];
    $password = $_POST['password'];
            
    $remember = isset($_POST['remember']) ? TRUE : FALSE;
            
    if ($this->auth->login($username, $password, $remember))
    {
        Request::instance()->redirect('admin/index');
    } else {
        $errors = array('Login or password incorrect');
    }
}



в) переписать защищенные методы с учетом включенного автологина. В моем случае пришлось проверять право на просмотр страницы с помощью $this->auth->logged_in('login'), что автоматически пытается заавтологинить юзера.


Для реализации простейшей ролевой системы вполне пригоден имеющийся в модуле Jelly-Auth функционал -- метод has_role($role). Например, разрешим добавлять пользователя только администратору. Однако для этого нужно предоставить возможность назначать роли пользователям.


Надо
а) в методе контроллера action_users() в ветке 'edit' добавить переменные, содержащие коллекцию ролей системы и массив активных ролей пользователя:

$roles = Jelly::select('roles')    
    ->execute();
...
	
if ($is_saved)
{
    $this->template->content = 'User profile was updated';
} else {
    // output user profile form
    $this->template->content = $content
        ->set('id', $user->id)
        ->set('username', $user->username)
        ->set('email', $user->email)
        ->set('user_roles', $user->roles->as_array())
        ->set('roles', $roles); 
}



б) соответственно, в форме ('views/admin/edit_user') нужно вставить массив чекбоксов типа

<SPAN>Assigned roles</SPAN>
<UL style="list-style-type: none;">
<?php 
$role_ids = array();
foreach ($user_roles as $ur)
{
    $role_ids[] = $ur['id'];
}
foreach ($roles as $role)
{
?>
<LI>
<?php echo Form::checkbox('roles[]', $role['id'], in_array($role['id'], $role_ids), array('id' => 'roles_'.$role['id'])).' '
        .Form::label('roles_'.$role['id'], $role['name'].'<BR>'.$role['description']);
?></LI>
<? 
}
?></UL>


Первый foreach выглядит не очень красиво, но я в силу неопытности не нашел лучшего способа извлечь массив role IDs из ассоциативного массива.


в) Остается обработать приходящий в POST массив номеров ролей:

$user->roles = isset($_POST['roles']) ? $_POST['roles'] : array();



Далее можем, например, разрешить добавление нового пользователя только админу

if ($this->user->has_role('admin'))
...



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

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


Скачать / Download 161 (zipped ~11 KiB)

twitter.com facebook.com vkontakte.ru odnoklassniki.ru mail.ru ya.ru rutvit.ru myspace.com technorati.com digg.com friendfeed.com pikabu.ru blogger.com liveinternet.ru livejournal.ru memori.ru google.com bobrdobr.ru mister-wong.ru yahoo.com yandex.ru del.icio.us

Комментариев: 6

  1. Неплохой пример реализации сложных вещей просто...

    Я думаю, у Jelly - большое будущее.

    Кстати, со вчера на Github (http://github.com/jonathangeiger/kohana-jelly) появилась версия с документацией на русском.

    Спасибо Сергею Гладковскому! (http://github.com/smgladkovskiy)

  2. 2010-04-23 в 22:05:14 | Александр Купреев

    да, Jelly мне кажется достаточно удобным инструментом, хотя тут не имею большого опыта. А на русском языке вообще приятно читать smile

  3. 1.

    if ($_POST)
    {
        $username = $_POST['username'];
        $password = $_POST['password'];
    ...

    Добавьте проверку на наличие этих ключей. Arr::get() я уже на автопилоте подставляю в любом месте, где работаю с массивами smile

    2. А зачем пункт "в" в реализации автологина? Для этого обычно используется метод Auth::instance()->auto_login(), который пробует залогиниться на основании кук (вернет TRUE, если получилось). Пихаем его в before(), если не прокатило, то уже тогда предлагаем залогиниться.

  4. 2010-05-11 в 20:33:46 | Александр Купреев

    пункт в) нужен из-за того, что я изначально отказался от менеджмента доступа к страницам в before(), решив разбираться в каждом методе отдельно smile а так конечно -- вы абсолютно правы

  5. Александр спасибо Вам за столь интересные статьи.

    Первый foreach выглядит не очень красиво, но я в силу неопытности не нашел лучшего способа извлечь массив role IDs из ассоциативного массива.

    Я немного поправил код в вашем примере. Надеюсь в лучшую сторону )

    1. Контроллер action_users(), ветка 'edit'

    ...
    if ($is_saved)
    {
        $this->template->content = 'User profile was updated';
    } else {
        // output user profile form
        $this->template->content = $content
            ->set('id', $user->id)
            ->set('username', $user->username)
            ->set('email', $user->email)
            ->set('user_roles', $user->roles->as_array('id'))
            // т.е. выводим ассоциативный массив ролей, где ключем
            // является поле ID
            ->set('roles', $roles); 
    }
    ...

    2.Форма views/admin/edit_user

    <SPAN>Assigned roles</SPAN>
    <UL style="list-style-type: none;">
    <?php 
    foreach ($roles as $role)
    {
    ?>
    <LI>
    <?php echo Form::checkbox('roles[]', $role['id'], array_key_exists($role['id'], $user_roles),
    array('id' => 'roles_'.$role['id'])) . ' ' . Form::label('roles_'.$role['id'], $role['name'].'<BR>'.$role['description']);
    ?>
    </LI>
    <? 
    }
    ?>
    </UL>

    Т.е. я в форме убрал лишний цикл и изменил поиск ID в массиве ролей пользователей.

  6. 2010-05-17 в 15:02:26 | Александр Купреев

    Anree, спасибо за отзыв и простите, что поздно отвечаю -- провайдер подорвал доверие :(

    Спасибо за правки, самое то!

Оставьте комментарий!

Используйте нормальные имена.

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email. При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д., а также подписку на новые комментарии.

Авторизация: MaxSiteAuth. Войти через loginza

(обязательно)