98 votes

Comment puis-je mettre en place une liste de contrôle d’accès dans mon application Web MVC ?

Première question

S'il vous plaît, pourriez-vous m'expliquer comment faire plus simple ACL pourrait être mis en œuvre dans MVC.

Voici la première approche de l'utilisation des Acl dans le Contrôleur...

<?php
class MyController extends Controller {

  public function myMethod() {        
    //It is just abstract code
    $acl = new Acl();
    $acl->setController('MyController');
    $acl->setMethod('myMethod');
    $acl->getRole();
    if (!$acl->allowed()) die("You're not allowed to do it!");
    ...    
  }

}
?>

Elle est très mauvaise approche, et c'est moins, c'est que nous avons à ajouter Acl morceau de code dans chaque méthode du contrôleur, mais nous n'avons pas besoin de dépendances supplémentaires!

Suivant l'approche est de rendre tout du contrôleur de méthodes d' private et ajouter le code ACL dans du contrôleur __call méthode.

<?php
class MyController extends Controller {

  private function myMethod() {
    ...
  }

  public function __call($name, $params) {
    //It is just abstract code
    $acl = new Acl();
    $acl->setController(__CLASS__);
    $acl->setMethod($name);
    $acl->getRole();
    if (!$acl->allowed()) die("You're not allowed to do it!");
    ...   
  }

}
?>

Il est mieux que le précédent code, mais les principaux inconvénients sont...

  • Tous les contrôleur de méthodes devrait être privé
  • Nous avons à ajouter le code ACL dans chaque contrôleur __appel de méthode.

L'approche suivante est de mettre le code Acl en Contrôleur de parent, mais nous avons encore besoin de garder tous les enfants du contrôleur de méthodes privées.

Quelle est la solution? Et quelle est la meilleure pratique? Où dois-je appeler Acl fonctions de décider d'autoriser ou d'interdire la méthode à exécuter.

Deuxième question

Deuxième question concerne l'obtention de rôle à l'aide de l'Acl. Imaginons que nous avons des invités, des utilisateurs et de leurs amis. L'utilisateur dispose d'un accès limité à l'affichage de son profil que seuls vos amis peuvent voir. Tous les invités ne peuvent pas voir ce profil de l'utilisateur. Donc, ici, c'est la logique..

  • nous devons nous assurer que la méthode appelée est de profil
  • nous avons pour détecter le propriétaire de ce profil
  • nous avons à détecter est la visionneuse est propriétaire de ce profil ou pas
  • nous avons à lire les règles de restriction sur ce profil
  • nous devons décider de signer ou de ne pas exécuter le profil de méthode

La question principale est de détecter les propriétaire de profil. Nous pouvons détecter qui est propriétaire de profil de l'exécution du modèle de la méthode $model->getOwner(), mais Acl n'ont pas accès à de modèle. Comment pouvons-nous mettre en œuvre?

J'espère que mes pensées sont claires. Désolé pour mon anglais.

Je vous remercie.

187voto

tereško Points 32847

Première partie/réponse (ACL de mise en œuvre)

À mon humble avis, la meilleure façon de procéder serait d'utiliser le pattern décorateur, Fondamentalement, cela signifie que vous prenez votre objet, et de le placer à l'intérieur d' un autre objet, qui va agir comme une coque de protection. Ce ne serait PAS vous obliger à prolonger la classe d'origine. Voici un exemple:

class SecureContainer
{

    protected $target = null;
    protected $acl = null;

    public function __construct( $target, $acl )
    {
        $this->target = $target;
        $this->acl = $acl;
    }

    public function __call( $method, $arguments )
    {
        if ( 
             method_exists( $this->target, $method )
          && $this->acl->isAllowed( get_class($this->target), $method )
        ){
            return call_user_func_array( 
                array( $this->target, $method ),
                $arguments
            );
        }
    }

}

Et ce serait la façon dont vous utilisez ce type de structure:

// assuming that you have two objects already: $currentUser and $controller
$acl = new AccessControlList( $currentUser );

$controller = new SecureContainer( $controller, $acl );
// you can execute all the methods you had in previous controller 
// only now they will be checked against ACL
$controller->actionIndex();

Comme vous pouvez le remarquer, cette solution a plusieurs avantages:

  1. le confinement peut être utilisé sur n'importe quel objet, pas seulement des instances de l' Controller
  2. vérification de l'autorisation qui se passe à l'extérieur de l'objet cible, ce qui signifie que:
    • objet d'origine n'est pas responsable pour le contrôle d'accès, adhère à la SRP
    • lorsque vous obtenez le message "permission denied", vous n'êtes pas enfermé à l'intérieur d'un contrôleur, plus d'options
  3. vous pouvez injecter cette sécurisée instance dans n'importe quel autre objet, il conservera la protection
  4. l'envelopper et l'oublier .. vous pouvez prétendre qu'il est l'objet d'origine, il va réagir de la même

Mais, il y a un problème majeur avec cette méthode vous ne peut pas nativement de vérifier si l'objet sécurisé met en œuvre et de l'interface ( qui s'applique également pour la recherche d'méthodes existantes ) ou fait partie d'un héritage de la chaîne.

Deuxième partie/répondre RBAC (pour les objets)

Dans ce cas, la principale différence que l'on doit reconnaître, c'est que vous avez des Objets du Domaine (dans l'exemple: Profile) elle-même contient des détails sur le propriétaire. Cela signifie, que pour vous de vérifier, si (et à quel niveau) l'utilisateur a accès, il va vous obliger à modifier cette ligne:

$this->acl->isAllowed( get_class($this->target), $method )

Essentiellement, vous avez deux options:

  • Fournir les ACL avec l'objet en question. Mais vous devez faire attention de ne pas violer la Loi de Déméter:

    $this->acl->isAllowed( get_class($this->target), $method )
    
  • Demande tous les détails pertinents et de fournir de l'ACL seulement avec ce dont il a besoin, ce qui permettra également de faire un peu plus de tests unitaires de l'environnement:

    $command = array( get_class($this->target), $method );
    /* -- snip -- */
    $this->acl->isAllowed( $this->target->getPermissions(), $command )
    

Quelques vidéos qui pourraient vous aider à venir avec votre propre mise en œuvre:

Côté notes

Vous semblez avoir le bien commun ( et complètement faux ), la compréhension de ce Modèle MVC. Le modèle n'est pas une classe. Si vous avez de la classe nommée FooBarModel ou quelque chose qui hérite AbstractModel , alors vous faites fausse route.

En bon MVC le Modèle est une couche qui contient beaucoup de classes. Grande partie des classes peuvent être séparées en deux groupes , en fonction de la responsabilité:

- Domaine De La Logique Métier

(lire en plus: ici et ici):

Les Instances de ce groupe de classes d'effectuer le calcul de valeurs, de vérifier les diverses conditions, de mettre en œuvre les ventes de règles et de faire tout le reste de ce que vous appelez "logique d'entreprise". Ils n'ont aucune idée de la façon dont les données sont stockées, où il est stocké ou, même si le stockage existe en premier lieu.

Entreprise du domaine de l'objet ne dépend pas de la base de données. Lorsque vous créez une facture, il n'importe pas où proviennent les données. Il peut être soit à partir de SQL ou à distance à partir d'un API REST, ou encore capture d'écran d'un document MSWord. La logique d'entreprise n'a pas de changement.

- Accès aux données et de Stockage

Les Instances issues de ce groupe de classes sont parfois appelés Objets d'Accès aux Données. Généralement les structures qui mettent en œuvre de Mapper des Données de motif ( à ne pas confondre avec l'Orm de même nom .. aucun rapport ). C'est là que vos instructions SQL (ou peut-être votre DomDocument, parce que vous le stocker dans le XML).

À côté de ces deux grandes parties, il y a un autre groupe de cas/classes, qui doivent être mentionnées:

- Les Services de

C'est là que votre et 3ème partie composantes entrent en jeu. Par exemple, vous pouvez penser à "authentification" en tant que service, qui peut être fourni par votre propre, ou de certains de code externe. Aussi "le courrier de l'expéditeur" serait un service, qui peut tricoter ensemble du domaine de l'objet avec un PHPMailer ou SwiftMailer, ou de votre propre messagerie composant de l'expéditeur.

Une autre source de services sont abstraction sur sur le domaine et les couches d'accès aux données. Ils sont créés pour simplifier le code utilisé par les contrôleurs. Par exemple: la création d'un nouveau compte d'utilisateur peut nécessiter de travailler avec plusieurs objets du domaine et des cartographes. Mais, par le biais d'un service, il n'aura besoin que d'une ou deux lignes dans le contrôleur.

Ce que vous devez retenir lors de la prise de services, est que l'ensemble de la couche est censé être mince. Il n'y a pas de logique métier dans les services. Ils ne sont là que de jongler avec des objets de domaine, des composants et des cartographes.

L'une des choses qu'ils ont tous en commun serait que les services ne sont pas d'incidence sur le point de Vue de la couche de manière directe, et sont autonomes à un point tel qu'ils peuvent être ( et quittez souvent - sont ) utilisé à l'extérieur de la structure MVC. Tel de auto-entretenue des structures de faire la migration vers un autre cadre/architecture beaucoup plus facile, en raison de très faible couplage entre le service et le reste de l'application.

16voto

hakre Points 102271

Les ACL et les Contrôleurs

Tout d'abord: ce sont des choses différentes / couches le plus souvent. Comme vous le critiquer les exemplaires code du contrôleur, il met les deux ensemble - le plus évidemment trop serré.

tereško déjà décrites d'une manière dont on pourrait dissocier ce plus avec le décorateur modèle.

Je ferais un pas en arrière, d'abord à chercher l'origine du problème auquel vous êtes confronté et d'en discuter un peu, puis.

D'une part, vous voulez avoir les contrôleurs de juste faire le travail qu'ils commandé (commande ou action, appelons ça de la commande).

D'autre part, vous voulez être en mesure de mettre des ACL dans votre application. Le domaine de travail de ces listes doit être - si j'ai bien compris votre question de droit de contrôler l'accès à certaines commandes de vos applications.

Ce type de contrôle d'accès a donc besoin de quelque chose d'autre qui rapproche ces deux ensemble. Basé sur le contexte dans lequel une commande est exécutée dans, ACL de coups de pied dans les décisions doivent être fait si ou non d'une commande peut être exécutée par un sujet particulier (par exemple l'utilisateur).

Résumons à ce point ce que nous avons:

  • Commande
  • ACL
  • L'utilisateur

Le composant ACL est ici une place centrale: Il doit au moins savoir quelque chose au sujet de la commande (pour identifier la commande pour être précis) et il doit être en mesure d'identifier l'utilisateur. Les utilisateurs sont normalement facilement identifié par un ID unique. Mais souvent dans les applications il y a des utilisateurs qui ne sont pas identifiés, souvent appelé invité, anonyme, tout le monde etc.. Pour cet exemple, nous supposons que l'ACL peut consommer un objet utilisateur et encapsuler ces détails loin. L'utilisateur de l'objet est lié à la demande d'application de l'objet et de l'ACL peut consommer.

Ce sujet de l'identification d'une commande? Votre interprétation du modèle MVC suggère qu'une commande est composé d'un nom de classe et un nom de méthode. Si nous regardons de plus près il y a même des arguments (paramètres) pour une commande. Donc c'est valable pour demander ce qu'est exactement identifie une commande? Le nom de la classe, le methodname, le numéro ou le nom des arguments, même les données à l'intérieur des arguments ou un mélange de tout cela?

Selon le niveau de détail dont vous avez besoin pour identifier une commande dans votre ACL pratiquent, cela peut varier beaucoup. Pour l'exemple, nous allons le garder simplement et de préciser qu'une commande est identifiée par le nom de la classe et le nom de la méthode.

De sorte que le contexte de la façon dont ces trois parties (ACL, de Commande et de l'Utilisateur) appartiennent les uns aux autres est maintenant plus clair.

On pourrait dire, avec un imaginaire ACL compontent on peut déjà faire le suivant:

$acl->commandAllowedForUser($command, $user);

Juste à voir ce qui est happeninig ici: En faisant à la fois le commandement et l'utilisateur identifiable, de l'ACL peut faire, c'est le travail. Le travail de l'ACL est sans rapport avec le travail de l'utilisateur de l'objet et le béton de commande.

Il y a une seule chose qui manque, ce ne peut pas vivre dans l'air. Et il ne le fait pas. Si vous avez besoin de localiser l'endroit où le contrôle d'accès doit coup de pied dans. Jetons un coup d'oeil ce qui se passe dans une norme webapplication:

User -> Browser -> Request (HTTP)
   -> Request (Command) -> Action (Command) -> Response (Command) 
   -> Response(HTTP) -> Browser -> User

Pour localiser ce lieu, nous savons que cela doit être avant que la commande est exécutée, de sorte que nous pouvons réduire la liste, puis seulement besoin de regarder dans la suite (potentiel) des lieux:

User -> Browser -> Request (HTTP)
   -> Request (Command)

À un certain moment dans votre application, vous savez qu'un utilisateur a demandé de réaliser un béton de commande. Vous faites déjà une sorte d'ACL avec ici: Si un utilisateur demande une commande qui n'existe pas, vous ne laissez pas que la commande à exécuter. Alors, où jamais qui se passe dans votre application pourrait être un bon endroit pour ajouter le "réel" ACL vérifie:

La commande a été localisé et que nous pouvons créer l'identification de celui-ci afin de l'ACL peut traiter avec elle. Dans le cas où la commande n'est pas autorisé pour un utilisateur, la commande ne sera pas exécutée (action). Peut-être un CommandNotAllowedResponse , au lieu de l' CommandNotFoundResponse pour les cas une demande ne peut pas être résolu sur un béton de commande.

L'endroit où la cartographie d'un béton HTTPRequest est mappée sur une commande est souvent appelé Routage. Comme le Routage a déjà le travail pour repérer une commande, pourquoi ne pas l'étendre à vérifier si la commande est effectivement autorisé par l'ACL? E. g. par l'extension de l' Router d'une ACL conscient routeur: RouterACL. Si votre routeur ne connaissent pas encore l' User, puis l' Router n'est pas le bon endroit, parce que pour l'ACL qui pratiquent non seulement la commande, mais aussi l'utilisateur doit être identifié. Si ce lieu peut varier, mais je suis sûr que vous pouvez facilement localiser l'endroit où vous devez étendre, parce que c'est l'endroit capable de réaliser l'utilisateur et la commande exigence:

User -> Browser -> Request (HTTP)
   -> Request (Command)

L'utilisateur est disponible depuis le début, d'abord la Commande avec Request(Command).

Ainsi, au lieu de mettre votre ACL contrôles à l'intérieur de chaque commande de la mise en œuvre concrète, vous la placez à l'avant. Vous n'avez pas besoin de lourdes modèles, de la magie, ou que ce soit, l'ACL-t-il son travail, l'utilisateur t-il son travail et surtout de la commande-t-il son emploi: il suffit de la commande, rien d'autre. La commande n'a aucun intérêt à savoir si ou de ne pas les rôles s'appliquer à elle, si c'est gardée quelque part ou pas.

Il suffit donc de garder les choses en dehors qui n'appartiennent pas les uns aux autres. Un peu la reformulation du Principe de Responsabilité Unique (SRP): Il devrait y avoir qu'une seule raison de changer une commande parce que la commande a changé. Non pas parce que vous présentez ACL pratiquent dans votre application. Non pas parce que vous passez l'Utilisateur de l'objet. Non pas parce que vous migrez à partir d'une adresse HTTP/HTML interface à un SAVON ou une interface de ligne de commande.

L'ACL dans votre cas, les contrôles de l'accès à une commande, pas la commande elle-même.

13voto

Artefacto Points 50896

Une possibilité est de placer toutes vos contrôleurs dans une autre classe qui étend la classe Contrôleur et il déléguer tous les appels de fonction à la enveloppé exemple, après vérification de l'autorisation.

Vous pouvez également le faire plus en amont, dans le répartiteur (si votre application ne prend en effet en avez un) et recherche les autorisations sur la base de l'Url, à la place de méthodes de contrôle.

edit: Si vous avez besoin d'accéder à une base de données, un serveur LDAP, etc. est orthogonal à la question. Mon point est que vous pouvez mettre en œuvre une autorisation fondée sur l'Url au lieu de méthodes de contrôleur. Ces est plus robuste parce que généralement, vous ne serez pas changer votre Url (Url de type zone de l'interface publique), mais vous pourriez tout aussi bien changer les implémentations de vos contrôleurs.

En général, vous avez un ou plusieurs fichiers de configuration où vous carte des modèles URL spécifique des méthodes d'authentification et d'autorisation des directives. Le répartiteur, avant l'envoi de la demande pour les contrôleurs, détermine si l'utilisateur est autorisé et abandonne l'envoi si il ne l'est pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X