diff --git a/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java b/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java index eb4cacf33..28e9708ac 100644 --- a/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java +++ b/hawkbit-http-security/src/test/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProviderTest.java @@ -21,6 +21,8 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc import org.springframework.security.core.Authentication; 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.Features; import ru.yandex.qatools.allure.annotations.Stories; @@ -45,8 +47,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { public void principalAndCredentialsNotTheSameThrowsAuthenticationException() { final String principal = "controllerIdURL"; final String credentials = "controllerIdHeader"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); // test, should throw authentication exception @@ -64,11 +66,12 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { public void principalAndCredentialsAreTheSameWithNoSourceIpCheckIsSuccessful() { final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); - final Authentication authenticate = underTestWithoutSourceIpCheck.authenticate(token); + final Authentication authenticate = underTestWithoutSourceIpCheck + .authenticate(token); assertThat(authenticate.isAuthenticated()).isTrue(); } @@ -78,8 +81,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { final String remoteAddress = "192.168.1.1"; final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(remoteAddress); @@ -99,14 +102,15 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { public void priniciapAndCredentialsAreTheSameAndSourceIpIsTrusted() { final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); // test, should throw authentication exception - final Authentication authenticate = underTestWithSourceIpCheck.authenticate(token); + final Authentication authenticate = underTestWithSourceIpCheck + .authenticate(token); assertThat(authenticate.isAuthenticated()).isTrue(); } @@ -116,8 +120,8 @@ public class PreAuthTokenSourceTrustAuthenticationProviderTest { "192.168.1.3" }; final String principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); 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 principal = "controllerId"; final String credentials = "controllerId"; - final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, - credentials); + final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( + principal, Lists.newArrayList(credentials)); token.setDetails(webAuthenticationDetailsMock); when(webAuthenticationDetailsMock.getRemoteAddress()).thenReturn(REQUEST_SOURCE_IP); diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java index edf2ef034..56baf3bfc 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/AbstractControllerAuthenticationFilter.java @@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory; /** * An abstraction for all controller based security. Check if the tenant * configuration is enabled. - * + * * * */ @@ -46,12 +46,6 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe return tenantAware.runAsTenant(secruityToken.getTenant(), configurationKeyTenantRunner); } - @Override - public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken); - - @Override - public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); - private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner { @Override public Boolean run() { diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java index 6836b8a31..fae0b53ad 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilter.java @@ -8,6 +8,10 @@ */ package org.eclipse.hawkbit.security; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.tenancy.TenantAware; @@ -20,8 +24,6 @@ import org.slf4j.LoggerFactory; * request URI and the credential from a request header in a the * {@link TenantSecurityToken}. * - * - * */ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractControllerAuthenticationFilter { @@ -77,8 +79,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont @Override public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { // retrieve the common name header and the authority name header from - // the http request and - // combine them together + // the http request and combine them together final String commonNameValue = secruityToken.getHeader(caCommonNameHeader); final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(), sslIssuerNameConfigTenantRunner); @@ -97,18 +98,20 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont } @Override - public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) { + public Object getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) { final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(), sslIssuerNameConfigTenantRunner); String controllerId = secruityToken.getControllerId(); // in case of legacy download artifact, the controller ID is not in the - // URL path, so then - // we just use the common name header + // URL path, so then we just use the common name header if (controllerId == null || "anonymous".equals(controllerId)) { controllerId = secruityToken.getHeader(caCommonNameHeader); } - return new HeaderAuthentication(controllerId, authorityNameConfigurationValue); + List knownHashes = splitMultiHashBySemicolon(authorityNameConfigurationValue); + + final String cntlId = controllerId; + return knownHashes.stream().map(hashItem -> new HeaderAuthentication(cntlId, hashItem)).collect(Collectors.toSet()); } /** @@ -117,12 +120,15 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont * It's ok if we find the the hash in any the trusted CA chain to accept * this request for this tenant. */ - private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHash) { + private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHashes) { + // there may be several knownIssuerHashes configured for the tenant + List knownHashes = splitMultiHashBySemicolon(knownIssuerHashes); + // iterate over the headers until we get a null header. int iHeader = 1; String foundHash; while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) { - if (foundHash.equals(knownIssuerHash)) { + if (knownHashes.contains(foundHash)) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader); } @@ -148,4 +154,8 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class).getValue()); } } + + private static List splitMultiHashBySemicolon(String knownIssuerHashes) { + return Arrays.asList(knownIssuerHashes.split(";")); + } } diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java index b4960737d..ffe1b7377 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthTokenSourceTrustAuthenticationProvider.java @@ -27,17 +27,17 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA * An spring authentication provider which supports authentication tokens of * type {@link PreAuthenticatedAuthenticationToken} created by the * {@link ControllerPreAuthenticatedSecurityHeaderFilter}. - * + * * Additionally to the authentication token providing the principal and the * credentials which must be match, this authentication provider can also check * the remote IP address of the request. - * + * * E.g. The request path is /controller/v1/{controllerId} then the controllerId * in the path is the principal. The credentials are the extracted information * from e.g. a certificate provided by an reverse proxy. Due this request is * only allowed from a specific source address this authentication manager can * also check the remote IP address of the request. - * + * * * */ @@ -58,7 +58,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica * Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given * source IP addresses which are trusted and should be checked against the * request remote IP address. - * + * * @param authorizedSourceIps * a list of IP addresses. */ @@ -70,7 +70,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica * Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given * source IP addresses which are trusted and should be checked against the * request remote IP address. - * + * * @param authorizedSourceIps * a list of IP addresses. */ @@ -87,7 +87,6 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica return null; } - boolean successAuthentication = false; final PreAuthenticatedAuthenticationToken token = (PreAuthenticatedAuthenticationToken) authentication; final Object credentials = token.getCredentials(); final Object principal = token.getPrincipal(); @@ -97,14 +96,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } - // check if principal equals credentials 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)) { - successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); - } + boolean successAuthentication = calculateAuthenticationSuccess(principal, credentials, tokenDetails); if (successAuthentication) { final Collection controllerAuthorities = new ArrayList<>(); @@ -119,6 +111,41 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica throw new BadCredentialsException("The provided principal and credentials are not match"); } + /** + * + * The credentials may either be of type HeaderAuthentication or of type + * Collection depending on the authentication mode in + * use (the latter is used in case of trusted reverse-proxy). It is checked + * whether principal equals credentials (respectively if 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. + * + * @param principal + * the {@link HeaderAuthentication} from the header + * @param credentials + * a single {@link HeaderAuthentication} or a Collection of + * HeaderAuthentication + * @param tokenDetails + * authentication details + * @return true if authentication succeeded, otherwise + * false + */ + private boolean calculateAuthenticationSuccess(Object principal, Object credentials, Object tokenDetails) { + boolean successAuthentication = false; + if (credentials instanceof Collection) { + final Collection multiValueCredentials = (Collection) credentials; + if (multiValueCredentials.contains(principal)) { + successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); + } + } else if (principal.equals(credentials)) { + successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails); + } + + return successAuthentication; + } + private boolean checkSourceIPAddressIfNeccessary(final Object tokenDetails) { boolean success = authorizedSourceIps == null; String remoteAddress = null; diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java index 90f2acd50..f0801ff4d 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/PreAuthentificationFilter.java @@ -22,7 +22,7 @@ public interface PreAuthentificationFilter { /** * Check if the filter is enabled. - * + * * @param secruityToken * the secruity info * @return is enabled diabled @@ -31,7 +31,7 @@ public interface PreAuthentificationFilter { /** * Extract the principal information from the current secruityToken. - * + * * @param secruityToken * the secruityToken * @return the extracted tenant and controller id @@ -40,17 +40,17 @@ public interface PreAuthentificationFilter { /** * Extract the principal credentials from the current secruityToken. - * + * * @param secruityToken * the secruityToken * @return the extracted tenant and controller id */ - HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); + Object getPreAuthenticatedCredentials(TenantSecurityToken secruityToken); /** * Allows to add additional authorities to the successful authenticated * token. - * + * * @return the authorities granted to the principal, or an empty collection * if the token has not been authenticated. Never null. * @see Authentication#getAuthorities() diff --git a/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java new file mode 100644 index 000000000..80979795f --- /dev/null +++ b/hawkbit-security-integration/src/test/java/org/eclipse/hawkbit/security/ControllerPreAuthenticatedSecurityHeaderFilterTest.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.security; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +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.FileResource; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Unit Tests - Security") +@Stories("Issuer hash based authentication") +@RunWith(MockitoJUnitRunner.class) +public class ControllerPreAuthenticatedSecurityHeaderFilterTest { + + private ControllerPreAuthenticatedSecurityHeaderFilter underTest; + + @Mock + private TenantConfigurationManagement tenantConfigurationManagementMock; + + @Mock + private TenantSecurityToken tenantSecurityTokenMock; + + private SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); + + 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 SINGLE_HASH = "hash1"; + private static final String SECOND_HASH = "hash2"; + private static final String UNKNOWN_HASH = "unknown"; + + private static final String MULTI_HASH = "hash1;hash2;hash3"; + + private static final TenantConfigurationValue CONFIG_VALUE_SINGLE_HASH = TenantConfigurationValue + .builder().value(SINGLE_HASH).build(); + + private static final TenantConfigurationValue CONFIG_VALUE_MULTI_HASH = TenantConfigurationValue + .builder().value(MULTI_HASH).build(); + + @Before + public void before() { + underTest = new ControllerPreAuthenticatedSecurityHeaderFilter(CA_COMMON_NAME, "X-Ssl-Issuer-Hash-%d", + tenantConfigurationManagementMock, + tenantAware, new SystemSecurityContext(tenantAware)); + } + + @Test + @Description("Tests the filter for issuer hash based authentication with a single known hash") + public void testIssuerHashBasedAuthenticationWithSingleKnownHash() { + final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH); + // use single known hash + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_SINGLE_HASH); + assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull(); + } + + @Test + @Description("Tests the filter for issuer hash based authentication with multiple known hashes") + public void testIssuerHashBasedAuthenticationWithMultipleKnownHashes() { + final TenantSecurityToken securityToken = prepareSecurityToken(SINGLE_HASH); + // use multiple known hashes + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_MULTI_HASH); + assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNotNull(); + } + + @Test + @Description("Tests the filter for issuer hash based authentication with unknown hash") + public void testIssuerHashBasedAuthenticationWithUnknownHash() { + final TenantSecurityToken securityToken = prepareSecurityToken(UNKNOWN_HASH); + // use single known hash + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_MULTI_HASH); + assertThat(underTest.getPreAuthenticatedPrincipal(securityToken)).isNull(); + } + + @Test + @Description("Tests different values for issuer hash header and inspects the credentials") + public void useDifferentValuesForIssuerHashHeader() { + final TenantSecurityToken securityToken1 = prepareSecurityToken(SINGLE_HASH); + final TenantSecurityToken securityToken2 = prepareSecurityToken(SECOND_HASH); + + final HeaderAuthentication expected1 = new HeaderAuthentication(CA_COMMON_NAME_VALUE, SINGLE_HASH); + final HeaderAuthentication expected2 = new HeaderAuthentication(CA_COMMON_NAME_VALUE, SECOND_HASH); + + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME), eq(String.class))) + .thenReturn(CONFIG_VALUE_MULTI_HASH); + + final Collection credentials1 = (Collection) underTest + .getPreAuthenticatedCredentials(securityToken1); + final Collection credentials2 = (Collection) underTest + .getPreAuthenticatedCredentials(securityToken2); + + Object principal1 = underTest.getPreAuthenticatedPrincipal(securityToken1); + Object principal2 = underTest.getPreAuthenticatedPrincipal(securityToken2); + + assertThat(credentials1.contains(expected1)).isTrue(); + assertThat(credentials2.contains(expected2)).isTrue(); + + assertEquals("hash1 expected in principal!", expected1, principal1); + assertEquals("hash2 expected in principal!", expected2, principal2); + + } + + private static TenantSecurityToken prepareSecurityToken(String issuerHashHeaderValue) { + final TenantSecurityToken securityToken = new TenantSecurityToken("DEFAULT", CA_COMMON_NAME_VALUE, + FileResource.createFileResourceBySha1("12345")); + securityToken.getHeaders().put(CA_COMMON_NAME, CA_COMMON_NAME_VALUE); + securityToken.getHeaders().put(X_SSL_ISSUER_HASH_1, issuerHashHeaderValue); + return securityToken; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java index c50b9f8fb..e041f5564 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/authentication/CertificateAuthenticationConfigurationItem.java @@ -63,13 +63,17 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti final Label caRootAuthorityLabel = new LabelBuilder().name("SSL Issuer Hash:").buildLabel(); caRootAuthorityLabel.setDescription( "The SSL Issuer iRules.X509 hash, to validate against the controller request certifcate."); + caRootAuthorityLabel.setWidthUndefined(); - caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(128).buildTextComponent(); - caRootAuthorityTextField.setWidth("500px"); + caRootAuthorityTextField = new TextFieldBuilder().immediate(true).maxLengthAllowed(160).buildTextComponent(); + caRootAuthorityTextField.setWidth("100%"); caRootAuthorityTextField.addTextChangeListener(event -> caRootAuthorityChanged()); caRootAuthorityLayout.addComponent(caRootAuthorityLabel); + caRootAuthorityLayout.setExpandRatio(caRootAuthorityLabel, 0); caRootAuthorityLayout.addComponent(caRootAuthorityTextField); + caRootAuthorityLayout.setExpandRatio(caRootAuthorityTextField, 1); + caRootAuthorityLayout.setWidth("100%"); detailLayout.addComponent(caRootAuthorityLayout);