How do I configure RestAssured http client to use socks proxy? - rest-assured

Apparently rest-assured misses support for socks proxies: https://github.com/rest-assured/rest-assured/issues/934. Is there way to overcome this and despite all connect through proxy?

Yes, it is possible to provide own http client configuration:
import io.restassured.RestAssured;
import io.restassured.config.HttpClientConfig;
import io.restassured.config.RestAssuredConfig;
import org.apache.http.HttpHost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpInetSocketAddress;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.junit.jupiter.api.Test;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
class SocksProxyTest {
#Test
public void shouldConnectUsingProxy() {
Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("localhost", 2000));
String statusLine = RestAssured.given()
.config(
RestAssuredConfig.config()
.httpClient(
HttpClientConfig.httpClientConfig()
.httpClientFactory(() -> getSocksProxyConfiguredHttpClient(proxy)))
)
.get("http://example.com")
.getStatusLine();
System.err.println(statusLine);
}
private DefaultHttpClient getSocksProxyConfiguredHttpClient(Proxy proxy) {
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", 80, new ProxiedPlainSocketFactory(proxy)));
schemeRegistry.register(new Scheme("https", 443, new ProxiedSSLSocketFactory(proxy, SSLContexts.createSystemDefault())));
DnsResolver fakeDnsResolver = host -> new InetAddress[] { InetAddress.getByAddress(new byte[] { 1, 1, 1, 1 }) };
PoolingClientConnectionManager ccm = new PoolingClientConnectionManager(schemeRegistry, fakeDnsResolver);
return new DefaultHttpClient(ccm);
}
static class ProxiedPlainSocketFactory extends PlainSocketFactory {
private final Proxy proxy;
public ProxiedPlainSocketFactory(Proxy proxy) {
this.proxy = proxy;
}
#Override
public Socket createSocket(HttpParams params) {
return new Socket(proxy);
}
#Override
public Socket createSocket() {
return new Socket(proxy);
}
#Override
public Socket connectSocket(Socket socket, InetSocketAddress remoteAddress, InetSocketAddress localAddress,
HttpParams params) throws IOException, ConnectTimeoutException {
InetSocketAddress unresolvedRemote = InetSocketAddress
.createUnresolved(((HttpInetSocketAddress) remoteAddress).getHttpHost().getHostName(), remoteAddress.getPort());
return super.connectSocket(socket, unresolvedRemote, localAddress, params);
}
}
static class ProxiedSSLSocketFactory extends SSLSocketFactory {
final Proxy proxy;
public ProxiedSSLSocketFactory(Proxy proxy, SSLContext sslContext) {
super(sslContext, ALLOW_ALL_HOSTNAME_VERIFIER);
this.proxy = proxy;
}
#Override
public Socket createSocket(HttpContext context) throws IOException {
return new Socket(proxy);
}
#Override
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException {
InetSocketAddress unresolvedRemote = InetSocketAddress
.createUnresolved(((HttpInetSocketAddress) remoteAddress).getHttpHost().getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}
}

Related

Successful login response with a 404

Currently, I learning security and I'm trying to make a simple login for my Android app. I'm using a custom Filter to request access to my protected data. This way I don't need the default login-page, and use a custom login in my android app instead. But when I do a successful login, I get a 404 response instead of a 200. I there a way to change this as it is set up right now?
MAIN.class
package com.ssl.Oauth2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
Controller.class
#RestController
public class Controller {
#RequestMapping("/protected-resource")
public String protectedResource() {
return "The Protected Resource";
}
#RequestMapping("/public-resource")
public String publicResource() {
return "The Public Resource";
}
}
SecurityConfiguration.class
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("john").password("{noop}12345").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomFilter mupaf = new CustomFilter();
mupaf.setAuthenticationManager(authenticationManager());
http
.csrf().disable()
.addFilterAt(
mupaf,
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/protected-resource").authenticated()
.antMatchers(HttpMethod.POST, "/login").permitAll();
}
}
CustomFilter
package com.ssl.Oauth2;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
public class CustomFilter extends AbstractAuthenticationProcessingFilter {
protected CustomFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
//
}
#Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username, password;
// Credentials to Java Map
try {
Map<String, String> requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = requestMap.get("username");
password = requestMap.get("password");
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
// Token
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// TEST
System.out.println("\n------------------------------------------------\n");
System.out.println("AUTHENTICATION REQUEST:\n" +this.getAuthenticationManager().authenticate(authRequest) );
System.out.println("\n------------------------------------------------\n");
// the method authenticate of the AuthenticationManager receives a token
return this.getAuthenticationManager().authenticate(authRequest);
}
}

Getting exception spring security User account is locked\

We have implemented Spring Security in our angular spring boot project.
Here we are getting exception spring security User account is locked
Please review the following code.
SecurityConfiguration.java
package com.jwt.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import com.jwt.security.filter.AuthenticationTokenFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration<jwtAuthenticationEntryPoint> extends WebSecurityConfigurerAdapter{
#Autowired private UserDetailsService userDetailsService;
#Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint ;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder( PasswordEncoder());
}
#Bean
public PasswordEncoder PasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationTokenFilter authenticationTokenFilterBean( ) {
return new AuthenticationTokenFilter();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/login").permitAll()
.antMatchers(HttpMethod.OPTIONS ,"/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
httpSecurity.headers().cacheControl();
httpSecurity.headers().httpStrictTransportSecurity().includeSubDomains(true).maxAgeInSeconds(31536000);
}
}
the authentication token filter AuthenticationTokenFilter.hjava
package com.jwt.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import com.jwt.security.JwtTokenUtil;
public class AuthenticationTokenFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value("${jwt.header}")
private String tokenHeader;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
String authToken = request.getHeader(this.tokenHeader);
if (authToken != null && authToken.length() > 7) {
authToken = authToken.substring(7);
}
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
boolean isValid = jwtTokenUtil.validateToken(authToken, userDetails);
if (isValid) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
Here i am getting null for authToken when running from postman
The code for JwtUtil is as following
package com.jwt.security;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#Component
public class JwtTokenUtil implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
#Value("${jwt.secret}")
private String secret;
#Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String authToken) {
String username = null;
try {
final Claims claims = getClaimsFromToken(authToken);
username = claims.getSubject();
} catch (Exception e) {
// TODO Auto-generated catch block
username = null;
}
return username;
}
private Claims getClaimsFromToken(String authToken) {
// TODO Auto-generated method stub
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken).getBody();
} catch (Exception e) {
// TODO Auto-generated catch block
claims = null;
}
return claims;
}
public boolean validateToken(String authToken, UserDetails userDetails) {
// TODO Auto-generated method stub
JwtUser user = (JwtUser) userDetails;
final String username = getUsernameFromToken(authToken);
return (username.equals(user.getUsername()) && !isTokenExpired(authToken));
}
private boolean isTokenExpired(String authToken) {
final Date expiration = getExpirationDateFromToken(authToken);
return expiration.before(new Date());
}
private Date getExpirationDateFromToken(String authToken) {
// TODO Auto-generated method stub
Date expiration = null;
final Claims claims = getClaimsFromToken(authToken);
if (claims != null) {
expiration = claims.getExpiration();
} else {
expiration = null;
}
return expiration;
}
public String generateToken(JwtUser userDetails) {
Map<String,Object> claims = new HashMap<String,Object>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
public String generateToken(Map<String , Object> claims ) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()).signWith(SignatureAlgorithm.HS512, secret).compact();
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
}
The code for CsrfHeaderFilter is as following
package com.jwt.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
public class CsrfHeaderFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("...CsrfToken.class.getName() :::" + CsrfToken.class.getName());
// CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
// CsrfToken csrfToken = new HttpSessionCsrfTokenRepository().loadToken(request);
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
String token = null;
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
if(csrfToken != null) {
token = csrfToken.getToken();
}
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
}
The controller used is AuthenticationController The code is as following
package com.jwt.security.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.jwt.security.JwtTokenUtil;
import com.jwt.security.JwtUser;
import com.jwt.security.domain.User;
import com.jwt.security.domain.UserDTO;
import com.jwt.security.exception.UnauthorizedException;
#RestController
public class AuthenticationController {
#Value("${jwt.header}")
private String tokenHeader;
#Autowired private AuthenticationManager authenticationManager;
#Autowired private JwtTokenUtil jwtTokenUtil;
#PostMapping(value="/login")
public ResponseEntity<UserDTO> login(#RequestBody User user, HttpServletRequest request , HttpServletResponse response) {
try {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println("matches ::" + encoder.matches("123", user.getPassword()));
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
final JwtUser userDetails = (JwtUser)authentication.getPrincipal();
SecurityContextHolder.getContext().setAuthentication(authentication);
final String token = jwtTokenUtil.generateToken(userDetails);
response.setHeader("Token", token);
return new ResponseEntity<UserDTO>(new UserDTO(userDetails.getUser(), token) , HttpStatus.OK);
}catch(UnauthorizedException ex) {
ex.printStackTrace();
throw new UnauthorizedException(ex.getMessage());
}
}
}
On calling http://localhost:8080/login from postman and passing the correct email and password , we are getting the following exception
org.springframework.security.authentication.LockedException: User account is locked
Please advice
The message says "User account is locked". This happens after a number of failed authentication events. The account eventually becomes unlocked depending on implementation.
There are only two places that happens in Spring Security:
AccountStatusUserDetailsChecker.check(UserDetails user)
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {
throw new LockedException(messages.getMessage(
"AccountStatusUserDetailsChecker.locked", "User account is locked"));
}
if (!user.isEnabled()) {
throw new DisabledException(messages.getMessage(
"AccountStatusUserDetailsChecker.disabled", "User is disabled"));
}
if (!user.isAccountNonExpired()) {
throw new AccountExpiredException(
messages.getMessage("AccountStatusUserDetailsChecker.expired",
"User account has expired"));
}
if (!user.isCredentialsNonExpired()) {
throw new CredentialsExpiredException(messages.getMessage(
"AccountStatusUserDetailsChecker.credentialsExpired",
"User credentials have expired"));
}
}
AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks.check(UserDetails user)
So if you want to set a breakpoint, that's where you start.
All of this happens in your UserDetailsService which you have in your configuration.
#Autowired private UserDetailsService userDetailsService;
This service returns an object that implements the UserDetails interface
public interface UserDetails {
boolean isAccountNonLocked();
}
if this method returns false, the account is locked. the name is a bit confusing.
Since we don't know what your UserDetailsService is, we can't tell you how this gets populated. So the recommendation is to just set a break point when the error is thrown.
If you don't want the account locking feature to be enabled, there are different ways to implement that. If you override the UserDetailsService bean you can always return users that are never locked.
Another way is to inject your own checker
DaoAuthenticationProvider daoProvider = ....
daoProvider.setPreAuthenticationChecks(toCheck -> {});
There is also a PostAuthenticationChecks object to see if your password has expired.

Adding Swagger to Dropwizard application, need to provide instance of SwaggerBundleConfiguration?

I'm super new to all of these frameworks so please try and bear with me here with what feels like should be a super simple thing.
I'm trying to learn the process of integrating Swagger into an existing Dropwizard application to generate the API documentation, ideally using the Swagger UI interface so that it's interactive for people trying to use/learn it. Specifically, using this bundle: smoketurner/dropwizard-swagger
Before trying to wrestle with the full sized project my employer wants it integrated with, I tried adding it into the standard Dropwizard example application, thinking that should be a hassle-free jumping off point. After following the instructions on the github page for the bundle, I'm hit by the following error:
Test set: com.example.helloworld.IntegrationTest
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.003 s <<< FAILURE! - in com.example.helloworld.IntegrationTest
com.example.helloworld.IntegrationTest Time elapsed: 2.003 s <<< ERROR!
java.lang.IllegalStateException: You need to provide an instance of SwaggerBundleConfiguration
What am I missing?
Below are the Configuration and Application files in question, with the additions made as per the github bundle instructions.
HelloWorldConfiguration:
package com.example.helloworld;
import com.example.helloworld.core.Template;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.Map;
public class HelloWorldConfiguration extends Configuration {
#NotEmpty
private String template;
#NotEmpty
private String defaultName = "Stranger";
#JsonProperty("swagger")
public SwaggerBundleConfiguration swaggerBundleConfiguration;
#Valid
#NotNull
private DataSourceFactory database = new DataSourceFactory();
#NotNull
private Map<String, Map<String, String>> viewRendererConfiguration = Collections.emptyMap();
#JsonProperty
public String getTemplate() {
return template;
}
#JsonProperty
public void setTemplate(String template) {
this.template = template;
}
#JsonProperty
public String getDefaultName() {
return defaultName;
}
#JsonProperty
public void setDefaultName(String defaultName) {
this.defaultName = defaultName;
}
public Template buildTemplate() {
return new Template(template, defaultName);
}
#JsonProperty("database")
public DataSourceFactory getDataSourceFactory() {
return database;
}
#JsonProperty("database")
public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
this.database = dataSourceFactory;
}
#JsonProperty("viewRendererConfiguration")
public Map<String, Map<String, String>> getViewRendererConfiguration() {
return viewRendererConfiguration;
}
#JsonProperty("viewRendererConfiguration")
public void setViewRendererConfiguration(Map<String, Map<String, String>> viewRendererConfiguration) {
final ImmutableMap.Builder<String, Map<String, String>> builder = ImmutableMap.builder();
for (Map.Entry<String, Map<String, String>> entry : viewRendererConfiguration.entrySet()) {
builder.put(entry.getKey(), ImmutableMap.copyOf(entry.getValue()));
}
this.viewRendererConfiguration = builder.build();
}
}
HelloWorldApplication:
package com.example.helloworld;
import com.example.helloworld.auth.ExampleAuthenticator;
import com.example.helloworld.auth.ExampleAuthorizer;
import com.example.helloworld.cli.RenderCommand;
import com.example.helloworld.core.Person;
import com.example.helloworld.core.Template;
import com.example.helloworld.core.User;
import com.example.helloworld.db.PersonDAO;
import com.example.helloworld.filter.DateRequiredFeature;
import com.example.helloworld.health.TemplateHealthCheck;
import com.example.helloworld.resources.FilteredResource;
import com.example.helloworld.resources.HelloWorldResource;
import com.example.helloworld.resources.PeopleResource;
import com.example.helloworld.resources.PersonResource;
import com.example.helloworld.resources.ProtectedResource;
import com.example.helloworld.resources.ViewResource;
import com.example.helloworld.tasks.EchoTask;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.configuration.EnvironmentVariableSubstitutor;
import io.dropwizard.configuration.SubstitutingSourceProvider;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.migrations.MigrationsBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.dropwizard.views.ViewBundle;
import io.federecio.dropwizard.swagger.SwaggerBundle;
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import java.util.Map;
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}
private final HibernateBundle<HelloWorldConfiguration> hibernateBundle =
new HibernateBundle<HelloWorldConfiguration>(Person.class) {
#Override
public DataSourceFactory getDataSourceFactory(HelloWorldConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
#Override
public String getName() {
return "hello-world";
}
#Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
// Enable variable substitution with environment variables
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(
bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor(false)
)
);
bootstrap.addCommand(new RenderCommand());
bootstrap.addBundle(new AssetsBundle());
bootstrap.addBundle(new MigrationsBundle<HelloWorldConfiguration>() {
#Override
public DataSourceFactory getDataSourceFactory(HelloWorldConfiguration configuration) {
return configuration.getDataSourceFactory();
}
});
bootstrap.addBundle(hibernateBundle);
bootstrap.addBundle(new ViewBundle<HelloWorldConfiguration>() {
#Override
public Map<String, Map<String, String>> getViewConfiguration(HelloWorldConfiguration configuration) {
return configuration.getViewRendererConfiguration();
}
});
bootstrap.addBundle(new SwaggerBundle<HelloWorldConfiguration>() {
#Override
protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(HelloWorldConfiguration configuration) {
return configuration.swaggerBundleConfiguration;
}
});
}
#Override
public void run(HelloWorldConfiguration configuration, Environment environment) {
final PersonDAO dao = new PersonDAO(hibernateBundle.getSessionFactory());
final Template template = configuration.buildTemplate();
environment.healthChecks().register("template", new TemplateHealthCheck(template));
environment.admin().addTask(new EchoTask());
environment.jersey().register(DateRequiredFeature.class);
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new ExampleAuthenticator())
.setAuthorizer(new ExampleAuthorizer())
.setRealm("SUPER SECRET STUFF")
.buildAuthFilter()));
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new HelloWorldResource(template));
environment.jersey().register(new ViewResource());
environment.jersey().register(new ProtectedResource());
environment.jersey().register(new PeopleResource(dao));
environment.jersey().register(new PersonResource(dao));
environment.jersey().register(new FilteredResource());
}
}
For anyone else trying to learn this and struggling, the issue was that the included test suite in the sample application has its own YML file, test-example.yml that you also need to add the swagger properties into. This will allow the tests to pass and everything should go smoothly from there.
Added properties to test-example.yml:
swagger:
resourcePackage: com.example.helloworld.resources

EventBus in Reactor 3.x

I know that EventBus is deprecated in Reactor3.x, and the suggested solution is ReplayProcessor. I have read https://github.com/reactor/reactor-core/issues/375. But the code here is too draft. I created a demo project to prove the idea here. Can someone give some comments?
======== Application.java
package hello;
import org.reactivestreams.Subscription;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.ReplayProcessor;
import reactor.core.publisher.BaseSubscriber;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application implements CommandLineRunner {
private static final int NUMBER_OF_QUOTES = 10;
#Bean
ReplayProcessor createReplayProcessor() {
ReplayProcessor<MyEvent> rp = ReplayProcessor.create();
Flux<MyEvent> interest1 = rp.filter(ev -> filterInterest1(ev));
Flux<MyEvent> interest2 = rp.filter(ev -> filterInterest2(ev));
interest1.subscribe(new BaseSubscriber<MyEvent>() {
#Override
protected void hookOnSubscribe(Subscription subscription) {
requestUnbounded();
}
#Override
protected void hookOnNext(MyEvent value) {
//todo: call service method
System.out.println("event 1 handler -> event name:" + value.getEventName());
}
});
interest2.subscribe(new BaseSubscriber<MyEvent>() {
#Override
protected void hookOnSubscribe(Subscription subscription) {
requestUnbounded();
}
#Override
protected void hookOnNext(MyEvent value) {
//todo: call service method
System.out.println("event2 handler -> event name:" + value.getEventName());
}
});
return rp;
}
public boolean filterInterest1(MyEvent myEvent) {
if (myEvent != null && myEvent.getEventName() != null
&& myEvent.getEventName().equalsIgnoreCase("event1")) {
return true;
}
return false;
}
public boolean filterInterest2(MyEvent myEvent) {
if (myEvent != null && myEvent.getEventName() != null
&& myEvent.getEventName().equalsIgnoreCase("event2")) {
return true;
}
return false;
}
#Autowired
private Publisher publisher;
#Bean
public CountDownLatch latch() {
return new CountDownLatch(NUMBER_OF_QUOTES);
}
#Override
public void run(String... args) throws Exception {
publisher.publishQuotes(NUMBER_OF_QUOTES);
}
public static void main(String[] args) throws InterruptedException {
ApplicationContext app = SpringApplication.run(Application.class, args);
app.getBean(CountDownLatch.class).await(10, TimeUnit.SECONDS);
}
}
==========MyEvent.java=============
package hello;
public class MyEvent {
private String eventName = "";
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public MyEvent(String eventName) {
this.eventName = eventName;
}
public void filterInterest1(MyEvent myEvent) {
}
}
=============Publisher.java ===========
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.ReplayProcessor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
#Service
public class Publisher {
#Autowired
ReplayProcessor rp;
#Autowired
CountDownLatch latch;
public void publishQuotes(int numberOfQuotes) throws InterruptedException {
long start = System.currentTimeMillis();
rp.onNext(new MyEvent("event1"));
rp.onNext(new MyEvent("event2"));
rp.onNext(new MyEvent("event3"));
long elapsed = System.currentTimeMillis() - start;
System.out.println("Elapsed time: " + elapsed + "ms");
System.out.println("Average time per quote: " + elapsed / numberOfQuotes + "ms");
}
}
The whole code is https://github.com/yigubigu/reactor-start-sample.git
IMHO you can relay in Spring event handlers. Matt Raible and Josh Long use that in this couple of tutorials:
https://developer.okta.com/blog/2018/09/24/reactive-apis-with-spring-webflux
https://developer.okta.com/blog/2018/09/25/spring-webflux-websockets-react
Key takeaways:
#Component
class ProfileCreatedEventPublisher implements
ApplicationListener<ProfileCreatedEvent>,
Consumer<FluxSink<ProfileCreatedEvent>>
Uses an event loop to take events from a LinkedBlockingQueue.
#Override
public void onApplicationEvent(ProfileCreatedEvent event)
Queue the events that can be published anywhere within your app.
ProfileCreatedEventPublisher is used in ServerSentEventController to create a Flux of events (that can be chained with a filter), it transforms and sends them to a web client.

How do I get Jersey to call a resource method with an HttpServletResponse wrapper?

I am trying to systematically address HTTP response splitting. I have developed a wrapper class for HttpServletResponse called HardenedHttpServletResponse that mitigates splitting attempts.
Regrettably, I cannot get Jersey to call my resource method with my HardenedHttpServletResponse. I get nulls when I try.
Here is a contrived JAX-RS resource with a HTTP response splitting vulnerability which is exploitable by putting percent-encoded CRLFs (%0d%0a) in the filename query parameter:
AttachmentResource.java:
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
#Path("/attachment")
#Produces(MediaType.APPLICATION_JSON)
public final class AttachmentResource {
#GET
#Path("/file")
public StreamingOutput getAttachment(
#Context HttpServletResponse response,
#QueryParam("filename") String filename
) throws Exception {
response.setHeader(
"content-disposition",
"attachment; filename=" + filename
);
return new DummyStreamingOutput();
}
}
Here is a dummy implementation of StreamingOutput to make it a somewhat full example:
DummyStreamingOutput.java:
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
private static DummyFileStreamingOutput implements StreamingOutput {
#Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
String message = "Hello, World!";
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
}
}
Here is the HttpServletResponse wrapper class that mitigates HTTP response splitting by throwing an exception if it detects CR or LF characters in header names or values:
HardenedHttpServletResponse.java:
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.ws.rs.core.Context;
final class HardenedHttpServletResponse extends HttpServletResponseWrapper {
#Inject
HardenedHttpServletResponse(#Context HttpServletResponse response) {
super(response);
}
#Override
public void setHeader(String name, String value) {
mitigateResponseSplitting(name);
mitigateResponseSplitting(value);
super.setHeader(name, value);
}
#Override
public void addHeader(String name, String value) {
mitigateResponseSplitting(name);
mitigateResponseSplitting(value);
super.setHeader(name, value);
}
#Override
public void setIntHeader(String name, int value) {
mitigateResponseSplitting(name);
super.setIntHeader(name, value);
}
#Override
public void setDateHeader(String name, long date) {
mitigateResponseSplitting(name);
super.setDateHeader(name, date);
}
private void mitigateResponseSplitting(String value) {
if (value != null && (value.contains("\r") || value.contains("\n"))) {
throw new HttpResponseSplittingException();
}
}
}
Jersey supplies the actual response object if the response parameter has type #Context HttpServletResponse, but null if the response parameter has type #Context HardenedHttpServletResponse.
How do I get Jersey to call a resource method with an HttpServletResponse wrapper?
You can just make it injectable by adding it the DI system.
resourceConfig.register(new AbstractBinder() {
#Override
public void configure() {
bindAsContract(HardenedHttpServletResponse.class)
.proxy(false)
.proxyForSameScope(false)
.in(RequestScoped.class);
}
});
You will need to make the class public and also its constructor public, so that the DI system can create it. This will allow you to inject HardenedHttpServletResponse
See also:
Dependency injection with Jersey 2.0

Resources