blob: 0e6d7a921ae4acdc0706784cc284ee9197b20220 [file] [log] [blame]
/**
* 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.wss4j.dom;
/**
* WSDocInfo holds information about the document to process. It provides a
* method to store and access document information about BinarySecurityToken,
* used Crypto, and others.
*
* Using the Document's hash a caller can identify a document and get
* the stored information that me be necessary to process the document.
* The main usage for this is (are) the transformation functions that
* are called during Signature/Verification process.
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.xml.crypto.dom.DOMCryptoContext;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.XMLUtils;
import org.apache.wss4j.dom.callback.CallbackLookup;
import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class WSDocInfo {
private Document doc;
private Crypto crypto;
// Here we map the token "Id" to the token itself. The token "Id" is the key as it must be unique to guard
// against various wrapping attacks. The "Id" name/namespace is stored as part of the entry (along with the
// element), so that we know what namespace to use when setting the token on the crypto context for signature
// creation or validation
private final Map<String, TokenValue> tokens = new HashMap<>();
private final List<WSSecurityEngineResult> results = new LinkedList<>();
private final Map<Integer, List<WSSecurityEngineResult>> actionResults = new HashMap<>();
private CallbackLookup callbackLookup;
private Element securityHeader;
public WSDocInfo(Document doc) {
//
// This is a bit of a hack. When the Document is a SAAJ SOAPPart instance, it may
// be that the "owner" document of any child elements is an internal Document, rather
// than the SOAPPart. This is the case for the SUN SAAJ implementation.
//
if (doc != null && doc.getDocumentElement() != null) {
this.doc = doc.getDocumentElement().getOwnerDocument();
} else {
this.doc = doc;
}
}
/**
* Clears the data stored in this object
*/
public void clear() {
crypto = null;
doc = null;
callbackLookup = null;
securityHeader = null;
tokens.clear();
results.clear();
actionResults.clear();
}
/**
* Store a token element for later retrieval. Before storing the token, we check for a
* previously processed token with the same (wsu/SAML) Id.
* @param element is the token element to store
*/
public void addTokenElement(Element element) throws WSSecurityException {
addTokenElement(element, true);
}
/**
* Store a token element for later retrieval. Before storing the token, we check for a
* previously processed token with the same (wsu/SAML) Id.
* @param element is the token element to store
* @param checkMultipleElements check for a previously stored element with the same Id.
*/
public void addTokenElement(Element element, boolean checkMultipleElements) throws WSSecurityException {
if (element == null) {
return;
}
if (element.hasAttributeNS(WSConstants.WSU_NS, "Id")) {
String id = element.getAttributeNS(WSConstants.WSU_NS, "Id");
TokenValue tokenValue = new TokenValue("Id", WSConstants.WSU_NS, element);
TokenValue previousValue = tokens.put(id, tokenValue);
if (checkMultipleElements && previousValue != null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "duplicateError"
);
}
}
if (element.hasAttributeNS(null, "Id")) {
String id = element.getAttributeNS(null, "Id");
TokenValue tokenValue = new TokenValue("Id", null, element);
TokenValue previousValue = tokens.put(id, tokenValue);
if (checkMultipleElements && previousValue != null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "duplicateError"
);
}
}
// SAML Assertions
if ("Assertion".equals(element.getLocalName())) {
if (WSConstants.SAML_NS.equals(element.getNamespaceURI())
&& element.hasAttributeNS(null, "AssertionID")) {
String id = element.getAttributeNS(null, "AssertionID");
TokenValue tokenValue = new TokenValue("AssertionID", null, element);
TokenValue previousValue = tokens.put(id, tokenValue);
if (checkMultipleElements && previousValue != null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "duplicateError"
);
}
} else if (WSConstants.SAML2_NS.equals(element.getNamespaceURI())
&& element.hasAttributeNS(null, "ID")) {
String id = element.getAttributeNS(null, "ID");
TokenValue tokenValue = new TokenValue("ID", null, element);
TokenValue previousValue = tokens.put(id, tokenValue);
if (checkMultipleElements && previousValue != null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "duplicateError"
);
}
}
}
}
/**
* Get a token Element for the given Id. The Id can be either a wsu:Id or a
* SAML AssertionID/ID.
* @param uri is the (relative) uri of the id
* @return the token element or null if nothing found
*/
public Element getTokenElement(String uri) {
String id = XMLUtils.getIDFromReference(uri);
if (id == null) {
return null;
}
TokenValue token = tokens.get(id);
if (token != null) {
return token.getToken();
}
return null;
}
/**
* Set all stored tokens on the DOMCryptoContext argument
* @param context
*/
public void setTokensOnContext(DOMCryptoContext context) {
if (!tokens.isEmpty() && context != null) {
for (Map.Entry<String, TokenValue> entry : tokens.entrySet()) {
TokenValue tokenValue = entry.getValue();
context.setIdAttributeNS(tokenValue.getToken(), tokenValue.getIdNamespace(),
tokenValue.getIdName());
}
}
}
public void setTokenOnContext(String uri, DOMCryptoContext context) {
String id = XMLUtils.getIDFromReference(uri);
if (id == null || context == null) {
return;
}
TokenValue tokenValue = tokens.get(id);
if (tokenValue != null) {
context.setIdAttributeNS(tokenValue.getToken(), tokenValue.getIdNamespace(),
tokenValue.getIdName());
}
}
/**
* Store a WSSecurityEngineResult for later retrieval.
* @param result is the WSSecurityEngineResult to store
*/
public void addResult(WSSecurityEngineResult result) {
results.add(result);
Integer resultTag = (Integer)result.get(WSSecurityEngineResult.TAG_ACTION);
if (resultTag != null) {
List<WSSecurityEngineResult> storedResults = actionResults.get(resultTag);
if (storedResults == null) {
storedResults = new ArrayList<>();
}
storedResults.add(result);
actionResults.put(resultTag, storedResults);
}
}
/**
* Get a copy of the security results list. Modifying the subsequent list does not
* change the internal results list.
*/
public List<WSSecurityEngineResult> getResults() {
if (results.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<>(results);
}
/**
* Return a copy of the map between security actions + results. Modifying the subsequent
* map does not change the internal map.
*/
public Map<Integer, List<WSSecurityEngineResult>> getActionResults() {
if (actionResults.isEmpty()) {
return Collections.emptyMap();
}
return new HashMap<>(actionResults);
}
/**
* Get a WSSecurityEngineResult for the given Id.
* @param uri is the (relative) uri of the id
* @return the WSSecurityEngineResult or null if nothing found
*/
public WSSecurityEngineResult getResult(String uri) {
String id = XMLUtils.getIDFromReference(uri);
if (id == null) {
return null;
}
if (!results.isEmpty()) {
for (WSSecurityEngineResult result : results) {
String cId = (String)result.get(WSSecurityEngineResult.TAG_ID);
if (id.equals(cId)) {
return result;
}
}
}
return null;
}
/**
* Get a unmodifiable list of WSSecurityEngineResults of the given Integer tag
*/
public List<WSSecurityEngineResult> getResultsByTag(Integer tag) {
if (actionResults.isEmpty() || !actionResults.containsKey(tag)) {
return Collections.emptyList();
}
return Collections.unmodifiableList(actionResults.get(tag));
}
/**
* See whether we have a WSSecurityEngineResult of the given Integer tag for the given Id
*/
public boolean hasResult(Integer tag, String uri) {
String id = XMLUtils.getIDFromReference(uri);
if (id == null || "".equals(uri)) {
return false;
}
if (!actionResults.isEmpty() && actionResults.containsKey(tag)) {
for (WSSecurityEngineResult result : actionResults.get(tag)) {
String cId = (String)result.get(WSSecurityEngineResult.TAG_ID);
if (id.equals(cId)) {
return true;
}
}
}
return false;
}
/**
* @return the signature crypto class used to process
* the signature/verify
*/
public Crypto getCrypto() {
return crypto;
}
/**
* @return the document
*/
public Document getDocument() {
return doc;
}
/**
* @param crypto is the signature crypto class used to
* process signature/verify
*/
public void setCrypto(Crypto crypto) {
this.crypto = crypto;
}
/**
* @param callbackLookup The CallbackLookup object to retrieve elements
*/
public void setCallbackLookup(CallbackLookup callbackLookup) {
this.callbackLookup = callbackLookup;
}
/**
* @return the CallbackLookup object to retrieve elements
*/
public CallbackLookup getCallbackLookup() {
return callbackLookup;
}
/**
* @return the wsse header being processed
*/
public Element getSecurityHeader() {
return securityHeader;
}
/**
* Sets the wsse header being processed
*
* @param securityHeader
*/
public void setSecurityHeader(Element securityHeader) {
this.securityHeader = securityHeader;
}
private static class TokenValue {
private final String idName;
private final String idNamespace;
private final Element token;
TokenValue(String idName, String idNamespace, Element token) {
this.idName = idName;
this.idNamespace = idNamespace;
this.token = token;
}
public String getIdName() {
return idName;
}
public String getIdNamespace() {
return idNamespace;
}
public Element getToken() {
return token;
}
}
}