Mutual authentication(two-way authentication) Callin

Callout 時の Mutual authentication については、サーバ編クライアント編 を参照。

Callin(外部から Salesforce への API 接続)時には、Salesforce のサーバ認証とは別に、クライアント認証も行えるようになっている(Salesforce.com Winter ’14 リリースノート/相互認証)。注意点としては、Callout 時のサーバ認証時と同様に Salesforce で信頼される認証局での署名である必要がある。対応している証明書は Outbound Messaging SSL CA Certificates にリストされている。

クライアント証明書のアップロードと、接続ユーザの SSL/TLS 相互認証を適用 が有効になれば設定は完了となる。テストとしては、cURL で以下の通り実行し疎通できれば良い。

$ curl -k https://ap.salesforce.com:8443/services/Soap/u/33.0 -H "Content-Type: text/xml; charset=UTF-8" -H "SOAPAction: login" -d @login.xml --cert mycrient.crt --key mycrient.key
<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Body>
    <n1:login xmlns:n1="urn:partner.soap.sforce.com">
      <n1:username>my username</n1:username>
      <n1:password>my password</n1:password>
    </n1:login>
  </env:Body>
</env:Envelope>

Hello Account (Visualforce Remote Objects)

Visualforce Remote Objects で Hello Account。

<apex:page>
  <apex:remoteObjects >
    <apex:remoteObjectModel name="Account" jsShorthand="Acct" fields="Id, Name">
      <apex:remoteObjectField name="Text1__c" jsShorthand="txt"/>
    </apex:remoteObjectModel>
  </apex:remoteObjects>

  <script>
    var doGetAccounts = function() {
      new SObjectModel.Acct().retrieve({ limit: 5 }, function(err, records, event) {
        if(err) {
          console.log(err.message);
        } else {
          records.forEach(function(record) {
            console.log(record.get("Name"));
          });
        }
      });
    };
  </script>

<button onclick="doGetAccounts()">Hello Account</button>

</apex:page>

Hello Account (Ruby)

Ruby(gem:databasedotcom) で Hello Account。

$ rails console

[1] pry(main)> client = Databasedotcom::Client.new(host: "test.salesforce.com", client_secret: <client secret>, client_id: <client id>)
[2] pry(main)> client.authenticate(username: <user name>, password: <password>)
[3] pry(main)> result = client.query("SELECT Id, Name FROM Account")
[4] pry(main)> result.each do |record| ; p record ; end

Hello Account (Java without wsc)

Java で Hello Account。

$ java -classpath ./:/usr/local/axis-1_4/lib/axis-ant.jar:/usr/local/axis-1_4/lib/commons-discovery-0.2.jar:/usr/local/axis-1_4/lib/jaxrpc.jar:/usr/local/axis-1_4/lib/wsdl4j-1.5.1.jar:/usr/local/axis-1_4/lib/axis.jar:/usr/local/axis-1_4/lib/commons-logging-1.0.4.jar:/usr/local/axis-1_4/lib/log4j-1.2.8.jar:/usr/local/axis-1_4/lib/saaj.jar org.apache.axis.wsdl.WSDL2Java partner.wsdl

$ javac -classpath ./:/usr/local/axis-1_4/lib/axis-ant.jar:/usr/local/axis-1_4/lib/commons-discovery-0.2.jar:/usr/local/axis-1_4/lib/jaxrpc.jar:/usr/local/axis-1_4/lib/wsdl4j-1.5.1.jar:/usr/local/axis-1_4/lib/axis.jar:/usr/local/axis-1_4/lib/commons-logging-1.0.4.jar:/usr/local/axis-1_4/lib/log4j-1.2.8.jar:/usr/local/axis-1_4/lib/saaj.jar com/sforce/soap/partner/*.java

$ find com/ | grep "class$" | xargs jar -cvf partner.jar

$ javac -classpath ./:/usr/local/axis-1_4/lib/axis-ant.jar:/usr/local/axis-1_4/lib/commons-discovery-0.2.jar:/usr/local/axis-1_4/lib/jaxrpc.jar:/usr/local/axis-1_4/lib/wsdl4j-1.5.1.jar:/usr/local/axis-1_4/lib/axis.jar:/usr/local/axis-1_4/lib/commons-logging-1.0.4.jar:/usr/local/axis-1_4/lib/log4j-1.2.8.jar:/usr/local/axis-1_4/lib/saaj.jar HelloAccount.java

$ java -classpath ./:/usr/local/axis-1_4/lib/axis-ant.jar:/usr/local/axis-1_4/lib/commons-discovery-0.2.jar:/usr/local/axis-1_4/lib/jaxrpc.jar:/usr/local/axis-1_4/lib/wsdl4j-1.5.1.jar:/usr/local/axis-1_4/lib/axis.jar:/usr/local/axis-1_4/lib/commons-logging-1.0.4.jar:/usr/local/axis-1_4/lib/log4j-1.2.8.jar:/usr/local/axis-1_4/lib/saaj.jar HelloAccount
import com.sforce.soap.partner.LoginResult;
import com.sforce.soap.partner.QueryResult;
import com.sforce.soap.partner.SessionHeader;
import com.sforce.soap.partner.SforceServiceLocator;
import com.sforce.soap.partner.SoapBindingStub;
import com.sforce.soap.partner.sobject.SObject;

public class HelloAccount {
  public static void main(String[] args) {
    try {
      SoapBindingStub binding = (SoapBindingStub) new SforceServiceLocator().getSoap();
      LoginResult loginResult = binding.login(<user name>, <password>);
      
      SessionHeader sh = new SessionHeader();
      sh.setSessionId(loginResult.getSessionId());      
      binding._setProperty(SoapBindingStub.ENDPOINT_ADDRESS_PROPERTY, loginResult.getServerUrl());
      binding.setHeader(new SforceServiceLocator().getServiceName().getNamespaceURI(), "SessionHeader", sh);
  
      QueryResult queryResult = binding.query("SELECT Id, Name FROM Account");
      for (SObject sobj : queryResult.getRecords()) {
        System.out.println(sobj.getId() + ", " + sobj.get_any()[1].getValue());
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Hello Account (Java with WSC)

Java (WSC) で Hello Account。

$ wget https://sfdc-wsc.googlecode.com/files/wsc-23.jar
$ java -classpath wsc-23.jar com.sforce.ws.tools.wsdlc partner.wsdl partner-wsc.jar
$ javac -classpath ./:wsc-23.jar:partner-wsc.jar HelloAccount.java
$ java -classpath ./:wsc-23.jar:partner-wsc.jar HelloAccount
import com.sforce.soap.partner.*;
import com.sforce.soap.partner.sobject.SObject;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;
import java.io.FileNotFoundException;

public class HelloAccount {
  private PartnerConnection connection;
  
  private String authEndpoint = "https://test.salesforce.com/services/Soap/u/30.0";
  private String userName = <user name>;
  private String password = <password>;
  private String query = "SELECT Id, Name FROM Account";
  
  public static void main(String[] args) throws FileNotFoundException {
    HelloAccount helloAcct = new HelloAccount();
    if (helloAcct.login()) {
      helloAcct.getAccounts();
    }
  }
  
  private boolean login() throws FileNotFoundException {
    boolean success = false;
    ConnectorConfig config = new ConnectorConfig();
    config.setUsername(userName);
    config.setPassword(password);
    config.setAuthEndpoint(authEndpoint);
    config.setTraceFile("trace.log");
    config.setTraceMessage(true);
    config.setPrettyPrintXml(true);
    try {
      connection = Connector.newConnection(config);
      success = true;
    } catch (ConnectionException e) {
      e.printStackTrace();
    }
    return success;
  }
  
  private void getAccounts() {
    QueryResult queryResult;
    try {
      queryResult = connection.query(query);
      for (SObject sobj : queryResult.getRecords()) {
        System.out.println(sobj.getId() + ", " + sobj.getField("Name"));
      }
    } catch (ConnectionException e) {
      e.printStackTrace();
    }
  }
}