/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package org.codice.ddf.security.handler.api;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.ws.security.sts.provider.model.secext.BinarySecurityTokenType;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.dom.WSConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class BSTAuthenticationToken extends BaseAuthenticationToken {
public static final String BASE64_ENCODING = WSConstants.SOAPMESSAGE_NS + "#Base64Binary";
public static final String BST_NS = "urn:org:codice:security:sso";
public static final String BST_LN = "Token";
public static final String TOKEN_VALUE_SEPARATOR = "#";
protected static final String BST_PRINCIPAL = "Principal:";
protected static final String BST_CREDENTIALS = "Credentials:";
protected static final String BST_REALM = "Realm:";
protected static final String NEWLINE = "\n";
private static final Logger LOGGER = LoggerFactory.getLogger(BSTAuthenticationToken.class);
private static final JAXBContext BINARY_TOKEN_CONTEXT = initContext();
// values to be included in the binary security token - specific to each subclass
protected String tokenValueType = BST_NS + TOKEN_VALUE_SEPARATOR + BST_LN;
protected String tokenId = BST_LN;
public BSTAuthenticationToken(Object principal, Object credentials) {
this(principal, credentials, DEFAULT_REALM);
}
public BSTAuthenticationToken(Object principal, Object credentials, String realm) {
super(principal, realm, credentials);
}
private static JAXBContext initContext() {
try {
return JAXBContext.newInstance(BinarySecurityTokenType.class);
} catch (JAXBException e) {
LOGGER.info("Unable to create BinarySecurityToken JAXB context.", e);
}
return null;
}
/**
* Creates an instance of BaseAuthenticationToken by parsing the given credential string. The
* passed boolean indicates if the provided credentials are encoded or not.
* If the string contains the necessary components (username, password, realm), a new instance of
* BaseAuthenticationToken is created and initialized with the credentials. If not, a null value
* is returned.
*
* @param stringBST unencoded credentials string
* @return initialized username/password token if parsed successfully, null otherwise
*/
public static BaseAuthenticationToken parse(String stringBST, boolean isEncoded)
throws WSSecurityException {
BaseAuthenticationToken baseAuthenticationToken = null;
org.apache.xml.security.Init.init();
String unencodedCreds = isEncoded ? new String(Base64.getDecoder()
.decode(stringBST), StandardCharsets.UTF_8) : stringBST;
if (!StringUtils.isEmpty(unencodedCreds) && unencodedCreds.startsWith(BST_PRINCIPAL)) {
String[] components = unencodedCreds.split(NEWLINE);
if (components.length == 3) {
String p = parseComponent(components[0], BST_PRINCIPAL);
String c = parseComponent(components[1], BST_CREDENTIALS);
String r = parseComponent(components[2], BST_REALM);
baseAuthenticationToken = new BaseAuthenticationToken(p, r, c);
}
}
if (baseAuthenticationToken == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
"Exception decoding specified credentials. Unable to find required components.");
}
return baseAuthenticationToken;
}
protected static String parseComponent(String s, String expectedStartsWith) {
return StringUtils.substringAfter(s, expectedStartsWith);
}
@Override
public String getCredentialsAsXMLString() {
return getBinarySecurityToken();
}
public String getBinarySecurityToken() {
return getBinarySecurityToken(getEncodedCredentials());
}
public String getEncodedCredentials() {
StringBuilder builder = new StringBuilder();
builder.append(BST_PRINCIPAL);
builder.append(getPrincipal());
builder.append(NEWLINE);
builder.append(BST_CREDENTIALS);
builder.append(getCredentials());
builder.append(NEWLINE);
builder.append(BST_REALM);
builder.append(getRealm());
String retVal = builder.toString();
if (LOGGER.isTraceEnabled()) {
String[] lines = retVal.split(NEWLINE);
if (lines.length >= 3) {
LOGGER.trace("Credentials String: {}\n{}\n{}",
lines[0],
BST_CREDENTIALS + "******",
lines[2]);
}
}
LOGGER.trace("Credential String: {}", retVal);
String encodedCreds = Base64.getEncoder()
.encodeToString(builder.toString()
.getBytes(StandardCharsets.UTF_8));
LOGGER.trace("BST: {}", encodedCreds);
return encodedCreds;
}
/**
* Creates a binary security token based on the provided credential.
*/
private synchronized String getBinarySecurityToken(String credential) {
Writer writer = new StringWriter();
Marshaller marshaller = null;
BinarySecurityTokenType binarySecurityTokenType = createBinarySecurityTokenType(credential);
JAXBElement<BinarySecurityTokenType> binarySecurityTokenElement =
new JAXBElement<BinarySecurityTokenType>(new QName(
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"BinarySecurityToken"),
BinarySecurityTokenType.class,
binarySecurityTokenType);
if (BINARY_TOKEN_CONTEXT != null) {
try {
marshaller = BINARY_TOKEN_CONTEXT.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
} catch (JAXBException e) {
LOGGER.debug("Exception while creating UsernameToken marshaller.", e);
}
if (marshaller != null) {
try {
marshaller.marshal(binarySecurityTokenElement, writer);
} catch (JAXBException e) {
LOGGER.debug("Exception while writing username token.", e);
}
}
}
String binarySecurityToken = writer.toString();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Binary Security Token: {}", binarySecurityToken);
}
return binarySecurityToken;
}
public BinarySecurityTokenType createBinarySecurityTokenType(String credentials) {
BinarySecurityTokenType binarySecurityTokenType = new BinarySecurityTokenType();
binarySecurityTokenType.setValueType(tokenValueType);
binarySecurityTokenType.setEncodingType(BASE64_ENCODING);
binarySecurityTokenType.setId(tokenId);
binarySecurityTokenType.setValue(credentials);
return binarySecurityTokenType;
}
public void setTokenValueType(String ns, String ln) {
this.tokenValueType = ns + TOKEN_VALUE_SEPARATOR + ln;
}
public void setTokenId(String tid) {
this.tokenId = tid;
}
}