Storing secrets or passwords using manage package in Salesforce – Video

Designing application to store passwords or secrets in Salesforce using Manage packages

Salesforce supports Encrypted Field out of the box on standard or custom objects. However, users with appropriate profile or permission set can easily view content of those fields. If we want to store secure information like passwords (of external systems), then there is no direct way to achieve this. If we think about passwords, no one should be able to view it. If we forget password, then instead of system sending old password, it forces us to reset it.

We will create same behaviour in Salesforce where stored password cannot be viewed by anyone.

Storing Passwords in Salesforce
Storing Passwords in Salesforce

We will need a developer org with any namespace. In my case, namespace is “Livecoding”. Namespace can be created from “Setup | Build | Create | Packages | Developer Settings | Edit”. We need namespace so that manage package can be created. Idea is to have a protected custom setting. Protected custom setting can only be accessed in manage package and we will create global class, which can be used in subscriber (Client) org to perform various operations related to storing and getting passwords.

I have created custom setting by name “Livecoding__secrets__c” (Livecoding is namespace added by default in API name). Field of type text is created to store password by name “Livecoding__Password__c”.

To perform various operations, below global class is created with three methods:

/**
 * 	@Author			:	Jitendra Zaa
 * 	@Date			:	13-Feb-2016
 * 	@Description	:	Various operations to be performed on passwords	
 * 
 * */
global class Operations {
    
    /**
     * To store username and secret, use this method
     * */
    global static void addSecrets(String username, String password){
        
        Livecoding__Secrets__c obj = new Livecoding__Secrets__c(name = username, Livecoding__Password__c= password);
        insert obj;
    }

	/**
	 * This method checks if password entered is correct or not 
	 * */    
    global static boolean isPasswordCorrect(String username, String password)
    { 
        //read custom setting using SOQL
        List<Livecoding__Secrets__c> lstRecords = [SELECT ID FROM Livecoding__Secrets__c WHERE Name = :username AND  Livecoding__Password__c =: password ];
        
        if(lstRecords.isEmpty())
        {
            return false;
        }
        return true;
    }
    
    /**
     * Reset password of existing user 
     * */
    global static boolean resetPassword(String username, String password){
        
        //read custom setting without SOQL
        Livecoding__Secrets__c userRecord = Livecoding__Secrets__c.getValues(username);
         
        if(userRecord == null)
        {
            return false;
        }
        
        userRecord.Livecoding__Password__c = password ;
        update userRecord; 
        
        return true;
    } 
}

To create manage package, we must need to have minimum 75% of test coverage. Below test class gives 100% test coverage to our manage package.

/**
 * 	@Author			:	Jitendra Zaa
 * 	@Date			:	13-Feb-2016
 * 	@Description	:	Test class for "Operations"
 * */

@isTest
public class OperationsTest {
	
    
    @testSetup 
    static void setupCustomSetting() {
        Livecoding__Secrets__c obj = new Livecoding__Secrets__c(name = 'abc@livecoding.tv', Livecoding__Password__c= 'sample1');
        insert obj;
	}
    
    
    static testMethod void addSecrets_Test(){
        Operations.addSecrets('xyz@livecoding.tv','xyz');
        
        List<Livecoding__Secrets__c> lstCustomSettings = Livecoding__Secrets__c.getAll().values();
        System.assertEquals(lstCustomSettings.size(), 2);  
    }
    
    static testMethod void isPasswordCorrect_No_Test(){
        boolean retValue = Operations.isPasswordCorrect('abc@livecoding.tv', 'password') ;
        System.assertNotEquals(retValue, true) ;        
    }
    
    static testMethod void isPasswordCorrect_Yes_Test(){
        boolean retValue = Operations.isPasswordCorrect('abc@livecoding.tv', 'sample1') ;
        System.assertEquals(retValue, true) ;        
    }
    
    static testMethod void resetPassword_No_Test(){
        boolean retValue = Operations.resetPassword('abc@livecoding.com', 'password') ;
        System.assertEquals(retValue, false) ;  
    }
    
    static testMethod void resetPassword_Yes_Test(){
        boolean retValue = Operations.resetPassword('abc@livecoding.tv', 'password') ;
        System.assertEquals(retValue, true) ;  
        
        retValue = Operations.isPasswordCorrect('abc@livecoding.tv', 'password') ;
        System.assertEquals(retValue, true) ;    
    }    
}

In above test class, we have used @testSetup method. It is used to create a common test records being used by all testmethod. This method is implicitly called before each testmethod in class.

It’s time to create Manage package now. Navigate to “Setup | Build | Packages | New”. This manage package will have below three components

  1. Protected public setting
  2. Global Apex class to perform operations on Custom Setting
  3. Test class for Apex to cover minimum 75%

If we need to store password in any org, we can install above manage package in that org. To perform operations related to password, we can use any of three global methods available in manage package.

Other resources:

Related posts