This is an automated email from the ASF dual-hosted git repository. ffang pushed a commit to branch camel-spring-boot-3.18.x in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit f144eed1d1fd4ae52bdab4dcd03b5ea7ba88e1a6 Author: Federico Mariani <34543311+cro...@users.noreply.github.com> AuthorDate: Thu Jul 28 07:33:47 2022 +0200 ✅ CXF Simple JAAS Test (#593) (cherry picked from commit 421e9cfacded78c26384be75c17edb30e27b16ee) --- components-starter/camel-cxf-soap-starter/pom.xml | 5 + .../component/cxf/security/GreetingService.java | 25 +++++ .../cxf/security/GreetingServiceImpl.java | 28 +++++ .../cxf/security/jaas/SimpleLoginModule.java | 104 ++++++++++++++++++ .../soap/springboot/WSSUsernameTokenHandler.java | 85 ++++++++++++++ .../cxf/soap/springboot/WSSUsernameTokenTest.java | 122 +++++++++++++++++++++ .../src/test/resources/routes/soap-security.xml | 49 +++++++++ .../src/test/resources/simple-jaas.conf | 19 ++++ .../springboot/MllpMaxConcurrentConsumersTest.java | 2 +- .../MllpTcpClientProducerConnectionErrorTest.java | 3 - 10 files changed, 438 insertions(+), 4 deletions(-) diff --git a/components-starter/camel-cxf-soap-starter/pom.xml b/components-starter/camel-cxf-soap-starter/pom.xml index 614681087ba..11bdee34a66 100644 --- a/components-starter/camel-cxf-soap-starter/pom.xml +++ b/components-starter/camel-cxf-soap-starter/pom.xml @@ -45,6 +45,11 @@ <version>${cxf-version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-ws-security</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cxf-common</artifactId> diff --git a/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/GreetingService.java b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/GreetingService.java new file mode 100644 index 00000000000..d6312cc7988 --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/GreetingService.java @@ -0,0 +1,25 @@ +/* + * 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.camel.component.cxf.security; + +import javax.jws.WebService; + +@WebService +public interface GreetingService { + + String greet(String name); +} diff --git a/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/GreetingServiceImpl.java b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/GreetingServiceImpl.java new file mode 100644 index 00000000000..7e84b17865a --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/GreetingServiceImpl.java @@ -0,0 +1,28 @@ +/* + * 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.camel.component.cxf.security; + +import javax.jws.WebService; + +@WebService(endpointInterface = "org.apache.camel.component.cxf.security.GreetingService") +public class GreetingServiceImpl implements GreetingService { + + @Override + public String greet(String name) { + return "Hello " + name; + } +} diff --git a/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/jaas/SimpleLoginModule.java b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/jaas/SimpleLoginModule.java new file mode 100644 index 00000000000..6d2302d05c8 --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/security/jaas/SimpleLoginModule.java @@ -0,0 +1,104 @@ +/* + * 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.camel.component.cxf.security.jaas; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import java.net.URL; +import java.util.Map; + +/** + * Simple LoginModule which checks plain username and password. + */ +@Component +public class SimpleLoginModule implements LoginModule { + private static final Logger LOG = LoggerFactory.getLogger(SimpleLoginModule.class); + + public static final String USERNAME = "admin"; + public static final String PASSWORD = "admin"; + + private CallbackHandler callbackHandler; + + private boolean succeeded = false; + + static { + final URL jaasConfig = SimpleLoginModule.class.getClassLoader().getResource("simple-jaas.conf"); + if (jaasConfig != null) { + // Set jaas configuration file + System.setProperty("java.security.auth.login.config", jaasConfig.toString()); + } else { + LOG.debug("JAAS configuration doesn't exist."); + } + } + + public SimpleLoginModule() { + } + + public boolean abort() throws LoginException { + return false; + } + + public boolean commit() throws LoginException { + return succeeded; + } + + public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { + this.callbackHandler = callbackHandler; + succeeded = false; + } + + public boolean login() throws LoginException { + succeeded = false; + final Callback[] callbacks = new Callback[2]; + callbacks[0] = new NameCallback("name:"); + callbacks[1] = new PasswordCallback("password:", false); + + try { + callbackHandler.handle(callbacks); + } catch (Exception e) { + throw new LoginException("Error with callback processing."); + } + + final NameCallback nameCallback = (NameCallback) callbacks[0]; + final PasswordCallback passwordCallback = (PasswordCallback) callbacks[1]; + + final String name = nameCallback.getName(); + final String password = new String(passwordCallback.getPassword()); + + if (USERNAME.equals(name) && PASSWORD.equals(password)) { + succeeded = true; + } else { + throw new FailedLoginException("Sorry! No login for you."); + } + return succeeded; + } + + public boolean logout() throws LoginException { + return false; + } +} diff --git a/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/soap/springboot/WSSUsernameTokenHandler.java b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/soap/springboot/WSSUsernameTokenHandler.java new file mode 100644 index 00000000000..e5a1bfa36e7 --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/soap/springboot/WSSUsernameTokenHandler.java @@ -0,0 +1,85 @@ +/* + * 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.camel.component.cxf.soap.springboot; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPElement; +import javax.xml.soap.SOAPEnvelope; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPHandler; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import java.util.Set; + +public class WSSUsernameTokenHandler implements SOAPHandler<SOAPMessageContext> { + + private final String username; + private final String password; + + public WSSUsernameTokenHandler(String userName, String password) { + this.username = userName; + this.password = password; + } + + @Override + public boolean handleMessage(SOAPMessageContext context) { + final Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); + if (isRequest.booleanValue()) { + try { + final SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope(); + SOAPHeader header = envelope.getHeader(); + if (header == null) { + header = envelope.addHeader(); + } + + final SOAPElement securityElement = header.addChildElement("Security", "wsse", + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); + + final SOAPElement usernameTokenElement = securityElement.addChildElement("UsernameToken", "wsse"); + usernameTokenElement.addAttribute(new QName("xmlns:wsu"), + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); + + final SOAPElement usernameElement = usernameTokenElement.addChildElement("Username", "wsse"); + usernameElement.addTextNode(username); + + final SOAPElement passwordElement = usernameTokenElement.addChildElement("Password", "wsse"); + passwordElement.setAttribute("Type", + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0" + + "#PasswordText"); + passwordElement.addTextNode(password); + } catch (Exception e) { + e.printStackTrace(); + } + } + return true; + } + + @Override + public boolean handleFault(SOAPMessageContext context) { + return true; + } + + @Override + public void close(MessageContext context) { + } + + @Override + public Set<QName> getHeaders() { + return null; + } +} diff --git a/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/soap/springboot/WSSUsernameTokenTest.java b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/soap/springboot/WSSUsernameTokenTest.java new file mode 100644 index 00000000000..ab3c41e9c3a --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/java/org/apache/camel/component/cxf/soap/springboot/WSSUsernameTokenTest.java @@ -0,0 +1,122 @@ +/* + * 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.camel.component.cxf.soap.springboot; + +import org.apache.camel.component.cxf.security.GreetingService; +import org.apache.camel.component.cxf.security.jaas.SimpleLoginModule; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ImportResource; +import org.springframework.test.annotation.DirtiesContext; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; +import javax.xml.ws.handler.Handler; +import javax.xml.ws.handler.HandlerResolver; +import javax.xml.ws.handler.PortInfo; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +@DirtiesContext +@CamelSpringBootTest +@SpringBootTest(classes = { + CamelAutoConfiguration.class, + WSSUsernameTokenTest.class, + SimpleLoginModule.class +} +) +@ImportResource({ + "classpath:routes/soap-security.xml" +}) +public class WSSUsernameTokenTest { + private static final Logger LOG = LoggerFactory.getLogger(WSSUsernameTokenTest.class); + + private static final String BAD_PASSWORD = "123"; + + private static final URL WSDL_URL; + + static { + try { + WSDL_URL = new URL("http://localhost:16232/cxf/ws/greeting-service?wsdl"); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + private static final QName SERVICE_NAME = new QName("http://security.cxf.component.camel.apache.org/", + "GreetingServiceImplService"); + + private void addWSSUsernameTokenHandler(Service service, final String username, final String password) { + // set a handler resolver providing WSSUsernameTokenHandler in the handler chain + final HandlerResolver handlerResolver = new HandlerResolver() { + @Override + public List<Handler> getHandlerChain(PortInfo portInfo) { + final ArrayList<Handler> handlerChain = new ArrayList<>(); + handlerChain.add(new WSSUsernameTokenHandler(username, password)); + return handlerChain; + } + }; + service.setHandlerResolver(handlerResolver); + } + + @Test + public void testAuthenticationCorrectCredentials() throws Exception { + final Service service = Service.create(WSDL_URL, SERVICE_NAME); + addWSSUsernameTokenHandler(service, SimpleLoginModule.USERNAME, SimpleLoginModule.PASSWORD); + final GreetingService greetingService = service.getPort(GreetingService.class); + + final String reply = greetingService.greet("you"); + Assertions.assertEquals(reply, "Hello you"); + } + + @Test + public void testAuthenticationIncorrectCredentials() throws Exception { + final Service service = Service.create(WSDL_URL, SERVICE_NAME); + addWSSUsernameTokenHandler(service, SimpleLoginModule.USERNAME, BAD_PASSWORD); + final GreetingService greetingService = service.getPort(GreetingService.class); + + try { + greetingService.greet("you"); + Assertions.fail("Authentication should failed"); + } catch (Exception e) { + Assertions.assertTrue(e.getMessage().contains("Authentication failed")); + } + } + + @Test + public void testAuthenticationMissingCredentials() throws Exception { + final Service service = Service.create(WSDL_URL, SERVICE_NAME); + final GreetingService greetingService = service.getPort(GreetingService.class); + + try { + greetingService.greet("you"); + Assertions.fail("Authentication should failed"); + } catch (Exception e) { + Assertions.assertTrue(e.getMessage().contains("security error")); + } + } +} diff --git a/components-starter/camel-cxf-soap-starter/src/test/resources/routes/soap-security.xml b/components-starter/camel-cxf-soap-starter/src/test/resources/routes/soap-security.xml new file mode 100644 index 00000000000..a125fe67d5c --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/resources/routes/soap-security.xml @@ -0,0 +1,49 @@ +<?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. + +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:cxf="http://camel.apache.org/schema/cxf/jaxws" + xmlns:jaxws="http://cxf.apache.org/jaxws" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/cxf/jaxws http://camel.apache.org/schema/cxf/jaxws/camel-cxf.xsd + http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> + + <jaxws:endpoint id="greetingService" address="http://localhost:16232/cxf/ws/greeting-service" implementor="org.apache.camel.component.cxf.security.GreetingServiceImpl"> + + <jaxws:inInterceptors> + <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> + <property name="properties"> + <map> + <entry key="action" value="UsernameToken"/> + <entry key="passwordType" value="PasswordText"/> + </map> + </property> + </bean> + <bean class="org.apache.cxf.interceptor.security.JAASLoginInterceptor"> + <property name="contextName" value="simple-jaas"/> + </bean> + </jaxws:inInterceptors> + + <jaxws:properties> + <entry key="ws-security.validate.token" value="false"/> + </jaxws:properties> + </jaxws:endpoint> +</beans> diff --git a/components-starter/camel-cxf-soap-starter/src/test/resources/simple-jaas.conf b/components-starter/camel-cxf-soap-starter/src/test/resources/simple-jaas.conf new file mode 100644 index 00000000000..5c0ae3156d4 --- /dev/null +++ b/components-starter/camel-cxf-soap-starter/src/test/resources/simple-jaas.conf @@ -0,0 +1,19 @@ +/* + * 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. + */ +simple-jaas{ + org.apache.camel.component.cxf.security.jaas.SimpleLoginModule required; +}; diff --git a/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpMaxConcurrentConsumersTest.java b/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpMaxConcurrentConsumersTest.java index 5a29e7c74da..4d7284d0440 100644 --- a/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpMaxConcurrentConsumersTest.java +++ b/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpMaxConcurrentConsumersTest.java @@ -47,7 +47,7 @@ import org.apache.camel.test.AvailablePortFinder; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; -@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) +@DirtiesContext @CamelSpringBootTest @SpringBootTest( classes = { diff --git a/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpTcpClientProducerConnectionErrorTest.java b/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpTcpClientProducerConnectionErrorTest.java index 393f0446142..e5e25f1f6a3 100644 --- a/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpTcpClientProducerConnectionErrorTest.java +++ b/components-starter/camel-mllp-starter/src/test/java/org/apache/camel/component/mllp/springboot/MllpTcpClientProducerConnectionErrorTest.java @@ -45,12 +45,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.test.annotation.DirtiesContext; import org.apache.camel.test.AvailablePortFinder; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; - -@DirtiesContext @CamelSpringBootTest @SpringBootTest( classes = {