I'm trying to develop a fuse CXF service protected by the WS-Trust "IssuedToken" policy, as explained in the official docs for the Security Token Service. I did everything as explained in the docs, cross-checking with some of the fuse and CXF official examples.
All seems to be working fine, but when I call the service with SoapUI and no token, instead of receiving an error (because of the missing token) the call goes through and returns the result, as if no protection was implemented at all.
I tried running the same code in "standalone mode" (outside fuse) as done in the CXF "sts" example, and the protection works. Of course in this case I used a different pom file and one more class (Server.java) plus a spring file (wssec-server.xml) to bootstrap the service, but everything else is the same.
So what could be preventing the security setup to work in fuse?
Fuse version: 6.2.1.084 on Ubuntu 14.04.5 LTS
Sources follow. Keep in mind that the whole attempt is a collage from the many examples which I don't fully understand yet, so some instructions might be misplaced here.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>rst-abc</groupId>
<artifactId>mock-fuse-service-secure-b</artifactId>
<version>2</version>
<packaging>bundle</packaging>
<name>MockFuseServiceSecureB</name>
<repositories>
<repository>
<id>fuse-public-repository</id>
<name>FuseSource Community Release Repository</name>
<url>https://repo.fusesource.com/nexus/content/groups/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
</repository>
<repository>
<id>fusesource.ea</id>
<name>JBoss Community Early Access Release Repository</name>
<url>https://repo.fusesource.com/nexus/content/groups/ea</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>fuse-public-repository</id>
<name>FuseSource Community Release Repository</name>
<url>https://repo.fusesource.com/nexus/content/groups/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.7</maven.compiler.source>
<cxf-version>3.0.4.redhat-621084</cxf-version>
<version.maven-surefire-plugin>2.15</version.maven-surefire-plugin>
<version.maven-bundle-plugin>2.3.7</version.maven-bundle-plugin>
<skipTests>true</skipTests>
<!-- the version of the BOM, defining all the dependency versions -->
<fabric.version>1.2.0-SNAPSHOT</fabric.version>
<!-- fabric8 deploy profile configuration -->
<fabric8.profile>quickstarts-cxf-soap</fabric8.profile>
<fabric8.parentProfiles>feature-cxf</fabric8.parentProfiles>
<fabric8.features>fabric-cxf cxf-jaxws</fabric8.features>
<!-- the version of the JBoss Fuse BOM, defining all the dependency versions -->
<jboss.fuse.bom.version>6.2.1.redhat-084</jboss.fuse.bom.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.fuse.bom</groupId>
<artifactId>jboss-fuse-parent</artifactId>
<version>${jboss.fuse.bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-policy</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
<build>
<finalName>shared</finalName>
<plugins>
<!-- Skip Test by default and enable them only in Test profile -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.maven-surefire-plugin}</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${version.maven-bundle-plugin}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
javax.jws,
javax.xml.bind,
javax.xml.bind.annotation,
javax.xml.namespace, javax.xml.ws,
javax.wsdl,
org.osgi.service.blueprint,
io.fabric8.cxf.endpoint,
javax.security.auth.callback,
org.apache.cxf.interceptor.security,
org.apache.cxf.transport.http,
io.fabric8.cxf,
org.apache.cxf, org.apache.cxf.endpoint, org.apache.cxf.frontend, org.apache.cxf.interceptor,
org.apache.cxf.jaxws, org.apache.cxf.message, org.apache.cxf.phase
</Import-Package>
<Import-Service>org.apache.aries.blueprint.NamespaceHandler;
osgi.service.blueprint.namespace=http://cxf.apache.org/transports/http/configuration
</Import-Service>
<Export-Package>it.rst.abc</Export-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.0.4.redhat-621084</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>target/generated/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>src/main/resources/wsdl/hello_world.wsdl</wsdl>
<wsdlLocation>classpath:wsdl/hello_world.wsdl</wsdlLocation>
<frontEnd>jaxws21</frontEnd>
<extraargs>
<extraarg>-impl</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
<additionalJvmArgs>-Djavax.xml.accessExternalSchema=jar:file,file</additionalJvmArgs>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>fabric8-maven-plugin</artifactId>
<version>1.2.0.redhat-621084</version>
</plugin>
</plugins>
</build>
</project>
hello_world.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://abc.rst.it/hello_world"
xmlns:x1="http://abc.rst.it/hello_world/types"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:wsaw="http://www.w3.org/2005/08/addressing"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="HelloWorld"
targetNamespace="http://abc.rst.it/hello_world">
<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://abc.rst.it/hello_world/types" targetNamespace="http://abc.rst.it/hello_world/types" elementFormDefault="qualified">
<simpleType name="MyStringType">
<restriction base="string">
<maxLength value="30"/>
</restriction>
</simpleType>
<element name="greetMe">
<complexType>
<sequence>
<element name="requestType" type="tns:MyStringType"/>
</sequence>
</complexType>
</element>
<element name="greetMeResponse">
<complexType>
<sequence>
<element name="responseType" type="string"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
<wsdl:message name="greetMeRequest">
<wsdl:part element="x1:greetMe" name="in"/>
</wsdl:message>
<wsdl:message name="greetMeResponse">
<wsdl:part element="x1:greetMeResponse" name="out"/>
</wsdl:message>
<wsdl:portType name="Greeter">
<wsdl:operation name="greetMe">
<wsdl:input message="tns:greetMeRequest" name="greetMeRequest"/>
<wsdl:output message="tns:greetMeResponse" name="greetMeResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="Greeter_SOAPBinding" type="tns:Greeter">
<wsp:PolicyReference URI="#AsymmetricSAML2Policy"/>
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="greetMe">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="greetMeRequest">
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Input_Policy"/>
</wsdl:input>
<wsdl:output name="greetMeResponse">
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Output_Policy"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="MockSecureService">
<wsdl:port binding="tns:Greeter_SOAPBinding" name="MockPort">
<soap:address location="http://0.0.0.0:9001/cxf/HelloWorldSecure"/>
</wsdl:port>
</wsdl:service>
<wsp:Policy wsu:Id="AsymmetricSAML2Policy">
<wsp:ExactlyOne>
<wsp:All>
<wsam:Addressing wsp:Optional="false">
<wsp:Policy/>
</wsam:Addressing>
<sp:AsymmetricBinding>
<wsp:Policy>
<sp:InitiatorToken>
<wsp:Policy>
<sp:IssuedToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<sp:RequestSecurityTokenTemplate>
<t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType>
<t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</t:KeyType>
</sp:RequestSecurityTokenTemplate>
<wsp:Policy>
<sp:RequireInternalReference/>
</wsp:Policy>
<sp:Issuer>
<wsaw:Address>http://localhost:8080/SecurityTokenService/
</wsaw:Address>
</sp:Issuer>
</sp:IssuedToken>
</wsp:Policy>
</sp:InitiatorToken>
<sp:RecipientToken>
<wsp:Policy>
<sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<sp:WssX509V3Token10/>
<sp:RequireIssuerSerialReference/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:RecipientToken>
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:OnlySignEntireHeadersAndBody/>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
</wsp:Policy>
</sp:AsymmetricBinding>
<sp:Wss11>
<wsp:Policy>
<sp:MustSupportRefIssuerSerial/>
<sp:MustSupportRefThumbprint/>
<sp:MustSupportRefEncryptedKey/>
</wsp:Policy>
</sp:Wss11>
<sp:Trust13>
<wsp:Policy>
<sp:MustSupportIssuedTokens/>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Input_Policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:EncryptedParts>
<sp:Body/>
</sp:EncryptedParts>
<sp:SignedParts>
<sp:Body/>
<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="AckRequested" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="SequenceAcknowledgement" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="Sequence" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="CreateSequence" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Output_Policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:EncryptedParts>
<sp:Body/>
</sp:EncryptedParts>
<sp:SignedParts>
<sp:Body/>
<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="AckRequested" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="SequenceAcknowledgement" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="Sequence" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="CreateSequence" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</wsdl:definitions>
blueprint.xml
<?xml version="1.0" encoding="UTF-8"?>
<blueprint
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsd">
<jaxws:endpoint
xmlns:s="http://abc.rst.it/hello_world"
id="server"
implementor="it.rst.abc.GreeterImpl"
endpointName="s:MockPort"
serviceName="s:MockSecureService"
wsdlLocation="classpath:wsdl/hello_world.wsdl"
address="http://0.0.0.0:9001/cxf/HelloWorldSecure">
<jaxws:properties>
<entry key="ws-security.username" value="myservicekey"/>
<entry key="ws-security.password" value="skpass"/>
<entry key="ws-security.signature.properties" value="serviceKeystore.properties"/>
<entry key="ws-security.encryption.properties" value="serviceKeystore.properties"/>
<entry key="ws-security.signature.username" value="myservicekey"/>
</jaxws:properties>
</jaxws:endpoint>
</blueprint>
serviceKeystore.properties
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=sspass
org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
org.apache.ws.security.crypto.merlin.keystore.file=keys/servicestore.jks
GreeterImpl.java
package it.rst.abc;
import it.rst.abc.hello_world.Greeter;
import java.util.logging.Logger;
@javax.jws.WebService(name = "Greeter", serviceName = "MockSecureService",
targetNamespace = "http://abc.rst.it/hello_world",
wsdlLocation = "classpath:wsdl/hello_world.wsdl")
public class GreeterImpl implements Greeter {
private static final Logger LOG =
Logger.getLogger(GreeterImpl.class.getPackage().getName());
public String greetMe(String me) {
LOG.info("Executing operation greetMe");
System.out.println("Executing operation greetMe");
System.out.println("Message received: " + me + "\n");
return "Hello " + me;
}
}
For the "standalone" example (which works as expected) I used the following (I removed some characters to keep this post within the SO limit):
pom-standalone.xml
<?xml ... ?>
<project xmlns=...>
<modelVersion...
<properties>
<cxf.version>${project.version}</cxf.version>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>src/main/resources/wsdl/hello_world.wsdl</wsdl>
<frontEnd>jaxws21</frontEnd>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>server</id>
<build>
<defaultGoal>test</defaultGoal>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>demo.wssec.server.Server</mainClass>
<arguments>
<argument>${basedir}/src/main/resources/wssec-server.xml</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-policy</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.14.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
<exclusion>
<artifactId>logkit</artifactId>
<groupId>logkit</groupId>
</exclusion>
<exclusion>
<artifactId>avalon-framework</artifactId>
<groupId>avalon-framework</groupId>
</exclusion>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
...other log4j dependencies here...
</dependencies>
</project>
Server.java
package demo.wssec.server;
import java.io.File;
import java.net.URL;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.bus.spring.SpringBusFactory;
public class Server {
protected Server(URL busURL) throws Exception {
SpringBusFactory bf = new SpringBusFactory();
Bus bus = bf.createBus(busURL.toString());
BusFactory.setDefaultBus(bus);
}
public static void main(String args[]) throws Exception {
URL busURL;
File busFile = new File(args[0]);
if (busFile.exists()) {
busURL = busFile.toURI().toURL();
} else {
busURL = new URL(args[0]);
}
new Server(busURL);
System.out.println("Server ready...");
Thread.sleep(5 * 60 * 1000);
}
}
wssec-server.xml
<?xml ... ?>
<beans xmlns=...>
<cxf:bus>
<cxf:features>
<cxf:logging/>
</cxf:features>
</cxf:bus>
<jaxws:endpoint
xmlns:s="http://abc.rst.it/hello_world"
id="server"
implementor="it.rst.abc.GreeterImpl"
endpointName="s:MockPort"
serviceName="s:MockSecureService"
wsdlLocation="classpath:wsdl/hello_world.wsdl"
address="http://0.0.0.0:9001/SoapcxContext/HelloWorldSecure">
<jaxws:properties>
<entry key="ws-security.signature.username" value="myservicekey"/>
<entry key="ws-security.username" value="myservicekey"/>
<entry key="ws-security.password" value="skpass"/>
<entry key="ws-security.signature.properties" value="serviceKeystore.properties"/>
<entry key="ws-security.encryption.properties" value="serviceKeystore.properties"/>
</jaxws:properties>
</jaxws:endpoint>
</beans>
The server is then started with
mvn -f pom-standalone.xml -Pserver
and when I connect with SoapUI I get the following (expected) error:
MessageAddressingHeaderRequired: A required header representing a Message Addressing Property is not present
The code is correct. What is missing is the proper configuration of karaf. By issuing a
features:list | grep cxf-ws-security
you can check if the feature is installed. To install it, just type
features:install cxf-ws-security
I found the solution in the README.md of the cxf/secure-soap fuse quickstart. The whole fuse cxf security guide doesn't mention it! Also, it's quite weird that a protected service works unprotected when its protection library is missing, with no messages in the log whatsoever.