|
1 |
|
2 Définition d'un type d'entité |
|
3 ----------------------------- |
|
4 |
|
5 Un type d'entité est définit par une classe python héritant de `EntityType`. Le |
|
6 nom de la classe correspond au nom du type. Ensuite le corps de la classe |
|
7 contient la description des attributs et des relations pour ce type d'entité, |
|
8 par exemple :: |
|
9 |
|
10 class Personne(EntityType): |
|
11 """une personne avec les propriétés et relations nécessaires à mon |
|
12 application""" |
|
13 |
|
14 nom = String(required=True, fulltextindexed=True) |
|
15 prenom = String(required=True, fulltextindexed=True) |
|
16 civilite = String(vocabulary=('M', 'Mme', 'Mlle')) |
|
17 date_naiss = Date() |
|
18 travaille_pour = SubjectRelation('Company', cardinality='?*') |
|
19 |
|
20 * le nom de l'attribut python correspond au nom de l'attribut ou de la relation |
|
21 dans cubicweb. |
|
22 |
|
23 * tout les types de bases sont disponibles nativement : `String`, `Int`, `Float`, |
|
24 `Boolean`, `Date`, `Datetime`, `Time`, `Byte`. |
|
25 |
|
26 * Chaque type d'entité a au moins les méta-relations suivantes : |
|
27 - `eid` (`Int`) |
|
28 - `creation_date` (`Datetime`) |
|
29 - `modification_date` (`Datetime`) |
|
30 - `owned_by` (`EUser`) |
|
31 - `is` (`EEType`) |
|
32 |
|
33 * il est également possible de définir des relations dont le type d'entité est |
|
34 l'objet en utilisant `ObjectRelation` plutôt que `SubjectRelation` |
|
35 |
|
36 * le premier argument de `SubjectRelation` et `ObjectRelation` donne |
|
37 respectivement le type d'entité objet /sujet de la relation. Cela |
|
38 peut être : |
|
39 |
|
40 * une chaine de caractères correspondant à un type d'entité |
|
41 |
|
42 * un tuple de chaines de caractères correspondant à plusieurs types d'entité |
|
43 |
|
44 * les chaînes de caractères spéciales suivantes : |
|
45 |
|
46 - "**" : tout les types d'entité |
|
47 - "*" : tout les types d'entité non méta |
|
48 - "@" : tout les types d'entité méta mais non "système" (i.e. servant à la |
|
49 description du schema en base) |
|
50 |
|
51 * il est possible d'utiliser l'attribut possible `meta` pour marquer un type |
|
52 d'entité comme étant "méta" (i.e. servant à décrire / classifier d'autre |
|
53 entités) |
|
54 |
|
55 * propriétés optionnelles des attributs et relations : |
|
56 |
|
57 - `description` : chaine de caractères décrivant un attribut ou une |
|
58 relation. Par défaut cette chaine sera utilisée dans le formulaire de saisie |
|
59 de l'entité, elle est donc destinée à aider l'utilisateur final et doit être |
|
60 marquée par la fonction `_` pour être correctement internationalisée. |
|
61 |
|
62 - `constraints` : liste de contraintes devant être respecté par la relation |
|
63 (c.f. `Contraintes`_) |
|
64 |
|
65 - `cardinality` : chaine de 2 caractères spécifiant la cardinalité de la |
|
66 relation. Le premier caractère donne la cardinalité de la relation sur le |
|
67 sujet, le 2eme sur l'objet. Quand une relation possède plusieurs sujets ou |
|
68 objets possibles, la cardinalité s'applique sur l'ensemble et non un à un (et |
|
69 doit donc à priori être cohérente...). Les valeurs possibles sont inspirées |
|
70 des expressions régulières : |
|
71 |
|
72 * `1`: 1..1 |
|
73 * `?`: 0..1 |
|
74 * `+`: 1..n |
|
75 * `*`: 0..n |
|
76 |
|
77 - `meta` : booléen indiquant que la relation est une méta relation (faux par |
|
78 défaut) |
|
79 |
|
80 * propriétés optionnelles des attributs : |
|
81 |
|
82 - `required` : booléen indiquant si l'attribut est obligatoire (faux par |
|
83 défaut) |
|
84 |
|
85 - `unique` : booléen indiquant si la valeur de l'attribut doit être unique |
|
86 parmi toutes les entités de ce type (faux par défaut) |
|
87 |
|
88 - `indexed` : booléen indiquant si un index doit être créé dans la base de |
|
89 données sur cette attribut (faux par défaut). C'est utile uniquement si vous |
|
90 savez que vous allez faire de nombreuses recherche sur la valeur de cet |
|
91 attribut. |
|
92 |
|
93 - `default` : valeur par défaut de l'attribut. A noter que dans le cas des |
|
94 types date, les chaines de caractères correspondant aux mots-clés RQL |
|
95 `TODAY` et `NOW` sont utilisables. |
|
96 |
|
97 - `vocabulary` : spécifie statiquement les valeurs possibles d'un attribut |
|
98 |
|
99 * propriétés optionnelles des attributs de type `String` : |
|
100 |
|
101 - `fulltextindexed` : booléen indiquant si l'attribut participe à l'index plein |
|
102 texte (faux par défaut) (*valable également sur le type `Byte`*) |
|
103 |
|
104 - `internationalizable` : booléen indiquant si la valeur de cet attribut est |
|
105 internationalisable (faux par défaut) |
|
106 |
|
107 - `maxsize` : entier donnant la taille maximum de la chaine (pas de limite par |
|
108 défaut) |
|
109 |
|
110 * propriétés optionnelles des relations : |
|
111 |
|
112 - `composite` : chaîne indiquant que le sujet (composite == 'subject') est |
|
113 composé de ou des objets de la relation. Pour le cas opposé (l'objet est |
|
114 composé de ou des sujets de la relation, il suffit de mettre 'object' comme |
|
115 valeur. La composition implique que quand la relation est supprimé (et donc |
|
116 aussi quand le composite est supprimé), le ou les composés le sont |
|
117 également. |
|
118 |
|
119 |
|
120 Contraintes |
|
121 ``````````` |
|
122 Par défaut les types de contraintes suivant sont disponibles : |
|
123 |
|
124 * `SizeConstraint` : permet de spécifier une taille minimale et/ou maximale sur |
|
125 les chaines de caractères (cas générique de `maxsize`) |
|
126 |
|
127 * `BoundConstraint` : permet de spécifier une valeur minimale et/ou maximale sur |
|
128 les types numériques |
|
129 |
|
130 * `UniqueConstraint` : identique à "unique=True" |
|
131 |
|
132 * `StaticVocabularyConstraint` : identique à "vocabulary=(...)" |
|
133 |
|
134 * `RQLConstraint` : permet de spécifier une requête RQL devant être satisfaite |
|
135 par le sujet et/ou l'objet de la relation. Dans cette requête les variables `S` |
|
136 et `O` sont préféfinies respectivement comme l'entité sujet et objet de la |
|
137 relation |
|
138 |
|
139 * `RQLVocabularyConstraint` : similaire à la précédente, mais exprimant une |
|
140 contrainte "faible", i.e. servant uniquement à limiter les valeurs apparaissant |
|
141 dans la liste déroulantes du formulaire d'édition, mais n'empêchant pas une |
|
142 autre entité d'être séléctionnée |
|
143 |
|
144 |
|
145 Définition d'un type de relation |
|
146 -------------------------------- |
|
147 |
|
148 Un type de relation est définit par une classe python héritant de `RelationType`. Le |
|
149 nom de la classe correspond au nom du type. Ensuite le corps de la classe |
|
150 contient la description des propriétés de ce type de relation, ainsi |
|
151 qu'éventuellement une chaine pour le sujet et une autre pour l'objet permettant |
|
152 de créer des définitions de relations associées (auquel cas il est possibles de |
|
153 donner sur la classe les propriétés de définition de relation explicitées |
|
154 ci-dessus), par exemple :: |
|
155 |
|
156 class verrouille_par(RelationType): |
|
157 """relation sur toutes les entités applicatives indiquant que celles-ci sont vérouillées |
|
158 inlined = True |
|
159 cardinality = '?*' |
|
160 subject = '*' |
|
161 object = 'EUser' |
|
162 |
|
163 En plus des permissions, les propriétés propres aux types de relation (et donc |
|
164 partagés par toutes les définitions de relation de ce type) sont : |
|
165 |
|
166 * `inlined` : booléen contrôlant l'optimisation physique consistant à stocker la |
|
167 relation dans la table de l'entité sujet au lieu de créer une table spécifique |
|
168 à la relation. Cela se limite donc aux relations dont la cardinalité |
|
169 sujet->relation->objet vaut 0..1 ('?') ou 1..1 ('1') |
|
170 |
|
171 * `symetric` : booléen indiquant que la relation est symétrique, i.e. "X relation |
|
172 Y" implique "Y relation X" |
|
173 |
|
174 Dans le cas de définitions de relations simultanée, `sujet` et `object` peuvent |
|
175 tout deux valoir la même chose que décrite pour le 1er argument de |
|
176 `SubjectRelation` et `ObjectRelation`. |
|
177 |
|
178 A partir du moment où une relation n'est ni mise en ligne, ni symétrique, et |
|
179 ne nécessite pas de permissions particulières, sa définition (en utilisant |
|
180 `SubjectRelation` ou `ObjectRelation`) est suffisante. |
|
181 |
|
182 |
|
183 Définition des permissions |
|
184 -------------------------- |
|
185 |
|
186 La définition des permissions se fait à l'aide de l'attribut `permissions` des |
|
187 types d'entité ou de relation. Celui-ci est un dictionnaire dont les clés sont |
|
188 les types d'accès (action), et les valeurs les groupes ou expressions autorisées. |
|
189 |
|
190 Pour un type d'entité, les actions possibles sont `read`, `add`, `update` et |
|
191 `delete`. |
|
192 |
|
193 Pour un type de relation, les actions possibles sont `read`, `add`, et `delete`. |
|
194 |
|
195 Pour chaque type d'accès, un tuple indique le nom des groupes autorisés et/ou |
|
196 une ou plusieurs expressions RQL devant être vérifiées pour obtenir |
|
197 l'accès. L'accès est donné à partir du moment où l'utilisateur fait parti d'un |
|
198 des groupes requis ou dès qu'une expression RQL est vérifiée. |
|
199 |
|
200 Les groupes standards sont : |
|
201 |
|
202 * `guests` |
|
203 |
|
204 * `users` |
|
205 |
|
206 * `managers` |
|
207 |
|
208 * `owners` : groupe virtuel correspondant au propriétaire d'une entité. Celui-ci |
|
209 ne peut être utilisé que pour les actions `update` et `delete` d'un type |
|
210 d'entité. |
|
211 |
|
212 Il est également possible d'utiliser des groupes spécifiques devant être pour |
|
213 cela créés dans le precreate de l'application (`migration/precreate.py`). |
|
214 |
|
215 Utilisation d'expression RQL sur les droits en écriture |
|
216 ``````````````````````````````````````````````````````` |
|
217 Il est possible de définir des expressions RQL donnant des droits de |
|
218 modification (`add`, `delete`, `update`) sur les types d'entité et de relation. |
|
219 |
|
220 Expression RQL pour les permissions sur un type d'entité : |
|
221 |
|
222 * il faut utiliser la classe `ERQLExpression` |
|
223 |
|
224 * l'expression utilisée correspond à la clause WHERE d'une requête RQL |
|
225 |
|
226 * dans cette expression, les variables X et U sont des références prédéfinies |
|
227 respectivement sur l'entité courante (sur laquelle l'action est vérifiée) et |
|
228 sur l'utilisateur ayant effectué la requête |
|
229 |
|
230 * il est possible d'utiliser dans cette expression les relations spéciales |
|
231 "has_<ACTION>_permission" dont le sujet est l'utilisateur et l'objet une |
|
232 variable quelquonque, signifiant ainsi que l'utilisateur doit avoir la |
|
233 permission d'effectuer l'action <ACTION> sur la ou les entités liées cette |
|
234 variable |
|
235 |
|
236 Pour les expressions RQL sur un type de relation, les principes sont les mêmes |
|
237 avec les différences suivantes : |
|
238 |
|
239 * il faut utiliser la classe `RRQLExpression` dans le cas d'une relation non |
|
240 finale |
|
241 |
|
242 * dans cette expression, les variables S, O et U sont des références |
|
243 prédéfinies respectivement sur le sujet et l'objet de la relation |
|
244 courante (sur laquelle l'action est vérifiée) et sur l'utilisateur |
|
245 ayant effectué la requête |
|
246 |
|
247 * On peut aussi définir des droits sur les attributs d'une entité (relation non |
|
248 finale), sachant les points suivants : |
|
249 |
|
250 - pour définir des expressions rql, il faut utiliser la classe `ERQLExpression` |
|
251 dans laquelle X représentera l'entité auquel appartient l'attribut |
|
252 |
|
253 - les permissions 'add' et 'delete' sont équivalentes. En pratique seul |
|
254 'add'/'read' son pris en considération |
|
255 |
|
256 |
|
257 En plus de cela, le type d'entité `EPermission` de la librairie standard permet |
|
258 de construire des modèles de sécurités très complexes et dynamiques. Le schéma |
|
259 de ce type d'entité est le suivant : :: |
|
260 |
|
261 |
|
262 class EPermission(MetaEntityType): |
|
263 """entity type that may be used to construct some advanced security configuration |
|
264 """ |
|
265 name = String(required=True, indexed=True, internationalizable=True, maxsize=100) |
|
266 require_group = SubjectRelation('EGroup', cardinality='+*', |
|
267 description=_('groups to which the permission is granted')) |
|
268 require_state = SubjectRelation('State', |
|
269 description=_("entity'state in which the permission is applyable")) |
|
270 # can be used on any entity |
|
271 require_permission = ObjectRelation('**', cardinality='*1', composite='subject', |
|
272 description=_("link a permission to the entity. This " |
|
273 "permission should be used in the security " |
|
274 "definition of the entity's type to be useful.")) |
|
275 |
|
276 |
|
277 Exemple de configuration extrait de *jpl* :: |
|
278 |
|
279 ... |
|
280 |
|
281 class Version(EntityType): |
|
282 """a version is defining the content of a particular project's release""" |
|
283 |
|
284 permissions = {'read': ('managers', 'users', 'guests',), |
|
285 'update': ('managers', 'logilab', 'owners',), |
|
286 'delete': ('managers', ), |
|
287 'add': ('managers', 'logilab', |
|
288 ERQLExpression('X version_of PROJ, U in_group G,' |
|
289 'PROJ require_permission P, P name "add_version",' |
|
290 'P require_group G'),)} |
|
291 |
|
292 ... |
|
293 |
|
294 class version_of(RelationType): |
|
295 """link a version to its project. A version is necessarily linked to one and only one project. |
|
296 """ |
|
297 permissions = {'read': ('managers', 'users', 'guests',), |
|
298 'delete': ('managers', ), |
|
299 'add': ('managers', 'logilab', |
|
300 RRQLExpression('O require_permission P, P name "add_version",' |
|
301 'U in_group G, P require_group G'),) |
|
302 } |
|
303 inlined = True |
|
304 |
|
305 Cette configuration suppose indique qu'une entité `EPermission` de nom |
|
306 "add_version" peut-être associée à un projet et donner le droit de créer des |
|
307 versions sur ce projet à des groupes spécifiques. Il est important de noter les |
|
308 points suivants : |
|
309 |
|
310 * dans ce cas il faut protéger à la fois le type d'entité "Version" et la |
|
311 relation liant une version à un projet ("version_of") |
|
312 |
|
313 * du fait de la généricité du type d'entité `EPermission`, il faut effectuer |
|
314 l'unification avec les groupes et / ou les états le cas échéant dans |
|
315 l'expression ("U in_group G, P require_group G" dans l'exemple ci-dessus) |
|
316 |
|
317 |
|
318 Utilisation d'expression RQL sur les droits en lecture |
|
319 `````````````````````````````````````````````````````` |
|
320 Les principes sont les mêmes mais avec les restrictions suivantes : |
|
321 |
|
322 * on ne peut de `RRQLExpression` sur les types de relation en lecture |
|
323 |
|
324 * les relations spéciales "has_<ACTION>_permission" ne sont pas utilisables |
|
325 |
|
326 |
|
327 Note sur l'utilisation d'expression RQL sur la permission 'add' |
|
328 ``````````````````````````````````````````````````````````````` |
|
329 L'utilisation d'expression RQL sur l'ajout d'entité ou de relation pose |
|
330 potentiellement un problème pour l'interface utilisateur car si l'expression |
|
331 utilise l'entité ou la relation à créer, on est pas capable de vérifier les |
|
332 droits avant d'avoir effectué l'ajout (noter que cela n'est pas un problème coté |
|
333 serveur rql car la vérification des droits est effectuée après l'ajout |
|
334 effectif). Dans ce cas les méthodes de vérification des droits (check_perm, |
|
335 has_perm) peuvent inidquer qu'un utilisateur n'a pas le droit d'ajout alors |
|
336 qu'il pourrait effectivement l'obtenir. Pour palier à ce soucis il est en général |
|
337 nécessaire dans tel cas d'utiliser une action reflétant les droits du schéma |
|
338 mais permettant de faire la vérification correctement afin qu'elle apparaisse |
|
339 bien le cas échéant. |