دروس CakePHP # 5 : بناء نظام تعدد المستخدمين والمجموعات والصلاحيات.

برمجة وأفكار برمجية   CakePHP   الزيارات: 1879    التعليقات:4

users

نبدأ في هذه التدوينة بموضوع هام لأي تطبيق ويب يعمل بشكل عملي ألا وهو نظام إعداد  المستخدمين ومجموعاتهم والصلاحيات لكل مستخدم ونظام تسجيل الدخول والخروج وغير ذلك من الأمور الهامة لكل نظام ، وبعد كل تدوينة جديدة نقوم بدمج ما تعلمناه بنظام "بنك الأفكار الابداعية" . وهذا التدوينة تفيد بشكل كبير الأخوة الذين لديهم خبرة سابقة في الكيك ، وسوف نستخدم لبناء هذا النظام كومبوننت 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






التعليقات


  1. السلام عليكم ، تحية طيبة ، احسن الله اليك عزيزي على هذه الدورة الرائعة . لكن ، بكل صدق قرأت الموضوع هذا ! فلم استفد شيئا !!!! بتاتاً لم استفد شيئاً ، السبب نقتسمه الاثنان ، قصور من طرفي في الفهم و عدم التفصيل في الشرح من قبلكم . فلم اعرف مذا كتبت في الموديولات باكملها! و كيف ربطت الجداول ! و لا حتى ما اضفناه الى المتحكم الاب ان صح التعبير AppConroller . فبالرجاء الملاحظة اني مبتدأ في هذا الاطار ، لي خبرة سابقة في اطار CodeIgniter ، و بصراحه هو ابسط من هذا بكثييير . جزيت خيراً انتظر منك متابعة .

  2. حياك الله أخي حمزة ... وأعتذر عن عدم التفصيل في هذه الدروس لاعتقاد سابق بأن من سيتابعها سوف يكون لديه خبرة سابقة في الكيك .. ولكن يبدو بأن هذا الاعتقاد غير صحيح .. وهذا ما جعلني أتوقف فجأو رغم أنني قررت عدم التوقف ... حرصا على فائدة الجميع .. ولدي سؤال لك وهو بما أنك كنت تعم لعلى إطار CodeIgniter فما الذي جذبك للكيك ؟

  3. جزاكم الله خيرا على الشرح ولكن كنت أبحث عن عمل هذا النظام باستخدام ال bake console وبعملها باسلوب aco's and aro's and aco_aro لأنى أبحث عن مساعدة بهذا الاسلوب بارك الله فيكم

  4. جزاكم الله خيرا على الشرح لكن الأكواد لا تظهر بالشكل السليم مطلقا وذلك يصعب وصولي للفهم

أضف تعليقك على هذه التدوينة


باستخدام برنامج التدوين المفتوح المصدر FishBlog | هذا القالب بواسطة & الأيقونات بواسطة N.Design Studio
RSSدخول