A few weeks ago, I wrote a blog post about embedding a Canvas App in a Model Driven form, and compared the options of whether, in my case I should have embedded the app using the Canvas App control, or as we implemented using an iframe on the form.
Although, while making this decision to use the iframe it seemed like the correct one to do, as we would be able to control the security of the Canvas app by passing a parameter from the Model-driven form into the app, that ended up being a crash and burn situation as we needed this to be available on both Desktop and Mobile applications. At the time of writing this article, Canvas apps embedded in iframes are not accessible on mobile devices.
Even Scott Durow’s Ribbon Workbench, using Smart buttons has the ability to open up a Canvas App in a new modal window, but that too has limitations as it will not work in mobile devices due to the Cross Domain authentication restrictions that are blocked by the Power Apps mobile application. You can vote for Scott’s support request by clicking on the link below:
So, now that we had this issue, we had to find a way to resolve it. As I mentioned in my previous post, we had a complex security model, which included the sharing of records, but also a custom implemented tab level security which needed to determine whether a read-only mode or a read-write mode of this app would be accessible to the end user. All of this logic was executed via JavaScript, so it was very easy to pass a parameter, but that wasn’t accessible via mobile device.
We consulted with Microsoft, and one approach was to get all the entities that make up that security model and write logic within our Canvas app to deal with that security model. That would be an overkill.
The other approach which we thought of, was of course that Power Apps can call Power Automate flows. We want to see if this could be done, and after thorough testing, we determined this would be a good choice. We needed to write the JavaScript logic that would test tab level security into our flow, we needed additional security to check whether the user the is logged in is the owner of the record or a member of any of the teams, we need to check if the user had the correct sharing privileges via principal object access, which would also give us the status of the record was read-only or not.
Most of the flow handled the tab level security, but the problem that we had was that we could not query the principal object access entity, and it is not available on the list of entities within of CDS.
Although, I was eager to start “playing” with Custom APIs, I reverted to using custom actions and plugin code to handle the Principal Object Access security. For some organizations that you work with, preview and new features require approvals.
So, I created a custom action to check whether user had the correct sharing (Write Access) and whether the user was an owner (or member of owner team) of the record. Since the accessrightsmask return value enumeration would return the sum of all possible access rights that are being shared with ta user, I used a BitArray in order to get the value of Write Access (2). The link below shows the write access types on the Microsoft Docs site.
Once I created the functions and tested the logic, I passed the two parameters back from the Custom Action of whether the user is the owner and the user has access.
The code below shows the code that would check if the principal (user or team) has the correct access in the principalobjectaccess entity.
private bool principalHasAccess(Guid principalId, Guid objectId, int position) { bool hasAccess = false; QueryExpression query = new QueryExpression("principalobjectaccess"); query.ColumnSet.AddColumns("accessrightsmask"); query.Criteria.AddCondition("principalid", ConditionOperator.Equal, principalId); query.Criteria.AddCondition("objectid", ConditionOperator.Equal, objectId); EntityCollection results = Context.SystemOrgService.RetrieveMultiple(query); if (results.Entities.Count > 0) { foreach(Entity poa in results.Entities) { int accessRightsMask = poa.GetAttributeValue < int > ("accessrightsmask"); BitArray accessRights = new BitArray(new int[] { accessRightsMask }); hasAccess = accessRights.Get(position); } } return hasAccess; }
Once the flow was completed, the only thing that was left to do was call it from the Canvas app, and set the return variable to whether the App should run as read-only and read-write.
The image below shows the temporary screenshot that was displayed to the user while the security flow was being executed
Now that we had to go through all this functionality to implement this, my thoughts were that has to be a way that we can pass parameters between a Model-driven app and a Canvas app in a supported way. Unfortunately, none that I am aware of (or my Microsoft contacts).
I created a new idea in the Power Apps forum, so if you think that this is something that would be helpful to you in future implementations, please vote up.
UPDATE: My friend and fellow MVP, Alex Shlega just posted a link on quering the Principal Access Object from within Power Automate flows. Check it out if you are looking for that part of the solution. Thank you Alex:
https://www.itaintboring.com/dynamics-crm/how-to-verify-principle-object-access-directly-from-the-flow/