Посоветуйте реализацию прав пользователей

Nogrogomed

Новичок
Nelius
Вот вам наглядный пример:
Модуль менеджеры.
Менеджер может:
1. посмотреть пользователей
2. Посмотреть заказы
3. Обновить инфу о заказах
4. Удалить заказ
5. Обновить заказ.

Модуль новости
1.2.3.4. смотреть/добавить/редактировать/удалить.

Т.е. для разных отношений (модулей) у вас различное количество прав. Следовательно - надо делать отношение "модуль" и "права" - в разных таблицах.

Автор оригинала: triumvirat
ИМХО, не вяжетццо с этим: Реляционная СУБД

Nelius
и с чем из этого конкретно не вяжется?
Это не вяжется прежде всего с тем, что содержимое каждого поля должно быть атомарным (т.е. неделимым)
 

Nelius

кипарис во дворе
Да да, вы правы, мой подход может подойти для частного случая, но он не универсален. Всем спасибо, хоть автор темы и не я но для себя многое вынес!!!

-~{}~ 09.11.07 22:45:

Последовав вашим советам я создал таблицу permissions:

id | uid | mod | act
1 | 1 | 1 | add
2 | 1 | 1 | delete
3 | 1 | 1 | edit
2 | 2 | 3 | view

Но смотрите что выходит... если модулей 30 а среднее количество действий 6 то это 180 записей на юзера
при 100000 юзеров это ~18 000 000 записей...
Мне кажется это много и тормозно...

Я провел тесты, создал в таблицах users и perms1 по 250 000 и 400000 записей соответственно. В цикле 30 000 подряд вынимаю эти данные из бд...
В итоге получаю время генерации страницы MIN: 28.6972169876 - MAX: 58.4005129337 сек

код:

PHP:
$stm = explode(' ',microtime());
$stm = $stm[1] + $stm[0];

ini_set('max_execution_time',190);

$db['charset'] = 'utf8';
$db['name'] = 'tests_db';
$db['host']  = 'localhost';
$db['user'] = 'юзер';
$db['pass'] = 'пароль';
$db['cid'] = '';

$db['cid'] = mysql_connect($db['host'],$db['user'],$db['pass']) or die('DB error #'.mysql_errno());
mysql_select_db($db['name'], $db['cid']) or die('DB error #'.mysql_errno());
mysql_query('SET NAMES \''.$db['charset'].'\'') or die('DB Error #'.mysql_errno());

for ($z=0;$z<30000;$z++) {
	$f_res = mysql_query('SELECT * FROM users1 WHERE id=\'2\'') or die('DB Error #'.mysql_errno());
	$user = mysql_fetch_assoc($f_res); mysql_free_result($f_res);
	$f_res = mysql_query('SELECT * FROM perms1 FORCE INDEX (`srch`) WHERE uid=\'2\' && `mod`=\'1\'') or die('DB Error #'.mysql_errno());
	$user['perms'] = mysql_fetch_assoc($f_res); mysql_free_result($f_res);
	unset($user);
}

mysql_close($db['cid']);

$etm = explode(' ',microtime());
$etm = $etm[1] + $etm[0];
print $etm = $etm - $stm;
Далее я пошел на эксперимент и нарушение признаков Реляционной СУБД создав таблицу вида:

id | uid | mod | act
1 | 1 | 1 | add:delete:edit:view

колличество рядов в таблицах оставил тоже самое(хотя при такой реаллизации на одного пользователя не 180 записей а в 6 раз меньше 30) провел тесты получил время генерации MIN: 17.2141671181 - MAX: 34.9395740032 сек

код:

PHP:
//без изменения

for ($z=0;$z<30000;$z++) {
	$f_res = mysql_query('SELECT a.*, b.act as `perms` FROM users1 a, perms b FORCE INDEX (`srch`) WHERE a.id=\'2\' && b.uid=\'2\' && b.mod=\'37673951\'') or die('DB Error #'.mysql_errno());
	$user = mysql_fetch_assoc($f_res); mysql_free_result($f_res);
	$user['perms'] = explode(':',$user['perms']);
	unset($user);
}
//без изменения
Что вы думаете по этому поводу? Второй способ выигрывает и по скорости и по объему данных, хоть и данные не атомарные... Хотелось бы знать мнение гуру :)

Кстати кто-то тут писал что не понимает для чего нужны роли, вот наглядный пример, при большом кол-ве юзеров вместо того чтобы иметь мега таблицы с 1000 000 записей и можно иметь 10 ролей и табличку с разрешениями максимум на 1000 строк... думаю поиск будет гораздо быстрее, а для описания частных случаев можно завести еще табличку куда добавляются права пользователя, допутим модератора, которому запрещенно удалять темы, но можно редактировать, итого роль+ 1 запись быстро, удобно.
 

MiksIr

miksir@home:~$
Из реализации ролей мне нравится древовидная. Там есть только роль, которая может наследовать права от какой-то другой. Роль имеет флаг - можно ли ее использовать для логина или нет. Например, заводим роль - модераторы, от них наследуем модераторов разделов, а от них уже - роли "логины".
Удобно, конечно, при большом кол-ве пользователей. Для небольшого, может, достаточно и обычной схемы "груупа-пользователь", которая, правда, легко строится на наследовании ролей.

-~{}~ 10.11.07 01:55:

По топу - если актов немного, а их скорее всего немного, введите их отдельными полями.
 

Nelius

кипарис во дворе
По топу - если актов немного, а их скорее всего немного, введите их отдельными полями.
Я бы с удовольствием ввел, но акты для разделов могут отличаться, так я могу просто дописать add:delete:edit:new_act:super_new_act не трогая БД, а если колонки, то потом придется добавлять :(

-~{}~ 10.11.07 02:06:

Кстати про древовидную реализауию ролей, мне она тоже нравится, но для моего проекта подойдут и группы, в какой-то степени они же и роли, тока примитивные) Так что будем следовать принципу Keep It Simple Stupid :)
 

Духовность™

Продвинутый новичок
for ($z=0;$z<30000;$z++) {
после этого уже смотреть что-то не хотелось..

-~{}~ 10.11.07 11:41:

Nelius
если модулей 30 а среднее количество действий 6 то это 180 записей на юзера
при 100000 юзеров это ~18 000 000 записей...
зачем, я не понял, на каждого юзера права ставить?
 

Mondain

Новичок
PHP:
abstract class User {
	function __construct ($name) {
		$this->name = $name;
	}

	function getName () {
		return $this->name;
	}

	// Методы определения прав доступа
	function hasReadPermission () {
		return true;
	}

	function hasModifyPermission () {
		return false;
	}

	function hasDeletePermission () {
		return false;
	}
}

class GuestUser extends User {
}

class CustomerUser extends user {
	function hasModifyPermission () {
		return true;
	}
}

class AdminUser extends User {
	function hasModifyPermission () {
		return true;
	}

	function hasDeletePermission () {
		return true;
	}
}

class UserFactory {
	private static $users = array ("Steel" => "Admin", "Alex" => "Customer", "Guest" => "Guest");

	static function Create ($name) {
		if (!isset(self::$users[$name])) {
			// Вывод сообщения об ошибке - пользователь не зарегистрирован
		}

		switch (self::$users[$name]) {
			case "Guest": return new GuestUser ($name);
			case "Customer":  return new CustomerUser ($name);
			case "Admin": return new AdminUser ($name);
			default: // Ошибка - неизвестный тип пользователя
		}
	}
}

function boolToStr ($b) {
	if ($b == true) {
		return "Да<br>";
	} else {
		return "Нет<br>";
	}
}

function displayPermissions (User $obj) {
	print "Права доступа пользователя ".$obj->getName().":<br>";
	print "Чтение: ".boolToStr($obj->hasReadPermission());
	print "Изменение: ".boolToStr($obj->hasModifyPermission());
	print "Удаление: ".boolToStr($obj->hasDeletePermission())."<br>";
}

$logins = array ("Steel", "Alex", "Guest");

foreach ($logins as $login) {
	displayPermissions (UserFactory::Create($login));
}
 

TutanXamoN

Новичок
Я закончил недавно такую системку ограничения прав(безусловно навеяно мусклом:=)):
group:
id int
name varchar(16)
User.R_s enum('N','Y')
User.K_s enum('N','Y')
и тд

user
id int
id_g int
chng tinyint(1)
name varchar(16)
password varchar(41)
User.R_s enum('N','Y')
User.K_s enum('N','Y')
и тд

Схема простая:
Заводим некоторые группы.
Заводим пользователей
При заведении права подтягиваються из группы и если нужно потом изменяються.


ЗЫ: на данный момент ~500 пользователей все довольны всем нравитсо)
 

Nelius

кипарис во дворе
Автор оригинала: triumvirat зачем, я не понял, на каждого юзера права ставить?
Я и не собираюсь ставить права на каждого пользователя, использую роли, просто хотел выяснить какая реаллизация будет быстрее.
 

TutanXamoN

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

Nelius

кипарис во дворе
Автор оригинала: TutanXamoN
Nelius роли необходимы но заводить отдельную роль когда нужно добавить или забрать привилегию у единственного пользователя непрально.
Согласен, я выше написал как я решаю эту задачу.
Повторюсь. Пользователь выполняет роль модератора значит обладает соответствующими правами, но есть еще таблица отдельно прав пользователей и если что-то меня не устраивает из прав роли модератора для данного пользователя я просто добавляю ему например move_topic: false. У роли модератор есть право на move_topic но я принудительно ее снял для конкретного пользователя.
Только возможно для моего случая понятие ролей использовать нельзя, а более правильно использовать понятие групп.
 

MiksIr

miksir@home:~$
Nelius, мне кажется в твоем случае цепочка ролей будет не сильно сложнее в реализации ;) всего-то надо взять цепочку ролей, и пройти по ней. Соотв-но в каждой роли доступ может: не метяться, запрещаться, разрешаться. Назови в админке роли без права логиниться - группой, а с правом - юзерами, и никто даж не поймет ничего ;)
 

vovanium

Новичок
кстати, почему-то никто не юзает бинарные операции?

К примеру, права:
PHP:
define('P_VIEW', 1);
define('P_ADD', 2);
define('P_EDIT', 4);
define('P_DELETE', 8);
define('P_ALL', 15);

$permision = P_ALL ^ P_DELETE; // все права кроме удаления
$permision = P_VIEW | P_ADD; // просмотр и добавление

if ($permision & P_VIEW) echo 'просмотр<BR>';
if ($permision & P_ADD) echo 'добавление<BR>'; 
if ($permision & P_EDIT) echo 'редактирование<BR>'; 
if ($permision & P_DELETE) echo 'удаление<BR>';
Удобно, наглядно и компактно, в один TINYINT можно сохранять 8 прав доступа, в INT до 32. Точно также можно выбирать из базы информацию о конкретных правах, к примеру, выбор всех юзеров у которых есть право просмотра:
PHP:
$res = mysql_query('SELECT * FROM perm & ' . P_VIEW);
 

MiksIr

miksir@home:~$
> кстати, почему-то никто не юзает бинарные операции?

Мне кажется просто потому, что мало кто из нынешних программеров имеет профильное образование или писал на низком уровне ;)
 

Nelius

кипарис во дворе
vovanium
Идея интересная, сам не догадался... (
Думаю так будет работать быстрее всего, спасибо, попробую...

MiksIr
у меня профильное образование, я программист, хоть и учился я далеко не в Бауманке... После того как пересел 3 года назад с С++ на php и С++ забросил сам начал замечать что порой простые задачи решаю криво и по ламерски...
Конечно хотелось бы сказать что мол пхп более прост и типа это он во всем виноват распологает к "недуманию", но понимаю что дело конечно же во мне.
 

MiksIr

miksir@home:~$
Ну я без личностей. Просто сталкивался с хорошими ПХП программерами, которых рассказ о битовых флагах вводил в ступор.
 
Сверху