صفر
دروس CakePHP # 5 : بناء نظام تعدد المستخدمين والمجموعات والصلاحيات.
برمجة وأفكار برمجية CakePHP الزيارات: 1880 التعليقات:4
نبدأ في هذه التدوينة بموضوع هام لأي تطبيق ويب يعمل بشكل عملي ألا وهو نظام إعداد المستخدمين ومجموعاتهم والصلاحيات لكل مستخدم ونظام تسجيل الدخول والخروج وغير ذلك من الأمور الهامة لكل نظام ، وبعد كل تدوينة جديدة نقوم بدمج ما تعلمناه بنظام "بنك الأفكار الابداعية" . وهذا التدوينة تفيد بشكل كبير الأخوة الذين لديهم خبرة سابقة في الكيك ، وسوف نستخدم لبناء هذا النظام كومبوننت Auth فقط بدون استخدام ACL للصعوبات المتعلقة بها ، سوف يكون تطبيق هذا الدرس على الاصدار السابق للكيك وهو cake_1.2.0.7692-rc3 وذلك لوجود اختلافات مع الاصدار الحالي للكيك وسوف نحاول معا بعد تطبيق هذا الدرس الانتقال إلى الاصدار الأخير من الكيك، ولتنزيل الاصدار cake_1.2.0.7692-rc3 انقر هنا .
مميزات النظام الذي سوف نبنيه في هذه التدوينة :
1- نظام مرن في منح الصلاحيات على كل Action تابع لأي Controller .
2- يمكن تعيين الصلاحيات للمجموعات .
3- عند وضع أي مستخدم في أي مجموعة فإن المستخدم سوف يمنح كافة صلاحيات تلك المجموعة.
4- يتم إنشاء قائمة لكل مستخدم عند تسجيل دخوله بحسب صلاحياته ، بحيث يعرض له الـ Controller الذي له صلاحية عليه. ( هذه الميزة أراحتني كثيرا كثيرا ) .
5- إدارة الصلاحيات تتم بشكل سهل جدا ومريح بدون تعقيدات ACL
6- يدمج في أي نظام قائم لك بسهولة بدون التأثير على ما تقوم به.
نبدأ الآن ببناء نظام تعدد المستخدمين ... أولا سوف نقوم بإنشاء 5 جداول جديدة في قاعدة البيانات لديك ( ملاحظة : لنسخ الكود انقر على صورة الورقة التي بجوار الطابعة أعلى كل صندوق للكود ثم انسخ ما سيظهر في الصندوق ) : [code language='sql'] CREATE TABLE IF NOT EXISTS `groups` ( `id` int(11) NOT NULL auto_increment, `name` varchar(50) collate utf8_unicode_ci NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS `groups_permissions` ( `group_id` int(11) NOT NULL, `permission_id` int(11) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS `groups_users` ( `group_id` int(11) NOT NULL, `user_id` int(11) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS `permissions` ( `id` int(11) NOT NULL auto_increment, `name` varchar(50) collate utf8_unicode_ci NOT NULL, `title` varchar(250) collate utf8_unicode_ci NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL auto_increment, `username` varchar(40) collate utf8_unicode_ci NOT NULL, `email_address` varchar(50) collate utf8_unicode_ci NOT NULL, `password` varchar(50) collate utf8_unicode_ci NOT NULL, `language_id` int(11) NULL, `active` tinyint(1) NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; [/code] نقوم الأن بإضافة بعض البيانات الهامة للجداول السابقة كما يلي : [code language='sql'] INSERT INTO `groups` (`id`, `name`, `created`, `modified`) VALUES (1, 'Administrators', '2008-11-07 18:44:13', '2008-11-07 21:47:24'); INSERT INTO `groups_permissions` (`group_id`, `permission_id`) VALUES (1, 1); INSERT INTO `groups_users` (`group_id`, `user_id`) VALUES (1, 1); INSERT INTO `permissions` (`id`, `name`, `title`, `created`, `modified`) VALUES (1, '*', 'جميع الصلاحيات', '2008-11-07 18:43:40', '2008-12-12 15:45:27'); INSERT INTO `users` (`id`, `username`, `email_address`, `password`, `language_id`, `active`, `created`, `modified`) VALUES (1, 'admin', 'na@nahedh.com', '3c2164ad6d86cefb2151cf1e1a8d6a2a084b759e', 2, 1, '2008-11-07 18:45:34', '2008-12-19 23:18:15'); [/code] بعد إعداد الجداول وإضافة البيانات إليها سوف يكون لدينا مستخدم واحد اسمه admin وكلمة المرور الخاصة به مشفرة وهي 1111 ، له كافة الصلاحيات * لأنه في مجموعة Administrators ، والأن نقوم بإنشاء الملفات المطلوبة : أولا نقوم بإنشاء الـ Models المطلوبة للربط مع الجداول السابقة : قم بإنشاء الملف التالي :
app/models/user.php
[code language='php'] array('email'), 'password' => array('alphaNumeric'), 'active' => array('numeric') ); var $hasAndBelongsToMany = array( 'Group' => array('className' => 'Group', 'joinTable' => 'groups_users', 'foreignKey' => 'user_id', 'associationForeignKey' => 'group_id', 'unique' => true ) ); } ?> [/code]قم الان بإنشاء الملف :
app/models/group.php
[code language='php'] array('className' => 'Permission', 'joinTable' => 'groups_permissions', 'foreignKey' => 'group_id', 'associationForeignKey' => 'permission_id', 'unique' => true ), 'User' => array('className' => 'User', 'joinTable' => 'groups_users', 'foreignKey' => 'group_id', 'associationForeignKey' => 'user_id', 'unique' => true ) ); } ?> [/code] ثم الملف:app/models/permission.php
[code language='php'] array('className' => 'Group', 'joinTable' => 'groups_permissions', 'foreignKey' => 'permission_id', 'associationForeignKey' => 'group_id', 'unique' => true ) ); } ?> [/code] نأتي الان إلى إنشاء الـ Controllers : قم بإنشاء الملف التالي :
app/controllers/users_controller.php
[code language='php']Session->del('Permissions'); $this->redirect($this->Auth->logout()); } } ?> [/code]
ثم المتحكم :
app/controllers/groups_controller.php
[code language='php'] [/code] وكذلك إنشأ الملف التالي :
app/controllers/permissions_controller.php
[code language='php'] [/code]
الان جاء دور المتحكم الرئيسي app_controller.php والذي تكون فيه الاعدادات العامة التي تطبق على كافة المتحكمات المشتقة منه ، قم بإنشاء المتحكم الرئيسي إذا لم يكن موجودا أو قم بالتعديل عليه وإضافة الكود التالي :
app/app_controller.php
[code language='php']Auth->fields = array('username'=>'username','password'=>'password'); //Set application wide actions which do not require authentication $this->Auth->allowedActions = array('display'); //Set the default redirect for users who logout $this->Auth->logoutRedirect = '/'; //Set the default redirect for users who login $this->Auth->loginRedirect = '/'; //Extend auth component to include authorisation via isAuthorized action $this->Auth->authorize = 'controller'; //Restrict access to only users with an active account $this->Auth->userScope = array('User.active = 1'); //Pass auth component data over to view files $this->set('Auth',$this->Auth->user()); } /** * beforeRender * * Application hook which runs after each action but, before the view file is * rendered * * @access public */ function beforeRender(){ //If we have an authorised user logged then pass over an array of controllers //to which they have index action permission if($this->Auth->user()){ $controllerList = Configure::listObjects('controller'); $permittedControllers = array(); foreach($controllerList as $controllerItem){ if($controllerItem <> 'App'){ if($this->__permitted($controllerItem,'index')){ $permittedControllers[] = $controllerItem; $this->admin[] = $controllerItem; } } } } $this->set(compact('permittedControllers')); $this->set('admin',$this->admin); } /** * isAuthorized * * Called by Auth component for establishing whether the current authenticated * user has authorization to access the current controller:action * * @return true if authorised/false if not authorized * @access public */ function isAuthorized(){ return $this->__permitted($this->name,$this->action); } /** * __permitted * * Helper function returns true if the currently authenticated user has permission * to access the controller:action specified by $controllerName:$actionName * @return * @param $controllerName Object * @param $actionName Object */ function __permitted($controllerName,$actionName){ //Ensure checks are all made lower case $controllerName = low($controllerName); $actionName = low($actionName); //If permissions have not been cached to session... if(!$this->Session->check('Permissions')){ //...then build permissions array and cache it $permissions = array(); //everyone gets permission to logout $permissions[]='users:logout'; //Import the User Model so we can build up the permission cache App::import('Model', 'User'); $thisUser = new User; //Now bring in the current users full record along with groups $thisGroups = $thisUser->find(array('User.id'=>$this->Auth->user('id'))); $thisGroups = $thisGroups['Group']; foreach($thisGroups as $thisGroup){ $thisPermissions = $thisUser->Group->find(array('Group.id'=>$thisGroup['id'])); $thisPermissions = $thisPermissions['Permission']; foreach($thisPermissions as $thisPermission){ $permissions[]=$thisPermission['name']; } } //write the permissions array to session $this->Session->write('Permissions',$permissions); }else{ //...they have been cached already, so retrieve them $permissions = $this->Session->read('Permissions'); } //Now iterate through permissions for a positive match foreach($permissions as $permission){ if($permission == '*'){ return true;//Super Admin Bypass Found } if($permission == $controllerName.':*'){ return true;//Controller Wide Bypass Found } if($permission == $controllerName.':'.$actionName){ return true;//Specific permission found } } return false; } } ?> [/code]طبعا باستخدام التحكم السابق سوف يتم تعيين الصلاحيات لكل مجموعة بحيث تكون كل صلاحية ممثلة باسم المتحكم ثم نقطتين ثم اسم الحدث المراد التصريح به Controller:Action مثال
clients:add
clients:index
clients:edit
الثلاث تصاريح السابقة تمنح المستخدم صلاحيات الاضافة والتحرير والصفحة الرئيسية لجدول clients هذا هو أسلوب منح الصلاحيات هنا
ولمنح كافة الصلاحيات يمكن استخدام النجمة * وكذلك يمنح منح كافة الـActions باستخدام النجمة مع اسم المتحكم كمثال :
clients:*
يمنح كافة الصلاحيات للأحداث لمتحكم clients
وهكذا ......
ملاحظة أخرى على السطر 24 : يكون التحقق بالاعتماد على اسم المستخدم username فلو أحببت بأن يكون التحقق على أي حقل آخر مثل البريد الالكتروني مثلا فقم باستبدال اسم الحقل باسم الحقل المطلوب .
نكمل الان باقي العمل حيث بقي القليل ، نقوم الان بإنشاء ملف الدخول
app/views/users/login.ctp
[code language='php']create('User', array('action' => 'login'));
echo $form->input('usersname',array('between'=>'
','class'=>'text'));
echo $form->input('password',array('between'=>'
','class'=>'text'));
echo $form->end('دخول');
?>
[/code]
حتى نعرض رسائل الصلاحيات نقوم بالتعديل على ملف القالب الافتراضي ونبحث عن
<div id="content"> ونضيف تحتها مباشرة
<?php $session->flash('auth'); ?>
قم الان بفتح ملف app/config/core.php
وقم مؤقتا بتغيير مفتاح Security.salt
إلى DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi1
وذلك بأن كلمة المرور 111 مشفرة بهذا المفتاح ، وتستطيع تغييرها بعد الدخول للنظام .
بعد إتمام هذه الخطوة نكون قد انتهينا من إعداد النظام ، يستحسن أن يكون لديك جداول خاصة بك لتستطيع التجربة عليها ، أما إذا لم يكن لديك أي جداول سابقة فيمكنك زيارة متحكم users كمثال http://localhost/test1/users/logout .
تستطيع الدخول الان لأي متحكم وولن يطلب منك الدخول لأنه كل الصلاحيات مفتوحة ، قم بإضافة بعض الصلاحيات الجديدة على بعض المتحكات الخاصة بك وكذلك لو أحببت قم بإضافة بعض المجموعات ومستخدمين آخرين ، بعد الانتهاء قم بتجربة الدخول بأكثر من مستخدم مختلفين في المجموعات .
ملاحظة أخيرة : إذا كانت أسماء الصلاحيات وغيرها لا تظهر اللغة العربية ويظهر بدلا منها علامات استفهام فأنصحك بتحديد نوع الترميز وذلك بإضافة السطر 'encoding' =>'UTF8' في ملف الربط مع قاعدة البيانات databse.php ليكون الكود النهائي كما يلي :
[code language='sql'] var $default = array( 'driver' => 'mysql', 'persistent' => false, 'host' => 'localhost', 'login' => 'root', 'password' => '1111', 'database' => 'test1', 'prefix' => '', 'encoding' =>'UTF8' ); [/code]
بقي الان نقوم نقوم ببناء نظام القوائم الذي يتغير بحسب الصلاحيات لكل مستخدم .. وهذا ما سنكمله في التدوينة القادمة بإذن الله ...
مراجع رئيسية لهذه التدوينة :Studio Canaria



التعليقات