test and fix case where remove_unsused_solutions remove some solutions that should be kept
.. -*- coding: utf-8 -*-Définition d'un type d'entité-----------------------------Un type d'entité est définit par une classe python héritant de `EntityType`. Lenom de la classe correspond au nom du type. Ensuite le corps de la classecontient la description des attributs et des relations pour ce type d'entité,par exemple :: class Personne(EntityType): """une personne avec les propriétés et relations nécessaires à mon application""" nom = String(required=True, fulltextindexed=True) prenom = String(required=True, fulltextindexed=True) civilite = String(vocabulary=('M', 'Mme', 'Mlle')) date_naiss = Date() travaille_pour = SubjectRelation('Company', cardinality='?*')* le nom de l'attribut python correspond au nom de l'attribut ou de la relation dans cubicweb.* tout les types de bases sont disponibles nativement : `String`, `Int`, `Float`, `Boolean`, `Date`, `Datetime`, `Time`, `Byte`.* Chaque type d'entité a au moins les méta-relations suivantes : - `eid` (`Int`) - `creation_date` (`Datetime`) - `modification_date` (`Datetime`) - `created_by` (`CWUser`) (quel utilisateur a créé l'entité) - `owned_by` (`CWUser`) (à qui appartient l'entité, par défaut le créateur mais pas forcément et il peut exister plusieurs propriétaires) - `is` (`CWEType`)* il est également possible de définir des relations dont le type d'entité est l'objet en utilisant `ObjectRelation` plutôt que `SubjectRelation`* le premier argument de `SubjectRelation` et `ObjectRelation` donne respectivement le type d'entité objet /sujet de la relation. Cela peut être : * une chaine de caractères correspondant à un type d'entité * un tuple de chaines de caractères correspondant à plusieurs types d'entité * les chaînes de caractères spéciales suivantes : - "**" : tout les types d'entité - "*" : tout les types d'entité non méta - "@" : tout les types d'entité méta mais non "système" (i.e. servant à la description du schema en base)* il est possible d'utiliser l'attribut possible `meta` pour marquer un type d'entité comme étant "méta" (i.e. servant à décrire / classifier d'autre entités) * propriétés optionnelles des attributs et relations : - `description` : chaine de caractères décrivant un attribut ou une relation. Par défaut cette chaine sera utilisée dans le formulaire de saisie de l'entité, elle est donc destinée à aider l'utilisateur final et doit être marquée par la fonction `_` pour être correctement internationalisée. - `constraints` : liste de contraintes devant être respecté par la relation (c.f. `Contraintes`_) - `cardinality` : chaine de 2 caractères spécifiant la cardinalité de la relation. Le premier caractère donne la cardinalité de la relation sur le sujet, le 2eme sur l'objet. Quand une relation possède plusieurs sujets ou objets possibles, la cardinalité s'applique sur l'ensemble et non un à un (et doit donc à priori être cohérente...). Les valeurs possibles sont inspirées des expressions régulières : * `1`: 1..1 * `?`: 0..1 * `+`: 1..n * `*`: 0..n - `meta` : booléen indiquant que la relation est une méta relation (faux par défaut)* propriétés optionnelles des attributs : - `required` : booléen indiquant si l'attribut est obligatoire (faux par défaut) - `unique` : booléen indiquant si la valeur de l'attribut doit être unique parmi toutes les entités de ce type (faux par défaut) - `indexed` : booléen indiquant si un index doit être créé dans la base de données sur cette attribut (faux par défaut). C'est utile uniquement si vous savez que vous allez faire de nombreuses recherche sur la valeur de cet attribut. - `default` : valeur par défaut de l'attribut. A noter que dans le cas des types date, les chaines de caractères correspondant aux mots-clés RQL `TODAY` et `NOW` sont utilisables. - `vocabulary` : spécifie statiquement les valeurs possibles d'un attribut* propriétés optionnelles des attributs de type `String` : - `fulltextindexed` : booléen indiquant si l'attribut participe à l'index plein texte (faux par défaut) (*valable également sur le type `Byte`*) - `internationalizable` : booléen indiquant si la valeur de cet attribut est internationalisable (faux par défaut) - `maxsize` : entier donnant la taille maximum de la chaine (pas de limite par défaut) * propriétés optionnelles des relations : - `composite` : chaîne indiquant que le sujet (composite == 'subject') est composé de ou des objets de la relation. Pour le cas opposé (l'objet est composé de ou des sujets de la relation, il suffit de mettre 'object' comme valeur. La composition implique que quand la relation est supprimé (et donc aussi quand le composite est supprimé), le ou les composés le sont également. Contraintes```````````Par défaut les types de contraintes suivant sont disponibles :* `SizeConstraint` : permet de spécifier une taille minimale et/ou maximale sur les chaines de caractères (cas générique de `maxsize`)* `BoundConstraint` : permet de spécifier une valeur minimale et/ou maximale sur les types numériques* `UniqueConstraint` : identique à "unique=True"* `StaticVocabularyConstraint` : identique à "vocabulary=(...)"* `RQLConstraint` : permet de spécifier une requête RQL devant être satisfaite par le sujet et/ou l'objet de la relation. Dans cette requête les variables `S` et `O` sont préféfinies respectivement comme l'entité sujet et objet de la relation* `RQLVocabularyConstraint` : similaire à la précédente, mais exprimant une contrainte "faible", i.e. servant uniquement à limiter les valeurs apparaissant dans la liste déroulantes du formulaire d'édition, mais n'empêchant pas une autre entité d'être séléctionnéeDéfinition d'un type de relation--------------------------------Un type de relation est définit par une classe python héritant de `RelationType`. Lenom de la classe correspond au nom du type. Ensuite le corps de la classecontient la description des propriétés de ce type de relation, ainsiqu'éventuellement une chaine pour le sujet et une autre pour l'objet permettantde créer des définitions de relations associées (auquel cas il est possibles dedonner sur la classe les propriétés de définition de relation explicitéesci-dessus), par exemple :: class verrouille_par(RelationType): """relation sur toutes les entités applicatives indiquant que celles-ci sont vérouillées inlined = True cardinality = '?*' subject = '*' object = 'CWUser'En plus des permissions, les propriétés propres aux types de relation (et doncpartagés par toutes les définitions de relation de ce type) sont :* `inlined` : booléen contrôlant l'optimisation physique consistant à stocker la relation dans la table de l'entité sujet au lieu de créer une table spécifique à la relation. Cela se limite donc aux relations dont la cardinalité sujet->relation->objet vaut 0..1 ('?') ou 1..1 ('1')* `symetric` : booléen indiquant que la relation est symétrique. i.e. `X relation Y` implique `Y relation X`Dans le cas de définitions de relations simultanée, `sujet` et `object` peuventtout deux valoir la même chose que décrite pour le 1er argument de`SubjectRelation` et `ObjectRelation`.A partir du moment où une relation n'est ni mise en ligne, ni symétrique, etne nécessite pas de permissions particulières, sa définition (en utilisant`SubjectRelation` ou `ObjectRelation`) est suffisante.Définition des permissions--------------------------La définition des permissions se fait à l'aide de l'attribut `permissions` destypes d'entité ou de relation. Celui-ci est un dictionnaire dont les clés sontles types d'accès (action), et les valeurs les groupes ou expressions autorisées. Pour un type d'entité, les actions possibles sont `read`, `add`, `update` et`delete`.Pour un type de relation, les actions possibles sont `read`, `add`, et `delete`.Pour chaque type d'accès, un tuple indique le nom des groupes autorisés et/ouune ou plusieurs expressions RQL devant être vérifiées pour obtenirl'accès. L'accès est donné à partir du moment où l'utilisateur fait parti d'undes groupes requis ou dès qu'une expression RQL est vérifiée.Les groupes standards sont :* `guests`* `users`* `managers`* `owners` : groupe virtuel correspondant au propriétaire d'une entité. Celui-ci ne peut être utilisé que pour les actions `update` et `delete` d'un type d'entité. Il est également possible d'utiliser des groupes spécifiques devant être pourcela créés dans le precreate de l'application (`migration/precreate.py`).Utilisation d'expression RQL sur les droits en écriture```````````````````````````````````````````````````````Il est possible de définir des expressions RQL donnant des droits demodification (`add`, `delete`, `update`) sur les types d'entité et de relation.Expression RQL pour les permissions sur un type d'entité :* il faut utiliser la classe `ERQLExpression`* l'expression utilisée correspond à la clause WHERE d'une requête RQL* dans cette expression, les variables X et U sont des références prédéfinies respectivement sur l'entité courante (sur laquelle l'action est vérifiée) et sur l'utilisateur ayant effectué la requête* il est possible d'utiliser dans cette expression les relations spéciales "has_<ACTION>_permission" dont le sujet est l'utilisateur et l'objet une variable quelquonque, signifiant ainsi que l'utilisateur doit avoir la permission d'effectuer l'action <ACTION> sur la ou les entités liées cette variablePour les expressions RQL sur un type de relation, les principes sont les mêmesavec les différences suivantes :* il faut utiliser la classe `RRQLExpression` dans le cas d'une relation non finale* dans cette expression, les variables S, O et U sont des références prédéfinies respectivement sur le sujet et l'objet de la relation courante (sur laquelle l'action est vérifiée) et sur l'utilisateur ayant effectué la requête* On peut aussi définir des droits sur les attributs d'une entité (relation non finale), sachant les points suivants : - pour définir des expressions rql, il faut utiliser la classe `ERQLExpression` dans laquelle X représentera l'entité auquel appartient l'attribut - les permissions 'add' et 'delete' sont équivalentes. En pratique seul 'add'/'read' son pris en considérationEn plus de cela, le type d'entité `CWPermission` de la librairie standard permetde construire des modèles de sécurités très complexes et dynamiques. Le schémade ce type d'entité est le suivant : :: class CWPermission(MetaEntityType): """entity type that may be used to construct some advanced security configuration """ name = String(required=True, indexed=True, internationalizable=True, maxsize=100) require_group = SubjectRelation('CWGroup', cardinality='+*', description=_('groups to which the permission is granted')) require_state = SubjectRelation('State', description=_("entity'state in which the permission is applyable")) # can be used on any entity require_permission = ObjectRelation('**', cardinality='*1', composite='subject', description=_("link a permission to the entity. This " "permission should be used in the security " "definition of the entity's type to be useful."))Exemple de configuration extrait de *jpl* :: ... class Version(EntityType): """a version is defining the content of a particular project's release""" permissions = {'read': ('managers', 'users', 'guests',), 'update': ('managers', 'logilab', 'owners',), 'delete': ('managers', ), 'add': ('managers', 'logilab', ERQLExpression('X version_of PROJ, U in_group G,' 'PROJ require_permission P, P name "add_version",' 'P require_group G'),)} ... class version_of(RelationType): """link a version to its project. A version is necessarily linked to one and only one project. """ permissions = {'read': ('managers', 'users', 'guests',), 'delete': ('managers', ), 'add': ('managers', 'logilab', RRQLExpression('O require_permission P, P name "add_version",' 'U in_group G, P require_group G'),) } inlined = TrueCette configuration suppose indique qu'une entité `CWPermission` de nom"add_version" peut-être associée à un projet et donner le droit de créer desversions sur ce projet à des groupes spécifiques. Il est important de noter lespoints suivants :* dans ce cas il faut protéger à la fois le type d'entité "Version" et la relation liant une version à un projet ("version_of")* du fait de la généricité du type d'entité `CWPermission`, il faut effectuer l'unification avec les groupes et / ou les états le cas échéant dans l'expression ("U in_group G, P require_group G" dans l'exemple ci-dessus)Utilisation d'expression RQL sur les droits en lecture``````````````````````````````````````````````````````Les principes sont les mêmes mais avec les restrictions suivantes :* on ne peut de `RRQLExpression` sur les types de relation en lecture* les relations spéciales "has_<ACTION>_permission" ne sont pas utilisablesNote sur l'utilisation d'expression RQL sur la permission 'add'```````````````````````````````````````````````````````````````L'utilisation d'expression RQL sur l'ajout d'entité ou de relation posepotentiellement un problème pour l'interface utilisateur car si l'expressionutilise l'entité ou la relation à créer, on est pas capable de vérifier lesdroits avant d'avoir effectué l'ajout (noter que cela n'est pas un problème cotéserveur rql car la vérification des droits est effectuée après l'ajouteffectif). Dans ce cas les méthodes de vérification des droits (check_perm,has_perm) peuvent inidquer qu'un utilisateur n'a pas le droit d'ajout alorsqu'il pourrait effectivement l'obtenir. Pour palier à ce soucis il est en généralnécessaire dans tel cas d'utiliser une action reflétant les droits du schémamais permettant de faire la vérification correctement afin qu'elle apparaissebien le cas échéant.Mise à jour du schema`````````````````````Il faut ensuite lancer son cubicweb en mode shell :: cubicweb-ctl shell moninstanceEt taper :: add_entity_type('Personne')Et on relance l'application!