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

Views: 6617Comments: 6
Web frameworks

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') нужно вставить массив чекбоксов типа

Assigned roles

    <?php
    $role_ids = array();
    foreach ($user_roles as $ur)
    {
    $role_ids[] = $ur['id'];
    }
    foreach ($roles as $role)
    {
    ?>

  • <?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'].'
    '.$role['description']);
    ?>

  • <?
    }
    ?>


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


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

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


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

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



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

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


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

Comments: 6 RSS

1 Sezarin 23-04-2010 19:46

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

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

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

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

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

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

3 biakaveron 11-05-2010 14:32

1.

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

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

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

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

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

5 Anree 13-05-2010 16:11

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

Первый 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

Assigned roles
<?php 
foreach ($roles as $role)
{
?>
<?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'].''.$role['description']);
?>
<? 
}
?>

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

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

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

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

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


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

     

  

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

MaxSiteAuth. Войти через loginza

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