function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Alex Packard 5Alex Packard 5 

UserRecordAccess Values Incorrect

If you do a SOQL query for a single UserRecordAccess record, you get the correct values. But if you query for multiple UserRecordAccess records in a single query, the values returned are often incorrect (for instance the HasAllAccess value may be true when it should be false).

This problem makes it impossible for us to reliably communicate object permissions to our users.

This is nearly identical to the following issues that was marked as fixed back in the Winter 15 release: https://success.salesforce.com/issues_view?id=a1p30000000T4iiAAC

Below is a test class I wrote that reproduces the issue.  In this test class I use the Macro sobject, but the problem applies to other objects as well.  I first noticed it on a custom object.
@isTest
public without sharing class UserRecordAccessTest {

	private static testMethod void test(){

		//Create the users that will own the records
		final User[] users = createStandardUsers(2);
		final User u1 = users[0];
		final User u2 = users[1];

		//Create the records, each owned by a different user
		final SObject o1 = createRecord(u1.Id);
		final SObject o2 = createRecord(u2.Id);
		insert new SObject[]{o1, o2};

		system.runAs(u1){

			//Query for each UserRecordAccess entry individually
			final UserRecordAccess access1 = queryForSingleUserRecordAccess(u1.Id, o1.Id);
			final UserRecordAccess access2 = queryForSingleUserRecordAccess(u1.Id, o2.Id);

			//Query for both UserRecordAccess entries at the same time
			final UserRecordAccess[] accessList = queryForUserRecordAccesses(u1.Id, new Set<Id>{o1.Id, o2.Id});

			//The UserRecordAccess entries should have the same values, regardless of how they were queried
			compareUserRecordAccesses(access1, accessList[0]);//This fails because the HasAllAccess value is false for the entry queried individually, but is true for the entry queried in a list
			compareUserRecordAccesses(access2, accessList[1]);

		}


	}

	private static UserRecordAccess queryForSingleUserRecordAccess(final Id userId, final Id recordId){
		return [
			SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess, HasTransferAccess, HasAllAccess
			FROM UserRecordAccess
			WHERE UserId = :userId AND RecordId = :recordId
		];
	}

	private static UserRecordAccess[] queryForUserRecordAccesses(final Id userId, final Set<Id> recordIds){
		return [
			SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess, HasTransferAccess, HasAllAccess
			FROM UserRecordAccess
			WHERE UserId = :userId AND RecordId IN :recordIds
			ORDER BY RecordId
		];
	}

	private static void compareUserRecordAccesses(final UserRecordAccess expected, final UserRecordAccess actual){

		system.assertEquals(expected.RecordId, 			actual.RecordId);
		system.assertEquals(expected.HasReadAccess, 	actual.HasReadAccess);
		system.assertEquals(expected.HasEditAccess, 	actual.HasEditAccess);
		system.assertEquals(expected.HasDeleteAccess, 	actual.HasDeleteAccess);
		system.assertEquals(expected.HasTransferAccess,	actual.HasTransferAccess);
		system.assertEquals(expected.HasAllAccess, 		actual.HasAllAccess);

	}

	private static User[] createStandardUsers(final Integer count){

		final Profile standardUser = [SELECT Id FROM Profile WHERE Name = 'Standard User'];

		final User[] users = new User[]{};
		for(Integer i = 0; i < count; i++){
			users.add(new User(
				UserName = 'test' + i + '@userrecordaccesstest.com',
				LastName = 'Smith',
				Email = 'test@user.com',
				Alias = 'test',
				CommunityNickname = 'test' + i,
				TimeZoneSidKey = 'America/Indiana/Indianapolis',
				LocaleSidKey = 'en_US',
				EmailEncodingKey = 'UTF-8',
				LanguageLocaleKey = 'en_US',
				ProfileId = standardUser.Id
			));
		}

		insert users;

		return users;
	}

	private static SObject createRecord(final Id ownerId){
		return new Macro(Name = 'Macro' + ownerId, OwnerId = ownerId);
	}

}

 
Alex Packard 5Alex Packard 5
I would like to express my frustration at the lack of responses here.  I originally logged a case with salesforce for this issue, and was told to post here because "We support our standard customers through the developer support boards".  I have received no such support.

I believe I have demonstrated that there is a bug in your system, one that it negatively impacting our ability to deliver a reliable product to our customers, what can I do to get this problem fixed (or at least acknowledged)?
Alex Packard 5Alex Packard 5
I was finally able to get Salesforce R&D to look at this issue.  They essentially explained that this was a "known limitation" of the UserRecordAcccess feature.  

This is a pretty dissapointing result, as it means that you can only look at accurate UserRecordAccess values for one record at a time.  

If anybody ever finds this post and has the same problem, please vote for this idea I created and maybe one day they will fix it:  https://partners.salesforce.com/ideaView?id=0873A000000lKROQA2
Piyali Chowdhury 15Piyali Chowdhury 15
Maybe get some alternatives from 'sfdc99.mailbag@gmail.com'
James WoodwardJames Woodward
Do we know if it is limited to the HasAllAcess field? Or ar all the HasXAccess fields unreliable?
Alex Packard 5Alex Packard 5
I have seen the problem on some of the other HasXAccess fields as well.