Code source wiki de Live Security
Modifié par Aurelie Bertrand le 2023/12/12 17:31
Afficher les derniers auteurs
author | version | line-number | content |
---|---|---|---|
1 | {{toc/}} | ||
2 | |||
3 | ---- | ||
4 | |||
5 | = Définir la fonction Live Security = | ||
6 | |||
7 | La fonction de live security, aussi appelée fonction de transformation de la sélection peut être configurée durant l'édition d'un modèle de données, au niveau de l'étape de **Configuration avancée du modèle de données**, dans l'onglet **Avancé**. Vous pouvez choisir une fonction de transformation de sélection existante, ou en créer une nouvelle, manuellement, pour des fonctions utilisant du code javascript, ou via un assistant. | ||
8 | |||
9 | L'utilisation de l'assistant permet d'associer des dimensions à des paramètre de l'utilisateur qui fera en sorte que toute sélection sur un cube forcera les dimensions configurées à être filtrées en fonction des valeurs des paramètres utilisateurs. | ||
10 | |||
11 | L'approche manuelle, par utilisation d'un script javascript permet d'aller plus loin. L'exemple suivant est la création d'une fonction avancée de transformation de sélection qui peut être changer pour ajouter des dimensions sécurisées (modifier entre (% style="color:#e67e22" %)**/* START CONFIG */**(%%) et (% style="color:#e67e22" %)**/* END CONFIG */**(%%)) : | ||
12 | |||
13 | {{code language="js"}} | ||
14 | /* START CONFIG */ | ||
15 | //version: 2019R2 | ||
16 | var securedDims = {}; | ||
17 | securedDims["DIMENSION_NAME_1"] = getUserAttribute("USER_PARAM_1"); | ||
18 | |||
19 | /* Ajouter ici les autres dimensions sur lequel du live security doit s’appliquer */ | ||
20 | /* | ||
21 | securedDims["DIMENSION_NAME_2"] = getUserAttribute("USER_PARAM_2"); | ||
22 | */ | ||
23 | |||
24 | /* END CONFIG */ | ||
25 | |||
26 | /* MODIFY THE SCRIPT BELOW WITH CAUTION */ | ||
27 | var sLogPrefix = "[LIVE_SECURITY] [live-sec-thread-" + Math.floor(Math.random()*16777215).toString(16) + "]"; /* 16777215 is FFFFFF in decimal */ | ||
28 | |||
29 | for (var dimId in securedDims) | ||
30 | { | ||
31 | var dim = selection.dm.objName[dimId]; | ||
32 | var persoVal = securedDims[dimId]; | ||
33 | |||
34 | //Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + "Applying user filters on: " + dimId + ", persoVal:" + persoVal); | ||
35 | if (dim) | ||
36 | { | ||
37 | if (persoVal == null || persoVal.length == 0) | ||
38 | { | ||
39 | // user must no see any value | ||
40 | var persoValuesTab = ["-noval-"]; | ||
41 | var filt = new FilterSelection(dim, -1, -1, [], persoValuesTab); | ||
42 | selection.setFilter(filt); | ||
43 | Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Filters (members) on " + dimId + ": [" + persoValuesTab + "]"); | ||
44 | } | ||
45 | else if (persoVal && persoVal != ".*") | ||
46 | { | ||
47 | //user is limited to some value(s) | ||
48 | var persoValuesTab = persoVal.split("|"); | ||
49 | Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Applying user filters on " + dimId + ": [" + persoValuesTab + "]"); | ||
50 | var exFilter = selection.filterByDimName[dimId]; | ||
51 | if (!exFilter) | ||
52 | { | ||
53 | //there is no exisitng filter on that dimension => create a new one | ||
54 | var filt = new FilterSelection(dim, -1, -1, [], persoValuesTab); | ||
55 | selection.setFilter(filt); | ||
56 | Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Resolved filters (members) on " + dimId + ": [" + persoValuesTab + "]"); | ||
57 | } | ||
58 | else | ||
59 | { | ||
60 | //there is already a filter on that dimension => merge (intersect) into a new one | ||
61 | var filt = new FilterSelection(dim, -1, -1, [], persoValuesTab); | ||
62 | filt.recalcIds(); | ||
63 | exFilter.recalcIds(); | ||
64 | exFilter = mergeFilters(exFilter, filt); | ||
65 | origIdsTab = exFilter.origIds; | ||
66 | selection.setFilter(exFilter); | ||
67 | Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Resolved filters (origIds) on " + dimId + ": [" + exFilter.origIds + "]"); | ||
68 | } | ||
69 | } | ||
70 | else // perso value is .* | ||
71 | { | ||
72 | // do nothing, user can see everything | ||
73 | Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Resolved filters on " + dimId + ": All (.*)"); | ||
74 | } | ||
75 | } | ||
76 | else | ||
77 | { | ||
78 | Packages.com.digdash.utils.MessageStack.getInstance().addError(sLogPrefix + " Dimension " + dimId + "not found"); | ||
79 | } | ||
80 | } | ||
81 | {{/code}} | ||
82 | |||
83 | == Attribut Utilisateur == | ||
84 | |||
85 | Prendre le soin de créer le paramètre utilisateur **USER_PARAM_1** et de l’avoir renseigné pour les utilisateurs concernés (Saisie “.*” pour les autres). | ||
86 | Dans l’exemple ci-dessous, admin aura le droit de voir les lignes de la dimension **DIMENSION_NAME_1** ou les valeurs **A, B ou C** apparaissent. | ||
87 | |||
88 | |||
89 | [[image:https://lh6.googleusercontent.com/PFz-w1dqknwbuP_W-YeSH0EVWjnR4mbESAsEgTjyNSAKFAwChSJPSkLCVaZOricQLA4g_SGytqGFj0go_OFQUlDRbd8ZSsMC3PrfLq4GV3Kluv_SSy8iO0LEqzqbcdvyYPKHuVfA9fU||height="460" width="905"]] | ||
90 | |||
91 | = Live Security pour interdire l’affichage d’une mesure = | ||
92 | |||
93 | Une utilisation avancée du Live Security permet de cacher des mesures en fonction du profil de l'utilisateur. Dans l'exemple suivant, les utilisateurs ont un paramètre **show_measure** qui peut avoir une valeur **oui **ou **non** : | ||
94 | |||
95 | {{code language="js"}} | ||
96 | var show_measure = getUserAttribute('show_measure'); | ||
97 | var measureId = 'CA'; // id of the measure to hide | ||
98 | |||
99 | if (show_measure == 'non') | ||
100 | { | ||
101 | var measIndex = selection.indexOfMeasure(measureId); | ||
102 | if (measIndex > 0) | ||
103 | selection.removeMeasure(measIndex); | ||
104 | } | ||
105 | {{/code}} | ||
106 | |||
107 | = Live Security à appliquer à un graphique précis = | ||
108 | |||
109 | On peut aussi spécialiser le Live Security en fonction du graphique : | ||
110 | |||
111 | {{code language="js"}} | ||
112 | if (flowId != null && flowId.indexOf("mon_flow_id") > -1) | ||
113 | { | ||
114 | //Ajouter le code ici pour transformer la sélection de manière spécifique au flux | ||
115 | } | ||
116 | else | ||
117 | { | ||
118 | //Ajouter le code ici pour tous les autres flux | ||
119 | } | ||
120 | {{/code}} | ||
121 | |||
122 | = Live Security utilisant un autre cube = | ||
123 | |||
124 | L'exemple le plus avancé est d'aller chercher les valeurs de filtrage dans un autre cube plutôt que dans les paramètres utilisateurs stockés dans le LDAP. | ||
125 | |||
126 | == Explication du cas == | ||
127 | |||
128 | La données « de base » est une table de transactions de flux monétaire. | ||
129 | Il existe un champ « SECURITY_CODE » qui sert à savoir au final si un utilisateur connecté à le droit de voir la transaction ou pas. | ||
130 | Il existe une autre table dite de confidentialité où l’on trouve les utilisateurs LOGIN, appartenant à des groupes GROUP_ID (relation n-n). Et les « SECURITY_CODE » rattachés à plusieurs groupes. | ||
131 | On ne peut pas déclarer ces SECURITY_CODE dans une variable utilisateur du LDAP car il y beaucoup de valeurs possibles et cette information doit rester en base car très changeante. | ||
132 | |||
133 | == Solution Live Security == | ||
134 | |||
135 | La solution est donc de ne pas faire la jointure mais de simplement ajouter une fonction Live Security au cube transactions qui va en temps réel aller « chercher » les « SECURITY_CODE » par rapport à l’utilisateur connecté dans le cube confidentialité et les imposer comme filtre. | ||
136 | De cette manière, la volumétrie du cube final n’augmente pas, on est sur des aplatissements très rapide et on à besoin que de 8GB de RAM sur le serveur alors qu’il en fallait au moins 32GB à Tableau pour des chargements lents. | ||
137 | |||
138 | Voici le code du script Live Security pour cet exemple (à adapter selon votre cas, entre **/* START CONFIG */** et **/* END CONFIG */**) : | ||
139 | |||
140 | {{code cssClass="notranslate" language="javascript"}} | ||
141 | /* START CONFIG */ | ||
142 | var cubeId = "id_cube_authorisations"; | ||
143 | var userAttr = getUserAttribute("USER_LOGIN_SSO"); | ||
144 | var dimSecurityCode = "SECURITY_CODE"; | ||
145 | var dimUser = "LOGIN"; | ||
146 | /* END CONFIG */ | ||
147 | |||
148 | var sLogPrefix = "[SECURITY] [live-sec-thread-" + Math.floor(Math.random()*16777215).toString(16) + "]"; | ||
149 | Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " start..."); | ||
150 | DimBean = function(id) | ||
151 | { | ||
152 | this.id = id; | ||
153 | this.members = []; | ||
154 | } | ||
155 | |||
156 | DimBean.prototype.toJSON = function() | ||
157 | { | ||
158 | return this.id; | ||
159 | } | ||
160 | |||
161 | Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " user is " + userAttr); | ||
162 | var sel = new DataModelSelection(); | ||
163 | sel.dm = { "variables":{} }; | ||
164 | sel.pivot = 0; | ||
165 | sel.addBrowse(1, new DimBean(dimSecurityCode), -1, -1, null); | ||
166 | sel.addFilter(new FilterSelection(new DimBean(dimUser), -1, -1, [], [userAttr])); | ||
167 | var resultJSON = Packages.com.digdash.utils.ResultCubeToJavascript.getResultCubeLiveJSON(sessionId, JSON.stringify(sel), cubeId, null); | ||
168 | var cubRes = null; | ||
169 | eval("cubRes = " + resultJSON); | ||
170 | var resAxis = cubRes.axis[1]; | ||
171 | var mbr = []; | ||
172 | for (var i = 0; i < resAxis.length; ++i) | ||
173 | { | ||
174 | mbr.push(resAxis[i].i); | ||
175 | } | ||
176 | if (mbr.length > 0) | ||
177 | { | ||
178 | var dim = selection.dm.getDimensionById(dimSecurityCode); | ||
179 | var filt = new FilterSelection(dim, -1, -1, [], mbr); | ||
180 | selection.setFilter(filt); | ||
181 | Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " Dimension " + dimSecurityCode + " filtered on " + mbr); | ||
182 | } | ||
183 | else | ||
184 | { | ||
185 | Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " No " + dimSecurityCode + " found for user " + userAttr); | ||
186 | } | ||
187 | {{/code}} | ||
188 | |||
189 | = Vérifier l'existence d'une dimension dans un modèle de données = | ||
190 | |||
191 | Dans certains cas il peut être intéressant de vérifier qu'une dimension est présente dans le modèle de données, par exemple si le script de Live Security doit être réutilisé dans un modèle dépendant (union, transformateur de colonnes, etc.) | ||
192 | |||
193 | {{code cssClass="notranslate" language="javascript"}} | ||
194 | if (!selection.dm.getDimensionById("LaDimensionObligatoire")) | ||
195 | { | ||
196 | Packages.com.digdash.utils.MessageStack.getInstance().addError("LaDimensionObligatoire n'est pas dans le modèle de données"); | ||
197 | //dans cet exemple on choisit de faire échouer le Live Security si la dimension est absente du modèle | ||
198 | throw new Error("LaDimensionObligatoire n'est pas dans le modèle de données"); | ||
199 | } | ||
200 | |||
201 | //ajouter un filtre sur LaDimensionObligatoire... | ||
202 | {{/code}} |