Merge remote-tracking branch 'eclipse/master' into

feature_split_repo_into_api_impl

# Conflicts:
#	hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
#	hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java


Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2016-05-24 21:51:59 +02:00
30 changed files with 263 additions and 137 deletions

View File

@@ -82,16 +82,26 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Log4j API and Core implementation required for binding -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<!-- Logging binding for java-util-logging e.g. Tomcat -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<!-- Logging binding for Jakarta Commons Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!-- Logging binding for Log4J Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>

View File

@@ -149,6 +149,8 @@ public class DeviceSimulatorUpdater {
eventbus.post(new ProgressUpdate(device));
return;
}
// download is 80% of the game after all
device.setProgress(0.8);
}
final double newProgress = device.getProgress() + 0.2;
@@ -262,7 +264,7 @@ public class DeviceSimulatorUpdater {
}
} catch (IOException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
LOGGER.error("Failed to download {} with {}", url, e.getMessage());
LOGGER.error("Failed to download" + url, e);
return new UpdateStatus(ResponseStatus.ERROR, "Failed to download " + url + ": " + e.getMessage());
}
@@ -272,6 +274,10 @@ public class DeviceSimulatorUpdater {
}
private static String hideTokenDetails(final String targetToken) {
if (targetToken == null) {
return "<NULL!>";
}
if (targetToken.isEmpty()) {
return "<EMTPTY!>";
}

View File

@@ -53,11 +53,13 @@ import com.vaadin.ui.renderers.ProgressBarRenderer;
*
*/
@SpringView(name = "")
// The inheritance comes from Vaadin
@SuppressWarnings("squid:MaximumInheritanceDepth")
public class SimulatorView extends VerticalLayout implements View {
private static final String NEXT_POLL_COUNTER_SEC_COL = "nextPollCounterSec";
private static final String RESPONSE_STATUS_COL = "responseStatus";
private static final String RESPONSE_STATUS_COL = "updateStatus";
private static final String PROTOCOL_COL = "protocol";
@@ -141,10 +143,9 @@ public class SimulatorView extends VerticalLayout implements View {
responseComboBox.setItemIcon(ResponseStatus.ERROR, FontAwesome.EXCLAMATION_CIRCLE);
responseComboBox.setNullSelectionAllowed(false);
responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
responseComboBox.addValueChangeListener(valueChangeEvent -> {
beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId)
.getItemProperty(RESPONSE_STATUS_COL).setValue(valueChangeEvent.getProperty().getValue()));
});
responseComboBox.addValueChangeListener(
valueChangeEvent -> beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId)
.getItemProperty(RESPONSE_STATUS_COL).setValue(valueChangeEvent.getProperty().getValue())));
// add all components
addComponent(caption);

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.eclipse.hawkbit.eventbus.DeadEventListener" level="WARN" />
<Logger name="org.springframework.boot.actuate.audit.listener.AuditListener" level="WARN" />
<Logger name="org.hibernate.validator.internal.util.Version" level="WARN" />
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
<Logger name="org.apache.tomcat.jdbc.pool.ConnectionPool" level="DEBUG" />
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
<!-- Security Log with hints on potential attacks -->
<logger name="server-security" level="INFO" />
<Root level="INFO">
<AppenderRef ref="Console" />
</Root>
</configuration>

View File

@@ -101,16 +101,26 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Log4j API and Core implementation required for binding -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<!-- Logging binding for java-util-logging e.g. Tomcat -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<!-- Logging binding for Jakarta Commons Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!-- Logging binding for Log4J Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.eclipse.hawkbit.eventbus.DeadEventListener" level="WARN" />
<Logger name="org.springframework.boot.actuate.audit.listener.AuditListener" level="WARN" />
<Logger name="org.hibernate.validator.internal.util.Version" level="WARN" />
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
<Logger name="org.apache.tomcat.jdbc.pool.ConnectionPool" level="DEBUG" />
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
<!-- Security Log with hints on potential attacks -->
<logger name="server-security" level="INFO" />
<Root level="INFO">
<AppenderRef ref="Console" />
</Root>
</configuration>

View File

@@ -18,8 +18,8 @@ import feign.Contract;
import feign.Feign;
/**
*
*
* Spring annotated java configuration class which defines necessary beans for
* configure the feign-client.
*/
@Configuration
@ConditionalOnClass(Feign.class)

View File

@@ -55,7 +55,6 @@
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<!-- need to overwrite for the interface inheritance feature of feign-core -->
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>

View File

@@ -13,7 +13,8 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.springframework.cloud.netflix.feign.FeignClient;
/**
*
* A feign-client interface declaration which allows to build a feign-client
* stub.
*/
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING)
public interface MgmtDownloadArtifactClientResource extends MgmtDownloadArtifactRestApi {

View File

@@ -13,7 +13,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleTypeRestApi;
import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the oftwareModuleType resource of the management API.
* Client binding for the SoftwareModuleType resource of the management API.
*/
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SOFTWAREMODULETYPE_V1_REQUEST_MAPPING)
public interface MgmtSoftwareModuleTypeClientResource extends MgmtSoftwareModuleTypeRestApi {

View File

@@ -61,7 +61,7 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
for (final Entry<String, String> entry : entrySet) {
if (entry.getKey().equals(PORT_PLACEHOLDER)) {
urlPattern = urlPattern.replace(":{" + entry.getKey() + "}",
Strings.isNullOrEmpty(entry.getValue()) ? "" : ":" + entry.getValue());
Strings.isNullOrEmpty(entry.getValue()) ? "" : (":" + entry.getValue()));
} else {
urlPattern = urlPattern.replace("{" + entry.getKey() + "}", entry.getValue());
}

View File

@@ -56,6 +56,9 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Deployment Action Resource")
public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoDB {
private static final String HTTP_LOCALHOST = "http://localhost:8080/";
private static final String HTTPS_LOCALHOST = "https://localhost:8080/";
@Test()
@Description("Ensures that artifacts are not found, when softare module does not exists.")
public void artifactsNotFound() throws Exception {
@@ -170,24 +173,22 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0].hashes.sha1",
equalTo(artifact.getSha1Hash())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.download.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.md5sum.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.download-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.md5sum-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.MD5SUM")))
@@ -200,23 +201,21 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1].hashes.sha1",
equalTo(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.download.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.md5sum.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.download-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.md5sum-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==bApp)][0].version",
@@ -315,12 +314,12 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0].hashes.sha1",
equalTo(artifact.getSha1Hash())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.download.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.md5sum.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.MD5SUM")))
@@ -333,23 +332,21 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1].hashes.sha1",
equalTo(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.download.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.md5sum.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.download-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.md5sum-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==bApp)][0].version",
@@ -445,23 +442,21 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
equalTo(artifact.getSha1Hash())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.download.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.md5sum.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.download-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0]._links.md5sum-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1].size", equalTo(5 * 1024)))
@@ -473,24 +468,22 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1].hashes.sha1",
equalTo(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.download.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.md5sum.href",
equalTo("https://localhost:8080/" + tenantAware.getCurrentTenant()
equalTo(HTTPS_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.download-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1]._links.md5sum-http.href",
equalTo("http://localhost:8080/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
equalTo(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))

View File

@@ -100,6 +100,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
assertTrue("No softwaremmodule should be contained in the request",
downloadAndUpdateRequest.getSoftwareModules().isEmpty());
}
@@ -116,6 +117,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
assertEquals("Expecting a size of 3 software modules in the reuqest", 3,
downloadAndUpdateRequest.getSoftwareModules().size());
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule softwareModule : downloadAndUpdateRequest
.getSoftwareModules()) {
assertTrue("Artifact list for softwaremodule should be empty", softwareModule.getArtifacts().isEmpty());
@@ -155,6 +157,8 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
assertEquals("DownloadAndUpdateRequest event should contains 3 software modules", 3,
downloadAndUpdateRequest.getSoftwareModules().size());
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule softwareModule : downloadAndUpdateRequest
.getSoftwareModules()) {
if (!softwareModule.getModuleId().equals(module.getId())) {

View File

@@ -39,6 +39,8 @@ import ru.yandex.qatools.allure.annotations.Stories;
AmqpTestConfiguration.class })
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
private static final String HTTPS_LOCALHOST = "https://localhost:8080/";
private static final String HTTP_LOCALHOST = "http://localhost:8080/";
@Autowired
private ArtifactUrlHandler urlHandlerProperties;
@Autowired
@@ -69,9 +71,8 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
final String url = urlHandlerProperties.getUrl(controllerId, softwareModuleId, fileName, sha1Hash,
UrlProtocol.HTTP);
assertEquals("http is build incorrect",
"http://localhost:8080/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
+ localArtifact.getFilename(),
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId + "/softwaremodules/"
+ localArtifact.getSoftwareModule().getId() + "/artifacts/" + localArtifact.getFilename(),
url);
}
@@ -81,7 +82,7 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
final String url = urlHandlerProperties.getUrl(controllerId, softwareModuleId, fileName, sha1Hash,
UrlProtocol.HTTPS);
assertEquals("https is build incorrect",
"https://localhost:8080/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
HTTPS_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
+ localArtifact.getFilename(),
url);

View File

@@ -85,4 +85,4 @@ public interface MgmtSystemRestApi {
@PathVariable("keyName") final String keyName,
@RequestBody final MgmtSystemTenantConfigurationValueRequest configurationValueRest);
}
}

View File

@@ -260,4 +260,4 @@ public interface MgmtTargetRestApi {
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("targetId") final String targetId);
}
}

View File

@@ -122,4 +122,5 @@ public class MgmtSystemResource implements MgmtSystemRestApi {
return new ResponseEntity<>(MgmtSystemMapper.toResponse(keyName, updatedValue), HttpStatus.OK);
}
}
}

View File

@@ -67,6 +67,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -131,6 +132,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Autowired
private SystemSecurityContext systemSecurityContext;
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Modifying
@@ -348,14 +352,14 @@ public class JpaDeploymentManagement implements DeploymentManagement {
private void assignDistributionSetEvent(final JpaTarget target, final Long actionId,
final List<JpaSoftwareModule> modules) {
((JpaTargetInfo) target.getTargetInfo()).setUpdateStatus(TargetUpdateStatus.PENDING);
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
@SuppressWarnings({ "unchecked", "rawtypes" })
final Collection<SoftwareModule> softwareModules = (Collection) modules;
afterCommit.afterCommit(() -> {
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(),
target.getSecurityToken()));
targetSecurityToken));
});
}

View File

@@ -44,6 +44,7 @@ import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.helper.SecurityChecker;
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.springframework.data.domain.Persistable;
@@ -210,11 +211,15 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
}
/**
* @return the securityToken
* @return the securityToken if the current security context contains the
* necessary permission {@link SpPermission#READ_TARGET_SEC_TOKEN}
* or the current context is executed as system code, otherwise
* {@code null}.
*/
@Override
public String getSecurityToken() {
if (SecurityChecker.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
if (SystemSecurityContextHolder.getInstance().getSystemSecurityContext().isCurrentThreadSystemCode()
|| SecurityChecker.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
return securityToken;
}
return null;

View File

@@ -48,6 +48,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil;
import org.eclipse.hawkbit.security.DosFilter;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.After;
import org.junit.AfterClass;
@@ -198,6 +199,9 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
@Autowired
protected RolloutRepository rolloutRepository;
@Autowired
protected SystemSecurityContext systemSecurityContext;
protected MockMvc mvc;
@Autowired

View File

@@ -160,19 +160,23 @@ public class WithSpringAuthorityRule implements TestRule {
}
public static WithUser withUser(final String principal, final String... authorities) {
return withUserAndTenant(principal, "default", true, authorities);
return withUserAndTenant(principal, "default", true, true, authorities);
}
public static WithUser withUser(final String principal, final boolean allSpPermision, final String... authorities) {
return withUserAndTenant(principal, "default", true, allSpPermision, authorities);
}
public static WithUser withUser(final boolean autoCreateTenant) {
return withUserAndTenant("bumlux", "default", autoCreateTenant, new String[] {});
return withUserAndTenant("bumlux", "default", autoCreateTenant, true, new String[] {});
}
public static WithUser withUserAndTenant(final String principal, final String tenant, final String... authorities) {
return withUserAndTenant(principal, tenant, true, new String[] {});
return withUserAndTenant(principal, tenant, true, true, new String[] {});
}
public static WithUser withUserAndTenant(final String principal, final String tenant,
final boolean autoCreateTenant, final String... authorities) {
final boolean autoCreateTenant, final boolean allSpPermission, final String... authorities) {
return new WithUser() {
@Override
@@ -197,7 +201,7 @@ public class WithSpringAuthorityRule implements TestRule {
@Override
public boolean allSpPermissions() {
return true;
return allSpPermission;
}
@Override

View File

@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.AbstractIntegrationTest;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.WithSpringAuthorityRule;
import org.eclipse.hawkbit.WithUser;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
@@ -58,6 +59,36 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Target Management")
public class TargetManagementTest extends AbstractIntegrationTest {
@Test
@Description("Ensures that retrieving the target security is only permitted with the necessary permissions.")
public void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception {
final Target createdTarget = targetManagement.createTarget(new JpaTarget("targetWithSecurityToken"));
// retrieve security token only with READ_TARGET_SEC_TOKEN permission
final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule
.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN.toString()), () -> {
return createdTarget.getSecurityToken();
});
// retrieve security token as system code execution
final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(() -> {
return createdTarget.getSecurityToken();
});
// retrieve security token without any permissions
final String securityTokenWithoutPermission = securityRule
.runAs(WithSpringAuthorityRule.withUser("NoPermission", false), () -> {
return createdTarget.getSecurityToken();
});
assertThat(createdTarget.getSecurityToken()).isNotNull();
assertThat(securityTokenWithReadPermission).isNotNull();
assertThat(securityTokenAsSystemCode).isNotNull();
assertThat(securityTokenWithoutPermission).isNull();
}
@Test
@Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists.")
@WithUser(tenantId = "tenantWhichDoesNotExists", allSpPermissions = true, autoCreateTenant = false)

View File

@@ -49,6 +49,21 @@ public class SystemSecurityContext {
this.tenantAware = tenantAware;
}
/**
* Runs a given {@link Callable} within a system security context, which is
* permitted to call secured system code. Often the system needs to call
* secured methods by it's own without relying on the current security
* context e.g. if the current security context does not contain the
* necessary permission it's necessary to execute code as system code to
* execute necessary methods and functionality.
*
* The security context will be switched to the system code and back after
* the callable is called.
*
* @param callable
* the callable to call within the system security context
* @return the return value of the {@link Callable#call()} method.
*/
public <T> T runAsSystem(final Callable<T> callable) {
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
@@ -68,6 +83,14 @@ public class SystemSecurityContext {
}
}
/**
* @return {@code true} if the current running code is running as system
* code block.
*/
public boolean isCurrentThreadSystemCode() {
return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication;
}
private static void setSystemContext() {
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
securityContextImpl.setAuthentication(new SystemCodeAuthentication());

View File

@@ -13,7 +13,6 @@ import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.ui.components.SPUIButton;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.filtermanagement.event.CustomFilterUIEvent;
import org.eclipse.hawkbit.ui.filtermanagement.state.FilterManagementUIState;
@@ -79,9 +78,8 @@ public class TargetFilterHeader extends VerticalLayout {
}
private Label createHeaderCaption() {
final Label captionLabel = SPUIComponentProvider.getLabel("Custom Filters",
return SPUIComponentProvider.getLabel(SPUIDefinitions.TARGET_FILTER_LIST_HEADER_CAPTION,
SPUILabelDefinitions.SP_WIDGET_CAPTION);
return captionLabel;
}
private void buildLayout() {
@@ -110,10 +108,9 @@ public class TargetFilterHeader extends VerticalLayout {
}
private Button createAddButton() {
final Button button = SPUIComponentProvider.getButton("camp.search.add.Id", "Create Filter", "Create Filter",
"", false, null, SPUIButtonStyleSmall.class);
final Button button = SPUIComponentProvider.getButton(SPUIComponetIdProvider.TARGET_FILTER_ADD_ICON_ID, "", "",
null, false, FontAwesome.PLUS, SPUIButtonStyleSmallNoBorder.class);
button.addClickListener(event -> addNewFilter());
button.addStyleName("on-focus-no-border link");
return button;
}

View File

@@ -296,8 +296,8 @@ public class ActionHistoryTable extends TreeTable implements Handler {
* add distribution name to the item which will be displayed in the
* table. The name should not exceed certain limit.
*/
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST).setValue(HawkbitCommonUtil
.getFormattedText(actionWithStatusCount.getDsName() + ":" + actionWithStatusCount.getDsVersion()));
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST).setValue(actionWithStatusCount.getDsName() + ":" +
actionWithStatusCount.getDsVersion());
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_FORCED).setValue(action);
/* Default no child */
@@ -446,8 +446,8 @@ public class ActionHistoryTable extends TreeTable implements Handler {
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE_HIDDEN).setValue("");
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST)
.setValue(HawkbitCommonUtil.getFormattedText(action.getDistributionSet().getName() + ":"
+ action.getDistributionSet().getVersion()));
.setValue(action.getDistributionSet().getName() + ":"
+ action.getDistributionSet().getVersion());
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DATETIME)
.setValue(SPDateTimeUtil.getFormattedDate(actionStatus.getCreatedAt()));

View File

@@ -431,27 +431,7 @@ public final class HawkbitCommonUtil {
return trimAndNullIfEmpty(orgText) == null ? SPUIDefinitions.SPACE : orgText;
}
/**
* Format the lengthy text.
*
* @param orgText
* text to be formatted
* @return String formatted text
*/
public static String getFormattedText(final String orgText) {
if (orgText == null) {
return StringUtils.EMPTY;
}
final int txtLengthAllowed = SPUIDefinitions.NAME_DESCRIPTION_LENGTH;
if (orgText.length() > txtLengthAllowed) {
return new StringBuilder(orgText.substring(0, txtLengthAllowed)).append("...").toString();
}
return orgText;
}
/**
/**
* Find extra height required to increase by all the components to utilize
* the full height of browser for the responsive UI.
*

View File

@@ -75,6 +75,11 @@ public final class SPUIComponetIdProvider {
*/
public static final String TARGET_TEXT_FIELD = "target.search.textfield";
/**
* ID for add target filter icon
*/
public static final String TARGET_FILTER_ADD_ICON_ID = "target.filter.add.id";
/**
* ID-Dist.
*/

View File

@@ -922,6 +922,11 @@ public final class SPUIDefinitions {
*/
public static final String CUSTOM_FILTER_ASSIGNED_DS = "Assigned DS";
/**
* TARGET_FILTER_MANAGEMENT - header caption .
*/
public static final String TARGET_FILTER_LIST_HEADER_CAPTION = "Custom Filters";
/**
* CUSTOM_FILTER_STATUS.
*/
@@ -1001,7 +1006,6 @@ public final class SPUIDefinitions {
*/
public static final String ROLLOUT_GROUP_STARTED_DATE = "Started date";
/**
* Rollout group status column property.
*/

View File

@@ -132,4 +132,12 @@ $v-included-components: remove($v-included-components, form);
left: 50%;
margin-left: -20px;
}
.v-generated-body &.v-app {
background-image: $app-background-image, linear-gradient(to bottom, $app-background-image-gradient) !important;
background-image: $app-background-image, -webkit-linear-gradient(top, $app-background-image-gradient) !important;
background-image: $app-background-image, -moz-linear-gradient(top, $app-background-image-gradient) !important;
background-position: bottom;
background-repeat: no-repeat;
}
}

29
pom.xml
View File

@@ -105,7 +105,6 @@
<jlorem.version>1.1</jlorem.version>
<json-simple.version>1.1.1</json-simple.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons.lang>2.3</commons.lang>
<json.version>20141113</json.version>
<rsql-parser.version>2.0.0</rsql-parser.version>
<!-- Misc libraries versions - END -->
@@ -449,39 +448,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -557,11 +533,6 @@
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>