+ Start a Discussion
sparkysparky 

determine edit/delete permissions in code?

Is it possible to determine whether the current user has delete permissions (aka Full Access) for a particular record (custom object), without actually attempting to delete the record?

I'm creating some custom actions using VF/apex, and for some of them I want to limit who can perform the action to the same users who can delete the record.  The action does not involve deleting the record, but I want it to be more restrictive than who can perform an edit.

I looked into the sharing table (object_share) but that only lists the owner as Full Access.  Since anyone above the owner in the role hierarchy also has full access, and the hierarchy might be several levels tall, it seems like a tall order for my code to walk the entire hierarchy and determine if that applies.  Is there an easier way?

Thanks much!
SiriusBlackOpSiriusBlackOp
Yes... you should be able to use the sObject Describe Methods.  These take a little bit of overhead as I understand because it probably works something like reflection in .NET, so don't use them everywhere, but it should get you what you need.
 
Here is a link to the info in the Apex Language Reference:
http://www.salesforce.com/us/developer/docs/apexcode/index.htm
 
Look at the "isDeletable" method.
 
SiriusBlackOpSiriusBlackOp
Ok... that link didn't work quite right. LOL!
 
Click the link, click search, type "isDeletable", click search, click on first item in search results called "sObject Describe Result Methods".
sparkysparky
OK, thanks for that.  But doesn't a describe method tell me if the user has delete permissions in general for the object?  (via their Profile?)

If so, that's only half the battle.  I need to know if they have delete permissions on the record (via their role).
SiriusBlackOpSiriusBlackOp

All you have to worry about is the Profile though... right? 

I thought Roles only limit visibility... not edit/delete.  If they can't see it, then they can't delete it. 

So, are you encountering some sort of cascading delete issue where they can delete the Parent but the Child is not viewable via their Role or something?

sparkysparky
Nope.  From the SF online help: 



Why do I get an "Insufficient privileges" error when trying to delete records?

The ability to delete records in Salesforce is controlled by the role hierarchy. Setting a sharing model to ""Public Read/Write"" alone does not give users the right to delete others records. There are 2 scenarios in which a user can delete a record:

1. The user attempting to delete the record is a System Administrator.

2. The user attempting to delete the record is the owner, or higher on the role hierarchy than the record owner.

Any other user that attempts to delete a record will receive an "Insufficient Privilege" error message.

Those below you on the role hierarchy may have read/write privileges according to the sharing model rules, however, they may not delete information from those individuals above them in that hierarchy.



SiriusBlackOpSiriusBlackOp

Ok... so it blocks a delete for anything they are not supposed to see anyway.  I guess that is good functionality.

There is your answer though.

If the userId = ownerId you are good to go.
If the userRole = ownerRole you are good to go.
If the userRole > ownerRole you are good to go.

I'm sure there is a way to query that last one, but you could just create a loop that walks up the chain looking for a match between the ParentRoleId and the other Id's.

sparkysparky
Sirius, I really do thank you for trying to help here, but that's not the answer I was looking for.  If you look back at my original post, crawling the role hierarchy tree is exactly what I was wanting to avoid.  I already knew that was one way to do it.

FYI, there are two factually incorrect statements in your last post.

"If the userRole = ownerRole you are good to go" -  Not true.  Another user w/ same role as owner cannot delete owner's records (unless both are sysadmins).

"so it blocks a delete for anything they are not supposed to see anyway"  - No, deletion and view/edit rights are separate.  Often a user can view and even make edits to a record that they cannot delete.


Does anyone else have any suggestions here?  I'm guessing there's no shortcut or someone would have offered it by now..

I suppose I could actually attempt the delete in a try block, and if it succeeds then immediately roll it back.  That seems risky, but maybe just because I don't understand the ramifications.  Anything I should be concerned with there?


VarunCVarunC

sparky wrote:
Nope.  From the SF online help: 



Why do I get an "Insufficient privileges" error when trying to delete records?

The ability to delete records in Salesforce is controlled by the role hierarchy. Setting a sharing model to ""Public Read/Write"" alone does not give users the right to delete others records. There are 2 scenarios in which a user can delete a record:

1. The user attempting to delete the record is a System Administrator.

2. The user attempting to delete the record is the owner, or higher on the role hierarchy than the record owner.

Any other user that attempts to delete a record will receive an "Insufficient Privilege" error message.

Those below you on the role hierarchy may have read/write privileges according to the sharing model rules, however, they may not delete information from those individuals above them in that hierarchy.



 

Any provision of allowing User to delet records with User Rolw lesser then the Role of Record Owner, in ANY Future Releases?

BudVieiraBudVieira

Hi Sparky and folks,

 

We are currently in design / planning for a feature that would allow you to query a user's access to a record (maybe a set of records?) prior to rendering a detail page, list view, or related list. Recursion on the Role Hierarchy is just the beginning of the complexity on this, so we are looking to short-cut this chore for you.

 

You have been talking about the single-record access check, but would you also find it valuable to be able to pre-query a list of records? For example, if you were presenting a list view, you could send a list and an access level you want to check, then we send you back the access decision for that user for each record in the list. You could then use this to figure out whether to show Edit or Delete links next to each item in your list.

 

Bud Vieira

Sr. Product Manager, Platform (Sharing)

Salesforce.com

steve.stognersteve.stogner
Please make the new call available to the web services API as well.
BudVieiraBudVieira

Hi all, this functionality is coming out in Spring '12, implemented as an API query in the form:

 

SELECT RecordId, [HasReadAccess, HasEditAccess, HasAllAccess, MaxAccessLevel] FROM UserRecordAccess

WHERE UserId = [single ID]

AND RecordId = [single ID]    //or Record IN [list of IDs]

 

It returns either a 1/0 decision on the access level you have chosen, or if you choose MaxAccessLeve, the highest access level the user has to each of the records in the list.

 

@steve.stogner - as it is a query and not a new API verb, it should be accessible from the current SOAP API

 

steve.stognersteve.stogner

As a quick test, I rebuilt the client jar from the 24.0 enterprise wsdl and tried:

 

SELECT RecordId FROM UserRecordAccess

 

but I'm encountering an INVALID_TYPE error.

 

[InvalidSObjectFault [ApiQueryFault [ApiFault  exceptionCode='INVALID_TYPE'
 exceptionMessage='sObject type 'UserRecordAccess' is not supported.'
]
 row='-1'
 column='-1'
]
]

craigmhcraigmh

Amazing, I'm surprised that SFDC is moving towards opening that up more. Historically, they've been very opposed to anything that even resembles reflection.

BudVieiraBudVieira

Hi Steve - that's puzzling. Just had one of our devs test it and working fine. Are you working from a clean build of Winter '12? The wsdl version is correct, anyway.

steve.stognersteve.stogner

OK nevermind -- version 24.0 is working: the client code was indeed updated, but the test runner was passing the old version number in the salesforce URL.  Thanks!

dsh210@lehigh.edudsh210@lehigh.edu

This is a fantastic new feature and is sure to save developers a ton of time. I am, however, getting some very unexpected results. I have a list of records that I own and I am using the following query:

 

List<UserRecordAccess> accessList = [SELECT HasDeleteAccess, HasEditAccess, HasReadAccess, RecordId FROM UserRecordAccess WHERE RecordId IN :myObjectIds AND UserId = :UserInfo.getUserId()];

 

This query is being run as the System Admin in a DE org (this user owns all records in that list).

 

The result of that query is saying that I have read access, edit access, but no delete access to these records. However, I can click the delete button on my Visualforce page and the record is deleted. It is also reporting that for some of the records, I have neither read access nor edit access, but I can view and edit them with no problems.

 

Has anyone experienced this inconsistency? Is there something that I am missing about how this object works?

hhkwonghhkwong

Hi,

 

The way you are you writing the query looks correct. What is the object type / object types of the records in the query? Do you see this with only certain object types?

 

Thanks,

Helen

dsh210@lehigh.edudsh210@lehigh.edu

Helen,

 

I am querying on the Task object. I have noticed that if I pass a single record id, I get the correct result (read/edit/delete), but if I pass a list of Task IDs (including the task I got the correct result with), I get just read/edit back for the same task. I have not yet tried this with a different object, but will do so.

 

-Derrek

michaelforcemichaelforce

Hi Helen,

 

Similar to Derek's problem, I have noticed that RecordId IN [List<Id>] works, but RecordId IN [Set<Id>] does not, although it doesn't throw any type of error.  Is this expected behavior?

 

It's not a big deal because I can convert from Set to List, but thought I would ask anyway.

Rick UptonRick Upton
For a "no code" solution involving Lightning Flow, check out the screenshots on my comment on this idea:
https://success.salesforce.com/ideaView?id=0873A000000LmcJQAS