You should now have completed the Following things:
- Importing implemented Artefacts
- Implement Wizard Step 1 (Part2)
Having completed the second part of the wizard is prerequisites for the following reasons:
- Background of PowerAutomate is beenficial since we will implement another flow to specify who has access to the newly created row
- Trigger of the flow is newly added record by the you implemented in step 2
Our application scenario would be a perfect fit for applying role based security. The users importing CO2 consumption could be for instance different from the users that approve them. In a reality an importing user would probably only allowed to see his own imports and not those of other departments.
Implementing everything would be beyond the scope of that hackathon. But we want to give you at least some hands-on ideas what power platform offers for various scenarios including:
-
Log out users
-
Assigning permissions at row level (=row is also called entity in dataverse)
We will create an example flow for that. The idea is to share access rights on a single entity to individual users. The reason for this approach is that it allows the most granual control over an entity to an additional single user, to only give access to the appropriate levels of information that is required to do their jobs.
⚠️ This tasks can't be fully tested in this Hackathon, but you will get a feeling for it.
There are several ways to handle the security of your Dataverse tables. The basis of all of them are access rights via roles. Pre-defined is a hierarchical data access structure, which counts the higher hierarchical rights over the lower ones. This means you can't limit an individual user's rights, if their team or even business unit already gave them. The structure looks as follows:
- Organisation (Root Business Unit)
- Child Business Units
- Team
- User
Where Child Business Units are optional and can be under another (Child) Business Unit.
Each of them can be given some of these access rights to its underlying users:
- CreateAccess
- ReadAccess
- WriteAccess
- DeleteAccess
- AppendAccess
- AppendToAccess
- AssignAccess
- ShareAccess
Besides whole Organisation owned or User/Team owned table permissions we can also give more granual rights per column or per row/entity. When we are going to look at row level permissions you must know that each one has a pre-defined column Owner
and a corresponding Owningbusinessunit
where it was created in, which you can simply overwrite to a different user/team/business unit. The Owner only sees his own entitys and can be a single user or a single team with multiple users inside.
Another way to give rights to multiple users or even multiple teams is by sharing access. This method is less documented and tougher to troubleshoot because it's not a consistently implemented access control, but needed if there are no dedicated teams defined.
Before we start we could add a signout option for canvas apps into our OvrImports
screen. Navigate to it and select inside OvrImpVertLayout
the nested OvrImpHdrLayout
element. We are going to insert a new button into it and set its Text parameter to Signout
and the OnSelect
action to Exit(true)
.
The optional boolean defines if the user shall be signed out or not. This does not work in the Editor or Simulatior though. You would need to publish
your version and play
it without Edit mode. Since Exit(true)
also only works with Canvas Apps and not with Model-driven apps like our Hackathon, you can't test it, but we are going to continue with our flow anyway to give you an idea:
To create an automated flow click on Flows
in the Power Apps Maker and then create a new named automated flow with the trigger When a row is added, modified or deleted
from Microsoft Dataverse:
The following general rules apply:
- To add an action at the end click on the button
New step
- To expand a step do a left mouse single click on the step
- To insert a step between two existing ones click on the plus symbol between the steps
- Clicking the
Flow Checker
informs you about errors - To delete a step click on the three dots right to the header. There you find an entry for removal.
- Saving the flow requires a name. Click on the text
Untitled
and enter your desired name. Afterwards press the save button.
In the next step we have to get the internal ID of the record being added, modified or deleted. We will use the When a row is added, modified or deleted
action within dataverse for that.
Set the fields as shown in the table:
Field | Value |
---|---|
Change type | Added or Modified |
Table name | IMP_CO2_CONS_RAW_HDR |
Scope | Organization |
Select columns | cr181_cst_imp_state |
Run as | Modifying user |
After running a test this should give us the output of a body containing:
"hackpp_sceapp_imp_co2_cons_raw_hdrid": "c210849e-31cb-ee11-9079-002248e466b9"
"_ownerid_value": "75591718-bcb3-ee11-a569-002248e466b9"
"_modifiedby_value": "75591718-bcb3-ee11-a569-002248e466b9"
"RunAsSystemUserId": "75591718-bcb3-ee11-a569-002248e466b9"
In our example the user is the same person who created and modified the entity and thereby who triggered the function as the RunAsSystemUser. Because we have no other user we are going to share ourself the access rights.
Add a new Step and select Perform an unbound action
from Dataverse. This action is a multitool for several functions and is not bound (associated with a table).
For Action Name
you pick GrantAccess
(or in other cases RevokeAccess). It will update the format of the action to fit your requested action with 2 new fields: Target and PrincipalAccess.
For Target you must find your logical name of your Table. In this case it is hackpp_sceapp_imp_co2_cons_raw_hdr.
Additionally you must pluralise the term by replacing the ending -y with -ies or else just adding -s and add its GUID inside Parentheses: hackpp_sceapp_imp_co2_cons_raw_hdrs(GUID)
. You must replace the GUID placeholder with the dynamic content of IMP_CO2_CONS_RAW_HDR
.
For PrincipalAccess the formular expects you to input valid JSON code, which you can not fully comply yet unless you follow the following steps:
{
"Principal": {
"systemuserid": "",
"@odata.type": "Microsoft.Dynamics.CRM.systemuser"
},
"AccessMask": "ReadAccess,WriteAccess"
}
- Hint: To add a team instead of a user you would replace CRM.systemuser with CRM.team and systemuserid with teamid.
-
Fill the value of systemuserid with the expression
triggerOutputs()?['body']?['RunAsSystemUserId']
. The questionmarks return null instead of error when its element could not be found. With this expression we input the RunAsSystemUserId from the body from the Output of our starting trigger. -
Replace the @ with a variable of @. The @ is an excape character and you can row 2 @@ to fix it temporarily, but the best and permanent solution is to insert a variable containing
@
:-
Insert a new Step above the
Perform an unbound action
by clicking on the+
on the arrow and selectingAdd an action
. ChooseInitialize variable
as action. Name itAt
with the type ofString
and the value@
. -
Replace the @ in PrincipalAccess with the variable.
-
After all steps are done the flow should look like this and you can save it without an error.
To remove our user from the owner column you must specify a different one:
- Add a new step
- Add the action
List rows
from Dataverse - Select the Table name:
Users
- Set Row count to
2
- Add a new step
- Add the action
Update a row
from Dataverse - Select Table name
IMP_CO2_CONS_RAW_HDR
- For Row ID input the dynamic content of
IMP_CO2_CONS_RAW_HDR
- For Owner (Owners) insert
/systemusers()
with the expressionoutputs('List_rows')?['body/value']?[1]?['systemuserid']
inside the parenthesis.
- Notice that the Owner input needs the pluralised logical name of the table
user
with or without a leading/
and that you select systemuserid of the second element from the output ofList Rows
. There are internal users in that table, but many will lead to an errorSecLib::CrmCheckPrivilege failed. Returned hr = -2147220839 on UserId: f3c78acb-abbf-ee11-9079-002248e61a82 and Privilege: prvReadHACKPP_SCEAPP_IMP_CO2_CONS_RAW_HDR
since they have no general read rights of the table to be the owner of a row.
Start from the import overview page WizardStepImpHeader
to ensure a correct screen context. Press the play button after selecting the overview screen to start the tests. Thanks to your changes the following scenarios should now work:
Test | Expected Result |
---|---|
Wizard first step: Click on Submit button | Either the Flow Test should run through and you should see a new record in the dataverse table after clicking submit with a different owner. Or only the last Update a row errors with SecLib::CrmCheckPrivilege failed |