Stuck creating Test Class for Custom Just In Time Handler (Auth.SamlJitHandler)
I am doing a PoC and have implemented a custom handler for JIT – see below
global class CustomJITRegHandler implements Auth.SamlJitHandler {
private class JitException extends Exception{} // Exception handling
/****************************************************************************************
* @description Main JIT
* @params List of SAML / SSO Params
* @returns void
*****************************************************************************************/
private void handleJit(boolean create, User u, Id samlSsoProviderId, Id communityId, Id portalId,
String federationIdentifier, Map<String, String> attributes, String assertion) {
// Enter Community User creation flow
if(communityId != null || portalId != null) {
handlerUser(attributes, u, federationIdentifier);
}
}
/****************************************************************************************
* @description Facility the user creation JIT/SSO flow
* @params Map of Attribute, Instance of User, String federationId
* @returns void
*****************************************************************************************/
private void handlerUser(Map<String,String> attributes, User u, String federationIdentifier){
// Extract additional attributes from Map
String accountFederatedId = attributes.get('AccountFederateId');
String lastName = attributes.get('LastName');
String email = attributes.get('Email');
// SOQL to find existing Contact
List<Contact> contacts = [SELECT Id,
FirstName,
LastName,
Email,
Account.Account_Support_Alias__c
FROM Contact
WHERE
Email =: email AND
LastName =: lastName AND
Account.Account_Support_Alias__c =: accountFederatedId LIMIT 1];
// Existing contact is found
if (!contacts.isEmpty()){
// Create Community User for existing contact
u = createNewUser(u, contacts[0], federationIdentifier);
} else {
//Find Account
List<Account> accounts = [SELECT Id,
Name FROM Account
WHERE Account_Support_Alias__c =: accountFederatedId];
//Account is found
if (!accounts.isEmpty()){
// Create Contact
Contact con = createNewContact(accounts, attributes);
// Create User
if (con!=null) {
u = createNewUser(u, con, federationIdentifier);
}
} else{
// @todo implement custom exception message
}
}
}
/****************************************************************************************
* @description create new contact record based on attributes
* @params List of Accounts, Map of Attributes
* @returns Instance of Contact object
* @todo Implement full Contact field mapping
*****************************************************************************************/
public contact createNewContact(List<Account> accts, Map<String,String> attributes){
Contact c = new Contact(
FirstName = 'SSO',
LastName = attributes.get('LastName'),
Email= attributes.get('Email'),
AccountId = accts[0].Id);
insert c;
return c;
}
/****************************************************************************************
* @description create new contact record based on attributes
* @params User object, Contact object, String
* @returns Instance of User object
* @todo Implement full User field mapping
****************************************************************************************/
public user createNewUser(user u, Contact con, String federationIdentifier) {
// Create Community User
u.IsActive = true;
u.Username = con.Email + 'BT';
u.Email = con.Email + 'BT';
u.FirstName = con.FirstName;
u.LastName = con.LastName;
u.CommunityNickname = 'nickSSo'+generateRandomString(6);
u.ContactId = con.Id;
u.Alias = 'BT';
u.TimeZoneSidKey = 'America/Phoenix'; // Required
u.LocaleSidKey = 'en_US'; // Required
u.EmailEncodingKey = 'ISO-8859-1'; // Required
u.LanguageLocaleKey = 'en_US'; // Required
u.ProfileId = '00e0Y000000Y5mu'; // Insert Profile Id from 7SBT org 00e0Y000001qX7Y
u.FederationIdentifier = federationIdentifier;
try {
insert u;
} catch (DmlException e){
System.debug('**** DMLException **** ' + e);
}
return u;
}
/****************************************************************************************
* @description Required method for Auth.SamlJitHandler interface
* @params Id SamlSsoProviderId
* @returns Instance of User
****************************************************************************************/
global User createUser(Id samlSsoProviderId,
Id communityId,
Id portalId,
String federationIdentifier,
Map<String, String> attributes,
String assertion) {
User u = new User();
handleJit(true, u, samlSsoProviderId, communityId, portalId,
federationIdentifier, attributes, assertion);
return u;
}
/****************************************************************************************
* @description Required method for Auth.SamlJitHandler interface
* @params Id SamlSsoProviderId
* @returns Void
* @todo Implement any existing user updates (e.g. change of active flag...)
****************************************************************************************/
global void updateUser(Id userId,
Id samlSsoProviderId,
Id communityId,
Id portalId,
String federationIdentifier,
Map<String, String> attributes,
String assertion) {
system.debug('-----userId---------'+userId);
User u = [SELECT Id FROM User WHERE Id=:userId];
// We don't need to update the User for the POC
}
/****************************************************************************************
* @description Utility method for generating ramdom string
* @params Integer length
* @returns A random String of text
****************************************************************************************/
public static String generateRandomString(Integer len) {
final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
String randStr = '';
while (randStr.length() < len) {
Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length());
randStr += chars.substring(idx, idx+1);
}
return randStr;
}
}
I am completely stuck on the Test Class front – here is what I have
@isTest(SeeAllData=false)
private class BTCustomJITRegHandlerTest {
@testSetup static void testData(){
Account a = (Account)TestFactory.createSObject(new Account());
insert a;
Contact c = (Contact)TestFactory.createSObject(new Contact (AccountId = a.Id));
insert c;
// @TODO insert test users
}
@isTest static void testCommunityKnown(){
Account testAccount = [select Id, Name from Account];
System.debug('**** testAccount ******' + testAccount);
Contact testContact = [select Id, FirstName, LastName, AccountId, Account.Name from Contact];
System.debug('**** testContact ******' + testContact);
BTCustomJITRegHandler handler = new BTCustomJITRegHandler();
Id samlSsoProviderId = '0LE0Y0000008Wkj'; //@TODO make dynamic
Id communityId = '0DB0Y0000000WEsWAM'; //@TODO make dynamic
Id portalId = '0DB0Y0000000WEsWAM';
String federationIdentifier = 'test';
Map<String, String> attributes = null;
String assertion = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIERlc3RpbmF0aW9uPSJodHRwczovLzdzYnQtcG9jLmZvcmNlLmNvbS9obWUvbG9naW4/c289MDBEMFkwMDAwMDM0QjFoIiBJRD0iXzZiM2E1MGE0LTdhZGE4NWRiIiBJc3N1ZUluc3RhbnQ9IjIwMTctMDktMjRUMTI6NDU6MjYuNjgyWiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIi8+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2RzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNfNmIzYTUwYTQtN2FkYTg1ZGIiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjxlYzpJbmNsdXNpdmVOYW1lc3BhY2VzIHhtbG5zOmVjPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIFByZWZpeExpc3Q9InhzIi8+PC9kczpUcmFuc2Zvcm0+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5PKzZsSmhCbUtKZU8ra1V3bDBycmxrNTFEbzA9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPmlOa0JLVStOTzVUbWluQmY4WDlkdlhMc3hYODBVL1JDMjlzaVczbWJpTUgvakJFUllSQVUvQT09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlEMHpDQ0E1R2dBd0lCQWdJRUYvdUZJVEFMQmdjcWhrak9PQVFEQlFBd2dib3hDekFKQmdOVkJBWVRBbFZUTVFzd0NRWURWUVFJCkV3SkRRVEVXTUJRR0ExVUVCeE1OVTJGdUlFWnlZVzVqYVhOamJ6RVNNQkFHQTFVRUNoTUpRWGhwYjIwZ1UxTlBNVkV3VHdZRFZRUUwKRTBoR1QxSWdSRVZOVDA1VFZGSkJWRWxQVGlCUVZWSlFUMU5GVXlCUFRreFpMaUJFVHlCT1QxUWdWVk5GSUVaUFVpQlFVazlFVlVOVQpTVTlPSUVWT1ZrbFNUMDVOUlU1VVV5NHhIekFkQmdOVkJBTVRGa0Y0YVc5dElFUmxiVzhnUTJWeWRHbG1hV05oZEdVd0hoY05NVFF3Ck5qSXdNRFF6TURJM1doY05OREV4TVRBMU1EUXpNREkzV2pDQnVqRUxNQWtHQTFVRUJoTUNWVk14Q3pBSkJnTlZCQWdUQWtOQk1SWXcKRkFZRFZRUUhFdzFUWVc0Z1JuSmhibU5wYzJOdk1SSXdFQVlEVlFRS0V3bEJlR2x2YlNCVFUwOHhVVEJQQmdOVkJBc1RTRVpQVWlCRQpSVTFQVGxOVVVrRlVTVTlPSUZCVlVsQlBVMFZUSUU5T1RGa3VJRVJQSUU1UFZDQlZVMFVnUms5U0lGQlNUMFJWUTFSSlQwNGdSVTVXClNWSlBUazFGVGxSVExqRWZNQjBHQTFVRUF4TVdRWGhwYjIwZ1JHVnRieUJEWlhKMGFXWnBZMkYwWlRDQ0FiZ3dnZ0VzQmdjcWhrak8KT0FRQk1JSUJId0tCZ1FEOWYxT0JIWFVTS1ZMZlNwd3U3T1RuOWhHM1VqenZSQURESGorQXRsRW1hVVZkUUNKUisxazlqVmo2djhYMQp1akQyeTV0VmJOZUJPNEFkTkcveVptQzNhNWxRcGFTZm4rZ0VleEFpd2srN3FkZit0OFliK0R0WDU4YW9waFVQQlB1RDl0UEZIc01DCk5WUVRXaGFSTXZaMTg2NHJZZGNxNy9JaUF4bWQwVWdCeHdJVkFKZGdVSThWSXd2TXNwSzVncUxyaEF2d1dCejFBb0dCQVBmaG9JWFcKbXozZXk3eXJYRGE0VjdsNWxLKzcranJxZ3ZsWFRBczlCNEpuVVZsWGpyclVXVS9tY1FjUWdZQzBTUlp4SStoTUtCWVR0ODhKTW96SQpwdUU4Rm5xTFZIeU5LT0Nqcmg0cnM2WjFrVzZqZnd2NklUVmk4ZnRpZWdFa084eWs4YjZvVVpDSnFJUGY0VnJsbndhU2kyWmVnSHRWCkpXUUJURHYrejBrcUE0R0ZBQUtCZ1FDWHIxbXA0VXZCeVk2ZEdiRE95cTN3TXM2TzdNQ3htRWtVMngzMkFrRXA2czdYZml5M01Zd0sKd1pRNHNMNEJtUVl6WjdRT1hQUDhkS2dyS0RRS0xrOXRYV09ndklvT0NpTkFkUURZbFJtMnNZZ3JJMlNVY3lNMWJLRHFMd0REOFo1TwpvTGV1UUF0Z01mQXEvZjFDNm5SRVdyUXVkUHhPd2FvTmRIa1ljUiswNjZNaE1COHdIUVlEVlIwT0JCWUVGRTJKQWM5N3dmSEs1YjQyCm5LYkFObjRTTWNxY01Bc0dCeXFHU000NEJBTUZBQU12QURBc0FoUitDanZwOFV3TmdLSGZ4MlBXSm9SaTAvMXE4QUlVTmhUWFdsR3oKSjNTZEJsZ1JzZEZnS3lGdGN4RT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDJwOlN0YXR1cz48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzUyZTc4OGFhLTEwZmMyMTgyIiBJc3N1ZUluc3RhbnQ9IjIwMTctMDktMjRUMTI6NDU6MjYuNjgxWiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyLz48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQiPmFnZW50Z2lsbDc0QGdtYWlsLmNvbTwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxNy0wOS0yNFQxMjo0NjoyNi42ODF
aIiBSZWNpcGllbnQ9Imh0dHBzOi8vN3NidC1wb2MuZm9yY2UuY29tL2htZS9sb2dpbj9zbz0wMEQwWTAwMDAwMzRCMWgiLz48L3NhbWwyOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sMjpTdWJqZWN0PjxzYW1sMjpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxNy0wOS0yNFQxMjo0NToyNi42ODFaIiBOb3RPbk9yQWZ0ZXI9IjIwMTctMDktMjRUMTI6NDY6MjYuNjgxWiI+PHNhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWwyOkF1ZGllbmNlPmh0dHBzOi8vc2FtbC5zYWxlc2ZvcmNlLmNvbTwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE3LTA5LTI0VDEyOjQ1OjI2LjY4MVoiPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOnVuc3BlY2lmaWVkPC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJzc29TdGFydFBhZ2UiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dW5zcGVjaWZpZWQiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5odHRwOi8vYXhpb21zc28uaGVyb2t1YXBwLmNvbS9SZXF1ZXN0U2FtbFJlc3BvbnNlLmFjdGlvbjwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJsb2dvdXRVUkwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dW5zcGVjaWZpZWQiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIi8+PC9zYW1sMjpBdHRyaWJ1dGU+PHNhbWwyOkF0dHJpYnV0ZSBOYW1lPSJvcmdhbml6YXRpb25faWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dW5zcGVjaWZpZWQiPjxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj4wMEQwWTAwMDAwMzRCMWg8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0icG9ydGFsX2lkIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVuc3BlY2lmaWVkIj48c2FtbDI6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+MERCMFkwMDAwMDAwV0VzV0FNPC9zYW1sMjpBdHRyaWJ1dGVWYWx1ZT48L3NhbWwyOkF0dHJpYnV0ZT48L3NhbWwyOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4='; //@TODO insert test SAML assertion XML
handler.createUser(samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
}
}
I have no idea how I need to mock up the assertion and test. I assume there are some methods User.Data for Auth.SamUtiHandler which I need to call which before calling the methods or do something else?
Test class is giving System.NullPointerException: Attempt to de-reference a null object
Any advice / steering in right direction much welcome.
Best Answer
First off, the test is failing because your
attributes
Map is null.The trick with testing these sorts of things is the only test the code you've written, don't try testing things that are not there or standard Salesforce functionality (if you don't have to).
Your
createUser
method doesn't even make use of thesamlSsoProviderId
,communityId
andportalId
variables, so don't bother making them real Id, dummy Ids will do, e.g.0LE000000000000' for
samlSsoProviderId`.You also don't need to worry about
User.Data
orAuth.SamlJitHandler
because you don't need to test what they do.Here is a basic outline of what the test class could look like with a single test for your
createUser
method (obviously it needs more tests methods written).