• Josh Packer 10
  • NEWBIE
  • 10 Points
  • Member since 2014

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 1
    Likes Given
  • 2
    Questions
  • 1
    Replies
We had to set up a custom auth provider and registration handler for our community. I was able to get the registration handler class and test completed.  Salesforce provided the auth provider class below, but no test.  I have been searching for ways to write a compatable test, with no luck.  

Please help.  Can somebody guide me on how to test this class?
 
global class FamilySearch implements Auth.AuthProviderPlugin {
    
    //You'll need to get this from your Auth Provider AFTER it's been created.  (we should provide this at runtime, but its a gap)
    public String redirectUrl = 'https://test.salesforce.com/services/authcallback/00D4C0000000OrwUAE/FamilySearch';
    
    global String getCustomMetadataType() {
        return 'FamilySearch__mdt';
    }
    
    global PageReference initiate(Map<string,string> authProviderConfiguration, String stateToPropagate) {
        System.debug(authProviderConfiguration);
        String authUrl = authProviderConfiguration.get('AuthorizationURL__c');
        String clientID = authProviderConfiguration.get('ClientID__c');
        
        String authZ = authUrl + '?response_type=code&client_id='+ clientID +'&scope=openid%20profile%20email&redirect_uri='+ redirectUrl + '&state=' + stateToPropagate;
        return new PageReference(authZ);
    }
    
    global Auth.AuthProviderTokenResponse handleCallback(Map<string,string> authProviderConfiguration, Auth.AuthProviderCallbackState state ) {
        
        String clientID = authProviderConfiguration.get('ClientID__c');
        String tokenURL = authProviderConfiguration.get('TokenURL__c');
        
        Map<String,String> queryParams = state.queryParameters;
        String code = queryParams.get('code');
        String sfdcState = queryParams.get('state');
        
        HttpRequest req = new HttpRequest();
        String url = tokenURL;
        req.setEndpoint(url);
        req.setHeader('Accept','application/json');
        req.setMethod('POST');
        String body = 'code=' + code + '&client_id=' + clientID + '&grant_type=authorization_code&redirect_uri='+ EncodingUtil.urlEncode(redirectUrl, 'UTF-8');
        req.setBody(body);
        
        Http http = new Http();
        HTTPResponse res = http.send(req);
        String responseBody = res.getBody();
        
        System.debug(res.getStatusCode() + ': ' + responseBody);
        
        String token = null;
        if (res.getStatusCode() == 200) {
            
            JSONParser parser = JSON.createParser(responseBody);
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME)){
                    String fieldName = parser.getText();
                    parser.nextToken();
                    if(fieldName == 'access_token') {
                        token = parser.getText();
                    } 
                }
            }
        } //TODO - error handling
        return new Auth.AuthProviderTokenResponse('FamilySearch', token, 'refreshToken', sfdcState);
        
        
    }
    
    
    global Auth.UserData  getUserInfo(Map<string,string> authProviderConfiguration, Auth.AuthProviderTokenResponse response) { 
        //Here the developer is responsible for constructing an Auth.UserData object
        String token = response.oauthToken;
        HttpRequest req = new HttpRequest();
        String userInfo = authProviderConfiguration.get('UserInfoURL__c');
        req.setHeader('Authorization', 'Bearer ' + token);
        req.setEndpoint(userInfo);
        req.setMethod('GET');
        
        Http http = new Http();
        HTTPResponse res = http.send(req);
        String responseBody = res.getBody();
        
        String[] jwtParts = responseBody.split('\\.');
        String header = EncodingUtil.base64Decode(jwtParts[0]).toString();
        String payload = EncodingUtil.base64Decode(jwtParts[1]).toString();
        
        Blob signature = Crypto.generateMac('hmacSHA256', Blob.valueOf(jwtParts[0] + '.' + jwtParts[1]), Blob.valueOf('698DF3C23215E7FF28AE5368343CD0F64743D2036AE315791E5052C3645D0982'));
        
        String output = encodingUtil.base64Encode(signature);
        output = output.replace('+', '-');
        output = output.replace('/', '_');
        while ( output.endsWith('=')){
            output = output.subString(0,output.length()-1);
        }
       
        if(jwtParts[2] == output){
            
            
            String sub;
            String email;
            Boolean email_verified;
            String locale;
            String family_name;
            String given_name;
            
            JSONParser parser = JSON.createParser(payload);
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME)){
                    String fieldName = parser.getText();
                    parser.nextToken();
                    if (fieldName == 'sub') {
                        sub = parser.getText();
                    } else if (fieldName == 'email') {
                        email = parser.getText();
                    } else if (fieldName == 'email_verified') {
                        email_verified = parser.getBooleanValue();
                    } else if (fieldName == 'locale') {
                        locale = parser.getText();
                    } else if (fieldName == 'family_name') {
                        family_name = parser.getText();
                    } else if (fieldName == 'given_name') {
                        given_name = parser.getText();
                    }
                }
            }


            Map<String,String> attrMap = new Map<String,String>();
            
            return new Auth.UserData(sub, given_name, family_name, given_name + ' ' + family_name, email,'', email, locale, 'FamilySearch', null, attrMap);

        }
        // TODO - proper error handling
        return null;
    }
    
    
}

 
I have nearly completed a registration handler for our community that uses person accounts, but need a little help finishing up.  Below you can see my registration handler class, along with the corresponding test.  
  • Since this is for a community, do I have to specify the Salesforce License type of Customer Community Login user when creating the user?
  • I tried to create a person account, then capture the ContactId to use when creating the user, does that look complete?
Also, both the class and test save, and when the test runs, it says that it covers 100% of the class code, but it fails.  I get the following error:

System.DmlException: Update failed. First exception on row 0 with id 0054C000000M2VbQAK; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Account: []
Class.FamilySearchRegHandler.updateUser: line 52, column 1
Class.FamilySearchRegHandler_Test.testCreateAndUpdateUser: line 20, column 1

I found this suggestion, but I am not quite sure how to implement it.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dml_non_mix_sobjects_test_methods.htm

Any suggestions?
 
global class FamilySearchRegHandler implements Auth.RegistrationHandler{

global User createUser(Id portalId, Auth.UserData data){

    RecordType personAccountRecordType =  [SELECT Id FROM RecordType WHERE Name = 'Person Account' and SObjectType = 'Account'];
    Account newPersonAccount = new Account();
        // for person accounts we can not update the Name field instead we have to update the FirstName and LastName individually
        newPersonAccount.FirstName = data.firstName;
        newPersonAccount.LastName =  data.lastName;
        newPersonAccount.RecordType = personAccountRecordType;
        insert newPersonAccount;
    	
    	Contact c = [SELECT AccountId,Id FROM Contact WHERE AccountId = :newPersonAccount.Id];   
    
        //TODO: Customize the username and profile. Also check that the username doesn't already exist and
        //possibly ensure there are enough org licenses to create a user. Must be 80 characters or less.
        User u = new User();
        Profile p = [SELECT Id FROM profile WHERE name='Community - FS - Public'];
        u.username = data.username + '.fscommunity';
        u.email = data.email;
        u.lastName = data.lastName;
        u.firstName = data.firstName;
        String alias = data.username;
        //Alias must be 8 characters or less
        if(alias.length() > 8) {
            alias = alias.substring(0, 8);
        }
        u.alias = alias;
        u.languagelocalekey = UserInfo.getLocale();
        u.localesidkey = UserInfo.getLocale();
        u.emailEncodingKey = 'UTF-8';
        u.timeZoneSidKey = 'America/Denver';
        u.profileId = p.Id;
        u.contactId = c.Id;
        return u;
    } 


global void updateUser(Id userId, Id portalId, Auth.UserData data){
    User u = new User(id=userId);
    //TODO: Customize the username. Must be 80 characters or less.
    //u.username = data.username + '.fscommunity';
    u.email = data.email;
    u.lastName = data.lastName;
    u.firstName = data.firstName;
    //String alias = data.username;
    //Alias must be 8 characters or less
    //if(alias.length() > 8) {
        //alias = alias.substring(0, 8);
    //}
    //u.alias = alias;
    update(u);
}
}
 
@isTest
private class FamilySearchRegHandler_Test {
static testMethod void testCreateAndUpdateUser() {
    FamilySearchRegHandler handler = new FamilySearchRegHandler();
    Auth.UserData sampleData = new Auth.UserData('testId', 'testFirst', 'testLast',
        'testFirst testLast', 'testuser@example.org', null, 'testuserlong@fs.org', 'en_US', 'facebook',
        null, new Map<String, String>{'language' => 'en_US'});
    User u = handler.createUser(null, sampleData);
    System.assertEquals('testuserlong@fs.org.fscommunity', u.userName);
    System.assertEquals('testuser@example.org', u.email);
    System.assertEquals('testLast', u.lastName);
    System.assertEquals('testFirst', u.firstName);
    System.assertEquals('testuser', u.alias);
    insert(u);
    String uid = u.id;
    
    sampleData = new Auth.UserData('testNewId', 'testNewFirst', 'testNewLast',
        'testNewFirst testNewLast', 'testnewuser@example.org', null, 'testnewuserlong', 'en_US', 'facebook',
        null, new Map<String, String>{});
    handler.updateUser(uid, null, sampleData);
    
    User updatedUser = [SELECT userName, email, firstName, lastName, alias FROM user WHERE id=:uid];
    System.assertEquals('testnewuserlong@salesforce.com', updatedUser.userName);
    System.assertEquals('testnewuser@example.org', updatedUser.email);
    System.assertEquals('testNewLast', updatedUser.lastName);
    System.assertEquals('testNewFirst', updatedUser.firstName);
    System.assertEquals('testnewu', updatedUser.alias);
}
}

 
I have nearly completed a registration handler for our community that uses person accounts, but need a little help finishing up.  Below you can see my registration handler class, along with the corresponding test.  
  • Since this is for a community, do I have to specify the Salesforce License type of Customer Community Login user when creating the user?
  • I tried to create a person account, then capture the ContactId to use when creating the user, does that look complete?
Also, both the class and test save, and when the test runs, it says that it covers 100% of the class code, but it fails.  I get the following error:

System.DmlException: Update failed. First exception on row 0 with id 0054C000000M2VbQAK; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Account: []
Class.FamilySearchRegHandler.updateUser: line 52, column 1
Class.FamilySearchRegHandler_Test.testCreateAndUpdateUser: line 20, column 1

I found this suggestion, but I am not quite sure how to implement it.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dml_non_mix_sobjects_test_methods.htm

Any suggestions?
 
global class FamilySearchRegHandler implements Auth.RegistrationHandler{

global User createUser(Id portalId, Auth.UserData data){

    RecordType personAccountRecordType =  [SELECT Id FROM RecordType WHERE Name = 'Person Account' and SObjectType = 'Account'];
    Account newPersonAccount = new Account();
        // for person accounts we can not update the Name field instead we have to update the FirstName and LastName individually
        newPersonAccount.FirstName = data.firstName;
        newPersonAccount.LastName =  data.lastName;
        newPersonAccount.RecordType = personAccountRecordType;
        insert newPersonAccount;
    	
    	Contact c = [SELECT AccountId,Id FROM Contact WHERE AccountId = :newPersonAccount.Id];   
    
        //TODO: Customize the username and profile. Also check that the username doesn't already exist and
        //possibly ensure there are enough org licenses to create a user. Must be 80 characters or less.
        User u = new User();
        Profile p = [SELECT Id FROM profile WHERE name='Community - FS - Public'];
        u.username = data.username + '.fscommunity';
        u.email = data.email;
        u.lastName = data.lastName;
        u.firstName = data.firstName;
        String alias = data.username;
        //Alias must be 8 characters or less
        if(alias.length() > 8) {
            alias = alias.substring(0, 8);
        }
        u.alias = alias;
        u.languagelocalekey = UserInfo.getLocale();
        u.localesidkey = UserInfo.getLocale();
        u.emailEncodingKey = 'UTF-8';
        u.timeZoneSidKey = 'America/Denver';
        u.profileId = p.Id;
        u.contactId = c.Id;
        return u;
    } 


global void updateUser(Id userId, Id portalId, Auth.UserData data){
    User u = new User(id=userId);
    //TODO: Customize the username. Must be 80 characters or less.
    //u.username = data.username + '.fscommunity';
    u.email = data.email;
    u.lastName = data.lastName;
    u.firstName = data.firstName;
    //String alias = data.username;
    //Alias must be 8 characters or less
    //if(alias.length() > 8) {
        //alias = alias.substring(0, 8);
    //}
    //u.alias = alias;
    update(u);
}
}
 
@isTest
private class FamilySearchRegHandler_Test {
static testMethod void testCreateAndUpdateUser() {
    FamilySearchRegHandler handler = new FamilySearchRegHandler();
    Auth.UserData sampleData = new Auth.UserData('testId', 'testFirst', 'testLast',
        'testFirst testLast', 'testuser@example.org', null, 'testuserlong@fs.org', 'en_US', 'facebook',
        null, new Map<String, String>{'language' => 'en_US'});
    User u = handler.createUser(null, sampleData);
    System.assertEquals('testuserlong@fs.org.fscommunity', u.userName);
    System.assertEquals('testuser@example.org', u.email);
    System.assertEquals('testLast', u.lastName);
    System.assertEquals('testFirst', u.firstName);
    System.assertEquals('testuser', u.alias);
    insert(u);
    String uid = u.id;
    
    sampleData = new Auth.UserData('testNewId', 'testNewFirst', 'testNewLast',
        'testNewFirst testNewLast', 'testnewuser@example.org', null, 'testnewuserlong', 'en_US', 'facebook',
        null, new Map<String, String>{});
    handler.updateUser(uid, null, sampleData);
    
    User updatedUser = [SELECT userName, email, firstName, lastName, alias FROM user WHERE id=:uid];
    System.assertEquals('testnewuserlong@salesforce.com', updatedUser.userName);
    System.assertEquals('testnewuser@example.org', updatedUser.email);
    System.assertEquals('testNewLast', updatedUser.lastName);
    System.assertEquals('testNewFirst', updatedUser.firstName);
    System.assertEquals('testnewu', updatedUser.alias);
}
}

 
Is there a way to use the Crypto.signWithCertificate() method using SHA-512?  We need to be able to sign a string using SHA-512, not any of the lower algorithms.

Several of the other Crypto methods support SHA-512, but the documentation (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_crypto.htm) does not list 512, nor will it work when we tried it in our code:

Blob signedSecret = Crypto.signWithCertificate('SHA-512', blobSecret, certificateName);

Suggestions?