| /** |
| * 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; |
| } |
| } |
| |
| } |