سؤال

أنا أجد صعوبة في تنفيذ ACL في CakePHP.بعد قراءة الوثائق في دليل الكعكة بالإضافة إلى العديد من البرامج التعليمية الأخرى ومنشورات المدونات وما إلى ذلك، وجدت البرنامج التعليمي الممتاز لـ Aran Johnson والذي ساعد في سد العديد من الثغرات.يبدو أن الأمثلة التي قدمها تتعارض مع الأمثلة الأخرى التي رأيتها في أماكن قليلة - وتحديدًا في بنية شجرة ARO التي يستخدمها.

في أمثلة تم إعداد مجموعات المستخدمين الخاصة به كشجرة متتالية، مع وجود نوع المستخدم الأكثر عمومية في أعلى الشجرة، ويتفرع أبناءه لكل نوع وصول مقيد أكثر.في أماكن أخرى، عادةً ما أرى كل نوع مستخدم كطفل من نفس نوع المستخدم العام.

كيف يمكنك إعداد AROs وACOs في CakePHP؟أي وجميع النصائح موضع تقدير!

هل كانت مفيدة؟

المحلول

يعد نظام ACL المدمج في CakePHP قويًا حقًا، ولكنه غير موثق بشكل جيد من حيث تفاصيل التنفيذ الفعلي.النظام الذي استخدمناه مع بعض النجاح في عدد من المشاريع القائمة على CakePHP هو كما يلي.

إنه تعديل لبعض أنظمة الوصول على مستوى المجموعة التي كانت موثقة في مكان آخر.تتمثل أهداف نظامنا في الحصول على نظام بسيط حيث يتم ترخيص المستخدمين على مستوى المجموعة، ولكن يمكن أن يكون لديهم حقوق إضافية محددة على العناصر التي قاموا بإنشائها، أو على أساس كل مستخدم.أردنا تجنب الاضطرار إلى إنشاء إدخال محدد لكل مستخدم (أو بشكل أكثر تحديدًا لكل ARO) في ملف aros_acos طاولة.

لدينا جدول المستخدمين وجدول الأدوار.

المستخدمين

user_id, user_name, role_id

الأدوار

id, role_name

قم بإنشاء شجرة ARO لكل دور (لدينا عادة 4 أدوار - ضيف غير مصرح به (المعرف 1)، مستخدم معتمد (المعرف 2)، مشرف الموقع (المعرف 3) والمسؤول (المعرف 4)) :

cake acl create aro / Role.1

cake acl create aro 1 Role.2 ... etc ...

بعد ذلك، يجب عليك استخدام SQL أو phpMyAdmin أو ما شابه ذلك لإضافة أسماء مستعارة لكل هذه الأشياء، حيث أن أداة سطر أوامر الكعكة لا تفعل ذلك.نحن نستخدم "Role-{id}" و"User-{id}" لجميع منتجاتنا.

نقوم بعد ذلك بإنشاء ROOT ACO -

cake acl create aco / 'ROOT'

ثم قم بإنشاء ACOs لجميع وحدات التحكم ضمن ROOT هذا:

cake acl create aco 'ROOT' 'MyController' ... etc ...

حتى الآن طبيعي جدا.نضيف حقلاً إضافيًا في جدول aros_acos يسمى _editown والتي يمكننا استخدامها كإجراء إضافي في actionMap الخاص بمكون ACL.

CREATE TABLE IF NOT EXISTS `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(11) NOT NULL default '0',
`_read` int(11) NOT NULL default '0',
`_update` int(11) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
`_editown` int(11) NOT NULL default '0',
PRIMARY KEY  (`id`),
KEY `acl` (`aro_id`,`aco_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

يمكننا بعد ذلك إعداد مكون المصادقة لاستخدام الأسلوب "الخام"، الذي يتحقق من صحة وحدة التحكم/الإجراء المطلوب مقابل AclComponent::check().في app_controller لدينا شيء على غرار:

private function setupAuth() {
    if(isset($this->Auth)) {
        ....
        $this->Auth->authorize = 'crud';
        $this->Auth->actionMap = array( 'index'     => 'read',
                        'add'       => 'create',
                        'edit'      => 'update'
                        'editMine'  => 'editown',
                        'view'      => 'read'
                        ... etc ...
                        );
        ... etc ...
    }
}

مرة أخرى، هذه أشياء قياسية إلى حد ما في CakePHP.لدينا بعد ذلك طريقة checkAccess في AppController والتي تضيف العناصر على مستوى المجموعة للتحقق مما إذا كان سيتم التحقق من مجموعة ARO أو ARO للمستخدم للوصول:

private function checkAccess() {
    if(!$user = $this->Auth->user()) {
        $role_alias = 'Role-1';
        $user_alias = null;
    } else {
        $role_alias = 'Role-' . $user['User']['role_id'];
        $user_alias = 'User-' . $user['User']['id'];
    }

    // do we have an aro for this user?
    if($user_alias && ($user_aro = $this->User->Aro->findByAlias($user_alias))) {
        $aro_alias = $user_alias;
    } else {
        $aro_alias = $role_alias;
    }

    if ('editown' == $this->Auth->actionMap[$this->action]) {
        if($this->Acl->check($aro_alias, $this->name, 'editown') and $this->isMine()) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    } else {
        // check this user-level aro for access
        if($this->Acl->check($aro_alias, $this->name, $this->Auth->actionMap[$this->action])) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    }
}

ال setupAuth() و checkAccess() يتم استدعاء الأساليب في AppControllerbeforeFilter() أتصل مرة أخرى.هناك isMine الطريقة في AppControler أيضًا (انظر أدناه) والتي تتحقق فقط من أن معرف المستخدم الخاص بالعنصر المطلوب هو نفس المستخدم الذي تمت مصادقته حاليًا.لقد تركت هذا من أجل الوضوح.

هذا حقا كل ما في الأمر.يمكنك بعد ذلك السماح/رفض وصول مجموعات معينة إلى نقاط اتصال محددة -

cake acl grant 'Role-2' 'MyController' 'read'

cake acl grant 'Role-2' 'MyController' 'editown'

cake acl deny 'Role-2' 'MyController' 'update'

cake acl deny 'Role-2' 'MyController' 'delete'

أنا متأكد من أنك حصلت على الصورة.

على أي حال، هذه الإجابة أطول بكثير مما كنت أقصده، وربما لا يكون لها أي معنى، ولكن آمل أن تكون مفيدة لك ...

-- يحرر --

كما هو مطلوب، إليك تعديل (للتوضيح فقط - هناك الكثير من الأشياء في الكود المعياري الخاص بنا والتي لا معنى لها هنا) isMine() الطريقة التي لدينا في AppController لدينا.لقد قمت أيضًا بإزالة الكثير من عناصر التحقق من الأخطاء، ولكن هذا هو جوهر الأمر:

function isMine($model=null, $id=null, $usermodel='User', $foreignkey='user_id') {
    if(empty($model)) {
        // default model is first item in $this->uses array
        $model = $this->uses[0];
    }

    if(empty($id)) {
        if(!empty($this->passedArgs['id'])) {
        $id = $this->passedArgs['id'];
        } elseif(!empty($this->passedArgs[0])) {
            $id = $this->passedArgs[0];
        }
    }

    if(is_array($id)) {
        foreach($id as $i) {
            if(!$this->_isMine($model, $i, $usermodel, $foreignkey)) {
                return false;
            }
        }

        return true;
    }

    return $this->_isMine($model, $id, $usermodel, $foreignkey);
}


function _isMine($model, $id, $usermodel='User', $foreignkey='user_id') {
    $user = Configure::read('curr.loggedinuser'); // this is set in the UsersController on successful login

    if(isset($this->$model)) {
        $model = $this->$model;
    } else {
        $model = ClassRegistry::init($model);
    }

    //read model
    if(!($record = $model->read(null, $id))) {
        return false;
    }

    //get foreign key
    if($usermodel == $model->alias) {
        if($record[$model->alias][$model->primaryKey] == $user['User']['id']) {
            return true;
        }
    } elseif($record[$model->alias][$foreignkey] == $user['User']['id']) {
        return true;
    }

    return false;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top