Streamline Access: Cleaning Up Inherited Permissions in Microsoft Dataverse
This article explains the process of removing inherited access for records in Microsoft Dataverse when changes are made to the cascade configuration of a table relationship. Understanding and managing inherited access is crucial for maintaining data security and ensuring users have appropriate permissions within your Dataverse environment. When relationship behaviors are modified, particularly concerning cascading actions, it’s essential to verify and, if necessary, clean up access permissions to prevent unintended data access. This article will guide you through identifying, verifying, and resolving issues related to inherited access, ensuring a streamlined and secure data environment.
Symptoms¶
One common symptom of this issue arises after the cascading behavior of a table relationship, specifically for the Reparent or Share actions, is altered to No Cascade. Despite this change, users may continue to possess access to related records that should have been revoked. This situation occurs because the initial access rights, granted through the cascade settings, might not be automatically removed when the cascading behavior is modified. This can lead to users inadvertently retaining permissions they should no longer have, potentially compromising data security and compliance. It is important to address these symptoms promptly to maintain the integrity of your security model.
How to verify the access to related records¶
When users report unexpected access to records, it’s essential to verify these claims and understand the origin of their permissions. Microsoft Dataverse offers two primary methods to verify access to related records: utilizing the Check Access feature within model-driven apps and employing the RetrieveAccessOrigin message for developers. Both methods provide insights into why a user has access, helping administrators and developers diagnose and resolve permission issues efficiently. Understanding these verification techniques is crucial for maintaining a secure and well-governed Dataverse environment.
Use the Check Access feature¶
The Check Access feature, integrated into model-driven apps, is a user-friendly tool designed to investigate user permissions on specific records. This feature empowers administrators to easily check access for individual users or comprehensively assess access for all users concerning a particular record. When using the access checker, the system provides a detailed list of reasons explaining why a user has access. Among these reasons, some will explicitly indicate that access was granted due to permissions on a related record. Examples of such reasons include:
- “Record was shared with me because I have access to related record.”
- “Record was shared with team(s) that I’m a member of because the team has access to related record.”
These messages clearly point to inherited access as the source of the user’s permissions, guiding administrators to focus on relationship-based security configurations when troubleshooting. The Check Access feature is an invaluable tool for quickly understanding and managing user permissions in Dataverse.
Use the RetrieveAccessOrigin message¶
For developers requiring a programmatic approach to access verification, the RetrieveAccessOrigin message provides a robust solution. This message allows developers to programmatically determine why a user has access to a record. The message returns a descriptive sentence detailing the origin of the access rights. Specific results from this message indicate that access was granted due to the sharing of a related record. These results typically follow a pattern that clearly identifies inherited access:
PrincipalId is owner of a parent entity of object (<record ID>)
PrincipalId is member of team (<team ID>) who is owner of a parent entity of object (<record ID>)
PrincipalId is member of organization (<organization ID>) who is owner of a parent entity of object (<record ID>)
PrincipalId has access to (<parent record ID>) through hierarchy security. (<parent record ID>) is owner of a parent entity of object (<record ID>)
These outputs explicitly state that the user’s access is derived from their permissions on a parent or related entity. Developers can parse these messages to programmatically identify and manage inherited access scenarios. For more detailed information on programmatic access determination, refer to the documentation on Determine why a user has access with code. This method is particularly useful for automated auditing and permission management processes.
Cause¶
The root cause of users retaining unexpected access after modifying cascading behavior lies in the asynchronous processes within Dataverse. When the cascading behavior for a table relationship is changed, Dataverse initiates an asynchronous system job designed to remove previously granted access rights. This job is crucial for ensuring that permission changes are effectively applied across the environment. However, in certain situations, this system job may fail to complete successfully. When the job fails, the intended revocation of access permissions does not occur, leading to users retaining access to records they should no longer be able to access. This failure can stem from various underlying issues, requiring administrators to monitor system jobs and implement corrective actions to ensure data security and access control are properly maintained.
Resolution¶
Addressing the issue of lingering inherited access requires a two-step resolution process. The initial step involves attempting to recreate the system job responsible for removing access. If recreating the job fails to resolve the issue, the subsequent step involves developers utilizing the ResetInheritedAccess message. This message allows for a more targeted approach, applying the necessary changes to a specific set of records. By following these steps, administrators and developers can effectively tackle the problem of users retaining unintended access, ensuring data permissions align with the intended security configurations.
Recreate the system job to remove access¶
To re-initiate the process of removing inherited access, developers can employ the CreateAsyncJobToRevokeInheritedAccess message. This message is specifically designed to trigger the creation of a new asynchronous job, named RevokeInheritedAccess. This action is essentially a retry mechanism to ensure the system attempts to clean up access permissions as intended after changes to cascading behaviors.
// Example code snippet (conceptual - language depends on context)
CreateAsyncJobToRevokeInheritedAccessRequest request = new CreateAsyncJobToRevokeInheritedAccessRequest();
CreateAsyncJobToRevokeInheritedAccessResponse response = (CreateAsyncJobToRevokeInheritedAccessResponse)_serviceProxy.Execute(request);
After initiating the job, administrators can monitor its progress and success through the system jobs monitoring interface. However, it’s important to note that there is no preview functionality available to determine precisely which records will be affected by this job beforehand. For comprehensive guidance on monitoring system jobs, refer to the documentation on monitoring system jobs or managing system jobs with code. Recreating the system job is a crucial first step in resolving inherited access issues.
Reset inherited access¶
If recreating the system job proves unsuccessful in removing inherited access, the next step involves utilizing the ResetInheritedAccess message. This message offers a more granular approach, allowing developers with system administrator or system customizer privileges to target a specific subset of records for access revocation. It may be necessary to execute this message multiple times to ensure all affected records have their access permissions correctly updated. This targeted approach is particularly useful when dealing with large datasets or when specific record sets are known to be affected.
// Example code snippet (conceptual - language depends on context)
ResetInheritedAccessRequest request = new ResetInheritedAccessRequest()
{
Query = fetchXmlQuery // FetchXml query to select POA records
};
ResetInheritedAccessResponse response = (ResetInheritedAccessResponse)_serviceProxy.Execute(request);
The ResetInheritedAccess message exhibits different execution modes based on the number of records it needs to process. When the number of matching records is small, the operation typically executes synchronously. In such cases, the ResetInheritedAccessResponse value will end with ExecutionMode : Sync, indicating immediate processing. However, when a large number of records are involved, the operation transitions to asynchronous execution. The ResetInheritedAccessResponse will then end with ExecutionMode : Async, and the system will create a background system job named Denormalization_PrincipalObjectAccess_principalobjectaccess:<caller ID>. Administrators can monitor the progress and success of this asynchronous job through the system jobs interface. Again, for detailed information on monitoring system jobs, refer to the documentation on monitoring system jobs or managing system jobs with code.
The ResetInheritedAccess message requires a FetchXml query to precisely identify the records that need their inherited access reset. This FetchXml query must adhere to specific requirements to function correctly:
- Target the
principalobjectaccess(POA) table: The query must be directed at the POA table, which stores information about principal access rights to objects. - Select only
principalobjectaccessid: The query should only retrieve theprincipalobjectaccessidcolumn. This is the primary identifier for records in the POA table. - Exclude
link-entityelements: Joins to other tables usinglink-entityare not permitted within this FetchXml query. The query must operate solely on theprincipalobjectaccesstable. - Filter only on
principalobjectaccesscolumns: Filtering conditions within the query must only reference columns belonging to theprincipalobjectaccesstable.
The principalobjectaccess table, while accessible via the Web API as the principalobjectaccess entity type, is not included in the standard Dataverse table/entity reference. This is because the POA table does not support direct data modification operations. To effectively construct FetchXml queries for ResetInheritedAccess, understanding the columns of the POA table is essential.
POA table columns¶
To compose effective FetchXml queries for use with the ResetInheritedAccess message, it’s crucial to understand the available columns within the principalobjectaccess (POA) table. These columns provide the necessary criteria for filtering and targeting specific access records. Below is a table outlining the key columns, their data types, and descriptions:
| Logical name | Type | Description |
|---|---|---|
accessrightsmask |
Integer | Contains the combined AccessRights enum member values representing the access rights that the principal has directly. |
changedon |
DateTime | The date and time when the principal’s access to the record was last changed. |
inheritedaccessrightsmask |
Integer | Contains the combined AccessRights enum member values representing access rights applied due to inheritance. |
objectid |
Unique Identifier | The ID of the record to which the principal has access. |
objecttypecode |
Integer | The EntityMetadata.ObjectTypeCode value corresponding to the table. This value may vary across environments. |
principalid |
Unique Identifier | The ID of the user or team that has access. |
principalobjectaccessid |
Unique Identifier | The primary key of the POA table, uniquely identifying each access record. |
principaltypecode |
Integer | The type code of the principal. SystemUser is represented by 8, and Team by 9. |
Understanding these columns allows for precise targeting of inherited access records within FetchXml queries.
The accessrightsmask and inheritedaccessrightsmask columns utilize values from the AccessRights enum. These enum values define the specific types of access rights. Here’s a table detailing some key AccessRights enum member values:
| Access type | Value | Description |
|---|---|---|
None |
0 | No access rights granted. |
Read |
1 | The right to read or view a record. |
Write |
2 | The right to update or modify a record. |
Append |
4 | The right to append the specified record to another record. |
AppendTo |
16 | The right to append another record to the specified record. |
Create |
32 | The right to create new records. |
Delete |
65,536 | The right to delete a record. |
Share |
262,144 | The right to share a record with other users or teams. |
Assign |
524,288 | The right to assign a record to a different user or team owner. |
It’s common to observe the inheritedaccessrightsmask value as 135,069,719. This value represents a combination of access types, encompassing all rights except Create. The Create right is typically excluded as inherited access primarily applies to records that already exist, not the creation of new records.
FetchXml examples¶
The following sections provide practical examples of FetchXml queries that can be used with the ResetInheritedAccess message. These examples demonstrate how to target specific scenarios for resetting inherited access. For more in-depth information on constructing FetchXml queries, refer to the guide on Use FetchXML to construct a query.
Reset inherited access given to a certain user for a specific account¶
This FetchXml query targets inherited access records for a specific user related to a particular account record. It filters the principalobjectaccess table to select records matching a specific principalid (user ID) and objectid (account ID).
<fetch>
<entity name="principalobjectaccess">
<attribute name="principalobjectaccessid"/>
<filter type="and">
<condition attribute="principalid" operator="eq" value="9b5f621b-584e-423f-99fd-4620bb00bf1f" />
<condition attribute="objectid" operator="eq" value="B52B7A48-EAFB-ED11-884B-00224809B6C7" />
</filter>
</entity>
</fetch>
In this example:
principalidwith the value “9b5f621b-584e-423f-99fd-4620bb00bf1f” represents the specific user whose inherited access is being targeted.objectidwith the value “B52B7A48-EAFB-ED11-884B-00224809B6C7” represents the specific account record for which inherited access is being reset.
Reset inherited access given to all child rows for a specified object type¶
This FetchXml query aims to reset inherited access for all records of a specific object type. It filters the principalobjectaccess table based on the objecttypecode, targeting all records associated with that entity type.
<fetch>
<entity name="principalobjectaccess">
<attribute name="principalobjectaccessid"/>
<filter type="and">
<condition attribute="objecttypecode" operator="eq" value="10042" />
</filter>
</entity>
</fetch>
Here, objecttypecode with the value “10042” represents a placeholder for the specific entity’s object type code. You would replace “10042” with the actual objecttypecode of the table for which you want to reset inherited access for all associated records. Determining the correct objecttypecode might require metadata browsing, as these codes can vary across environments, particularly for custom tables.
Reset inherited access given to a specified user for all object types¶
<fetch>
<entity name="principalobjectaccess">
<attribute name="principalobjectaccessid"/>
<filter type="and">
<condition attribute="principalid" operator="eq" value="9b5f621b-584e-423f-99fd-4620bb00bf1f" />
</filter>
</entity>
</fetch>
This FetchXml query focuses on resetting inherited access for a specific user across all object types within the Dataverse environment. It filters the principalobjectaccess table based solely on the principalid, effectively selecting all inherited access records associated with the specified user, regardless of the record type.
In this query, principalid with the value “9b5f621b-584e-423f-99fd-4620bb00bf1f” represents the user for whom inherited access is being reset across all object types. This type of query is useful when you need to comprehensively clean up permissions for a user who might have accumulated broad inherited access rights.
By understanding these methods and examples, administrators and developers can effectively manage and clean up inherited access in Microsoft Dataverse, ensuring a secure and properly permissioned environment.
If you have any questions or insights about managing inherited permissions in Dataverse, feel free to leave a comment below!
Post a Comment