Sicherheit
PowerPortalsPro bietet ein flexibles, codebasiertes Sicherheitsmodell, das es Ihnen ermöglicht, den Zugriff sowohl auf Tabellen- als auch auf Datensatzebene zu steuern. Sicherheit wird implementiert, indem Berechtigungshandler-Klassen erstellt und im Dependency Injection-Container registriert werden.
Überblick
Es gibt zwei Schnittstellen, die du je nach benötigtem Kontrollniveau implementieren kannst:
ITablePermissionHandler— Kontrolliert, ob ein Benutzer die Operationen Erstellen, Lesen, Aktualisieren, Löschen, Anhängen oder Anhängen auf einer gegebenen Tabelle ausführen kann. Entscheidungen basieren ausschließlich auf der Identität des Nutzers.ITableRecordPermissionHandler— ErweitertITablePermissionHandlersich um Datensatzebene-Überlastungen, die das tatsächlichTableRecordbearbeitete Werk empfangen, wodurch feingradige Regeln wie "Benutzer können nur ihre eigenen Datensätze bearbeiten" ermöglichen.
Tipp
Wenn Sie nur Kontrolle auf Tabellenebene benötigen (z. B. "authentifizierte Benutzer können diese Tabelle lesen"), verwenden Sie
ITablePermissionHandler. Wenn Sie Datensatz-Logik benötigen (z. B. "Benutzer können nur Datensätze löschen, die sie besitzen"), verwendenITableRecordPermissionHandlerSie .
Anfang
1. Einen Berechtigungshandler erstellen
Erstelle eine Klasse, die oder ITableRecordPermissionHandlerimplementiertITablePermissionHandler. Setze die Eigenschaft Table auf den logischen Namen der Dataverse-Tabelle, auf die der Handler angewendet wird, und implementiere jede Berechtigungsmethode.
Hier ist ein einfaches Beispiel, das allen Benutzern für eine bestimmte Tabelle vollen Zugriff gewährt:
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. Registrieren in der Abhängigkeitsinjektion
Registrieren Sie Ihren Handler im DI-Container in der Program.cs Datei Ihres Projekts (oder in einer Service Collection Extension Methode):
builder.Services.AddSingleton<ITablePermissionHandler, MyTablePermissionHandler>();
Da Handler aus dem DI-Container aufgelöst werden, können Sie alle erforderlichen Services (wie Datenbankkontext oder Benutzerdienst) per Konstruktor-Injektion injizieren.
Rekordsicherheit auf Rekordniveau
Für Szenarien, in denen der Zugriff von dem spezifischen Datensatz abhängt, der bearbeitet wird, implementieren ITableRecordPermissionHandlerSie . Diese Schnittstelle erstreckt ITablePermissionHandler sich mit Überlastungen, die das empfangen.TableRecord
Das folgende Beispiel erlaubt es allen Benutzern, Datensätze global zu lesen, beschränkt jedoch die Schreibvorgänge auf Datensätze, die dem aktuellen Benutzer gehören:
public class ContactPermissionHandler : ITableRecordPermissionHandler
{
public string Table => "contact";
public List<string> RequiredColumns => [];
// Tischniveau: Zugang allgemein erlauben
public Task<bool> CanReadAsync(Guid? userId) => Task.FromResult(true);
public Task<bool> CanCreateAsync(Guid? userId)
=> Task.FromResult(userId != null && userId != Guid.Empty);
// Datensatzebene: Beschränke Schreibvorgänge auf den Datensatzbesitzer
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);
}
// ... Implementiere die verbleibenden Methoden
}
Anmerkung
Die
CanUpdateAsyncDatensatzebene-Überladung stellt sowohl den Datensatz mit den vorgeschlagenen Aktualisierungen (recordWithUpdates) als auch den Datensatz im aktuell gespeicherten Zustand bereit (currentRecord), sodass Sie Werte vergleichen oder spezifische Feldänderungen validieren können.
Erforderliche Spalten
Die Eigenschaft RequiredColumns legt fest, welche Spalten abgerufen werden müssen, damit der Handler die Berechtigungen auf Datensatzebene auswerten kann. Das Framework fügt diese Spalten automatisch zu FetchXML-Abfragen hinzu, wenn sie noch nicht enthalten sind, sodass der Handler immer die benötigten Daten hat – selbst wenn die Ansicht oder Abfrage diese Spalten nicht enthält.
Das folgende Beispiel verwendet die ppp_owningportaluserid Nachschlagespalte, um den Zugriff auf Datensätze des aktuellen Benutzers einzuschränken:
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);
}
// ... Implementiere die verbleibenden Methoden
}
Anmerkung
Wenn
RequiredColumnseine leere Liste zurückgegeben wird und die Methoden auf Datensatzebene des Handlers nur verwendenrecord.Id, werden keine zusätzlichen Spalten zur Abfrage hinzugefügt. Die Eigenschaftownerwird immer von der Plattform bereitgestellt, unabhängig davon.RequiredColumns
FetchXML Abfragefilter-Interceptoren
Zusätzlich zur Sicherheit auf Tabellen- und Datensatzebene unterstützt IFetchXmlQueryFilterInterceptor PowerPortalsPro – eine Schnittstelle, mit der Sie FetchXML-Abfragen vor deren Ausführung anpassen können. Dies ist nützlich, um Zeilen-Filterung durchzusetzen, etwa indem sichergestellt wird, dass ein Benutzer nur Datensätze sehen kann, die ihm oder seinem Team gehören.
IFetchXmlQueryFilterInterceptorITablePermissionHandlererweitert , sodass jeder Abfanger auch tabellenbezogene Berechtigungen über die Table Eigenschaft und die Standardberechtigungsmethoden definiert. Die OnQueryAsync Methode des Interceptors wird nur für Abfragen aufgerufen, die auf die angegebene Tabelle abzielen.
Im Gegensatz zu Datensatz-Berechtigungshandlern, die jeden Datensatz nach dem Abruf evaluieren, ändern Abfragefilter-Interceptoren die Abfrage, bevor sie Dataverse erreicht. Das bedeutet, dass herausgefilterte Datensätze nie abgerufen werden, was sowohl die Leistung als auch die Sicherheit verbessert.
1. Erstellen Sie einen Abfragefilter-Interceptor
Implementiere IFetchXmlQueryFilterInterceptor, setze die Table Eigenschaft auf den logischen Namen der Tabelle, implementiere die Berechtigungsmethoden und nutze die FetchXMLBuilder In, OnQueryAsync um Filter zur Abfrage hinzuzufügen. Der FetchXmlParameters Datensatz liefert das aktuelle Benutzer.EntityReference
FetchXMLBuilder
Das folgende Beispiel beschränkt Abfragen in der contact Tabelle darauf, nur den Datensatz zurückzugeben, der dem aktuellen Benutzer entspricht:
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. Registrieren in der Abhängigkeitsinjektion
Registrieren Sie Ihren Interceptor sowohl als ein IFetchXmlQueryFilterInterceptor als auch als an ITablePermissionHandler , damit sowohl die Abfragefilterung als auch die Berechtigungen auf Tabellenebene angewendet werden. Mehrere Abfangsender können registriert werden, und jeder wird für Abfragen ausgeführt, die auf seine Tabelle abzielen.
builder.Services.AddTransient<ContactQueryFilterInterceptor>();
builder.Services.AddTransient<ITablePermissionHandler>(
sp => sp.GetRequiredService<ContactQueryFilterInterceptor>());
builder.Services.AddTransient<IFetchXmlQueryFilterInterceptor>(
sp => sp.GetRequiredService<ContactQueryFilterInterceptor>());
Tipp
Da
IFetchXmlQueryFilterInterceptorerweitertITablePermissionHandler, übernimmt eine einzelne Klasse sowohl Berechtigungen auf Tabellenebene als auch Abfragefilterung. Die MethodeOnQueryAsyncwird nur für Abfragen aufgerufen, die mit derTableEigenschaft des Interceptors übereinstimmen, sodass der Tabellenname innerhalb der Methode nicht überprüft werden muss.
Eingebaute Basisklassen
Das Framework stellt Basisklassen bereit, um gängige Sicherheitsmuster zu vereinfachen:
AnonymousPermissionHandler— Gewährt alle Operationen allen Nutzern (einschließlich Anonymen). Nützlich für öffentlich zugängliche Tabellen. Einfach erben und setzen Sie die EigenschaftTable.
API-Referenz
ITablePermissionHandler Interface
Eigenschaften
Name | Typ | Default | Beschreibung |
|---|---|---|---|
Table | string | Logischer Name der Tabelle, auf die dieser Berechtigungshandler angewendet wird. |
TableMethoden
Name | Parameter | Typ | Beschreibung |
|---|---|---|---|
CanAppendAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz anhängen kann. |
CanAppendToAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer in der Lage sein sollte, einen Datensatz anzuhängen. |
CanCreateAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer in der Lage sein sollte, einen Datensatz zu erstellen. |
CanDeleteAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz löschen kann. |
CanReadAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz lesen kann. |
CanUpdateAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz aktualisieren kann. |
CanAppendAsyncCanAppendToAsyncCanCreateAsyncCanDeleteAsyncCanReadAsyncCanUpdateAsyncITableRecordPermissionHandler Interface
Eigenschaften
Name | Typ | Default | Beschreibung |
|---|---|---|---|
RequiredColumns | List<string> | Liste von logischen Spaltennamen, die für diesen Handler abgerufen werden müssen, um Datensatzberechtigungen zu bewerten. | |
Table | string | Logischer Name der Tabelle, auf die dieser Berechtigungshandler angewendet wird. |
RequiredColumnsTableMethoden
Name | Parameter | Typ | Beschreibung |
|---|---|---|---|
CanAppendAsync | Guid? userId TableRecord record | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer den bereitgestellten Datensatz anhängen kann. |
CanAppendToAsync | Guid? userId TableRecord record | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer in der Lage sein sollte, den bereitgestellten Datensatz anzuhängen. |
CanCreateAsync | Guid? userId TableRecord record | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer den bereitgestellten Datensatz erstellen kann. |
CanDeleteAsync | Guid? userId TableRecord record | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer den bereitgestellten Datensatz löschen kann. |
CanReadAsync | Guid? userId TableRecord record | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer den bereitgestellten Datensatz lesen kann. |
CanUpdateAsync | Guid? userId TableRecord recordWithUpdates TableRecord currentRecord | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer den bereitgestellten Datensatz aktualisieren kann. |
CanAppendAsyncTableRecord record
CanAppendToAsyncTableRecord record
CanCreateAsyncTableRecord record
CanDeleteAsyncTableRecord record
CanReadAsyncTableRecord record
CanUpdateAsyncTableRecord recordWithUpdates
TableRecord currentRecord
IFetchXmlQueryFilterInterceptor Interface
Eigenschaften
Name | Typ | Default | Beschreibung |
|---|---|---|---|
Table | string | Logischer Name der Tabelle, auf die dieser Berechtigungshandler angewendet wird. |
TableMethoden
Name | Parameter | Typ | Beschreibung |
|---|---|---|---|
CanAppendAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz anhängen kann. |
CanAppendToAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer in der Lage sein sollte, einen Datensatz anzuhängen. |
CanCreateAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer in der Lage sein sollte, einen Datensatz zu erstellen. |
CanDeleteAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz löschen kann. |
CanReadAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz lesen kann. |
CanUpdateAsync | Guid? userId | Task<bool> | Methode, um zu bestimmen, ob ein Benutzer einen Datensatz aktualisieren kann. |
OnQueryAsync | FetchXMLBuilder fetchXmlBuilder FetchXmlParameters parameters | Task<FetchXMLBuilder> | Aufruft, um die Änderung einer FetchXML-Abfrage vor deren Ausführung zu ermöglichen. |
CanAppendAsyncCanAppendToAsyncCanCreateAsyncCanDeleteAsyncCanReadAsyncCanUpdateAsyncOnQueryAsyncFetchXmlParameters parameters
