Utiliser des tables de mappage pour le contrôle d’accès dynamique

Ce tutoriel montre comment utiliser une table de mappage pour contrôler l’accès au niveau des lignes et des colonnes sans gérer un grand nombre de groupes. Une table de recherche unique pilote le filtrage de lignes et le masquage des colonnes. Les modifications d’accès nécessitent uniquement une mise à jour de ligne. Vous n’avez pas besoin de créer de nouveaux groupes ou de réécrire des stratégies.

Ce tutoriel montre également le masquage conditionnel : les colonnes d’informations personnelles sont masquées différemment en fonction de la valeur d’une autre colonne sur la même ligne. Commandes marquées confidential ont leur PII entièrement occultée indépendamment du niveau d’autorisation de l’utilisateur.

Pour obtenir des conseils généraux sur la conception des tables de mappage, consultez Utiliser des tables de mappage pour créer une liste de contrôle d’accès.

Prerequisites

  • Databricks Runtime 16.4 ou version supérieure, ou calcul sans serveur.
  • Autorisations d’administrateur de compte ou d’administrateur d’espace de travail (pour créer des balises régies).
  • MANAGE autorisation sur le catalogue ou le schéma cible.
  • EXECUTE sur les UDF.
  • Un bloc-notes SQL ou un éditeur de requête.

Scénario

Votre organisation a des employés dans quatre régions (USA Est, USA Ouest, UE, APAC) et quatre départements. Chaque utilisateur doit voir uniquement les lignes qui correspondent à leur région et à leur service, et les colonnes d’informations personnelles doivent être masquées en fonction de deux facteurs : le niveau d’autorisation de l’utilisateur (fullou maskednone) stocké dans une table de mappage et les commandes order_priority.

Avec une approche basée sur un groupe, vous avez besoin d’un groupe pour chaque combinaison de services régionaux. Par exemple, vous avez besoin de 16 groupes pour quatre régions et quatre services. Ajouter des niveaux d'autorisation pour les informations personnelles triples ce décompte. Chaque nouvelle région ou service nécessite de nouveaux groupes et mises à jour de stratégie.

L’approche de la table de mappage remplace cela par une table de choix unique : une ligne par utilisateur, une colonne par dimension d’accès. Pour modifier l’accès d’un utilisateur, vous mettez à jour une ligne.

Étape 1 : Créer des balises régies

Avant d’exécuter une requête SQL, créez les balises régulées suivantes dans l’interface utilisateur de l’Explorateur de Catalogue (Catalogue>Gouverner>Balises régies>Créer une balise régie) :

Clé d’étiquette Valeurs autorisées
region (balise clé uniquement)
department (balise clé uniquement)
pii name, email
priority (balise clé uniquement)

Les balises region et department indiquent à la stratégie de filtre de ligne quelles colonnes passer à l'UDF de filtrage. La pii balise indique aux stratégies de masque de colonne les colonnes à masquer et le type d’informations personnelles qu’ils contiennent. La priority balise permet aux politiques de masquage de colonne de passer la order_priority valeur à la fonction UDF de masquage pour le masquage conditionnel.

Avertissement

Les données de balise sont stockées sous forme de texte brut et peuvent être répliquées globalement. N’utilisez pas de noms d’étiquettes, de valeurs ou de descripteurs susceptibles de compromettre la sécurité de vos ressources. Par exemple, n’utilisez pas de noms d’étiquettes, de valeurs ou de descripteurs qui contiennent des informations personnelles ou sensibles.

Étape 2 : Générer des exemples de données

Créez un catalogue, un schéma et une table commandes. La order_priority colonne génère un masquage conditionnel : les commandes marquées confidential ont leurs PII entièrement masquées, même pour les utilisateurs disposant d’une autorisation élevée.

CREATE CATALOG IF NOT EXISTS abac_tutorial;
USE CATALOG abac_tutorial;

CREATE SCHEMA IF NOT EXISTS mapping_demo;
USE SCHEMA mapping_demo;
CREATE OR REPLACE TABLE orders (
  order_id INT,
  customer_name STRING,
  customer_email STRING,
  sales_region STRING,
  dept STRING,
  amount DOUBLE,
  order_date DATE,
  order_priority STRING
);

INSERT INTO orders VALUES
  (1,  'Acme Corp',     'orders@acme.com',    'us_east', 'engineering', 50000,  '2025-01-15', 'standard'),
  (2,  'Beta Inc',      'sales@beta.com',     'us_east', 'sales',       75000,  '2025-02-01', 'confidential'),
  (3,  'Gamma LLC',     'info@gamma.com',     'us_west', 'engineering', 30000,  '2025-01-20', 'standard'),
  (4,  'Delta Co',      'deals@delta.com',    'us_west', 'sales',       95000,  '2025-03-01', 'confidential'),
  (5,  'Epsilon GmbH',  'kontakt@epsilon.de', 'eu',      'engineering', 45000,  '2025-02-15', 'standard'),
  (6,  'Zeta SA',       'contact@zeta.fr',    'eu',      'sales',       62000,  '2025-01-30', 'standard'),
  (7,  'Eta Ltd',       'hello@eta.sg',       'apac',    'marketing',   28000,  '2025-03-10', 'confidential'),
  (8,  'Theta Corp',    'biz@theta.com',      'us_east', 'marketing',   55000,  '2025-02-20', 'standard'),
  (9,  'Iota KK',       'info@iota.jp',       'apac',    'engineering', 41000,  '2025-01-25', 'standard'),
  (10, 'Kappa Inc',     'sales@kappa.com',    'us_west', 'marketing',   33000,  '2025-03-05', 'standard');

Étape 3 : Appliquer des balises régies

Étiquetez les colonnes afin que les stratégies ABAC puissent les découvrir automatiquement. La colonne order_priority est étiquetée avec la balise priority de manière à ce que les politiques de masquage de colonne puissent l'associer via MATCH COLUMNS et transmettre sa valeur à la fonction UDF de masquage.

ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN sales_region SET TAGS ('region' = '');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN dept SET TAGS ('department' = '');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN customer_name SET TAGS ('pii' = 'name');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN customer_email SET TAGS ('pii' = 'email');
ALTER TABLE abac_tutorial.mapping_demo.orders
  ALTER COLUMN order_priority SET TAGS ('priority' = '');

Étape 4 : Créer la table de mappage

Au lieu de créer des groupes pour chaque combinaison de région, de service et d’autorisation, vous conservez une table avec une ligne par utilisateur. La pii_access colonne contrôle l’apparence des colonnes d’informations personnelles :

  • full — voir la valeur réelle (pour les commandes de priorité standard)
  • masked — voir une valeur partielle telle que A*** ou o***@acme.com
  • none — voir ***REDACTED***

La expires_on colonne définit une date d’expiration pour chaque entrée d’accès. Après cette date, la fonction UDF du filtre de lignes cesse de correspondre à l’entrée et l’utilisateur perd silencieusement l’accès sans révocation manuelle nécessaire. Cela est utile pour les sous-traitants, les contrats de partage de données temporaires ou les projets limités dans le temps.

Si un utilisateur a besoin d’accéder à plusieurs combinaisons de régions et de services, ajoutez des lignes supplémentaires.

Note

Conservez les tables de mappage petites et simples. Chaque requête sur une table protégée exécute le filtre de lignes et les fonctions définies par l'utilisateur pour le masque de colonne, qui interrogent à leur tour la table de mappage. Les tables de mappage volumineuses et la logique UDF complexe peuvent avoir un impact sur les performances des requêtes. Utilisez des schémas étroits et conservez la logique UDF dans une recherche unique si possible.

CREATE OR REPLACE TABLE abac_tutorial.mapping_demo.user_access (
  user_email STRING,
  region STRING,
  department STRING,
  pii_access STRING,
  expires_on DATE
);

INSERT INTO abac_tutorial.mapping_demo.user_access VALUES
  (current_user(),      'us_east', 'engineering', 'masked', '2099-12-31'),
  ('bob@example.com',   'us_west', 'sales',       'full',   '2099-12-31'),
  ('carol@example.com', 'eu',      'engineering', 'none',   '2099-12-31'),
  ('david@example.com', 'apac',    'marketing',   'masked', '2099-12-31');

Étape 5 : Créer le filtre de lignes UDF

Cette fonction UDF reçoit les valeurs sales_region et dept d'une ligne (transmises par la politique via la correspondance de balises), recherche l'utilisateur actuel dans la table de mappage et retourne TRUE uniquement si une entrée correspondante existe et n’a pas expiré. Les utilisateurs ne figurant pas dans la table de mappage, ou dont l’accès a expiré, ne voient aucune ligne (conception non fermée).

CREATE OR REPLACE FUNCTION abac_tutorial.mapping_demo.access_filter(
  region_val STRING,
  dept_val STRING
)
RETURNS BOOLEAN
RETURN EXISTS (
  SELECT 1 FROM abac_tutorial.mapping_demo.user_access
  WHERE user_email = current_user()
    AND region = region_val
    AND department = dept_val
    AND expires_on >= current_date()
);

Étape 6 : Créer la fonction UDF du masque de colonne

Cette fonction UDF contrôle la façon dont les colonnes PII sont affichées. Il prend trois arguments : la valeur de colonne, le type PII ('name' ou 'email') et la ligne order_priority. La logique de masquage comporte deux couches :

  • Couche 1 (masquage conditionnel) : Si c’est le cas order_priorityconfidential, les informations personnelles sont toujours entièrement masquées, quel que soit le niveau d’autorisation de l’utilisateur.
  • Couche 2 (accréditation de l'utilisateur) : Pour les lignes standard, l'UDF vérifie la table de mappage pour le niveau d'accès pii_access de l'utilisateur et applique le masque correspondant. Si un utilisateur a plusieurs entrées de table de mappage (accès multirégion), l’autorisation la plus élevée sur toutes les lignes s’applique.
CREATE OR REPLACE FUNCTION abac_tutorial.mapping_demo.pii_mask(
  val STRING,
  pii_type STRING,
  order_pri STRING
)
RETURNS STRING
RETURN CASE
  WHEN order_pri = 'confidential' THEN '***REDACTED***'
  WHEN EXISTS (
    SELECT 1 FROM abac_tutorial.mapping_demo.user_access
    WHERE user_email = current_user() AND pii_access = 'full'
  ) THEN val
  WHEN EXISTS (
    SELECT 1 FROM abac_tutorial.mapping_demo.user_access
    WHERE user_email = current_user() AND pii_access = 'masked'
  ) THEN
    CASE pii_type
      WHEN 'email' THEN CONCAT(LEFT(val, 1), '***@', SUBSTRING_INDEX(val, '@', -1))
      WHEN 'name'  THEN CONCAT(LEFT(val, 1), '***')
      ELSE CONCAT(LEFT(val, 1), '***')
    END
  ELSE '***REDACTED***'
END;

Étape 7 : Créer les stratégies

Créez trois stratégies, toutes pilotées par la même table de mappage. Les deux stratégies de masque de colonne utilisent la même pii_mask fonction. L’argument pii_type indique à la fonction le style de masquage à appliquer. Vous n’avez donc pas besoin d’une fonction UDF distincte par type de colonne.

La balise priority régie est utilisée dans MATCH COLUMNS pour faire correspondre la colonne order_priority et transmettre sa valeur à l'UDF de masquage en tant que order_pri. C’est ainsi que le masquage conditionnel est implémenté : la stratégie transmet la valeur de priorité de la ligne à la fonction UDF (User-Defined Function) au moment de la requête.

CREATE POLICY user_access_filter
ON SCHEMA abac_tutorial.mapping_demo
ROW FILTER abac_tutorial.mapping_demo.access_filter
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag('region') AS r, has_tag('department') AS d
USING COLUMNS (r, d);
CREATE POLICY pii_mask_name
ON SCHEMA abac_tutorial.mapping_demo
COLUMN MASK abac_tutorial.mapping_demo.pii_mask
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag_value('pii', 'name') AS m,
  has_tag('priority') AS pri
ON COLUMN m
USING COLUMNS ('name', pri);

CREATE POLICY pii_mask_email
ON SCHEMA abac_tutorial.mapping_demo
COLUMN MASK abac_tutorial.mapping_demo.pii_mask
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag_value('pii', 'email') AS m,
  has_tag('priority') AS pri
ON COLUMN m
USING COLUMNS ('email', pri);

Étape 8 : Vérifier les résultats

Votre entrée de table de mappage vous donne accès à l’autorisation us_east / engineeringmasked . Exécutez la requête suivante pour vérifier que vous voyez uniquement l’ordre n°1, avec des informations d’identification personnelle partiellement masquées.

SELECT * FROM abac_tutorial.mapping_demo.orders;

La commande n° 1 contient order_priority = 'standard', donc votre autorisation masked s’applique.

Résultat attendu pour votre utilisateur :

identifiant_de_commande customer_name customer_email région_de_vente Département Montant date_de_commande priorité_de_commande
1 A*** o***@acme.com us_east Ingénierie 50000 2025-01-15 Norme

Ce que d’autres utilisateurs voient :

Utilisateur Commandes visibles priorité_de_commande Comportement des informations personnelles identifiables
bob@example.com (full autorisation) #4 (us_west, ventes) Confidentiel ***REDACTED*** — remplacements confidentiels de full l’autorisation
carol@example.com (none autorisation) #5 (eu, ingénierie) Norme ***REDACTED*** — l'autorisation none signifie une suppression complète
david@example.com (masked autorisation) #7 (APAC, marketing) Confidentiel ***REDACTED*** — confidentiel prend le pas sur masked autorisation
(propriétaire du catalogue) Tous les 10 Tous les éléments non masqués (le propriétaire est exempté des politiques)
(utilisateur non répertorié) Aucun Le filtre de lignes ne retourne aucune ligne

Notez que Bob dispose d’une full autorisation mais voit toujours ***REDACTED*** car l’ordre n° 4 est confidential. Il s’agit d’un masquage conditionnel : la valeur de priorité de la ligne remplace l’autorisation de l’utilisateur.

Étape 9 : Mettre à jour l’accès dynamiquement

L’avantage clé de l’approche de la table de mappage est que vous pouvez modifier l’accès en mettant à jour les lignes de la table. Vous n’avez pas besoin de mettre à jour les stratégies, les fonctions utilisateur ou les appartenances aux groupes.

Réaffecter à un autre service

Modifiez votre service de engineering à sales. La commande n° 2 (Beta Inc) est une confidential commande de vente, de sorte que ses PII sont entièrement masquées même avec masked autorisations.

UPDATE abac_tutorial.mapping_demo.user_access
SET department = 'sales'
WHERE user_email = current_user();

Exécutez la requête suivante pour vérifier. Vous devez voir l’ordre n° 2 avec ***REDACTED*** PII.

SELECT * FROM abac_tutorial.mapping_demo.orders;

Rétablir la modification :

UPDATE abac_tutorial.mapping_demo.user_access
SET department = 'engineering'
WHERE user_email = current_user();

Mise à niveau de l’autorisation d’identification personnelle

Changez votre autorisation de masked à full. Pour les lignes de priorité standard, vous voyez maintenant les valeurs d’identification personnelle réelles.

UPDATE abac_tutorial.mapping_demo.user_access
SET pii_access = 'full'
WHERE user_email = current_user();

Exécutez la requête suivante pour vérifier. L’ordre n° 1 est standard prioritaire, donc avec full autorisation, vous devriez voir Acme Corp et orders@acme.com.

SELECT * FROM abac_tutorial.mapping_demo.orders;

Rétablir la modification :

UPDATE abac_tutorial.mapping_demo.user_access
SET pii_access = 'masked'
WHERE user_email = current_user();

Accorder l’accès à une région supplémentaire

Insérez une deuxième ligne pour accorder l’accès à l’ingénierie européenne. Aucun nouveau groupe ou stratégie n’est requis.

INSERT INTO abac_tutorial.mapping_demo.user_access
VALUES (current_user(), 'eu', 'engineering', 'masked', '2099-12-31');

Exécutez la requête suivante pour vérifier. Vous devez maintenant voir à la fois les commandes n°1 (us_east, ingénierie) et n°5 (eu, ingénierie), avec PII partiellement masqué.

SELECT * FROM abac_tutorial.mapping_demo.orders;

Supprimez l’accès supplémentaire :

DELETE FROM abac_tutorial.mapping_demo.user_access
WHERE user_email = current_user() AND region = 'eu';

Expiration de l’accès

Définissez votre entrée d’accès sur une date passée. Les fonctions UDF du filtre de lignes vérifient expires_on >= current_date(), de sorte que les entrées expirées sont ignorées en silence et l'accès est automatiquement révoqué. Cela est utile pour les sous-traitants, les contrats de partage de données avec une durée fixe ou des projets limités dans le temps.

UPDATE abac_tutorial.mapping_demo.user_access
SET expires_on = current_date() - INTERVAL 1 DAY
WHERE user_email = current_user();

Exécutez la requête suivante pour vérifier qu’aucune ligne n’apparaît.

SELECT * FROM abac_tutorial.mapping_demo.orders;

Restaurer l’accès avec une date d’expiration ultérieure :

UPDATE abac_tutorial.mapping_demo.user_access
SET expires_on = '2099-12-31'
WHERE user_email = current_user();

Exécutez la requête suivante pour vérifier que l’accès est restauré.

SELECT * FROM abac_tutorial.mapping_demo.orders;

Résumé

Ce tutoriel a présenté trois modèles :

  • Modèle de table de mappage : une table de choix unique contrôle le filtrage de lignes et le masquage des colonnes. Les modifications d’accès sont apportées en mettant à jour les lignes, sans aucune stratégie ou aucune modification de groupe nécessaire.
  • Masquage conditionnel : la fonction UDF vérifie la order_priority colonne sur chaque ligne pour déterminer comment masquer les PII. Les lignes confidentielles sont toujours entièrement expurgées, quel que soit le niveau d’autorisation de l’utilisateur, ceci étant implémenté en les étiquetant avec order_priority et en les transmettant à l’UDF via MATCH COLUMNS.
  • Expiration de l’accès : la table de mappage inclut une expires_on date. La fonction UDF du filtre de lignes compare cette date à current_date(), de sorte que les entrées expirées sont ignorées discrètement, et l'accès est automatiquement révoqué sans intervention manuelle.

Nettoyage

Pour supprimer tous les objets créés dans ce didacticiel, exécutez ce qui suit.

DROP POLICY user_access_filter ON SCHEMA abac_tutorial.mapping_demo;
DROP POLICY pii_mask_name ON SCHEMA abac_tutorial.mapping_demo;
DROP POLICY pii_mask_email ON SCHEMA abac_tutorial.mapping_demo;
DROP FUNCTION IF EXISTS abac_tutorial.mapping_demo.access_filter;
DROP FUNCTION IF EXISTS abac_tutorial.mapping_demo.pii_mask;
DROP TABLE IF EXISTS abac_tutorial.mapping_demo.orders;
DROP TABLE IF EXISTS abac_tutorial.mapping_demo.user_access;
DROP SCHEMA IF EXISTS abac_tutorial.mapping_demo CASCADE;

Pour supprimer les balises region, department, pii et priority régies, utilisez l’interface utilisateur de l’Explorateur de catalogues.