Skip to content
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
@@ -0,0 +1,32 @@
package com.ctrip.framework.apollo.adminservice;

import com.ctrip.framework.apollo.adminservice.filter.AdminServiceAuthenticationFilter;
import com.ctrip.framework.apollo.biz.config.BizConfig;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AdminServiceAutoConfiguration {

private final BizConfig bizConfig;

public AdminServiceAutoConfiguration(final BizConfig bizConfig) {
this.bizConfig = bizConfig;
}

@Bean
public FilterRegistrationBean<AdminServiceAuthenticationFilter> adminServiceAuthenticationFilter() {
FilterRegistrationBean<AdminServiceAuthenticationFilter> filterRegistrationBean = new FilterRegistrationBean<>();

filterRegistrationBean.setFilter(new AdminServiceAuthenticationFilter(bizConfig));
filterRegistrationBean.addUrlPatterns("/apps/*");
filterRegistrationBean.addUrlPatterns("/appnamespaces/*");
filterRegistrationBean.addUrlPatterns("/instances/*");
filterRegistrationBean.addUrlPatterns("/items/*");
filterRegistrationBean.addUrlPatterns("/namespaces/*");
filterRegistrationBean.addUrlPatterns("/releases/*");

return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.ctrip.framework.apollo.adminservice.filter;

import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import java.io.IOException;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;

public class AdminServiceAuthenticationFilter implements Filter {

private static final Logger logger = LoggerFactory
.getLogger(AdminServiceAuthenticationFilter.class);
private static final Splitter ACCESS_TOKEN_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults();

private final BizConfig bizConfig;
private volatile String lastAccessTokens;
private volatile List<String> accessTokenList;

public AdminServiceAuthenticationFilter(BizConfig bizConfig) {
this.bizConfig = bizConfig;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
if (bizConfig.isAdminServiceAccessControlEnabled()) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;

String token = request.getHeader(HttpHeaders.AUTHORIZATION);

if (!checkAccessToken(token)) {
logger.warn("Invalid access token: {} for uri: {}", token, request.getRequestURI());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
}

chain.doFilter(req, resp);
}

private boolean checkAccessToken(String token) {
String accessTokens = bizConfig.getAdminServiceAccessTokens();

// if user forget to configure access tokens, then default to pass
if (Strings.isNullOrEmpty(accessTokens)) {
return true;
}

// no need to check
if (Strings.isNullOrEmpty(token)) {
return false;
}

// update cache
if (!accessTokens.equals(lastAccessTokens)) {
synchronized (this) {
accessTokenList = ACCESS_TOKEN_SPLITTER.splitToList(accessTokens);
lastAccessTokens = accessTokens;
}
}

return accessTokenList.contains(token);
}

@Override
public void destroy() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private void postConstruct() {
}

@Value("${local.server.port}")
int port;
protected int port;

protected String url(String path) {
return "http://localhost:" + port + path;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package com.ctrip.framework.apollo.adminservice.filter;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.ctrip.framework.apollo.biz.config.BizConfig;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpHeaders;

@RunWith(MockitoJUnitRunner.class)
public class AdminServiceAuthenticationFilterTest {

@Mock
private BizConfig bizConfig;
private HttpServletRequest servletRequest;
private HttpServletResponse servletResponse;
private FilterChain filterChain;

private AdminServiceAuthenticationFilter authenticationFilter;

@Before
public void setUp() throws Exception {
authenticationFilter = new AdminServiceAuthenticationFilter(bizConfig);
initVariables();
}

private void initVariables() {
servletRequest = mock(HttpServletRequest.class);
servletResponse = mock(HttpServletResponse.class);
filterChain = mock(FilterChain.class);
}

@Test
public void testWithAccessControlDisabled() throws Exception {
when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(false);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(bizConfig, never()).getAdminServiceAccessTokens();
verify(servletRequest, never()).getHeader(HttpHeaders.AUTHORIZATION);
verify(servletResponse, never()).sendError(anyInt(), anyString());
}

@Test
public void testWithAccessControlEnabledWithTokenSpecifiedWithValidTokenPassed()
throws Exception {
String someValidToken = "someToken";

when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someValidToken);
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someValidToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(bizConfig, times(1)).getAdminServiceAccessTokens();
verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());
}

@Test
public void testWithAccessControlEnabledWithTokenSpecifiedWithInvalidTokenPassed()
throws Exception {
String someValidToken = "someValidToken";
String someInvalidToken = "someInvalidToken";

when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someValidToken);
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someInvalidToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(bizConfig, times(1)).getAdminServiceAccessTokens();
verify(servletResponse, times(1))
.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
verify(filterChain, never()).doFilter(servletRequest, servletResponse);
}

@Test
public void testWithAccessControlEnabledWithTokenSpecifiedWithNoTokenPassed() throws Exception {
String someValidToken = "someValidToken";

when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someValidToken);
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(null);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(bizConfig, times(1)).getAdminServiceAccessTokens();
verify(servletResponse, times(1))
.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
verify(filterChain, never()).doFilter(servletRequest, servletResponse);
}


@Test
public void testWithAccessControlEnabledWithMultipleTokenSpecifiedWithValidTokenPassed()
throws Exception {
String someToken = "someToken";
String anotherToken = "anotherToken";

when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens())
.thenReturn(String.format("%s,%s", someToken, anotherToken));
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(bizConfig, times(1)).getAdminServiceAccessTokens();
verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());
}

@Test
public void testWithAccessControlEnabledWithNoTokenSpecifiedWithTokenPassed() throws Exception {
String someToken = "someToken";

when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens()).thenReturn(null);
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(bizConfig, times(1)).getAdminServiceAccessTokens();
verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());
}

@Test
public void testWithAccessControlEnabledWithNoTokenSpecifiedWithNoTokenPassed() throws Exception {
String someToken = "someToken";

when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens()).thenReturn(null);
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(null);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(bizConfig, times(1)).isAdminServiceAccessControlEnabled();
verify(bizConfig, times(1)).getAdminServiceAccessTokens();
verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());
}

@Test
public void testWithConfigChanged() throws Exception {
String someToken = "someToken";
String anotherToken = "anotherToken";
String yetAnotherToken = "yetAnotherToken";

// case 1: init state
when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(true);
when(bizConfig.getAdminServiceAccessTokens()).thenReturn(someToken);

when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());

// case 2: change access tokens specified
initVariables();
when(bizConfig.getAdminServiceAccessTokens())
.thenReturn(String.format("%s,%s", anotherToken, yetAnotherToken));
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(someToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(servletResponse, times(1))
.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
verify(filterChain, never()).doFilter(servletRequest, servletResponse);

initVariables();
when(servletRequest.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn(anotherToken);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());

// case 3: change access control flag
initVariables();
when(bizConfig.isAdminServiceAccessControlEnabled()).thenReturn(false);

authenticationFilter.doFilter(servletRequest, servletResponse, filterChain);

verify(filterChain, times(1)).doFilter(servletRequest, servletResponse);
verify(servletResponse, never()).sendError(anyInt(), anyString());
verify(servletRequest, never()).getHeader(HttpHeaders.AUTHORIZATION);
}
}
Loading