Wiki source code of Live Security

Last modified by jhurst on 2021/04/21 10:01

Show last authors
1
2
3 {{ddtoc/}}
4
5 ----
6
7 = Préambule =
8
9 La fonctionnalité « Live Security » est un moyen supplémentaire pour sécuriser les informations contenues dans les cubes de données. Ceci est généralement appelé Sécurité au niveau ligne (« Row Level Security »). Nous appelons aussi cela la « personnalisation » des cubes et des flux.
10
11 Ce document résume les techniques traditionnelles de personnalisation de données et présente une nouvelle technique de personnalisation avancée appelée « Live Security ».
12
13 (% class="box infomessage" %)
14 (((
15 **NB :** Fonctionnalité introduite dans DigDash Enterprise 2017R1
16 )))
17
18 = Mécanismes traditionnels de personnalisation de DigDash Enteprise =
19
20 Dans les versions précédentes la sécurité au niveau ligne est résolue soit par la personnalisation des cubes, soit par la personnalisation des flux via l’utilisation de variables utilisateur (**${user.<nomvariable>}**). Voici un rappel de ces méthodes traditionnelles de personnalisation.
21
22 == Personnalisation au niveau du modèle de données (cube) ==
23
24 La sécurité au niveau ligne est résolue à la génération du cube de données via l’utilisation de variables utilisateur (**${user.<nomvariable>}**) dans la définition de la source de données et du modèle de données. Par exemple en SQL : SELECT * FROM UneTable WHERE pays='${user.pays}' AND service in '${user.services}'.
25
26 Dans cet exemple, chaque utilisateur a une variable //pays// égale à son pays, et une variable //services// qui est une liste de noms de services séparés par des virgules (Marketing,Ventes,Logistique) auquel l’utilisateur à le droit d’accès.
27
28 Ainsi pour différents utilisateurs on génère un cube différent (un par valeur des variables) ne contenant que les données correspondant au « profil de personnalisation » de chaque utilisateur. Si plusieurs utilisateurs ont les mêmes valeurs pour ces variables, on dit qu’ils ont le même profil, alors ils partagent le même cube.
29
30 Ce mécanisme est très simple à mettre en place, et permet en général de segmenter la volumétrie des données, et donc la charge mémoire du serveur lors de la consultation. En effet les utilisateurs se connectent rarement tous en même temps sur le serveur, celui-ci n’aura pas besoin de stocker tous les cubes en mémoire à chaque instant.
31
32 Un autre avantage est que selon le type de la source de données la règle de sécurisation peut être très avancée. Notre exemple simple de filtrage sur deux colonnes //pays// et //services// peut prendre en compte d’autres critères, provenir d’autres tables (qu’on ne souhaite pas intégrer au cube), etc.
33
34 Par contre ce mécanisme multiplie le nombre de cubes à générer et donc le nombre de requêtes vers la source de données.
35
36 Enfin la règle de sécurisation peut engendrer une redondance de données entre différents cubes, représentant la partie commune des données que plusieurs utilisateurs peuvent voir. Cette partie commune est dupliquée dans plusieurs cubes.
37
38 * **Avantages **: Sécurisation forte, simplicité de mise en œuvre, grande flexibilité (la complexité de sécurisation est dépendante des capacités de la source de données).
39 * **Inconvénients **: Plus de requêtes pour la génération des cubes, risque de redondance de données dans plusieurs cubes.
40
41 [[image:live_security_fr_html_6eaa6052687470b4.gif||height="395" width="391"]]
42 __//Schéma de personnalisation au niveau du cube//__
43
44 == Personnalisation au niveau des flux (graphique/tableau) ==
45
46 Une autre approche plus dynamique est d’utiliser ces variables utilisateur dans la valeur de filtres sur chaque graphique exploitant le cube. Pour reprendre l’exemple précédent, le cube prend ses données d’une source SQL : SELECT * FROM UneTable. Il est unique (non personnalisé) pour tous les utilisateurs, donc volumineux. Dans tous les flux utilisant ce cube on peut ajouter un filtre sur la dimension Pays (règle //« égal à »// ${user.pays}) et sur la dimension Service (règle //« est contenu dans » //${user.services}) . A chaque affichage d’un flux le filtre sera appliqué sur le cube de données pour ne conserver que les lignes auxquelles l’utilisateur a accès. Bien sûr la navigation sur ces dimensions doit être interdite pour l’utilisateur.
47
48 L’avantage est qu’on a qu’un seul cube à générer, donc une seule requête vers la source de données.
49
50 Par contre le filtrage étant commandé par les flux eux-mêmes lors de la consultation des tableaux de bord il y a un risque de permettre à des utilisateurs d’accéder à des données qui ne les concernent pas. Soit à cause d’un défaut de conception des pages du tableaux de bord, exemple : l’interdiction de la navigation sur une dimension a été omise, soit par manipulation (par un utilisateur expert) des requêtes d’affichage envoyées au serveur.
51
52 Enfin un autre inconvénient est une plus faible flexibilité de la règle de sécurisation. Dans cette approche elle dépend directement de ce qui a été stocké dans le cube et ne s’appuie que sur du filtrage de dimension.
53
54 * **Avantages **: Une seule requête pour la génération d’un seul cube
55 * **Inconvénients **: Fastidieux à mettre en œuvre (pré-configurer un filtrage sur tous les flux), sécurisation faible, règle de sécurisation limitée.
56
57 [[image:live_security_fr_html_db1cee0c8234453e.gif||height="426" width="429"]]
58 __//Schéma de personnalisation au niveau du flux//__
59
60 Voici un tableau comparatif de ces deux approches de personnalisation :
61
62 | |**Personnalisation des cubes**|**Personnalisation des flux**
63 |**Sécurisation**|Forte|Faible
64 |**Génération des cubes**|(((
65 X cubes générés (1 par « profil de personnalisation »)
66
67 ~=> X requêtes
68 )))|(((
69 1 cube généré
70
71 ~=> 1 seule requête
72 )))
73 |**Mise en place**|Simple|Fastidieuse
74 |**Flexibilité**|En fonction de la source de données (ex : SQL)|Filtrage sur les données du cube uniquement
75
76 = Nouvelle Approche « Live Security » =
77
78 == Concept ==
79
80 Cette nouvelle approche, introduite dans la version 2017R1 vise à ne garder que les avantages des deux mécanismes de personnalisation précédents. A savoir une sécurisation forte, un nombre de requêtes nécessaires minimum pour la génération du cube, une mise en place aussi simple que possible tout en conservant un niveau de flexibilité suffisamment élevé.
81
82 Elle permet d’ajouter des règles de sécurisation au niveau du modèle de données lui-même, sans démultiplier le nombre de cubes générés et sans obliger à gérer la sécurité au niveau ligne dans les flux.
83
84 La règle de sécurisation est exprimée grâce à un script Javascript qui sera exécuté à chaque interrogation du cube sur la sélection entrante. C’est véritablement une transformation de la sélection qui peut être faite. Aussi bien du filtrage simple analogue à l’approche « personnalisation de flux » que de la transformation plus complexe en fonction du profil de l’utilisateur. Par exemple, changer un niveau d’exploration, supprimer un axe, une ou plusieurs mesure, etc.
85
86 Coté sécurisation, cette transformation est appliquée avant le traitement de la sélection coté serveur et ne dépend que du profil de l’utilisateur. Il n’y a aucun moyen pour un utilisateur, même expert, d’altérer ce processus comme pour la personnalisation des flux.
87
88 * **Avantages **: Une seule requête pour la génération d’un seul cube, sécurisation forte, grande flexibilité (transformation potentiellement totale de la sélection du flux)
89 * **Inconvénients **: Mise en place complexe (Javascript)
90
91 [[image:live_security_fr_html_b6bea0d8a0772539.gif||height="382" width="554"]]{{comment}}Capture en basse résolution{{/comment}}
92 __//Schéma de personnalisation « Live Security »//__
93
94 Voici un tableau comparatif reprenant cette nouvelle approche avec les deux précédentes approches de personnalisation :
95
96 | |**Personnalisation des cubes**|**Personnalisation des flux**|**Personnalisation « Live Security »**
97 |**Sécurisation**|Forte|Faible|Forte
98 |**Génération des cubes**|(((
99 X cubes générés (1 par « profil de personnalisation »)
100
101 ~=> X requêtes
102 )))|(((
103 1 cube généré
104
105 ~=> 1 seule requête
106 )))|(((
107 1 cube généré
108
109 ~=> 1 seule requête
110 )))
111 |**Mise en place**|Simple|Fastidieuse (par flux)|Avancée
112 |**Flexibilité**|En fonction de la source de données (ex : SQL)|Filtrage sur les données du cube uniquement|Transformation de la sélection
113
114 == Mise en place ==
115
116 Ce chapitre décrit comment mettre en place le mécanisme Live Security au travers d’un exemple standard de filtrage de dimension.
117
118 L’exemple se base sur une source de données qui contient au moins deux colonnes //pays// et //service// et des colonnes de valeurs (mesures). Chaque utilisateur est assigné à un pays et à un ou plusieurs services et ne verra donc que les chiffres qui s’y rapportent. Certains utilisateur peuvent avoir le droit de voir tous les pays et/ou tous les services.
119
120 __Prérequis__
121
122 Chaque utilisateur a deux variables dans le LDAP : //pays// et //services// qui définissent son périmètre de sécurité. Chacune des deux variables peut être vide, cela signifie que l’utilisateur peut voir tous les pays et/ou services. La variable services peut être une liste de noms de services séparés par des virgules (pas d’espace après les virgules).
123
124 __Exemple :__
125
126 Utilisateur U1 :
127
128 pays = '//fr'//
129
130 services = 'Marketing,Ventes'
131
132 Utilisateur U2 :
133
134 pays = 'de'
135
136 services = (chaîne vide)
137
138 __Modèle de données__
139
140 La source de données est une source SQL sur laquelle on exécute la requête suivante :
141
142 SELECT pays, service, val1, val2 FROM UneTable
143
144 On peut noter qu’il n’y a pas de clause WHERE dans cette requête car le but est de générer un cube unique contenant toutes les données pour tous les utilisateurs.
145
146 Le modèle de données contient donc deux dimensions et deux mesures.
147
148 (% class="box warningmessage" %)
149 (((
150 //Important : les variables utilisateur ${user.pays}, ${user.service} ne doivent pas être utilisées dans ce contexte, ni dans la source de données (clause WHERE), ni dans une formule de mesure dérivée ou n’importe où dans le modèle de données. Sinon cela impose au système d’utiliser la personnalisation par cubes, ce que l’on ne veut pas dans cette approche.//
151 )))
152
153 L’activation du Live Security se fait dans l’écran de configuration du modèle de données, onglet **Avancé :**
154
155 1. Choisissez le **Mode de traitement du cube** : **Toujours sur le serveur**(((
156 (% class="box warningmessage" %)
157 (((
158 //Important : Le mode de personnalisation « Live Security » intervenant sur le serveur, il est nécessaire de s’assurer que le cube sera aussi traité coté serveur. Il faut donc choisir ce mode de traitement.//
159 )))
160 )))
161 1. Cliquez sur le bouton **Editer…** au niveau de la boite de sélection de la **Fonction de transformation de sélection**.
162 1. Choisissez **Script** dans le **Type** de fonction.
163
164 Vous pouvez maintenant saisir un script de transformation des sélections faites sur le cube correspondant à ce modèle de données.
165
166 (% style="text-align:center" %)
167 [[image:1599127564174-106.png||height="361" width="640"]]
168
169 === Script ===
170
171 Le script à écrire est le corps d’une fonction Javascript qui prend en paramètre un objet « selection ». Cet objet représente la description du résultat que l’on souhaite obtenir du cube (opération « d’aplatissement »). Cet objet complexe définit les axes souhaités, les dimensions, les filtres, les mesures et d’autres paramètres propres à chaque type de flux (graphique, tableaux…). Ce qui nous intéresse dans cet exemple ce sont les paramètres de filtrage de dimension. Le but est de transformer cette sélection afin d’y inclure deux filtres « forcés », un sur le pays de l’utilisateur et un sur ses services, si l’utilisateur a des variables définies (non vides) pour le pays et/ou les services.
172
173 Commençons par récupérer la valeur de la variable de l’utilisateur //pays// en utilisant la fonction **getUserAttribute(‘<nomvariable>’)** :
174
175 {{code language="JAVASCRIPT"}}
176 var pays = getUserAttribute('pays');
177 {{/code}}
178
179 Ensuite, si la variable pays est définie et non vide, nous appliquons un filtre sur l’objet selection :
180
181 {{code language="JAVASCRIPT"}}
182 ...
183
184 if (pays != null && pays != '')
185
186 {
187
188 var tabPays = [pays]; //création d'un tableau contenant le pays
189
190 var dim = selection.dm.getDimensionById('Pays');
191
192 var filt = new FilterSelection(dim, -1, -1, [], tabPays);
193
194 selection.setFilter(filt);
195
196 }
197 {{/code}}
198
199 Explication : La construction du filtre se fait avec l’aide de l’instruction **new FilterSelection(Dimension, nomHierarchie, nomNiveau, [ ], tableauMembres)** :
200
201 * **Dimension** : L’objet dimension peut-être récupéré directement dans le modèle de données accessible via **selection.dm**, grâce à la fonction **getDimensionById(dimId)**.
202 * **nomHierarchie** et **nomNiveau** : Ensuite, dans cet exemple, nous filtrons directement les membres racines de la dimension, il n’y a pas de hiérarchie ni de niveau à préciser (...-1, -1...).
203 * **[ ] **(tableau vide) : Utilisé de manière interne, ne pas modifier.
204 * **TableauMembres** : Enfin, un filtre sur une dimension peut avoir plusieurs membres sélectionnés, il faut donc lui passer un tableau de membres (ici **tabPays**). Pour le pays, comme l’utilisateur n’en a qu’un, le tableau ne contient qu’un élément.
205
206 Puis nous appliquons ce filtre à la sélection par **selection.setFilter(…**).
207
208 Enfin, on fait la même chose pour la variable //services// __qui représente une liste de services__, qu’on traite spécifiquement grâce à la fonction Javascript **split**(). Elle découpe une chaîne de caractères en un tableau de chaînes de caractères selon un caractère séparateur (la virgule dans notre cas) :
209
210 {{code language="JAVASCRIPT"}}
211 ...
212
213 var services = getUserAttribute('services');
214
215 if (services != null && services != '')
216
217 {
218
219 var tabServices = services.split(',');
220
221 var dim = selection.dm.getDimensionById('Service');
222
223 var filt = new FilterSelection(dim, -1, -1, [], tabServices);
224
225 selection.setFilter(filt);
226
227 }
228 {{/code}}
229
230 === Utilisation ===
231
232 Il ne reste plus qu’à créer un flux sur ce modèle de données et à le placer dans une page de tableau de bord.
233
234 Si on ne cache pas les dimensions pays et service dans le tableau de bord (ou dans le flux) il sera toujours possible de filtrer sur une de ces dimensions. Cependant le filtre sera écrasé par le script de Live Security si l’utilisateur a une valeur non vide pour sa variable //pays// ou //services//. Au final le filtrage sur ces dimensions dans le tableau de bord n’est utile que pour les utilisateurs ayant le droit de tout voir c’est à dire ceux qui ont une valeur vide dans leur variable //pays// et/ou //services//.
235
236 == Référence API ==
237
238 Cette fonctionnalité est très récente, son API est susceptible d’évoluer dans de futures versions. Pour l’instant il n’y a que quelques méthodes recommandées pour manipuler une sélection. Ces méthodes sont décrites dans ce paragraphe.
239
240 __Récupération d’informations utilisateurs__
241
242 {{code language="JAVASCRIPT"}}
243 (Chaîne) getUserAttribute (attr)
244 {{/code}}
245
246 Description : Retourne l'attribut LDAP **attr** de l'utilisateur. Aussi appelé « variable d’utilisateur ».
247
248 Exemple :
249
250 {{code language="JAVASCRIPT"}}
251 var userName = getUserAttribute('displayName');
252
253 (Chaîne) getSessionAttribute (attr)
254 {{/code}}
255
256 Description : Retourne l'attribut Session **attr** de l'utilisateur pour la session courante.
257
258 L’utilisation de variable de session est couverte dans le document **Tutoriel variables de session**.
259
260 Exemple :
261
262 {{code language="JAVASCRIPT"}}
263 var scenario = getUserAttribute('Scenario');
264 {{/code}}
265
266 __Récupération d’informations de la sélection ou du modèle de données courant__
267
268 {{code language="JAVASCRIPT"}}
269 (Nombre) getDDVar(variable)
270 {{/code}}
271
272 Description : Retourne la valeur de la **variable du modèle** (DDVar) correspondante. Ces variables sont définies dans le modèle de données et correspondent à un contrôle dans la page de tableau de bord.
273
274 (% class="box warningmessage" %)
275 (((
276 //Attention : ne pas confondre la DDVar avec la variable d’utilisateur (attribut LDAP).//
277 )))
278
279 Exemple :
280
281 {{code language="JAVASCRIPT"}}
282 var tauxEuroDollar = getDDVar('txEuroDollar');
283
284 (Dimension) selection.dm.getDimensionById(dimId)
285 {{/code}}
286
287 Description : Retourne un objet Javascript correspondant à la dimension d’identifiant **dimId**. Retourne **null** si cette dimension n’est pas dans modèle courant. Cet objet est nécessaire à d’autres fonctions, par exemple pour la construction d’un filtre.
288
289 Exemple :
290
291 {{code language="JAVASCRIPT"}}
292 var dimPays = selection.dm.getDimensionById('Pays');
293 {{/code}}
294
295 __Transformation de la sélection__
296
297 {{code language="JAVASCRIPT"}}
298 (Filtre) new FilterSelection(Dimension, nomHierarchie, nomNiveau, [ ], tableauMembres)
299 {{/code}}
300
301 Description : Retourne un objet Javascript correspondant au filtre souhaité sur la dimension en paramètre, dans une hiérarchie et un niveau donné (**-1** si pas de hiérarchie/niveau) et avec les membres spécifiés. Cet objet est nécessaire à d’autres fonctions, par exemple pour l’application du filtre sur la sélection.
302
303 (% class="box warningmessage" %)
304 (((
305 //Attention : le 4ème paramètre ([ ]) est un tableau vide utilisé de manière interne. Il ne faut pas modifier ce paramètre.//
306 )))
307
308 Exemple :
309
310 {{code language="JAVASCRIPT"}}
311 var filter = new FilterSelection(dim, -1, -1, [], new Array('fr','it','de'));
312
313 void selection.setFilter(filtre)
314 {{/code}}
315
316 Description : Applique un filtre sur la sélection courante. Écrase le filtre existant sur cette dimension s’il y en avait un.
317
318 Exemple :
319
320 {{code language="JAVASCRIPT"}}
321 selection.setFilter(filter);
322
323 void setDDVar(variable, valeur)
324 {{/code}}
325
326 Description : Modifie la valeur d’une variable de modèle (DDVar) pour la sélection courante. La valeur de la variable n’est pas modifiée de manière persistante, seulement pour cette sélection.
327
328 (% class="box warningmessage" %)
329 (((
330 //Attention : ne pas confondre la DDVar avec la variable d’utilisateur (attribut LDAP).//
331 )))
332
333 Exemple :
334
335 {{code language="JAVASCRIPT"}}
336 setDDVar('txEuroDollar', 1.06);
337 {{/code}}