Tag Archives: SFDC

Login from Salesforce to Salesforce using Authentication Provider

We can login from many ways to Salesforce instance using Microsoft Access, GitHub, Janrain, Google, Facebook, Linked, Twitter and also from other Salesforce Org. In this article I’ll describe, how we can login from one Salesforce Org to another Salesforce Org using Authentication provider.

In this article I’m using two Salesforce Org one is “Org A” and another one is “Org B”. Here I’ll login from “Org B” with “Org A” credentials, and “Org B” will authenticate user and will act as source organization for login.

Step 1:
Enable my domain in your “Org B”. This step is important so that it will display all available Authentication provider for that Salesforce instance.

To enable my domain navigate to “Setup | Administration Setup | Domain Management | My Domain.
Enter a name for your domain after https:// and click Check Availability.
If the name is available, click the Terms and Conditions check box, then click Register Domain.

Step 2:
Here we need “Org A” User to be logged in from “Org B”. So, “Org B” should be able to identify that request is coming from “Org A”. Therefore Connected App needs to be created in “Org A”.

Create Connected App in “Org A”, Navigate to “Setup | Build | Create | Apps | Connected Apps” and click on New. Provide All information except “Callback URL”. We will comeback again on this step later to provide Callback URL.

“Callback URL” is the URL where “Authentication Provider instance” should return after providing access. Even if somehow “Consumer Key” and “Consumer Secret” is compromised, it will return to Callback URL which is your application.

Connected App also has “Consumer Key” and “Consumer Secret” which is equivalent to “username” and “password” for that App. Once you save this Connected app, it will provide “Consumer Key” and “Consumer Secret”, we need this information in next step.

Step 3:
Create Authorization Provider in “Org B”, Navigate to “Setup | Administer | Security Controls | Auth. Providers | Create New”.

Use the following settings:
Provider Type: Salesforce
Name: Salesforce Auth Provider
URL Suffix: Will auto populate as per the Name field
Consumer Key: Provide the “Org A” created connected App consumer key
Consumer Secret: Put the “Org A” created connected App consumer Secret key
Authorize Endpoint URL: https://OrgAinstance/services/oauth2/token
Token Endpoint URL: https://OrgAinstance/services/oauth2/authorize
Default Scopes: The value should be “refresh_token full”. “refresh_token” and “full” should be separated by space.
Registration Handler: Click on “Automatically create a registration handler template”, it will generate one apex class. We can modify that class as per our requirement, here the class name is “UserRegistrationHandler”.
Execute Registration As: Then Select User who should be used to execute this Apex class when user tries to login.
Icon URL: You can choose one sample icons

Here is the sample class “UserRegistrationHandler”.
This class is in “Org B”. When the “Org A” user will login with “Org A” credentials, then this class will execute. And will check, if the user is not exist then it will create a new user, otherwise it will update that user information.

Note: It is a sample class, you can modify it as per your business requirement.

/**
*   @Author         :   Biswajeet Samal
*   @Date           :   03/01/2017
*   @Description    :   This class includes the basics for an User Registration
**/

global class UserRegistrationHandler implements Auth.RegistrationHandler{

    global boolean canCreateUser(Auth.UserData data) {

        if(data != null && data.email != null && data.lastName != null && data.firstName != null){
            return true;
        }
        return false;
    }

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

        //Check that User doesn't already exist
        if(!canCreateUser(data)) {
            if(data.email != null){
                User u = [Select Id, Username From User Where email =: data.email];
                return u;
            } 
            else {
                return null;
            }
        }
        
        //The user is authorized, so create their Salesforce User
        //Here I'm creating 'Chatter Free User' Profile User
        User u = new User();
        Profile p = [SELECT Id FROM profile WHERE name = 'Chatter Free User'];
        
        //Customize the Username as per your required format.
        //Also check that the Username doesn't already exist.
        //Possibly ensure there are enough org licenses to create a user. Must be 80 characters or less.
        u.username = 'Test' + data.username; 
        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/Los_Angeles';
        u.profileId = p.Id;
        return u;
    }

    global void updateUser(Id userId, Id portalId, Auth.UserData data){
        User u = new User(id=userId);
        u.email = data.email;
        u.lastName = data.lastName;
        u.firstName = data.firstName;
        update(u);
    }
}

Step 4:
Set Callback URL in Connected App, Once you save “Auth. Provider” in “Org B”, it will provide you list of URL as shown in below image. Copy Callback URL from “Org B” and edit Connected App in “Org A” and set this URL.

Step 5:

Navigate to “Setup | Administer | Domain Management | My Domain | Authentication Configuration | Edit”.
This page will provide, list of all Authentication providers like LinkedIn, Facebook, Google, In our case Salesforce.

Now logout and navigate to Login page specific to your instance and you should be able to see all Authentication provider buttons for your instance. Pleas note that, Authentication provider button will not appear on “https://login.salesforce.com” page, it has to be Mydomain login URL.

This is sample of Login page, how it will look like.

Difference between Process Builder and Workflow in Salesforce

Process builder allows you to do the below actions:

  • Create a new record.
  • Update any related record.
  • Quick action to create a record, update a record, or log a call.
  • Launch a flow.
  • Send an email
  • Post to Chatter.
  • Submit for approval
  • Call apex methods
  • But the process builder doesn’t support outbound messages.

Workflows can only handle four types of actions:

  • Create Task.
  • Update Field.
  • Email Alert.
  • Outbound Message.

Best Practice for Test Classes in Salesforce

  • To deploy to production at-least 75% code coverage is required, But your focus shouldn’t be on the percentage of code that is covered. Instead, you should make sure that every use case of your application is covered, including positive and negative cases, as well as bulk and single records. This should lead to 75% or more of your code being covered by unit tests.
  • Test class must start with @isTest annotation if class version is more than 25.
  • @isTest annotation with test method  is equivalent to testMethod keyword.
  • Test class and method default access is private ,no need to add access specifier.
  • Classes with @isTest annotation can’t be a interface or enum.
  • Test method code can’t be invoked by non test request.
  • Stating with salesforce API 28.0 test method can not reside inside non test classes.
  • Always use @testSetup method dedicated to create test records for that class. This is newly added annotation and very powerful.
  • If possible Don’t use seeAllData=true, Create your Own Test Data. SeeAllData=true will not work for API 23 version earlier.
  • User, profile, organization, AsyncApexjob, Corntrigger, RecordType, ApexClass, ApexComponent ,ApexPage we can access without (seeAllData=true).
  • @TestVisible annotation can be used to access private members and methods inside Test Class. Now we don’t need to compromise with access specifiers for sake of code coverage.
  • Test method and test classes are not counted as a part of code limit.
  • Test method takes no argument, commit no data to database, send no email, flagged with testMethod keyword.
  • Use Test.startTest() to reset Governor limits in Test methods.
  • If you are doing any Asynchronous operation in code, then don’t forget to call Test.stopTest() to make sure that operation is completed.
  • Use System.runAs() method to enforce OWD and Profile related testings. This is very important from Security point of View.
  • System.runAs() will not enforce user permission or field level permission.
  • Use As much as Assertions like System.AssertEquals or System.AssertNotEquals.
  • Always test Batch Capabilities of your code by passing 20 to 100 records.
  • Always try to pass null values in every methods. This is the area where most of program fails, unknowingly.
  • Please use call out mock to test web-service call out .
  • System.debug statement are not counted as a part of apex code limit.
  • We can run unit test by using Salesforce Standard UI,Force.com IDE ,Console ,API.
  • Maximum number of test classes run per 24 hour of period is  not grater of 500 or 10 multiplication of test classes of your organization.

Testing HTTP Callouts by Implementing the HttpCalloutMock Interface in Salesforce

Callout Class:

public class CalloutClass {
    
    public static HttpResponse getInfoFromExternalService() {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://api.salesforce.com/foo/bar');
        req.setMethod('GET');
        Http h = new Http();
        HttpResponse res = h.send(req);
        return res;
    }
}

Mock Http Response Generator Class:

@isTest
global class MockHttpResponseGenerator implements HttpCalloutMock {
    
    global HTTPResponse respond(HTTPRequest req) {
        
        System.assertEquals('http://api.salesforce.com/foo/bar', req.getEndpoint());
        System.assertEquals('GET', req.getMethod());
        
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody('{"foo":"bar"}');
        res.setStatusCode(200);
        return res;
    }
}

Callout Test Class:

@isTest
private class CalloutClassTest {
     @isTest static void testCallout() {
         
        Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
        
        HttpResponse res = CalloutClass.getInfoFromExternalService();
        
        String contentType = res.getHeader('Content-Type');
        System.assert(contentType == 'application/json');
        
        String actualValue = res.getBody();
        String expectedValue = '{"foo":"bar"}';
        
        System.assertEquals(actualValue, expectedValue);
        System.assertEquals(200, res.getStatusCode());
    }
}

Limit Class and Limit Methods in Salesforce

The Limits methods return the specific limit for the particular governor, such as the number of calls of a method or the amount of heap size remaining.

Because Apex runs in a multitenant environment, the Apex runtime engine strictly enforces a number of limits to ensure that runaway Apex doesn’t monopolize shared resources.

None of the Limits methods require an argument. The format of the limits methods is as follows:

 Integer queryLimitRows = Limits.getLimitQueryRows();

There are two versions of every method: the first returns the amount of the resource that has been used while the second version contains the word limit and returns the total amount of the resource that is available.

Limits Methods:
The following are methods for Limits. All methods are static.

System.debug('Limits.getAggregateQueries - '+ Limits.getAggregateQueries());
System.debug('Limits.getLimitAggregateQueries - '+ Limits.getLimitAggregateQueries());
System.debug('Limits.getCallouts - '+ Limits.getCallouts());
System.debug('Limits.getLimitCallouts - '+ Limits.getLimitCallouts());
System.debug('Limits.getCpuTime - '+ Limits.getCpuTime());
System.debug('Limits.getLimitCpuTime - '+ Limits.getLimitCpuTime());
System.debug('Limits.getDMLRows - '+ Limits.getDMLRows());
System.debug('Limits.getLimitDMLRows - '+ Limits.getLimitDMLRows());
System.debug('Limits.getDMLStatements - '+ Limits.getDMLStatements());
System.debug('Limits.getLimitDMLStatements - '+ Limits.getLimitDMLStatements());
System.debug('Limits.getEmailInvocations - '+ Limits.getEmailInvocations());
System.debug('Limits.getLimitEmailInvocations - '+ Limits.getLimitEmailInvocations());
System.debug('Limits.getFutureCalls - '+ Limits.getFutureCalls());
System.debug('Limits.getLimitFutureCalls - '+ Limits.getLimitFutureCalls());
System.debug('Limits.getHeapSize - '+ Limits.getHeapSize());
System.debug('Limits.getLimitHeapSize - '+ Limits.getLimitHeapSize());
System.debug('Limits.getMobilePushApexCalls - '+ Limits.getMobilePushApexCalls());
System.debug('Limits.getLimitMobilePushApexCalls - '+ Limits.getLimitMobilePushApexCalls());
System.debug('Limits.getQueries - '+ Limits.getQueries());
System.debug('Limits.getLimitQueries - '+ Limits.getLimitQueries());
System.debug('Limits.getQueryLocatorRows - '+ Limits.getQueryLocatorRows());
System.debug('Limits.getLimitQueryLocatorRows - '+ Limits.getLimitQueryLocatorRows());
System.debug('Limits.getQueryRows - '+ Limits.getQueryRows());
System.debug('Limits.getLimitQueryRows - '+ Limits.getLimitQueryRows());
System.debug('Limits.getQueueableJobs - '+ Limits.getQueueableJobs());
System.debug('Limits.getLimitQueueableJobs - '+ Limits.getLimitQueueableJobs());
System.debug('Limits.getSoslQueries - '+ Limits.getSoslQueries());
System.debug('Limits.getLimitSoslQueries - '+ Limits.getLimitSoslQueries());