Partager via


Sécurité au niveau de la colonne avec code

La sécurité au niveau des colonnes est appliquée aux colonnes qui contiennent des informations sensibles. Les mots de passe, les numéros de compte bancaire, les identifiants officiels, les numéros de téléphone ou les adresses e-mail peuvent être sécurisés au niveau de la colonne.

Cet article explique comment les développeurs peuvent utiliser les fonctionnalités de sécurité au niveau de la colonne à l’aide du code et du Kit de développement logiciel (SDK) pour .NET ou l’API web Dataverse. Nul besoin d’écrire de code pour utiliser cette fonctionnalité. Découvrez comment configurer la sécurité au niveau de la colonne pour contrôler l’accès. Les développeurs doivent également comprendre comment configurer la sécurité au niveau de la colonne avec Power Apps.

Découvrir quelles colonnes sont sécurisées

Détectez quelles colonnes sont sécurisées en récupérant la définition de la colonne et en examinant la propriété booléenne AttributeMetadata.IsSecured.

Il existe deux façons de découvrir quelles colonnes sont sécurisées par du code. Ces façons sont décrites dans les deux sections suivantes :

Récupérer les données de colonne filtrées sur IsSecured

Cette méthode interroge les métadonnées de l’organisation pour identifier les colonnes marquées avec la propriété IsSecured définie sur true. Chacun a accès à ces données. En savoir plus sur l’interrogation des définitions de schéma

Le fichier CSV qui en résulte contient deux colonnes : Table et Colonne, représentant respectivement les noms de schéma des tables et leurs colonnes sécurisées.

/// <summary>
/// Generates a CSV file containing the names of secured columns for all tables 
/// in the organization.
/// </summary>
/// <remarks>This method queries the organization's metadata to identify columns 
/// marked as secured (i.e., columns with the <c>IsSecured</c> property set to 
/// <see langword="true"/>). The resulting CSV file contains two columns: "Table" 
/// and "Column", representing the schema names of the tables and their secured 
/// columns, respectively. <para> Ensure that the provided 
/// <paramref name="filepath"/> is writable and that the user has appropriate 
/// permissions to access the specified directory. </para></remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// retrieve metadata from the organization.</param>
/// <param name="filepath">The directory path where the CSV file will be saved. 
/// Must be a valid and accessible file path.</param>
/// <param name="filename">The name of the CSV file to be created. Defaults to 
/// "SecuredColumns.csv" if not specified.</param>
static internal void GetSecuredColumns(IOrganizationService service,
    string filepath, string filename = "SecuredColumns.csv")
{
    EntityQueryExpression query = new()
    {
        Properties = new MetadataPropertiesExpression(
            "SchemaName",
            "Attributes"),
        Criteria = new MetadataFilterExpression(),
        AttributeQuery = new()
        {
            Properties = new MetadataPropertiesExpression(
                "SchemaName",
                "AttributeTypeName"),
            Criteria = new MetadataFilterExpression()
            {
                Conditions = {
                    {
                        new MetadataConditionExpression(
                            "IsSecured",
                            MetadataConditionOperator.Equals,
                            true)
                    }
                }
            }
        }
    };

    RetrieveMetadataChangesRequest request = new()
    {
        Query = query
    };

    var response = (RetrieveMetadataChangesResponse)service.Execute(request);


    // Create a StringBuilder to hold the CSV data
    StringBuilder csvContent = new();

    string[] columns = {
        "Table",
        "Column" };

    // Add headers
    csvContent.AppendLine(string.Join(",", columns));

    foreach (var table in response.EntityMetadata)
    {
        foreach (var column in table.Attributes)
        {
            string[] values = {
                table.SchemaName,
                column.SchemaName
            };

            // Add values
            csvContent.AppendLine(string.Join(",", values));
        }
    }

    File.WriteAllText(
        Path.Combine(filepath, filename),
        csvContent.ToString());
}

Récupérer le rôle FieldSecurityProfile pour le rôle d’administrateur système

Cette méthode interroge la table d’autorisation du champ Dataverse pour identifier les colonnes sécurisées par l’enregistrement du profil de sécurité du champ (FieldSecurityProfile) avec un ID 572329c1-a042-4e22-be47-367c6374ea45. Cet enregistrement gère l’accès aux colonnes sécurisées pour les administrateurs système. En règle générale, seuls les administrateurs système ont le privilège prvReadFieldPermission de récupérer ces données.

La méthode GetSecuredColumnList statique renvoie des noms de colonne complets dans le format TableName.ColumnName, triés par ordre alphabétique.

/// <summary>
/// Retrieves a list of secured columns managed by the specified field security 
/// profile.
/// </summary>
/// <remarks>This method queries the Dataverse field permission table to identify 
/// columns that are secured by the field security profile with ID 
/// <c>572329c1-a042-4e22-be47-367c6374ea45</c>. The returned list contains fully 
/// qualified column names in the format <c>TableName.ColumnName</c>, sorted 
/// alphabetically.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// interact with the Dataverse service.</param>
/// <returns>A sorted list of strings representing the fully qualified names of 
/// secured columns.</returns>
/// <exception cref="Exception">Thrown if the calling user does not have read 
/// access to the field permission table or if an error occurs while retrieving 
/// field permissions.</exception>
 static internal List<string> GetSecuredColumnList(IOrganizationService service)
 {
     QueryExpression query = new("fieldpermission")
     {
         ColumnSet = new ColumnSet("entityname", "attributelogicalname"),
         Criteria = new FilterExpression(LogicalOperator.And)
         {
             Conditions =
             {
               // Field security profile with ID '572329c1-a042-4e22-be47-367c6374ea45' 
               // manages access for system administrators. It always contains
               // references to each secured column

                 new ConditionExpression("fieldsecurityprofileid", ConditionOperator.Equal,
                     new Guid("572329c1-a042-4e22-be47-367c6374ea45"))
             }
         }
     };

     EntityCollection fieldPermissions;

     try
     {
         fieldPermissions = service.RetrieveMultiple(query);
     }
     catch (FaultException<OrganizationServiceFault> ex)
     {

         if (ex.Detail.ErrorCode.Equals(-2147220960))
         {
             string message = "The calling user doesn't have read access to the fieldpermission table";

             throw new Exception(message);
         }

         else
         {
             throw new Exception($"Dataverse error retrieving field permissions: {ex.Message}");
         }
     }
     catch (Exception ex)
     {
         throw new Exception($"Error retrieving field permissions: {ex.Message}", ex);
     }

     List<string> values = [];
     foreach (var fieldpermission in fieldPermissions.Entities)
     {
         string tableName = fieldpermission.GetAttributeValue<string>("entityname")!;
         string columnName = fieldpermission.GetAttributeValue<string>("attributelogicalname")!;
         values.Add($"{tableName}.{columnName}");
     }
     values.Sort();
     return values;
 }

Découvrir quelles colonnes peuvent être sécurisées

Vous ne pouvez pas sécuriser toutes les colonnes. Lorsque vous activez la sécurité de colonne à l’aide de Power Apps, la case à cocher Activer la sécurité de colonne est désactivée pour certains champs. Vous n’avez pas besoin de vérifier manuellement chaque colonne pour savoir si vous pouvez la sécuriser. Écrivez une requête pour récupérer les colonnes que vous pouvez sécuriser.

Trois propriétés booléennes AttributeMetadata contrôlent si vous pouvez sécuriser n’importe quelle colonne :

Lorsque toutes ces propriétés sont définies sur false, la colonne ne peut pas être sécurisée. Certaines colonnes peuvent n’être sécurisées que pour une ou deux des trois opérations : Create, Read, et Update.

Les requêtes suivantes renvoient ces données afin que vous puissiez découvrir quelles colonnes de votre environnement peuvent être sécurisées :

Cette méthode statique DumpColumnSecurityInfo récupère les métadonnées relatives aux attributs d’entité, y compris les propriétés liées à la sécurité, et écrit les informations dans un fichier CSV. Le fichier de sortie contient des détails tels que si les colonnes sont sécurisées, peuvent être sécurisées pour les opérations de création, de mise à jour ou de lecture, et d’autres métadonnées pertinentes.

/// <summary>
/// Exports column security information for all entities in the organization to a 
/// CSV file.
/// </summary>
/// <remarks>This method retrieves metadata about entity attributes, including 
/// security-related properties, and writes the information to a CSV file. The output 
/// file contains details such as whether columns are secured, can be secured for 
/// create, update, or read operations, and other relevant metadata.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// retrieve metadata from the organization.</param>
/// <param name="filepath">The directory path where the CSV file will be saved. This 
/// must be a valid, writable directory.</param>
/// <param name="filename">The name of the CSV file to create. Defaults to 
/// "ColumnSecurityInfo.csv" if not specified.</param>
static internal void DumpColumnSecurityInfo(IOrganizationService service,
    string filepath, string filename = "ColumnSecurityInfo.csv")
{
    EntityQueryExpression query = new()
    {
        Properties = new MetadataPropertiesExpression("SchemaName", "Attributes"),
        Criteria = new MetadataFilterExpression
        {
            FilterOperator = LogicalOperator.And,
            Conditions =
             {
                 new MetadataConditionExpression(
                     "IsPrivate",
                     MetadataConditionOperator.Equals,
                     false),
             }
        },
        AttributeQuery = new()
        {
            Properties = new MetadataPropertiesExpression(
                "SchemaName",
                "AttributeTypeName",
                "IsPrimaryName",
                "IsSecured",
                "CanBeSecuredForCreate",
                "CanBeSecuredForUpdate",
                "CanBeSecuredForRead"),
            Criteria = new MetadataFilterExpression()
            {
                Conditions = {
                    { // Exclude Virtual columns
                        new MetadataConditionExpression(
                        "AttributeTypeName",
                        MetadataConditionOperator.NotEquals,
                        AttributeTypeDisplayName.VirtualType)
                    }
                }
            }
        }
    };

    RetrieveMetadataChangesRequest request = new()
    {
        Query = query
    };

    var response = (RetrieveMetadataChangesResponse)service.Execute(request);


    // Create a StringBuilder to hold the CSV data
    StringBuilder csvContent = new();

    string[] columns = {
        "Column",
        "Type",
        "IsPrimaryName",
        "IsSecured",
        "CanBeSecuredForCreate",
        "CanBeSecuredForUpdate",
        "CanBeSecuredForRead" };

    // Add headers
    csvContent.AppendLine(string.Join(",", columns));

    foreach (var table in response.EntityMetadata)
    {
        foreach (AttributeMetadata column in table.Attributes)
        {
            string[] values = {
                $"{table.SchemaName}.{column.SchemaName}",
                column.AttributeTypeName.Value,
                column.IsPrimaryName?.ToString() ?? "False",
                column.IsSecured?.ToString() ?? "False",
                column.CanBeSecuredForCreate?.ToString() ?? "False",
                column.CanBeSecuredForUpdate.ToString() ?? "False",
                column.CanBeSecuredForRead.ToString() ?? "False"
            };

            // Add values
            csvContent.AppendLine(string.Join(",", values));
        }
    }

    File.WriteAllText(
        Path.Combine(filepath, filename),
        csvContent.ToString());
}

Sécuriser une colonne avec du code

Il est plus facile de Sécuriser une colonne à l’aide de Power Apps. Si vous devez automatiser la sécurisation d’une colonne, utilisez le code pour mettre à jour la définition de colonne afin de définir la propriété AttributeMetadata.IsSecured, comme illustré dans les exemples suivants :

Cette méthode SetColumnIsSecured statique récupère la définition actuelle de la colonne spécifiée et met à jour son état de sécurité uniquement si la valeur fournie diffère de la valeur actuelle. Si la colonne est déjà définie sur l’état de sécurité spécifié, aucune demande de mise à jour n’est envoyée.

/// <summary>
/// Updates the security status of a column in a Dataverse table.
/// </summary>
/// <remarks>This method retrieves the current definition of the specified column 
/// and updates its security status only if the provided value differs from the 
/// current value. If the column is already set to the specified security status, 
/// no update request is sent.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// interact with the Dataverse service.</param>
/// <param name="tableLogicalName">The logical name of the table containing the 
/// column to be updated. Cannot be null or empty.</param>
/// <param name="columnLogicalName">The logical name of the column whose security 
/// status is to be updated. Cannot be null or empty.</param>
/// <param name="value">A <see langword="true"/> value indicates that the column 
/// should be secured; otherwise, <see langword="false"/>.</param>
/// <param name="solutionUniqueName">The unique name of the solution in which the 
/// column update should be applied. Cannot be null or empty.</param>
/// <exception cref="Exception">Thrown if an error occurs while retrieving or 
/// updating the column definition.</exception>
static internal void SetColumnIsSecured(
    IOrganizationService service,
    string tableLogicalName,
    string columnLogicalName,
    bool value,
    string solutionUniqueName)
{

    // Update request requires the entire column definition,
    // So retrieving that first

    RetrieveAttributeRequest retrieveRequest = new()
    {
        EntityLogicalName = tableLogicalName,
        LogicalName = columnLogicalName
    };

    AttributeMetadata columnDefinition;

    try
    {
        var retrieveResponse = (RetrieveAttributeResponse)service.Execute(retrieveRequest);

        columnDefinition = retrieveResponse.AttributeMetadata;
    }
    catch (Exception ex)
    {
        throw new Exception($"Error retrieving column definition: {ex.Message}", ex);
    }

    if (!columnDefinition.IsSecured.HasValue || columnDefinition.IsSecured.Value != value)
    {
        // Set the IsSecured property to value
        columnDefinition.IsSecured = value;

        UpdateAttributeRequest updateRequest = new()
        {
            EntityName = tableLogicalName,
            Attribute = columnDefinition,
            MergeLabels = true,
            SolutionUniqueName = solutionUniqueName
        };

        try
        {
            service.Execute(updateRequest);
        }
        catch (Exception ex)
        {
            throw new Exception($"Error updating column definition: {ex.Message}", ex);
        }
    }
    else
    {
        //Don't send a request to set the value to what it already is.
    }
}

Découvrez comment mettre à jour une colonne à l’aide du Kit de développement logiciel (SDK) pour .NET

Fournir l’accès aux colonnes sécurisées

Par défaut, lorsqu’une colonne est sécurisée, seules les personnes ayant le rôle de sécurité Administrateur système peuvent lire ou définir la valeur. Un administrateur système peut fournir à d’autres utilisateurs l’accès aux colonnes sécurisées de deux manières :

Gérer l’accès aux profils de sécurité du champ

Cette approche est la plus courante lorsque vous avez différents groupes d’utilisateurs qui nécessitent différents niveaux d’accès. Consultez l’exemple de sécurité au niveau de la colonne qui décrit comment sécuriser des champs pour différents utilisateurs à l’aide du centre d’administration Power Platform.

Pour ce faire à l’aide de code, créez des enregistrements de profil de sécurité de champ (FieldSecurityProfile) qui associent les principaux (utilisateurs et équipes) à des enregistrements d’autorisation de champ (FieldPermission) qui contrôlent les opérations de données pouvant être effectuées sur cette colonne pour n’importe quel enregistrement.

Diagramme de relation d’entité pour la table FieldSecurityProfile et les tables associées

Vous pouvez associer des utilisateurs système et des équipes à votre profil de sécurité de champ à l’aide des relations plusieurs-à-plusieurs systemuserprofiles_association et teamprofiles_association respectivement.

Associez les autorisations de champ aux profils de sécurité de champ à l’aide de la lk_fieldpermission_fieldsecurityprofileidrelation un-à-plusieurs. Le tableau suivant décrit les colonnes importantes de la table d’autorisation de champ :

Column Type Description
FieldSecurityProfileId Lookup Fait référence au profil de sécurité de champ auquel s’applique cette autorisation de champ.
EntityName String Nom logique de la table contenant la colonne sécurisée.
AttributeLogicalName String Nom logique de la colonne sécurisée.
CanCreate Option Si l’accès à la création est autorisé. Voir Options du type d’autorisation de sécurité de champ
CanRead Option Si l’accès en lecture est autorisé. Voir Options du type d’autorisation de sécurité de champ
CanUpdate Option Indique si l’accès à la mise à jour est autorisé. Voir Options du type d’autorisation de sécurité de champ
CanReadUnmasked Option Indique si une valeur non masquée peut être récupérée quand CanRead est autorisée.

Options du type d’autorisation de sécurité de champ

Les colonnes de choix CanCreate, CanRead, et CanUpdate utilisent les valeurs définies par le choix global field_security_permission_type :

  • 0 Pas autorisé
  • 4 Autorisé

Nonte

Ne définissez pas la colonne CanReadUnmasked, sauf si vous utilisez la fonctionnalité Affichage des données masquées et que vous souhaitez activer une application pour qu’elle renvoie la valeur non masquée.

Partager les données dans les champs sécurisés

Créez des enregistrements de partage de champ (PrincipalObjectAttributeAccess) pour partager l’accès à un champ sécurisé pour un enregistrement spécifique avec quelqu’un d’autre.

Nonte

Conceptuellement, ce processus est similaire à la table PrincipalObjectAccess qui gère le partage des enregistrements. La différence réside dans le fait qu’avec le partage d’enregistrements, vous utilisez les messages GrantAccess, ModifyAccess, et RevokeAccess pour ajouter, modifier et supprimer des enregistrements de la table PrincipalObjectAccess. En savoir plus sur le partage des enregistrements

Avec le partage de champ, utilisez la table pour accorder, modifier et révoquer l’accès PrincipalObjectAttributeAccess aux champs à l’aide des opérations de création, de mise à jour et de suppression sur une ligne de table.

La table PrincipalObjectAttributeAccess a les colonnes suivantes :

Column Type Description
AttributeId Guid Le champ AttributeMetadata.MetadataId de la colonne sécurisée.
ObjectId Lookup Référence à l’enregistrement qui contient la colonne sécurisée.
PrincipalId Lookup Référence au principal (utilisateur ou équipe) auquel vous accordez l’accès.
ReadAccess Bool Accorder ou non un accès en lecture aux données de champ
UpdateAccess Bool Indique s’il faut accorder ou non l’accès de mise à jour aux données du champ

Obtention de l’ID d’attribut de colonne

La colonne PrincipalObjectAttributeAccess.AttributeId utilise le champ AttributeMetadata.MetadataId plutôt que le nom logique de la colonne. Vous devez récupérer cette valeur à partir des métadonnées. Si votre application dispose d’un cache de métadonnées, vous pouvez inclure ces données et y accéder selon vos besoins.

Exemple de récupération d’AttributeId de colonne

Cet exemple montre comment obtenir la valeur valeur AttributeMetadata.MetadataId dont vous avez besoin pour définir la valeur de colonne PrincipalObjectAttributeAccess.AttributeId.

Les exemples SDK for .NET Accorder l’accès à la colonne, Modifier l’accès à la colonne et Révoquer l’accès à la colonne utilisent la méthode RetrieveColumnId statique pour récupérer la valeur AttributeMetadata.MetadataId utilisée dans la colonne PrincipalObjectAttributeAccess.AttributeId.

/// <summary>
/// Retrieves the unique identifier (MetadataId) of a column in a specified 
/// Dataverse table.
/// </summary>
/// <remarks>
/// This method queries the organization's metadata to locate the specified column 
/// within the given table and returns its MetadataId. If the table or column is 
/// not found, an exception is thrown.
/// </remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// retrieve metadata from the organization.</param>
/// <param name="tableLogicalName">The logical name of the table containing the 
/// column. Must not be null or empty.</param>
/// <param name="columnLogicalName">The logical name of the column whose MetadataId 
/// is to be retrieved. Must not be null or empty.</param>
/// <returns>The <see cref="Guid"/> representing the MetadataId of the specified 
/// column.</returns>
/// <exception cref="Exception">Thrown if the table or column is not found in the 
/// metadata.</exception>
private static Guid RetrieveColumnId(
    IOrganizationService service,
    string tableLogicalName,
    string columnLogicalName)
{

    EntityQueryExpression query = new()
    {
        Properties = new MetadataPropertiesExpression("Attributes"),
        Criteria = new MetadataFilterExpression(filterOperator: LogicalOperator.Or)
        {
            Conditions = {
                {
                    new MetadataConditionExpression(
                        propertyName:"LogicalName",
                        conditionOperator: MetadataConditionOperator.Equals,
                        value:tableLogicalName)
                }
            },
        },
        AttributeQuery = new AttributeQueryExpression
        {
            Properties = new MetadataPropertiesExpression("MetadataId"),
            Criteria = new MetadataFilterExpression(filterOperator: LogicalOperator.And)
            {
                Conditions = {
                    {
                        new MetadataConditionExpression(
                        propertyName:"LogicalName",
                        conditionOperator: MetadataConditionOperator.Equals,
                        value:columnLogicalName)
                    }
                }
            }
        }
    };

    RetrieveMetadataChangesRequest request = new()
    {
        Query = query
    };

    var response = (RetrieveMetadataChangesResponse)service.Execute(request);

    Guid columnId;

    if (response.EntityMetadata.Count == 1)
    {

        if (response.EntityMetadata[0].Attributes.Length == 1)
        {

            // Nullable property will not be null when retrieved. It is set by the system.
            columnId = response.EntityMetadata[0].Attributes[0].MetadataId!.Value;
        }
        else
        {
            throw new Exception($"Column {columnLogicalName} not found in {tableLogicalName}.");
        }
    }
    else
    {

        throw new Exception($"Table {tableLogicalName} not found");
    }
    return columnId;
}

En savoir plus sur l’interrogation des définitions de schéma

Exemple d’octroi d’accès à la colonne

Ces exemples créent un enregistrement de partage de champ (PrincipalObjectAttributeAccess) pour partager l’accès au champ spécifié.

Cette méthode vous permet de partager les autorisations de lecture et/ou de mise à jour d’une colonne sécurisée d’une table Dataverse avec un principal spécifique (utilisateur ou équipe). La colonne doit être configurée en tant que champ sécurisé dans Dataverse.

Cet exemple dépend de l’exemple de fonction RetrieveColumnId trouvé dans l’exemple Récupérer l’exemple de la colonne AttributeId.

/// <summary>
/// Grants access to a secured column for a specified principal in Dataverse.
/// </summary>
/// <remarks>This method allows you to share read and/or update permissions for a 
/// secured column in a Dataverse table with a specific principal (user or team). 
/// The column must be configured as a secured field in Dataverse.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// interact with Dataverse.</param>
/// <param name="record">A reference to the record (entity instance) containing the 
/// secured column.</param>
/// <param name="columnLogicalName">The logical name of the secured column to grant 
/// access to.</param>
/// <param name="principal">A reference to the principal (user or team) to whom 
/// access is being granted.</param>
/// <param name="readAccess"><see langword="true"/> to grant read access to the 
/// secured column; otherwise, <see langword="false"/>.</param>
/// <param name="updateAccess"><see langword="true"/> to grant update access to the 
/// secured column; otherwise, <see langword="false"/>.</param>
/// <exception cref="Exception">Thrown if the column has already been shared or if 
/// an error occurs during the operation.</exception>
static internal void GrantColumnAccess(
    IOrganizationService service,
    EntityReference record,
    string columnLogicalName,
    EntityReference principal,
    bool readAccess,
    bool updateAccess)
{
    // This information should come from cached metadata,
    // but for this sample it is retrieved each time.
    Guid columnId = RetrieveColumnId(
        service: service,
        tableLogicalName: record.LogicalName,
        columnLogicalName: columnLogicalName);

    // https://learn.microsoft.com/power-apps/developer/data-platform/reference/entities/principalobjectattributeaccess
    Entity poaa = new("principalobjectattributeaccess")
    {
        //Unique identifier of the shared secured field
        ["attributeid"] = columnId,
        //Unique identifier of the entity instance with shared secured field
        ["objectid"] = record,
        //Unique identifier of the principal to which secured field is shared
        ["principalid"] = principal,
        // Read permission for secured field instance
        ["readaccess"] = readAccess,
        //Update permission for secured field instance
        ["updateaccess"] = updateAccess
    };

    try
    {
        service.Create(poaa);
    }
    catch (FaultException<OrganizationServiceFault> ex)
    {
        if (ex.Detail.ErrorCode.Equals(-2147158773))
        {
            throw new Exception("The column has already been shared");
        }

        throw new Exception($"Dataverse error in GrantColumnAccess: {ex.Message}");

    }
    catch (Exception ex)
    {
        throw new Exception($"Error in GrantColumnAccess: {ex.Message}");
    }
}

Exemple de modification de l’accès aux colonnes

Ces exemples récupèrent et mettent à jour un enregistrement de partage de champ (PrincipalObjectAttributeAccess) existant pour modifier l’accès au champ spécifié.

Cet exemple dépend de l’exemple de fonction RetrieveColumnId trouvé dans l’exemple Récupérer l’exemple de la colonne AttributeId.

/// <summary>
/// Modifies access permissions for a secure column in a table for a specified 
/// principal.
/// </summary>
/// <remarks>This method updates or creates a record in the 
/// PrincipalObjectAttributeAccess table to reflect the specified access 
/// permissions. If no matching record is found, an exception is thrown.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// interact with the organization service.</param>
/// <param name="record">An <see cref="EntityReference"/> representing the record 
/// containing the secure column.</param>
/// <param name="columnLogicalName">The logical name of the secure column whose 
/// access permissions are being modified.</param>
/// <param name="principal">An <see cref="EntityReference"/> representing the 
/// principal (user or team) for whom access permissions are being 
/// modified.</param>
/// <param name="readAccess">A <see langword="bool"/> indicating whether read 
/// access to the secure column should be granted (<see langword="true"/>) or 
/// revoked (<see langword="false"/>).</param>
/// <param name="updateAccess">A <see langword="bool"/> indicating whether update 
/// access to the secure column should be granted (<see langword="true"/>) or 
/// revoked (<see langword="false"/>).</param>
/// <exception cref="Exception">Thrown if no matching 
/// PrincipalObjectAttributeAccess record is found for the specified column, 
/// record, and principal.</exception>
static internal void ModifyColumnAccess(
    IOrganizationService service,
    EntityReference record,
    string columnLogicalName,
    EntityReference principal,
    bool readAccess,
    bool updateAccess)
{

    // This information should come from cached metadata,
    // but for this sample it is retrieved each time.
    Guid columnId = RetrieveColumnId(
        service: service,
        tableLogicalName: record.LogicalName,
        columnLogicalName: columnLogicalName);

    // Retrieve the record
    QueryExpression query = new("principalobjectattributeaccess")
    {
        ColumnSet = new ColumnSet(
            "principalobjectattributeaccessid",
            "readaccess",
            "updateaccess"),
        Criteria = new FilterExpression(LogicalOperator.And)
        {
            // There can only be one record or zero records matching these criteria.
            Conditions = {
                {
                    new ConditionExpression(
                        attributeName:"attributeid",
                        conditionOperator: ConditionOperator.Equal,
                        value:columnId)
                },
                {
                    new ConditionExpression(
                        attributeName:"objectid",
                        conditionOperator: ConditionOperator.Equal,
                        value:record.Id)
                },
                {
                    new ConditionExpression(
                        attributeName:"principalid",
                        conditionOperator: ConditionOperator.Equal,
                        value:principal.Id)
                },
                {
                    new ConditionExpression(
                        attributeName:"principalidtype",
                        conditionOperator: ConditionOperator.Equal,
                        value:principal.LogicalName)
                }
            }
        }
    };

    EntityCollection queryResults = service.RetrieveMultiple(query);

    if (queryResults.Entities.Count == 1)
    {
        // Update the record that granted access to the secure column
        Entity retrievedPOAARecord = queryResults.Entities[0];
        // Get the current values and only update if different
        bool currentRead = retrievedPOAARecord.GetAttributeValue<bool>("readaccess");
        bool currentUpdate = retrievedPOAARecord.GetAttributeValue<bool>("updateaccess");

        Entity POAAForUpdate = new("principalobjectattributeaccess", retrievedPOAARecord.Id);

        if (currentRead != readAccess)
        {
            POAAForUpdate.Attributes.Add("readaccess", readAccess);
        }
        if (currentUpdate != updateAccess)
        {
            POAAForUpdate.Attributes.Add("updateaccess", updateAccess);
        }

        // Don't update if nothing there is nothing to change
        if (POAAForUpdate.Attributes.Count > 0)
        {
            // Update the principalobjectattributeaccess record
            service.Update(POAAForUpdate);
        }
    }
    else
    {
        throw new Exception("No matching PrincipalObjectAttributeAccess record found.");
    }
}

Exemple de révocation de l’accès à la colonne

Ces exemples récupèrent et suppriment un enregistrement de partage de champ (PrincipalObjectAttributeAccess) existant pour révoquer l’accès au champ spécifié.

Cet exemple dépend de l’exemple de fonction RetrieveColumnId trouvé dans l’exemple Récupérer l’exemple de la colonne AttributeId.

/// <summary>
/// Revokes access to a secure column for a specified principal in a given record.
/// </summary>
/// <remarks>This method removes the access granted to a secure column for the 
/// specified principal. If no matching access record is found, an exception is 
/// thrown.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// interact with the Dataverse service.</param>
/// <param name="record">An <see cref="EntityReference"/> representing the record 
/// containing the secure column.</param>
/// <param name="columnLogicalName">The logical name of the secure column for which 
/// access is being revoked.</param>
/// <param name="principal">An <see cref="EntityReference"/> representing the 
/// principal (user or team) whose access to the secure column is being 
/// revoked.</param>
/// <exception cref="Exception">Thrown if no matching 
/// PrincipalObjectAttributeAccess record is found for the specified column, 
/// record, and principal.</exception>
internal static void RevokeColumnAccess(IOrganizationService service,
    EntityReference record,
    string columnLogicalName,
    EntityReference principal)
{

    // This information should come from cached metadata,
    // but for this sample it is retrieved each time.
    Guid columnId = RetrieveColumnId(
        service: service,
        tableLogicalName: record.LogicalName,
        columnLogicalName: columnLogicalName);


    QueryExpression query = new("principalobjectattributeaccess")
    {
        ColumnSet = new ColumnSet("principalobjectattributeaccessid"),
        Criteria = new FilterExpression(LogicalOperator.And)
        {
            // These conditions return one or zero records
            Conditions = {
                {
                    new ConditionExpression(
                        attributeName:"attributeid",
                        conditionOperator: ConditionOperator.Equal,
                        value:columnId)
                },
                {
                    new ConditionExpression(
                        attributeName:"objectid",
                        conditionOperator: ConditionOperator.Equal,
                        value:record.Id)
                },
                {
                    new ConditionExpression(
                        attributeName:"principalid",
                        conditionOperator: ConditionOperator.Equal,
                        value:principal.Id)
                },
                {
                    new ConditionExpression(
                        attributeName:"principalidtype",
                        conditionOperator: ConditionOperator.Equal,
                        value:principal.LogicalName)
                }
            }
        }
    };

    EntityCollection queryResults = service.RetrieveMultiple(query);

    if (queryResults.Entities.Count == 1)
    {
        // Delete the record that granted access to the secure column
        service.Delete("principalobjectattributeaccess", queryResults.Entities[0].Id);
    }
    else
    {
        throw new Exception("No matching PrincipalObjectAttributeAccess record found.");
    }
}

Afficher les données masquées

Le comportement par défaut de l’API lors du renvoi d’une valeur pour une colonne sécurisée est de ne renvoyer aucune donnée. L’application appelante ne peut pas faire la distinction entre une valeur sécurisée et une valeur nulle.

Il existe désormais une fonctionnalité d’évaluation que vous pouvez utiliser pour spécifier qu’une valeur de chaîne est renvoyée lorsque des données existent. Cette chaîne peut totalement obscurcir la valeur ou afficher des parties des données en fonction des règles de masquage que vous définissez. De cette façon, l’application peut mieux gérer les données sensibles.

Grâce à cette fonctionnalité, vous pouvez configurer des enregistrements d’autorisation de champ (FieldPermission) pour créer des profils de sécurité de champ qui permettent aux applications d’envoyer des demandes de récupération d’enregistrements sans le masquage afin que les données puissent être affichées dans des circonstances contrôlées. En savoir plus sur la récupération des données non masquées

Créer une règle de masquage sécurisée

Chaque colonne qui affiche des données masquées doit faire référence à une ligne de table Règle de masquage sécurisée (MaskingRule). Vous pouvez créer des règles de masquage sécurisées dans Power Apps et les ajouter à votre solution, ou vous pouvez utiliser l’une des règles existantes.

Créez des enregistrements de table Colonne de Masquage Sécurisée (AttributeMaskingRule) pour spécifier une règle de masquage à utiliser par une colonne sécurisée.

Le diagramme suivant décrit ces tables :

Diagramme montrant les colonnes et les relations entre les tables MaskingRule et AttributeMaskingRule

Colonnes de règle de masquage sécurisée

La table Règle de masquage sécurisée (MaskingRule) comporte les colonnes inscriptibles suivantes :

Column Type Description
Name String Nom unique de la règle de masquage sécurisée.
Description String Description de la règle de masquage sécurisée.
DisplayName String Nom d’affichage de la règle de masque sécurisé.
MaskedCharacter String Caractère utilisé pour masquer.
RegularExpression String Expression régulière dans C#.
IsCustomizable BooleanManagedProperty Information indiquant si ce composant est personnalisable. En savoir plus sur les propriétés gérées
RichTestData String Définissez les données de test de texte enrichi pour tester cette règle de masquage sécurisée.
MaskedRichTestData String Données de la colonne RichTestData évaluées par cette règle de masquage sécurisée.
TestData String Définissez les données de test pour tester cette règle de masquage sécurisée.
MaskedTestData String Données de la colonne TestData évaluées par une règle de masquage sécurisée.

Nonte

Les colonnes RichTestData, MaskedRichTestData, TestData, et MaskedTestData existent pour prendre en charge l’expérience de test des règles de masquage dans Power Apps. En savoir plus sur la création des règles de masquage.

Colonnes de colonne de masquage sécurisée

La table Colonne de masquage sécurisée (MaskingRule) comporte les colonnes inscriptibles suivantes :

Column Type Description
AttributeLogicalName String Nom logique de la colonne pour laquelle la règle de masquage sécurisé est utilisée.
EntityName String Nom logique de la table contenant la colonne.
MaskingRuleId Lookup La règle de masquage utilisée par la colonne
UniqueName String Le nom unique de la colonne de masquage sécurisée.
IsCustomizable BooleanManagedProperty Information indiquant si ce composant est personnalisable. En savoir plus sur les propriétés gérées

Récupérer les données non masquées

Lorsqu’une colonne d’enregistrement de type CanRead est définie sur Autorisée, définsisez la colonne de choix CanReadUnmasked lorsque la colonne a un enregistrement Colonne de masquage sécurisée (AttributeMaskingRule) qui y est associé.

La colonne CanReadUnmasked prend en charge les options suivantes définies par le choix global field_security_permission_readunmasked.

Valeur Label Description
0 Pas autorisé Valeur par défaut. S’il n’y a pas de AttributeMaskingRule pour la colonne, vous ne pouvez pas définir toute autre valeur.
1 Un enregistrement Les données non masquées ne peuvent être renvoyées qu’à l’aide d’une opération Retrieve.
3 Tous les enregistrements Les données non masquées ne peuvent être renvoyées qu’à l’aide des opérations Retrieve et RetrieveMultiple.

Exemple : récupérer les données non masquées

Les exemples suivants montrent comment utiliser le UnMaskedData paramètre facultatif pour demander que la valeur non masquée soit renvoyée lorsque la configuration de l’autorisation de champ le permet.

L’exemple GetUnmaskedExampleRows renvoie des valeurs non masquées pour toute des colonnes demandées où la valeur de la colonne CanReadUnmasked d’autorisation de champ est définie sur Tous les enregistrements, car le paramètre facultatif UnMaskedData est ajouté à la demande RetrieveMultiple.

Cette méthode interroge la table sample_example et récupère des colonnes spécifiques, y compris des données sensibles telles que l’ID officiel et la date de naissance. Les résultats de la requête sont classés par la colonne sample_name dans l’ordre décroissant.

/// <summary>
/// Retrieves a collection of example entities with unmasked data.
/// </summary>
/// <remarks>This method queries the "sample_example" entity and retrieves specific 
/// columns, including sensitive data such as government ID and date of birth. The 
/// query results are ordered by the "sample_name" column in descending order. The 
/// method uses the "UnMaskedData" optional parameter to ensure that sensitive data 
/// is returned unmasked. For more information on optional parameters, see <see 
/// href="https://learn.microsoft.com/power-apps/developer/data-platform/optional-parameters">Optional 
/// Parameters in Dataverse</see>.</remarks>
/// <param name="service">The <see cref="IOrganizationService"/> instance used to 
/// execute the query.</param>
/// <returns>An <see cref="EntityCollection"/> containing the retrieved entities. 
/// The collection includes unmasked data for the specified columns.</returns>
internal static EntityCollection GetUnmaskedExampleRows(IOrganizationService service)
{
    QueryExpression query = new("sample_example")
    {
        ColumnSet = new ColumnSet(
            "sample_name",
            "sample_email",
            "sample_governmentid",
            "sample_telephonenumber",
            "sample_dateofbirth"),
        Criteria = new FilterExpression(),
        Orders = {
            {
                new OrderExpression(
                    "sample_name",
                    OrderType.Descending)
            }
        }
    };

    RetrieveMultipleRequest request = new()
    {
        Query = query,
        // This example uses 'UnMaskedData' as an optional parameter
        // https://learn.microsoft.com/power-apps/developer/data-platform/optional-parameters
        ["UnMaskedData"] = true
    };


    var response = (RetrieveMultipleResponse)service.Execute(request);

    return response.EntityCollection;
}

Sécurité et accès aux données
Partage et attribution
Exemple : Sécurité au niveau de la colonne à l’aide de Dataverse SDK for .NET
Exemple : Sécurité au niveau de la colonne à l’aide de l’API web Dataverse (PowerShell)