View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.dom.misc;
21  
22  import java.security.Principal;
23  import java.security.cert.X509Certificate;
24  import java.util.List;
25  
26  import javax.security.auth.callback.CallbackHandler;
27  import javax.xml.namespace.QName;
28  
29  import org.apache.wss4j.common.crypto.Crypto;
30  import org.apache.wss4j.common.crypto.CryptoFactory;
31  import org.apache.wss4j.common.crypto.CryptoType;
32  import org.apache.wss4j.common.ext.WSSecurityException;
33  import org.apache.wss4j.common.principal.SAMLTokenPrincipal;
34  import org.apache.wss4j.common.principal.UsernameTokenPrincipal;
35  import org.apache.wss4j.common.saml.SAMLCallback;
36  import org.apache.wss4j.common.saml.SAMLUtil;
37  import org.apache.wss4j.common.saml.SamlAssertionWrapper;
38  import org.apache.wss4j.common.token.BinarySecurity;
39  import org.apache.wss4j.common.token.X509Security;
40  import org.apache.wss4j.common.util.SOAPUtil;
41  import org.apache.wss4j.common.util.XMLUtils;
42  import org.apache.wss4j.dom.WSConstants;
43  import org.apache.wss4j.dom.common.SAML1CallbackHandler;
44  import org.apache.wss4j.dom.common.SAML2CallbackHandler;
45  
46  import org.apache.wss4j.dom.common.UsernamePasswordCallbackHandler;
47  import org.apache.wss4j.dom.engine.WSSConfig;
48  import org.apache.wss4j.dom.engine.WSSecurityEngine;
49  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
50  import org.apache.wss4j.dom.handler.RequestData;
51  import org.apache.wss4j.dom.handler.WSHandlerResult;
52  import org.apache.wss4j.dom.message.WSSecHeader;
53  import org.apache.wss4j.dom.message.WSSecSAMLToken;
54  import org.apache.wss4j.dom.message.WSSecUsernameToken;
55  import org.apache.wss4j.dom.util.WSSecurityUtil;
56  import org.apache.wss4j.dom.validate.Credential;
57  import org.apache.wss4j.dom.validate.Validator;
58  
59  import org.junit.jupiter.api.Test;
60  import org.w3c.dom.Document;
61  
62  import static org.junit.jupiter.api.Assertions.assertNotNull;
63  import static org.junit.jupiter.api.Assertions.assertTrue;
64  
65  /**
66   * Test various principal objects after processing a security token.
67   */
68  public class PrincipalTest {
69      private static final org.slf4j.Logger LOG =
70          org.slf4j.LoggerFactory.getLogger(PrincipalTest.class);
71  
72      private CallbackHandler callbackHandler = new UsernamePasswordCallbackHandler();
73  
74      /**
75       * Test the principal that is created after processing a Username Token
76       */
77      @Test
78      public void testUsernameToken() throws Exception {
79          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
80          WSSecHeader secHeader = new WSSecHeader(doc);
81          secHeader.insertSecurityHeader();
82  
83          WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
84          builder.setUserInfo("wernerd", "verySecret");
85          Document signedDoc = builder.build();
86  
87          if (LOG.isDebugEnabled()) {
88              String outputString =
89                  XMLUtils.prettyDocumentToString(signedDoc);
90              LOG.debug(outputString);
91          }
92          WSHandlerResult results = verify(signedDoc, null);
93  
94          Principal principal =
95              (Principal)results.getResults().get(0).get(WSSecurityEngineResult.TAG_PRINCIPAL);
96          assertTrue(principal instanceof UsernameTokenPrincipal);
97          assertTrue("wernerd".equals(principal.getName()));
98          UsernameTokenPrincipal userPrincipal = (UsernameTokenPrincipal)principal;
99          assertNotNull(userPrincipal.getCreatedTime());
100         assertNotNull(userPrincipal.getNonce());
101         assertNotNull(userPrincipal.getPassword());
102         assertTrue(userPrincipal.isPasswordDigest());
103         assertTrue(WSConstants.PASSWORD_DIGEST.equals(userPrincipal.getPasswordType()));
104     }
105 
106     /**
107      * Test the principal that is created after processing a Username Token, which has been
108      * transformed into a SAML Assertion.
109      */
110     @Test
111     public void testTransformedUsernameToken() throws Exception {
112         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
113         WSSecHeader secHeader = new WSSecHeader(doc);
114         secHeader.insertSecurityHeader();
115 
116         WSSecUsernameToken builder = new WSSecUsernameToken(secHeader);
117         builder.setUserInfo("wernerd", "verySecret");
118         Document signedDoc = builder.build();
119 
120         if (LOG.isDebugEnabled()) {
121             String outputString =
122                 XMLUtils.prettyDocumentToString(signedDoc);
123             LOG.debug(outputString);
124         }
125         WSHandlerResult results =
126             verify(signedDoc, new DummyValidator(), WSConstants.USERNAME_TOKEN, null);
127 
128         Principal principal =
129             (Principal)results.getResults().get(0).get(WSSecurityEngineResult.TAG_PRINCIPAL);
130         assertTrue(principal instanceof SAMLTokenPrincipal);
131         assertTrue(principal.getName().contains("uid=joe"));
132         assertNotNull(((SAMLTokenPrincipal)principal).getToken());
133     }
134 
135     /**
136      * Test the principal that is created after processing a SAML Token
137      */
138     @Test
139     public void testSAMLToken() throws Exception {
140         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
141         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
142         callbackHandler.setIssuer("www.example.com");
143 
144         SAMLCallback samlCallback = new SAMLCallback();
145         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
146         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
147 
148         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
149         WSSecHeader secHeader = new WSSecHeader(doc);
150         secHeader.insertSecurityHeader();
151 
152         WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
153 
154         Document unsignedDoc = wsSign.build(samlAssertion);
155 
156         if (LOG.isDebugEnabled()) {
157             String outputString =
158                 XMLUtils.prettyDocumentToString(unsignedDoc);
159             LOG.debug(outputString);
160         }
161 
162         WSHandlerResult results = verify(unsignedDoc, null);
163 
164         List<WSSecurityEngineResult> samlResults =
165             results.getActionResults().get(WSConstants.ST_UNSIGNED);
166         WSSecurityEngineResult actionResult = samlResults.get(0);
167 
168         SamlAssertionWrapper receivedSamlAssertion =
169             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
170         assertNotNull(receivedSamlAssertion);
171 
172         Principal principal = (Principal)actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
173         assertTrue(principal instanceof SAMLTokenPrincipal);
174         assertTrue(principal.getName().contains("uid=joe"));
175         assertNotNull(((SAMLTokenPrincipal)principal).getToken());
176     }
177 
178     /**
179      * Test the principal that is created after processing a SAML2 Token
180      */
181     @Test
182     public void testSAML2Token() throws Exception {
183         SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
184         callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
185         callbackHandler.setIssuer("www.example.com");
186 
187         SAMLCallback samlCallback = new SAMLCallback();
188         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
189         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
190 
191         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
192         WSSecHeader secHeader = new WSSecHeader(doc);
193         secHeader.insertSecurityHeader();
194 
195         WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
196 
197         Document unsignedDoc = wsSign.build(samlAssertion);
198 
199         if (LOG.isDebugEnabled()) {
200             String outputString =
201                 XMLUtils.prettyDocumentToString(unsignedDoc);
202             LOG.debug(outputString);
203         }
204 
205         WSHandlerResult results = verify(unsignedDoc, null);
206 
207         List<WSSecurityEngineResult> samlResults =
208             results.getActionResults().get(WSConstants.ST_UNSIGNED);
209         WSSecurityEngineResult actionResult = samlResults.get(0);
210 
211         SamlAssertionWrapper receivedSamlAssertion =
212             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
213         assertNotNull(receivedSamlAssertion);
214 
215         Principal principal = (Principal)actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
216         assertTrue(principal instanceof SAMLTokenPrincipal);
217         assertTrue(principal.getName().contains("uid=joe"));
218         assertNotNull(((SAMLTokenPrincipal)principal).getToken());
219     }
220 
221     /**
222      * Test the principal that is created after processing a SAML Token, which has been
223      * transformed into another SAML Token.
224      */
225     @Test
226     public void testTransformedSAMLToken() throws Exception {
227         SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
228         callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
229         callbackHandler.setIssuer("www.example.com");
230 
231         SAMLCallback samlCallback = new SAMLCallback();
232         SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
233         SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
234 
235         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
236         WSSecHeader secHeader = new WSSecHeader(doc);
237         secHeader.insertSecurityHeader();
238 
239         WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
240 
241         Document unsignedDoc = wsSign.build(samlAssertion);
242 
243         if (LOG.isDebugEnabled()) {
244             String outputString =
245                 XMLUtils.prettyDocumentToString(unsignedDoc);
246             LOG.debug(outputString);
247         }
248 
249         WSHandlerResult results =
250             verify(unsignedDoc, new DummyValidator(), WSConstants.SAML_TOKEN, null);
251 
252         List<WSSecurityEngineResult> samlResults =
253             results.getActionResults().get(WSConstants.ST_UNSIGNED);
254         WSSecurityEngineResult actionResult = samlResults.get(0);
255 
256         SamlAssertionWrapper receivedSamlAssertion =
257             (SamlAssertionWrapper) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
258         assertNotNull(receivedSamlAssertion);
259 
260         Principal principal = (Principal)actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
261         assertTrue(principal instanceof SAMLTokenPrincipal);
262         assertTrue(principal.getName().contains("uid=joe"));
263         assertNotNull(((SAMLTokenPrincipal)principal).getToken());
264     }
265 
266     /**
267      * Test the principal that is created after processing (and explicitly validating)
268      * a BinarySecurityToken.
269      */
270     @Test
271     public void testBinarySecurityToken() throws Exception {
272         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
273 
274         WSSecHeader secHeader = new WSSecHeader(doc);
275         secHeader.insertSecurityHeader();
276 
277         X509Security bst = new X509Security(doc);
278         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
279         cryptoType.setAlias("wss40");
280         Crypto crypto = CryptoFactory.getInstance("wss40.properties");
281         X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
282         bst.setX509Certificate(certs[0]);
283 
284         WSSecurityUtil.prependChildElement(secHeader.getSecurityHeaderElement(), bst.getElement());
285 
286         if (LOG.isDebugEnabled()) {
287             String outputString =
288                 XMLUtils.prettyDocumentToString(doc);
289             LOG.debug(outputString);
290         }
291 
292         WSHandlerResult results =
293             verify(doc, new DummyValidator(), WSConstants.BINARY_TOKEN, crypto);
294 
295         List<WSSecurityEngineResult> bstResults =
296             results.getActionResults().get(WSConstants.BST);
297         WSSecurityEngineResult actionResult = bstResults.get(0);
298 
299         BinarySecurity token =
300             (BinarySecurity)actionResult.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
301         assertNotNull(token);
302 
303         Principal principal = (Principal)actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
304         assertTrue(principal instanceof SAMLTokenPrincipal);
305         assertTrue(principal.getName().contains("uid=joe"));
306         assertNotNull(((SAMLTokenPrincipal)principal).getToken());
307     }
308 
309     /**
310      * Verifies the soap envelope
311      */
312     private WSHandlerResult verify(
313         Document doc,
314         Crypto crypto
315     ) throws Exception {
316         return verify(doc, null, null, crypto);
317     }
318 
319     /**
320      * Verifies the soap envelope
321      */
322     private WSHandlerResult verify(
323         Document doc,
324         Validator validator,
325         QName validatorName,
326         Crypto crypto
327     ) throws Exception {
328         RequestData requestData = new RequestData();
329         requestData.setCallbackHandler(callbackHandler);
330         requestData.setDecCrypto(crypto);
331         requestData.setSigVerCrypto(crypto);
332         requestData.setValidateSamlSubjectConfirmation(false);
333 
334         WSSecurityEngine secEngine = new WSSecurityEngine();
335         WSSConfig config = WSSConfig.getNewInstance();
336         secEngine.setWssConfig(config);
337 
338         if (validator != null && validatorName != null) {
339             config.setValidator(validatorName, validator);
340         }
341         return secEngine.processSecurityHeader(doc, requestData);
342     }
343 
344     /**
345      * A Dummy Validator instance that just creates a new SAML Assertion, ignoring the
346      * credential it has been passed.
347      */
348     private static class DummyValidator implements Validator {
349 
350         public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
351             try {
352                 SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
353                 callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
354                 callbackHandler.setIssuer("www.example.com");
355 
356                 SAMLCallback samlCallback = new SAMLCallback();
357                 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
358                 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
359 
360                 credential.setTransformedToken(samlAssertion);
361                 return credential;
362             } catch (Exception ex) {
363                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
364             }
365         }
366 
367     }
368 }