Sunday, February 2, 2020

Integrate Java with Salesforce through Soap API

Steps I followed to get Enterpise.jar

 >>First and foremost, I took the Enterprise wsdl from Salesforce and downloaded "force-wsc-40.0.0.jar","ST4-4.0.8.jar",antlr-runtime-3.5.2.jar, js.jar(Rhino) and then took tools.jar from JDK.

>> Kept all these files in one single folder(alljars).

>> Given the path of this folder in the cmd(C:\Users\thimma.v\Desktop\alljars).

>> Then then adjusted JAVA_HOME, CLASSPATH and Path(please exact path which I had set in my environment variables)

path: .;%JAVA_HOME%

ClASSPATH : .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\jre\lib;%JAVA_HOME%\jre\lib\rt.jar;.\force-wsc-40.0.0.jar;.\enterprise.jar;

JAVA_HOME: C:\Program Files\Java\jdk1.8.0_241\bin

C:\Users\thimma.v\Desktop\alljars>java -classpath  ST4-4.0.8.jar;tools.jar;js.jar;antlr-runtime-3.5.2.jar;force-wsc-40.0.0.jar com.sforce.ws.tools.wsdlc ent.wsdl.xml enterprise.jar
[WSC][wsdlc.main:71]Generating Java files from schema ...
[WSC][wsdlc.main:71]Generated 990 java files.
[WSC][wsdlc.main:71]Compiling to target 1.6...
warning: [options] bootstrap class path not set in conjunction with -source 1.6
1 warning
[WSC][wsdlc.main:71]Compiled 994 java files.
[WSC][wsdlc.main:71]Generating jar file ... enterprise.jar
[WSC][wsdlc.main:71]Generated jar file enterprise.jar


>> Now I received generated Enterprise.jar.

>> Add Below Jars in the Eclipse.

force-wsc-40.0.0.jar(retrieved from Maven repository) and enterprise.jar(retrieved with the below cmd)

java -classpath  ST4-4.0.8.jar;tools.jar;js.jar;antlr-runtime-3.5.2.jar;force-wsc-40.0.0.jar com.sforce.ws.tools.wsdlc ent.wsdl.xml enterprise.jar

>> Now run below code to get session ID and do further operations.



import com.sforce.soap.enterprise.Connector;
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.Error;
import com.sforce.soap.enterprise.QueryResult;
import com.sforce.soap.enterprise.SaveResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.SObject;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;

/*
 * This class exercises the following functionality of the Web Services API
 * using the enterprise.wsdl:
 * - Login
 * - Query
 * - Update
 */
public class QueryAndUpdateaccountApp {

static final String USERNAME = "David@701.com"; 
static final String PASSWORD = "xxxxxxx";
static final String TOKEN = "AlDF5HNLniEjYxtvwuNFSheqp";
// the variable that will contain the connection that will be used to execute all
// SalesForce Web Services API.
static EnterpriseConnection connection;

// class constructor
public QueryAndUpdateaccountApp() {
// upon instantiation invoke process to perform the application logic
doWork();
}

// this is the entry point into the application
public static void main(String[] args) {
// create a new instance of the class to begin processing
new QueryAndUpdateaccountApp();

}

// this method comprises the bulk of the application logic
private void doWork() {
//TODO (Exercise 4-2): Set doQuery to true.
boolean doQuery = true;

//TODO (Exercise 4-3): Set doUpdate to true.
boolean doUpdate = true;

if (doLogin() && doQuery) {
// perform a sample query and output account information
Account account = doaccountQuery();

// if a record was returned, proceed with update
if (account != null && doUpdate) {
// update account
doaccountUpdate(account);
}
}
}

// this function encapsulates the logic necessary to login to salesforce.com
private boolean doLogin() {

ConnectorConfig config = new ConnectorConfig();
config.setUsername(USERNAME);
//append token to password if needed.
config.setPassword(PASSWORD + TOKEN);
//config.setTraceMessage(true);
//If using a proxy to get through a firewall, uncomment the following code and set appropriate values.
//config.setProxy("proxyServer.corp.myCorp.com",8080);
//config.setNtlmDomain("NtlmDom");
//config.setProxyUsername("proxyUserName");
//config.setProxyPassword("***");
try {
connection = Connector.newConnection(config);

// display some current settings
System.out.println("Auth EndPoint: " + config.getAuthEndpoint());
System.out.println("Service EndPoint: " + config.getServiceEndpoint());
System.out.println("Username: " + config.getUsername());
System.out.println("SessionId: " + config.getSessionId());
System.out.println("Sforce service created.");
return true;
} catch (ConnectionException e1) {
e1.printStackTrace();
return false;
}
}

// this function encapsulates the logic necessary to query salesforce.com and
// output information about existing accounts
private Account doaccountQuery() {

// declare local vars
QueryResult qr = null; // create a variable to hold the query result
Account account = null; // create a variable to hold the return value

/***********************************************************************
* TODO - (Exercise 4-2): WRITE THE LOGIC NECESSARY TO QUERY AND RETURN A SINGLE account
* 1. Declare a string to hold the SOQL query which will retrieve the id, first name, last name of a single account
* 2. Using the connection obtained in the doLogin() method, execute the query and obtain the result
* 3. This will need to be wrapped in a try-catch block. Simply catch a generic Exception and print out the
* error message to the console.
***********************************************************************/
String query = "select id, Name, Phone from Account LIMIT 1"; // query for account data
// call the query saving the results in qr
try {
qr = connection.query(query);
} catch (Exception e) {
System.out.println("Error getting accounts " + e.getMessage());
}

/***********************************************************************
* TODO - (Exercise 4-2):
* 4. Using the query result, obtain the account record from the first element in the list of records
* 5. Print out account's first and last name to the console.
***********************************************************************/

// iterate over the results and output the returned information
if (qr != null && qr.getSize() > 0) {
SObject[] records = qr.getRecords();
if (records.length != 0) {
// get a handle on the retrieved account
account = (Account) records[0];

// output information about the account
System.out.println("Retrieved account: ");
System.out.println(account);
System.out.println(account.getId());
}
}

// return the account
return account;
}

// this function encapsulates the logic necessary to update a received account
// within salesforce.com and output the results
private void doaccountUpdate(Account account) {
// First we will create a account object array, all calls are batch ours is a single element batch
Account[] accounts = new Account[1];

/***********************************************************************
* TODO (Exercise 4-3) - WRITE THE LOGIC NECESSARY TO UPDATE THE PHONE AND MOBILE FOR THE RECIEVED account
*
* 6. Set the phone and mobile fields on the account object
* 7. Add the account object to the array of accounts
***********************************************************************/
// set the values for the account
account.setName("Dave");
account.setPhone("777-666-5555");

// add the account to the array of objects
accounts[0] = account;

// we are now ready to update the record
// create an array for the results
SaveResult[] saveResults = null;
/***********************************************************************
* TODO (Exercise 4-3)
* 8. Using the connection object initialized in the doLogin method, update the list of accounts and put the result in the saveResults array
* 9. This will need to be wrapped in a try-catch block. Simply catch a generic Exception and print out the error message to the console.
***********************************************************************/

try {
saveResults = connection.update(accounts);
} catch (Exception e) {
System.out.println("Error updating accounts " + e.getMessage());
}
// we will create a separate save result object for clarity
for (SaveResult saveResult : saveResults) {
// check to see if the first update was a success
if (saveResult.isSuccess())
// the id that we passed should be the one we get back.
{
System.out.println("Updated account with id: " + saveResult.getId());
} else {
// an error occurred on this record
System.out.println("Error: ");
Error error = saveResult.getErrors()[0];
System.out.println(error.getMessage());
}
}
}
}


====================Finally got below result=====================

Auth EndPoint: https://login.salesforce.com/services/Soap/c/47.0/0DF2v0000000ofJ
Service EndPoint: https://david701-dev-ed.my.salesforce.com/services/Soap/c/47.0/00D2v000001uTD6/0DF2v0000000ofJ
Username: David@701.com
SessionId: 00D2v000001uTD6!AQIAQB45BK1HOuJjUacxaDOcpUkyZje4GkwMGp3ra86PrkVThlgbCGaX1wpk45SMt3U3jLeTotQdplyrzzUXpwIfW_oEocuW
Sforce service created.
Retrieved account:
[Account [SObject  fieldsToNull='{[0]}'
 Id='0012v00002bjSF7AAM'
]
.
.
 Phone='777-666-5555'
.
.

]

0012v00002bjSF7AAM
Updated account with id: 0012v00002bjSF7AAM

Note: If you have a trouble in getting enterprise.jar, you can use any jar which were retrieved by any developer earlier(to avoid the head of path setting and other errors related to it). You can ping me on thimma.salesforce@gmail.com in case if you want to get enterprise.jar for your POC.





Friday, January 31, 2020

How to identify root, intermediate and server certs.

Lot of us gets confuse about the certificate chain to identify which is root, intermediate and server certificate.  I tried to demonstrate it with pictorial representation to get some insight on this.

Primarily to see any domain certificate follow the process specified in the below image.



Root: The Root CA Certificate is always signed by the CA itself. The signatures of all certificates in the chain must be verified up to the Root CA Certificate.






If we notice that in the above picture Issued to and Issued by both are same because the Root CA Certificate is always signed by the CA itself.  If the issued by and Issued to both are same,then it is root cert. By decoding it we can easily identify it is root certificate.

Intermediate : Any certificate that sits between the SSL Certificate and the Root Certificate is called a chain or Intermediate Certificate. The Intermediate Certificate is the signer/issuer of the SSL  Certificate. The Root CA Certificate is the signer/issuer of the Intermediate Certificate. If the Intermediate Certificate is not installed on the server (where the SSL certificate is installed) it may prevent some browsers, mobile devices, applications, etc. from trusting the SSL certificate. 





In order to make the SSL certificate compatible with all clients, it is necessary that the Intermediate Certificate be installed.

Server/Domain certServer certificates (SSL certificates) are used to authenticate the identity of a server. When installed on a website, an SSL certificate turns the protocol on the website from HTTP to HTTPS and installs indicators that vouch for the authenticity of the website






If we notice in the above picture, the Issued by is pointing to the Intermediate cert(DigiCert SHA Secure Server CA) and Issued is pointing to Domain or Sever certificate.

This is how we establish the chaining for any certificates.

 Please find below step by to step  process to create a certificate chain.

1. Get CA signed certificate for domain.
2. Import or Download that certificate as base64.
3. Do the same for all the intermediate certificates (if more than one) and the root certificate.
4. Now create a new file. Example:  certificate_chain.crt.
5. Open that file in text editor and stack all 3 certificates on after the other and save.
1. Order of the certificates is starting from the domain and up towards the root
  • Domain cert
  • Intermediate cert 1 above domain
  • Intermediate cert 2 above that and so on
  • Root cert

2. You must include all certificates up to and including root

Example of merging certificates

-----BEGIN CERTIFICATE-----
MIIGvTCCBaWgAwIBAgIQBsyeRo2C7ECRbEpmpu+mazANBgkqhkiG9w0BAQUFADBI
[TRUNCATE]
MDEyMDAwMFowgYcxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDESMBAG
v+PMGxmcJcqnBrJT3yOyzxIZow==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh
[TRUNCATE]
slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh
v+PMGxmcJcqnBrJT3yOyzxIZow==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
[TRUNCATE]
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

 If you want to decode certificates on your own computer, run this OpenSSL command:
openssl x509 -in certificate.crt -text -noout

Monday, January 13, 2020

Salesforce oAuth 2.0 flows with Node Js Integration.

Tried to wrap all the oAuth flows in one place and able to complete it. Sharing it to help to accelearte
the progress of your poc/project. Please tweak below code to use any flow with node
Js integration.



var sf = require('node-salesforce');
var http = require('http');
var url = require('url');
var fs = require('fs');
request = require('request');
path = require("path"),
https = require('https'), 
fs = require('fs'),  
base64url = require('base64-url'), 
nJwt = require('njwt')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;

let client_Id ='3MVG9d8..z.hDcPKuO0qkcUX0IqWf.AIAsDF_dkcBYL80RvSIKADdH0svSMV_gKhpJAh1HwI4tRNfimBeuwrO';
let client_Secret='67D3847C8AC12050F369457935933CD1205A120C70E69E370B169BFC8CC3AE27';
let  sfdcURL = 'https://login.salesforce.com/services/oauth2/authorize' ;
let  tokenurl = 'https://login.salesforce.com/services/oauth2/token' ;
  
//
// OAuth2 client information can be shared with multiple connections.
//
var express = require('express');
var app = express();
var oauth2 = new sf.OAuth2({
    loginUrl : 'https://login.salesforce.com',
    clientId:client_Id,
    clientSecret:client_Secret,
    redirectUri : 'http://localhost:8000/oauth2/code'
});
//
// Get authz url and redirect to it.

//
app.get('/oauth2/auth', function(req, res) {
  res.redirect(oauth2.getAuthorizationUrl());
});

app.get('/', function (req, res) {
    res.send(`<html>
    <head>
    <style>
    ul {
      list-style-type: none;
      margin: 0;
      padding: 0;
      overflow: hidden;
      background-color: #333333;
    }
    li {
      float: left;
    }
    
    li a {
      display: block;
      color: white;
      text-align: center;
      padding: 16px;
      text-decoration: none;
    }
    
    li a:hover {
      background-color: #111111;
    }
    </style>
    </head>
    <body>
    
    <ul>
      <li><a href="http://localhost:8000/jwt">JWT Flow</a></li>
      <li><a href="http://localhost:8000/oauth2/auth">Web server</a></li>
      <li><a href="http://localhost:8000/username_password">User name and Password</a></li>
      <li><a href="http://localhost:8000/useragent">User agent</a></li>
      <li><a href="http://localhost:8000/refresh">Refresh token flow</a></li>
      <li><a href="http://localhost:8000/samlassertionaccesstoken">SAML Assertion</a></li>
      <li><a href="http://localhost:8000/device">Device flow</a></li>
      <li><a href="https://asset-tokens.herokuapp.com/">Asset token flow</a></li>
    </ul>
    
    </body>
    </html>`);
    
});
app.get('/oauth2/code', function(req, res) {
  var q = url.parse(req.url, true);
  request({   url : tokenurl+'?client_id='+
  client_Id
    +'&redirect_uri='+
          'http://localhost:8000/'+'&grant_type=authorization_code&code='+
          q.query.code+'&client_secret='+client_Secret,  
         method:'POST' 
       },
       function(err, remoteResponse, remoteBody) {
       
         res.send(remoteBody);
       } 
     );

  
});

app.get('/useragent', function (req,res){  
request({   url : sfdcURL+'?client_id='+client_Id+'&redirect_uri='+'http://localhost:8000/'+'&response_type=token',  
        method:'GET' 
      }).pipe(res); 
   
} );

/**
*  Username Password oAuth Flow
*/
app.get('/username_password', function (req,res){  

  var sfdcURL = 'https://login.salesforce.com/services/oauth2/token' ;

 let uname='David@abc.com';
 let pwd='passwordonly'

  var upurl = sfdcURL+
  '?client_id='+ client_Id+
   '&grant_type=password'+
   '&client_secret='+client_Secret+
   '&username='+uname+
   '&password='+pwd ;
 

   request({  url : upurl,  
        method:'POST' 
      },
      function(err, remoteResponse, remoteBody) {
        var sfdcResponse = JSON.parse(remoteBody); 
        res.send(sfdcResponse);
      } 
    );  
} );


function encryptUsingPrivateKey_nJWTLib (claims) {
  var absolutePath = path.resolve("pykey.pem");   
    var cert = fs.readFileSync(absolutePath );  
  var jwt_token = nJwt.create(claims,cert,'RS256'); 
  console.log(jwt_token); 
  var jwt_token_b64 = jwt_token.compact();
  console.log(jwt_token_b64);
 
  return jwt_token_b64;     
};

function getJWTSignedToken_nJWTLib(){ 

  var claims = {
    iss: '3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX',   
    sub: 'david@lightning601.com',     
    aud: 'https://login.salesforce.com',
    exp: (Math.floor(Date.now() / 1000) + (60*3))
   
  }

  return encryptUsingPrivateKey_nJWTLib(claims);
}
app.get('/jwt', function (req,res){  
  
  var sfdcURL = 'https://login.salesforce.com/services/oauth2/token' ;
  
  var sfdcUserName = 'david@lightning601.com';
  var token = getJWTSignedToken_nJWTLib(); 
    
  var paramBody = 'grant_type='+base64url.escape('urn:ietf:params:oauth:grant-type:jwt-bearer')+'&assertion='+token ; 
  var req_sfdcOpts = {  url : sfdcURL,  
              method:'POST', 
              headers: { 'Content-Type' : 'application/x-www-form-urlencoded'} ,
              body:paramBody 
            };
        
  request(req_sfdcOpts, 
    function(err, remoteResponse, remoteBody) {
      res.send(remoteBody);
    } 
  ); 
} );

/**
 * Device Authentication Flow
 */
app.get('/device', function (req,res){  


  let deviceurl = tokenurl+
  '?client_id='+ client_Id+
   '&response_type=device_code&redirect_uri=http://localhost:8000/' ;
 

   request({  url : deviceurl,  
        method:'POST' 
      },
      function(err, remoteResponse, remoteBody) {
        
        console.log(remoteBody) ;
        var sfdcResponse = JSON.parse(remoteBody); 
        var jsondata={
          "verification_uri" : sfdcResponse.verification_uri,
          "user_code" : sfdcResponse.user_code,
          "device_code" : sfdcResponse.device_code
        
        };
        console.log('@@@@@@@',jsondata) ;
        var hrefs=`http://localhost:8000/devicePol?device_code=${sfdcResponse.device_code}`;
        var step2redirect=`<html><head>${JSON.stringify(jsondata)} </head><body><a href=${hrefs}> Post authorizing in different browser/device click here: Device flow step 2 </a> </body></html>`;
        console.log(step2redirect) ;
        //res.send(JSON.parse(JSON.stringify(step2redirect)));
//${jsondata}
        res.send(step2redirect);
      } 
    );  
} ); 

/**
 *  Keep polling till device is verified using code
 */

app.get('/devicePol', function (req,res){ 
  var device_code = req.query.device_code;
  console.log('@@@@@@@@@device code@@@@@@@@@@@@',device_code);
  var devicePolurl = tokenurl+'?client_id='+ client_Id+'&code='+device_code+'&grant_type=device';
 //console.log('@@@@@@devicePolurl@@@@@@@',devicePolurl)
   request({  url : devicePolurl,  
      method:'POST' 
    },
    function(err, remoteResponse, remoteBody) {
      
      //console.log(remoteBody) ;
      var sfdcResponse = JSON.parse(remoteBody); 
      res.send(sfdcResponse);   
    } 
  );  
} ); 

 

  app.get('/samlassertionaccesstoken', function (req,res){ 
   
    let assertionvalue='PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIERlc3RpbmF0aW9uPSJodHRwczovL2xpZ2h0NjAxZGVwLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbT9zbz0wMEQ3RjAwMDAwNEIxRXYiIElEPSJfNzAyNjBhYzItMjg0MTI0MmMiIElzc3VlSW5zdGFudD0iMjAxOS0xMi0yNlQxOToyMDozMy4xODdaIiBWZXJzaW9uPSIyLjAiPjxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPkF4aW9tPC9zYW1sMjpJc3N1ZXI';   tokenurl='https://light601dep-dev-ed.my.salesforce.com/services/oauth2/token';
    var samlurl = tokenurl+'?grant_type=assertion&assertion_type=urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser&assertion='+assertionvalue;
    console.log(samlurl);
      request({   url : samlurl,  
         method:'POST' 
       },
       function(err, remoteResponse, remoteBody) {
         
         //console.log(remoteBody) ;
         var sfdcResponse = JSON.parse(remoteBody); 
         console.log(sfdcResponse)
         res.send(sfdcResponse);    
       } );
      });
      app.get('/refresh', function(req, res) {
       
        let refresh_token = '5Aep8613hy0tHCYdhyHUIj3Fev55mM_VcyWVY6uxHslAKE.FvcRJitylwr4jQhZcx9Hwo7SA6lbd6f.AWbyKtfE';
        var q = url.parse(req.url, true);
        request({   url : tokenurl+'?client_id='+client_Id+'&redirect_uri='+
                'http://localhost:8000/'+'&grant_type=refresh_token&refresh_token='+
                refresh_token,  
               method:'POST' 
             },
             function(err, remoteResponse, remoteBody) {
             
               res.send(remoteBody);
             } 
           );
      
  
    
  });
   
var server = app.listen(8000, function () {
  
});

All Salesforce oAuth 2.0 flows in one place with python integration

Below code covers most of the oAuth 2.0 flows of Salesforce. Tweaking below code helps you
to implement any oAuth 2.0 flow within no time. All the Best !!

import flask
import requests
import tkinter as tk
import json
from json2html import *
from flask import request, jsonify
from base64 import urlsafe_b64encode
from datetime import datetime, timedelta
import Crypto
from flask import (
    Flask,
    render_template,
    jsonify,
)

import os
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

app = flask.Flask(__name__)
app = Flask(__name__)
app.config["DEBUG"] = TrueJWT_HEADER = '{"alg":"RS256"}'JWT_CLAIM_ISS = "3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX"JWT_CLAIM_SUB = "david@lightning601.com"JWT_CLAIM_AUD = "https://login.salesforce.com"JWT_AUTH_EP = "https://login.salesforce.com/services/oauth2/token"'''===========================Home page ============================================='''

@app.route('/', methods=["GET"])
def home():
    return "<h1> This blog is a great place to learn advanced concepts </h1>"

##############webserver first step ###########################
@app.route('/sfconnect', methods=["GET"])
def sfconnect():
    # return "<a href='https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsalesforcedata'> Webserver flow </a>"    allflows = "<div style='background-color: #fad0c4;background-image: linear-gradient(315deg, #fad0c4 0%, #f1a7f1 74%);height:570px;width:1260px' ><div style='padding: 1em;position: absolute;'><ul style='list-style-type:disc;color: green;'>" + "<li style='color: green;font-size: 150%'>" + "<a href='https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsalesforcedata'> Webserver flow </a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsfconnect'> User agent flow </a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/unamepwd'>User name & Password flow </a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/refresh'>Refresh token flow</a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/jwtflow'>JWT Bearer flow</a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/Deviceflow'>Device flow step 1</a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/samlassertionflow'>Saml Assertion flow</a>" + "</li>"\
               "<li style='color: green;font-size: 150%'>" + "<a href='https://asset-tokens.herokuapp.com/' target = '_blank' >Asset Token flow</a>" + "</li>"+ "</ul></div></div>"
    return allflows


############################################################################################ Get token ###############################################################################################
@app.route('/salesforcedata', methods=["GET"])
def sfdata():
    print(request.args)
    if 'code' in request.args:
        code = request.args["code"]

    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"    # r = requests.post(url=AUTHORIZE_ENDPOINT, data=dataauthorize)
    datafortoken = {'grant_type': 'authorization_code',
                    'client_id': '3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX',
                    'client_secret': 'B682A1156FF3A2CCF0E83764552CD1DD0FD2A4510577DDE69C408D80FB9F4A76',
                    'code': code,
                    'redirect_uri': 'http://localhost:5000/salesforcedata'                    }

    r = requests.post(url=API_ENDPOINT, data=datafortoken)
    sfdcres = r.text
    print("The pastebin URL is:%s" % sfdcres)

    dicdata = json.loads(sfdcres)

    # print('#############',jsondata,'$$$$$$$$$$',dicdata);
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)

    # place your own domain name.    return getdata(dicdata["access_token"])


############################################################################# User name and password flow####################################################################################
@app.route('/unamepwd', methods=["GET"])
def username_pwd():
    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"
    clientid = '3MVG9G9pzCUSkzZvyptQFNKyeFZLuFEuMqSNrj_gLpwyqgHTmUSLK0qb0EvWiNm6VYzLixRXeULtJZlm3LevO'    clientsec = '32B4AFDB1FD3E9B8FD62FD2333ACC9B33A65778C35634F8D618745746A2A6C86'    # data to be sent to api    data = {'grant_type': 'password',
            'client_id': clientid,
            'client_secret': clientsec,
            'username': 'David@abc.com',
            'password': 'pwdsecuritytoken'            }

    # String reqbody = 'grant_type=password&client_id='+clientid+'&client_secret='+clientsec+'&username='+'David@lightning601.com'+'&password='+'Welcome@18G841Lq4OPaBc3nvtMcGmhC5GH';
    # sending post request and saving response as response object    r = requests.post(url=API_ENDPOINT, data=data)

    # extracting response text    # sfdcres = json.dumps(r.text)    resp = json.loads(r.text)
    # return json2html.convert(json=json.loads(r.text))
    return getdata(resp["access_token"])


############################################################################# Refresh token flow#############################################################################################
@app.route('/refresh', methods=["GET"])
def refrehflow():
    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"
    clientid = '3MVG9G9pzCUSkzZvyptQFNKyeFZLuFEuMqSNrj_gLpwyqgHTmUSLK0qb0EvWiNm6VYzLixRXeULtJZlm3LevO'    refresh_token = '5Aep8613hy0tHCYdhyHUIj3Fev55mM_VcyWVY6uxHslAKE.Fvf2x7ni.qVWP4HfxXN7ieW.wzJFXROLFe4F8cR9'  # data to be sent to api    data = {'grant_type': 'refresh_token',
            'client_id': clientid,
            'refresh_token': refresh_token
            }

    # String reqbody = 'grant_type=password&client_id='+clientid+'&client_secret='+clientsec+'&username='+'David@lightning601.com'+'&password='+'Welcome@18G841Lq4OPaBc3nvtMcGmhC5GH';
    # sending post request and saving response as response object    r = requests.post(url=API_ENDPOINT, data=data)

    # extracting response text    sfdcres = r.text
    # return json2html.convert(json=json.loads(r.text))    resp = json.loads(r.text)
    # return json2html.convert(json=json.loads(r.text))
    return getdata(resp["access_token"])


############re usable method #########################
def getdata(acctkn):
    END_POINT = "https://light601dep-dev-ed.my.salesforce.com/services/apexrest/contactsfordatabase/";

    data = {"Authorization": "Bearer " + acctkn,
            "Content-Type": "application/json"            }

    r = requests.get(END_POINT, headers=data)

    print("contacts data from salesforce:%s" % r.content)

    # dict = json.loads(r.content)
    # return r.content    return json2html.convert(json=json.loads(r.content))


def jwt_claim():
    '''    Function to package JWT Claim data in a base64 encoded string    :return:    base64 encoded jwt claims data    '''
    claim_template = '{{"iss": "{0}", "sub": "{1}", "aud": "{2}", "exp": {3}}}'    claim = urlsafe_b64encode(JWT_HEADER.encode()).decode()
    claim += "."
    # expiration_ts = (datetime.now(tz=timezone.utc) + timedelta(minutes=5)).timestamp()    expiration_ts = int((datetime.now() + timedelta(seconds=300)).timestamp())
    payload = claim_template.format(JWT_CLAIM_ISS, JWT_CLAIM_SUB, JWT_CLAIM_AUD, expiration_ts)
    print(payload)

    claim += urlsafe_b64encode(payload.encode()).decode()
    return claim


def sign_data(data):
    f = open('pykey.pem', 'r')
    print('$$$$$$$file$$$$$$$$', f)
    rsa1key = RSA.importKey(f.read())
    signer = PKCS1_v1_5.new(rsa1key)
    digest = SHA256.new()
    digest.update(data.encode())
    sign = signer.sign(digest)
    return urlsafe_b64encode(sign).decode()


def do_auth(endpoint, data):
    '''    Function to POST JWT claim to SFDC /oauth/token endpoint and receive an access_token    :return:    access token    '''
    r = requests.post(endpoint, data=data)
    return r


@app.route('/jwtflow', methods=["GET"])
def jwtflow():
    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    claim = jwt_claim().replace("=", "")

    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    signed_claim = sign_data(claim).replace("=", "")

    target_payload = claim + "." + signed_claim

    auth_payload = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": target_payload}
    response = do_auth(JWT_AUTH_EP, data=auth_payload)

    #  convert the text dictionary to data structure so it can be rendered as a json properly    response_text = eval(response.text)

    response_headers = eval(str(response.headers))

    return_dict = {"claim": claim, "signed_claim": signed_claim, "target_payload": target_payload,
                   "response_text": response_text, "response_headers": response_headers}
    # return jsonify(return_dict)    sfdcres = response.text
    print("The SFDC response:%s" % sfdcres)

    dicdata = json.loads(sfdcres)

    # print('#############',jsondata,'$$$$$$$$$$',dicdata);
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)

    # place your own domain name.    return getdata(dicdata["access_token"])


#####################Device flow#######################################
@app.route('/Deviceflow', methods=["GET"])
def deviceflow():
    print(request.args)
    Deviceflowclientid = '3MVG9d8..z.hDcPKuO0qkcUX0IvjSlDS5xKC7RazmseJKnAbeQxg3zIE55yk7zvsy3NpjLIayWxNoUU3RfYz0';

    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"
    getdevicecode = {'response_type': 'device_code',
                     'client_id': Deviceflowclientid,
                     'client_secret': 'B682A1156FF3A2CCF0E83764552CD1DD0FD2A4510577DDE69C408D80FB9F4A76',
                     'redirect_uri': 'http://localhost:5000/Deviceflowstep2'                     }

    r = requests.post(url=API_ENDPOINT, data=getdevicecode)
    sfdcres = r.text

    dicdata = json.loads(sfdcres)
    devicecode = dicdata["device_code"]
    parseddata = ''    for i in dicdata:
        parseddata = parseddata+"<tr><th>" + str(i) + "</th><td>" + str(dicdata[i]) + "</td></tr>"    parseddata = "<table border='1' style='color:blue;background-color:gold;'><tbody>" + parseddata + "</tbody></table>"    stepsbeforegettingtoken="Before clicking below link, take the verification uri <b style='color:red'>{}</b> and enter it into another device(Mobile/Deskstop)/n and then enter User code <b style='color:red'>{}</b>".format(str(dicdata['verification_uri']),str(dicdata['user_code']))
    step2link = "<br/><br/><b style='color:Green'>"+stepsbeforegettingtoken+"</b><br/><h2><a href = " + "http://127.0.0.1:5000/Deviceflowstep2?devicecode=" + devicecode + "> Device flow step 2 (Get access token) " + "</h2>"    parseddata = parseddata + step2link
    return parseddata

###################################
#####################Device flow#######################################
@app.route('/Deviceflowstep2', methods=["GET"])
def deviceflowstep2():
    print('####################', request.args)
    # return request.args.devicecode
    dicdata = json.dumps(request.args)
    # devicecode = dicdata["devicecode"]    print(dicdata)

    dicdata = json.loads(dicdata)
    Deviceflowclientid = '3MVG9d8..z.hDcPKuO0qkcUX0IvjSlDS5xKC7RazmseJKnAbeQxg3zIE55yk7zvsy3NpjLIayWxNoUU3RfYz0';

    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"    # r = requests.post(url=AUTHORIZE_ENDPOINT, data=dataauthorize)
    getdevicecode = {'grant_type': 'device',
                     'client_id': Deviceflowclientid,
                     'code': dicdata['devicecode']
                     }

    r = requests.post(url=API_ENDPOINT, data=getdevicecode)
    sfdcres = r.text

    dicdata = json.loads(sfdcres)
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)


    return json2html.convert(json=json.loads(r.content))


#########################################################################################



def samlflow(entries):

    assertion=str(entries['assertion'].get())
    API_ENDPOINT = "https://light601dep-dev-ed.my.salesforce.com/services/oauth2/token"    # r = requests.post(url=AUTHORIZE_ENDPOINT, data=dataauthorize)    import base64

    data=str(entries['assertion'].get())
    getdevicecode = {'grant_type': 'assertion',
                     'assertion_type': 'urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser',
                     'assertion': data}
    r = requests.post(url=API_ENDPOINT, data=getdevicecode)
    sfdcres = r.text
    print('##################',sfdcres)
    dicdata = json.loads(sfdcres)
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)

    return json2html.convert(json=json.loads(r.content))


def makeform(root, fields):
    entries = {}
    for field in fields:
        print(field)
        row = tk.Frame(root)
        lab = tk.Label(row, width=22, text=field+": ", anchor='w')
        ent = tk.Entry(row)
        ent.insert(0, "0")
        row.pack(side=tk.TOP,
                 fill=tk.X,
                 padx=5,
                 pady=5)
        lab.pack(side=tk.LEFT)
        ent.pack(side=tk.RIGHT,
                 expand=tk.YES,
                 fill=tk.X)
        entries[field] = ent
    return entries

@app.route('/samlassertionflow', methods=["GET"])
def samlassertionflow():
    app = flask.Flask(__name__)
    app = Flask(__name__)
    root = tk.Tk()
    fields = ('assertion', 'End point')
    ents = makeform(root, fields)
    b1 = tk.Button(root, text='Saml assertion flow',
           command=(lambda e=ents: samlflow(e)))
    b1.pack(side=tk.LEFT, padx=5, pady=5)

    root.mainloop()

    return 'Proceed with the generated input interface'


#########################################################################################
if __name__ == "__main__":
    port = int(os.getenv("PORT", 5000))
    # Run the app, listening on all IPs with our chosen port number    app.run(host="0.0.0.0", port=port, debug=True, use_reloader=True)

app.run()

Salesforce JWT Flow with Python to retrieve and insert data into SQL.

We can use below code to fetch the data from SF via JWT flow from python and insert it into 
Database.In case if you want to schedule your python program to retrieve the data from SF to
take the backup of your data you can refer this link 
"https://datatofish.com/python-script-windows-scheduler/" to learn how to schedule 
any python program from Windows. 

Note: You need to tweak your code according to your Salesforce connected app settings like 
consumerkey and certificates and username of your SF.



import flask
import requests
import tkinter as tk
import json
from json2html import *
import mysql.connector
from flask import request, jsonify
from base64 import urlsafe_b64encode
from datetime import datetime, timedelta
from mysql.connector import Error
from mysql.connector import errorcode
import Crypto
from flask import (
    Flask,
    render_template,
    jsonify,
)

import os
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

app = flask.Flask(__name__)
app = Flask(__name__)
app.config["DEBUG"] = TrueJWT_HEADER = '{"alg":"RS256"}'JWT_CLAIM_ISS = "3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX"JWT_CLAIM_SUB = "david@abc.com"JWT_CLAIM_AUD = "https://login.salesforce.com"JWT_AUTH_EP = "https://login.salesforce.com/services/oauth2/token"
@app.route('/', methods=["GET"])
def home():
    return "<h1> This blog is a great place to learn advanced concepts </h1>"

@app.route('/sfconnect', methods=["GET"])
def sfconnect():
    # return "     allflows = "<div style='background-color: #fad0c4;background-image: linear-gradient(315deg, #fad0c4 0%, #f1a7f1 74%);height:570px;width:1260px' ><div style='padding: 1em;position: absolute;'><ul style='list-style-type:disc;color: green;'>" + \
   "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/jwtflow'>JWT Bearer flow</a>" + "</li>" + "</ul></div></div>"     return allflows

############re usable method #########################
def getdata(acctkn):
    END_POINT = "https://light601dep-dev-ed.my.salesforce.com/services/apexrest/contactsfordatabase/";

    data = {"Authorization": "Bearer " + acctkn,
            "Content-Type": "application/json"            }

    r = requests.get(END_POINT, headers=data)
    r2= json.loads(r.content)
    print("###test#### get typeof from salesforce:%s", type(r2))
    sf_data = r.content
    data=r2
    print("###test123####contacts data from salesforce:%s",data)
    mydb = mysql.connector.connect(
        host="127.0.0.1",
        user="root",
        passwd="password",
        database="salesforcedata",auth_plugin='mysql_native_password')
    mycursor = mydb.cursor()
    for x in data:
        sql = "INSERT INTO Contactsf (FirstName, LastName) VALUES (%s, %s)"        val = (x['FirstName'], x['LastName'])
        mycursor.execute(sql, tuple(val))
        mydb.commit()
        continue
    mycursor.close()  ## here all loops done    mydb.close()  ## close db connection
    return json2html.convert(json=json.loads(r.content))


def jwt_claim():
    '''    Function to package JWT Claim data in a base64 encoded string    :return:    base64 encoded jwt claims data    '''
    claim_template = '{{"iss": "{0}", "sub": "{1}", "aud": "{2}", "exp": {3}}}'    claim = urlsafe_b64encode(JWT_HEADER.encode()).decode()
    claim += "."
    # expiration_ts = (datetime.now(tz=timezone.utc) + timedelta(minutes=5)).timestamp()    expiration_ts = int((datetime.now() + timedelta(seconds=300)).timestamp())
    payload = claim_template.format(JWT_CLAIM_ISS, JWT_CLAIM_SUB, JWT_CLAIM_AUD, expiration_ts)
    print(payload)

    claim += urlsafe_b64encode(payload.encode()).decode()
    return claim


def sign_data(data):
    f = open('pykey.pem', 'r')
    print('$$$$$$$file$$$$$$$$', f)
    rsa1key = RSA.importKey(f.read())
    signer = PKCS1_v1_5.new(rsa1key)
    digest = SHA256.new()
    digest.update(data.encode())
    sign = signer.sign(digest)
    return urlsafe_b64encode(sign).decode()


def do_auth(endpoint, data):
    '''    Function to POST JWT claim to SFDC /oauth/token endpoint and receive an access_token    :return:    access token    '''
    r = requests.post(endpoint, data=data)
    return r


@app.route('/jwtflow', methods=["GET"])
def jwtflow():
    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    claim = jwt_claim().replace("=", "")

    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    signed_claim = sign_data(claim).replace("=", "")

    target_payload = claim + "." + signed_claim

    auth_payload = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": target_payload}
    response = do_auth(JWT_AUTH_EP, data=auth_payload)

    #  convert the text dictionary to data structure so it can be rendered as a json properly    response_text = eval(response.text)

    response_headers = eval(str(response.headers))

    return_dict = {"claim": claim, "signed_claim": signed_claim, "target_payload": target_payload,
                   "response_text": response_text, "response_headers": response_headers}
    # return jsonify(return_dict)    sfdcres = response.text
    print("The SFDC response:%s" % sfdcres)

    dicdata = json.loads(sfdcres)

    # print('#############',jsondata,'$$$$$$$$$$',dicdata);
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)


    return getdata(acctkn)

if __name__ == "__main__":
    port = int(os.getenv("PORT", 8001))
    # Run the app, listening on all IPs with our chosen port number    app.run(host="0.0.0.0", port=port, debug=True, use_reloader=True)

app.run()

Friday, December 27, 2019

Get current release of the Salesforce with the Rest API

>> Some times when integrating with salesforce, we are unsure what is the current release of the org which we are trying to integrate.

>> Even after logging into the salesforce, we can only see the image of the current release and version number but in none of the places, we can see whether it is winter, spring or summer release.

>> To get the information, login into the workbench and execute the below URL to get the releases data, the last record is the current version/release of your instance. From the below picture, you can identify the current release of my org is Winter 20

/services/data/
















If you wish to get the same information in the any other integration system to check if there is any release that took place recently, use the below URL after getting access token/session Id.


It also provides the latest version/release of the organization which you are trying to hit.

Note: Salesforce runs on multiple server instances. The examples in this guide use yourInstance in place of a specific instance. Replace that text with the instance for your org