Live Security

Modifié par Aurelie Bertrand le 2023/12/12 17:31


Définir la fonction Live Security

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.

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.

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 /* START CONFIG */ et /* END CONFIG */) :

/* START CONFIG */
//version: 2019R2
var securedDims = {};
securedDims["DIMENSION_NAME_1"] = getUserAttribute("USER_PARAM_1");

/* Ajouter ici les autres dimensions sur lequel du live security doit s’appliquer */
/*
securedDims["DIMENSION_NAME_2"] = getUserAttribute("USER_PARAM_2");
*/


/* END CONFIG */

/* MODIFY THE SCRIPT BELOW WITH CAUTION */
var sLogPrefix = "[LIVE_SECURITY] [live-sec-thread-" + Math.floor(Math.random()*16777215).toString(16) + "]"; /* 16777215 is FFFFFF in decimal */

for (var dimId in securedDims)
{
   var dim = selection.dm.objName[dimId];
   var persoVal = securedDims[dimId];

   //Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + "Applying user filters on: " + dimId + ", persoVal:" + persoVal);
   if (dim)
    {
       if (persoVal == null || persoVal.length == 0)
        {
           // user must no see any value
           var persoValuesTab = ["-noval-"];
           var filt = new FilterSelection(dim, -1, -1, [], persoValuesTab);
            selection.setFilter(filt);
           Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Filters (members) on " + dimId + ": [" + persoValuesTab + "]");
        }
       else if (persoVal && persoVal != ".*")
        {
           //user is limited to some value(s)
           var persoValuesTab = persoVal.split("|");
           Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Applying user filters on " + dimId + ": [" + persoValuesTab + "]");
           var exFilter = selection.filterByDimName[dimId];
           if (!exFilter)
            {
               //there is no exisitng filter on that dimension => create a new one
               var filt = new FilterSelection(dim, -1, -1, [], persoValuesTab);
                selection.setFilter(filt);
               Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Resolved filters (members) on " + dimId + ": [" + persoValuesTab + "]");
            }
           else
            {
               //there is already a filter on that dimension => merge (intersect) into a new one
               var filt = new FilterSelection(dim, -1, -1, [], persoValuesTab);
                filt.recalcIds();
                exFilter.recalcIds();
                exFilter = mergeFilters(exFilter, filt);
                origIdsTab = exFilter.origIds;
                selection.setFilter(exFilter);
               Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Resolved filters (origIds) on " + dimId + ": [" + exFilter.origIds + "]");
            }
        }
       else // perso value is .*
       {
           // do nothing, user can see everything
           Packages.com.digdash.utils.MessageStack.getInstance().addDebug(sLogPrefix + " Resolved filters on " + dimId + ": All (.*)");
        }
    }
   else
    {
       Packages.com.digdash.utils.MessageStack.getInstance().addError(sLogPrefix + " Dimension " + dimId + "not found");
    }
}

Attribut Utilisateur

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).
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.

https://lh6.googleusercontent.com/PFz-w1dqknwbuP_W-YeSH0EVWjnR4mbESAsEgTjyNSAKFAwChSJPSkLCVaZOricQLA4g_SGytqGFj0go_OFQUlDRbd8ZSsMC3PrfLq4GV3Kluv_SSy8iO0LEqzqbcdvyYPKHuVfA9fU

Live Security pour interdire l’affichage d’une mesure

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 :

var show_measure = getUserAttribute('show_measure');
var measureId = 'CA'; // id of the measure to hide

if (show_measure == 'non')
{
   var measIndex = selection.indexOfMeasure(measureId);
   if (measIndex > 0)
        selection.removeMeasure(measIndex);
}

Live Security à appliquer à un graphique précis

On peut aussi spécialiser le Live Security en fonction du graphique :

if (flowId != null && flowId.indexOf("mon_flow_id") > -1)
{
   //Ajouter le code ici pour transformer la sélection de manière spécifique au flux
}
else
{
   //Ajouter le code ici pour tous les autres flux
}

Live Security utilisant un autre cube

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.

Explication du cas

La données « de base » est une table de transactions de flux monétaire.
Il existe un champ « SECURITY_CODE » qui sert à savoir au final si un utilisateur connecté à le droit de voir la transaction ou pas.
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.
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.

Solution Live Security

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.
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.

Voici le code du script Live Security pour cet exemple (à adapter selon votre cas, entre /* START CONFIG */ et /* END CONFIG */) :

/* START CONFIG */
var cubeId = "id_cube_authorisations";
var userAttr = getUserAttribute("USER_LOGIN_SSO");
var dimSecurityCode = "SECURITY_CODE";
var dimUser = "LOGIN";
/* END CONFIG */

var sLogPrefix = "[SECURITY] [live-sec-thread-" + Math.floor(Math.random()*16777215).toString(16) + "]";
Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " start...");
DimBean = function(id)
{
   this.id = id;
   this.members = [];
}

DimBean.prototype.toJSON = function()
{
   return this.id;
}

Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " user is " + userAttr);
var sel = new DataModelSelection();
sel.dm = { "variables":{} };
sel.pivot = 0;
sel.addBrowse(1, new DimBean(dimSecurityCode), -1, -1, null);
sel.addFilter(new FilterSelection(new DimBean(dimUser), -1, -1, [], [userAttr]));
var resultJSON = Packages.com.digdash.utils.ResultCubeToJavascript.getResultCubeLiveJSON(sessionId, JSON.stringify(sel), cubeId, null);
var cubRes = null;
eval("cubRes = " + resultJSON);
var resAxis = cubRes.axis[1];
var mbr = [];
for (var i = 0; i < resAxis.length; ++i)
{
    mbr.push(resAxis[i].i);
}
if (mbr.length > 0)
{
   var dim = selection.dm.getDimensionById(dimSecurityCode);
   var filt = new FilterSelection(dim, -1, -1, [], mbr);
    selection.setFilter(filt);
   Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " Dimension " + dimSecurityCode + " filtered on " + mbr);
}
else
{
   Packages.com.digdash.utils.MessageStack.getInstance().addText(sLogPrefix + " No " + dimSecurityCode + " found for user " + userAttr);
}

Vérifier l'existence d'une dimension dans un modèle de données

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.)

if (!selection.dm.getDimensionById("LaDimensionObligatoire"))
{
   Packages.com.digdash.utils.MessageStack.getInstance().addError("LaDimensionObligatoire n'est pas dans le modèle de données");
   //dans cet exemple on choisit de faire échouer le Live Security si la dimension est absente du modèle
   throw new Error("LaDimensionObligatoire n'est pas dans le modèle de données");
}

//ajouter un filtre sur LaDimensionObligatoire...