Fix code review findings:

* adapt return type of #getPreAuthenticatedPrincipal to the
#getPreAuthenticatedCredentials return type (both Object)
* #splitMultiHash returns list instead of array

Signed-off-by: Marcel Mager (INST-IOT/ESB) <Marcel.Mager@bosch-si.com>
This commit is contained in:
Marcel Mager (INST-IOT/ESB)
2016-09-15 09:45:28 +02:00
parent 0ccd458585
commit 5215580cd7
6 changed files with 79 additions and 43 deletions

View File

@@ -21,6 +21,8 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import com.google.common.collect.Lists;
import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories; import ru.yandex.qatools.allure.annotations.Stories;
@@ -45,8 +47,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
public void principalAndCredentialsNotTheSameThrowsAuthenticationException() { public void principalAndCredentialsNotTheSameThrowsAuthenticationException() {
final String principal = "controllerIdURL"; final String principal = "controllerIdURL";
final String credentials = "controllerIdHeader"; final String credentials = "controllerIdHeader";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
credentials); principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock); token.setDetails(webAuthenticationDetailsMock);
// test, should throw authentication exception // test, should throw authentication exception
@@ -64,11 +66,12 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() { public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() {
final String principal = "controllerId"; final String principal = "controllerId";
final String credentials = "controllerId"; final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
credentials); principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock); token.setDetails(webAuthenticationDetailsMock);
final Authentication authenticate = underTestWithoutSourceIpCheck.authenticate(token); final Authentication authenticate = underTestWithoutSourceIpCheck
.authenticate(token);
assertThat(authenticate.isAuthenticated()).isTrue(); assertThat(authenticate.isAuthenticated()).isTrue();
} }
@@ -78,8 +81,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
final String remoteAddress = "192.168.1.1"; final String remoteAddress = "192.168.1.1";
final String principal = "controllerId"; final String principal = "controllerId";
final String credentials = "controllerId"; final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
credentials); principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock); token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress);
@@ -99,14 +102,15 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() { public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() {
final String principal = "controllerId"; final String principal = "controllerId";
final String credentials = "controllerId"; final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
credentials); principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock); token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
// test, should throw authentication exception // test, should throw authentication exception
final Authentication authenticate = underTestWithSourceIpCheck.authenticate(token); final Authentication authenticate = underTestWithSourceIpCheck
.authenticate(token);
assertThat(authenticate.isAuthenticated()).isTrue(); assertThat(authenticate.isAuthenticated()).isTrue();
} }
@@ -116,8 +120,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
"192.168.1.3" }; "192.168.1.3" };
final String principal = "controllerId"; final String principal = "controllerId";
final String credentials = "controllerId"; final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
credentials); principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock); token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);
@@ -135,8 +139,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest {
final String[] trustedIPAddresses = new String[] { "192.168.1.1", "192.168.1.2", "192.168.1.3" }; final String[] trustedIPAddresses = new String[] { "192.168.1.1", "192.168.1.2", "192.168.1.3" };
final String principal = "controllerId"; final String principal = "controllerId";
final String credentials = "controllerId"; final String credentials = "controllerId";
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(
credentials); principal, Lists.newArrayList(credentials));
token.setDetails(webAuthenticationDetailsMock); token.setDetails(webAuthenticationDetailsMock);
when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP);

View File

@@ -47,7 +47,7 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
} }
@Override @Override
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); public abstract Object getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
@Override @Override
public abstract Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); public abstract Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.security;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
@@ -79,10 +80,9 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
} }
@Override @Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { public Object getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
// retrieve the common name header and the authority name header from // retrieve the common name header and the authority name header from
// the http request and // the http request and combine them together
// combine them together
final String commonNameValue = secruityToken.getHeader(caCommonNameHeader); final String commonNameValue = secruityToken.getHeader(caCommonNameHeader);
final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(), final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
sslIssuerNameConfigTenantRunner); sslIssuerNameConfigTenantRunner);
@@ -106,23 +106,17 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
sslIssuerNameConfigTenantRunner); sslIssuerNameConfigTenantRunner);
String controllerId = secruityToken.getControllerId(); String controllerId = secruityToken.getControllerId();
// in case of legacy download artifact, the controller ID is not in the // in case of legacy download artifact, the controller ID is not in the
// URL path, so then // URL path, so then we just use the common name header
// we just use the common name header
if (controllerId == null || "anonymous".equals(controllerId)) { if (controllerId == null || "anonymous".equals(controllerId)) {
controllerId = secruityToken.getHeader(caCommonNameHeader); controllerId = secruityToken.getHeader(caCommonNameHeader);
} }
String[] knownHashes = splitMultiHash(authorityNameConfigurationValue); List<String> knownHashes = splitMultiHash(authorityNameConfigurationValue);
if (knownHashes.length > 1) {
Set<HeaderAuthentication> multiHashes = new HashSet<>();
final String cntlId = controllerId;
Arrays.asList(knownHashes)
.forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem)));
return multiHashes;
} else { Set<HeaderAuthentication> multiHashes = new HashSet<>();
return new HeaderAuthentication(controllerId, authorityNameConfigurationValue); final String cntlId = controllerId;
} knownHashes.forEach(hashItem -> multiHashes.add(new HeaderAuthentication(cntlId, hashItem)));
return multiHashes;
} }
/** /**
@@ -132,12 +126,15 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
* this request for this tenant. * this request for this tenant.
*/ */
private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) { private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) {
// there may be several knownIssuerHashes configured for the tenant
// separated by a semicolon
List<String> knownHashes = splitMultiHash(knownIssuerHashes);
// iterate over the headers until we get a null header. // iterate over the headers until we get a null header.
String[] knownHashes = splitMultiHash(knownIssuerHashes);
int iHeader = 1; int iHeader = 1;
String foundHash; String foundHash;
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) { while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
if (Arrays.asList(knownHashes).contains(foundHash)) { if (knownHashes.contains(foundHash)) {
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader); LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader);
} }
@@ -164,7 +161,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
} }
} }
private static String[] splitMultiHash(String knownIssuerHashes) { private static List<String> splitMultiHash(String knownIssuerHashes) {
return knownIssuerHashes.split(";"); return Arrays.asList(knownIssuerHashes.split(";"));
} }
} }

View File

@@ -97,14 +97,16 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
throw new BadCredentialsException("The provided principal and credentials are not match"); throw new BadCredentialsException("The provided principal and credentials are not match");
} }
// check if principal equals credentials because we want to check if // The credentials may either be of type HeaderAuthentication or of type
// e.g. controllerId // Collection<HeaderAuthentication> depending on the authentication mode
// containing in the URL equals the controllerId in the special header // in use (the latter is used in case of trusted reverse-proxy).
// set by the reverse // It is checked whether principal equals credentials (respectively if
// proxy which extracted the CN from the certificate // credentials contains principal in case of collection) because we want
// to check if e.g. controllerId containing in the URL equals the
// controllerId in the special header set by the reverse-proxy which
// extracted the CN from the certificate.
if (principal.equals(credentials)) { if (principal.equals(credentials)) {
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
} else if (Collection.class.isAssignableFrom(credentials.getClass())) { } else if (Collection.class.isAssignableFrom(credentials.getClass())) {
final Collection<?> multiValueCredentials = (Collection<?>) credentials; final Collection<?> multiValueCredentials = (Collection<?>) credentials;
if (multiValueCredentials.contains(principal)) { if (multiValueCredentials.contains(principal)) {

View File

@@ -36,7 +36,7 @@ public interface PreAuthentificationFilter {
* the secruityToken * the secruityToken
* @return the extracted tenant and controller id * @return the extracted tenant and controller id
*/ */
HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); Object getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
/** /**
* Extract the principal credentials from the current secruityToken. * Extract the principal credentials from the current secruityToken.

View File

@@ -12,6 +12,8 @@ import static org.junit.Assert.*;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.Collection;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
@@ -102,10 +104,41 @@ public class ControllerPreAuthenticatedSecurityHeaderFilterTest {
assertNull(underTest.getPreAuthenticatedPrincipal(securityToken)); assertNull(underTest.getPreAuthenticatedPrincipal(securityToken));
} }
@Test
@Description("Tests different values for issuer hash header and inspects the credentials")
public void useDifferentValuesForIssuerHashHeader() {
// prepare security token
TenantSecurityToken securityToken = prepareSecurityToken();
securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash1");
when(tenantConfigurationManagementMock.getConfigurationValue(
eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class)))
.thenReturn(CONFIG_VALUE_MULTI_HASH);
HeaderAuthentication expected = new HeaderAuthentication("box1", "hash1");
Collection<HeaderAuthentication> credentials = (Collection<HeaderAuthentication>) underTest
.getPreAuthenticatedCredentials(securityToken);
assertTrue(credentials.contains(expected));
Object principal = underTest.getPreAuthenticatedPrincipal(securityToken);
assertEquals(expected, principal);
securityToken = prepareSecurityToken();
securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, "hash2");
expected = new HeaderAuthentication("box1", "hash2");
credentials = (Collection<HeaderAuthentication>) underTest.getPreAuthenticatedCredentials(securityToken);
assertTrue(credentials.contains(expected));
principal = underTest.getPreAuthenticatedPrincipal(securityToken);
assertEquals(expected, principal);
}
private static TenantSecurityToken prepareSecurityToken() { private static TenantSecurityToken prepareSecurityToken() {
final TenantSecurityToken securityToken = new TenantSecurityToken("default", "1234", final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", "box1",
FileResource.createFileResourceBySha1("12345")); FileResource.createFileResourceBySha1("12345"));
securityToken.getHeaders().put(CA_COMMON_NAME, "any"); securityToken.getHeaders().put(CA_COMMON_NAME, "box1");
return securityToken; return securityToken;
} }