Sécurité des tables

Power Portals Pro sécurise les données à trois niveaux : quelles tables un utilisateur peut toucher, quelles colonnes de ces tables il peut voir et modifier, et quelles lignes reviennent du tout. Les trois sont décidées lors d’un seul enregistrement par tableau. Décrivez cette inscription avec AddTableAccess — c’est la manière d’accorder une table, et ce que les modèles de projet utilisent pour chaque table qu’ils enregistrent. Là où une règle est indescriptible, les mêmes trois réponses peuvent être écrites comme une ITablePermissionProvider classe ; toutes deux produisent la même chose et composent de la même manière. Rien n’est accordé tant que l’une d’elles ne le dit pas : une table couverte par aucun enregistrement est inaccessible, ce qui est le défaut de faille-fermeture.

Conseil

Vous n’êtes pas obligé d’écrire cela à partir d’une page blanche. Les projets générés livrent une secure-a-table compétence : demandez à votre agent de codage de sécuriser une table et il passe par la table, à qui elle s’adresse, à quelles lignes, quelles opérations et quelles colonnes, puis écrit l’enregistrement et effectue les modifications correspondantes dans Dataverse via le serveur Power Portals Pro MCP — créant le rôle du portail si l’accès est lié au rôle, et accordant à l’utilisateur de l’application les privilèges que les réponses impliquent. Ensuite, check_setup signalez toute table à laquelle vos pages sont liées et à laquelle rien n’accorde d’accès, ainsi que toute inscription qui se retire de la sécurité des colonnes. Voir Démarrer → serveur MCP.

Tout le monde, ou seulement quelques personnes

Chaque enregistrement indique à qui il s’adresse, et il y a deux réponses. Enregistrez-le auprès du portail et il applique à un public vos noms de code — tout le monde, ou chaque utilisateur connecté. Enregistrez-le à l’intérieur AddPortalRole et cela s’applique aux titulaires de ce rôle, ce qui est une décision de données prise par un administrateur dans Dataverse. L’accès lui-même est décrit de la même manière dans les deux cas.

Enregistrement S’applique à Pour changer qui l’obtient
AddTableAccess("account", t => t.ForSignedInUsers()…) tous ceux que le public couvre, à chaque demande Modifier le code et redéployer
AddPortalRole("Credit Manager", role => role.WithTableAccess("account", …)) des contacts occupant ce rôle de Portail, et personne d’autre assigner ou désassigner le rôle dans Dataverse — pas de déploiement

Un tableau peut être enregistré une fois pour chaque audience à l’échelle du portail — ForEveryone() pour ce que chacun peut atteindre, ForSignedInUsers() pour ce que la connexion apporte — et les deux s’additionnent. Un visiteur obtient le premier ; un utilisateur connecté obtient les deux, chaque opération étant confinée aux rangées de l’enregistrement qui l’accorde. Avec les deux inscriptions ci-dessous, tout le monde lit chaque article, et un auteur connecté peut aussi en écrire un et éditer le sien :

Les rôles s’additionnent ; ils ne soustraient jamais. L’accès d’un utilisateur correspond aux inscriptions à l’échelle du portail plus chaque rôle qu’il occupe, syndiqué — donc celui à l’échelle du portail est le plancher sur lequel tout le monde se tient, et un rôle ne peut que l’augmenter. Un rôle ne peut pas reprendre une colonne ou une ligne accordée par l’enregistrement à l’échelle du portail ; pour retenir quelque chose à la plupart des gens, l’accorder dans un rôle plutôt que dans tout le portail. (Réglé SecurityOptions.Roles.CompositionBehavior à Restrictive si vous avez besoin que chaque groupe soit d’accord à la place.)

Donc : si chaque utilisateur de ce type devait avoir cet accès, enregistrez-le auprès du portail. Si seulement certains le devraient — et quelle personne est une question dont la réponse change sans autorisation — le mettront dans un rôle. Une inscription à un rôle n’accorde rien tant que quelqu’un ne crée pas un enregistrement de rôle de portail avec ce nom et ne l’attribue, ce qui explique pourquoi on peut s’asseoir dans un projet frais sans danger.

Description de l’accès d’une table

La plupart des tables veulent les mêmes quelques règles : pour qui est la table, quelles lignes ils atteignent, ce qu’ils peuvent faire, et quelles colonnes ils voient. AddTableAccess dit les quatre en un seul registre et produit un ordinaire ITablePermissionProvider — rien en aval ne fait la différence. Ce qu’il achète, c’est que les trois décisions qui doivent s’accorder (le filtre, la création et les colonnes écrivables) sont prises une fois au lieu de trois.

Un public, une ligne de contrôle et Allow(…) tous sont obligatoires. Aucun d’eux n’a de défaut, car chaque défaut plausible est un moyen de publier des données en omettant quelque chose — et un enregistrement qui se trompe échoue au démarrage plutôt qu’en production.

Lunettes à rangées

Le scope filtre chaque requête pour la table — grilles, recherches, sous-grilles, graphiques, et la récupération avant une sauvegarde ou une suppression. Les longerons nommés couvrent les formes dont les portails ont besoin constamment :

Quelles rangées Portée Sur créer
Chaque rangée AllRows() rien n’est fixé
Les propres enregistrements de l’utilisateur RowsOwnedByContact("customerid") la recherche est assignée à eux
Les dossiers de leurs collègues, au même compte RowsOfTheirAccount("customerid") La recherche est configurée sur leur compte
Des dossiers suspendus à un parent qu’ils peuvent atteindre RowsViaParent("salesorderid", "salesorder", p => …) Rien n’est fixé — c’est l’accès du parent qui décide
Des archives qui les pointent de plus d’un point RowsMatchingAny(r => r.RowsOwnedByContact("customerid"), r => r.RowsOfTheirAccount("customerid")) Ce que la première portée de propriété listée définit
Leur propre conflit de contacts OwnRecordOnly() Pas de création

Les champs de propriété définissent eux-mêmes la colonne lors de la création d’un enregistrement, après les vérifications des permissions : l’utilisateur n’a pas besoin d’accès en écriture ni d’Append To sur la table vers laquelle il pointe, la valeur provient de la session plutôt que de la requête, et une sauvegarde ultérieure qui la modifierait est refusée. C’est ce qui maintient un enregistrement créé par quelqu’un dans le filtre auquel il est soumis. Utilisez OwnedBy('column') quand la colonne propriétaire n’est pas celle qui filtre les lignes, et WithoutStamping() quand un plugin la définit déjà.

Quand la lecture et le changement diffèrent

One scope répond à chaque opération sauf indication contraire. RowsForWriting Réduit les lignes qu’un utilisateur peut modifier sans réduire celles qu’il peut voir — « chacun lit chaque contact, chacun n’édite que le sien » est cette distinction et rien d’autre — et RowsForDeleting réduit la destruction encore plus, car être autorisé à corriger un enregistrement n’est pas la même chose que pouvoir le détruire. Chacun revient à celui au-dessus, donc un enregistrement qui ne fait aucune distinction ne fait aucune distinction. Les créations ne sont pas affectées : il n’y a pas encore de ligne à filtrer.

Quand les lunettes nommées ne s’ajustent pas

RowsMatching Prend un filtre à vous sans quitter l’enregistrement. Donnez-lui FetchXML — un <filter> élément ou un tout <fetch> dont les filtres et jointures sont copiés, avec {currentUserId} pour le contact connecté — ou un rappel qui s’exécute par requête et renvoie soit le FetchXML, composé à partir de la réponse de vos services, soit un filtre qu’il a construit. Les deux reçoivent la même TableQueryContext chose qu’un fournisseur, ils savent donc qui demande et si les lignes vont être lues, mises à jour ou supprimées. Ne rien renvoyer ne donne aucune ligne : TableQueryFilter.AllRows c’est ainsi que vous dites chaque ligne.

Description de l’accès d’un rôle

À l’intérieur AddPortalRole du rôle se trouve le public, donc l’inscription ne nomme aucun autre. Les rôles ajoutent et ne soustrait jamais : l’accès d’un utilisateur correspond aux inscriptions à l’échelle du portail plus chaque rôle qu’il occupe, syndiqué. Les rôles du portail eux-mêmes — ce qu’ils sont, comment ils sont attribués, et comment ils se composent — sont la section suivante.

Note

Une classe de fournisseur reste le moyen d’exprimer tout ce qu’une description ne peut pas, et elle compose de manière identique — AddTablePermissionProvider<T> et WithPermissions<T> reste inchangée. Les projets générés n’en livrent plus un : chaque table qu’ils enregistrent est décrite, y compris contact et le rôle de Gestionnaire de crédit.

Rôles du portail

Un rôle de portail est la méthode préférée pour sécuriser un portail. Il lie les fournisseurs de permissions à un nom que les administrateurs attribuent aux contacts dans Dataverse, de sorte que les fournisseurs ne s’appliquent qu’aux personnes occupant ce rôle — et qui le détient sont des données, pas du code. Un fournisseur enregistré directement Program.cs s’applique à chaque utilisateur en comparaison.

La division est délibérée. Dataverse possède les noms et les attributions, que l’administrateur gère dans l’application pilotée par modèle. Votre code possède ce que chaque nom signifie. Accorder ou révoquer l’accès de quelqu’un est alors un changement de données : pas de reconstruction, pas de redéploiement.

1. Créer le rôle et l’attribuer

Créez un enregistrement de rôle Portal et attribuez-le aux contacts qui devraient l’avoir. L’enregistrement de rôle ne contient qu’un nom et une description — aucune donnée d’autorisation ne réside dans Dataverse.

2. Lier le rôle à ses prestataires

Faites correspondre au nom du dossier de poste et indiquez ce qu’il accorde. Les types de prestataires sont enregistrés pour vous, donc vous ne les enregistrez pas vous-même.

Ce dernier point est important : ne jamais enregistrer aussi le fournisseur d’un rôle avec AddTablePermissionProvider<T>(). Cela le place dans l’ensemble global et l’accorde à tout le monde, annulant silencieusement le rôle. Réservez AddTablePermissionProvider<T>() les règles qui s’appliquent réellement à chaque utilisateur.

Comment les rôles se combinent

Les permissions d’un utilisateur proviennent des fournisseurs enregistrés mondialement ainsi que de chaque rôle qu’il occupe, et ces groupes se rejoignent ainsi :

Conseil

Sous composition additive , rien ne refuse — quelque chose reste fermé uniquement pendant qu’aucun groupe ne l’ouvre. Un fournisseur enregistré mondialement est le bon foyer pour une règle qui s’applique à chaque utilisateur, car elle est toujours dans le groupe, mais ce n’est pas un veto : un rôle qui accorde l’opération, ou nomme la colonne, la rouvre. Pour garder quelque chose fermé, quels que soient les rôles qu’un utilisateur occupe, ne l’accordez aucun rôle — ou fixez-le SecurityOptions.Roles.CompositionBehavior à Restrictive, où chaque groupe doit être d’accord.

Rôles dans les pages et la navigation

Les noms de rôles atteignent le navigateur en tant que revendications de rôle ASP.NET standard, donc l’autorisation de page et l’interface utilisateur conditionnelle fonctionnent sans rien de plus :

La pile React lit les mêmes noms depuis auth.user.roles, et <AuthorizedView roles="Dealer"> porte le contenu de la même manière. Les connexions internes (utilisateurs système) portent ici leurs noms de rôles de sécurité Dataverse, donc une seule vérification couvre les deux types d’utilisateurs.

Autoriser sur le bon type de poste

Un rôle de portail et un rôle de sécurité Dataverse peuvent partager un nom, et [Authorize(Roles = "…")] ne peuvent pas les différencier — il correspond à la revendication de rôle non typé, qui porte les deux. Deux attributs posent la question plus étroite :

Les noms des rôles vont mot pour mot : virgules, deux-points et guillemets sont tous corrects, car le nom n’est jamais extrait d’une liste. Empiler deux attributs nécessite les deux, comme les attributs empilés [Authorize] l’ont toujours fait ; lister deux noms dans un seul attribut nécessite l’un ou l’autre. La même vérification est disponible dans le code sous le nom User.IsInPortalRole("Dealer") et User.IsInSecurityRole("Sales Manager").

La même distinction dans le balisage Blazor. <AuthorizeView Roles="…"> lit la revendication de rôle non typé et porte donc la même ambiguïté que l’attribut plain ; <PortalRoleView> demande un type. Donnez-lui PortalRole / SecurityRole pour un seul nom, ou PortalRoles / SecurityRoles pour plusieurs — une collection plutôt qu’une chaîne séparée par des virgules, car les noms Dataverse contiennent régulièrement des virgules :

Celui de <AuthorizedView> Réaction prend les mêmes deux portes, en plus de l’existante roles . Fournir plusieurs noms dans une porte signifie n’importe lequel d’entre eux ; fournir deux portes signifie les deux. Les prédicats derrière eux — hasPortalRole, hasSecurityRole, hasRole — sont exportés pour des contrôles impératifs :

Bonne pratique — filtrer à la source

Utilisez ces fonctions chaque fois que la protection est réellement administrative. Créer un rôle de portail est une barre délibérément plus basse que de se voir attribuer un rôle de sécurité Dataverse, donc un chèque qui accepte permet à un administrateur de délivrer l’accès que le rôle Dataverse protégeait simplement en nommant un rôle de portail d’après celui-ci.

Note

Les rôles ne sont pas gratuits. Chacun qu’un utilisateur détient est écrit deux fois dans le cookie d’authentification — une fois comme revendication de rôle standard, donc [Authorize(Roles)] fonctionne, et une fois comme revendication spécifique au type qui rend possibles les vérifications ci-dessus — ce qui mesure environ 160 octets par rôle. Environ quinze rôles par utilisateur peuvent encore contenir un seul cookie ; au-delà de cela, le cookie se divise en morceaux, et la première chose à céder est généralement la limite d’en-tête d’un proxy inverse, qui rapporte un 400 opaque plutôt que de mentionner des rôles. Préfère quelques rôles généraux plutôt que de nombreux rôles étroits.

Donner aux nouveaux utilisateurs un rôle de départ

Nommer les rôles que chaque nouveau compte devrait avoir au départ, ainsi que le rôle applicable aux visiteurs déconnectés :

Ou décidez-le dans Dataverse, sans publication. La table de configuration des rôles du portail associe un rôle à un mode d’attribution, et un administrateur peut changer qui reçoit quoi via l’application pilotée par modèle :

Lorsque les rôles dépendent de la personne — son domaine email, le fournisseur avec lequel elle s’est connecté, une invitation — inscrivez-vous et IPortalRoleAssignmentProvider à la place. Chaque fournisseur enregistré est consulté, donc le vôtre s’ajoute à la liste configurée plutôt que de la remplacer. Dans tous les cas, l’attribution s’exécute une fois, lors de la création du compte ; elle n’est jamais réappliquée lors de la connexion, donc un rôle qu’un administrateur supprime reste supprimé.

Note

Un utilisateur récupère un nouveau rôle attribué en se déconnectant puis en se reconnectant — se connecter relit ses rôles. Les attributions de rôles sont autrement mises en cache par utilisateur pendant 24 heures, donc pour appliquer un changement à quelqu’un déjà connecté, il faut vider le cache des rôles du portail depuis la zone d’administration du site : sa session le récupère lors de la prochaine rafraîchissement en dix minutes, et un rôle supprimé cesse de fonctionner alors qu’un rôle ajouté commence. La table de configuration des rôles du portail est mise en cache séparément pendant quinze minutes, donc une ligne ajoutée à cet endroit atteint la prochaine inscription — videz le cache « PortalRoleConfiguration » pour le rendre immédiat. Désactiver une ligne de configuration empêche la distribution de son rôle, de la même manière que désactiver un rôle le révoque.

Écrire plutôt un prestataire

Choisissez une classe lorsqu’une description ne peut pas dire ce que vous voulez dire — une règle qui dépend de quelque chose que seul votre code connaît, ou une règle qui doit inspecter l’enregistrement en cours. ITablePermissionProvider C’est ce que chaque enregistrement devient dans tous les cas : un fournisseur gouverne une table, répond à ce qu’un utilisateur peut en faire, à quelles colonnes il peut lire et écrire, et — via le filtre de requête ci-dessous — quelles lignes il peut voir. Une inscription décrite et une écrite composent de la même maman, donc un portail peut utiliser les deux, et une table peut être accordée par l’une et élargie par l’autre.

L’interface comporte un membre obligatoire. GetPermissionsAsync reçoit un UserInfo contenant l’identifiant de l’utilisateur, ses rôles de portail et leurs rôles de sécurité Dataverse, et retourne un TablePermissions document décrivant tout ce que cet utilisateur reçoit :

Enregistrement d’un prestataire

Un fournisseur enregistré de cette manière s’applique à tous les utilisateurs, connectés ou non :

C’est aussi la seule façon de retirer quelque chose à tout le monde : les rôles se composent de manière additive par défaut, donc un rôle peut ajouter un accès sans jamais le supprimer. Les restrictions à l’échelle du portail appartiennent à un fournisseur enregistré globalement, qui fait toujours partie du groupe.

Opérations

TablePermissions.Operations est une TableSecurityPermission valeur de drapeaux — Read, Create, Write, Delete, et Append AppendTo. Deux choses méritent d’être connues à son sujet :

Note

Il n’y a délibérément pas de crochet « inspecter chaque ligne après son chargement ». Filtrer ainsi casse la pagination — une page de 50 en donne moins — rend le total des comptes erronés, et paie Dataverse pour récupérer les lignes qu’il jette puis jette. L’extension de la requête permet plutôt de faire en sorte que la pagination et les comptes soient corrects par construction.

Bonne pratique — filtrer à la source

Étends les données dans la requête, pas après. Le framework n’offre délibérément aucun hook qui inspecte chaque ligne une fois qu’elle a été récupérée : filtrer ainsi casse la pagination (il manque une page de 50), rend le compte total erroné, et paie Dataverse pour récupérer les lignes qu’il élimine puis rejette. GetQueryFilterAsync Ajoute les conditions avant l’exécution de la requête, donc le pageage et les comptages restent corrects par construction et les lignes qu’un utilisateur ne voit pas ne sont jamais récupérées.

Sécurité au niveau des lignes

GetQueryFilterAsync renvoie un filtre que le framework ajoute à la requête avant que Dataverse ne l’exécute, de sorte que les lignes que l’utilisateur peut ne pas voir ne sont jamais récupérées. Il s’applique à chaque requête que le portail émet pour la table — grilles, recherches, sous-grilles, graphiques et chargements à enregistrement unique — et il s’exécute sous les deux modèles de sécurité, ce qui le reste en vigueur même lorsque la sécurité native Dataverse contourne la porte d’opération.

Construisez le filtre avec context.CreateFilter() et retournez-le ; retourner null signifie pas de restriction de ligne. Le contexte porte l’utilisateur actuel comme EntityReference, les rôles qu’il détient, et à quoi servent les lignes. Les conditions sont ajoutées via la même FetchXMLBuilder API utilisée partout ailleurs : FetchXMLBuilder

Ce fournisseur accorde trois colonnes de contact et relie chaque requête de contact à l’enregistrement propre de l’utilisateur connecté :

Lecture, mise à jour et suppression

TableQueryContext.Operation Explique pourquoi les rangées sont récupérées, afin qu’un prestataire puisse les définir différemment pour chacune :

Comment plusieurs filtres se combinent

Une table peut avoir plus d’un fournisseur — un enregistré mondialement, plus un de chaque rôle occupé par l’utilisateur. Le cadre décide comment leurs filtres se combinent, de sorte qu’un fournisseur n’a jamais à savoir ce que les autres ont fait :

Note

Les lignes échouent à se fermer, comme les colonnes : un fournisseur qui retourne null — ou un filtre sans conditions — n’accorde aucune ligne, et au sein d’un groupe qui vide le groupe. Chaque ligne doit être demandée avec TableQueryFilter.AllRows. C’est là que réside la seule asymétrie : l’union de chaque ligne avec n’importe quel est chaque ligne, donc si un groupe qui accorde la table répond AllRows, la table revient sans filtre, aussi soigneusement que les autres rôles l’aient soigneusement délimitée. C’est ce que signifie la composition additive, mais c’est la seule façon dont le filtre d’un rôle peut sembler être ignoré.

Filtrage via une table associée

AddLink rejoint une table pour que le filtre puisse tester ses colonnes. Placez le test dans le filtre plutôt que sur le lien — les conditions à l’intérieur du filtre continuent de fonctionner lorsque le cadre assouplit la jointure vers l’extérieur, et les conditions sur le lien ne fonctionneraient pas :

Note

La jointure ci-dessus s’étend à plusieurs lignes, donc Dataverse rend le compte une fois par adhésion — une grille répétant les lignes et un total qui les compte. Le framework reconnaît cette forme et marque la requête distinct, projetant la clé primaire de la table afin que la déduplication ne puisse supprimer que les lignes ajoutées par la jointure. Une jointure qui s’étend vers le haut , rejoignant la clé primaire de la table concernée, correspond au maximum à une ligne et reste exactement comme avant. Dans tous les cas, vous écrivez le filtre et rien d’autre — mais il est utile de savoir qu’une jointure n’est pas gratuite, et qu’une règle que vous pouvez exprimer comme des conditions plates sur les colonnes propres à la table est la forme moins chère.

Conseil

Quand une table nécessite un champ d’application différent par rôle, vous pouvez toujours écrire un fournisseur et brancher — context.User.PortalRoles mais vous n’êtes plus obligé. Deux rôles peuvent chacun porter leur propre fournisseur et le framework réunionne ses filtres pour vous.

Qui possède un nouveau disque

Un filtre de lignes permet de lire les lectures, de mettre à jour et de supprimer, mais un mode de création n’a pas de ligne à filtrer. Ainsi, un tableau avec la portée de « lignes que je possède » permettrait toujours à un utilisateur de créer une ligne appartenant à quelqu’un d’autre — ou à personne, que son créateur ne peut alors pas voir. Déclarez la colonne qui indique qui possède un enregistrement, et le cadre le définit :

Associez-le à un filtre de lignes sur la même colonne, de sorte que les enregistrements créés par un utilisateur soient ceux qu’il peut voir. Pour une propriété qui n’est pas simplement le contact créateur — par exemple le compte du créateur — implémentez-le ITableRecordOwnershipProvider et enregistrez-le avec AddTableRecordOwnershipProvider<T>(). Ne définissez pas un propriétaire dans un ITableRecordInterceptor instead : les intercepteurs s’exécutent avant les vérifications des permissions, donc ce qu’ils définissent est jugé comme l’écriture propre de l’utilisateur.

Le framework l’appelle à chaque création de cette table, après les vérifications des permissions, et définit chaque colonne Columns dans ce qu’elle retourne — en rejetant tout ce que la requête envoyait, et en refusant une mise à jour ultérieure qui le modifierait, exactement comme pour OwnedBy. Associez-le à un filtre de ligne correspondant à ce qu’il définit, de sorte que les personnes sous lesquelles il enregistre un enregistrement soient celles qui peuvent le voir — ici RowsMatchingAny(r => r.RowsOfTheirAccount("customerid"), r => r.RowsOwnedByContact("customerid")), avec WithoutStamping() donc l’enregistrement laisse la colonne à ce fournisseur : deux qui définissent une colonne différemment échouent à la sauvegarde.

Sécurité au niveau de la colonne

La sécurité des colonnes est un consentement volontaire. Un fournisseur accorde les colonnes qu’il veut exposer, et tout le reste — y compris une colonne ajoutée à la table l’année suivante — reste retenu jusqu’à ce que ce soit nommé. L’identifiant principal du dossier est toujours lisible, car un enregistrement que le framework ne peut identifier n’est utile à personne.

Les lectures et écritures sont des ensembles indépendants : une colonne peut être lisible mais pas écrivable (le cas « afficher la limite de crédits, ne pas les laisser la changer »), ou être écrite mais non lisible. Nommez-les avec Reading, Writing et ReadingAndWriting, ou désinscrivez-vous entièrement d’une dimension avec ReadingAllColumns / WritingAllColumns:

Note

ReadingAllColumns() est délibérément quelque chose que vous devez taper. Il ne s’ouvre pas pour tout ce qui est ajouté à la table ultérieurement, ce qui n’est le bon échange que lorsque la table est réellement publique — donc le cadre ne l’assumera pas en votre nom.

L’écriture peut aussi différer entre créer un enregistrement et en modifier un. Writing couvre les deux. WritingOnCreate permet à l’utilisateur de définir une colonne lors de la création de l’enregistrement et de le rendre en lecture seule à partir de là — la priorité d’un cas, choisie lors de son enregistrement. WritingOnUpdate est l’inverse : modifiable sur un enregistrement existant, non réglable au début. Les deux rendent la colonne lisible aussi, comme Writing, et les deux existent sur TablePermissions pour un fournisseur à vous. Ils imitent les permissions distinctes de création et de mise à jour accordées par la sécurité de champ de Dataverse :

Le cadre impose le résultat à chaque chemin que les données franchissent la frontière du serveur :

Note

La colonne principale d’identification d’une table n’est jamais retenue, quoi que dise l’ensemble de lecture. L’id est l’identité plutôt que les données — il est déjà dans l’URL utilisée par l’appelant pour accéder à l’enregistrement — et les clés lignes, les liens d’édition, les relations de sous-grille et la suppression se défont sans cela. Une liste d’autorisation qui nomme les colonnes dont une page a besoin et oublie l’id démonterait autrement le portail sans rien protéger.

Comment se comportent les composants

Les composants du framework respectent les autorisations de colonne par eux-mêmes — vous ne câblez rien. Une colonne que l’utilisateur ne peut pas écrire rend en lecture seule et désactivée partout où elle apparaît : chaque éditeur la récupère via sa résolution partagée en lecture seule, et la grille ne donne pas d’éditeur en ligne à la cellule, donc elle revient au texte d’affichage brut. Une colonne fournie readOnly="false" par le consommateur ne la remplace pas — le serveur rejetterait la sauvegarde de toute façon.

Une colonne que l’utilisateur ne peut pas lire — une colonne masquée — est rendue à travers <ColumnDisplayValue> la colonne au lieu d’un éditeur, dans les deux piles. Ce n’est pas une simple question : une colonne masquée n’a aucune valeur, seulement le masque comme valeur formatée , donc un éditeur lié à elle se rendrait vide ; ColumnDisplayValue lit la valeur formatée et affiche donc le placeholder, quel que soit le type de colonne. Les colonnes retenues par omission n’atteignent jamais un composant — elles ne sont pas enregistrées dans l’enregistrement et les métadonnées du tableau ne les listent pas.

Ce que reçoit le client

Les permissions de colonne viennent sous forme de ColumnPermissions carte — nom logique de colonne vers une ColumnSecurityPermission paire de drapeaux — pour les colonnes dont l’accès est restreint, ce Write qui signifie que la colonne peut être modifiée sur un enregistrement existant. Tout ce qui est absent a un accès complet, donc une table non restreinte ne contient rien du tout. Un enregistrement récupéré seul porte la carte ; les enregistrements récupérés ensemble partagent une carte, envoyée une fois sur la réponse, puisque les permissions appartiennent à l’utilisateur et à la table plutôt qu’à une ligne — les clients des deux piles la remettent sur chaque enregistrement, donc lire les permissions d’un enregistrement fonctionne de la même manière dans les deux cas. Les réponses en grille les rapportent également par colonne sur ResolvedColumn.Permissions. Un enregistrement en création n’a pas encore de carte propre, donc le point de terminaison des permissions de table le retourne NewRecordColumnPermissions aussi — la même forme, jugée par ce qui peut être défini lors de la création — et les deux piles RecordContext le placent sur un nouvel enregistrement pour vous. Les colonnes retenues par omission sont délibérément absentes de chaque carte — l’omission existe donc rien dans la réponse ne révèle la colonne, et la nommer là annulerait cela. Lisez-la vous-même à travers record.CanReadColumn(column) / record.CanWriteColumn(column) dans Blazor, ou les correspondances canReadColumn / canWriteColumn aides dans @powerportalspro/core React.

Omission ou masquage

Par défaut, une colonne retenue est masquée : l’enregistrement ne porte aucune valeur pour elle, et SecurityOptions.WithheldColumnMask (***** par défaut) comme sa valeur formatée, donc un formulaire ou une grille affiche toujours le champ et indique clairement que la valeur est retenue plutôt que vide. Le masque est le même que la colonne ait une valeur ou non, donc il la cache aussi. Réglez SecurityOptions.WithheldColumnBehavior sur Omit pour supprimer complètement la colonne de l’enregistrement, comme si la requête ne l’avait jamais demandée, donc rien dans la réponse ne révèle que la colonne existe — le bon choix lorsque l’existence même de la colonne est sensible. Retenir une colonne retient également toute colonne qui la republie — un compagnon de nom de recherche, le jumeau de la monnaie de base d’une colonne d’argent — donc une liste de refus nommant une colonne ne peut pas être contournée par une autre. Un seul tableau peut passer outre le choix à l’échelle du site via TableColumnPermissions.WithheldColumnBehavior.

Note

Associez une colonne masquée à un déni d’écriture. Une colonne masquée n’a aucune valeur sur l’enregistrement, donc un appelant qui renvoie un enregistrement chargé complet ne renvoie rien pour celui-ci — mais le framework prend une charge utile de sauvegarde à la valeur nominale, donc une requête qui définit la colonne, y compris le masque, écrit au-dessus de la valeur stockée si la colonne est écrivable. Le refus d’écriture est ce qui le refuse.

Chroniques qui republient une autre chronique

Retenir une colonne retient également toute colonne portant la même valeur sous un second nom. Dataverse crée pour vous : une recherche …name et …yominame des partenaires, une étiquette de choix, et un jumeau de monnaie de base pour chaque colonne de change — creditlimit_base à côté creditlimitde . Ce sont des colonnes ordinaires, donc une dénomination creditlimit par liste de refus ne dit rien sur creditlimit_base, et sans cela, un montant masqué reviendrait directement par son jumeau. Le lien provient des métadonnées propres de la plateforme plutôt que d’une règle de nommage, donc il s’applique aussi à vos propres colonnes, et s’applique aux lectures, à une requête nommant explicitement le compagnon, dans les métadonnées de table, dans les colonnes de grille et sur les sauvegardes.

Bonne pratique — filtrer à la source

Un cas que le framework ne peut pas voir pour vous : une colonne calculée ou regroupante, et les colonnes composites de la plateforme — fullname, yomifullname, address1_composite et leurs sœurs. Dataverse ne rapporte pas quelles colonnes les alimentent, donc la retenue lastname ne retient pas fullname, et une colonne calculée sur une colonne que vous avez refusée affichera toujours son résultat. Nommez-les vous-même dans le jeu de lecture lorsque leurs entrées sont sensibles. Une liste de permis (ColumnPermissions.Allow(…)) évite complètement le problème, puisque rien n’est lisible tant que vous ne l’avez pas nommé — ce qui est le meilleur choix par défaut pour une table contenant tout ce qui mérite de retenir.

Note

Lorsque plusieurs fournisseurs couvrent une même table, leurs ensembles de colonnes se composent de la même manière que leurs opérations : de manière additive entre les rôles, et sous MultiplePermissionHandlersBehavior le même groupe. Un fournisseur qui existe uniquement pour définir les lignes et ne dit rien sur les colonnes est complètement exclu du pliage de colonnes, ce qui ne permet ni d’élargir ni de restreindre ce que les autres ont accordé.

Dataverse-Native Security (Aperçu)

Le modèle de sécurité recommandé pour la suite permet à Dataverse lui-même d’appliquer la sécurité pour vos utilisateurs de portails, basé sur la fonctionnalité de sécurité unifiée de Power Pages . Au lieu d’écrire des gestionnaires de permissions dans le code, vous gérez l’accès avec des rôles web Power Pages et des permissions de table, que la plateforme transforme en rôles de sécurité Dataverse que Dataverse applique à chaque requête. Le modèle est actuellement en version preview — à la fois la fonctionnalité de Microsoft et le support de Power Portals Pro — et Power Portals Pro suivra le calendrier de sortie de Microsoft jusqu’à la disponibilité générale.

Une fois activé, PowerPortalsPro fournit automatiquement un système géré (C2) systemuser pour chaque contact portail — à l’enregistrement pour les nouveaux contacts, et paresseusement à la connexion pour les existants — ainsi que l’enregistrement powerpagesusermapping qui relie le contact, l’utilisateur système et votre site Power Pages. Chaque appel Dataverse pour la session se fait alors passer pour cet utilisateur système, de sorte que les rôles de sécurité que Power Pages synchronise à partir de vos rôles web et vos permissions de table régissent exactement ce que l’utilisateur peut voir et faire. La session comporte un PortalUserType de ContactStubUser afin que votre interface puisse toujours distinguer ces utilisateurs provisionnés des utilisateurs internes réels.

Note

Pour utiliser ce modèle de sécurité, vous devez créer un site web Power Pages dans votre environnement Dataverse — c’est ce qui installe les composants du cadre Microsoft et permet d’activer la fonctionnalité de sécurité unifiée. Vous n’avez pas besoin d’un forfait Power Pages payant, et vous pouvez supprimer le site web lui-même immédiatement après avoir installé et activé la fonctionnalité. L’enregistrement de site Power Pages (powerpagesite) référencé par PowerPagesSiteId doit exister dans l’environnement, car les mappages utilisateur et la synchronisation sécurité-rôle se résolvent contre celui-ci.

Activation de la sécurité Dataverse Native

Activez le modèle dans SecurityOptions le fichier de Program.csvotre serveur , en fournissant l’identifiant de votre site Power Pages :

La configuration est validée au démarrage de l’application : lorsqu’elle ProvisionSystemUsers est activée, PowerPagesSiteId elle doit être définie et doit se résoudre à un enregistrement existant powerpagesite , sinon l’application ne démarre pas avec des instructions.

Accordez l’accès exactement comme le fait Power Pages : configurez les rôles web et les permissions de table — dans l’application Power Pages Management, ou sur la page Web Roles de votre portail via la solution Power Portals Pro Enhanced Authorization — et attribuez des rôles web aux contacts (via l’application Management, de manière programmatique ou automatique pour les nouveaux utilisateurs avec des règles sur cette même page). La plateforme synchronise ces rôles en rôles de sécurité auto-générés (nommés PowerPages_*) et les assigne automatiquement en tant que changements d’appartenance aux rôles web — comme la configuration réside entièrement dans Power Pages, votre portail peut se déplacer vers ou depuis un site Power Pages réel sans aucune friction. Notez que les changements de rôles et de privilèges peuvent prendre de quelques secondes à quelques minutes pour se propager dans les caches de Dataverse avant de prendre effet.

Suivez le guide de configuration étape par étape

Associer une connexion externe à un contact

Lorsqu’une personne se connecte à Microsoft, Google ou à un autre fournisseur externe, le portail doit déterminer à quel contact existant — le cas échéant — cette connexion appartient. Cela résout cela en deux étapes :

  1. Un lien existant. Si le compte s’est déjà connecté, une adx_externalidentity ligne le relie déjà à un contact. C’est une déclaration explicite stockée « ce compte est cette personne », donc elle gagne toujours et n’est jamais affectée par les paramètres ci-dessous.
  2. Le plan de secours par e-mail. Sans encore de lien, le portail fait correspondre l’adresse e-mail fournie par le fournisseur avec les colonnes du contact. C’est l’étape que vous pouvez configurer — et celle qui décide si un client récurrent rejoint son contact actuel ou reçoit un double.

Par défaut, deux colonnes sont recherchées, nom d’utilisateur du portail en premier. Les contacts créés via le portail portent adx_identity_username, tandis que les contacts importés ou saisis par le personnel n’ont généralement que emailaddress1 — la recherche des deux permet à un client existant de se connecter et d’accéder à son propre dossier au lieu d’un nouveau :

L’ordre est important : c’est la préséance, pas seulement une liste. Les colonnes sont recherchées ensemble dans une seule requête, et la première colonne de votre liste correspondant à quelque chose décide du gagnant — donc si un contact détient l’adresse comme nom d’utilisateur du portail et qu’un autre la détient, emailaddress1la correspondance du nom d’utilisateur l’emporte dans l’ordre par défaut.

Listez moins de colonnes pour affiner : [ContactEmailColumn.IdentityUsername] cela reste aux comptes créés par portail, et une liste vide désactive complètement le repli par email, laissant un lien d’identité externe existant comme seule façon pour un compte de se résoudre à un contact. Ajoutez ContactEmailColumn.EmailAddress2 et ContactEmailColumn.EmailAddress3 élargissez — ils sont délibérément exclus du système par défaut, pour la raison ci-dessous.

Bonne pratique — filtrer à la source

Chaque colonne que vous listez est un moyen pour que celui qui contrôle une adresse e-mail soit reconnu comme le contact qui la détient. Listez uniquement les colonnes que votre processus de saisie de données considère comme appartenant au contact lui-même — si emailaddress2 c’est là que votre équipe enregistre l’adresse d’un assistant ou une boîte de réception partagée de l’entreprise, l’inscrire permet à celui qui lit cette boîte de réception de se connecter sous le nom de ce contact.

Bonne pratique — filtrer à la source

La correspondance fait de l’adresse e-mail un identifiant, pas seulement une étiquette — elle décide de quel contact existant devient une connexion. Ainsi, le portail ne saute l’email de confirmation que lorsque le fournisseur d’identité a effectivement vérifié cette adresse exacte. Si l’adresse a été saisie au-dessus de celle fournie par le fournisseur, ou si le fournisseur ne l’a jamais vérifiée, un lien de confirmation est envoyé et la connexion externe n’est pas associée au contact tant que ce lien n’est pas suivi.

Les connexions qui comptent comme vérifiées ne sont pas une propriété du fournisseur. Google vérifie chaque adresse qu’il affirme. Microsoft ne le fait pas : un compte personnel Microsoft a vérifié son adresse, mais un compte Entra professionnel ou scolaire fournit ce qui est dans l’attribut de mail l’annuaire, qu’un administrateur locataire peut définir à n’importe quelle valeur dans n’importe quel domaine — y compris un domaine que son locataire ne possède pas. Le portail lit plutôt le jeton du fournisseur : un compte personnel est vérifié, et un compte professionnel ou scolaire n’est vérifié que lorsque Entra garantit cette adresse exacte — sa verified_primary_email revendication verified_secondary_email ou son nom la liste, ou xms_edov indique que son domaine appartient au locataire propre de l’utilisateur. Entra n’envoie ces revendications que lorsque l’enregistrement de l’application les demande (voir Configuration de connexion Entra ID) ; sans elles, les connexions professionnelles et scolaires confirment par e-mail la première fois.

Note

Ces paramètres ne s’appliquent qu’à la connexion externe. La connexion, l’enregistrement et la réinitialisation du mot de passe locaux continuent de correspondre adx_identity_username de manière autonome, délibérément — le flux de réinitialisation du mot de passe envoie son lien à l’adresse saisie, donc correspondre aux colonnes d’email secondaires à cet endroit permettrait à quelqu’un de réinitialiser le mot de passe d’une autre personne.

Ne laisser se connecter que les contacts existants

Par défaut, n’importe qui peut devenir utilisateur du portail : s’inscrire ou se connecter avec un fournisseur externe sous une adresse que personne n’a crée un contact. Lorsque le portail est destiné aux personnes que vous détenez déjà dans Dataverse — clients, membres, partenaires — désactivez l’inscription, et elles seules peuvent se connecter :

La page d’inscription et ses liens disparaissent, et le serveur refuse de créer un utilisateur de la manière dont on le demande. Une connexion externe trouve toujours son contact comme décrit ci-dessus et y fait un lien ; un contact qui ne correspond à personne est refusé au lieu de créer un contact. Les comptes locaux ne sont pas affectés pour les personnes qui en ont déjà un.

Note

Un refus de connexion n’est expliqué à l’écran que lorsque le prestataire a vérifié cette adresse exacte, car alors la personne qui se connecte a déjà prouvé qu’elle en est propriétaire. Pour toute autre adresse, le portail répond exactement comme pour une adresse ayant un contact — « vérifier votre e-mail » — et envoie l’adresse par courrier avec un court préavis indiquant qu’elle n’est pas enregistrée. En informer directement le visiteur, n’importe qui devrait taper des adresses dans le formulaire pour savoir qui possède un compte.

L’avis est envoyé via l’expéditeur de l’email du portail. Un hôte qui le remplace par sa propre pile d’emails doit également enregistrer un ISignUpRefusalEmailSender; sans un d’eux, le refus est enregistré et aucun avis n’est envoyé.

Classes de base intégrées

Le cadre fournit des classes de base pour simplifier les schémas de sécurité courants :

Référence API

AuthorizePortalRoleAttribute Classe

Propriétés

Nom
Type
Par défaut
Description
AuthenticationSchemesstring?
Policystring?
RoleNamesIReadOnlyList<string>
Les noms de rôles que cet attribut requiert, chacun d’eux le satisfait.
Rolesstring?
TypeIdObject
Nom: AuthenticationSchemes
Type: string?
Nom: Policy
Type: string?
Nom: RoleNames
Type: IReadOnlyList<string>
Description: Les noms de rôles que cet attribut requiert, chacun d’eux le satisfait.
Nom: Roles
Type: string?
Nom: TypeId
Type: Object

Méthodes

Nom
Paramètres
Type
Description
IsDefaultAttributebool
MatchObject obj
bool
Nom: IsDefaultAttribute
Type: bool
Nom: Match
Paramètres: Object obj
Type: bool

ITablePermissionProvider Interface

Propriétés

Nom
Type
Par défaut
Description
Tablestring
Nom logique du tableau auquel ce fournisseur s’applique. Correspondu exactement, donc ça doit être le Nom logique du Dataverse en minuscules.
Nom: Table
Type: string
Description: Nom logique du tableau auquel ce fournisseur s’applique. Correspondu exactement, donc ça doit être le Nom logique du Dataverse en minuscules.

Méthodes

Nom
Paramètres
Type
Description
GetPermissionsAsyncUserInfo user
Task<TablePermissions>
Que user peut faire avec ce tableau, et quelles colonnes ils peuvent lire et écrire. Retour TablePermissions.None pour un utilisateur qui ne devrait pas avoir accès. Les opérations sont une porte au niveau de la table, et non un filtre de ligne : grant TableSecurityPermission.Read si l’utilisateur peut voir l’une des tables lignes et portée de laquelle dans Services.TableQueryContext). Les colonnes sont volontaires. Rien n’est lisible ou écrivable tant que ce n’est pas nommé — ce qui permet d’accorder TableSecurityPermission.Read À lui seul n’expose que l’identifiant du disque. Utilisation Reading(System.String[]) et Writing(System.String[]) de les nommer, ou TablePermissions.AllColumns pour se désinscrire de la sécurité de colonne pour ce tableau. Un fournisseur qui n’existe que pour définir les lignes peut laisser les colonnes intactes — sans parler à leur sujet et il ne se manifeste pas comme une opinion, ni élargissant ni rétrécissement l’autre Les fournisseurs sur la table sont accordés. Dire que rien reste une faille fermée au complet : quand non Le fournisseur nomme une colonne, aucune n’est lisible. Appelé une fois par table par utilisateur et mis en cache pour la requête, donc cela peut être aussi cher que ça doit l’être.
GetQueryFilterAsyncTableQueryContext context
Task<TableQueryFilter>
Étendues sur lesquelles l’utilisateur peut agir dans les lignes de cette table : un filtre construit avec Query.LogicalOperator), TableQueryFilter.AllRows pour tout Rower selon les opérations autorisées, ou nullsans lignes. Les lignes échouent à être fermées. Un fournisseur doit indiquer quelles lignes il accorde, de la même manière qu’il doit le faire Nommer les colonnes : null — ou un filtre sans conditions — n’accorde aucune, donc Oublier de définir un tableau la refuse plutôt que de publier chaque ligne. Disons TableQueryFilter.AllRows Quand chaque dispute est ce que tu veux dire. Le filtre est ajouté à la requête avant que Dataverse ne l’exécute, donc les lignes que l’utilisateur peut ne pas voir sont jamais récupérée. Elle s’applique à toutes les requêtes que le portail lance pour la table : grilles, recherches, sous-grilles, graphiques et chargements à enregistrement unique, plus la récupération pré-vol avant une mise à jour ou un supprimer — ce qui TableQueryContext.Operation distingue. Il s’exécute aussi sous les deux modèles de sécurité, il reste donc en vigueur lors de la sécurité native Dataverse contourne Services.UserInfo). C’est le framework qui décide comment les filtres se combinent, pas le fournisseur. Filtres des fournisseurs Dans le même groupe, s’intersectent — deux fournisseurs dans un même rôle sont censés s’entendre. Filtres de différents rôles s’unifient, donc occuper un autre rôle élargit ce que vous voyez, en correspondant à la façon dont le les opérations composent. Réglent PortalRoleOptions.CompositionBehavior à RoleCompositionBehavior.Restrictive pour croiser aussi plusieurs rôles. Au sein d’un groupe, un prestataire qui répond null ne laisse aucune ligne au groupe, et TableQueryFilter.AllRows laisse aux filtres des autres fournisseurs décider. De l’autre côté groupes, un groupe accordant TableQueryFilter.AllRows élargit l’additif par défaut Union à chaque ligne — donc le filtre d’un rôle ne signifie quelque chose que lorsque les autres groupes accordent Ce filtre de table aussi.
Nom: GetPermissionsAsync
Paramètres: UserInfo user
Type: Task<TablePermissions>
Description: Que user peut faire avec ce tableau, et quelles colonnes ils peuvent lire et écrire. Retour TablePermissions.None pour un utilisateur qui ne devrait pas avoir accès. Les opérations sont une porte au niveau de la table, et non un filtre de ligne : grant TableSecurityPermission.Read si l’utilisateur peut voir l’une des tables lignes et portée de laquelle dans Services.TableQueryContext). Les colonnes sont volontaires. Rien n’est lisible ou écrivable tant que ce n’est pas nommé — ce qui permet d’accorder TableSecurityPermission.Read À lui seul n’expose que l’identifiant du disque. Utilisation Reading(System.String[]) et Writing(System.String[]) de les nommer, ou TablePermissions.AllColumns pour se désinscrire de la sécurité de colonne pour ce tableau. Un fournisseur qui n’existe que pour définir les lignes peut laisser les colonnes intactes — sans parler à leur sujet et il ne se manifeste pas comme une opinion, ni élargissant ni rétrécissement l’autre Les fournisseurs sur la table sont accordés. Dire que rien reste une faille fermée au complet : quand non Le fournisseur nomme une colonne, aucune n’est lisible. Appelé une fois par table par utilisateur et mis en cache pour la requête, donc cela peut être aussi cher que ça doit l’être.
Nom: GetQueryFilterAsync
Paramètres: TableQueryContext context
Type: Task<TableQueryFilter>
Description: Étendues sur lesquelles l’utilisateur peut agir dans les lignes de cette table : un filtre construit avec Query.LogicalOperator), TableQueryFilter.AllRows pour tout Rower selon les opérations autorisées, ou nullsans lignes. Les lignes échouent à être fermées. Un fournisseur doit indiquer quelles lignes il accorde, de la même manière qu’il doit le faire Nommer les colonnes : null — ou un filtre sans conditions — n’accorde aucune, donc Oublier de définir un tableau la refuse plutôt que de publier chaque ligne. Disons TableQueryFilter.AllRows Quand chaque dispute est ce que tu veux dire. Le filtre est ajouté à la requête avant que Dataverse ne l’exécute, donc les lignes que l’utilisateur peut ne pas voir sont jamais récupérée. Elle s’applique à toutes les requêtes que le portail lance pour la table : grilles, recherches, sous-grilles, graphiques et chargements à enregistrement unique, plus la récupération pré-vol avant une mise à jour ou un supprimer — ce qui TableQueryContext.Operation distingue. Il s’exécute aussi sous les deux modèles de sécurité, il reste donc en vigueur lors de la sécurité native Dataverse contourne Services.UserInfo). C’est le framework qui décide comment les filtres se combinent, pas le fournisseur. Filtres des fournisseurs Dans le même groupe, s’intersectent — deux fournisseurs dans un même rôle sont censés s’entendre. Filtres de différents rôles s’unifient, donc occuper un autre rôle élargit ce que vous voyez, en correspondant à la façon dont le les opérations composent. Réglent PortalRoleOptions.CompositionBehavior à RoleCompositionBehavior.Restrictive pour croiser aussi plusieurs rôles. Au sein d’un groupe, un prestataire qui répond null ne laisse aucune ligne au groupe, et TableQueryFilter.AllRows laisse aux filtres des autres fournisseurs décider. De l’autre côté groupes, un groupe accordant TableQueryFilter.AllRows élargit l’additif par défaut Union à chaque ligne — donc le filtre d’un rôle ne signifie quelque chose que lorsque les autres groupes accordent Ce filtre de table aussi.

TablePermissions Classe

Propriétés

Nom
Type
Par défaut
Description
ColumnsTableColumnPermissions
Les colonnes que l’utilisateur peut lire et écrire. TableColumnPermissions.None sauf si Quelque chose nommé : l’accès aux colonnes est accordé, jamais supposé.
OperationsTableSecurityPermission
Les opérations que l’utilisateur peut effectuer sur la table, en tant que valeur de drapeau. C’est un niveau de table gate, pas un filtre de ligne — incluez-les TableSecurityPermission.Read si l’utilisateur le peut voir n’importe quelle ligne du tableau, et faire passer par lesquelles Services.TableQueryContext).
Nom: Columns
Type: TableColumnPermissions
Description: Les colonnes que l’utilisateur peut lire et écrire. TableColumnPermissions.None sauf si Quelque chose nommé : l’accès aux colonnes est accordé, jamais supposé.
Nom: Operations
Type: TableSecurityPermission
Description: Les opérations que l’utilisateur peut effectuer sur la table, en tant que valeur de drapeau. C’est un niveau de table gate, pas un filtre de ligne — incluez-les TableSecurityPermission.Read si l’utilisateur le peut voir n’importe quelle ligne du tableau, et faire passer par lesquelles Services.TableQueryContext).

Méthodes

Nom
Paramètres
Type
Description
AllColumnsTablePermissions
Refuse totalement la sécurité au niveau de la colonne — chaque colonne lisible et écrivable. Le Comportement pré-sécurité de la colonne, explicité plutôt qu’hérité.
AllowTableSecurityPermission operations
TablePermissions
Cela ajoute operations à ce que cette valeur apporte déjà.
AllowingTableSecurityPermission operations
TablePermissions
Concessions operations et pas de colonnes. Chaîne Reading(System.String[]), Writing(System.String[]) Ou TablePermissions.ReadingAllColumns pour ouvrir des colonnes.
Readingstring[] columns
TablePermissions
Autorise la lecture des colonnes nommées, en plus de celles déjà autorisées. Insensible aux majuscules, Donc l’orthographe du portail créateur fonctionne aussi bien que le nom logique.
ReadingAllColumnsTablePermissions
Refuse la sécurité de lecture au niveau de la colonne pour ce tableau : chaque colonne, y compris celles ajoutées à la table plus tard. Délibérément explicite — le cadre ne le supposera pas.
ReadingAllColumnsExceptstring[] columns
TablePermissions
Refuse la sécurité de lecture au niveau de la colonne sauf pour les colonnes nommées — chaque colonne sauf ceux-ci, y compris ceux ajoutés plus tard. Ne s’ouvre pas pour tout nouveau, donc je préfère Reading(System.String[]) sur des tables contenant des données sensibles.
ReadingAndWritingstring[] columns
TablePermissions
Permet à la fois de lire et d’écrire les colonnes nommées.
WithColumnsTableColumnPermissions columns
TablePermissions
Remplace les règles de colonne en gros, pour un fournisseur qui préférerait construire un Services.TableColumnPermissions lui-même que de chaîner les méthodes fluides.
WithholdingWithheldColumnBehavior behavior
TablePermissions
Comment les colonnes retenues de ce tableau reviennent — omises ou présentes avec une valeur masquée. Outrepasse le site SecurityOptions.WithheldColumnBehavior pour ce tableau.
Writingstring[] columns
TablePermissions
Permet d’écrire les colonnes nommées — lorsqu’un enregistrement est créé et lorsqu’il est mis à jour — dans en plus de celles déjà permises. Écrire une chronique ne signifie pas la lire — nomme-la Reading(System.String[]) Et si l’utilisateur devait voir ce qu’il a écrit.
WritingAllColumnsTablePermissions
Refuse la sécurité d’écriture au niveau de la colonne pour ce tableau : l’utilisateur peut écrire n’importe quelle colonne, lors de la création et lors de la mise à jour. Remplace les deux ensembles d’écriture, donc appelez WritingOnCreate(System.String[]) / WritingOnUpdate(System.String[]) après, pas avant.
WritingAllColumnsExceptstring[] columns
TablePermissions
Refuse la sécurité d’écriture au niveau de la colonne sauf pour les colonnes nommées, qui restent en lecture seule. Remplace les deux ensembles d’écriture, donc call WritingOnCreate(System.String[]) / WritingOnUpdate(System.String[]) Après, pas avant.
WritingOnCreatestring[] columns
TablePermissions
Permet de définir les colonnes nommées lors de la création d’un enregistrement, et seulement alors : une fois qu’il existe ils sont en lecture seule, même si un appel antérieur — Writing(System.String[]), TablePermissions.WritingAllColumns — les rendait écrivables. La priorité d’une affaire, choisie quand elle l’est enregistré et corrigé à partir de ce moment. Appelez-le après tout WritingAllColumn..., qui remplace Les deux écrivent des ensembles. Comme Writing(System.String[]), cela n’implique pas de lecture.
WritingOnUpdatestring[] columns
TablePermissions
Permet de modifier les colonnes nommées sur un enregistrement existant, mais sans les définir quand il est créés, même si un appel antérieur les rendait écrivables — des notes de résolution qui ne signifient que quelque chose une fois que le cas existe. Appelez-le après tout WritingAllColumn..., qui remplace Les deux écrivent des ensembles. Comme Writing(System.String[]), cela n’implique pas de lecture.
Nom: AllColumns
Type: TablePermissions
Description: Refuse totalement la sécurité au niveau de la colonne — chaque colonne lisible et écrivable. Le Comportement pré-sécurité de la colonne, explicité plutôt qu’hérité.
Nom: Allow
Paramètres: TableSecurityPermission operations
Type: TablePermissions
Description: Cela ajoute operations à ce que cette valeur apporte déjà.
Nom: Allowing
Paramètres: TableSecurityPermission operations
Type: TablePermissions
Description: Concessions operations et pas de colonnes. Chaîne Reading(System.String[]), Writing(System.String[]) Ou TablePermissions.ReadingAllColumns pour ouvrir des colonnes.
Nom: Reading
Paramètres: string[] columns
Type: TablePermissions
Description: Autorise la lecture des colonnes nommées, en plus de celles déjà autorisées. Insensible aux majuscules, Donc l’orthographe du portail créateur fonctionne aussi bien que le nom logique.
Nom: ReadingAllColumns
Type: TablePermissions
Description: Refuse la sécurité de lecture au niveau de la colonne pour ce tableau : chaque colonne, y compris celles ajoutées à la table plus tard. Délibérément explicite — le cadre ne le supposera pas.
Nom: ReadingAllColumnsExcept
Paramètres: string[] columns
Type: TablePermissions
Description: Refuse la sécurité de lecture au niveau de la colonne sauf pour les colonnes nommées — chaque colonne sauf ceux-ci, y compris ceux ajoutés plus tard. Ne s’ouvre pas pour tout nouveau, donc je préfère Reading(System.String[]) sur des tables contenant des données sensibles.
Nom: ReadingAndWriting
Paramètres: string[] columns
Type: TablePermissions
Description: Permet à la fois de lire et d’écrire les colonnes nommées.
Nom: WithColumns
Paramètres: TableColumnPermissions columns
Type: TablePermissions
Description: Remplace les règles de colonne en gros, pour un fournisseur qui préférerait construire un Services.TableColumnPermissions lui-même que de chaîner les méthodes fluides.
Nom: Withholding
Paramètres: WithheldColumnBehavior behavior
Type: TablePermissions
Description: Comment les colonnes retenues de ce tableau reviennent — omises ou présentes avec une valeur masquée. Outrepasse le site SecurityOptions.WithheldColumnBehavior pour ce tableau.
Nom: Writing
Paramètres: string[] columns
Type: TablePermissions
Description: Permet d’écrire les colonnes nommées — lorsqu’un enregistrement est créé et lorsqu’il est mis à jour — dans en plus de celles déjà permises. Écrire une chronique ne signifie pas la lire — nomme-la Reading(System.String[]) Et si l’utilisateur devait voir ce qu’il a écrit.
Nom: WritingAllColumns
Type: TablePermissions
Description: Refuse la sécurité d’écriture au niveau de la colonne pour ce tableau : l’utilisateur peut écrire n’importe quelle colonne, lors de la création et lors de la mise à jour. Remplace les deux ensembles d’écriture, donc appelez WritingOnCreate(System.String[]) / WritingOnUpdate(System.String[]) après, pas avant.
Nom: WritingAllColumnsExcept
Paramètres: string[] columns
Type: TablePermissions
Description: Refuse la sécurité d’écriture au niveau de la colonne sauf pour les colonnes nommées, qui restent en lecture seule. Remplace les deux ensembles d’écriture, donc call WritingOnCreate(System.String[]) / WritingOnUpdate(System.String[]) Après, pas avant.
Nom: WritingOnCreate
Paramètres: string[] columns
Type: TablePermissions
Description: Permet de définir les colonnes nommées lors de la création d’un enregistrement, et seulement alors : une fois qu’il existe ils sont en lecture seule, même si un appel antérieur — Writing(System.String[]), TablePermissions.WritingAllColumns — les rendait écrivables. La priorité d’une affaire, choisie quand elle l’est enregistré et corrigé à partir de ce moment. Appelez-le après tout WritingAllColumn..., qui remplace Les deux écrivent des ensembles. Comme Writing(System.String[]), cela n’implique pas de lecture.
Nom: WritingOnUpdate
Paramètres: string[] columns
Type: TablePermissions
Description: Permet de modifier les colonnes nommées sur un enregistrement existant, mais sans les définir quand il est créés, même si un appel antérieur les rendait écrivables — des notes de résolution qui ne signifient que quelque chose une fois que le cas existe. Appelez-le après tout WritingAllColumn..., qui remplace Les deux écrivent des ensembles. Comme Writing(System.String[]), cela n’implique pas de lecture.

AuthClientOptions Classe

Propriétés

Nom
Type
Par défaut
Description
LocalAccountsEnabledbool
True
Que le portail accepte les comptes locaux de nom d’utilisateur/mot de passe ? Par défaut vrai. Réglez sur false pour un portail interne uniquement (connexion Microsoft/Entra uniquement). Cela désactive les comptes locaux sur le serveur, pas seulement dans l’interface. MapAuthEndpoints ne correspond pas à la connexion du mot de passe, à l’inscription, au réinitialisation du mot de passe, au réglage ou au changement d’un mot de passe mot de passe, ou gestion à deux facteurs, donc ces routes 404. La couche d’identité du portail refuse également la connexion par mot de passe et refuse de créer, ajouter, changer ou réinitialiser un mot de passe, qui couvre également les pages de compte rendues par le serveur. Connexion via des fournisseurs externes, Confirmation par email, Gestion du profil, Connexion externe, téléchargement de données personnelles et La suppression de comptes n’est pas affectée. Les clients le lisent pour masquer le formulaire de connexion local et les pages de registre / réinitialisation du mot de passe. Les mots de passe déjà enregistrés pour les comptes existants restent dans Dataverse mais ne peuvent plus l’être Avant de se connecter.
SignUpEnabledbool
True
Si la connexion peut créer un utilisateur du portail. Par défaut vrai : toute personne qui s’enregistre, ou Se connecte via un fournisseur externe dont l’adresse n’a aucun contact, et on obtient un nouveau contact. Réglez sur faux pour que seules les personnes ayant déjà un contact puissent se connecter. Avec ce lien désactivé, une connexion externe correspond toujours à un contact existant — par un lien antérieur, ou par les colonnes email dans SecurityOptions.ContactMatching — et des liens vers celui-ci, envoyant un Confirmation d’abord lorsque le fournisseur ne garantit pas l’adresse. Une connexion que personne ne correspond est refusé au lieu de créer un contact. La personne qui se connecte n’est informée que lorsque le le fournisseur a vérifié l’adresse ; sinon, l’adresse reçoit un avis par courrier et la connexion est répondu comme correspondant, donc le formulaire ne peut pas être utilisé pour savoir quelles adresses ont un contact. MapAuthEndpoints ne cartographie pas l’enregistrement, donc la route 404s, et le la couche Identité du portail refuse de créer un utilisateur, ce qui couvre les comptes rendus par serveur Pages aussi. Les clients le lisent pour masquer la page de registre et le lien, et pour expliquer un refus de connexion.
Nom: LocalAccountsEnabled
Type: bool
Par défaut: True
Description: Que le portail accepte les comptes locaux de nom d’utilisateur/mot de passe ? Par défaut vrai. Réglez sur false pour un portail interne uniquement (connexion Microsoft/Entra uniquement). Cela désactive les comptes locaux sur le serveur, pas seulement dans l’interface. MapAuthEndpoints ne correspond pas à la connexion du mot de passe, à l’inscription, au réinitialisation du mot de passe, au réglage ou au changement d’un mot de passe mot de passe, ou gestion à deux facteurs, donc ces routes 404. La couche d’identité du portail refuse également la connexion par mot de passe et refuse de créer, ajouter, changer ou réinitialiser un mot de passe, qui couvre également les pages de compte rendues par le serveur. Connexion via des fournisseurs externes, Confirmation par email, Gestion du profil, Connexion externe, téléchargement de données personnelles et La suppression de comptes n’est pas affectée. Les clients le lisent pour masquer le formulaire de connexion local et les pages de registre / réinitialisation du mot de passe. Les mots de passe déjà enregistrés pour les comptes existants restent dans Dataverse mais ne peuvent plus l’être Avant de se connecter.
Nom: SignUpEnabled
Type: bool
Par défaut: True
Description: Si la connexion peut créer un utilisateur du portail. Par défaut vrai : toute personne qui s’enregistre, ou Se connecte via un fournisseur externe dont l’adresse n’a aucun contact, et on obtient un nouveau contact. Réglez sur faux pour que seules les personnes ayant déjà un contact puissent se connecter. Avec ce lien désactivé, une connexion externe correspond toujours à un contact existant — par un lien antérieur, ou par les colonnes email dans SecurityOptions.ContactMatching — et des liens vers celui-ci, envoyant un Confirmation d’abord lorsque le fournisseur ne garantit pas l’adresse. Une connexion que personne ne correspond est refusé au lieu de créer un contact. La personne qui se connecte n’est informée que lorsque le le fournisseur a vérifié l’adresse ; sinon, l’adresse reçoit un avis par courrier et la connexion est répondu comme correspondant, donc le formulaire ne peut pas être utilisé pour savoir quelles adresses ont un contact. MapAuthEndpoints ne cartographie pas l’enregistrement, donc la route 404s, et le la couche Identité du portail refuse de créer un utilisateur, ce qui couvre les comptes rendus par serveur Pages aussi. Les clients le lisent pour masquer la page de registre et le lien, et pour expliquer un refus de connexion.