This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git


The following commit(s) were added to refs/heads/master by this push:
     new 2a67fc398 Properly wire UsernameToken + RSA15 flags for the streaming 
layer (#717)
2a67fc398 is described below

commit 2a67fc398cbe7cb4b9315773bf73b008ed6d9e95
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Mon Sep 21 09:29:11 2026 +0100

    Properly wire UsernameToken + RSA15 flags for the streaming layer (#717)
---
 THREAT-MODEL.md                                    |  27 ++++
 src/site/asciidoc/streaming.adoc                   |   8 ++
 .../wss4j/policy/stax/enforcer/PolicyEnforcer.java | 124 ++++++++++++++---
 .../policy/stax/enforcer/PolicyInputProcessor.java |  16 ++-
 .../policy/stax/test/ScopedEngineDefaultsTest.java | 155 +++++++++++++++++++++
 .../testdata/wsdl/mixedPasswordPolicies.wsdl       |  98 +++++++++++++
 6 files changed, 404 insertions(+), 24 deletions(-)

diff --git a/THREAT-MODEL.md b/THREAT-MODEL.md
index 5852447bf..0ea98b153 100644
--- a/THREAT-MODEL.md
+++ b/THREAT-MODEL.md
@@ -573,6 +573,33 @@ on each is captured in §14 Q10–Q11.
 - *(documented:
   `ws-security-policy-stax/src/test/java/.../VulnerabliltyVectorsTest.java`)*
 
+### P12 — An engine default relaxed by policy is scoped to the operation the 
message invokes (StAX policy mode)
+
+- **Condition**: streaming engine in policy mode (`PolicyInputProcessor`),
+  where the caller has not itself set `AllowUsernameTokenNoPassword` or
+  `AllowRSA15KeyTransportAlgorithm`. Those are explicit overrides by the
+  caller and are left alone.
+- **Violation symptom**: one operation of an endpoint asks for
+  `sp:NoPassword`, or for an AlgorithmSuite whose asymmetric key wrap is
+  rsa-1_5, and the engine's corresponding hardened default is thereby
+  lowered for every other operation of that endpoint — so a
+  password-less UsernameToken is accepted for an operation whose policy
+  names no UsernameToken at all, and is examined by no assertion.
+- **Mechanism**: the relaxation is decided while the security header is
+  read, before the Body names the operation. It is therefore scoped to
+  the operation's own policy where SOAPAction already selected it, and
+  otherwise granted across the policy set and reimposed once the
+  operation is known.
+- **Residual**: an rsa-1_5 unwrap granted across the policy set is
+  performed before the operation is known. The message is rejected, but
+  the unwrap has happened, so the oracle surface of §8 P4 is reachable
+  for an operation whose own policy forbids rsa-1_5. Only a
+  SOAPAction-selected operation avoids that.
+- **Severity**: **security-critical** for the UsernameToken case;
+  `VALID-HARDENING` for the rsa-1_5 case.
+- *(documented:
+  `ws-security-policy-stax/src/test/java/.../ScopedEngineDefaultsTest.java`)*
+
 ## §9 Security properties the project does *not* provide
 
 State each plainly so a triager can route an inbound report to the
diff --git a/src/site/asciidoc/streaming.adoc b/src/site/asciidoc/streaming.adoc
index 3d5484c85..705fc26e1 100644
--- a/src/site/asciidoc/streaming.adoc
+++ b/src/site/asciidoc/streaming.adoc
@@ -87,6 +87,14 @@ nothing on its own. Check the effective policy if you rely 
on a nested
 assertion being enforced.
  * Where a compact policy offers several alternatives within a nested policy,
 only the first is read.
+ * A hardened engine default that WS-SecurityPolicy relaxes - accepting a
+password-less UsernameToken for an sp:NoPassword policy, or rsa-1_5 key 
transport
+for an AlgorithmSuite that asks for it - has to be decided while the security
+header is read, before the Body says which operation the message invokes. Where
+SOAPAction has already selected the operation only that operation's policy is
+consulted. Otherwise the relaxation is granted across the endpoint's operations
+and reimposed once the operation is known, which rejects the message but, for
+rsa-1_5, only after the key has been unwrapped.
  * The REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS 
("requireSignedEncryptedDataElements")
 configuration tag is not read by the streaming code and is not enforced by it.
 The tag is shared between both stacks, and the streaming ConfigurationConverter
diff --git 
a/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyEnforcer.java
 
b/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyEnforcer.java
index db1322d96..c3280c74f 100644
--- 
a/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyEnforcer.java
+++ 
b/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyEnforcer.java
@@ -104,8 +104,10 @@ import 
org.apache.wss4j.policy.stax.assertionStates.X509TokenAssertionState;
 import org.apache.wss4j.stax.ext.WSSConstants;
 import org.apache.wss4j.stax.securityEvent.NoSecuritySecurityEvent;
 import org.apache.wss4j.stax.securityEvent.OperationSecurityEvent;
+import org.apache.wss4j.stax.securityEvent.UsernameTokenSecurityEvent;
 import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
 import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.stax.securityEvent.AlgorithmSuiteSecurityEvent;
 import org.apache.xml.security.stax.securityEvent.SecurityEvent;
 import org.apache.xml.security.stax.securityEvent.SecurityEventConstants;
 import org.apache.xml.security.stax.securityEvent.SecurityEventListener;
@@ -156,6 +158,8 @@ public class PolicyEnforcer implements 
SecurityEventListener {
     private boolean faultOccurred;
     private final PolicyAsserter policyAsserter;
     private boolean soap12;
+    private boolean usernameTokenNoPasswordRelaxedForAllOperations;
+    private boolean rsa15KeyTransportRelaxedForAllOperations;
 
     public PolicyEnforcer(List<OperationPolicy> operationPolicies, String 
soapAction, boolean initiator,
                           String actorOrRole, int attachmentCount, 
PolicyAsserter policyAsserter, boolean soap12
@@ -892,6 +896,11 @@ public class PolicyEnforcer implements 
SecurityEventListener {
                         new IllegalArgumentException(message));
             }
 
+            //The operation is known now, so an engine default relaxed on the 
strength of
+            //the whole policy set can be reimposed where this operation's 
policy does not
+            //ask for it.
+            verifyRelaxedEngineDefaults();
+
             try {
                 Iterator<SecurityEvent> securityEventIterator = 
securityEventQueue.descendingIterator();
                 while (securityEventIterator.hasNext()) {
@@ -923,18 +932,25 @@ public class PolicyEnforcer implements 
SecurityEventListener {
     }
 
     /**
-     * Returns true if any configured operation policy contains a 
UsernameToken assertion
-     * that explicitly allows password-less tokens (sp:NoPassword). Used to 
decide whether
-     * the engine's hardened default (rejecting password-less UsernameTokens) 
may be
-     * relaxed in policy mode.
+     * Returns true if the policy that governs this message contains a 
UsernameToken
+     * assertion that explicitly allows password-less tokens (sp:NoPassword). 
Used to
+     * decide whether the engine's hardened default (rejecting password-less
+     * UsernameTokens) may be relaxed in policy mode.
+     *
+     * This is asked while the security header is being read, so the operation 
is known
+     * only where SOAPAction already selected its policy. Failing that the 
question can be
+     * answered across the configured operations and no more, which relaxes 
the default for
+     * a message that may turn out to invoke an operation whose own policy 
does not allow
+     * it. Record that, so that verifyRelaxedEngineDefaults() can reimpose the 
default once
+     * the operation is known.
      */
     public boolean isUsernameTokenNoPasswordAllowedByPolicy() {
+        if (effectivePolicy != null) {
+            return allowsUsernameTokenNoPassword(effectivePolicy);
+        }
         for (OperationPolicy operationPolicy : operationPolicies) {
-            org.apache.neethi.Policy policy = operationPolicy.getPolicy();
-            if (policy != null && policyContains(policy,
-                assertion -> assertion instanceof UsernameToken
-                    && ((UsernameToken)assertion).getPasswordType()
-                        == UsernameToken.PasswordType.NoPassword)) {
+            if (allowsUsernameTokenNoPassword(operationPolicy)) {
+                usernameTokenNoPasswordRelaxedForAllOperations = true;
                 return true;
             }
         }
@@ -942,28 +958,92 @@ public class PolicyEnforcer implements 
SecurityEventListener {
     }
 
     /**
-     * Returns true if any configured operation policy contains an 
AlgorithmSuite whose
-     * asymmetric key wrap is RSA v1.5. Used to decide whether the engine's 
hardened
-     * default (rejecting rsa-1_5 key transport) may be relaxed in policy mode.
+     * Returns true if the policy that governs this message contains an 
AlgorithmSuite whose
+     * asymmetric key wrap is RSA v1.5. Used to decide whether the engine's 
hardened default
+     * (rejecting rsa-1_5 key transport) may be relaxed in policy mode. Scoped 
to the
+     * operation, and recorded when it cannot be, exactly as above.
      */
     public boolean isRSA15KeyTransportAllowedByPolicy() {
+        if (effectivePolicy != null) {
+            return allowsRSA15KeyTransport(effectivePolicy);
+        }
         for (OperationPolicy operationPolicy : operationPolicies) {
-            org.apache.neethi.Policy policy = operationPolicy.getPolicy();
-            if (policy != null && policyContains(policy, assertion -> {
-                if (!(assertion instanceof AlgorithmSuite)) {
-                    return false;
-                }
-                AlgorithmSuite.AlgorithmSuiteType algorithmSuiteType =
-                    ((AlgorithmSuite)assertion).getAlgorithmSuiteType();
-                return algorithmSuiteType != null
-                    && 
SPConstants.KW_RSA15.equals(algorithmSuiteType.getAsymmetricKeyWrap());
-            })) {
+            if (allowsRSA15KeyTransport(operationPolicy)) {
+                rsa15KeyTransportRelaxedForAllOperations = true;
                 return true;
             }
         }
         return false;
     }
 
+    private static boolean allowsUsernameTokenNoPassword(OperationPolicy 
operationPolicy) {
+        Policy policy = operationPolicy.getPolicy();
+        return policy != null && policyContains(policy,
+            assertion -> assertion instanceof UsernameToken
+                && ((UsernameToken)assertion).getPasswordType()
+                    == UsernameToken.PasswordType.NoPassword);
+    }
+
+    private static boolean allowsRSA15KeyTransport(OperationPolicy 
operationPolicy) {
+        Policy policy = operationPolicy.getPolicy();
+        return policy != null && policyContains(policy, assertion -> {
+            if (!(assertion instanceof AlgorithmSuite)) {
+                return false;
+            }
+            AlgorithmSuite.AlgorithmSuiteType algorithmSuiteType =
+                ((AlgorithmSuite)assertion).getAlgorithmSuiteType();
+            return algorithmSuiteType != null
+                && 
SPConstants.KW_RSA15.equals(algorithmSuiteType.getAsymmetricKeyWrap());
+        });
+    }
+
+    /**
+     * An engine default that was relaxed on the strength of the whole set of 
operation
+     * policies - because the operation was not yet known when the security 
header was read
+     * - must still hold for the operation the message turned out to invoke. 
Otherwise one
+     * operation that asks for sp:NoPassword or for rsa-1_5 lowers the 
engine's floor for
+     * every other operation of the endpoint, and whether that is caught 
depends on whether
+     * the effective policy happens to carry an assertion that rejects what 
was accepted: a
+     * password-less UsernameToken is not examined at all by a policy that 
names no
+     * UsernameToken.
+     */
+    private void verifyRelaxedEngineDefaults() throws WSSecurityException {
+        if (usernameTokenNoPasswordRelaxedForAllOperations
+            && !allowsUsernameTokenNoPassword(effectivePolicy)) {
+            for (SecurityEvent securityEvent : securityEventQueue) {
+                if (securityEvent instanceof UsernameTokenSecurityEvent
+                    && 
((UsernameTokenSecurityEvent)securityEvent).getSecurityToken() != null
+                    && WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE
+                        == 
((UsernameTokenSecurityEvent)securityEvent).getUsernameTokenPasswordType()) {
+                    rejectRelaxedEngineDefault("a UsernameToken with no 
password");
+                }
+            }
+        }
+        if (rsa15KeyTransportRelaxedForAllOperations
+            && !allowsRSA15KeyTransport(effectivePolicy)) {
+            for (SecurityEvent securityEvent : securityEventQueue) {
+                if (securityEvent instanceof AlgorithmSuiteSecurityEvent) {
+                    AlgorithmSuiteSecurityEvent algorithmSuiteSecurityEvent =
+                        (AlgorithmSuiteSecurityEvent)securityEvent;
+                    if 
(WSSConstants.Asym_Key_Wrap.equals(algorithmSuiteSecurityEvent.getAlgorithmUsage())
+                        && 
WSSConstants.NS_XENC_RSA15.equals(algorithmSuiteSecurityEvent.getAlgorithmURI()))
 {
+                        rejectRelaxedEngineDefault("rsa-1_5 key transport");
+                    }
+                }
+            }
+        }
+    }
+
+    private void rejectRelaxedEngineDefault(String what) throws 
WSSecurityException {
+        String message = "The message uses " + what + ", which the policy for 
operation "
+            + effectivePolicy.getOperationName() + " does not allow";
+        LOG.warn("{}; rejecting the message", message);
+        securityEventQueue.clear();
+        throw new WSSecurityException(
+                WSSecurityException.ErrorCode.INVALID_SECURITY,
+                new IllegalArgumentException(message));
+    }
+
     private static boolean policyContains(PolicyComponent policyComponent, 
Predicate<Assertion> predicate) {
         if (policyComponent instanceof PolicyOperator) {
             for (PolicyComponent childComponent
diff --git 
a/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyInputProcessor.java
 
b/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyInputProcessor.java
index 78a4d3ef2..90d7264fd 100644
--- 
a/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyInputProcessor.java
+++ 
b/ws-security-policy-stax/src/main/java/org/apache/wss4j/policy/stax/enforcer/PolicyInputProcessor.java
@@ -254,11 +254,23 @@ public class PolicyInputProcessor extends 
AbstractInputProcessor {
             // unconditionally, which silently reversed both defaults even 
when the
             // configured policy contained no assertion that re-imposes the 
check. Only
             // relax an engine default when the policy actually covers it.
-            if (policyEnforcer.isRSA15KeyTransportAllowedByPolicy()) {
+            // Where the caller has already relaxed a default itself there is 
nothing to
+            // grant, and asking would arm a re-check against a policy the 
caller has
+            // deliberately overruled, so leave that case alone. The 
properties are
+            // optional on this processor - a caller may construct it with 
none - and then
+            // the caller has relaxed nothing.
+            XMLSecurityProperties properties = getSecurityProperties();
+            WSSSecurityProperties securityProperties =
+                properties instanceof WSSSecurityProperties ? 
(WSSSecurityProperties) properties : null;
+            boolean callerAllowsRSA15 =
+                securityProperties != null && 
securityProperties.isAllowRSA15KeyTransportAlgorithm();
+            boolean callerAllowsNoPassword =
+                securityProperties != null && 
securityProperties.isAllowUsernameTokenNoPassword();
+            if (!callerAllowsRSA15 && 
policyEnforcer.isRSA15KeyTransportAllowedByPolicy()) {
                 inputProcessorChain.getSecurityContext().put(
                     WSSConstants.PROP_ALLOW_RSA15_KEYTRANSPORT_ALGORITHM, 
Boolean.TRUE);
             }
-            if (policyEnforcer.isUsernameTokenNoPasswordAllowedByPolicy()) {
+            if (!callerAllowsNoPassword && 
policyEnforcer.isUsernameTokenNoPasswordAllowedByPolicy()) {
                 inputProcessorChain.getSecurityContext().put(
                     WSSConstants.PROP_ALLOW_USERNAMETOKEN_NOPASSWORD, 
Boolean.TRUE.toString());
             }
diff --git 
a/ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/ScopedEngineDefaultsTest.java
 
b/ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/ScopedEngineDefaultsTest.java
new file mode 100644
index 000000000..b97605567
--- /dev/null
+++ 
b/ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/ScopedEngineDefaultsTest.java
@@ -0,0 +1,155 @@
+/**
+ * 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.policy.stax.test;
+
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+
+import javax.xml.namespace.QName;
+
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.util.DateUtil;
+import org.apache.wss4j.policy.stax.enforcer.PolicyEnforcer;
+import org.apache.wss4j.policy.stax.enforcer.PolicyEnforcerFactory;
+import org.apache.wss4j.stax.ext.WSSConstants;
+import org.apache.wss4j.stax.impl.securityToken.UsernameSecurityTokenImpl;
+import org.apache.wss4j.stax.securityEvent.OperationSecurityEvent;
+import org.apache.wss4j.stax.securityEvent.UsernameTokenSecurityEvent;
+import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.stax.impl.util.IDGenerator;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The engine's hardened defaults - rejecting password-less UsernameTokens and 
rsa-1_5 key
+ * transport - are relaxed in policy mode so that the corresponding policy 
assertions can
+ * take over. The relaxation belongs to the operation whose policy asks for 
it, not to every
+ * operation of the endpoint.
+ *
+ * The WSDL behind these tests has two operations: one asks for sp:NoPassword, 
the other
+ * names no UsernameToken at all, so nothing in its own policy would reject a 
password-less
+ * token that the engine has already accepted.
+ */
+public class ScopedEngineDefaultsTest extends AbstractPolicyTestBase {
+
+    private static final String NAMESPACE = 
"http://www.example.net/MixedPolicyService";;
+    private static final QName NO_PASSWORD_OPERATION = new QName(NAMESPACE, 
"noPasswordOperation");
+    private static final QName PASSWORD_OPERATION = new QName(NAMESPACE, 
"passwordOperation");
+
+    /**
+     * Where SOAPAction has already selected the operation, only that 
operation's policy
+     * decides. The operation that asks for no password gets the relaxation; 
the one that
+     * does not, does not.
+     */
+    @Test
+    public void testRelaxationIsScopedToTheOperationSelectedBySOAPAction() 
throws Exception {
+        
assertTrue(newPolicyEnforcer("noPasswordOperationAction").isUsernameTokenNoPasswordAllowedByPolicy());
+        
assertFalse(newPolicyEnforcer("passwordOperationAction").isUsernameTokenNoPasswordAllowedByPolicy());
+    }
+
+    /**
+     * Without a SOAPAction the operation is not known while the security 
header is being
+     * read, so the question can only be answered across every configured 
operation. The
+     * engine default is still relaxed, as before, or a deployment whose 
operation cannot be
+     * determined that early would stop working.
+     */
+    @Test
+    public void testRelaxationStillGrantedWhenTheOperationIsNotYetKnown() 
throws Exception {
+        
assertTrue(newPolicyEnforcer("").isUsernameTokenNoPasswordAllowedByPolicy());
+    }
+
+    /**
+     * ...but once the operation turns out to be the one whose policy says 
nothing about
+     * UsernameTokens, a password-less token accepted under that relaxation is 
rejected.
+     */
+    @Test
+    public void 
testPasswordlessTokenRejectedForAnOperationThatDoesNotAllowIt() throws 
Exception {
+        PolicyEnforcer policyEnforcer = newPolicyEnforcer("");
+        assertTrue(policyEnforcer.isUsernameTokenNoPasswordAllowedByPolicy());
+        policyEnforcer.registerSecurityEvent(passwordlessUsernameTokenEvent());
+
+        OperationSecurityEvent operationSecurityEvent = new 
OperationSecurityEvent();
+        operationSecurityEvent.setOperation(PASSWORD_OPERATION);
+
+        WSSecurityException ex = assertThrows(WSSecurityException.class,
+                () -> 
policyEnforcer.registerSecurityEvent(operationSecurityEvent));
+
+        assertEquals("The message uses a UsernameToken with no password, which 
the policy for "
+                        + "operation " + PASSWORD_OPERATION + " does not 
allow",
+                ex.getCause().getMessage());
+        assertEquals(WSSecurityException.INVALID_SECURITY, ex.getFaultCode());
+    }
+
+    /**
+     * The same message is accepted for the operation whose policy does ask for
+     * sp:NoPassword.
+     */
+    @Test
+    public void testPasswordlessTokenAcceptedForTheOperationThatAllowsIt() 
throws Exception {
+        PolicyEnforcer policyEnforcer = newPolicyEnforcer("");
+        assertTrue(policyEnforcer.isUsernameTokenNoPasswordAllowedByPolicy());
+        policyEnforcer.registerSecurityEvent(passwordlessUsernameTokenEvent());
+
+        OperationSecurityEvent operationSecurityEvent = new 
OperationSecurityEvent();
+        operationSecurityEvent.setOperation(NO_PASSWORD_OPERATION);
+
+        policyEnforcer.registerSecurityEvent(operationSecurityEvent);
+    }
+
+    /**
+     * A message that carries no password-less token is unaffected, even 
though the
+     * relaxation was granted across the endpoint.
+     */
+    @Test
+    public void testOperationWithoutAPasswordlessTokenIsUnaffected() throws 
Exception {
+        PolicyEnforcer policyEnforcer = newPolicyEnforcer("");
+        assertTrue(policyEnforcer.isUsernameTokenNoPasswordAllowedByPolicy());
+
+        OperationSecurityEvent operationSecurityEvent = new 
OperationSecurityEvent();
+        operationSecurityEvent.setOperation(PASSWORD_OPERATION);
+
+        policyEnforcer.registerSecurityEvent(operationSecurityEvent);
+    }
+
+    private PolicyEnforcer newPolicyEnforcer(String soapAction) throws 
Exception {
+        PolicyEnforcerFactory policyEnforcerFactory = 
PolicyEnforcerFactory.newInstance(
+                
this.getClass().getClassLoader().getResource("testdata/wsdl/mixedPasswordPolicies.wsdl"));
+        return policyEnforcerFactory.newPolicyEnforcer(soapAction, false, 
null, 0, false);
+    }
+
+    private UsernameTokenSecurityEvent passwordlessUsernameTokenEvent() throws 
XMLSecurityException {
+        UsernameTokenSecurityEvent usernameTokenSecurityEvent = new 
UsernameTokenSecurityEvent();
+        
usernameTokenSecurityEvent.setUsernameTokenProfile(WSSConstants.NS_USERNAMETOKEN_PROFILE11);
+        ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
+        String created = DateUtil.getDateTimeFormatter(true).format(now);
+        UsernameSecurityTokenImpl usernameSecurityToken = new 
UsernameSecurityTokenImpl(
+                WSSConstants.UsernameTokenPasswordType.PASSWORD_NONE,
+                "username", null, created, null, new byte[10], 10L,
+                null, IDGenerator.generateID(null),
+                
WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE);
+        
usernameSecurityToken.addTokenUsage(WSSecurityTokenConstants.TOKENUSAGE_SUPPORTING_TOKENS);
+        usernameTokenSecurityEvent.setSecurityToken(usernameSecurityToken);
+        return usernameTokenSecurityEvent;
+    }
+}
diff --git 
a/ws-security-policy-stax/src/test/resources/testdata/wsdl/mixedPasswordPolicies.wsdl
 
b/ws-security-policy-stax/src/test/resources/testdata/wsdl/mixedPasswordPolicies.wsdl
new file mode 100644
index 000000000..015ceb6ac
--- /dev/null
+++ 
b/ws-security-policy-stax/src/test/resources/testdata/wsdl/mixedPasswordPolicies.wsdl
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<wsdl:definitions
+        name="MixedPolicyService"
+        targetNamespace="http://www.example.net/MixedPolicyService";
+        xmlns:tns="http://www.example.net/MixedPolicyService";
+        xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy";
+        xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702";
+        
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
+        xmlns:xs="http://www.w3.org/2001/XMLSchema";
+        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/";
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/";
+        >
+
+    <!-- Two operations of one endpoint, only one of which asks for 
password-less
+         UsernameTokens. The other names no UsernameToken at all, so nothing 
in its own
+         policy would reject one that the engine has already accepted. -->
+
+    <wsdl:types>
+        <xs:schema targetNamespace="http://www.example.net/MixedPolicyService";
+                   xmlns:xs="http://www.w3.org/2001/XMLSchema";>
+            <xs:element name="noPasswordOperation" type="xs:string"/>
+            <xs:element name="passwordOperation" type="xs:string"/>
+        </xs:schema>
+    </wsdl:types>
+
+    <wsp:Policy wsu:Id="NoPasswordPolicy">
+        <wsp:ExactlyOne>
+            <wsp:All>
+                <sp:SupportingTokens>
+                    <wsp:Policy>
+                        <sp:UsernameToken>
+                            <wsp:Policy>
+                                <sp:NoPassword/>
+                            </wsp:Policy>
+                        </sp:UsernameToken>
+                    </wsp:Policy>
+                </sp:SupportingTokens>
+            </wsp:All>
+        </wsp:ExactlyOne>
+    </wsp:Policy>
+
+    <wsdl:message name="noPasswordOperationRequest">
+        <wsdl:part name="parameters" element="tns:noPasswordOperation"/>
+    </wsdl:message>
+    <wsdl:message name="passwordOperationRequest">
+        <wsdl:part name="parameters" element="tns:passwordOperation"/>
+    </wsdl:message>
+
+    <wsdl:portType name="MixedPolicyPort">
+        <wsdl:operation name="noPasswordOperation">
+            <wsdl:input message="tns:noPasswordOperationRequest"/>
+        </wsdl:operation>
+        <wsdl:operation name="passwordOperation">
+            <wsdl:input message="tns:passwordOperationRequest"/>
+        </wsdl:operation>
+    </wsdl:portType>
+
+    <wsdl:binding name="MixedPolicySOAPBinding" type="tns:MixedPolicyPort">
+        <soap:binding transport="http://schemas.xmlsoap.org/soap/http"; 
style="document"/>
+        <wsdl:operation name="noPasswordOperation">
+            <wsp:PolicyReference URI="#NoPasswordPolicy"/>
+            <soap:operation soapAction="noPasswordOperationAction" 
style="document"/>
+            <wsdl:input>
+                <soap:body use="literal"/>
+            </wsdl:input>
+        </wsdl:operation>
+        <wsdl:operation name="passwordOperation">
+            <soap:operation soapAction="passwordOperationAction" 
style="document"/>
+            <wsdl:input>
+                <soap:body use="literal"/>
+            </wsdl:input>
+        </wsdl:operation>
+    </wsdl:binding>
+
+    <wsdl:service name="MixedPolicyService">
+        <wsdl:port name="MixedPolicy" binding="tns:MixedPolicySOAPBinding">
+            <soap:address location="http://localhost:8080/MixedPolicyService"/>
+        </wsdl:port>
+    </wsdl:service>
+</wsdl:definitions>

Reply via email to