/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.sts.token.provider;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.sts.STSConstants;
import org.apache.cxf.sts.request.Renewing;
import org.apache.cxf.sts.request.TokenRequirements;
import org.apache.cxf.ws.security.sts.provider.STSException;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.cxf.ws.security.trust.STSUtils;
import org.apache.wss4j.common.derivedKey.ConversationConstants;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.dom.engine.WSSConfig;
import org.apache.wss4j.dom.message.token.SecurityContextToken;
/**
* A TokenProvider implementation that provides a SecurityContextToken.
*/
public class SCTProvider implements TokenProvider {
private static final Logger LOG = LogUtils.getL7dLogger(SCTProvider.class);
private boolean returnEntropy = true;
private long lifetime = 60L * 30L;
/**
* Return the lifetime of the generated SCT
* @return the lifetime of the generated SCT
*/
public long getLifetime() {
return lifetime;
}
/**
* Set the lifetime of the generated SCT
* @param lifetime the lifetime of the generated SCT
*/
public void setLifetime(long lifetime) {
this.lifetime = lifetime;
}
/**
* Return true if this TokenProvider implementation is capable of providing a token
* that corresponds to the given TokenType.
*/
public boolean canHandleToken(String tokenType) {
return canHandleToken(tokenType, null);
}
/**
* Return true if this TokenProvider implementation is capable of providing a token
* that corresponds to the given TokenType in a given realm. The realm is ignored in this
* token provider.
*/
public boolean canHandleToken(String tokenType, String realm) {
return STSUtils.TOKEN_TYPE_SCT_05_02.equals(tokenType)
|| STSUtils.TOKEN_TYPE_SCT_05_12.equals(tokenType);
}
/**
* Set whether Entropy is returned to the client or not
* @param returnEntropy whether Entropy is returned to the client or not
*/
public void setReturnEntropy(boolean returnEntropy) {
this.returnEntropy = returnEntropy;
}
/**
* Get whether Entropy is returned to the client or not
* @return whether Entropy is returned to the client or not
*/
public boolean isReturnEntropy() {
return returnEntropy;
}
/**
* Create a token given a TokenProviderParameters
*/
public TokenProviderResponse createToken(TokenProviderParameters tokenParameters) {
TokenRequirements tokenRequirements = tokenParameters.getTokenRequirements();
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Handling token of type: " + tokenRequirements.getTokenType());
}
if (tokenParameters.getTokenStore() == null) {
LOG.log(Level.FINE, "A cache must be configured to use the SCTProvider");
throw new STSException("Can't serialize SCT", STSException.REQUEST_FAILED);
}
SymmetricKeyHandler keyHandler = new SymmetricKeyHandler(tokenParameters);
keyHandler.createSymmetricKey();
try {
Document doc = DOMUtils.createDocument();
SecurityContextToken sct =
new SecurityContextToken(getWSCVersion(tokenRequirements.getTokenType()), doc);
WSSConfig wssConfig = WSSConfig.getNewInstance();
sct.setID(wssConfig.getIdAllocator().createId("sctId-", sct));
TokenProviderResponse response = new TokenProviderResponse();
response.setTokenId(sct.getIdentifier());
if (returnEntropy) {
response.setEntropy(keyHandler.getEntropyBytes());
}
long keySize = keyHandler.getKeySize();
response.setKeySize(keySize);
response.setComputedKey(keyHandler.isComputedKey());
// putting the secret key into the cache
Instant created = Instant.now();
response.setCreated(created);
Instant expires = null;
if (lifetime > 0) {
expires = created.plusSeconds(lifetime);
response.setExpires(expires);
}
SecurityToken token =
new SecurityToken(sct.getIdentifier(), created, expires);
token.setSecret(keyHandler.getSecret());
token.setPrincipal(tokenParameters.getPrincipal());
Map<String, Object> props = token.getProperties();
if (props == null) {
props = new HashMap<>();
}
token.setProperties(props);
if (tokenParameters.getRealm() != null) {
props.put(STSConstants.TOKEN_REALM, tokenParameters.getRealm());
}
// Handle Renewing logic
Renewing renewing = tokenParameters.getTokenRequirements().getRenewing();
if (renewing != null) {
props.put(
STSConstants.TOKEN_RENEWING_ALLOW,
String.valueOf(renewing.isAllowRenewing())
);
props.put(
STSConstants.TOKEN_RENEWING_ALLOW_AFTER_EXPIRY,
String.valueOf(renewing.isAllowRenewingAfterExpiry())
);
} else {
props.put(STSConstants.TOKEN_RENEWING_ALLOW, "true");
props.put(STSConstants.TOKEN_RENEWING_ALLOW_AFTER_EXPIRY, "false");
}
tokenParameters.getTokenStore().add(token);
if (tokenParameters.isEncryptToken()) {
Element el = TokenProviderUtils.encryptToken(sct.getElement(), response.getTokenId(),
tokenParameters.getStsProperties(),
tokenParameters.getEncryptionProperties(),
tokenParameters.getKeyRequirements(),
tokenParameters.getMessageContext());
response.setToken(el);
} else {
response.setToken(sct.getElement());
}
// Create the references
TokenReference attachedReference = new TokenReference();
attachedReference.setIdentifier(sct.getID());
attachedReference.setUseDirectReference(true);
attachedReference.setWsseValueType(tokenRequirements.getTokenType());
response.setAttachedReference(attachedReference);
TokenReference unAttachedReference = new TokenReference();
unAttachedReference.setIdentifier(sct.getIdentifier());
unAttachedReference.setUseDirectReference(true);
unAttachedReference.setWsseValueType(tokenRequirements.getTokenType());
response.setUnattachedReference(unAttachedReference);
LOG.fine("SecurityContextToken successfully created");
return response;
} catch (Exception e) {
LOG.log(Level.WARNING, "", e);
throw new STSException("Can't serialize SCT", e, STSException.REQUEST_FAILED);
}
}
/**
* Get the Secure Conversation version from the TokenType parameter
*/
private static int getWSCVersion(String tokenType) throws WSSecurityException {
if (tokenType == null) {
return ConversationConstants.DEFAULT_VERSION;
}
if (tokenType.startsWith(ConversationConstants.WSC_NS_05_02)) {
return ConversationConstants.getWSTVersion(ConversationConstants.WSC_NS_05_02);
} else if (tokenType.startsWith(ConversationConstants.WSC_NS_05_12)) {
return ConversationConstants.getWSTVersion(ConversationConstants.WSC_NS_05_12);
} else {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
"unsupportedSecConvVersion");
}
}
}