Skip to content

Multi tenancy for Resource Server #6563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
Expand All @@ -32,9 +33,9 @@
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
Expand Down Expand Up @@ -128,6 +129,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<

private final ApplicationContext context;

private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private BearerTokenResolver bearerTokenResolver;

private JwtConfigurer jwtConfigurer;
Expand All @@ -154,6 +156,13 @@ public OAuth2ResourceServerConfigurer<H> authenticationEntryPoint(Authentication
return this;
}

public OAuth2ResourceServerConfigurer<H> authenticationManagerResolver
(AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
this.authenticationManagerResolver = authenticationManagerResolver;
return this;
}

public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
this.bearerTokenResolver = bearerTokenResolver;
Expand Down Expand Up @@ -188,10 +197,12 @@ public void configure(H http) throws Exception {
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);

AuthenticationManager manager = http.getSharedObject(AuthenticationManager.class);
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
if (resolver == null) {
resolver = request -> http.getSharedObject(AuthenticationManager.class);
}

BearerTokenAuthenticationFilter filter =
new BearerTokenAuthenticationFilter(manager);
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setBearerTokenResolver(bearerTokenResolver);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter = postProcess(filter);
Expand All @@ -203,7 +214,9 @@ public void configure(H http) throws Exception {
"same time");
}

if (this.jwtConfigurer == null && this.opaqueTokenConfigurer == null) {
if (this.jwtConfigurer == null && this.opaqueTokenConfigurer == null &&
this.authenticationManagerResolver == null ) {

throw new IllegalStateException("Jwt and Opaque Token are the only supported formats for bearer tokens " +
"in Spring Security and neither was found. Make sure to configure JWT " +
"via http.oauth2ResourceServer().jwt() or Opaque Tokens via " +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* 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
*
* https://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.springframework.security.authentication;

/**
* An interface for resolving an {@link AuthenticationManager} based on the provided context
*
* @author Josh Cummings
* @since 5.2
*/
public interface AuthenticationManagerResolver<C> {

/**
* Resolve an {@link AuthenticationManager} from a provided context
* @param context
* @return the {@link AuthenticationManager} to use
*/
AuthenticationManager resolve(C context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public final class JwtAuthenticationProvider implements AuthenticationProvider {

public JwtAuthenticationProvider(JwtDecoder jwtDecoder) {
Assert.notNull(jwtDecoder, "jwtDecoder cannot be null");

this.jwtDecoder = jwtDecoder;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@

import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
Expand Down Expand Up @@ -51,7 +52,7 @@
* @see JwtAuthenticationProvider
*/
public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
private final AuthenticationManager authenticationManager;
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;

private final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource =
new WebAuthenticationDetailsSource();
Expand All @@ -60,13 +61,24 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter

private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();

/**
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
* @param authenticationManagerResolver
*/
public BearerTokenAuthenticationFilter
(AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {

Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
this.authenticationManagerResolver = authenticationManagerResolver;
}

/**
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
* @param authenticationManager
*/
public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager) {
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
this.authenticationManager = authenticationManager;
this.authenticationManagerResolver = request -> authenticationManager;
}

/**
Expand Down Expand Up @@ -104,7 +116,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));

try {
Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);

SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authenticationResult);
Expand Down Expand Up @@ -139,5 +152,4 @@ public final void setAuthenticationEntryPoint(final AuthenticationEntryPoint aut
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
this.authenticationEntryPoint = authenticationEntryPoint;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,12 +17,12 @@

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

Expand All @@ -31,6 +31,7 @@
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
Expand All @@ -57,6 +58,9 @@ public class BearerTokenAuthenticationFilterTests {
@Mock
AuthenticationManager authenticationManager;

@Mock
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;

@Mock
BearerTokenResolver bearerTokenResolver;

Expand All @@ -66,27 +70,38 @@ public class BearerTokenAuthenticationFilterTests {

MockFilterChain filterChain;

@InjectMocks
BearerTokenAuthenticationFilter filter;

@Before
public void httpMocks() {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.filterChain = new MockFilterChain();
}

@Before
public void setterMocks() {
this.filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
this.filter.setBearerTokenResolver(this.bearerTokenResolver);
@Test
public void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletException, IOException {
when(this.bearerTokenResolver.resolve(this.request)).thenReturn("token");

BearerTokenAuthenticationFilter filter =
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);

ArgumentCaptor<BearerTokenAuthenticationToken> captor =
ArgumentCaptor.forClass(BearerTokenAuthenticationToken.class);

verify(this.authenticationManager).authenticate(captor.capture());

assertThat(captor.getValue().getPrincipal()).isEqualTo("token");
}

@Test
public void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletException, IOException {
public void doFilterWhenUsingAuthenticationManagerResolverThenAuthenticates() throws Exception {
BearerTokenAuthenticationFilter filter =
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManagerResolver));

when(this.bearerTokenResolver.resolve(this.request)).thenReturn("token");
when(this.authenticationManagerResolver.resolve(any())).thenReturn(this.authenticationManager);

this.filter.doFilter(this.request, this.response, this.filterChain);
filter.doFilter(this.request, this.response, this.filterChain);

ArgumentCaptor<BearerTokenAuthenticationToken> captor =
ArgumentCaptor.forClass(BearerTokenAuthenticationToken.class);
Expand Down Expand Up @@ -137,36 +152,56 @@ public void doFilterWhenAuthenticationFailsThenPropagatesError() throws ServletE
when(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class)))
.thenThrow(exception);

this.filter.doFilter(this.request, this.response, this.filterChain);
BearerTokenAuthenticationFilter filter =
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);

verify(this.authenticationEntryPoint).commence(this.request, this.response, exception);
}

@Test
public void setAuthenticationEntryPointWhenNullThenThrowsException() {
assertThatCode(() -> this.filter.setAuthenticationEntryPoint(null))
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
assertThatCode(() -> filter.setAuthenticationEntryPoint(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("authenticationEntryPoint cannot be null");
}

@Test
public void setBearerTokenResolverWhenNullThenThrowsException() {
assertThatCode(() -> this.filter.setBearerTokenResolver(null))
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
assertThatCode(() -> filter.setBearerTokenResolver(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("bearerTokenResolver cannot be null");
}

@Test
public void constructorWhenNullAuthenticationManagerThenThrowsException() {
assertThatCode(() -> new BearerTokenAuthenticationFilter(null))
assertThatCode(() -> new BearerTokenAuthenticationFilter((AuthenticationManager) null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("authenticationManager cannot be null");
}

@Test
public void constructorWhenNullAuthenticationManagerResolverThenThrowsException() {
assertThatCode(() ->
new BearerTokenAuthenticationFilter((AuthenticationManagerResolver<HttpServletRequest>) null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("authenticationManagerResolver cannot be null");
}

private BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) {
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setBearerTokenResolver(this.bearerTokenResolver);
return filter;
}

private void dontAuthenticate()
throws ServletException, IOException {

this.filter.doFilter(this.request, this.response, this.filterChain);
BearerTokenAuthenticationFilter filter =
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);

verifyNoMoreInteractions(this.authenticationManager);
}
Expand Down
Loading