Skip to content

Commit

Permalink
Implement Bus Security over Gateways (frankframework#5286)
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsm5 authored Sep 7, 2023
1 parent 7c22ffb commit 4aabe11
Show file tree
Hide file tree
Showing 13 changed files with 782 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
package nl.nn.adapterframework.runner;
package nl.nn.adapterframework.web;

import java.util.Map;

Expand All @@ -23,14 +23,16 @@
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import lombok.Setter;
import nl.nn.adapterframework.lifecycle.DynamicRegistration.ServletWithParameters;
import nl.nn.adapterframework.management.web.ServletDispatcher;
import nl.nn.adapterframework.util.SpringUtils;

public class ConsoleBackendBean implements ApplicationContextAware {
private final Logger log = LogManager.getLogger(ConsoleBackendBean.class);
@Configuration
public class ConsoleBackend implements ApplicationContextAware {
private final Logger log = LogManager.getLogger(ConsoleBackend.class);

private @Setter ApplicationContext applicationContext;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
Copyright 2023 WeAreFrank!
Licensed 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 nl.nn.adapterframework.web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import lombok.Setter;
import nl.nn.adapterframework.management.security.JwtKeyGenerator;
import nl.nn.adapterframework.util.SpringUtils;

@Configuration
public class JwksEndpoint implements ApplicationContextAware {
private final Logger log = LogManager.getLogger(JwksEndpoint.class);

private @Setter ApplicationContext applicationContext;

@Bean
public ServletRegistrationBean<JwksServlet> createJkwsEndpoint() {
JwksServlet servlet = SpringUtils.createBean(applicationContext, JwksServlet.class);
log.info("registering servlet [{}]", servlet::getName);

ServletRegistrationBean<JwksServlet> bean = new ServletRegistrationBean<>(servlet);
bean.setName(servlet.getName());
bean.addUrlMappings("/iaf/management/jwks");

log.info("created IAF API servlet endpoint {}", bean::getUrlMappings);

return bean;
}

private static class JwksServlet extends HttpServlet implements ApplicationContextAware {
private JwtKeyGenerator keyGenerator;

public String getName() {
return "JwksServlet";
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write(keyGenerator.getPublicJwkSet());
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
keyGenerator = applicationContext.getBean("JwtKeyGenerator", JwtKeyGenerator.class);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright 2023 WeAreFrank!
Licensed 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 nl.nn.adapterframework.web;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.RequestMatcher;

import nl.nn.adapterframework.lifecycle.DynamicRegistration;

@Configuration
@EnableWebSecurity //Enables Spring Security (classpath)
@EnableMethodSecurity(jsr250Enabled = true, prePostEnabled = false) //Enables JSR 250 (JAX-RS) annotations
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityChainConfigurer {

private SecurityFilterChain configureAuthenticator(HttpSecurity http) throws Exception {
http.anonymous().authorities(getRolePrefixedAuthorities());
return http.build();
}

// Adds AuthorityAuthorizationManager#ROLE_PREFIX
private List<GrantedAuthority> getRolePrefixedAuthorities() {
return Arrays.asList(DynamicRegistration.ALL_IBIS_USER_ROLES).stream().map(e -> "ROLE_" + e).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}

@Bean
public SecurityFilterChain configureChain(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.securityMatcher( new MatchAllButJWKS() );
http.headers().frameOptions().sameOrigin(); //Allow same origin iframe request
http.csrf().disable();
http.formLogin().disable(); //Disable the form login filter
http.anonymous().disable(); //Disable the default anonymous filter

return configureAuthenticator(http);
}

private static class MatchAllButJWKS implements RequestMatcher {
private static final String JWKS_ENDPOINT = "/iaf/management/jwks";

@Override
public boolean matches(HttpServletRequest request) {
return !JWKS_ENDPOINT.equals(request.getServletPath());
}

@Override
public String toString() {
return "All endpoints except ["+JWKS_ENDPOINT+"]";
}
}
}
5 changes: 4 additions & 1 deletion console/war/src/main/resources/FrankConsoleContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
<context:property-placeholder location="classpath:application.properties"/>

<bean class="nl.nn.adapterframework.runner.ConsoleFrontendBean"/>
<bean class="nl.nn.adapterframework.runner.ConsoleBackendBean"/>

<context:component-scan base-package="nl.nn.adapterframework.web" />

<!-- Messaging Gateway -->
<bean id="outboundGateway" class="nl.nn.adapterframework.management.bus.OutboundGatewayFactory">
<property name="gatewayClassname" value="${management.gateway.outbound.class}" />
</bean>

<bean id="JwtKeyGenerator" class="nl.nn.adapterframework.management.security.JwtKeyGenerator"/>

<bean id="messageBuilderFactory" class="org.springframework.integration.support.DefaultMessageBuilderFactory" />
</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@
import static java.util.Objects.requireNonNull;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import org.apache.cxf.bus.spring.SpringBus;
Expand All @@ -41,6 +38,7 @@
import org.springframework.util.ResourceUtils;

import nl.nn.adapterframework.util.AppConstants;
import nl.nn.adapterframework.util.Environment;
import nl.nn.adapterframework.util.LogUtil;
import nl.nn.adapterframework.util.SpringUtils;

Expand Down Expand Up @@ -256,19 +254,23 @@ private void lookupApplicationModules() {
List<String> modulesToScanFor = new ArrayList<>();

modulesToScanFor.add("ibis-adapterframework-akamai");
modulesToScanFor.add("ibis-adapterframework-aspose");
modulesToScanFor.add("ibis-adapterframework-aws");
modulesToScanFor.add("ibis-adapterframework-cmis");
modulesToScanFor.add("ibis-adapterframework-commons");
modulesToScanFor.add("ibis-adapterframework-console-backend");
modulesToScanFor.add("ibis-adapterframework-coolgen");
modulesToScanFor.add("ibis-adapterframework-core");
modulesToScanFor.add("credentialprovider");
modulesToScanFor.add("ibis-adapterframework-ibm");
modulesToScanFor.add("ibis-adapterframework-idin");
modulesToScanFor.add("ibis-adapterframework-ifsa");
modulesToScanFor.add("ibis-adapterframework-ladybug");
modulesToScanFor.add("ibis-adapterframework-larva");
modulesToScanFor.add("iaf-management-gateway");
modulesToScanFor.add("ibis-adapterframework-sap");
modulesToScanFor.add("ibis-adapterframework-tibco");
modulesToScanFor.add("ibis-adapterframework-webapp");
modulesToScanFor.add("ibis-adapterframework-console");
modulesToScanFor.add("ibis-adapterframework-aws");

registerApplicationModules(modulesToScanFor);
}
Expand All @@ -280,7 +282,7 @@ private void lookupApplicationModules() {
*/
private void registerApplicationModules(List<String> modules) {
for (String module : modules) {
String version = getModuleVersion(module);
String version = Environment.getModuleVersion(module);

if (version != null) {
iafModules.put(module, version);
Expand All @@ -289,30 +291,4 @@ private void registerApplicationModules(List<String> modules) {
}
}
}

/**
* Get IBIS module version
*
* @param module name of the module to fetch the version
* @return module version or null if not found
*/
private String getModuleVersion(String module) {
ClassLoader classLoader = this.getClass().getClassLoader();
String basePath = "META-INF/maven/org.ibissource/";
URL pomProperties = classLoader.getResource(basePath + module + "/pom.properties");

if (pomProperties == null) {
// unable to find module, assume it's not on the classpath
return null;
}
try (InputStream is = pomProperties.openStream()) {
Properties props = new Properties();
props.load(is);
return (String) props.get("version");
} catch (IOException e) {
applicationLog.warn("unable to read pom.properties file for module[{}]", module, e);

return "unknown";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Optional;
import java.util.Properties;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Environment {
private static Logger log = LogManager.getLogger(Environment.class);
private static String IBISSOURCE_NAMESPACE = "META-INF/maven/org.ibissource/";

public static Properties getEnvironmentVariables() throws IOException {
Properties props = new Properties();
Expand Down Expand Up @@ -111,4 +118,29 @@ public static Optional<String> getSystemProperty(String property, String def) {
return Optional.ofNullable(def);
}
}

/**
* Get IBIS module version
*
* @param module name of the module to fetch the version
* @return module version or null if not found
*/
public static @Nullable String getModuleVersion(@Nonnull String module) {
ClassLoader classLoader = Environment.class.getClassLoader();
URL pomProperties = classLoader.getResource(IBISSOURCE_NAMESPACE + module + "/pom.properties");

if (pomProperties == null) {
// unable to find module, assume it's not on the classpath
return null;
}
try (InputStream is = pomProperties.openStream()) {
Properties props = new Properties();
props.load(is);
return (String) props.get("version");
} catch (IOException e) {
log.warn("unable to read pom.properties file for module [{}]", module, e);

return "unknown";
}
}
}
10 changes: 10 additions & 0 deletions management-gateway/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
Expand All @@ -39,6 +43,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.31</version>
</dependency>

<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
Expand Down
Loading

0 comments on commit 4aabe11

Please sign in to comment.