0

We have a public login-based portal hosted from our Salesforce org that updates our Salesforce org data as people fill out information about themselves in Visualforce web forms hosted inside the portal.

Portal users all get a "Force.com - App Subscription"-typed profile.

I am writing a trigger to post-process certain org data edits originating from this portal, and one of the things I need my Apex code to be able do during the trigger is fetch a User, filtered by username and profile type.

However, it seems that SOQL queries run as "Force.com - App Subscription"-typed users can't see the majority of the "User" or "Profile" tables -- based on some quick poking around, they seem to be unable to see any custom profiles or any users bearing a ProfileId that's a custom profile.

Throwing my SOQL query into a method contained in a "without sharing" class isn't doing the trick to get around this restriction, and "runAs()" is just for test classes ... what else might I try?

For context, I'm trying to have Apex take care of fetching User IDs by username so that some custom metadata records explaining which user is responsible for what work can be more readable for the humans maintaining the custom metadata than 18-digit User IDs would be.

This test should pass when I code getCertainTypesOfUserIdByIdRoot() correctly:

static testMethod void test() {
    Id profileIdToQuery = [select id, name from profile where name='Staff Member'][0].Id; // A custom profile
    User staffUser = new User(alias = 'su1', email='poipoivds289c8@example.com', emailencodingkey='UTF-8', lastname='miscStaff', languagelocalekey='en_US', localesidkey='en_US', profileid = profileIdToQuery, timezonesidkey='America/Los Angeles', username='poipoivds289c8@example.com');
    Id portalProfileId = [select Id, Name from Profile where name='Portal User'][0].Id; // Also a custom profile; Force.com-typed
    User portalUser = new User(alias = 'pu1', email='Xhv1f4C7Lt7vX56@example.com', emailencodingkey='UTF-8', lastname='miscPortal', languagelocalekey='en_US', localesidkey='en_US', profileid = portalProfileId, timezonesidkey='America/Los Angeles', username='Xhv1f4C7Lt7vX56@example.com');
    INSERT new List<User>{staffUser, portalUser}; {}
    Test.startTest();
    Test.stopTest();
    System.runAs(portalUser) {
        System.assert(![SELECT Id, Profile.Name, Username FROM User WHERE (Profile.Name LIKE 'Staff%' OR Profile.Name LIKE '%Admin%') AND Username LIKE '%@example.com%' AND Id=:staffUser.Id].isEmpty(),'Can\'t test getCertainTypesOfUserIdByIdRoot() because test user isn\'t among the types of profiles that method handles or doesn\'t have a username in our domain');
        System.assertEquals(staffUser.Id, UtilsNoShare.getCertainTypesOfUserIdByIdRoot('poipoivds289c8'), 'getCertainTypesOfUserIdByIdRoot() did not return expected User ID');
    }
}

Here's the code that I can't yet get to "see" all 200+ matching Users in its SOQL query when invoked from a "Force.com"-profiled User context and therefore can't get to return a non-null value to the test above:

private static Map<String, Id> userIDsByIdRootString = new Map<String, Id>();
public static Id getCertainTypesOfUserIdByIdRoot(String idRootString) {
    // Returns "NULL" if no User identified among "users of interest" by String passed through parameter
    if ( userIDsByIdRootString.isEmpty() ) { 
        for (User u: [SELECT Id, Username FROM User WHERE (Profile.Name LIKE 'Staff%' OR Profile.Name LIKE '%Admin%') AND Username LIKE '%@example.com%']) { 
            userIDsByIdRootString.put(u.Username.toLowerCase().replaceFirst('^(.*)@example\\.com.*$', '$1'), u.Id);
        }
    } // Lazy-populate userIDsByIdRootString
    if ( idRootString != NULL && userIDsByIdRootString.containsKey(idRootString.toLowerCase()) ) { 
        return userIDsByIdRootString.get(idRootString.toLowerCase());
    }
    return NULL;
}
0

I found the bug.

It's my 1st test; the "can I even run this test?" test that I once thought made sense, that's failing.

That SOQL query isn't wrapped in the protection of a "without sharing," so it fails.

If I move the 1st test outside the "runAs()," both tests pass. The design is just fine; I was just testing it wrong.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.