Pull to refresh

Как заставить работать расширения yii-user и rights совместно?

Reading time 4 min
Views 19K

Предисловие


Доброе время суток хабравчане.

Для начала представлюсь. Меня зовут Роман, и я занимаюсь разработкой сайтов (в основном на php-фреймворке «yii», но проскакивают и другие php\python фреймворки\цмс\велосипеды\быдлокоды). В своих проектах на yii всеы нам часто приходится реализовывать тривиальные функции регистрации, авторизации и тд. Плюс ко всему прибавьте к этому ещё и распределние прав доступа для различных пользователей. Готов поспорить, что у каждого толкового разработчика есть хотя бы одна заготовка реализации этого функционали, либо он использует сторонее расширение. Вот и я каждый раз использовал одну и туже реализацию, от пректа к проекту допиливал её, интегрировал с другими частями системы. Но недавно я всё же решил порыть в сторону готовых решений, которые удовлетворяли бы мои потребности и таковые нашлись довольно быстро. 2 самых популярных расширения для yii из оффициального репозитория«yii-user» и «rights».

Интеграция


Оба расширения оформлены в виде модулей. Так что могут быть легко интегрированны в уже существующий проект (если в вашем проекте уже есть модули «user» и «rights» сделайте бекапы), но я для чистоты буду их соединять в новом приложении. Для начала его нужно создать, как это делать отлично описано в документации. Скачиваеим дополнения и распаковываем в папку /protected/modules (её может не быть, создайте вручную). Теперь в нашем проекте появились 2 новых модуля – «user» и «rights». Включим их в конфиге (/protected/config/main.php).
'import'=>array(
	#...
	'application.modules.user.*',
	'application.modules.user.models.*',
	'application.modules.user.components.*',
	'application.modules.rights.*',
	'application.modules.rights.models.*',
	'application.modules.rights.components.*',
	#...
),
'modules'=>array(
	#...
	'user' => array(
		// названия таблиц взяты по умолчанию, их можно изменить
		'tableUsers' => 'tbl_users',
		'tableProfiles' => 'tbl_profiles',
		'tableProfileFields' => 'tbl_profiles_fields',
	),
	'rights',
	#...
),
'components'=>array(
	#...
	'user'=>array(
		'class' => 'RWebUser',
		'allowAutoLogin'=>true,
	),
	'authManager'=>array(
		'class'=>'RDbAuthManager',
		'defaultRoles' => array('Guest') // дефолтная роль
	),
	#...
),

Далее создаем таблицы в базе данных mysql из файлов /protected/modules/user/data/schema.mysql.sql и /protected/modules/user/data/schema.sq.
Ура! Мы успешно установили модули. Проверяем. Заходим по ссылке yiitest/?r=user и видим —

По умолчанию при установке модуля создаются 2 пользователя — admin и demo с паролями admin и demo соответственно. Вы можете авторизоваться под admin:admin и посмотреть все прелести данного модуля. Вкраце о них:
  • Готовая реализация регистрации, авторизации, восстановления пароля, активации аккаунта;
  • Есть механизм дополнительных полей профиля. То есть к профилю пользователя можно безболезненно прикрутить дополнительные поля, например дату рождения, город, телефон и тд. Можно настроить название поля, тип, код, дефолтное значение, регулярное выражение для валидации, навесить свой виджет вместо стандартного инпута и ещё много всякого разного и вкусного;
  • Готовый интерфейс администрирования всего модуля (в стиле CRUD).
Теперь проверяем yiitest/?r=rights. Но к нашему удивлению видем сообщение об ошибке 403 «There must be at least one superuser!». Простая авторизация под админом тут не прокатит. Модуль по прежнему будет требовать авторизаваться под суперпользователем. Как же его назначить? Тут выясняется что у модуля rights есть ещё некий инсталер, который нужен для добавления дефолтных значений, среди который кстати и находится привязка статуса суперпользователя к текущему юзеру (по умолчанию инсталер не доступен, его нужно вкулючить в настройках модуля). Но мы проигнорируем инсталлер и, как настоящие джедаи, исполняем следующие запросы:
INSERT INTO `AuthItem` (`name`, `type`, `description`, `bizrule`, `data`) VALUES
('Admin', 2, 'Администратор', NULL, 'N;'),
('Authenticated', 2, 'Зарегистрированный пользователь', NULL, 'N;'),
('Guest', 2, 'Гость', NULL, 'N;');

INSERT INTO `AuthAssignment` (`itemname`, `userid`, `bizrule`, `data`) VALUES
('Admin', '1', NULL, 'N;'), -- цифру 1 нужно заменить на ID администратора (в нашем примере это первый пользователь)
('Authenticated', '2', NULL, 'N;');

Обновляем страницу yiitest/?r=rights, если снова просит авторизоваться под superuser, авторизуемся под пользователем с ID=1 (в нашем примере это admin:admin). И, как говорят в одной замечательной стране, вуаля! Сразу видим интерфейс администрирования. Вкраце о возможностях:
  • Можно привязать несколько ролей к одному пользователю;
  • Операции группируются;
  • Роли могут наследовать разрешения;
  • Многое другое.
Для того, что бы наши правила заработали, Controller должен наследоваться от класса RController и в него (Controller) нужно добавить (или дописать) метод filters
public function filters(){
	return array(
		#...,
		'rights'
	);
}


На сладкое


Это все конечно очень здорово, но во всей этой схеме закрался небольшой багнебаг (разработчики не предусмотрели, либо я чего-то ещё не изучил). А именно после регистрации вручную придется выставлять пользователю роль «Authenticated». Для этого я написал небольшой костыль. В модуль «user» в папке components создаем файл OnAfterRegistrationBehavior.php со следующим содержимым:
class OnAfterRegistrationBehavior extends CActiveRecordBehavior{
	
	function afterSave($event){
		
		// получаем таблицу в БД
		$assignmentTable = Yii::app()->getAuthManager()->assignmentTable;
		
		// получаем параметры нового пользователя
		$attr = $event->sender->getAttributes();
		
		// вытаскиваем название роли по умолчанию из настроек модуля rights
		$defRole = Yii::app()->getModule('rights')->authenticatedName;
		
		// добавляем привязку
		Yii::app()->db->createCommand(
				"INSERT INTO {$assignmentTable}
				(`itemname`,`userid`,`bizrule`,`data`)
				VALUES
				('{$defRole}','{$attr['id']}',NULL,'N;')")->execute();
		
	}
	
}

После чего к модели «RegistrationForm» добавляем поведение «OnAfterRegistrationBehavior»
public function behaviors(){
	return array(
		'OnAfterRegistrationBehavior' => array(
			'class' => 'application.modules.user.components.OnAfterRegistrationBehavior'
		)
	);
}
Только после обновления модуля user не забудьте восстановить поведение. Будем надеяться, что разработчики прикрутят events к своим контроллерам, что бы в будущем не пришлось прибегать к таким костылям.
Если кто-то знает более изящное решения, буду рад услышать.

Спасибо за внимание.
Tags:
Hubs:
+25
Comments 19
Comments Comments 19

Articles