Refactor header authority controller authentication (#2954)
1. (breaking changes) hawkbit.server.ddi.security.rp.cnHeader and sslIssuerHashHeader are renamed to controllerIdHeader and authorityHeader correspondingly. 2. (breaking changes) their default values are changed: X-Ssl-Client-Cn -> X-Controller-Id and X-Ssl-Issuer-Hash-%d -> X-Authority 3. Now the authority header configuration is not a string forma but just a string. The implemenation checks for this header as comma or ; separated list or seeks for header iteration <authority_header>-%d (iteration starts from 0 or 1 4. Doc fixed 5. As there are breaking changes configuration changes may be needed: a) with changing the hawkbit.server.ddi.security.rp you could turn back the previous default headers (note X-Ssl-Issuer-Hash-%d shall now be X-Ssl-Issuer-Hash), or b) you may change the headers sent by the reverse proxy Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -118,8 +118,8 @@ server.forward-headers-strategy=NATIVE
|
||||
```
|
||||
|
||||
2. In Hawkbit's UI section, under system configuration, make sure to select *Allow targets to authenticate via a
|
||||
certificate authenticated by a reverse proxy* and input the fixed issuer hash as "Hawkbit". This can be whetever you
|
||||
have configured in the nginx configuration in `proxy_set_header X-Ssl-Issuer-Hash-1` below.
|
||||
certificate authenticated by a reverse proxy* and input the fixed issuer hash as "Hawkbit". This can be whenever you
|
||||
have configured in the nginx configuration in `proxy_set_header X-Authority-1` below.
|
||||
|
||||
3. After placing your certificates and keys, you need to deploy your proxy server and apply the provided configurations.
|
||||
You can apply mutual TLS specifically to the URL given below to implement the process only for devices using the
|
||||
@@ -184,8 +184,8 @@ server {
|
||||
|
||||
# Client certificate Common Name and Issuer Hash is required
|
||||
# for auth in hawkbit.
|
||||
proxy_set_header X-Ssl-Client-Cn $ssl_client_s_dn_cn;
|
||||
proxy_set_header X-Ssl-Issuer-Hash-1 Hawkbit;
|
||||
proxy_set_header X-Controller-Id $ssl_client_s_dn_cn;
|
||||
proxy_set_header X-Authority-1 Hawkbit;
|
||||
|
||||
# These are required for clients to upload and download software.
|
||||
proxy_request_buffering off;
|
||||
|
||||
@@ -29,14 +29,6 @@ public class DdiSecurityProperties {
|
||||
private final Rp rp = new Rp();
|
||||
private final Authentication authentication = new Authentication();
|
||||
|
||||
public Authentication getAuthentication() {
|
||||
return authentication;
|
||||
}
|
||||
|
||||
public Rp getRp() {
|
||||
return rp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse proxy configuration. Defines the security properties for
|
||||
* authenticating controllers behind a reverse proxy which terminates the
|
||||
@@ -47,16 +39,19 @@ public class DdiSecurityProperties {
|
||||
public static class Rp {
|
||||
|
||||
/**
|
||||
* HTTP header field for common name of a DDI target client certificate.
|
||||
* HTTP header field for controller ID (e.g. CN of the controller certificate) of a DDI target client certificate.
|
||||
*/
|
||||
private String cnHeader = "X-Ssl-Client-Cn";
|
||||
private String controllerIdHeader = "X-Controller-Id";
|
||||
/**
|
||||
* HTTP header field for issuer hash of a DDI target client certificate.
|
||||
* HTTP header field for authority(ies) (e.g. SHA-256 fingerprints of issuer certificates) of a DDI target client certificate.
|
||||
*/
|
||||
private String sslIssuerHashHeader = "X-Ssl-Issuer-Hash-%d";
|
||||
private String authorityHeader = "X-Authority";
|
||||
/**
|
||||
* List of trusted (reverse proxy) IP addresses for performing DDI
|
||||
* client certificate auth.
|
||||
* Regular expression for authorities list separator
|
||||
*/
|
||||
private String authoritiesSeparatorRegex = "[;,]";
|
||||
/**
|
||||
* List of trusted (reverse proxy) IP addresses for performing DDI client certificate auth.
|
||||
*/
|
||||
private List<String> trustedIPs;
|
||||
}
|
||||
@@ -67,14 +62,14 @@ public class DdiSecurityProperties {
|
||||
@Data
|
||||
public static class Authentication {
|
||||
|
||||
private final Targettoken targettoken = new Targettoken();
|
||||
private final Gatewaytoken gatewaytoken = new Gatewaytoken();
|
||||
private final TargetToken targettoken = new TargetToken();
|
||||
private final GatewayToken gatewaytoken = new GatewayToken();
|
||||
|
||||
/**
|
||||
* Target token auth. Tokens are defined per target.
|
||||
*/
|
||||
@Data
|
||||
public static class Targettoken {
|
||||
public static class TargetToken {
|
||||
|
||||
/**
|
||||
* Set to true to enable target token auth.
|
||||
@@ -86,7 +81,7 @@ public class DdiSecurityProperties {
|
||||
* Gateway token auth. Tokens are defined per tenant. Use with care!
|
||||
*/
|
||||
@Data
|
||||
public static class Gatewaytoken {
|
||||
public static class GatewayToken {
|
||||
|
||||
/**
|
||||
* Gateway token based authentication enabled.
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.security.controller;
|
||||
|
||||
import static org.eclipse.hawkbit.audit.SecurityLogger.LOGGER;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asTenant;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_HEADER_AUTHORITY_NAME;
|
||||
import static org.eclipse.hawkbit.repository.helper.TenantConfigHelper.getAsSystem;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_HEADER_AUTHORITY;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
@@ -29,62 +29,46 @@ import org.springframework.security.core.Authentication;
|
||||
@Slf4j
|
||||
public class SecurityHeaderAuthenticator extends Authenticator.AbstractAuthenticator {
|
||||
|
||||
private static final Logger LOG_SECURITY_AUTH = LoggerFactory.getLogger("server-security.auth");
|
||||
// e.g: X-Controller-Id: controller-1 or X-Subject-CN: controller-1
|
||||
private final String controllerIdHeader;
|
||||
// e.g.: X-Authority: X,Y or X-CA-Fingerprint-0: <e.g. SHA-256 fingerprint>
|
||||
// could be used with one or multiple authorities that has confirmed the controller id:
|
||||
// 1. <authority>=X,Y,Z -> comma separated list of authorities (could be single authority)
|
||||
// 2. <authority>-0=X, <authority>-1=Y, .. ... until we get a null header
|
||||
private final String authorityHeader;
|
||||
private final String authoritiesSeparatorRegex;
|
||||
|
||||
// Example Headers with Cert Information
|
||||
// Clientip: 217.24.201.180
|
||||
// X-Forwarded-Proto: https
|
||||
// X-Ssl-Client-Cn: my.name
|
||||
// X-Ssl-Client-Dn: CN=my.name,CN=O,CN=R,CN=DE,CN=BOSCH,CN=pki,DC=bosch,DC=com
|
||||
// X-Ssl-Client-Hash: 7f:87:cb:b5:9c:e0:c5:0a:1a:a6:57:69:0f:ca:0a:95
|
||||
// X-Ssl-Client-Notafter: Dec 18 08:02:45 2017 GMT
|
||||
// X-Ssl-Client-Notbefore: Dec 18 07:32:45 2014 GMT
|
||||
// X-Ssl-Client-Verify: ok
|
||||
// X-Ssl-Issuer: CN=Bosch-CA1-DE,CN=PKI,DC=Bosch,DC=com
|
||||
// X-Ssl-Issuer-Dn-1: CN=Bosch-CA-DE,CN=PKI,DC=Bosch,DC=com
|
||||
// X-Ssl-Issuer-Hash-1: ae:11:f5:6a:0a:e8:74:50:81:0e:0c:37:ec:c5:22:fc
|
||||
private final String caCommonNameHeader;
|
||||
// the X-Ssl-Issuer-Hash basic header: Contains the x509 fingerprint hash, this
|
||||
// header exists multiple times in the request for all trusted chains.
|
||||
private final String sslIssuerHashBasicHeader;
|
||||
|
||||
public SecurityHeaderAuthenticator(final String caCommonNameHeader, final String caAuthorityNameHeader) {
|
||||
this.caCommonNameHeader = caCommonNameHeader;
|
||||
this.sslIssuerHashBasicHeader = caAuthorityNameHeader;
|
||||
public SecurityHeaderAuthenticator(final DdiSecurityProperties.Rp rp) {
|
||||
this.controllerIdHeader = rp.getControllerIdHeader();
|
||||
this.authorityHeader = rp.getAuthorityHeader();
|
||||
this.authoritiesSeparatorRegex = rp.getAuthoritiesSeparatorRegex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(final ControllerSecurityToken controllerSecurityToken) {
|
||||
// retrieve the common name header and the authority name header from the http request and combine them together
|
||||
final String commonNameValue = controllerSecurityToken.getHeader(caCommonNameHeader);
|
||||
if (commonNameValue == null) {
|
||||
log.debug("The request doesn't contain the 'common name' header");
|
||||
final String verifiedControllerId = controllerSecurityToken.getHeader(controllerIdHeader);
|
||||
if (verifiedControllerId == null) {
|
||||
log.debug("The request doesn't contain the '{}' header", controllerIdHeader);
|
||||
return null;
|
||||
}
|
||||
if (!commonNameValue.equals(controllerSecurityToken.getControllerId())) {
|
||||
log.debug("The request contains the 'common name' header but it doesn't match the controller id");
|
||||
if (!verifiedControllerId.equals(controllerSecurityToken.getControllerId())) {
|
||||
log.debug("The request contains the '{}' header but it doesn't match the controller id", controllerIdHeader);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isEnabled(controllerSecurityToken)) {
|
||||
if (!isEnabled(controllerSecurityToken)) { // in order to do not do calls to db - check after previous header checks
|
||||
log.debug("The gateway header authentication is disabled");
|
||||
return null;
|
||||
}
|
||||
|
||||
final String sslIssuerHashValue = getIssuerHashHeader(
|
||||
controllerSecurityToken,
|
||||
asTenant(
|
||||
controllerSecurityToken.getTenant(),
|
||||
() -> TenantConfigHelper.getAsSystem(AUTHENTICATION_HEADER_AUTHORITY_NAME, String.class)));
|
||||
if (sslIssuerHashValue == null) {
|
||||
log.debug("The request contains the 'common name' header but trusted hash is not found");
|
||||
final String tenant = controllerSecurityToken.getTenant();
|
||||
if (verify(controllerSecurityToken, asTenant(tenant, () -> getAsSystem(AUTHENTICATION_HEADER_AUTHORITY, String.class)))) {
|
||||
log.trace("Found trusted authority ****, using as credentials (tenant: {})", tenant);
|
||||
return authenticatedController(tenant, verifiedControllerId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.debug("Found sslIssuerHash ****, using as credentials for tenant {}", controllerSecurityToken.getTenant());
|
||||
}
|
||||
|
||||
return authenticatedController(controllerSecurityToken.getTenant(), commonNameValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,28 +82,52 @@ public class SecurityHeaderAuthenticator extends Authenticator.AbstractAuthentic
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the {@link #sslIssuerHashBasicHeader} basic header {@code X-Ssl-Issuer-Hash-%d} and try to find the same hash as known.
|
||||
* It's ok if we find the hash in any the trusted CA chain to accept this request for this tenant.
|
||||
* Check {@link #authorityHeader} basic header or iterates over {@link #authorityHeader}-%d and try to find the same authority as trusted.
|
||||
* It's ok if we find any authority (in headers, authenticated the controller) to accept this request for this tenant.
|
||||
*/
|
||||
@SuppressWarnings("java:S2629") // check if debug is enabled is maybe heavier then evaluation
|
||||
private String getIssuerHashHeader(final ControllerSecurityToken controllerSecurityToken, final String knownIssuerHashes) {
|
||||
// there may be several knownIssuerHashes configured for the tenant
|
||||
final List<String> knownHashes = Arrays.stream(knownIssuerHashes.split("[;,]")).map(String::toLowerCase).toList();
|
||||
@SuppressWarnings({ "java:S2629", "java:S135", "java:S3776" }) // check if debug is enabled is maybe heavier than evaluation, rest - fine
|
||||
private boolean verify(final ControllerSecurityToken controllerSecurityToken, final String headerAuthority) {
|
||||
// there may be several trusted authorities (headerAuthority config value) configured for the tenant
|
||||
final List<String> trustedAuthorities = Arrays.stream(headerAuthority.split(authoritiesSeparatorRegex))
|
||||
.map(String::toLowerCase).map(String::trim).toList();
|
||||
|
||||
// iterate over the headers until we get a null header.
|
||||
String foundHash;
|
||||
for (int iHeader = 1; (foundHash = controllerSecurityToken.getHeader(
|
||||
String.format(sslIssuerHashBasicHeader, iHeader))) != null; iHeader++) {
|
||||
if (knownHashes.contains(foundHash.toLowerCase())) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Found matching ssl issuer hash at position {}", iHeader);
|
||||
}
|
||||
return foundHash.toLowerCase();
|
||||
boolean hasAuthorityHeader = false;
|
||||
String matchingAuthority = null;
|
||||
|
||||
final String authorityHeaderValue = controllerSecurityToken.getHeader(authorityHeader);
|
||||
if (authorityHeaderValue == null) {
|
||||
// go for authority header prefixed iteration. iterate over the headers until we get a null header. Start from index 0 or 1
|
||||
for (int i = 0; ; i++) {
|
||||
final String authority = controllerSecurityToken.getHeader(authorityHeader + "-" + i);
|
||||
if (authority == null) {
|
||||
if (i != 0) {
|
||||
break; // end of index iteration
|
||||
} // if 0, try if start from 1
|
||||
} else {
|
||||
hasAuthorityHeader = true;
|
||||
final String authorityLower = authority.toLowerCase();
|
||||
if (trustedAuthorities.contains(authorityLower)) {
|
||||
matchingAuthority = authorityLower;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG_SECURITY_AUTH.debug(
|
||||
"Certificate request but no matching hash found in headers {} for common name {} in request",
|
||||
sslIssuerHashBasicHeader, controllerSecurityToken.getHeader(caCommonNameHeader));
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
hasAuthorityHeader = true;
|
||||
matchingAuthority = Arrays.stream(authorityHeaderValue.split(authoritiesSeparatorRegex))
|
||||
.map(String::toLowerCase).map(String::trim)
|
||||
.filter(trustedAuthorities::contains)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
if (matchingAuthority == null) {
|
||||
if (hasAuthorityHeader) {
|
||||
LOGGER.debug("[SEC_HEADER_AUTH] Request has an authority header(s) but it doesn't match any trusted authority");
|
||||
}
|
||||
} else if (log.isTraceEnabled()) {
|
||||
log.trace("Found matching authority {}", matchingAuthority);
|
||||
}
|
||||
|
||||
return matchingAuthority != null;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
package org.eclipse.hawkbit.security.controller;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_HEADER_AUTHORITY_NAME;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_HEADER_AUTHORITY;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_HEADER_ENABLED;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -33,23 +33,23 @@ class SecurityHeaderAuthenticatorTest {
|
||||
private static final String CA_COMMON_NAME = "ca-cn";
|
||||
private static final String CA_COMMON_NAME_VALUE = "box1";
|
||||
|
||||
private static final String X_SSL_ISSUER_HASH_1 = "X-Ssl-Issuer-Hash-1";
|
||||
private static final String X_AUTHORITY_1 = "X-Authority-1";
|
||||
|
||||
private static final String SINGLE_HASH = "hash1";
|
||||
private static final String SECOND_HASH = "hash2";
|
||||
private static final String THIRD_HASH = "hash3";
|
||||
private static final String UNKNOWN_HASH = "unknown";
|
||||
private static final String SINGLE_AUTHORITY = "hash1";
|
||||
private static final String SECOND_AUTHORITY = "hash2";
|
||||
private static final String THIRD_AUTHORITY = "hash3";
|
||||
private static final String UNKNOWN_AUTHORITY = "unknown";
|
||||
|
||||
private static final String MULTI_HASH = "HASH1;hash2,HASH3,HASH1";
|
||||
private static final String MULTI_AUTHORITY = "HASH1;hash2,HASH3,HASH1";
|
||||
|
||||
private static final TenantConfigurationValue<String> CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue
|
||||
.<String> builder().value(SINGLE_HASH).build();
|
||||
private static final TenantConfigurationValue<String> CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue
|
||||
.<String> builder().value(MULTI_HASH).build();
|
||||
private static final TenantConfigurationValue<Boolean> CONFIG_VALUE_ENABLED = TenantConfigurationValue
|
||||
.<Boolean> builder().value(true).build();
|
||||
private static final TenantConfigurationValue<Boolean> CONFIG_VALUE_DISABLED = TenantConfigurationValue
|
||||
.<Boolean> builder().value(false).build();
|
||||
private static final TenantConfigurationValue<String> CONFIG_VALUE_SINGLE_AUTHORITY = TenantConfigurationValue.<String> builder()
|
||||
.value(SINGLE_AUTHORITY).build();
|
||||
private static final TenantConfigurationValue<String> CONFIG_VALUE_MULTI_AUTHORITY = TenantConfigurationValue.<String> builder()
|
||||
.value(MULTI_AUTHORITY).build();
|
||||
private static final TenantConfigurationValue<Boolean> CONFIG_VALUE_ENABLED = TenantConfigurationValue.<Boolean> builder()
|
||||
.value(true).build();
|
||||
private static final TenantConfigurationValue<Boolean> CONFIG_VALUE_DISABLED = TenantConfigurationValue.<Boolean> builder()
|
||||
.value(false).build();
|
||||
|
||||
private Authenticator authenticator;
|
||||
|
||||
@@ -59,17 +59,19 @@ class SecurityHeaderAuthenticatorTest {
|
||||
@BeforeEach
|
||||
void before() {
|
||||
TenantConfigHelper.setTenantConfigurationManagement(tenantConfigurationManagementMock);
|
||||
authenticator = new SecurityHeaderAuthenticator(CA_COMMON_NAME, "X-Ssl-Issuer-Hash-%d");
|
||||
final DdiSecurityProperties.Rp rp = new DdiSecurityProperties.Rp();
|
||||
rp.setControllerIdHeader(CA_COMMON_NAME);
|
||||
authenticator = new SecurityHeaderAuthenticator(rp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests successful authentication with multiple a single hashes
|
||||
*/
|
||||
@Test
|
||||
void testWithSingleKnownHash() {
|
||||
final ControllerSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_AUTHORITY_NAME, String.class))
|
||||
.thenReturn(CONFIG_VALUE_SINGLE_HASH);
|
||||
void testWithSingleTrustedAuthority() {
|
||||
final ControllerSecurityToken securityToken = prepareSecurityToken(SINGLE_AUTHORITY);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_AUTHORITY, String.class))
|
||||
.thenReturn(CONFIG_VALUE_SINGLE_AUTHORITY);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_ENABLED, Boolean.class))
|
||||
.thenReturn(CONFIG_VALUE_ENABLED);
|
||||
|
||||
@@ -82,19 +84,19 @@ class SecurityHeaderAuthenticatorTest {
|
||||
* Tests successful authentication with multiple hashes
|
||||
*/
|
||||
@Test
|
||||
void testWithMultipleKnownHashes() {
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_AUTHORITY_NAME, String.class))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||
void testWithMultipleTrustedAuthority() {
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_AUTHORITY, String.class))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_AUTHORITY);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_ENABLED, Boolean.class))
|
||||
.thenReturn(CONFIG_VALUE_ENABLED);
|
||||
|
||||
assertThat(authenticator.authenticate(prepareSecurityToken(SINGLE_HASH)))
|
||||
assertThat(authenticator.authenticate(prepareSecurityToken(SINGLE_AUTHORITY)))
|
||||
.isNotNull()
|
||||
.hasFieldOrPropertyWithValue("principal", CA_COMMON_NAME_VALUE);
|
||||
assertThat(authenticator.authenticate(prepareSecurityToken(SECOND_HASH)))
|
||||
assertThat(authenticator.authenticate(prepareSecurityToken(SECOND_AUTHORITY)))
|
||||
.isNotNull()
|
||||
.hasFieldOrPropertyWithValue("principal", CA_COMMON_NAME_VALUE);
|
||||
assertThat(authenticator.authenticate(prepareSecurityToken(THIRD_HASH)))
|
||||
assertThat(authenticator.authenticate(prepareSecurityToken(THIRD_AUTHORITY)))
|
||||
.isNotNull()
|
||||
.hasFieldOrPropertyWithValue("principal", CA_COMMON_NAME_VALUE);
|
||||
}
|
||||
@@ -103,10 +105,10 @@ class SecurityHeaderAuthenticatorTest {
|
||||
* Tests that if the hash is unknown, the authentication fails
|
||||
*/
|
||||
@Test
|
||||
void testWithUnknownHash() {
|
||||
final ControllerSecurityToken securityToken = prepareSecurityToken(UNKNOWN_HASH);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_AUTHORITY_NAME, String.class))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_HASH);
|
||||
void testWithUnTrustedAuthority() {
|
||||
final ControllerSecurityToken securityToken = prepareSecurityToken(UNKNOWN_AUTHORITY);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_AUTHORITY, String.class))
|
||||
.thenReturn(CONFIG_VALUE_MULTI_AUTHORITY);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_ENABLED, Boolean.class))
|
||||
.thenReturn(CONFIG_VALUE_ENABLED);
|
||||
|
||||
@@ -120,7 +122,7 @@ class SecurityHeaderAuthenticatorTest {
|
||||
void testWithNonMatchingCN() {
|
||||
final ControllerSecurityToken securityToken = new ControllerSecurityToken("DEFAULT", "otherControllerID");
|
||||
securityToken.putHeader(CA_COMMON_NAME, CA_COMMON_NAME_VALUE);
|
||||
securityToken.putHeader(X_SSL_ISSUER_HASH_1, SINGLE_HASH);
|
||||
securityToken.putHeader(X_AUTHORITY_1, SINGLE_AUTHORITY);
|
||||
|
||||
assertThat(authenticator.authenticate(securityToken)).isNull();
|
||||
}
|
||||
@@ -137,10 +139,9 @@ class SecurityHeaderAuthenticatorTest {
|
||||
* Tests that if disabled, the authentication fails
|
||||
*/
|
||||
@Test
|
||||
void testWithSingleKnownHashButDisabled() {
|
||||
final ControllerSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_ENABLED, Boolean.class))
|
||||
.thenReturn(CONFIG_VALUE_DISABLED);
|
||||
void testWithSingleTrustedAuthorityButDisabled() {
|
||||
final ControllerSecurityToken securityToken = prepareSecurityToken(SINGLE_AUTHORITY);
|
||||
when(tenantConfigurationManagementMock.getConfigurationValue(AUTHENTICATION_HEADER_ENABLED, Boolean.class)).thenReturn(CONFIG_VALUE_DISABLED);
|
||||
|
||||
assertThat(authenticator.authenticate(securityToken)).isNull();
|
||||
}
|
||||
@@ -148,7 +149,7 @@ class SecurityHeaderAuthenticatorTest {
|
||||
private static ControllerSecurityToken prepareSecurityToken(final String issuerHashHeaderValue) {
|
||||
final ControllerSecurityToken securityToken = new ControllerSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE);
|
||||
securityToken.putHeader(CA_COMMON_NAME, CA_COMMON_NAME_VALUE);
|
||||
securityToken.putHeader(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue);
|
||||
securityToken.putHeader(X_AUTHORITY_1, issuerHashHeaderValue);
|
||||
return securityToken;
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,7 @@ class ControllerDownloadSecurityConfiguration {
|
||||
.anonymous(AbstractHttpConfigurer::disable)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.addFilterBefore(new AuthenticationFilters.SecurityHeaderAuthenticationFilter(
|
||||
new SecurityHeaderAuthenticator(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(), ddiSecurityConfiguration.getRp().getSslIssuerHashHeader()),
|
||||
new SecurityHeaderAuthenticator(ddiSecurityConfiguration.getRp()),
|
||||
ddiSecurityConfiguration), AuthorizationFilter.class)
|
||||
.addFilterBefore(new AuthenticationFilters.SecurityTokenAuthenticationFilter(
|
||||
new SecurityTokenAuthenticator(controllerManagement),
|
||||
|
||||
@@ -91,9 +91,7 @@ class ControllerSecurityConfiguration {
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.addFilterBefore(
|
||||
new AuthenticationFilters.SecurityHeaderAuthenticationFilter(
|
||||
new SecurityHeaderAuthenticator(
|
||||
ddiSecurityConfiguration.getRp().getCnHeader(),
|
||||
ddiSecurityConfiguration.getRp().getSslIssuerHashHeader()), ddiSecurityConfiguration),
|
||||
new SecurityHeaderAuthenticator(ddiSecurityConfiguration.getRp()), ddiSecurityConfiguration),
|
||||
AuthorizationFilter.class)
|
||||
.addFilterBefore(
|
||||
new AuthenticationFilters.SecurityTokenAuthenticationFilter(
|
||||
|
||||
@@ -69,9 +69,9 @@ public class TenantConfigurationProperties {
|
||||
*/
|
||||
public static final String AUTHENTICATION_HEADER_ENABLED = "authentication.header.enabled";
|
||||
/**
|
||||
* Header based authentication authority name.
|
||||
* Header based authentication authority(-ies, could be list).
|
||||
*/
|
||||
public static final String AUTHENTICATION_HEADER_AUTHORITY_NAME = "authentication.header.authority";
|
||||
public static final String AUTHENTICATION_HEADER_AUTHORITY = "authentication.header.authority";
|
||||
/**
|
||||
* Target token based authentication enabled.
|
||||
*/
|
||||
|
||||
@@ -90,7 +90,7 @@ public class HawkbitFlywayDbInit {
|
||||
log.info("Start ({}): {}@{}, table: {}, locations: {}, sql-migration-suffixes: {}",
|
||||
MODE, USER, URL, TABLE, LOCATIONS, SQL_MIGRATION_SUFFIXES);
|
||||
|
||||
// configured via system properties callbacks are with prority. If not confiured - try to load via service loader
|
||||
// configured via system properties callbacks are with priority. If not configured - try to load via service loader
|
||||
final Callback[] callbackViaServiceLoader = CALLBACKS.length == 0 ? ServiceLoader.load(Callback.class).stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.toArray(Callback[]::new) : new Callback[0];
|
||||
|
||||
Reference in New Issue
Block a user