You need to sign in to do that
Don't have an account?

ws-security in WebService
Hi all.
I am trying to connect from Apex to an external WebService that requires the use of ws-security. I have discovered that this is not directly supported by Apex, but a suggested solution was to manually edit the WDSL document to include the ws-security header information.
I have been trying to do this without much luck, and was wondering if anyone out there had a simple example of what I need to inject into the WSDL, and where?
My thanks in advance,
Ivar
I'm assuming that you're trying to insert WS-Security username/password tokens into your Apex callout (as opposed to some other WS-Security profile like SAML etc.). If so, the following write-up by Chuck Mortimore, Security PM at Salesforce.com should be very helpful. It waks thru how you can modify the generated WSDL2Apex class to manually insert WSS username/password SOAP headers into a sample web service request. Note that with this approach, you don't have to modify the WSDL itself - just the generated Apex class.
1) Generate Code from this WSDL and called the class “echo”
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://doc.sample.com/docSample"
targetNamespace="http://doc.sample.com/docSample"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://doc.sample.com/docSample">
<s:element name="EchoString">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="EchoStringResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="EchoStringResult" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="EchoStringSoapIn">
<wsdl:part name="parameters" element="tns:EchoString" />
</wsdl:message>
<wsdl:message name="EchoStringSoapOut">
<wsdl:part name="parameters" element="tns:EchoStringResponse" />
</wsdl:message>
<wsdl:portType name="DocSamplePortType">
<wsdl:operation name="EchoString">
<wsdl:input message="tns:EchoStringSoapIn" />
<wsdl:output message="tns:EchoStringSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="DocSampleBinding">
<wsdl:operation name="EchoString">
<soap:operation soapAction="urn:dotnet.callouttest.soap.sforce.com/EchoString" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="DocSample">
<wsdl:port name="DocSamplePort" binding="tns:DocSampleBinding">
<soap:address location="http://www.qaresponder.info/WebServices/DocSample.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
2) Modified code to talk to postbin.org...a cool service that echo’s posts, so you can see what is there. Don’t forget to whitelist postbin in “Remote Sites”
Changed: public String endpoint_x = 'http://www.qaresponder.info/WebServices/DocSample.asmx';
To: public String endpoint_x = 'http://www.postbin.org/p97gfq';
Resulting in this class:
public class echo {
public class EchoStringResponse_element {
public String EchoStringResult;
private String[] EchoStringResult_type_info = new String[]{'EchoStringResult','http://www.w3.org/2001/XMLSchema','string','0','1','false'};
private String[] apex_schema_type_info = new String[]{'http://doc.sample.com/docSample','true','false'};
private String[] field_order_type_info = new String[]{'EchoStringResult'};
}
public class DocSamplePort {
public String endpoint_x = 'http://www.postbin.org/p97gfq';
public Map<String,String> inputHttpHeaders_x;
public Map<String,String> outputHttpHeaders_x;
public String clientCertName_x;
public String clientCert_x;
public String clientCertPasswd_x;
public Integer timeout_x;
private String[] ns_map_type_info = new String[]{'http://doc.sample.com/docSample', 'echo'};
public String EchoString(String input) {
echo.EchoString_element request_x = new echo.EchoString_element();
echo.EchoStringResponse_element response_x;
request_x.input = input;
Map<String, echo.EchoStringResponse_element> response_map_x = new Map<String, echo.EchoStringResponse_element>();
response_map_x.put('response_x', response_x);
WebServiceCallout.invoke(
this,
request_x,
response_map_x,
new String[]{endpoint_x,
'urn:dotnet.callouttest.soap.sforce.com/EchoString',
'http://doc.sample.com/docSample',
'EchoString',
'http://doc.sample.com/docSample',
'EchoStringResponse',
'echo.EchoStringResponse_element'}
);
response_x = response_map_x.get('response_x');
return response_x.EchoStringResult;
}
}
public class EchoString_element {
public String input;
private String[] input_type_info = new String[]{'input','http://www.w3.org/2001/XMLSchema','string','0','1','false'};
private String[] apex_schema_type_info = new String[]{'http://doc.sample.com/docSample','true','false'};
private String[] field_order_type_info = new String[]{'input'};
}
}
3) Tested my service to make sure it works by calling it from Anonymous Apex:
echo.DocSamplePort e = new echo.DocSamplePort();
e.EchoString('helloworld');
And I got this:
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header /><env:Body><EchoString xmlns="http://doc.sample.com/docSample"><input>helloworld</input></EchoString></env:Body></env:Envelope>
4) Modified my class to this ( bold shows the changes )
public class echo {
public class EchoStringResponse_element {
public String EchoStringResult;
private String[] EchoStringResult_type_info = new String[]{'EchoStringResult','http://www.w3.org/2001/XMLSchema','string','0','1','false'};
private String[] apex_schema_type_info = new String[]{'http://doc.sample.com/docSample','true','false'};
private String[] field_order_type_info = new String[]{'EchoStringResult'};
}
public class DocSamplePort {
public String endpoint_x = 'http://www.postbin.org/p97gfq';
public Map<String,String> inputHttpHeaders_x;
public Map<String,String> outputHttpHeaders_x;
public String clientCertName_x;
public String clientCert_x;
public String clientCertPasswd_x;
public Integer timeout_x;
public echo.Security_element Security;
private String Security_hns = 'Security=http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd';
private String[] ns_map_type_info = new String[]{'http://doc.sample.com/docSample', 'echo'};
public String EchoString(String input, String userid, String password) {
Security = new echo.Security_element(userid,password);
echo.EchoString_element request_x = new echo.EchoString_element();
echo.EchoStringResponse_element response_x;
request_x.input = input;
Map<String, echo.EchoStringResponse_element> response_map_x = new Map<String, echo.EchoStringResponse_element>();
response_map_x.put('response_x', response_x);
WebServiceCallout.invoke(
this,
request_x,
response_map_x,
new String[]{endpoint_x,
'urn:dotnet.callouttest.soap.sforce.com/EchoString',
'http://doc.sample.com/docSample',
'EchoString',
'http://doc.sample.com/docSample',
'EchoStringResponse',
'echo.EchoStringResponse_element'}
);
response_x = response_map_x.get('response_x');
return response_x.EchoStringResult;
}
}
public class EchoString_element {
public String input;
private String[] input_type_info = new String[]{'input','http://www.w3.org/2001/XMLSchema','string','0','1','false'};
private String[] apex_schema_type_info = new String[]{'http://doc.sample.com/docSample','true','false'};
private String[] field_order_type_info = new String[]{'input'};
}
public class Security_element{
public Security_element(String username, String password) {
usernameToken = new UsernameToken_element(username,password);
}
public UsernameToken_element usernameToken;
private String[] usernameToken_type_info = new String[]{'UsernameToken','http://www.w3.org/2001/XMLSchema','element','1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd','true','false'};
private String[] field_order_type_info = new String[]{'usernameToken'};
}
public class UsernameToken_element {
public UsernameToken_element(String username, String password) {
this.username = username;
this.password = password;
}
public String username;
public String password;
private String[] username_type_info = new String[]{'Username','http://www.w3.org/2001/XMLSchema','string','1','1','false'};
private String[] password_type_info = new String[]{'Password','http://www.w3.org/2001/XMLSchema','string','1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd','true','false'};
private String[] field_order_type_info = new String[]{'username','password'};
}
}
5) Tested it like this:
echo.DocSamplePort e = new echo.DocSamplePort();
e.EchoString('helloworld', 'username', 'password');
And got this:
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header><Security xmlns="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"><UsernameToken><Username>username</Username><Password>password</Password></UsernameToken></Security></env:Header><env:Body><EchoString xmlns="http://doc.sample.com/docSample"><input>helloworld</input></EchoString></env:Body></env:Envelope>
Done!
Hi,
I tried to add WS-Security info in proxy classes but "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" is not supported in my case so i have used "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" provided by API doc. When I tried to call the webservice I am getting error "Type must be provided for the password" - in my case server is expecting soap header as follows -
<UsernameToken>
<Username>username</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wssusername-token-profile-1.0#PasswordText">password</Password>
</UsernameToken>
instead of -
<UsernameToken>
<Username>username</Username>
<Password>password</Password>
</UsernameToken>
Please let me know if any body has implmented this, how can I modify proxy class to include attribute info for the child node.
There is an approach I found @StackOverflow to add attribute in parent node using following code-
Apex classes would be generated-
I tried some combinations for the following xml
I tried many combinations but nothing worked for me, please if somebody have implemented it successfully, let me know what i missed.
Thanks,
Lakhan
has anyone figured out what the proxy class looks like in order to generate the attribute on the password tag?
<Password Type="XXX">value</Password>
have you been able to figure out how to pass the Type attribute into the password? Thanks
Controler:- I fixed as below
// Below created date and nonce string we need to send along with the Tibco user name and password details to the stub
DateTime d = System.now();
String createdStr = d.formatGmt('yyyy-MM-dd HH:mm:ss.SSSSSS');
String nonceStr = String.valueOf(Crypto.getRandomInteger());
Blob nonceBlob = Blob.valueOf(nonceStr);
nonceStr=EncodingUtil.base64Encode(nonceBlob);
// Calling the webserivce callout..
SearchMDCPLookUpStub3.MDCPLookUpSearch stub = new SearchMDCPLookUpStub3.MDCPLookUpSearch();
stub.endpoint_x='https://g5t1173g.atlanta.hp.com/hpit/sfdc/dev/mdcp/lookupsearch';
stub.timeout_x=60000;
stub.clientCertName_x='SFDC_Certificate';
stub.inputHttpHeaders_x = new Map<String, String>();
stub.inputHttpHeaders_x.put('Content-Type', 'application/soap+xml');
stub.inputHttpHeaders_x.put('SOAPAction','/SFDCServices/MDCPLookUpSearch.serviceagent/MDCPLookUp/MDCPLookUpSearch');
SearchMDCPLookUpService3.organizationAccountSearchResponse_element accountSearchOutputElementList = stub.MDCPLookUpSearch(mdcpQuery,0,1000,'tibadmin','tibadmin',nonceStr,createdStr);
Controler:-See //SNA
public class SearchMDCPLookUpStub2 { public class MDCPLookUpSearch { public SearchMDCPLookUpStub.Security_element Security; private String Security_hns = 'Security=http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; Security = new SearchMDCPLookUpStub.Security_element(userid,password,nonce,dateVal); } public class Security_element{ public Security_element(String username, String password,String nonce,String dateVal ) { usernameToken = new UsernameToken_element(username,password,nonce,dateVal); } public UsernameToken_element usernameToken; private String[] usernameToken_type_info = new String[]{'n1:UsernameToken','http://www.w3.org/2001/XMLSchema','element','1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd','true','false'}; private String[] field_order_type_info = new String[]{'usernameToken'}; } public class UsernameToken_element { public UsernameToken_element(String username, String password,string nonce,string dateVal) { this.username = username; this.password = password; this.nonce= nonce; this.created= dateVal; } public String username; public String password; public String nonce; public String created; private String[] username_type_info = new String[]{'n1:Username','http://www.w3.org/2001/XMLSchema','string','1','1','false'}; private String[] nonce_type_info = new String[]{'n1:Nonce','http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary','string','1','1','false'}; private String[] created_type_info = new String[]{'n1:Created','http://www.w3.org/2001/XMLSchema','string','1','1','false'}; private String[] password_type_info = new String[]{'n1:Password','http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText','string','1','1','false'}; private String[] apex_schema_type_info = new String[]{'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd','true','false'}; private String[] field_order_type_info = new String[]{'username','password','nonce','created'}; }
The code is below and see if it helps for anybody.
String username= 'UrUsername';
String password= 'UrPassword';
Blob headerValue = Blob.valueOf(username + ':' + password);
String authorizationHeader = 'Basic '+ EncodingUtil.base64Encode(headerValue);
sapComDocumentSapRfcFunctions wsdlClass = new sapComDocumentSapRfcFunctions();
sapComDocumentSapRfcFunctions.ZWS_SFDC_MATERIAL_NBP zws = new sapComDocumentSapRfcFunctions.ZWS_SFDC_MATERIAL_NBP();
zws.inputHttpHeaders_x = new Map<String,String>();
zws.inputHttpHeaders_x.put('Authorization',authorizationHeader);
zws.inputHttpHeaders_x.put('Content-Type', 'text/xml;charset=UTF-8;');
zws.timeout_x = 60000;
Regards,
Surender Madipeddi