Question

Je construis un site communautaire dans Rails pour les membres d'une organisation du monde réel. J'essaie d'adhérer aux meilleures pratiques de la conception RESTful, et la plupart d'entre elles sont plus ou moins à la livre. La question qui me fait tourner la tête dans les cercles RESTful est celle de l'autorisation. L’authentification est un problème simple et résolu de longue date avec des solutions RESTful largement acceptées, mais l’autorisation RESTful semble être un peu un art noir. J'essaie de trouver l'approche qui fournira le cadre le plus général et le plus flexible pour contrôler l'accès aux ressources tout en restant aussi simple que possible, tout en respectant une architecture RESTful. (Aussi, un poney.)

Considérations:

  1. Je dois contrôler l'accès à diverses ressources, telles que les utilisateurs, les pages, les publications, etc. ".
  2. L’autorisation pour une ressource donnée doit être plus fine que le simple CRUD.
  3. Je souhaite me permettre, ainsi qu'à d'autres personnes, de modifier les règles d'autorisation à partir de l'application.
  4. Les règles d'autorisation doivent pouvoir dépendre de prédicats, tels que le propriétaire (conceptuel) du propriétaire (utilisateur, ressource) ou le verrouillé (sujet)

La considération (2) est celle qui me concerne le plus. Il semble y avoir un décalage d’impédance entre ma conception des autorisations et la conception RESTful des actions. Par exemple, prenez les messages (comme dans un forum). REST dicte l'existence de quatre opérations sur la ressource Post: Créer, Lire, Mettre à jour et Supprimer. Il est simple de dire qu'un utilisateur devrait pouvoir mettre à jour ses propres publications, mais seuls certains utilisateurs (ou rôles, ou groupes) devraient être autorisés à les verrouiller. La manière traditionnelle de représenter le verrouillage est dans l’état de la publication, mais cela laisse supposer qu’un utilisateur dans les mêmes conditions peut ou non pouvoir mettre à jour une publication en fonction des valeurs (complètement valides) qu’il fournit. Il me semble évident qu'il y a en réalité deux actions différentes pour changer l'état de la poste, et les ignorer, c'est simplement dissimuler une violation des principes RESTful.

(Je dois noter que ce problème est assez distinct du problème d'une mise à jour infructueuse en raison de données invalides ou incohérentes & # 8212; une demande de verrouillage émanant d'un utilisateur sans privilège est en principe tout à fait valide, simplement interdit.)

  

La décomposition n'est-elle pas un autre mot pourri?

Cela peut être surmonté en décomposant la publication: un verrou est une sous-source d'une publication particulière. Créer ou détruire une personne peut alors disposer d'autorisations distinctes. Cette solution a l’appellation de REST, mais pose des difficultés à la fois théoriques et pratiques. Si je décompose les verrous, qu’en est-il des autres attributs? Supposons que je décide, dans un caprice du caprice, que seul un membre de l'administrateur devrait être autorisé à modifier le titre de la poste? Un simple changement d'autorisation nécessiterait alors une restructuration de la base de données pour l'adapter! Ce n'est pas vraiment une solution. Pour permettre ce genre de flexibilité dans le cadre d’une stratégie de décomposition, il faudrait que chaque attribut soit une ressource. Cela pose un peu un dilemme. Mon hypothèse implicite a été qu'une ressource est représentée dans la base de données sous forme de table. Sous cette hypothèse, une ressource pour chaque attribut signifie une table pour chaque attribut. Clairement, ce n'est pas pratique. Cependant, supprimer cette hypothèse présente un déséquilibre impédance entre les tables et les ressources, ce qui pourrait ouvrir sa propre boîte de Pandore. Utiliser cette approche nécessiterait une réflexion beaucoup plus approfondie que celle que je l’ai donnée. D'une part, les utilisateurs s'attendent raisonnablement à pouvoir modifier plusieurs attributs à la fois. Où va la demande? À la plus petite ressource qui contient tous les attributs? À chaque ressource individuelle en parallèle? Vers la lune?

  

Certaines de ces choses ne ressemblent pas aux autres & # 8230;

Supposons alors que je ne décompose pas les attributs. L’alternative semble alors définir un ensemble de privilèges pour chaque ressource. À ce stade, cependant, l'homogénéité de REST est perdue. Afin de définir les règles d'accès pour une ressource, le système doit avoir une connaissance spécifique des capacités de cette ressource. En outre, il est maintenant impossible de propager de manière générique les autorisations sur les ressources descendantes, même si une ressource enfant dispose d’un privilège du même nom, il n’existe aucune connexion sémantique inhérente entre ces privilèges. Définir un ensemble de privilèges standard de type REST me semble être le pire des deux mondes. Je suis donc coincé avec une hiérarchie des autorisations distincte pour chaque type de ressource.

  

Eh bien, nous avons fait le nez. Et le chapeau. Mais c'est une ressource!

Une suggestion que j'ai déjà vue qui atténue certains des inconvénients de l'approche ci-dessus consiste à définir les autorisations comme création / suppression sur les ressources et en lecture / écriture sur les attributs . Ce système est un compromis entre les attributs en tant que ressources et les privilèges par ressource: il ne reste que CRUD, mais aux fins de l’autorisation, Lire et Mettre à jour se rapportent à des attributs, que l’on pourrait qualifier de pseudo-ressources. Cela offre de nombreux avantages pratiques de l’approche attribut-ressources, bien que l’intégrité conceptuelle soit compromise dans une certaine mesure. Les autorisations peuvent toujours se propager d'une ressource à l'autre et d'une ressource à une pseudo-ressource, mais jamais à partir d'une pseudo-ressource. Je n'ai pas complètement exploré les ramifications de cette stratégie, mais il semble que cela puisse être prometteur. Il me semble qu'un tel système fonctionnerait mieux en tant que partie intégrante du modèle. Dans Rails, par exemple, il pourrait s'agir d'une adaptation de ActiveRecord . Cela me semble assez radical, mais l'autorisation est une préoccupation transversale tellement fondamentale que cela peut être justifié.

  

Oh, et n'oubliez pas le poney

Tout cela ignore le problème des autorisations prédicatives. De toute évidence, un utilisateur devrait pouvoir éditer ses propres messages, mais personne d'autre. De même, le tableau des autorisations écrites par l'administrateur ne doit pas comporter d'enregistrements distincts pour chaque utilisateur. Il s’agit là d’une exigence peu commune et l’astuce consiste à la rendre générique. Je pense que toutes les fonctionnalités dont j'ai besoin pourraient être obtenues en ne prenant que les règles comme prédicatives, de sorte que l'applicabilité de la règle puisse être décidée rapidement et immédiatement. Une règle " autorise l'utilisateur à écrire un message où Auteur (utilisateur, message) " se traduirait par "" pour tous les utilisateurs, toutes les publications, tel que l'auteur (utilisateur, la publication), autoriser l'utilisateur à rédiger la publication "et" > nier toute écriture lorsque la publication serait verrouillée (/) " pour "" pour toutes les publications telles que Verrouillé (publication), refuse toute écriture "". (Ce serait grand si tous ces prédicats pouvaient être exprimés en termes d'un utilisateur et d'une ressource.) Le résultat final "conceptuel" les règles seraient non prédicatives. Cela soulève la question de savoir comment mettre en œuvre un tel système. Les prédicats doivent être membres des classes Model, mais je ne vois pas comment on pourrait les appeler gracieusement dans le contexte des règles. Pour le faire en toute sécurité, il faudrait une sorte de réflexion. Là encore, j’ai le sentiment que cela nécessiterait une adaptation ultérieure de la mise en œuvre du modèle.

  

Comment épelez-vous encore cela?

La dernière question est de savoir comment représenter au mieux ces règles d'autorisation en tant que données. Une table de base de données peut faire l'affaire, avec des colonnes enum pour autoriser / refuser et C / R / U / D (ou peut-être des bits CRUD? Ou peut-être {C, R, U, D} & # 215; {autoriser, refuser, hériter }?), et une colonne de ressources avec un chemin. Peut-être, pour plus de commodité, un "hériter" bit. Je suis désemparé par rapport aux prédicats. Table séparée? Certainement beaucoup de cache pour l'empêcher d'être trop trop impie.

Je suppose que c'est beaucoup demander. J'ai essayé de faire mes devoirs avant de poser la question, mais pour le moment, j'ai vraiment besoin d'une perspective extérieure. J'apprécierais toute contribution de votre part à ce problème.

Était-ce utile?

La solution

Désolé, je n’ai pas le temps de répondre à cette question, mais c’est bien de voir des questions bien pensées sur SO. Voici quelques commentaires:

Ne tombez pas dans le piège de mapper les verbes HTTP à CRUD. Oui, GET et DELETE mappent assez proprement, mais PUT peut créer et mettre à jour (mais le remplacement complet uniquement) et POST est un verbe générique. POST est vraiment pour gérer tout ce qui ne rentre pas dans GET, PUT et EFFACER.

L'utilisation d'attributs pour représenter le statut d'un objet n'est qu'une approche de la gestion des états. Je suppose que vous pouvez imaginer ce que la requête suivante pourrait faire:

POST /LockedPosts?url=/Post/2010

Une sous-ressource est également une approche valide pour gérer l'état actuel d'une ressource. Je ne me sentirais pas obligé de traiter "l'état" d'une ressource. attributs et ses "données" attribue de manière cohérente.

Tenter de mapper des ressources directement sur des tables va vous contraindre sérieusement. N'oubliez pas que lorsque vous suivez les contraintes REST, les verbes disponibles sont très limités. Vous devez être en mesure de compenser cela en faisant preuve de créativité dans les ressources que vous utilisez. Vous limiter à une ressource égale à une table limitera sévèrement les fonctionnalités de votre application finale.

Nous voyons régulièrement des utilisateurs de Rails, ASP.NET MVC et de repos WCF poser des questions ici sur StackOverflow sur la manière dont certaines choses s’effectuent dans les contraintes de REST. Le problème n'est souvent pas une contrainte de REST mais les limites du cadre dans sa prise en charge des applications RESTful. Je pense qu’il est essentiel de commencer par trouver une solution RESTful à un problème, puis de voir s’il peut être adapté à la structure de votre choix.

En ce qui concerne la création d'un modèle d'autorisations dont le grain est plus fin que celui de la ressource elle-même. N'oubliez pas que l'une des principales contraintes de REST est l'hypermédia. Hypermedia peut être utilisé pour autre chose que la recherche d'entités associées, il peut également être utilisé pour représenter des transitions d'état valides / autorisées. Si vous renvoyez une représentation contenant des liens incorporés, conditionnellement basée sur des autorisations, vous pouvez alors contrôler quelles actions peuvent être effectuées par qui. En d'autres termes, si un utilisateur dispose des autorisations nécessaires pour déverrouiller le POST 342, vous pouvez renvoyer le lien suivant incorporé dans la représentation:

<Link href="/UnlockedPosts?url=/Post/342" method="POST"/>

S'ils ne disposent pas de cette autorisation, ne renvoyez pas le lien.

Je pense que l’une de vos difficultés ici est que vous essayez de traiter un trop gros problème à la fois. Je pense que vous devez examiner l'interface RESTful que vous essayez d'exposer au client, ce qui est un problème distinct de la façon dont vous allez gérer les autorisations et les prédicats afin de gérer l'autorisation dans votre modèle de domaine.

Je réalise que je n'ai répondu directement à aucune de vos questions, mais j'espère avoir fourni quelques points de vue pouvant aider d'une certaine manière.

Autres conseils

J'ai récemment découvert une solution d'authentification qui semble résoudre la plupart de mes problèmes. Si vous préférez cette question, elle pourrait vous intéresser:

https://github.com/stffn/declarative_authorization

Comme Darrel l'a fait remarquer - REST n'est pas CRUD. Si vous trouvez que vos ressources identifiées sont trop grossières et que l'interface uniforme ne fournit pas assez de contrôle, divisez votre ressource en sous-ressources et utilisez la ressource d'origine en tant que "collection" de liens hypertexte vers ses composants.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top