Segurança
O PowerPortalsPro oferece um modelo de segurança flexível e orientado por código, que permite controlar o acesso tanto no nível da tabela quanto no nível do registro. A segurança é implementada criando classes de manipuladores de permissões e registrando-as no container de injeção de dependências.
Visão geral
Existem duas interfaces que você pode implementar dependendo do nível de controle que precisa:
ITablePermissionHandler— Controle se um usuário pode realizar operações de Crear, Lê, Atualizar, Deletar, Anexar ou Anexar em uma determinada tabela. As decisões são baseadas unicamente na identidade do usuário.ITableRecordPermissionHandler— EstendeITablePermissionHandlercom sobrecargas em nível de registro que recebem o realTableRecordem que está sendo operado, permitindo regras detalhadas como "os usuários só podem editar seus próprios registros."
Dica
Se você só precisar de verificações em nível de tabela (por exemplo, "usuários autenticados podem ler esta tabela"), use
ITablePermissionHandler. Se você precisar de lógica em nível de registro (por exemplo, "usuários só podem deletar registros que possuem"), useITableRecordPermissionHandler.
Começando
1. Criar um Manipulador de Permissão
Crie uma classe que implemente ITablePermissionHandler ou ITableRecordPermissionHandler. Defina a Table propriedade para o nome lógico da tabela Dataverse à qual o handler se aplica e implemente cada método de permissão.
Aqui está um exemplo simples que concede acesso total a todos os usuários para uma tabela específica:
public class MyTablePermissionHandler : ITablePermissionHandler
{
public string Table => "my_customtable";
public Task<bool> CanCreateAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanReadAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanUpdateAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanDeleteAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanAppendAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanAppendToAsync(Guid? userId) => Task.FromResult(true);
}
2. Registro na Injeção de Dependência
Registre seu handler no contêiner DI do Program.cs arquivo do seu projeto (ou em um método de extensão de coleção de serviços):
builder.Services.AddSingleton<ITablePermissionHandler, MyTablePermissionHandler>();
Como os handlers são resolvidos a partir do contêiner DI, você pode injetar quaisquer serviços necessários (como contexto de banco de dados ou serviço ao usuário) por meio da injeção de construtores.
Segurança em Nível de Registro
Para cenários em que o acesso depende do registro específico em que está sendo operado, implemente ITableRecordPermissionHandler. Essa interface se estende ITablePermissionHandler com sobrecargas que recebem o TableRecord.
O exemplo a seguir permite que todos os usuários leiam registros globalmente, mas restringe as operações de escrita aos registros pertencentes ao usuário atual:
public class ContactPermissionHandler : ITableRecordPermissionHandler
{
public string Table => "contact";
public List<string> RequiredColumns => [];
// Nível de mesa: permitir acesso amplo
public Task<bool> CanReadAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanCreateAsync(Guid? userId)
=> Task.FromResult(userId != null && userId != Guid.Empty);
// Nível de registro: restringa as escritas ao proprietário do disco
public Task<bool> CanUpdateAsync(
Guid? userId, TableRecord recordWithUpdates, TableRecord currentRecord)
{
return Task.FromResult(userId == currentRecord.Id);
}
public Task<bool> CanDeleteAsync(Guid? userId, TableRecord record)
{
return Task.FromResult(userId == record.Id);
}
// ... implementar métodos restantes
}
Nota
A
CanUpdateAsyncsobrecarga em nível de registro fornece tanto ao registro as atualizações propostas (recordWithUpdates) quanto ao registro em seu estado persistido atualmente (currentRecord), permitindo que você compare valores ou valide mudanças específicas de campo.
Colunas Obrigatórias
A RequiredColumns propriedade especifica quais colunas devem ser recuperadas para que o manipulador avalie permissões em nível de registro. O framework adiciona automaticamente essas colunas às consultas FetchXML quando elas ainda não estão incluídas, garantindo que o manipulador sempre tenha os dados necessários — mesmo que a visualização ou consulta não inclua essas colunas.
O exemplo a seguir usa a ppp_owningportaluserid coluna de consulta para restringir o acesso a registros pertencentes ao usuário atual:
public class RegionPermissionHandler : ITableRecordPermissionHandler
{
public string Table => "ppp_region";
public List<string> RequiredColumns => ["ppp_owningportaluserid"];
public Task<bool> CanReadAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanCreateAsync(Guid? userId) => Task.FromResult(userId != null);
public Task<bool> CanReadAsync(Guid? userId, TableRecord record)
{
return Task.FromResult(
record.GetValueOrDefault<LookupValue>("ppp_owningportaluserid")?.Value == userId);
}
// ... implementar métodos restantes
}
Nota
Se
RequiredColumnsretorna uma lista vazia e os métodos em nível de registro do handler usamrecord.Idapenas , nenhuma coluna adicional é adicionada à consulta. Aownerpropriedade é sempre fornecida pela plataforma, independentemente deRequiredColumns.
Interceptadores de Filtro de Consulta FetchXML
Além da segurança em nível de tabela e registro, o PowerPortalsPro suporta IFetchXmlQueryFilterInterceptor — uma interface que permite modificar consultas FetchXML antes de serem executadas. Isso é útil para impor filtragem em nível de linha, como garantir que um usuário só possa ver registros que pertencem a ele ou à sua equipe.
IFetchXmlQueryFilterInterceptor estende ITablePermissionHandler, então cada interceptor também define permissões em nível de tabela por meio da Table propriedade e dos métodos padrão de permissões. O método do OnQueryAsync interceptor é chamado apenas para consultas que têm como alvo sua tabela especificada.
Diferente dos manipuladores de permissões em nível de registro, que avaliam cada registro após a recuperação, interceptadores de filtro de consulta modificam a consulta antes que ela chegue ao Dataverse. Isso significa que registros filtrados nunca são recuperados, o que melhora tanto o desempenho quanto a segurança.
1. Criar um interceptador de filtro de consulta
Implemente IFetchXmlQueryFilterInterceptor, defina a Table propriedade para o nome lógico da tabela, implemente os métodos de permissão e use o FetchXMLBuilder in OnQueryAsync para adicionar filtros à consulta. O FetchXmlParameters registro fornece o arquivo do usuário EntityReferenceatual
FetchXMLBuilder
O exemplo a seguir restringe consultas na contact tabela para devolver apenas o registro correspondente ao usuário atual:
public class ContactQueryFilterInterceptor : IFetchXmlQueryFilterInterceptor
{
public string Table => "contact";
public Task<bool> CanCreateAsync(Guid? userId) => Task.FromResult(false);
public Task<bool> CanReadAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanUpdateAsync(Guid? userId) => Task.FromResult(false);
public Task<bool> CanDeleteAsync(Guid? userId) => Task.FromResult(false);
public Task<bool> CanAppendAsync(Guid? userId) => Task.FromResult(false);
public Task<bool> CanAppendToAsync(Guid? userId) => Task.FromResult(false);
public Task<FetchXMLBuilder> OnQueryAsync(
FetchXMLBuilder fetchXmlBuilder, FetchXmlParameters parameters)
{
var filter = fetchXmlBuilder.Fetch.Entity.AddFilter();
var condition = filter.AddCondition();
condition.Column = "contactid";
condition.Operator = ConditionOperator.Equal;
condition.Value = parameters.CurrentUser.Id.ToString();
return Task.FromResult(fetchXmlBuilder);
}
}
2. Registro na Injeção de Dependência
Registre seu interceptor tanto como um IFetchXmlQueryFilterInterceptor quanto como um ITablePermissionHandler para que tanto o filtro de consulta quanto as permissões em nível de tabela sejam aplicados. Múltiplos interceptadores podem ser registrados e cada um será executado para consultas direcionadas à sua tabela.
builder.Services.AddTransient<ContactQueryFilterInterceptor>();
builder.Services.AddTransient<ITablePermissionHandler>(
sp => sp.GetRequiredService<ContactQueryFilterInterceptor>());
builder.Services.AddTransient<IFetchXmlQueryFilterInterceptor>(
sp => sp.GetRequiredService<ContactQueryFilterInterceptor>());
Dica
Como
IFetchXmlQueryFilterInterceptorestendeITablePermissionHandler, uma única classe lida tanto com permissões em nível de tabela quanto com filtragem em nível de consulta. OOnQueryAsyncmétodo é invocado apenas para consultas que correspondem à propriedade doTableinterceptor, então não há necessidade de verificar o nome da tabela dentro do método.
Classes Base Integradas
A estrutura fornece classes base para simplificar padrões comuns de segurança:
AnonymousPermissionHandler— Concede todas as operações a todos os usuários (incluindo anônimos). Útil para tabelas acessíveis publicamente. Simplesmente herde e estabelece oTableimóvel.
Referência API
ITablePermissionHandler Interface
Propriedades
Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
Table | string | Nome lógico da tabela à qual esse manipulador de permissão se aplica. |
TableMétodos
Nome | Parâmetros | Tipo | Descrição |
|---|---|---|---|
CanAppendAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de anexar um registro. |
CanAppendToAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de anexar a um registro. |
CanCreateAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de criar um registro. |
CanDeleteAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de excluir um registro. |
CanReadAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de ler um registro. |
CanUpdateAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de atualizar um registro. |
CanAppendAsyncCanAppendToAsyncCanCreateAsyncCanDeleteAsyncCanReadAsyncCanUpdateAsyncITableRecordPermissionHandler Interface
Propriedades
Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
RequiredColumns | List<string> | Lista de nomes lógicos de colunas que devem ser recuperados para que este manipulador possa avaliar permissões em nível de registro. | |
Table | string | Nome lógico da tabela à qual esse manipulador de permissão se aplica. |
RequiredColumnsTableMétodos
Nome | Parâmetros | Tipo | Descrição |
|---|---|---|---|
CanAppendAsync | Guid? userId TableRecord record | Task<bool> | Método para determinar se um usuário deve ser capaz de anexar o registro fornecido. |
CanAppendToAsync | Guid? userId TableRecord record | Task<bool> | Método para determinar se um usuário deve ser capaz de adicionar ao registro fornecido. |
CanCreateAsync | Guid? userId TableRecord record | Task<bool> | Método para determinar se um usuário deve ser capaz de criar o registro fornecido. |
CanDeleteAsync | Guid? userId TableRecord record | Task<bool> | Método para determinar se um usuário deve ser capaz de excluir o registro fornecido. |
CanReadAsync | Guid? userId TableRecord record | Task<bool> | Método para determinar se um usuário deve ser capaz de ler o registro fornecido. |
CanUpdateAsync | Guid? userId TableRecord recordWithUpdates TableRecord currentRecord | Task<bool> | Método para determinar se um usuário deve ser capaz de atualizar o registro fornecido. |
CanAppendAsyncTableRecord record
CanAppendToAsyncTableRecord record
CanCreateAsyncTableRecord record
CanDeleteAsyncTableRecord record
CanReadAsyncTableRecord record
CanUpdateAsyncTableRecord recordWithUpdates
TableRecord currentRecord
IFetchXmlQueryFilterInterceptor Interface
Propriedades
Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
Table | string | Nome lógico da tabela à qual esse manipulador de permissão se aplica. |
TableMétodos
Nome | Parâmetros | Tipo | Descrição |
|---|---|---|---|
CanAppendAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de anexar um registro. |
CanAppendToAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de anexar a um registro. |
CanCreateAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de criar um registro. |
CanDeleteAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de excluir um registro. |
CanReadAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de ler um registro. |
CanUpdateAsync | Guid? userId | Task<bool> | Método para determinar se um usuário deve ser capaz de atualizar um registro. |
OnQueryAsync | FetchXMLBuilder fetchXmlBuilder FetchXmlParameters parameters | Task<FetchXMLBuilder> | Chamado para permitir a modificação de uma consulta FetchXML antes de sua execução. |
CanAppendAsyncCanAppendToAsyncCanCreateAsyncCanDeleteAsyncCanReadAsyncCanUpdateAsyncOnQueryAsyncFetchXmlParameters parameters
