Tip #1089: User Delete Privileges Stretch Further Than You Think

This one was discovered by my KPMG partners in crime Fiona Whiteing and Jijeesh Kunhiraman on a recent project. As you may know, for a record such as a Contact, if I own the record, I can see the child Activities whether my security role gives me permission or not. I do not know if this obscure exception to record level security has a name. I call it ‘inherited security’. In fact it also works for Team ownership; if I am a member of a Team which owns a Contact, I can see the Activities of that Contact whether my security role (or the Team’s security role) allows me to or not.

If you did not know the above, end here and walk away intellectually nourished by an informative tip. If you did know the above, here is the twist. Let us say you are a member of a Team which owns a Contact and you can see the associated Activities thanks to inherited security. Let is assume none of the Activities are yours. If I now give your User record’s Security Role User-level delete privileges to Activities, you can now delete all those child Activities. Even though they are owned by other people and you can only see them because the Activities are on a Contact, owned by a Team which you are a member, you still get to blow them away.

Somehow inherited security amplifies the User-level delete privilege to work on any child record it renders visible, regardless of the actual owner. Is this a bug? Maybe. My guess is, as inherited security pre-dates the introduction of Team ownership in Dynamics, the subtle nuances of the two combining were not fully tested and give rise to unusual results. The moral of this tip? If you are securing records and inherited security is involved, make sure you test everything within an inch of its life.

3 thoughts on “Tip #1089: User Delete Privileges Stretch Further Than You Think

  1. AdamV says:

    There’s several things going on here which appear to give an end result which is complicated and appears uncontrolled, but if broken down it makes more sense.

    Most of this is down to cascading behaviours in 1:N relationships. The first part is down to the “reparent” behaviour. This is the control which detemines what access is given to child records when they are linked to a parent record for the first time, or moved to a new parent. If the record owner is not the same as the owner of the parent, then the child record is share with the parent’s owner (user or Team) to give them access to it. This is an “implicit” share (rather than “explicit”) – there’s your technical term. It makes a lot of sense for scenarios such as an Account Manager owning an Account, but various people in their inside sales team owning different Contacts, or Opportunities. The Account Manager wants to deal with all those child records as if they owned them directly.

    The bitmask value for this share appears in a separate column of the POA table, although it also has a specific bit set to declare it as an implicit share. It also has all other bits set to 1, so it confers all possible rights over the child record. What!? ALL rights? Well, sort of. It is flagged for all rights, but in usage this is still controlled by the user’s security privileges. Let’s say Alice owns an Account, and Bob owns a Contact under that Account, and the cascading behaviour for Reparent is set to “Cascade All” (the OOB setting). When the Contact was created, an implicit share was added by the system showing that Alice has some rights to the Contact. Alice can then do anything to the Contact that she could do if she actually owned it (even though she does not). So if Alice has the right to Delete Contacts at user level or above, then she can delete this Contact. Similarly to Assign, Write etc.

    So you can control this behaviour in two ways.
    First, by removing Alice’s rights to delete any Contacts. Now, because she cannot delete her own Contacts, she can’t delete those which are implicitly shared with her either (possibly harder to justify for Activities, which is an issue).
    Second, by configuring the reparent cascading behaviour to “none”. This would mean that Alice does not have any extra rights over that Contact just because it is under her Account. This might not matter if you have a fairly transparent organisation where Alice can at least read all Contact records anyway, but in many scenarios you might be relying on this cascading to give additional rights. Remember: this behaviour is controllable for each parental 1:N relationship, so you could cascade rights from the Account to the Opportunity (perhaps to ensure that Alice has Assign privileges over all opps for any of her Accounts), while not cascading them to Contacts (where Alice already has business unit level Read and Append To access, and needs nothing else on Contacts she does not own). Important note: changing cascading behaviours only makes a difference going forward, it does not change any retrospective shring already performed.

    The second things thrown in the mix here is Team ownwership, which (as always) complicates things or at least appears to. However, I don’t think there is anything magic in the way this is working here because of the Team element. And since the inherited security relies on special shares, which are recorded in the POA table, and when the POA table is queried it always looks for any security principal related to the user (their own user GUID plus all GUIDs of all Teams they are in), I can’t see why the behaviour should be any different.

    I think what you should be seeing here is that any member of the Team that owns the parent record can only do things to the child records that *the Team* has the rights to do. So if Alice has the right to delete her own Contacts, but the Team that owns the Account does NOT have delete privileges for Contacts, then I would expect that Alice cannot delete these implicitly-shared Contacts – the security privilege check is to see if the parent’s owner could do this action if they owned the record. In this case, the Team could not delete the Contact even if it owned them, so a member of the Team cannot do that either.
    An interesting one to test, but could make it easier to deal with by ensuring that the security roles assigned to Teams do not include unwanted Delete privileges. I would be interested to know whether the behaviour you are seeing is only true if the *Team* has the delete privilege, or if it is genuinely taking the user’s privileges into account for the child records, which I think it should not. And of course I assume you are not using a regular user security role for the team as well 😉
    This might provide you with a fix, in that it might be hard to justify users NOT having delete rights for their own activities, but should be relatively easy to justify that no Team needs these rights.

    A third thing here that is probably even more important to understand is how the cascading behaviour for “Delete” works. If this is set to “Cascade All”, then this setting is always honoured. After all, cascading behaviours are configured by all-powerful administrators, right? So if Alice has the right to Delete an Account then this will cascade down to all the Contacts under that Account, regardless of who owns them, and irrespective of whether Alice could even read those records before deleting the Account. It is perfectly possible for a user to do all the due diligence possible, by looking for child records such as Contacts, Opportunities, Cases and Activities. If they don’t find anything because there are records they do not have access to, then they might be tempted to delete the parent record thinking there is no knock-on effect. Unfortunately, they can wipe out the whole record hierarchy under that one top-level record. They don’t need to have any kind of delete privileges to the child entities, their super-delete-power is conferred on them by the cascading delete configuration. After all, that is what the administrator configured, so it must be obeyed. And helpfully, “Cascade All” is the default configuration for pretty much all the OOB 1:N relationships. I would ALWAYS suggest that this is re-considered during your security design phase and where possible set to “Restrict delete” (which will not let the user delete a parent record which has any children) or as a last resort “Remove link” (which might leave orphan records, usually not a good idea except in some edge cases).

    In summary, the morals of the lesson are:
    1) Don’t let users have delete privileges to anything, ever, unless you are 100% certain of all the possible knock-on effects, and 100% certain that the business need to delete a record outweighs the potential risks.
    Also consider adding two steps to the process in order to delete something, and/or require two users to do this For example: user A assigns the record to a special Team that they are not a member of. A second user is a member of that Team, and the Team has delete privileges at “user” (=owner) level for the relevant entity so the Team members can delete records owned by the Team. Second user can “reject” the deletion by assigning the record back to a user. (Watch out for cascading behaviours on Assign here of course!)

    2) Don’t leave cascading behaviours at OOB settings.
    Consider whether Reparent behaviour in particular achieves any particular business objectives for your scenario.
    Consider whether the “nuke it from space” outcome of a cascading delete is really a power you want to give to users.

  2. MrE says:

    Hi

    It was never ever hard to me to justify users NOT having deletion rights as long they could still deactivate the mistaken records.

    Mostly, they were even happy to know that if they “deleted” = deactivated a record by mistake, you/they could still easily reactivate them again.

    Loss / risk aversion comes into play and helps us here.
    People tend to prefer a few deactivated (remaining) records they have created by mistake rather than to risk a total data loss from an erroneous irreversible deletion of a single record (even if they don’t know any of the interesting aspects AdamV has outlined above).

  3. jackskellington says:

    @AdamV Thank you, your comment is the most informative thing I’ve ever read on the behaviour of CRM relationship cascading. Much more enlightening than the official documentation!

Leave a Reply

Your email address will not be published. Required fields are marked *