package com.microsoft.aad.adal4j;
import javax.net.ssl.SSLSocketFactory;
import java.net.Proxy;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class WSTrustRequest {
private final static Logger log = LoggerFactory
.getLogger(WSTrustRequest.class);
private final static int MAX_EXPECTED_MESSAGE_SIZE = 1024;
final static String DEFAULT_APPLIES_TO = "urn:federation:MicrosoftOnline";
static WSTrustResponse execute(String username, String password, String cloudAudienceUrn, BindingPolicy policy,
Proxy proxy, SSLSocketFactory sslSocketFactory) throws Exception {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/soap+xml; charset=utf-8");
headers.put("return-client-request-id", "true");
String soapAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue";
if (policy.getVersion() == WSTrustVersion.WSTRUST2005) {
soapAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue";
}
headers.put("SOAPAction", soapAction);
String body = buildMessage(policy.getUrl(), username, password,
policy.getVersion(), cloudAudienceUrn).toString();
String response = HttpHelper.executeHttpPost(log, policy.getUrl(),
body, headers, proxy, sslSocketFactory);
return WSTrustResponse.parse(response, policy.getVersion());
}
static WSTrustResponse execute(String url, String username, String password, String cloudAudienceUrn,
Proxy proxy, SSLSocketFactory sslSocketFactory, boolean logPii) throws Exception {
String mexResponse = HttpHelper.executeHttpGet(log, url, proxy, sslSocketFactory);
BindingPolicy policy = MexParser.getWsTrustEndpointFromMexResponse(mexResponse, logPii);
if(policy == null){
throw new AuthenticationException("WsTrust endpoint not found in metadata document");
}
return execute(username, password, cloudAudienceUrn, policy, proxy, sslSocketFactory);
}
static WSTrustResponse execute(String mexURL, String cloudAudienceUrn, Proxy proxy,
SSLSocketFactory sslSocketFactory, boolean logPii) throws Exception {
String mexResponse = HttpHelper.executeHttpGet(log, mexURL, proxy, sslSocketFactory);
BindingPolicy policy = MexParser.getPolicyFromMexResponseForIntegrated(mexResponse, logPii);
if(policy == null){
throw new AuthenticationException("WsTrust endpoint not found in metadata document");
}
return execute(null, null, cloudAudienceUrn, policy, proxy, sslSocketFactory);
}
static StringBuilder buildMessage(String address, String username,
String password, WSTrustVersion addressVersion, String cloudAudienceUrn) {
boolean integrated = (username == null) & (password == null);
StringBuilder securityHeaderBuilder = new StringBuilder(MAX_EXPECTED_MESSAGE_SIZE);
if (!integrated) {
buildSecurityHeader(securityHeaderBuilder, username, password, addressVersion);
}
String guid = UUID.randomUUID().toString();
StringBuilder messageBuilder = new StringBuilder(
MAX_EXPECTED_MESSAGE_SIZE);
String schemaLocation = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
String soapAction = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue";
String rstTrustNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512";
String keyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer";
String requestType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue";
if (addressVersion == WSTrustVersion.WSTRUST2005) {
soapAction = "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue";
rstTrustNamespace = "http://schemas.xmlsoap.org/ws/2005/02/trust";
keyType = "http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey";
requestType = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue";
}
messageBuilder
.append(String
.format("<s:Envelope xmlns:s='http://www.w3.org/2003/05/soap-envelope' xmlns:a='http://www.w3.org/2005/08/addressing' xmlns:u='%s'>"
+ "<s:Header>"
+ "<a:Action s:mustUnderstand='1'>%s</a:Action>"
+ "<a:messageID>urn:uuid:"
+ "%s"
+
"</a:messageID>"
+ "<a:ReplyTo>"
+ "<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>"
+ "</a:ReplyTo>"
+ "<a:To s:mustUnderstand='1'>"
+ "%s"
+
"</a:To>"
+ "%s"
+
"</s:Header>"
+ "<s:Body>"
+ "<trust:RequestSecurityToken xmlns:trust='%s'>"
+ "<wsp:AppliesTo xmlns:wsp='http://schemas.xmlsoap.org/ws/2004/09/policy'>"
+ "<a:EndpointReference>"
+ "<a:Address>"
+ "%s"
+
"</a:Address>"
+ "</a:EndpointReference>"
+ "</wsp:AppliesTo>"
+ "<trust:KeyType>%s</trust:KeyType>"
+ "<trust:RequestType>%s</trust:RequestType>"
+
"</trust:RequestSecurityToken>"
+ "</s:Body>"
+ "</s:Envelope>", schemaLocation, soapAction,
guid, address,
integrated ? "" : securityHeaderBuilder.toString(),
rstTrustNamespace,
StringUtils.isNotEmpty(cloudAudienceUrn) ? cloudAudienceUrn : DEFAULT_APPLIES_TO,
keyType,
requestType));
return messageBuilder;
}
private static StringBuilder (
StringBuilder securityHeaderBuilder, String username,
String password, WSTrustVersion version) {
StringBuilder messageCredentialsBuilder = new StringBuilder(
MAX_EXPECTED_MESSAGE_SIZE);
String guid = UUID.randomUUID().toString();
username = StringEscapeUtils.escapeXml10(username);
password = StringEscapeUtils.escapeXml10(password);
DateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = new Date();
String currentTimeString = dateFormat.format(date);
int toAdd = 60 * 1000 * 10;
date = new Date(date.getTime() + toAdd);
String expiryTimeString = dateFormat.format(date);
messageCredentialsBuilder.append(String.format(
"<o:UsernameToken u:Id='uuid-" + "%s'>" +
"<o:Username>%s</o:Username>" +
"<o:Password>%s</o:Password>" +
"</o:UsernameToken>", guid, username, password));
securityHeaderBuilder
.append("<o:Security s:mustUnderstand='1' xmlns:o='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'>");
securityHeaderBuilder.append(String.format("<u:Timestamp u:Id='_0'>"
+ "<u:Created>%s</u:Created>" +
"<u:Expires>%s</u:Expires>" +
"</u:Timestamp>", currentTimeString, expiryTimeString));
securityHeaderBuilder.append(messageCredentialsBuilder.toString());
securityHeaderBuilder.append("</o:Security>");
return securityHeaderBuilder;
}
}