Compartir a través de


Seguridad de nivel de columna con código

La seguridad de nivel de columna se aplica a las columnas que contienen información confidencial. Las contraseñas, los números de cuentas bancarias, la identificación oficial, los números de teléfono o las direcciones de correo electrónico se pueden proteger en el nivel de columna.

Este artículo explica cómo los desarrolladores pueden trabajar con capacidades de seguridad de nivel de columna mediante código y el SDK de Dataverse para .NET o la API web. No necesita escribir código para usar esta característica. Obtenga información acerca de cómo configurar la seguridad de nivel de columna para controlar el acceso. Los desarrolladores también deben comprender cómo configurar la seguridad de nivel de columna mediante Power Apps.

Descubrir qué columnas están protegidas

Para detectar qué columnas están protegidas, recupere la definición de la columna y examine la propiedad AttributeMetadata.IsSecured booleana.

Hay dos maneras de detectar qué columnas están protegidas con código. Estas maneras se describen en las dos secciones siguientes:

Recuperar datos de columna filtrados en IsSecured

Este método consulta los metadatos de la organización para identificar las columnas marcadas con la propiedad IsSecured establecida en true. Todo el mundo tiene acceso para ver estos datos. Más información acerca de definiciones de esquemas de consulta

El archivo CSV resultante contiene dos columnas: Tabla y Columna, que representan los nombres de esquema de las tablas y sus columnas protegidas, respectivamente.

/// <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());
}

Recuperar FieldSecurityProfile para el rol de administrador del sistema

Este método consulta la tabla de permisos de campo de Dataverse para identificar las columnas que protege el registro de Perfil de seguridad de campo (FieldSecurityProfile) con id. 572329c1-a042-4e22-be47-367c6374ea45. Este registro administra el acceso a las columnas protegidas para los administradores del sistema. Normalmente, solo los administradores del sistema tienen el privilegio de prvReadFieldPermission para recuperar estos datos.

El método GetSecuredColumnList estático devuelve nombres de columna completos en el formato TableName.ColumnName, ordenados alfabéticamente.

/// <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;
 }

Descubrir qué columnas se pueden proteger

No se pueden proteger todas las columnas. Al habilitar la seguridad de columna mediante Power Apps, la casilla Habilitar seguridad de columna está deshabilitada para determinados campos. No es necesario que compruebe manualmente cada columna para averiguar si puede protegerla. Escriba una consulta para recuperar qué columnas puede proteger.

Tres propiedades AttributeMetadata booleanas controlan si puede proteger una columna:

Cuando todas estas propiedades son falsas, la columna no se puede proteger. Es posible que algunas columnas solo estén protegidas para una o dos de las tres operaciones: Create, Read y Update.

Las siguientes consultas devuelven estos datos para que pueda detectar qué columnas de su entorno se pueden proteger:

Este método DumpColumnSecurityInfo estático recupera metadatos sobre los atributos de la entidad, incluidas las propiedades relacionadas con la seguridad, y escribe la información en un archivo CSV. El archivo de salida contiene detalles como si las columnas están protegidas, se pueden proteger para operaciones de creación, actualización o lectura, y otros metadatos relevantes.

/// <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());
}

Proteger una columna con código

Es más fácil Proteger una columna mediante Power Apps. Si necesita automatizar la protección de una columna, use código para actualizar la definición de columna para establecer la propiedad AttributeMetadata.IsSecured, como se muestra en los ejemplos siguientes:

Este método SetColumnIsSecured estático recupera la definición actual de la columna especificada y actualiza su estado de seguridad solo si el valor proporcionado difiere del valor actual. Si la columna ya está configurada en el estado de seguridad especificado, no se envía ninguna solicitud de actualización.

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

Más información acerca de cómo actualizar una columna con el SDK para .NET

Proporcionar acceso a columnas protegidas

De forma predeterminada, cuando una columna está protegida, solo las personas que tienen el rol de seguridad de administrador del sistema pueden leer o establecer el valor. Un administrador del sistema puede proporcionar a otros usuarios acceso a las columnas protegidas de dos maneras:

Administrar el acceso usando perfiles de seguridad de campo

Este enfoque es el más común cuando tiene diferentes grupos de usuarios que requieren diferentes niveles de acceso. Consulte el ejemplo de seguridad de nivel de columna que describe cómo proteger campos para diferentes usuarios que usan el centro de administración de Power Platform.

Para hacer esto usando código, cree registros de perfil de seguridad de campo (FieldSecurityProfile) que asocien entidades de seguridad (usuarios y equipos) con registros de permisos de campo (FieldPermission) que controlen qué operaciones de datos se pueden realizar en esa columna para cualquier registro.

Diagrama de relación de entidades para la tabla FieldSecurityProfile y tablas relacionadas

Puede asociar equipos y usuarios del sistema a su perfil de seguridad de campo mediante las relaciones de varios a varios systemuserprofiles_association y teamprofiles_association, respectivamente.

Asocie permisos de campo a los perfiles de seguridad de campo mediante la lk_fieldpermission_fieldsecurityprofileid relación de uno a varios. En la tabla siguiente se describen las columnas importantes de la tabla de permisos de campo:

Column Type Description
FieldSecurityProfileId Lookup Se refiere al perfil de seguridad de campo al que se aplica este permiso de campo.
EntityName String El nombre lógico de la tabla que contiene la columna protegida.
AttributeLogicalName String El nombre lógico de la columna protegida.
CanCreate Opción Si se permite crear acceso. Consulte Opciones de tipos de permisos de seguridad de campos
CanRead Opción Si se permite el acceso de lectura. Consulte Opciones de tipos de permisos de seguridad de campos
CanUpdate Opción Si se permite el acceso de actualización. Consulte Opciones de tipos de permisos de seguridad de campos
CanReadUnmasked Opción Si un valor desenmascarado se puede recuperar cuando CanRead es Permitido.

Opciones de tipos de permisos de seguridad de campos

Las columnas de opción CanCreate, CanRead y CanUpdate usan los valores definidos por la opción global field_security_permission_type:

  • 0 No permitido
  • 4 Permitidas

Nota

No establezca la columna CanReadUnmasked a menos que esté usando la característica de mostrar datos enmascarados y desee habilitar una aplicación para que devuelva el valor desenmascarado.

Compartir datos en campos protegidos

Cree registros de Uso compartido de campos (PrincipalObjectAttributeAccess) para compartir el acceso a un campo protegido para un registro específico con otra persona.

Nota

Conceptualmente, este proceso es similar a la tabla PrincipalObjectAccess que administra el uso compartido de registros. La diferencia es que con el uso compartido de registros, usa los mensajes GrantAccess, ModifyAccess y RevokeAccess para agregar, modificar y eliminar registros de la tabla PrincipalObjectAccess. Más información sobre el uso compartido de registros

Con el uso compartido de campos, utilice la tabla PrincipalObjectAttributeAccess para conceder, modificar y revocar el acceso a campos mediante operaciones de creación, actualización y eliminación en una fila de tabla.

La tabla PrincipalObjectAttributeAccess tiene estas columnas:

Column Type Description
AttributeId GUID The AttributeMetadata.MetadataId de la columna protegida.
ObjectId Lookup Una referencia al registro que contiene la columna protegida.
PrincipalId Lookup Una referencia a la entidad de seguridad (usuario o equipo) a la que concede acceso.
ReadAccess Booleano Si se debe conceder acceso de lectura a los datos de campo
UpdateAccess Booleano Si se concede acceso de actualización a los datos de campo

Obtención de la columna AttributeId

La columna PrincipalObjectAttributeAccess.AttributeId usa AttributeMetadata.MetadataId en lugar del nombre lógico de la columna. Debe recuperar este valor de los metadatos. Si la aplicación tiene una caché de metadatos, puede incluir estos datos y acceder a ellos cuando sea necesario.

Ejemplo de recuperación de AttributeId de columna

En este ejemplo se muestra cómo obtener el valor AttributeMetadata.MetadataId que necesita para establecer el valor de la columna PrincipalObjectAttributeAccess.AttributeId.

Los ejemplos del SDK de Conceder acceso a columnas, Modificar acceso a columnas y Revocar acceso a columnas para .NET usan el método estático RetrieveColumnId para recuperar el valor AttributeMetadata.MetadataId usado en la columna 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;
}

Más información acerca de definiciones de esquemas de consulta

Ejemplo de concesión de acceso a columna

Estos ejemplos crean un nuevo registro Uso compartido de campos (PrincipalObjectAttributeAccess) para compartir el acceso al campo especificado.

Este método le permite compartir permisos de lectura y/o actualización para una columna protegida en una tabla de Dataverse con una entidad de seguridad específica (usuario o equipo). La columna debe configurarse como un campo protegido en Dataverse.

Este ejemplo depende de la función de ejemplo RetrieveColumnId que se encuentra en Recuperar ejemplo de AttributeId de columna.

/// <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}");
    }
}

Ejemplo de modificación de acceso a columnas

Estos ejemplos recuperan y actualizan un registro de Uso compartido de campos (PrincipalObjectAttributeAccess) existente para modificar el acceso al campo especificado.

Este ejemplo depende de la función de ejemplo RetrieveColumnId que se encuentra en Recuperar ejemplo de AttributeId de columna.

/// <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.");
    }
}

Ejemplo de revocación de acceso a columna

Estos ejemplos recuperan y eliminan un registro de Uso compartido de campos (PrincipalObjectAttributeAccess) existente para revocar el acceso al campo especificado.

Este ejemplo depende de la función de ejemplo RetrieveColumnId que se encuentra en Recuperar ejemplo de AttributeId de columna.

/// <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.");
    }
}

Mostrar datos enmascarados

El comportamiento predeterminado de la API al devolver un valor para una columna protegida es no devolver datos. La aplicación que realiza la llamada no puede distinguir entre un valor protegido y un valor nulo.

Ahora hay una característica en vista previa que puede usar para especificar que se devuelva un valor de cadena cuando existan datos. Esta cadena puede ofuscar totalmente el valor o mostrar partes de los datos, según las reglas de enmascaramiento que defina. De esta manera, la aplicación puede administrar mejor los datos confidenciales.

Con esta característica, puede configurar registros de permisos de campo (FieldPermission) para crear perfiles de seguridad de campo que permitan a las aplicaciones enviar solicitudes para recuperar registros sin enmascaramiento, de modo que los datos se puedan mostrar en circunstancias controladas. Más información acerca de la recuperación de datos desenmascarados

Crear una regla de enmascaramiento segura

Cada columna que muestra datos enmascarados debe hacer referencia a una fila de la tabla Regla de enmascaramiento protegida (MaskingRule). Puede crear reglas de enmascaramiento seguras en Power Apps y agregarlas a su solución, o puede usar cualquiera de las reglas existentes.

Cree registros de la tabla Columna de enmascaramiento protegido (AttributeMaskingRule) para especificar qué regla de enmascaramiento debe usar una columna segura.

El diagrama siguiente describe estas tablas:

Diagrama que muestra las columnas y las relaciones entre las tablas MaskingRule y AttributeMaskingRule

Columnas de regla de enmascaramiento protegida

La tabla Regla de enmascaramiento protegida (MaskingRule) tiene estas columnas que se pueden escribir:

Column Type Description
Name String El nombre único de la regla de enmascaramiento segura.
Description String Descripción de la regla de enmascaramiento protegida.
DisplayName String El nombre para mostrar de la regla de enmascaramiento segura.
MaskedCharacter String Carácter utilizado para el enmascaramiento.
RegularExpression String Expresión regular de C#
IsCustomizable BooleanManagedProperty Información que especifica si este componente se puede personalizar. Más información acerca de las propiedades administradas
RichTestData String Establezca datos de prueba de texto enriquecido para probar esta regla de enmascaramiento protegida.
MaskedRichTestData String Datos de columna RichTestData evaluados por esta regla de enmascaramiento protegida.
TestData String Establezca datos de prueba para probar esta regla de enmascaramiento protegida.
MaskedTestData String Datos de columna TestData evaluados por una regla de enmascaramiento protegida.

Nota

Las columnas RichTestData, MaskedRichTestData, TestData y MaskedTestData existen para admitir la experiencia para probar las reglas de enmascaramiento en Power Apps. Más información acerca de la creación de reglas de enmascaramiento.

Columnas de enmascaramiento protegida

La tabla Columna de enmascaramiento protegida (AttributeMaskingRule) tiene estas columnas que se pueden escribir:

Column Type Description
AttributeLogicalName String Nombre lógico de la columna para la que se utiliza la regla de enmascaramiento protegida.
EntityName String Nombre lógico de la tabla que contiene la columna.
MaskingRuleId Lookup La regla de enmascaramiento que usa la columna
UniqueName String El nombre único de la columna de enmascaramiento protegida.
IsCustomizable BooleanManagedProperty Información que especifica si este componente se puede personalizar. Más información acerca de las propiedades administradas

Recuperar datos desenmascarados

Cuando una columna de registro de CanRead esPermitido, puede establecer la columna de opción CanReadUnmasked cuando la columna tenga un registro Columna de enmascaramiento protegida (AttributeMaskingRule) asociado a ella.

La columna CanReadUnmasked admite las siguientes opciones definidas por la opción global field_security_permission_readunmasked.

valor Label Description
0 No permitido Valor predeterminado del campo. Si no hay un AttributeMaskingRule para la columna, no puede establecer ningún otro valor.
1 Un registro Los datos desenmascarados solo se pueden devolver mediante una operación Retrieve.
3 Todos los registros Los datos desenmascarados solo se pueden devolver mediante operaciones Retrieve y RetrieveMultiple.

Ejemplo de recuperación de datos desenmascarados

Los siguientes ejemplos muestran cómo utilizar el parámetro opcional UnMaskedData para solicitar que se devuelva el valor desenmascarado cuando la configuración del permiso de campo lo permite.

El ejemplo GetUnmaskedExampleRows devuelve valores desenmascarados para cualquiera de las columnas solicitadas en las que el valor de la columna CanReadUnmasked de permiso de campo se establece en Todos los registros porque el parámetro UnMaskedData opcional se agrega a la solicitud RetrieveMultiple.

Este método consulta la tabla sample_example y recupera columnas específicas, incluidos datos confidenciales, como la identificación gubernamental y la fecha de nacimiento. Los resultados de la consulta se ordenan por la columna sample_name en orden descendente.

/// <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;
}

Seguridad y datos de acceso
Compartir y asignar
Ejemplo: Seguridad de nivel de columna mediante SDK de Dataverse para .NET
Ejemplo: Seguridad de nivel de columna mediante API Web de Dataverse (PowerShell)