Custom Tenant configuration. (#395)

* Tenant configuration configurable.
Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2016-12-23 07:19:46 +01:00
committed by GitHub
parent 4d35413f71
commit feb3369858
53 changed files with 730 additions and 447 deletions

View File

@@ -7,6 +7,7 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# Displayed basic auth realm # Displayed basic auth realm
security.basic.realm=HawkBit security.basic.realm=HawkBit
@@ -82,3 +83,45 @@ hawkbit.artifact.url.protocols.md5sum-http.port=${hawkbit.artifact.url.protocols
hawkbit.artifact.url.protocols.md5sum-http.supports=DDI hawkbit.artifact.url.protocols.md5sum-http.supports=DDI
hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM
# DDI and download security
hawkbit.server.ddi.security.authentication.header.enabled=false
hawkbit.server.ddi.security.authentication.header.authority=
hawkbit.server.ddi.security.authentication.targettoken.enabled=false
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
hawkbit.server.download.anonymous.enabled=false
hawkbit.server.ddi.security.authentication.gatewaytoken.key=
# Default tenant configuration properties
hawkbit.server.tenant.configuration.authentication-header-enabled.keyName=authentication.header.enabled
hawkbit.server.tenant.configuration.authentication-header-enabled.defaultValue=${hawkbit.server.ddi.security.authentication.header.enabled}
hawkbit.server.tenant.configuration.authentication-header-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-header-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-header-authority.keyName=authentication.header.authority
hawkbit.server.tenant.configuration.authentication-header-authority.defaultValue=${hawkbit.server.ddi.security.authentication.header.authority}
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.keyName=authentication.targettoken.enabled
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.defaultValue=${hawkbit.server.ddi.security.authentication.targettoken.enabled}
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.keyName=authentication.gatewaytoken.enabled
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.defaultValue=${hawkbit.server.ddi.security.authentication.gatewaytoken.enabled}
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.keyName=authentication.gatewaytoken.key
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.defaultValue=${hawkbit.server.ddi.security.authentication.gatewaytoken.key}
hawkbit.server.tenant.configuration.polling-time.keyName=pollingTime
hawkbit.server.tenant.configuration.polling-time.defaultValue=${hawkbit.controller.pollingTime}
hawkbit.server.tenant.configuration.polling-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.server.tenant.configuration.polling-overdue-time.keyName=pollingOverdueTime
hawkbit.server.tenant.configuration.polling-overdue-time.defaultValue=${hawkbit.controller.pollingOverdueTime}
hawkbit.server.tenant.configuration.polling-overdue-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.server.tenant.configuration.anonymous-download-enabled.keyName=anonymous.download.enabled
hawkbit.server.tenant.configuration.anonymous-download-enabled.defaultValue=${hawkbit.server.download.anonymous.enabled}
hawkbit.server.tenant.configuration.anonymous-download-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.anonymous-download-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator

View File

@@ -10,9 +10,7 @@ package org.eclipse.hawkbit;
import java.io.Serializable; import java.io.Serializable;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
/** /**
* Defines global configuration for the controllers/clients on the provisioning * Defines global configuration for the controllers/clients on the provisioning
@@ -20,8 +18,7 @@ import org.springframework.context.EnvironmentAware;
* *
* *
* Note: many of the controller related properties can be overridden on tenant * Note: many of the controller related properties can be overridden on tenant
* level. As a result they are not defined here but in * level.
* {@link TenantConfigurationKey} and injected using {@link EnvironmentAware}.
* *
*/ */
@ConfigurationProperties(prefix = "hawkbit.controller") @ConfigurationProperties(prefix = "hawkbit.controller")
@@ -29,8 +26,8 @@ public class ControllerPollProperties implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Maximum polling time that can be configured by a tenant in HH:MM:SS * Maximum polling time that can be configured system wide and by tenant in
* notation. * HH:MM:SS notation.
*/ */
private String maxPollingTime = "23:59:59"; private String maxPollingTime = "23:59:59";
@@ -40,6 +37,34 @@ public class ControllerPollProperties implements Serializable {
*/ */
private String minPollingTime = "00:00:30"; private String minPollingTime = "00:00:30";
/**
* Controller polling time that can be configured system wide and by tenant
* in HH:MM:SS notation.
*/
private String pollingTime = "00:05:00";
/**
* Controller polling overdue time that can be configured system wide and by
* tenant in HH:MM:SS notation.
*/
private String pollingOverdueTime = "00:05:00";
public String getPollingTime() {
return pollingTime;
}
public void setPollingTime(final String pollingTime) {
this.pollingTime = pollingTime;
}
public String getPollingOverdueTime() {
return pollingOverdueTime;
}
public void setPollingOverdueTime(final String pollingOverdueTime) {
this.pollingOverdueTime = pollingOverdueTime;
}
public String getMaxPollingTime() { public String getMaxPollingTime() {
return maxPollingTime; return maxPollingTime;
} }

View File

@@ -22,12 +22,52 @@ public class HawkbitServerProperties {
*/ */
private String url = "http://localhost:8080"; private String url = "http://localhost:8080";
private final Anonymous anonymous = new Anonymous();
private final Build build = new Build(); private final Build build = new Build();
public Anonymous getAnonymous() {
return anonymous;
}
public Build getBuild() { public Build getBuild() {
return build; return build;
} }
/**
* Properties for anonymous API access by Devices/Controllers.
*
*/
public static class Anonymous {
private final Download download = new Download();
public Download getDownload() {
return download;
}
/**
* Properties for artifact download under anonymous API access by
* Devices/Controllers.
*
*/
public static class Download {
/**
* Unauthenticated artifact download possible if true.
*/
private boolean enabled;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}
}
/** /**
* Build information of the hawkBit instance. Influenced by maven. * Build information of the hawkBit instance. Influenced by maven.
* *

View File

@@ -1,168 +0,0 @@
/**
* 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.tenancy.configuration;
import java.util.Arrays;
import java.util.Optional;
import org.eclipse.hawkbit.ControllerPollProperties;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationStringValidator;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidator;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.springframework.context.ApplicationContext;
/**
* An enum which defines the tenant specific configurations which can be
* configured for each tenant separately. The non overridable properties are
* configured in {@link ControllerPollProperties} instead.
*
*/
public enum TenantConfigurationKey {
/**
* boolean value {@code true} {@code false}.
*/
AUTHENTICATION_MODE_HEADER_ENABLED("authentication.header.enabled", "hawkbit.server.ddi.security.authentication.header.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class),
/**
*
*/
AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME("authentication.header.authority", "hawkbit.server.ddi.security.authentication.header.authority", String.class, Boolean.FALSE.toString(), TenantConfigurationStringValidator.class),
/**
* boolean value {@code true} {@code false}.
*/
AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED("authentication.targettoken.enabled", "hawkbit.server.ddi.security.authentication.targettoken.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class),
/**
* boolean value {@code true} {@code false}.
*/
AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED("authentication.gatewaytoken.enabled", "hawkbit.server.ddi.security.authentication.gatewaytoken.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class),
/**
* string value which holds the name of the security token key.
*/
AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME("authentication.gatewaytoken.name", "hawkbit.server.ddi.security.authentication.gatewaytoken.name", String.class, null, TenantConfigurationStringValidator.class),
/**
* string value which holds the actual security-key of the gateway security
* token.
*/
AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY("authentication.gatewaytoken.key", "hawkbit.server.ddi.security.authentication.gatewaytoken.key", String.class, null, TenantConfigurationStringValidator.class),
/**
* string value which holds the polling time interval in the format HH:mm:ss
*/
POLLING_TIME_INTERVAL("pollingTime", "hawkbit.controller.pollingTime", String.class, null, TenantConfigurationPollingDurationValidator.class),
/**
* string value which holds the polling time interval in the format HH:mm:ss
*/
POLLING_OVERDUE_TIME_INTERVAL("pollingOverdueTime", "hawkbit.controller.pollingOverdueTime", String.class, null, TenantConfigurationPollingDurationValidator.class),
/**
* boolean value {@code true} {@code false}.
*/
ANONYMOUS_DOWNLOAD_MODE_ENABLED("anonymous.download.enabled", "hawkbit.server.download.anonymous.enabled", Boolean.class, Boolean.FALSE.toString(), TenantConfigurationBooleanValidator.class);
private final String keyName;
private final String defaultKeyName;
private final Class<?> dataType;
private final String defaultValue;
private final Class<? extends TenantConfigurationValidator> validator;
/**
*
* @param key
* the property key name
* @param defaultKeyName
* the allowed values for this specific key
* @param dataType
* the class of the property
* @param defaultValue
* value which should be returned, in case there is no value in
* the database
* @param validator
* Validator which validates, that property is of correct format
*/
TenantConfigurationKey(final String key, final String defaultKeyName, final Class<?> dataType,
final String defaultValue, final Class<? extends TenantConfigurationValidator> validator) {
this.keyName = key;
this.dataType = dataType;
this.defaultKeyName = defaultKeyName;
this.defaultValue = defaultValue;
this.validator = validator;
}
/**
* @return the keyName
*/
public String getKeyName() {
return keyName;
}
/**
* @return the defaultKeyName
*/
public String getDefaultKeyName() {
return defaultKeyName;
}
/**
* @return the defaultValue
*/
public String getDefaultValue() {
return defaultValue;
}
/**
*
* @return the data type of the tenant configuration value. (e.g.
* Integer.class, String.class)
*/
@SuppressWarnings("unchecked")
public <T> Class<T> getDataType() {
return (Class<T>) dataType;
}
/**
* validates if a object matches the allowed data format of the
* corresponding key
*
* @param context
* application context
* @param value
* which will be validated
* @throws TenantConfigurationValidatorException
* is thrown, when object is invalid
*/
public void validate(final ApplicationContext context, final Object value) {
final TenantConfigurationValidator createdBean = context.getAutowireCapableBeanFactory().createBean(validator);
try {
createdBean.validate(value);
} finally {
context.getAutowireCapableBeanFactory().destroyBean(createdBean);
}
}
/**
* @param keyName
* name of the TenantConfigurationKey
* @return the TenantConfigurationKey with the name keyName
*/
public static TenantConfigurationKey fromKeyName(final String keyName) {
final Optional<TenantConfigurationKey> optKey = Arrays.stream(TenantConfigurationKey.values())
.filter(conf -> conf.getKeyName().equals(keyName)).findFirst();
if (optKey.isPresent()) {
return optKey.get();
}
throw new InvalidTenantConfigurationKeyException("The given configuration key name does not exist.");
}
}

View File

@@ -0,0 +1,169 @@
/**
* 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.tenancy.configuration;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.ControllerPollProperties;
import org.eclipse.hawkbit.HawkbitServerProperties.Anonymous.Download;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationStringValidator;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidator;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
/**
* Properties for tenant configuration default values.
*
*/
@ConfigurationProperties("hawkbit.server.tenant")
public class TenantConfigurationProperties {
private final Map<String, TenantConfigurationKey> configuration = new HashMap<>();
/**
* @return full map of all configured tenant properties
*/
public Map<String, TenantConfigurationKey> getConfiguration() {
return configuration;
}
/**
* @return full list of {@link TenantConfigurationKey}s
*/
public Collection<TenantConfigurationKey> getConfigurationKeys() {
return configuration.values();
}
/**
* @param keyName
* name of the TenantConfigurationKey
* @return the TenantConfigurationKey with the name keyName
*/
public TenantConfigurationKey fromKeyName(final String keyName) {
return configuration.values().stream().filter(conf -> conf.getKeyName().equals(keyName)).findFirst()
.orElseThrow(() -> new InvalidTenantConfigurationKeyException(
"The given configuration key " + keyName + " does not exist."));
}
/**
* Tenant specific configurations which can be configured for each tenant
* separately by means of override of the system defaults.
*
*/
public static class TenantConfigurationKey {
/**
* Header based authentication enabled.
*/
public static final String AUTHENTICATION_MODE_HEADER_ENABLED = "authentication.header.enabled";
/**
* Header based authentication authority name.
*/
public static final String AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME = "authentication.header.authority";
/**
* Target token based authentication enabled.
*/
public static final String AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED = "authentication.targettoken.enabled";
/**
* Gateway token based authentication enabled.
*/
public static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED = "authentication.gatewaytoken.enabled";
/**
* Gateway token value.
*/
public static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY = "authentication.gatewaytoken.key";
/**
* See system default in
* {@link ControllerPollProperties#getPollingTime()}.
*/
public static final String POLLING_TIME_INTERVAL = "pollingTime";
/**
* See system default in
* {@link ControllerPollProperties#getPollingOverdueTime()}.
*/
public static final String POLLING_OVERDUE_TIME_INTERVAL = "pollingOverdueTime";
/**
* See system default {@link Download#isEnabled()}.
*/
public static final String ANONYMOUS_DOWNLOAD_MODE_ENABLED = "anonymous.download.enabled";
private String keyName;
private String defaultValue = "";
private Class<?> dataType = String.class;
private Class<? extends TenantConfigurationValidator> validator = TenantConfigurationStringValidator.class;
public String getKeyName() {
return keyName;
}
public void setKeyName(final String keyName) {
this.keyName = keyName;
}
/**
*
* @return the data type of the tenant configuration value. (e.g.
* Integer.class, String.class)
*/
@SuppressWarnings("unchecked")
public <T> Class<T> getDataType() {
return (Class<T>) dataType;
}
public void setDataType(final Class<?> dataType) {
this.dataType = dataType;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(final String defaultValue) {
this.defaultValue = defaultValue;
}
public Class<? extends TenantConfigurationValidator> getValidator() {
return validator;
}
public void setValidator(final Class<? extends TenantConfigurationValidator> validator) {
this.validator = validator;
}
/**
* validates if a object matches the allowed data format of the
* corresponding key
*
* @param context
* application context
* @param value
* which will be validated
* @throws TenantConfigurationValidatorException
* is thrown, when object is invalid
*/
public void validate(final ApplicationContext context, final Object value) {
final TenantConfigurationValidator createdBean = context.getAutowireCapableBeanFactory()
.createBean(validator);
try {
createdBean.validate(value);
} finally {
context.getAutowireCapableBeanFactory().destroyBean(createdBean);
}
}
}
}

View File

@@ -26,6 +26,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
@@ -43,7 +44,7 @@ import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -68,7 +69,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = { CONTROLLER_ROLE, @WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = { CONTROLLER_ROLE,
SYSTEM_ROLE }, autoCreateTenant = false) SYSTEM_ROLE }, autoCreateTenant = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetDeletedEvent.class, count = 1) }) @Expect(type = TargetDeletedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception { public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception {
mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print()) mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
@@ -92,7 +93,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@WithUser(principal = "knownPrincipal", authorities = { SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET, @WithUser(principal = "knownPrincipal", authorities = { SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET,
SpPermission.CREATE_TARGET }, allSpPermissions = false) SpPermission.CREATE_TARGET }, allSpPermissions = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1) }) @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void targetPollDoesNotModifyAuditData() throws Exception { public void targetPollDoesNotModifyAuditData() throws Exception {
// create target first with "knownPrincipal" user and audit data // create target first with "knownPrincipal" user and audit data
final String knownTargetControllerId = "target1"; final String knownTargetControllerId = "target1";
@@ -128,7 +129,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test @Test
@Description("Ensures that the system creates a new target in plug and play manner, i.e. target is authenticated but does not exist yet.") @Description("Ensures that the system creates a new target in plug and play manner, i.e. target is authenticated but does not exist yet.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPlugAndPlay() throws Exception { public void rootRsPlugAndPlay() throws Exception {
final long current = System.currentTimeMillis(); final long current = System.currentTimeMillis();
@@ -155,7 +157,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test @Test
@Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.") @Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.")
@WithUser(principal = "knownpricipal", allSpPermissions = false) @WithUser(principal = "knownpricipal", allSpPermissions = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void pollWithModifiedGloablPollingTime() throws Exception { public void pollWithModifiedGloablPollingTime() throws Exception {
securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> { securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL, tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
@@ -174,6 +177,13 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test @Test
@Description("Ensures that etag check results in not modified response if provided etag by client is identical to entity in repository.") @Description("Ensures that etag check results in not modified response if provided etag by client is identical to entity in repository.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 7),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void rootRsNotModified() throws Exception { public void rootRsNotModified() throws Exception {
final String etag = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())) final String etag = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -239,7 +249,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Description("Ensures that the target state machine of a precomissioned target switches from " @Description("Ensures that the target state machine of a precomissioned target switches from "
+ "UNKNOWN to REGISTERED when the target polls for the first time.") + "UNKNOWN to REGISTERED when the target polls for the first time.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1) }) @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPrecommissioned() throws Exception { public void rootRsPrecommissioned() throws Exception {
final Target target = testdataFactory.createTarget("4711"); final Target target = testdataFactory.createTarget("4711");
@@ -263,7 +273,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test @Test
@Description("Ensures that the source IP address of the polling target is correctly stored in repository") @Description("Ensures that the source IP address of the polling target is correctly stored in repository")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPlugAndPlayIpAddress() throws Exception { public void rootRsPlugAndPlayIpAddress() throws Exception {
// test // test
final String knownControllerId1 = "0815"; final String knownControllerId1 = "0815";
@@ -278,7 +289,8 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Test @Test
@Description("Ensures that the source IP address of the polling target is not stored in repository if disabled") @Description("Ensures that the source IP address of the polling target is not stored in repository if disabled")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsIpAddressNotStoredIfDisabled() throws Exception { public void rootRsIpAddressNotStoredIfDisabled() throws Exception {
securityProperties.getClients().setTrackRemoteIp(false); securityProperties.getClients().setTrackRemoteIp(false);
@@ -300,7 +312,7 @@ public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
@Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 3),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception { public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet(""); final DistributionSet ds = testdataFactory.createDistributionSet("");

View File

@@ -37,6 +37,15 @@ flyway.sqlMigrationSuffix=${spring.jpa.database}.sql
hawkbit.controller.pollingTime=00:01:00 hawkbit.controller.pollingTime=00:01:00
hawkbit.controller.pollingOverdueTime=00:01:00 hawkbit.controller.pollingOverdueTime=00:01:00
hawkbit.server.tenant.configuration.polling-time.keyName=pollingTime
hawkbit.server.tenant.configuration.polling-time.defaultValue=${hawkbit.controller.pollingTime}
hawkbit.server.tenant.configuration.polling-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.server.tenant.configuration.polling-overdue-time.keyName=pollingOverdueTime
hawkbit.server.tenant.configuration.polling-overdue-time.defaultValue=${hawkbit.controller.pollingOverdueTime}
hawkbit.server.tenant.configuration.polling-overdue-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.artifact.url.protocols[0].rel=download hawkbit.artifact.url.protocols[0].rel=download
hawkbit.artifact.url.protocols[0].protocol=http hawkbit.artifact.url.protocols[0].protocol=http
hawkbit.artifact.url.protocols[0].supports=DMF,DDI hawkbit.artifact.url.protocols[0].supports=DMF,DDI

View File

@@ -15,6 +15,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ApiType;
import org.eclipse.hawkbit.api.ArtifactUrl;
import org.eclipse.hawkbit.api.ArtifactUrlHandler; import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.api.URLPlaceholder; import org.eclipse.hawkbit.api.URLPlaceholder;
import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData;
@@ -216,7 +217,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
new SoftwareData(localArtifact.getSoftwareModule().getId(), localArtifact.getFilename(), new SoftwareData(localArtifact.getSoftwareModule().getId(), localArtifact.getFilename(),
localArtifact.getId(), localArtifact.getSha1Hash())), localArtifact.getId(), localArtifact.getSha1Hash())),
ApiType.DMF) ApiType.DMF)
.stream().collect(Collectors.toMap(e -> e.getProtocol(), e -> e.getRef()))); .stream().collect(Collectors.toMap(ArtifactUrl::getProtocol, ArtifactUrl::getRef)));
artifact.setFilename(localArtifact.getFilename()); artifact.setFilename(localArtifact.getFilename());
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash())); artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash()));

View File

@@ -44,7 +44,7 @@ import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymo
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp; import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
import org.eclipse.hawkbit.security.SecurityContextTenantAware; import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;

View File

@@ -11,13 +11,14 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValue; import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValue;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
/** /**
* A mapper which maps repository model to RESTful model representation and * A mapper which maps repository model to RESTful model representation and
@@ -32,19 +33,17 @@ public final class MgmtSystemMapper {
/** /**
* @param tenantConfigurationManagement * @param tenantConfigurationManagement
* instance of TenantConfigurationManagement * instance of TenantConfigurationManagement
* @param tenantConfigurationProperties
* to get defined keys
* @return a map of all existing configuration values * @return a map of all existing configuration values
*/ */
public static Map<String, MgmtSystemTenantConfigurationValue> toResponse( public static Map<String, MgmtSystemTenantConfigurationValue> toResponse(
final TenantConfigurationManagement tenantConfigurationManagement) { final TenantConfigurationManagement tenantConfigurationManagement,
final TenantConfigurationProperties tenantConfigurationProperties) {
final Map<String, MgmtSystemTenantConfigurationValue> configurationMap = new HashMap<>(); return tenantConfigurationProperties.getConfigurationKeys().stream()
.collect(Collectors.toMap(TenantConfigurationKey::getKeyName, key -> toResponse(key.getKeyName(),
for (final TenantConfigurationKey key : TenantConfigurationKey.values()) { tenantConfigurationManagement.getConfigurationValue(key.getKeyName()))));
configurationMap.put(key.getKeyName(),
toResponse(key.getKeyName(), tenantConfigurationManagement.getConfigurationValue(key)));
}
return configurationMap;
} }
/** /**

View File

@@ -19,7 +19,7 @@ import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationV
import org.eclipse.hawkbit.mgmt.rest.api.MgmtSystemRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtSystemRestApi;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -38,8 +38,15 @@ public class MgmtSystemResource implements MgmtSystemRestApi {
private static final Logger LOG = LoggerFactory.getLogger(MgmtSystemResource.class); private static final Logger LOG = LoggerFactory.getLogger(MgmtSystemResource.class);
private final TenantConfigurationManagement tenantConfigurationManagement;
private final TenantConfigurationProperties tenantConfigurationProperties;
@Autowired @Autowired
private TenantConfigurationManagement tenantConfigurationManagement; MgmtSystemResource(final TenantConfigurationManagement tenantConfigurationManagement,
final TenantConfigurationProperties tenantConfigurationProperties) {
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.tenantConfigurationProperties = tenantConfigurationProperties;
}
@Override @Override
public ResponseEntity<ResourceSupport> getSystem() { public ResponseEntity<ResourceSupport> getSystem() {
@@ -53,7 +60,9 @@ public class MgmtSystemResource implements MgmtSystemRestApi {
*/ */
@Override @Override
public ResponseEntity<Map<String, MgmtSystemTenantConfigurationValue>> getSystemConfiguration() { public ResponseEntity<Map<String, MgmtSystemTenantConfigurationValue>> getSystemConfiguration() {
return new ResponseEntity<>(MgmtSystemMapper.toResponse(tenantConfigurationManagement), HttpStatus.OK); return new ResponseEntity<>(
MgmtSystemMapper.toResponse(tenantConfigurationManagement, tenantConfigurationProperties),
HttpStatus.OK);
} }
/** /**
@@ -69,9 +78,7 @@ public class MgmtSystemResource implements MgmtSystemRestApi {
@Override @Override
public ResponseEntity<Void> deleteConfigurationValue(@PathVariable("keyName") final String keyName) { public ResponseEntity<Void> deleteConfigurationValue(@PathVariable("keyName") final String keyName) {
final TenantConfigurationKey configKey = TenantConfigurationKey.fromKeyName(keyName); tenantConfigurationManagement.deleteConfiguration(keyName);
tenantConfigurationManagement.deleteConfiguration(configKey);
LOG.debug("{} config value deleted, return status {}", keyName, HttpStatus.OK); LOG.debug("{} config value deleted, return status {}", keyName, HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.NO_CONTENT); return new ResponseEntity<>(HttpStatus.NO_CONTENT);
@@ -91,11 +98,10 @@ public class MgmtSystemResource implements MgmtSystemRestApi {
public ResponseEntity<MgmtSystemTenantConfigurationValue> getConfigurationValue( public ResponseEntity<MgmtSystemTenantConfigurationValue> getConfigurationValue(
@PathVariable("keyName") final String keyName) { @PathVariable("keyName") final String keyName) {
final TenantConfigurationKey configKey = TenantConfigurationKey.fromKeyName(keyName);
LOG.debug("{} config value getted, return status {}", keyName, HttpStatus.OK); LOG.debug("{} config value getted, return status {}", keyName, HttpStatus.OK);
return new ResponseEntity<>(MgmtSystemMapper.toResponse(configKey.getKeyName(), return new ResponseEntity<>(
tenantConfigurationManagement.getConfigurationValue(configKey)), HttpStatus.OK); MgmtSystemMapper.toResponse(keyName, tenantConfigurationManagement.getConfigurationValue(keyName)),
HttpStatus.OK);
} }
/** /**
@@ -115,10 +121,8 @@ public class MgmtSystemResource implements MgmtSystemRestApi {
@PathVariable("keyName") final String keyName, @PathVariable("keyName") final String keyName,
@RequestBody final MgmtSystemTenantConfigurationValueRequest configurationValueRest) { @RequestBody final MgmtSystemTenantConfigurationValueRequest configurationValueRest) {
final TenantConfigurationKey configKey = TenantConfigurationKey.fromKeyName(keyName);
final TenantConfigurationValue<? extends Serializable> updatedValue = tenantConfigurationManagement final TenantConfigurationValue<? extends Serializable> updatedValue = tenantConfigurationManagement
.addOrUpdateConfiguration(configKey, configurationValueRest.getValue()); .addOrUpdateConfiguration(keyName, configurationValueRest.getValue());
return new ResponseEntity<>(MgmtSystemMapper.toResponse(keyName, updatedValue), HttpStatus.OK); return new ResponseEntity<>(MgmtSystemMapper.toResponse(keyName, updatedValue), HttpStatus.OK);
} }

View File

@@ -50,4 +50,12 @@ MYSQL.spring.datasource.password=
# SP Controller configuration # SP Controller configuration
hawkbit.controller.pollingTime=00:01:00 hawkbit.controller.pollingTime=00:01:00
hawkbit.controller.pollingOverdueTime=00:01:00 hawkbit.controller.pollingOverdueTime=00:01:00
hawkbit.server.tenant.configuration.polling-time.keyName=pollingTime
hawkbit.server.tenant.configuration.polling-time.defaultValue=${hawkbit.controller.pollingTime}
hawkbit.server.tenant.configuration.polling-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.server.tenant.configuration.polling-overdue-time.keyName=pollingOverdueTime
hawkbit.server.tenant.configuration.polling-overdue-time.defaultValue=${hawkbit.controller.pollingOverdueTime}
hawkbit.server.tenant.configuration.polling-overdue-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator

View File

@@ -29,7 +29,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;

View File

@@ -643,5 +643,4 @@ public interface TargetManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
Target updateTarget(TargetUpdate update); Target updateTarget(TargetUpdate update);
} }

View File

@@ -13,7 +13,7 @@ import java.io.Serializable;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException; import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@@ -29,7 +29,7 @@ public interface TenantConfigurationManagement {
* Adds or updates a specific configuration for a specific tenant. * Adds or updates a specific configuration for a specific tenant.
* *
* *
* @param configurationKey * @param configurationKeyName
* the key of the configuration * the key of the configuration
* @param value * @param value
* the configuration value which will be written into the * the configuration value which will be written into the
@@ -42,8 +42,7 @@ public interface TenantConfigurationManagement {
* if the property cannot be converted to the given * if the property cannot be converted to the given
*/ */
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration( <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(String configurationKeyName, T value);
TenantConfigurationKey configurationKey, T value);
/** /**
* Build the tenant configuration by the given key * Build the tenant configuration by the given key
@@ -69,14 +68,14 @@ public interface TenantConfigurationManagement {
* the configuration key to be deleted * the configuration key to be deleted
*/ */
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
void deleteConfiguration(TenantConfigurationKey configurationKey); void deleteConfiguration(String configurationKey);
/** /**
* Retrieves a configuration value from the e.g. tenant overwritten * Retrieves a configuration value from the e.g. tenant overwritten
* configuration values or in case the tenant does not a have a specific * configuration values or in case the tenant does not a have a specific
* configuration the global default value hold in the {@link Environment}. * configuration the global default value hold in the {@link Environment}.
* *
* @param configurationKey * @param configurationKeyName
* the key of the configuration * the key of the configuration
* @return the converted configuration value either from the tenant specific * @return the converted configuration value either from the tenant specific
* configuration stored or from the fall back default values or * configuration stored or from the fall back default values or
@@ -90,7 +89,7 @@ public interface TenantConfigurationManagement {
* {@code propertyType} * {@code propertyType}
*/ */
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey); <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String configurationKeyName);
/** /**
* Retrieves a configuration value from the e.g. tenant overwritten * Retrieves a configuration value from the e.g. tenant overwritten
@@ -99,7 +98,7 @@ public interface TenantConfigurationManagement {
* *
* @param <T> * @param <T>
* the type of the configuration value * the type of the configuration value
* @param configurationKey * @param configurationKeyName
* the key of the configuration * the key of the configuration
* @param propertyType * @param propertyType
* the type of the configuration value, e.g. {@code String.class} * the type of the configuration value, e.g. {@code String.class}
@@ -116,7 +115,7 @@ public interface TenantConfigurationManagement {
* {@code propertyType} * {@code propertyType}
*/ */
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey, <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String configurationKeyName,
Class<T> propertyType); Class<T> propertyType);
/** /**
@@ -125,7 +124,7 @@ public interface TenantConfigurationManagement {
* *
* @param <T> * @param <T>
* the type of the configuration value * the type of the configuration value
* @param configurationKey * @param configurationKeyName
* the key of the configuration * the key of the configuration
* @param propertyType * @param propertyType
* the type of the configuration value, e.g. {@code String.class} * the type of the configuration value, e.g. {@code String.class}
@@ -140,5 +139,5 @@ public interface TenantConfigurationManagement {
* {@code propertyType} * {@code propertyType}
*/ */
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T> T getGlobalConfigurationValue(TenantConfigurationKey configurationKey, Class<T> propertyType); <T> T getGlobalConfigurationValue(String configurationKeyName, Class<T> propertyType);
} }

View File

@@ -0,0 +1,42 @@
/**
* 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.repository.event.remote;
import org.eclipse.hawkbit.repository.model.Target;
/**
* Event is send in case a target polls either through DDI or DMF.
*/
public class TargetPollEvent extends RemoteTenantAwareEvent {
private static final long serialVersionUID = 1L;
private String controllerId;
private String targetAdress;
/**
* Default constructor.
*/
public TargetPollEvent() {
// for serialization libs like jackson
}
public TargetPollEvent(final Target target, final String applicationId) {
super(target.getControllerId(), target.getTenant(), applicationId);
this.controllerId = target.getControllerId();
this.targetAdress = target.getTargetInfo().getAddress().toString();
}
public String getControllerId() {
return controllerId;
}
public String getTargetAdress() {
return targetAdress;
}
}

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
@@ -90,10 +91,12 @@ public class EventType {
// download // download
TYPES.put(20, DownloadProgressEvent.class); TYPES.put(20, DownloadProgressEvent.class);
TYPES.put(21, SoftwareModuleCreatedEvent.class); TYPES.put(21, SoftwareModuleCreatedEvent.class);
TYPES.put(22, SoftwareModuleDeletedEvent.class); TYPES.put(22, SoftwareModuleDeletedEvent.class);
TYPES.put(23, SoftwareModuleUpdatedEvent.class); TYPES.put(23, SoftwareModuleUpdatedEvent.class);
TYPES.put(24, TargetPollEvent.class);
} }
private int value; private int value;

View File

@@ -13,11 +13,11 @@ import java.time.Instant;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder; import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper; import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
/** /**
* Calculates non-persistent timestamps , e.g. the point a time a * Calculates non-persistent timestamps , e.g. the point a time a target is
* target is declared as overdue.<br> * declared as overdue.<br>
* Therefore tenant specific configuration may be considered. * Therefore tenant specific configuration may be considered.
* *
*/ */
@@ -43,11 +43,11 @@ public final class TimestampCalculator {
- getDurationForKey(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL).toMillis(); - getDurationForKey(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL).toMillis();
} }
private static Duration getDurationForKey(TenantConfigurationKey key) { private static Duration getDurationForKey(final String key) {
return DurationHelper.formattedStringToDuration(getRawStringForKey(key)); return DurationHelper.formattedStringToDuration(getRawStringForKey(key));
} }
private static String getRawStringForKey(TenantConfigurationKey key) { private static String getRawStringForKey(final String key) {
return getTenantConfigurationManagement().getConfigurationValue(key, String.class).getValue(); return getTenantConfigurationManagement().getConfigurationValue(key, String.class).getValue();
} }

View File

@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent; import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException; import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
@@ -49,11 +50,10 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -127,14 +127,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
public String getPollingTime() { public String getPollingTime() {
final TenantConfigurationKey configurationKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final Class<String> propertyType = String.class;
JpaTenantConfigurationManagement.validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
.buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration).getValue()); .getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
} }
@Override @Override
@@ -203,10 +197,15 @@ public class JpaControllerManagement implements ControllerManagement {
final JpaTarget target = targetRepository.findOne(spec); final JpaTarget target = targetRepository.findOne(spec);
if (target == null) { if (target == null) {
return targetManagement.createTarget(entityFactory.target().create().controllerId(controllerId) final Target result = targetManagement.createTarget(entityFactory.target().create()
.description("Plug and Play target: " + controllerId).name(controllerId) .controllerId(controllerId).description("Plug and Play target: " + controllerId).name(controllerId)
.status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis()) .status(TargetUpdateStatus.REGISTERED).lastTargetQuery(System.currentTimeMillis())
.address(Optional.ofNullable(address).map(URI::toString).orElse(null))); .address(Optional.ofNullable(address).map(URI::toString).orElse(null)));
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new TargetPollEvent(result, applicationContext.getId())));
return result;
} }
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget(); return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
@@ -224,9 +223,6 @@ public class JpaControllerManagement implements ControllerManagement {
if (lastTargetQuery != null) { if (lastTargetQuery != null) {
mtargetInfo.setLastTargetQuery(lastTargetQuery); mtargetInfo.setLastTargetQuery(lastTargetQuery);
} }
if (address != null) {
mtargetInfo.setAddress(address.toString());
}
if (mtargetInfo.getUpdateStatus() == TargetUpdateStatus.UNKNOWN) { if (mtargetInfo.getUpdateStatus() == TargetUpdateStatus.UNKNOWN) {
mtargetInfo.setUpdateStatus(TargetUpdateStatus.REGISTERED); mtargetInfo.setUpdateStatus(TargetUpdateStatus.REGISTERED);
@@ -234,6 +230,12 @@ public class JpaControllerManagement implements ControllerManagement {
.publishEvent(new TargetUpdatedEvent(mtargetInfo.getTarget(), applicationContext.getId()))); .publishEvent(new TargetUpdatedEvent(mtargetInfo.getTarget(), applicationContext.getId())));
} }
if (address != null) {
mtargetInfo.setAddress(address.toString());
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new TargetPollEvent(mtargetInfo.getTarget(), applicationContext.getId())));
}
return targetInfoRepository.save(mtargetInfo); return targetInfoRepository.save(mtargetInfo);
} }

View File

@@ -14,16 +14,15 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration; import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException; import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -34,22 +33,26 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
public class JpaTenantConfigurationManagement implements EnvironmentAware, TenantConfigurationManagement { public class JpaTenantConfigurationManagement implements TenantConfigurationManagement {
@Autowired @Autowired
private TenantConfigurationRepository tenantConfigurationRepository; private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired
private TenantConfigurationProperties tenantConfigurationProperties;
@Autowired @Autowired
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private static final ConfigurableConversionService conversionService = new DefaultConversionService(); private static final ConfigurableConversionService conversionService = new DefaultConversionService();
private Environment environment;
@Override @Override
@Cacheable(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") @Cacheable(value = "tenantConfiguration", key = "#configurationKeyName")
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue( public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String configurationKeyName,
final TenantConfigurationKey configurationKey, final Class<T> propertyType) { final Class<T> propertyType) {
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
validateTenantConfigurationDataType(configurationKey, propertyType); validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
@@ -69,6 +72,7 @@ public class JpaTenantConfigurationManagement implements EnvironmentAware, Tenan
*/ */
static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey, static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) { final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) { if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException( throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.", String.format("Cannot parse the database value of type %s into the type %s.",
@@ -87,46 +91,44 @@ public class JpaTenantConfigurationManagement implements EnvironmentAware, Tenan
.lastModifiedBy(tenantConfiguration.getLastModifiedBy()) .lastModifiedBy(tenantConfiguration.getLastModifiedBy())
.value(conversionService.convert(tenantConfiguration.getValue(), propertyType)).build(); .value(conversionService.convert(tenantConfiguration.getValue(), propertyType)).build();
} else if (configurationKey.getDefaultKeyName() != null) { } else if (configurationKey.getDefaultValue() != null) {
return TenantConfigurationValue.<T> builder().global(true).createdBy(null).createdAt(null) return TenantConfigurationValue.<T> builder().global(true).createdBy(null).createdAt(null)
.lastModifiedAt(null).lastModifiedBy(null) .lastModifiedAt(null).lastModifiedBy(null)
.value(getGlobalConfigurationValue(configurationKey, propertyType)).build(); .value(getGlobalConfigurationValue(configurationKey.getKeyName(), propertyType)).build();
} }
return null; return null;
} }
@Override @Override
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue( public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(
final TenantConfigurationKey configurationKey) { final String configurationKeyName) {
return getConfigurationValue(configurationKey, configurationKey.getDataType()); final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
return getConfigurationValue(configurationKeyName, configurationKey.getDataType());
} }
@Override @Override
public <T> T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey, public <T> T getGlobalConfigurationValue(final String configurationKeyName, final Class<T> propertyType) {
final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) { final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.", if (!key.getDataType().isAssignableFrom(propertyType)) {
configurationKey.getDataType(), propertyType)); throw new TenantConfigurationValidatorException(String.format(
"Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
} }
final T valueInProperties = environment.getProperty(configurationKey.getDefaultKeyName(), propertyType); return conversionService.convert(key.getDefaultValue(), propertyType);
if (valueInProperties == null) {
return conversionService.convert(configurationKey.getDefaultValue(), propertyType);
}
return valueInProperties;
} }
@Override @Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") @CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration( public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(
final TenantConfigurationKey configurationKey, final T value) { final String configurationKeyName, final T value) {
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) { if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) {
throw new TenantConfigurationValidatorException(String.format( throw new TenantConfigurationValidatorException(String.format(
@@ -159,15 +161,10 @@ public class JpaTenantConfigurationManagement implements EnvironmentAware, Tenan
} }
@Override @Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") @CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional(isolation = Isolation.READ_UNCOMMITTED) @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
public void deleteConfiguration(final TenantConfigurationKey configurationKey) { public void deleteConfiguration(final String configurationKeyName) {
tenantConfigurationRepository.deleteByKey(configurationKey.getKeyName()); tenantConfigurationRepository.deleteByKey(configurationKeyName);
}
@Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
} }
} }

View File

@@ -64,6 +64,7 @@ import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
@@ -94,7 +95,8 @@ import com.google.common.collect.Maps;
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
@Configuration @Configuration
@ComponentScan @ComponentScan
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, RolloutProperties.class }) @EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, RolloutProperties.class,
TenantConfigurationProperties.class })
@EnableScheduling @EnableScheduling
@EntityScan("org.eclipse.hawkbit.repository.jpa.model") @EntityScan("org.eclipse.hawkbit.repository.jpa.model")
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {

View File

@@ -48,7 +48,7 @@ import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder; import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper; import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.persistence.annotations.CascadeOnDelete; import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
@@ -78,6 +79,9 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired @Autowired
protected TenantAwareCacheManager cacheManager; protected TenantAwareCacheManager cacheManager;
@Autowired
protected TenantConfigurationProperties tenantConfigurationProperties;
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) { public List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
return actionRepository.findByRolloutAndStatus((JpaRollout) rollout, actionStatus); return actionRepository.findByRolloutAndStatus((JpaRollout) rollout, actionStatus);

View File

@@ -9,17 +9,17 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.fest.assertions.api.Assertions.fail;
import java.io.Serializable; import java.io.Serializable;
import java.time.Duration; import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper; import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.InvalidTenantConfigurationKeyException;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException; import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@@ -35,13 +35,13 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Tests that tenant specific configuration can be persisted and in case the tenant does not have specific configuration the default from environment is used instead.") @Description("Tests that tenant specific configuration can be persisted and in case the tenant does not have specific configuration the default from environment is used instead.")
public void storeTenantSpecificConfigurationAsString() { public void storeTenantSpecificConfigurationAsString() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME; final String envPropertyDefault = environment
final String envPropertyDefault = environment.getProperty(configKey.getDefaultKeyName()); .getProperty("hawkbit.server.ddi.security.authentication.gatewaytoken.key");
assertThat(envPropertyDefault).isNotNull(); assertThat(envPropertyDefault).isNotNull();
// get the configuration from the system management // get the configuration from the system management
final TenantConfigurationValue<String> defaultConfigValue = tenantConfigurationManagement final TenantConfigurationValue<String> defaultConfigValue = tenantConfigurationManagement.getConfigurationValue(
.getConfigurationValue(configKey, String.class); TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class);
assertThat(defaultConfigValue.isGlobal()).isEqualTo(true); assertThat(defaultConfigValue.isGlobal()).isEqualTo(true);
assertThat(defaultConfigValue.getValue()).isEqualTo(envPropertyDefault); assertThat(defaultConfigValue.getValue()).isEqualTo(envPropertyDefault);
@@ -49,11 +49,13 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
// update the tenant specific configuration // update the tenant specific configuration
final String newConfigurationValue = "thisIsAnotherTokenName"; final String newConfigurationValue = "thisIsAnotherTokenName";
assertThat(newConfigurationValue).isNotEqualTo(defaultConfigValue.getValue()); assertThat(newConfigurationValue).isNotEqualTo(defaultConfigValue.getValue());
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, newConfigurationValue); tenantConfigurationManagement.addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue);
// verify that new configuration value is used // verify that new configuration value is used
final TenantConfigurationValue<String> updatedConfigurationValue = tenantConfigurationManagement final TenantConfigurationValue<String> updatedConfigurationValue = tenantConfigurationManagement
.getConfigurationValue(configKey, String.class); .getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY,
String.class);
assertThat(updatedConfigurationValue.isGlobal()).isEqualTo(false); assertThat(updatedConfigurationValue.isGlobal()).isEqualTo(false);
assertThat(updatedConfigurationValue.getValue()).isEqualTo(newConfigurationValue); assertThat(updatedConfigurationValue.getValue()).isEqualTo(newConfigurationValue);
@@ -63,7 +65,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Tests that the tenant specific configuration can be updated") @Description("Tests that the tenant specific configuration can be updated")
public void updateTenantSpecifcConfiguration() { public void updateTenantSpecifcConfiguration() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME; final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY;
final String value1 = "firstValue"; final String value1 = "firstValue";
final String value2 = "secondValue"; final String value2 = "secondValue";
@@ -81,7 +83,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Tests that the configuration value can be converted from String to Integer automatically") @Description("Tests that the configuration value can be converted from String to Integer automatically")
public void storeAndUpdateTenantSpecificConfigurationAsBoolean() { public void storeAndUpdateTenantSpecificConfigurationAsBoolean() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED; final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED;
final Boolean value1 = true; final Boolean value1 = true;
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, value1); tenantConfigurationManagement.addOrUpdateConfiguration(configKey, value1);
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Boolean.class).getValue()) assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Boolean.class).getValue())
@@ -95,7 +97,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Tests that the get configuration throws exception in case the value cannot be automatically converted from String to Boolean") @Description("Tests that the get configuration throws exception in case the value cannot be automatically converted from String to Boolean")
public void wrongTenantConfigurationValueTypeThrowsException() { public void wrongTenantConfigurationValueTypeThrowsException() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED; final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED;
final String value1 = "thisIsNotABoolean"; final String value1 = "thisIsNotABoolean";
// add value as String // add value as String
@@ -110,13 +112,13 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Tests that a deletion of a tenant specific configuration deletes it from the database.") @Description("Tests that a deletion of a tenant specific configuration deletes it from the database.")
public void deleteConfigurationReturnNullConfiguration() { public void deleteConfigurationReturnNullConfiguration() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY; final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY;
// gateway token does not have default value so no configuration value // gateway token does not have default value so no configuration value
// is should be available // should be available
final String defaultConfigValue = tenantConfigurationManagement.getConfigurationValue(configKey, String.class) final String defaultConfigValue = tenantConfigurationManagement.getConfigurationValue(configKey, String.class)
.getValue(); .getValue();
assertThat(defaultConfigValue).isNull(); assertThat(defaultConfigValue).isEmpty();
// update the tenant specific configuration // update the tenant specific configuration
final String newConfigurationValue = "thisIsAnotherValueForPolling"; final String newConfigurationValue = "thisIsAnotherValueForPolling";
@@ -131,14 +133,14 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
// delete the tenant specific configuration // delete the tenant specific configuration
tenantConfigurationManagement.deleteConfiguration(configKey); tenantConfigurationManagement.deleteConfiguration(configKey);
// ensure that now gateway token is set again, because is deleted and // ensure that now gateway token is set again, because is deleted and
// must be null now // must be empty now
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, String.class).getValue()).isNull(); assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, String.class).getValue()).isEmpty();
} }
@Test @Test
@Description("Test that an Exception is thrown, when an integer is stored but a string expected.") @Description("Test that an Exception is thrown, when an integer is stored but a string expected.")
public void storesIntegerWhenStringIsExpected() { public void storesIntegerWhenStringIsExpected() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME; final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY;
final Integer wrongDataype = 123; final Integer wrongDataype = 123;
try { try {
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataype); tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataype);
@@ -151,7 +153,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Test that an Exception is thrown, when an integer is stored but a boolean expected.") @Description("Test that an Exception is thrown, when an integer is stored but a boolean expected.")
public void storesIntegerWhenBooleanIsExpected() { public void storesIntegerWhenBooleanIsExpected() {
final TenantConfigurationKey configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED; final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED;
final Integer wrongDataype = 123; final Integer wrongDataype = 123;
try { try {
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataype); tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataype);
@@ -164,7 +166,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Test that an Exception is thrown, when an integer is stored as PollingTime.") @Description("Test that an Exception is thrown, when an integer is stored as PollingTime.")
public void storesIntegerWhenPollingIntervalIsExpected() { public void storesIntegerWhenPollingIntervalIsExpected() {
final TenantConfigurationKey configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final Integer wrongDataype = 123; final Integer wrongDataype = 123;
try { try {
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataype); tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataype);
@@ -177,7 +179,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Test that an Exception is thrown, when an invalid formatted string is stored as PollingTime.") @Description("Test that an Exception is thrown, when an invalid formatted string is stored as PollingTime.")
public void storesWrongFormattedStringAsPollingInterval() { public void storesWrongFormattedStringAsPollingInterval() {
final TenantConfigurationKey configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String wrongFormatted = "wrongFormatted"; final String wrongFormatted = "wrongFormatted";
try { try {
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongFormatted); tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongFormatted);
@@ -190,7 +192,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Test that an Exception is thrown, when an invalid formatted string is stored as PollingTime.") @Description("Test that an Exception is thrown, when an invalid formatted string is stored as PollingTime.")
public void storesTooSmallDurationAsPollingInterval() { public void storesTooSmallDurationAsPollingInterval() {
final TenantConfigurationKey configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String tooSmallDuration = DurationHelper final String tooSmallDuration = DurationHelper
.durationToFormattedString(DurationHelper.getDurationByTimeValues(0, 0, 1)); .durationToFormattedString(DurationHelper.getDurationByTimeValues(0, 0, 1));
@@ -205,7 +207,7 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Stores a correct formatted PollignTime and reads it again.") @Description("Stores a correct formatted PollignTime and reads it again.")
public void storesCorrectDurationAsPollingInterval() { public void storesCorrectDurationAsPollingInterval() {
final TenantConfigurationKey configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final Duration duration = DurationHelper.getDurationByTimeValues(1, 2, 0); final Duration duration = DurationHelper.getDurationByTimeValues(1, 2, 0);
assertThat(duration).isEqualTo(Duration.ofHours(1).plusMinutes(2)); assertThat(duration).isEqualTo(Duration.ofHours(1).plusMinutes(2));
@@ -233,9 +235,9 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Verifies that every TenenatConfiguraationKeyName exists only once") @Description("Verifies that every TenenatConfiguraationKeyName exists only once")
public void verifyThatAllKeysAreDifferent() { public void verifyThatAllKeysAreDifferent() {
final Map<String, Void> keynames = new HashMap<String, Void>(); final Map<String, Void> keynames = new HashMap<>();
Arrays.stream(TenantConfigurationKey.values()).forEach(key -> { tenantConfigurationProperties.getConfigurationKeys().forEach(key -> {
if (keynames.containsKey(key.getKeyName())) { if (keynames.containsKey(key.getKeyName())) {
throw new IllegalStateException("The key names are not unique"); throw new IllegalStateException("The key names are not unique");
@@ -248,9 +250,20 @@ public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTes
@Test @Test
@Description("Get TenantConfigurationKeyByName") @Description("Get TenantConfigurationKeyByName")
public void getTenantConfigurationKeyByName() { public void getTenantConfigurationKeyByName() {
final TenantConfigurationKey configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
assertThat(TenantConfigurationKey.fromKeyName(configKey.getKeyName())).isEqualTo(configKey); assertThat(tenantConfigurationProperties.fromKeyName(configKey).getKeyName()).isEqualTo(configKey);
} }
@Test
@Description("Tenant configuration which is not declared throws exception")
public void storeTenantConfigurationWhichIsNotDeclaredThrowsException() {
final String configKeyWhichDoesNotExists = "configKeyWhichDoesNotExists";
try {
tenantConfigurationManagement.addOrUpdateConfiguration(configKeyWhichDoesNotExists, "value");
fail("Expected InvalidTenantConfigurationKeyException for tenant configuration key which is not declared");
} catch (final InvalidTenantConfigurationKeyException e) {
// expected exception
}
}
} }

View File

@@ -40,7 +40,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@@ -61,7 +61,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
public class RSQLUtilityTest { public class RSQLUtilityTest {
@Spy @Spy
private VirtualPropertyResolver macroResolver = new VirtualPropertyResolver(); private final VirtualPropertyResolver macroResolver = new VirtualPropertyResolver();
@Mock @Mock
private TenantConfigurationManagement confMgmt; private TenantConfigurationManagement confMgmt;
@@ -78,9 +78,9 @@ public class RSQLUtilityTest {
private Attribute attribute; private Attribute attribute;
private static final TenantConfigurationValue<String> TEST_POLLING_TIME_INTERVAL = TenantConfigurationValue private static final TenantConfigurationValue<String> TEST_POLLING_TIME_INTERVAL = TenantConfigurationValue
.<String>builder().value("00:05:00").build(); .<String> builder().value("00:05:00").build();
private static final TenantConfigurationValue<String> TEST_POLLING_OVERDUE_TIME_INTERVAL = TenantConfigurationValue private static final TenantConfigurationValue<String> TEST_POLLING_OVERDUE_TIME_INTERVAL = TenantConfigurationValue
.<String>builder().value("00:07:37").build(); .<String> builder().value("00:07:37").build();
@Test @Test
public void wrongRsqlSyntaxThrowSyntaxException() { public void wrongRsqlSyntaxThrowSyntaxException() {
@@ -111,8 +111,7 @@ public class RSQLUtilityTest {
String wrongRSQL = TargetFields.ATTRIBUTE + "==abc"; String wrongRSQL = TargetFields.ATTRIBUTE + "==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaQueryMock, criteriaBuilderMock);
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
} }
@@ -120,8 +119,7 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ATTRIBUTE + ".unkwon.wrong==abc"; wrongRSQL = TargetFields.ATTRIBUTE + ".unkwon.wrong==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaQueryMock, criteriaBuilderMock);
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
} }
@@ -141,8 +139,7 @@ public class RSQLUtilityTest {
String wrongRSQL = TargetFields.ASSIGNEDDS + "==abc"; String wrongRSQL = TargetFields.ASSIGNEDDS + "==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaQueryMock, criteriaBuilderMock);
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
} }
@@ -150,8 +147,7 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField==abc"; wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaQueryMock, criteriaBuilderMock);
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
} }
@@ -159,8 +155,7 @@ public class RSQLUtilityTest {
wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField.ToMuch==abc"; wrongRSQL = TargetFields.ASSIGNEDDS + ".unknownField.ToMuch==abc";
try { try {
RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(wrongRSQL, TargetFields.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaQueryMock, criteriaBuilderMock);
criteriaBuilderMock);
fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax"); fail("Missing expected RSQLParameterSyntaxException because of wrong RSQL syntax");
} catch (final RSQLParameterUnsupportedFieldException e) { } catch (final RSQLParameterUnsupportedFieldException e) {
} }
@@ -174,7 +169,7 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("version")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("version")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class))) when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class)); .thenReturn(mock(Predicate.class));
// test // test
@@ -192,7 +187,7 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class))) when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class)); .thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))) when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock)); .thenReturn(pathOfString(baseSoftwareModuleRootMock));
@@ -213,7 +208,7 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class))) when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class)); .thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))) when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock)); .thenReturn(pathOfString(baseSoftwareModuleRootMock));
@@ -234,7 +229,7 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>greaterThanOrEqualTo(any(Expression.class), any(String.class))) when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class)); .thenReturn(mock(Predicate.class));
// test // test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null).toPredicate(baseSoftwareModuleRootMock,
@@ -255,8 +250,7 @@ public class RSQLUtilityTest {
// test // test
RSQLUtility.parse(correctRsql, TestFieldEnum.class, null).toPredicate(baseSoftwareModuleRootMock, RSQLUtility.parse(correctRsql, TestFieldEnum.class, null).toPredicate(baseSoftwareModuleRootMock,
criteriaQueryMock, criteriaQueryMock, criteriaBuilderMock);
criteriaBuilderMock);
// verfication // verfication
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class)); verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
@@ -291,16 +285,17 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("testfield")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("testfield")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) String.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) String.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>lessThanOrEqualTo(any(Expression.class), eq(overduePropPlaceholder))) when(criteriaBuilderMock.<String> lessThanOrEqualTo(any(Expression.class), eq(overduePropPlaceholder)))
.thenReturn(mock(Predicate.class)); .thenReturn(mock(Predicate.class));
// test // test
RSQLUtility.parse(correctRsql, TestFieldEnum.class, setupMacroLookup()) RSQLUtility.parse(correctRsql, TestFieldEnum.class, setupMacroLookup()).toPredicate(baseSoftwareModuleRootMock,
.toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); criteriaQueryMock, criteriaBuilderMock);
// verification // verification
verify(macroResolver).lookup(overdueProp); verify(macroResolver).lookup(overdueProp);
// the macro is already replaced when passed to #lessThanOrEqualTo -> the method is never invoked with the // the macro is already replaced when passed to #lessThanOrEqualTo ->
// the method is never invoked with the
// placeholder: // placeholder:
verify(criteriaBuilderMock, never()).lessThanOrEqualTo(eq(pathOfString(baseSoftwareModuleRootMock)), verify(criteriaBuilderMock, never()).lessThanOrEqualTo(eq(pathOfString(baseSoftwareModuleRootMock)),
eq(overduePropPlaceholder)); eq(overduePropPlaceholder));
@@ -316,16 +311,17 @@ public class RSQLUtilityTest {
when(baseSoftwareModuleRootMock.get("testfield")).thenReturn(baseSoftwareModuleRootMock); when(baseSoftwareModuleRootMock.get("testfield")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) String.class); when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) String.class);
when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class)); when(criteriaBuilderMock.equal(any(Root.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String>lessThanOrEqualTo(any(Expression.class), eq(overduePropPlaceholder))) when(criteriaBuilderMock.<String> lessThanOrEqualTo(any(Expression.class), eq(overduePropPlaceholder)))
.thenReturn(mock(Predicate.class)); .thenReturn(mock(Predicate.class));
// test // test
Predicate result = RSQLUtility.parse(correctRsql, TestFieldEnum.class, setupMacroLookup()) final Predicate result = RSQLUtility.parse(correctRsql, TestFieldEnum.class, setupMacroLookup())
.toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock); .toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
// verification // verification
verify(macroResolver).lookup(overdueProp); verify(macroResolver).lookup(overdueProp);
// the macro is unknown and hence never replaced -> #lessThanOrEqualTo is invoked with the placeholder: // the macro is unknown and hence never replaced -> #lessThanOrEqualTo
// is invoked with the placeholder:
verify(criteriaBuilderMock).lessThanOrEqualTo(eq(pathOfString(baseSoftwareModuleRootMock)), verify(criteriaBuilderMock).lessThanOrEqualTo(eq(pathOfString(baseSoftwareModuleRootMock)),
eq(overduePropPlaceholder)); eq(overduePropPlaceholder));
} }
@@ -353,7 +349,9 @@ public class RSQLUtilityTest {
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.eclipse.hawkbit.server.rest.resource.model.FieldNameProvider# getFieldName() * @see
* org.eclipse.hawkbit.server.rest.resource.model.FieldNameProvider#
* getFieldName()
*/ */
@Override @Override
public String getFieldName() { public String getFieldName() {

View File

@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.TimestampCalculator; import org.eclipse.hawkbit.repository.TimestampCalculator;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -41,7 +41,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
public class VirtualPropertyResolverTest { public class VirtualPropertyResolverTest {
@Spy @Spy
private VirtualPropertyResolver resolverUnderTest = new VirtualPropertyResolver(); private final VirtualPropertyResolver resolverUnderTest = new VirtualPropertyResolver();
@Mock @Mock
private TenantConfigurationManagement confMgmt; private TenantConfigurationManagement confMgmt;
@@ -59,66 +59,65 @@ public class VirtualPropertyResolverTest {
public void before() { public void before() {
nowTestTime = Instant.now().toEpochMilli(); nowTestTime = Instant.now().toEpochMilli();
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class)) when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class))
.thenReturn(TEST_POLLING_TIME_INTERVAL); .thenReturn(TEST_POLLING_TIME_INTERVAL);
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)) when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class))
.thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL); .thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL);
mockStatic(TimestampCalculator.class); mockStatic(TimestampCalculator.class);
when(TimestampCalculator.getTenantConfigurationManagement()).thenReturn(confMgmt); when(TimestampCalculator.getTenantConfigurationManagement()).thenReturn(confMgmt);
substitutor = new StrSubstitutor(resolverUnderTest, substitutor = new StrSubstitutor(resolverUnderTest, StrSubstitutor.DEFAULT_PREFIX,
StrSubstitutor.DEFAULT_PREFIX,
StrSubstitutor.DEFAULT_SUFFIX, StrSubstitutor.DEFAULT_ESCAPE); StrSubstitutor.DEFAULT_SUFFIX, StrSubstitutor.DEFAULT_ESCAPE);
} }
@Test @Test
@Description("Tests resolution of NOW_TS by using a StrSubstitutor configured with the VirtualPropertyResolver.") @Description("Tests resolution of NOW_TS by using a StrSubstitutor configured with the VirtualPropertyResolver.")
public void resolveNowTimestampPlaceholder() { public void resolveNowTimestampPlaceholder() {
String placeholder = "${NOW_TS}"; final String placeholder = "${NOW_TS}";
String testString = "lhs=lt=" + placeholder; final String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString); final String resolvedPlaceholders = substitutor.replace(testString);
assertThat("NOW_TS has to be resolved!", resolvedPlaceholders, not(containsString(placeholder))); assertThat("NOW_TS has to be resolved!", resolvedPlaceholders, not(containsString(placeholder)));
} }
@Test @Test
@Description("Tests resolution of OVERDUE_TS by using a StrSubstitutor configured with the VirtualPropertyResolver.") @Description("Tests resolution of OVERDUE_TS by using a StrSubstitutor configured with the VirtualPropertyResolver.")
public void resolveOverdueTimestampPlaceholder() { public void resolveOverdueTimestampPlaceholder() {
String placeholder = "${OVERDUE_TS}"; final String placeholder = "${OVERDUE_TS}";
String testString = "lhs=lt=" + placeholder; final String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString); final String resolvedPlaceholders = substitutor.replace(testString);
assertThat("OVERDUE_TS has to be resolved!", resolvedPlaceholders, not(containsString(placeholder))); assertThat("OVERDUE_TS has to be resolved!", resolvedPlaceholders, not(containsString(placeholder)));
} }
@Test @Test
@Description("Tests case insensititity of VirtualPropertyResolver.") @Description("Tests case insensititity of VirtualPropertyResolver.")
public void resolveOverdueTimestampPlaceholderLowerCase() { public void resolveOverdueTimestampPlaceholderLowerCase() {
String placeholder = "${overdue_ts}"; final String placeholder = "${overdue_ts}";
String testString = "lhs=lt=" + placeholder; final String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString); final String resolvedPlaceholders = substitutor.replace(testString);
assertThat("overdue_ts has to be resolved!", resolvedPlaceholders, not(containsString(placeholder))); assertThat("overdue_ts has to be resolved!", resolvedPlaceholders, not(containsString(placeholder)));
} }
@Test @Test
@Description("Tests VirtualPropertyResolver with a placeholder unknown to VirtualPropertyResolver.") @Description("Tests VirtualPropertyResolver with a placeholder unknown to VirtualPropertyResolver.")
public void handleUnknownPlaceholder() { public void handleUnknownPlaceholder() {
String placeholder = "${unknown}"; final String placeholder = "${unknown}";
String testString = "lhs=lt=" + placeholder; final String testString = "lhs=lt=" + placeholder;
String resolvedPlaceholders = substitutor.replace(testString); final String resolvedPlaceholders = substitutor.replace(testString);
assertThat("unknown should not be resolved!", resolvedPlaceholders, containsString(placeholder)); assertThat("unknown should not be resolved!", resolvedPlaceholders, containsString(placeholder));
} }
@Test @Test
@Description("Tests escape mechanism for placeholders (syntax is $${SOME_PLACEHOLDER}).") @Description("Tests escape mechanism for placeholders (syntax is $${SOME_PLACEHOLDER}).")
public void handleEscapedPlaceholder() { public void handleEscapedPlaceholder() {
String placeholder = "${OVERDUE_TS}"; final String placeholder = "${OVERDUE_TS}";
String escaptedPlaceholder = StrSubstitutor.DEFAULT_ESCAPE + placeholder; final String escaptedPlaceholder = StrSubstitutor.DEFAULT_ESCAPE + placeholder;
String testString = "lhs=lt=" + escaptedPlaceholder; final String testString = "lhs=lt=" + escaptedPlaceholder;
String resolvedPlaceholders = substitutor.replace(testString); final String resolvedPlaceholders = substitutor.replace(testString);
assertThat("Escaped OVERDUE_TS should not be resolved!", resolvedPlaceholders, containsString(placeholder)); assertThat("Escaped OVERDUE_TS should not be resolved!", resolvedPlaceholders, containsString(placeholder));
} }
} }

View File

@@ -13,9 +13,6 @@ logging.level.org.eclipse.persistence=ERROR
spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value} spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value}
spring.data.mongodb.port=28017 spring.data.mongodb.port=28017
hawkbit.server.ddi.security.authentication.header.enabled=true
hawkbit.server.ddi.security.authentication.gatewaytoken.name=TestToken
multipart.max-file-size=5MB multipart.max-file-size=5MB
hawkbit.server.security.dos.maxStatusEntriesPerAction=100 hawkbit.server.security.dos.maxStatusEntriesPerAction=100
@@ -38,4 +35,48 @@ flyway.sqlMigrationSuffix=${spring.jpa.database}.sql
# DDI configuration # DDI configuration
hawkbit.controller.pollingTime=00:01:00 hawkbit.controller.pollingTime=00:01:00
hawkbit.controller.pollingOverdueTime=00:01:00 hawkbit.controller.pollingOverdueTime=00:01:00
# DDI and download security
hawkbit.server.ddi.security.authentication.header.authority=
hawkbit.server.ddi.security.authentication.targettoken.enabled=false
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
hawkbit.server.download.anonymous.enabled=false
hawkbit.server.ddi.security.authentication.header.enabled=true
hawkbit.server.ddi.security.authentication.gatewaytoken.name=TestToken
hawkbit.server.ddi.security.authentication.gatewaytoken.key=
# Default tenant configuration properties
hawkbit.server.tenant.configuration.authentication-header-enabled.keyName=authentication.header.enabled
hawkbit.server.tenant.configuration.authentication-header-enabled.defaultValue=${hawkbit.server.ddi.security.authentication.header.enabled}
hawkbit.server.tenant.configuration.authentication-header-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-header-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-header-authority.keyName=authentication.header.authority
hawkbit.server.tenant.configuration.authentication-header-authority.defaultValue=${hawkbit.server.ddi.security.authentication.header.authority}
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.keyName=authentication.targettoken.enabled
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.defaultValue=${hawkbit.server.ddi.security.authentication.targettoken.enabled}
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.keyName=authentication.gatewaytoken.enabled
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.defaultValue=${hawkbit.server.ddi.security.authentication.gatewaytoken.enabled}
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.keyName=authentication.gatewaytoken.key
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.defaultValue=${hawkbit.server.ddi.security.authentication.gatewaytoken.key}
hawkbit.server.tenant.configuration.polling-time.keyName=pollingTime
hawkbit.server.tenant.configuration.polling-time.defaultValue=${hawkbit.controller.pollingTime}
hawkbit.server.tenant.configuration.polling-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.server.tenant.configuration.polling-overdue-time.keyName=pollingOverdueTime
hawkbit.server.tenant.configuration.polling-overdue-time.defaultValue=${hawkbit.controller.pollingOverdueTime}
hawkbit.server.tenant.configuration.polling-overdue-time.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationPollingDurationValidator
hawkbit.server.tenant.configuration.anonymous-download-enabled.keyName=anonymous.download.enabled
hawkbit.server.tenant.configuration.anonymous-download-enabled.defaultValue=${hawkbit.server.download.anonymous.enabled}
hawkbit.server.tenant.configuration.anonymous-download-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.anonymous-download-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator

View File

@@ -37,6 +37,7 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware; import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -67,7 +68,7 @@ import org.springframework.util.AntPathMatcher;
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = false, securedEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = false, securedEnabled = true)
@EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class, @EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class,
ArtifactUrlHandlerProperties.class, ArtifactFilesystemProperties.class, HawkbitSecurityProperties.class, ArtifactUrlHandlerProperties.class, ArtifactFilesystemProperties.class, HawkbitSecurityProperties.class,
ControllerPollProperties.class }) ControllerPollProperties.class, TenantConfigurationProperties.class })
@Profile("test") @Profile("test")
@EnableAutoConfiguration @EnableAutoConfiguration
public class TestConfiguration implements AsyncConfigurer { public class TestConfiguration implements AsyncConfigurer {

View File

@@ -102,7 +102,7 @@ import com.google.common.collect.Lists;
// strange test failures. // strange test failures.
@DirtiesContext(classMode = ClassMode.AFTER_CLASS) @DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public abstract class AbstractIntegrationTest implements EnvironmentAware { public abstract class AbstractIntegrationTest implements EnvironmentAware {
private final static Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class);
protected static final Pageable pageReq = new PageRequest(0, 400); protected static final Pageable pageReq = new PageRequest(0, 400);

View File

@@ -228,11 +228,11 @@ public final class SpPermission {
/* /*
* Spring security eval expressions. * Spring security eval expressions.
*/ */
private static final String BRACKET_OPEN = "("; public static final String BRACKET_OPEN = "(";
private static final String BRACKET_CLOSE = ")"; public static final String BRACKET_CLOSE = ")";
private static final String HAS_AUTH_PREFIX = "hasAuthority" + BRACKET_OPEN + "'"; public static final String HAS_AUTH_PREFIX = "hasAuthority" + BRACKET_OPEN + "'";
private static final String HAS_AUTH_SUFFIX = "'" + BRACKET_CLOSE; public static final String HAS_AUTH_SUFFIX = "'" + BRACKET_CLOSE;
private static final String HAS_AUTH_AND = " and "; public static final String HAS_AUTH_AND = " and ";
/** /**
* The role which contains in the spring security context in case an * The role which contains in the spring security context in case an

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -39,7 +38,7 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
this.configurationKeyTenantRunner = new SecurityConfigurationKeyTenantRunner(); this.configurationKeyTenantRunner = new SecurityConfigurationKeyTenantRunner();
} }
protected abstract TenantConfigurationKey getTenantConfigurationKey(); protected abstract String getTenantConfigurationKey();
@Override @Override
public boolean isEnable(final TenantSecurityToken secruityToken) { public boolean isEnable(final TenantSecurityToken secruityToken) {

View File

@@ -15,7 +15,7 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -106,7 +106,7 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro
} }
@Override @Override
protected TenantConfigurationKey getTenantConfigurationKey() { protected String getTenantConfigurationKey() {
return TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED; return TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED;
} }
} }

View File

@@ -14,7 +14,7 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -58,7 +58,7 @@ public class ControllerPreAuthenticatedAnonymousDownload extends AbstractControl
} }
@Override @Override
protected TenantConfigurationKey getTenantConfigurationKey() { protected String getTenantConfigurationKey() {
return TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED; return TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED;
} }

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -78,7 +78,7 @@ public class ControllerPreAuthenticatedGatewaySecurityTokenFilter extends Abstra
} }
@Override @Override
protected TenantConfigurationKey getTenantConfigurationKey() { protected String getTenantConfigurationKey() {
return TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED; return TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED;
} }

View File

@@ -15,7 +15,7 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -108,10 +108,11 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
controllerId = secruityToken.getHeader(caCommonNameHeader); controllerId = secruityToken.getHeader(caCommonNameHeader);
} }
List<String> knownHashes = splitMultiHashBySemicolon(authorityNameConfigurationValue); final List<String> knownHashes = splitMultiHashBySemicolon(authorityNameConfigurationValue);
final String cntlId = controllerId; final String cntlId = controllerId;
return knownHashes.stream().map(hashItem -> new HeaderAuthentication(cntlId, hashItem)).collect(Collectors.toSet()); return knownHashes.stream().map(hashItem -> new HeaderAuthentication(cntlId, hashItem))
.collect(Collectors.toSet());
} }
/** /**
@@ -122,7 +123,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
*/ */
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 // there may be several knownIssuerHashes configured for the tenant
List<String> knownHashes = splitMultiHashBySemicolon(knownIssuerHashes); final List<String> knownHashes = splitMultiHashBySemicolon(knownIssuerHashes);
// iterate over the headers until we get a null header. // iterate over the headers until we get a null header.
int iHeader = 1; int iHeader = 1;
@@ -143,7 +144,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
} }
@Override @Override
protected TenantConfigurationKey getTenantConfigurationKey() { protected String getTenantConfigurationKey() {
return TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED; return TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED;
} }
@@ -155,7 +156,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
} }
} }
private static List<String> splitMultiHashBySemicolon(String knownIssuerHashes) { private static List<String> splitMultiHashBySemicolon(final String knownIssuerHashes) {
return Arrays.asList(knownIssuerHashes.split(";")); return Arrays.asList(knownIssuerHashes.split(";"));
} }
} }

View File

@@ -19,7 +19,7 @@ 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;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;

View File

@@ -13,20 +13,18 @@ import java.io.Serializable;
import org.eclipse.hawkbit.im.authentication.PermissionService; import org.eclipse.hawkbit.im.authentication.PermissionService;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/** /**
* Bean which contains all SP permissions. * Bean which contains all SP permissions.
* *
*/ */
@Service
public class SpPermissionChecker implements Serializable { public class SpPermissionChecker implements Serializable {
private static final long serialVersionUID = 2757865286212875704L; private static final long serialVersionUID = 2757865286212875704L;
private transient PermissionService permissionService; protected transient PermissionService permissionService;
@Autowired @Autowired
SpPermissionChecker(final PermissionService permissionService) { protected SpPermissionChecker(final PermissionService permissionService) {
this.permissionService = permissionService; this.permissionService = permissionService;
} }

View File

@@ -170,7 +170,7 @@ public final class UserDetailsFormatter {
return Optional.ofNullable(userPrincipal.getEmail()); return Optional.ofNullable(userPrincipal.getEmail());
} }
private static UserDetails getCurrentUser() { public static UserDetails getCurrentUser() {
final SecurityContext context = (SecurityContext) VaadinService.getCurrentRequest().getWrappedSession() final SecurityContext context = (SecurityContext) VaadinService.getCurrentRequest().getWrappedSession()
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); .getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
return (UserDetails) context.getAuthentication().getPrincipal(); return (UserDetails) context.getAuthentication().getPrincipal();

View File

@@ -27,6 +27,8 @@ public abstract class AbstractTextFieldBuilder<E extends AbstractTextField> {
private String id; private String id;
private boolean immediate; private boolean immediate;
private boolean required; private boolean required;
private boolean readOnly;
private boolean enabled = true;
private int maxLengthAllowed; private int maxLengthAllowed;
/** /**
@@ -69,6 +71,26 @@ public abstract class AbstractTextFieldBuilder<E extends AbstractTextField> {
return this; return this;
} }
/**
* @param readOnly
* the readOnly to set
* @return the builder
*/
public AbstractTextFieldBuilder<E> readOnly(final boolean readOnly) {
this.readOnly = readOnly;
return this;
}
/**
* @param enabled
* the enabled to set
* @return the builder
*/
public AbstractTextFieldBuilder<E> enabled(final boolean enabled) {
this.enabled = enabled;
return this;
}
/** /**
* @param prompt * @param prompt
* the prompt to set * the prompt to set
@@ -119,6 +141,8 @@ public abstract class AbstractTextFieldBuilder<E extends AbstractTextField> {
textComponent.setRequired(required); textComponent.setRequired(required);
textComponent.setImmediate(immediate); textComponent.setImmediate(immediate);
textComponent.setReadOnly(readOnly);
textComponent.setEnabled(enabled);
if (StringUtils.isNotEmpty(caption)) { if (StringUtils.isNotEmpty(caption)) {
textComponent.setCaption(caption); textComponent.setCaption(caption);

View File

@@ -20,7 +20,12 @@ public class SPUICheckBox extends CheckBox {
SPUICheckBox(final String caption, final String style, final String styleName, final boolean required, SPUICheckBox(final String caption, final String style, final String styleName, final boolean required,
final String data) { final String data) {
decorate(caption, style, styleName, required, data); decorate(null, caption, style, styleName, required, data);
}
SPUICheckBox(final String id, final String caption, final String style, final String styleName,
final boolean required, final String data) {
decorate(id, caption, style, styleName, required, data);
} }
/** /**
@@ -37,24 +42,27 @@ public class SPUICheckBox extends CheckBox {
* @param promt * @param promt
* inputpromt of the CheckBox * inputpromt of the CheckBox
*/ */
private void decorate(final String caption, final String style, final String styleName, final boolean required, private void decorate(final String id, final String caption, final String style, final String styleName,
final String data) { final boolean required, final String data) {
// Default settings // Default settings
setRequired(required); setRequired(required);
addStyleName(ValoTheme.CHECKBOX_SMALL); addStyleName(ValoTheme.CHECKBOX_SMALL);
if (null != caption) { if (id != null) {
setId(id);
}
if (caption != null) {
setCaption(caption); setCaption(caption);
} }
// Add style // Add style
if (null != style) { if (style != null) {
setStyleName(style); setStyleName(style);
} }
// Add style Name // Add style Name
if (null != styleName) { if (styleName != null) {
addStyleName(styleName); addStyleName(styleName);
} }
// Set Data // Set Data
if (null != data) { if (data != null) {
setData(data); setData(data);
} }
} }

View File

@@ -96,6 +96,28 @@ public final class SPUIComponentProvider {
return new SPUICheckBox(caption, style, styleName, required, data); return new SPUICheckBox(caption, style, styleName, required, data);
} }
/**
* Get Label UI component.
*
* @param id
* id of the checkbox
* @param caption
* as caption
* @param style
* combo style to add
* @param styleName
* combo style to set
* @param required
* signifies if combo is mandatory
* @param data
* combo box data
* @return ComboBox
*/
public static CheckBox getCheckBox(final String id, final String caption, final String style,
final String styleName, final boolean required, final String data) {
return new SPUICheckBox(id, caption, style, styleName, required, data);
}
/** /**
* Get Button - Factory Approach for decoration. * Get Button - Factory Approach for decoration.
* *

View File

@@ -125,7 +125,7 @@ public final class DashboardMenu extends CustomComponent {
return dashboardMenuLayout; return dashboardMenuLayout;
} }
private VerticalLayout getMenuLayout() { private static VerticalLayout getMenuLayout() {
final VerticalLayout menuContent = new VerticalLayout(); final VerticalLayout menuContent = new VerticalLayout();
menuContent.addStyleName(ValoTheme.MENU_PART); menuContent.addStyleName(ValoTheme.MENU_PART);
menuContent.addStyleName("sidebar"); menuContent.addStyleName("sidebar");
@@ -197,7 +197,7 @@ public final class DashboardMenu extends CustomComponent {
} }
private Component buildUserMenu() { private static Component buildUserMenu() {
final MenuBar settings = new MenuBar(); final MenuBar settings = new MenuBar();
settings.addStyleName("user-menu"); settings.addStyleName("user-menu");
settings.setHtmlContentAllowed(true); settings.setHtmlContentAllowed(true);

View File

@@ -25,7 +25,7 @@ public abstract class BaseConfigurationView extends CustomComponent implements C
private final List<ConfigurationItemChangeListener> configurationChangeListeners = new ArrayList<>(); private final List<ConfigurationItemChangeListener> configurationChangeListeners = new ArrayList<>();
protected void notifyConfigurationChanged() { protected void notifyConfigurationChanged() {
configurationChangeListeners.forEach(listener -> listener.configurationHasChanged()); configurationChangeListeners.forEach(ConfigurationItemChangeListener::configurationHasChanged);
} }
@Override @Override

View File

@@ -25,4 +25,12 @@ public interface ConfigurationGroup extends Component, ConfigurationItem {
* called to rollback any configuration changes. * called to rollback any configuration changes.
*/ */
void undo(); void undo();
/**
* @return <code>true</code> if view can be shown (e.g. sufficient
* permissions).
*/
default boolean show() {
return true;
}
} }

View File

@@ -14,7 +14,7 @@ import org.eclipse.hawkbit.ControllerPollProperties;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper; import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.tenantconfiguration.polling.DurationConfigField; import org.eclipse.hawkbit.ui.tenantconfiguration.polling.DurationConfigField;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
@@ -108,7 +108,7 @@ public class PollingConfigurationView extends BaseConfigurationView
} }
} }
private void saveDurationConfigurationValue(final TenantConfigurationKey key, final Duration duration) { private void saveDurationConfigurationValue(final String key, final Duration duration) {
if (duration == null) { if (duration == null) {
tenantConfigurationManagement.deleteConfiguration(key); tenantConfigurationManagement.deleteConfiguration(key);
} else { } else {

View File

@@ -8,7 +8,9 @@
*/ */
package org.eclipse.hawkbit.ui.tenantconfiguration; package org.eclipse.hawkbit.ui.tenantconfiguration;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@@ -68,6 +70,9 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
private final List<ConfigurationGroup> configurationViews = Lists.newArrayListWithExpectedSize(3); private final List<ConfigurationGroup> configurationViews = Lists.newArrayListWithExpectedSize(3);
@Autowired(required = false)
private Collection<ConfigurationGroup> customConfigurationViews;
@Autowired @Autowired
TenantConfigurationDashboardView(final I18N i18n, final UiProperties uiProperties, TenantConfigurationDashboardView(final I18N i18n, final UiProperties uiProperties,
final UINotification uINotification, final SystemManagement systemManagement, final UINotification uINotification, final SystemManagement systemManagement,
@@ -94,6 +99,10 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
configurationViews.add(defaultDistributionSetTypeLayout); configurationViews.add(defaultDistributionSetTypeLayout);
configurationViews.add(authenticationConfigurationView); configurationViews.add(authenticationConfigurationView);
configurationViews.add(pollingConfigurationView); configurationViews.add(pollingConfigurationView);
if (customConfigurationViews != null) {
configurationViews.addAll(
customConfigurationViews.stream().filter(ConfigurationGroup::show).collect(Collectors.toList()));
}
final Panel rootPanel = new Panel(); final Panel rootPanel = new Panel();
rootPanel.setStyleName("tenantconfig-root"); rootPanel.setStyleName("tenantconfig-root");

View File

@@ -12,7 +12,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
@@ -28,7 +28,7 @@ abstract class AbstractAuthenticationTenantConfigurationItem extends VerticalLay
private final I18N i18n; private final I18N i18n;
private final TenantConfigurationKey configurationKey; private final String configurationKey;
private final transient TenantConfigurationManagement tenantConfigurationManagement; private final transient TenantConfigurationManagement tenantConfigurationManagement;
private final List<ConfigurationItemChangeListener> configurationChangeListeners = new ArrayList<>(); private final List<ConfigurationItemChangeListener> configurationChangeListeners = new ArrayList<>();
@@ -40,7 +40,7 @@ abstract class AbstractAuthenticationTenantConfigurationItem extends VerticalLay
* the tenant configuration management to retrieve the * the tenant configuration management to retrieve the
* configuration value * configuration value
*/ */
public AbstractAuthenticationTenantConfigurationItem(final TenantConfigurationKey configurationKey, public AbstractAuthenticationTenantConfigurationItem(final String configurationKey,
final TenantConfigurationManagement tenantConfigurationManagement, final I18N i18n) { final TenantConfigurationManagement tenantConfigurationManagement, final I18N i18n) {
this.configurationKey = configurationKey; this.configurationKey = configurationKey;
this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantConfigurationManagement = tenantConfigurationManagement;
@@ -57,7 +57,10 @@ abstract class AbstractAuthenticationTenantConfigurationItem extends VerticalLay
@Override @Override
public boolean isConfigEnabled() { public boolean isConfigEnabled() {
return tenantConfigurationManagement.getConfigurationValue(configurationKey, Boolean.class).getValue(); final TenantConfigurationValue<Boolean> enabled = tenantConfigurationManagement
.getConfigurationValue(configurationKey, Boolean.class);
return enabled.getValue() && !enabled.isGlobal();
} }
/** /**
@@ -70,12 +73,12 @@ abstract class AbstractAuthenticationTenantConfigurationItem extends VerticalLay
/** /**
* @return the configurationKey * @return the configurationKey
*/ */
protected TenantConfigurationKey getConfigurationKey() { protected String getConfigurationKey() {
return configurationKey; return configurationKey;
} }
protected void notifyConfigurationChanged() { protected void notifyConfigurationChanged() {
configurationChangeListeners.forEach(listener -> listener.configurationHasChanged()); configurationChangeListeners.forEach(ConfigurationItemChangeListener::configurationHasChanged);
} }
@Override @Override

View File

@@ -9,7 +9,7 @@
package org.eclipse.hawkbit.ui.tenantconfiguration.authentication; package org.eclipse.hawkbit.ui.tenantconfiguration.authentication;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
/** /**

View File

@@ -9,7 +9,7 @@
package org.eclipse.hawkbit.ui.tenantconfiguration.authentication; package org.eclipse.hawkbit.ui.tenantconfiguration.authentication;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;

View File

@@ -8,11 +8,11 @@
*/ */
package org.eclipse.hawkbit.ui.tenantconfiguration.authentication; package org.eclipse.hawkbit.ui.tenantconfiguration.authentication;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
@@ -21,7 +21,6 @@ import com.vaadin.server.FontAwesome;
import com.vaadin.ui.Button; import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label; import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme; import com.vaadin.ui.themes.ValoTheme;
@@ -35,15 +34,11 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
private final transient SecurityTokenGenerator securityTokenGenerator; private final transient SecurityTokenGenerator securityTokenGenerator;
private final TextField gatewayTokenNameTextField;
private final Label gatewayTokenkeyLabel; private final Label gatewayTokenkeyLabel;
private boolean configurationEnabled; private boolean configurationEnabled;
private boolean configurationEnabledChange; private boolean configurationEnabledChange;
private boolean keyNameChanged;
private boolean keyChanged; private boolean keyChanged;
private final VerticalLayout detailLayout; private final VerticalLayout detailLayout;
@@ -61,10 +56,6 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
detailLayout = new VerticalLayout(); detailLayout = new VerticalLayout();
detailLayout.setImmediate(true); detailLayout.setImmediate(true);
gatewayTokenNameTextField = new TextFieldBuilder().immediate(true).buildTextComponent();
// hide text field until we support multiple gateway tokens for a tenan
gatewayTokenNameTextField.setVisible(false);
gatewayTokenNameTextField.addTextChangeListener(event -> doKeyNameChanged());
final Button gatewaytokenBtn = SPUIComponentProvider.getButton("TODO-ID", "Regenerate Key", "", final Button gatewaytokenBtn = SPUIComponentProvider.getButton("TODO-ID", "Regenerate Key", "",
ValoTheme.BUTTON_TINY + " " + "redicon", true, null, SPUIButtonStyleSmall.class); ValoTheme.BUTTON_TINY + " " + "redicon", true, null, SPUIButtonStyleSmall.class);
@@ -80,24 +71,17 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
keyGenerationLayout.setSpacing(true); keyGenerationLayout.setSpacing(true);
keyGenerationLayout.setImmediate(true); keyGenerationLayout.setImmediate(true);
keyGenerationLayout.addComponent(gatewayTokenNameTextField);
keyGenerationLayout.addComponent(gatewayTokenkeyLabel); keyGenerationLayout.addComponent(gatewayTokenkeyLabel);
keyGenerationLayout.addComponent(gatewaytokenBtn); keyGenerationLayout.addComponent(gatewaytokenBtn);
detailLayout.addComponent(keyGenerationLayout); detailLayout.addComponent(keyGenerationLayout);
if (isConfigEnabled()) { if (isConfigEnabled()) {
gatewayTokenNameTextField.setValue(getSecurityTokenName());
gatewayTokenkeyLabel.setValue(getSecurityTokenKey()); gatewayTokenkeyLabel.setValue(getSecurityTokenKey());
setDetailVisible(true); setDetailVisible(true);
} }
} }
private void doKeyNameChanged() {
keyNameChanged = true;
notifyConfigurationChanged();
}
private void setDetailVisible(final boolean visible) { private void setDetailVisible(final boolean visible) {
if (visible) { if (visible) {
addComponent(detailLayout); addComponent(detailLayout);
@@ -122,22 +106,13 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
configurationEnabled = true; configurationEnabled = true;
setDetailVisible(true); setDetailVisible(true);
String gatewayTokenKey = getSecurityTokenKey(); String gatewayTokenKey = getSecurityTokenKey();
String gatewayTokenName = getSecurityTokenName(); if (StringUtils.isEmpty(gatewayTokenKey)) {
if (gatewayTokenKey == null) {
gatewayTokenName = "GeneratedToken";
keyNameChanged = true;
gatewayTokenKey = securityTokenGenerator.generateToken(); gatewayTokenKey = securityTokenGenerator.generateToken();
keyChanged = true; keyChanged = true;
} }
gatewayTokenNameTextField.setValue(gatewayTokenName);
gatewayTokenkeyLabel.setValue(gatewayTokenKey); gatewayTokenkeyLabel.setValue(gatewayTokenKey);
} }
private String getSecurityTokenName() {
return getTenantConfigurationManagement().getConfigurationValue(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME, String.class).getValue();
}
private String getSecurityTokenKey() { private String getSecurityTokenKey() {
return getTenantConfigurationManagement().getConfigurationValue( return getTenantConfigurationManagement().getConfigurationValue(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class).getValue(); TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class).getValue();
@@ -159,11 +134,6 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED, configurationEnabled); TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED, configurationEnabled);
} }
if (keyNameChanged) {
getTenantConfigurationManagement().addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_NAME,
gatewayTokenNameTextField.getValue());
}
if (keyChanged) { if (keyChanged) {
getTenantConfigurationManagement().addOrUpdateConfiguration( getTenantConfigurationManagement().addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY,
@@ -174,9 +144,7 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
@Override @Override
public void undo() { public void undo() {
configurationEnabledChange = false; configurationEnabledChange = false;
keyNameChanged = false;
keyChanged = false; keyChanged = false;
gatewayTokenNameTextField.setValue(getSecurityTokenName());
gatewayTokenkeyLabel.setValue(getSecurityTokenKey()); gatewayTokenkeyLabel.setValue(getSecurityTokenKey());
} }

View File

@@ -9,7 +9,7 @@
package org.eclipse.hawkbit.ui.tenantconfiguration.authentication; package org.eclipse.hawkbit.ui.tenantconfiguration.authentication;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.I18N;
/** /**

View File

@@ -64,6 +64,7 @@
margin-left: 1px; margin-left: 1px;
position: relative; position: relative;
z-index: 1; z-index: 1;
font-size: 14px !important;
} }
//Version displayed at bottom of menu * //Version displayed at bottom of menu *

View File

@@ -135,7 +135,7 @@
} }
.margin-top-style { .margin-top-style {
margin-top: 10px; margin-top: 10px !important;
} }
//confimation dialogue popup - Content margin adjustment //confimation dialogue popup - Content margin adjustment