Merge pull request #120 from bsinno/Download_server_supports_download_through_HTTP_and_HTTPs

DDI supports actifact download through http and anonyomous
This commit is contained in:
Michael Hirsch
2016-04-06 08:12:53 +02:00
52 changed files with 1043 additions and 477 deletions

View File

@@ -10,7 +10,6 @@ package org.eclipse.hawkbit.autoconfigure.security;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import javax.annotation.PostConstruct;
import javax.servlet.Filter;
@@ -35,6 +34,7 @@ import org.eclipse.hawkbit.security.ControllerTenantAwareAuthenticationDetailsSo
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DosFilter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateAnonymousDownloadFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.HttpControllerPreAuthenticatedSecurityHeaderFilter;
@@ -83,6 +83,8 @@ import org.vaadin.spring.security.web.VaadinRedirectStrategy;
import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler;
import com.google.common.collect.Lists;
/**
* All configurations related to SP authentication and authorization layer.
*
@@ -147,6 +149,12 @@ public class SecurityManagedConfiguration {
gatewaySecurityTokenFilter.setCheckForPrincipalChanges(true);
gatewaySecurityTokenFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
final HttpControllerPreAuthenticateAnonymousDownloadFilter controllerAnonymousDownloadFilter = new HttpControllerPreAuthenticateAnonymousDownloadFilter(
tenantConfigurationManagement, tenantAware, systemSecurityContext);
controllerAnonymousDownloadFilter.setAuthenticationManager(authenticationManager());
controllerAnonymousDownloadFilter.setCheckForPrincipalChanges(true);
controllerAnonymousDownloadFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
HttpSecurity httpSec = http.csrf().disable().headers()
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY)).contentTypeOptions()
.xssProtection().httpStrictTransportSecurity().and();
@@ -159,15 +167,17 @@ public class SecurityManagedConfiguration {
LOG.info(
"******************\n** Anonymous controller security enabled, should only use for developing purposes **\n******************");
final AnonymousAuthenticationFilter anoymousFilter = new AnonymousAuthenticationFilter(
"controllerAnonymousFilter", "anonymous", Collections.singletonList(
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS)));
"controllerAnonymousFilter", "anonymous",
Lists.newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS),
new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE)));
anoymousFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
httpSec.requestMatchers().antMatchers("/*/controller/v1/**", "/*/controller/artifacts/v1/**").and()
.securityContext().disable().anonymous().authenticationFilter(anoymousFilter);
} else {
httpSec.addFilter(securityHeaderFilter).addFilter(securityTokenFilter)
.addFilter(gatewaySecurityTokenFilter).antMatcher("/*/controller/**").anonymous().disable()
.authorizeRequests().anyRequest().authenticated().and().exceptionHandling()
.addFilter(gatewaySecurityTokenFilter).addFilter(controllerAnonymousDownloadFilter)
.antMatcher("/*/controller/**").anonymous().disable().authorizeRequests().anyRequest()
.authenticated().and().exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response
.setStatus(HttpStatus.UNAUTHORIZED.value()))
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

View File

@@ -0,0 +1,37 @@
/**
* 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.api;
/**
* Interface declaration of the {@link ArtifactUrlHandler} which generates the
* URLs to specific artifacts.
*
*/
@FunctionalInterface
public interface ArtifactUrlHandler {
/**
* Returns a generated download URL for a given artifact parameters for a
* specific protocol.
*
* @param controllerId
* the authenticated controller id
* @param softwareModuleId
* the softwareModuleId belonging to the artifact
* @param filename
* the filename of the artifact
* @param sha1Hash
* the sha1Hash of the artifact
* @param protocol
* the protocol the URL should be generated
* @return an URL for the given artifact parameters in a given protocol
*/
String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash,
final UrlProtocol protocol);
}

View File

@@ -6,13 +6,13 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.util;
package org.eclipse.hawkbit.api;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
*
*
* Artifact handler properties class for holding all supported protocols with
* host, ip, port and download pattern.
*/
@ConfigurationProperties("hawkbit.artifact.url")
public class ArtifactUrlHandlerProperties {
@@ -23,23 +23,14 @@ public class ArtifactUrlHandlerProperties {
private final Https https = new Https();
private final Coap coap = new Coap();
/**
* @return the http
*/
public Http getHttp() {
return http;
}
/**
* @return the https
*/
public Https getHttps() {
return https;
}
/**
* @return the coap
*/
public Coap getCoap() {
return coap;
}
@@ -66,9 +57,6 @@ public class ArtifactUrlHandlerProperties {
/**
* Interface for declaring common properties through all supported protocols
* pattern.
*
*
*
*/
public interface ProtocolProperties {
/**
@@ -94,9 +82,6 @@ public class ArtifactUrlHandlerProperties {
/**
* Object to hold the properties for the HTTP protocol.
*
*
*
*/
public static class Http implements ProtocolProperties {
private String hostname = LOCALHOST;
@@ -108,66 +93,38 @@ public class ArtifactUrlHandlerProperties {
*/
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
/**
* @return the hostname
*/
@Override
public String getHostname() {
return hostname;
}
/**
* @param hostname
* the hostname to set
*/
public void setHostname(final String hostname) {
this.hostname = hostname;
}
/**
* @return the ip
*/
@Override
public String getIp() {
return ip;
}
/**
* @param ip
* the ip to set
*/
public void setIp(final String ip) {
this.ip = ip;
}
/**
* @return the urlPattern
*/
@Override
public String getPattern() {
return pattern;
}
/**
* @param urlPattern
* the urlPattern to set
*/
public void setPattern(final String urlPattern) {
this.pattern = urlPattern;
}
/**
* @return the port
*/
@Override
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final String port) {
this.port = port;
}
@@ -175,9 +132,6 @@ public class ArtifactUrlHandlerProperties {
/**
* Object to hold the properties for the HTTP protocol.
*
*
*
*/
public static class Https implements ProtocolProperties {
private String hostname = LOCALHOST;
@@ -189,66 +143,38 @@ public class ArtifactUrlHandlerProperties {
*/
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
/**
* @return the hostname
*/
@Override
public String getHostname() {
return hostname;
}
/**
* @param hostname
* the hostname to set
*/
public void setHostname(final String hostname) {
this.hostname = hostname;
}
/**
* @return the ip
*/
@Override
public String getIp() {
return ip;
}
/**
* @param ip
* the ip to set
*/
public void setIp(final String ip) {
this.ip = ip;
}
/**
* @return the urlPattern
*/
@Override
public String getPattern() {
return pattern;
}
/**
* @param urlPattern
* the urlPattern to set
*/
public void setPattern(final String urlPattern) {
this.pattern = urlPattern;
}
/**
* @return the port
*/
@Override
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final String port) {
this.port = port;
}
@@ -256,9 +182,6 @@ public class ArtifactUrlHandlerProperties {
/**
* Object to hold the properties for the HTTP protocol.
*
*
*
*/
public static class Coap implements ProtocolProperties {
private String hostname = LOCALHOST;
@@ -270,68 +193,41 @@ public class ArtifactUrlHandlerProperties {
*/
private String pattern = "{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}";
/**
* @return the hostname
*/
@Override
public String getHostname() {
return hostname;
}
/**
* @param hostname
* the hostname to set
*/
public void setHostname(final String hostname) {
this.hostname = hostname;
}
/**
* @return the ip
*/
@Override
public String getIp() {
return ip;
}
/**
* @param ip
* the ip to set
*/
public void setIp(final String ip) {
this.ip = ip;
}
/**
* @return the urlPattern
*/
@Override
public String getPattern() {
return pattern;
}
/**
* @param urlPattern
* the urlPattern to set
*/
public void setPattern(final String urlPattern) {
this.pattern = urlPattern;
}
/**
* @return the port
*/
@Override
public String getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(final String port) {
this.port = port;
}
}
}

View File

@@ -6,17 +6,15 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.util;
package org.eclipse.hawkbit.api;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.ProtocolProperties;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.ArtifactUrlHandlerProperties.ProtocolProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
@@ -24,8 +22,8 @@ import org.springframework.stereotype.Component;
import com.google.common.base.Strings;
/**
*
*
* Implementation for ArtifactUrlHandler for creating urls to download resource
* based on pattern.
*/
@Component
@EnableConfigurationProperties(ArtifactUrlHandlerProperties.class)
@@ -48,7 +46,9 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
private TenantAware tenantAware;
@Override
public String getUrl(final String targetId, final LocalArtifact artifact, final Artifact.UrlProtocol protocol) {
public String getUrl(final String targetId, final Long softwareModuleId, final String filename,
final String sha1Hash, final UrlProtocol protocol) {
final String protocolString = protocol.name().toLowerCase();
final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString);
if (properties == null || properties.getPattern() == null) {
@@ -56,8 +56,8 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
}
String urlPattern = properties.getPattern();
final Set<Entry<String, String>> entrySet = getReplaceMap(targetId, artifact, protocolString, properties)
.entrySet();
final Set<Entry<String, String>> entrySet = getReplaceMap(targetId, softwareModuleId, filename, sha1Hash,
protocolString, properties).entrySet();
for (final Entry<String, String> entry : entrySet) {
if (entry.getKey().equals(PORT_PLACEHOLDER)) {
urlPattern = urlPattern.replace(":{" + entry.getKey() + "}",
@@ -69,18 +69,18 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
return urlPattern;
}
private Map<String, String> getReplaceMap(final String targetId, final LocalArtifact artifact,
final String protocol, final ProtocolProperties properties) {
private Map<String, String> getReplaceMap(final String targetId, final Long softwareModuleId, final String filename,
final String sha1Hash, final String protocol, final ProtocolProperties properties) {
final Map<String, String> replaceMap = new HashMap<>();
replaceMap.put(IP_PLACEHOLDER, properties.getIp());
replaceMap.put(HOSTNAME_PLACEHOLDER, properties.getHostname());
replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, artifact.getFilename());
replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, artifact.getSha1Hash());
replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, filename);
replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, sha1Hash);
replaceMap.put(PROTOCOL_PLACEHOLDER, protocol);
replaceMap.put(PORT_PLACEHOLDER, properties.getPort());
replaceMap.put(TENANT_PLACEHOLDER, tenantAware.getCurrentTenant());
replaceMap.put(TARGET_ID_PLACEHOLDER, targetId);
replaceMap.put(SOFTWARE_MODULE_ID_PLACDEHOLDER, String.valueOf(artifact.getSoftwareModule().getId()));
replaceMap.put(SOFTWARE_MODULE_ID_PLACDEHOLDER, String.valueOf(softwareModuleId));
return replaceMap;
}

View File

@@ -0,0 +1,16 @@
/**
* 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.api;
/**
* Represented the supported protocols for artifact url's.
*/
public enum UrlProtocol {
COAP, HTTP, HTTPS
}

View File

@@ -30,40 +30,60 @@ 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_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),
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),
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),
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),
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),
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),
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);
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;

View File

@@ -25,6 +25,11 @@
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>

View File

@@ -13,12 +13,14 @@ import java.util.List;
import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedGatewaySecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedSecurityHeaderFilter;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
@@ -90,6 +92,11 @@ public class AmqpControllerAuthentfication {
tenantConfigurationManagement, controllerManagement, tenantAware, systemSecurityContext);
filterChain.add(securityTokenFilter);
final ControllerPreAuthenticatedAnonymousDownload anonymousDownloadFilter = new ControllerPreAuthenticatedAnonymousDownload(
tenantConfigurationManagement, tenantAware, systemSecurityContext);
filterChain.add(anonymousDownloadFilter);
filterChain.add(new ControllerPreAuthenticatedAnonymousFilter(ddiSecruityProperties));
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
}
@@ -100,7 +107,7 @@ public class AmqpControllerAuthentfication {
* the authentication request object
* @return the authentfication object
*/
public Authentication doAuthenticate(final TenantSecruityToken secruityToken) {
public Authentication doAuthenticate(final TenantSecurityToken secruityToken) {
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null);
for (final PreAuthenficationFilter filter : filterChain) {
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken);
@@ -115,7 +122,7 @@ public class AmqpControllerAuthentfication {
}
private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthenficationFilter filter,
final TenantSecruityToken secruityToken) {
final TenantSecurityToken secruityToken) {
if (!filter.isEnable(secruityToken)) {
return null;

View File

@@ -14,6 +14,8 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.api.UrlProtocol;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
@@ -25,7 +27,6 @@ import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.util.ArtifactUrlHandler;
import org.eclipse.hawkbit.util.IpUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
@@ -152,12 +153,16 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) {
final Artifact artifact = new Artifact();
artifact.getUrls().put(Artifact.UrlProtocol.COAP,
artifactUrlHandler.getUrl(targetId, localArtifact, Artifact.UrlProtocol.COAP));
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP));
artifact.getUrls().put(Artifact.UrlProtocol.HTTP,
artifactUrlHandler.getUrl(targetId, localArtifact, Artifact.UrlProtocol.HTTP));
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP));
artifact.getUrls().put(Artifact.UrlProtocol.HTTPS,
artifactUrlHandler.getUrl(targetId, localArtifact, Artifact.UrlProtocol.HTTPS));
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS));
artifact.setFilename(localArtifact.getFilename());
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), null));

View File

@@ -28,7 +28,8 @@ import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
@@ -157,25 +158,28 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private Message handleAuthentifiactionMessage(final Message message) {
final DownloadResponse authentificationResponse = new DownloadResponse();
final MessageProperties messageProperties = message.getMessageProperties();
final TenantSecruityToken secruityToken = convertMessage(message, TenantSecruityToken.class);
final String sha1 = secruityToken.getSha1();
final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class);
final FileResource fileResource = secruityToken.getFileResource();
try {
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
final LocalArtifact localArtifact = artifactManagement
.findFirstLocalArtifactsBySHA1(secruityToken.getSha1());
final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource);
if (localArtifact == null) {
throw new EntityNotFoundException();
}
// check action for this download purposes, the method will throw an
// EntityNotFoundException in case the controller is not allowed to
// download this file
// because it's not assigned to an action and not assigned to this
// controller.
final Action action = controllerManagement.getActionForDownloadByTargetAndSoftwareModule(
secruityToken.getControllerId(), localArtifact.getSoftwareModule());
LOG.info("Found action for download authentication request action: {}, sha1: {}", action,
secruityToken.getSha1());
// download this file because it's not assigned to an action and not
// assigned to this controller. Otherwise no controllerId is set =
// anonymous download
if (secruityToken.getControllerId() != null) {
final Action action = controllerManagement.getActionForDownloadByTargetAndSoftwareModule(
secruityToken.getControllerId(), localArtifact.getSoftwareModule());
LOG.info("Found action for download authentication request action: {}, resource: {}", action,
secruityToken.getFileResource());
}
final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact));
if (artifact == null) {
@@ -183,7 +187,9 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
}
authentificationResponse.setArtifact(artifact);
final String downloadId = UUID.randomUUID().toString();
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, sha1);
// SHA1 key is set, download by SHA1
final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1,
localArtifact.getSha1Hash());
cache.put(downloadId, downloadCache);
authentificationResponse
.setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI())
@@ -198,7 +204,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
authentificationResponse.setMessage("Building download URI failed");
} catch (final EntityNotFoundException e) {
final String errorMessage = "Artifact with sha1 " + sha1 + "not found ";
final String errorMessage = "Artifact for resource " + fileResource + "not found ";
LOG.warn(errorMessage, e);
authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value());
authentificationResponse.setMessage(errorMessage);
@@ -207,6 +213,21 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
return getMessageConverter().toMessage(authentificationResponse, messageProperties);
}
private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) {
if (fileResource.getSha1() != null) {
return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1());
} else if (fileResource.getFilename() != null) {
return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst()
.orElse(null);
} else if (fileResource.getSoftwareModuleFilenameResource() != null) {
return artifactManagement
.findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(),
fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId())
.stream().findFirst().orElse(null);
}
return null;
}
private static Artifact convertDbArtifact(final DbArtifact dbArtifact) {
final Artifact artifact = new Artifact();
artifact.setSize(dbArtifact.getSize());

View File

@@ -1,35 +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.util;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
/**
* Interface declaration of the {@link ArtifactUrlHandler} which generates the
* URLs to specific artifacts.
*
*/
@FunctionalInterface
public interface ArtifactUrlHandler {
/**
* Returns a generated URL for a given artifact for a specific protocol.
*
* @param controllerId
* the authentifacted controller id
* @param localArtifact
* the artifact to retrieve a URL to
* @param protocol
* the protocol the URL should be generated
* @return an URL for the given artifact in a given protocol
*/
String getUrl(String controllerId, LocalArtifact localArtifact, final Artifact.UrlProtocol protocol);
}

View File

@@ -19,12 +19,14 @@ import static org.mockito.Mockito.when;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.security.DdiSecurityProperties;
import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymous;
import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp;
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SystemSecurityContext;
@@ -79,8 +81,14 @@ public class AmqpControllerAuthenticationTest {
final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class);
final Rp rp = mock(Rp.class);
final org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication ddiAuthentication = mock(
org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.class);
final Anonymous anonymous = mock(Anonymous.class);
when(secruityProperties.getRp()).thenReturn(rp);
when(rp.getSslIssuerHashHeader()).thenReturn("X-Ssl-Issuer-Hash-%d");
when(secruityProperties.getAuthentication()).thenReturn(ddiAuthentication);
when(ddiAuthentication.getAnonymous()).thenReturn(anonymous);
when(anonymous.isEnabled()).thenReturn(false);
authenticationManager.setSecruityProperties(secruityProperties);
tenantConfigurationManagement = mock(TenantConfigurationManagement.class);
@@ -105,7 +113,8 @@ public class AmqpControllerAuthenticationTest {
@Test
@Description("Tests authentication manager without principal")
public void testAuthenticationeBadCredantialsWithoutPricipal() {
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
FileResource.sha1("12345"));
try {
authenticationManager.doAuthenticate(securityToken);
fail("BadCredentialsException was excepeted since principal was missing");
@@ -118,11 +127,12 @@ public class AmqpControllerAuthenticationTest {
@Test
@Description("Tests authentication manager without wrong credential")
public void testAuthenticationBadCredantialsWithWrongCredential() {
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
FileResource.sha1("12345"));
when(tenantConfigurationManagement.getConfigurationValue(
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
.thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
try {
authenticationManager.doAuthenticate(securityToken);
fail("BadCredentialsException was excepeted due to wrong credential");
@@ -135,11 +145,12 @@ public class AmqpControllerAuthenticationTest {
@Test
@Description("Tests authentication successfull")
public void testSuccessfullAuthentication() {
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
FileResource.sha1("12345"));
when(tenantConfigurationManagement.getConfigurationValue(
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
.thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
final Authentication authentication = authenticationManager.doAuthenticate(securityToken);
assertThat(authentication).isNotNull();
}
@@ -149,7 +160,8 @@ public class AmqpControllerAuthenticationTest {
public void testAuthenticationMessageBadCredantialsWithoutPricipal() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
FileResource.sha1("12345"));
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);
@@ -167,11 +179,12 @@ public class AmqpControllerAuthenticationTest {
@Description("Tests authentication message without wrong credential")
public void testAuthenticationMessageBadCredantialsWithWrongCredential() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
FileResource.sha1("12345"));
when(tenantConfigurationManagement.getConfigurationValue(
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
.thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID);
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);
@@ -189,11 +202,12 @@ public class AmqpControllerAuthenticationTest {
@Description("Tests authentication message successfull")
public void testSuccessfullMessageAuthentication() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, CONTROLLLER_ID, "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
FileResource.sha1("12345"));
when(tenantConfigurationManagement.getConfigurationValue(
eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class)))
.thenReturn(CONFIG_VALUE_TRUE);
securityToken.getHeaders().put(TenantSecruityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID);
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);

View File

@@ -13,6 +13,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -25,6 +26,7 @@ import java.util.List;
import org.eclipse.hawkbit.AbstractIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
@@ -36,7 +38,6 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.util.ArtifactUrlHandler;
import org.eclipse.hawkbit.util.IpUtil;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -77,7 +78,8 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
amqpMessageDispatcherService.setAmqpSenderService(senderService);
final ArtifactUrlHandler artifactUrlHandlerMock = Mockito.mock(ArtifactUrlHandler.class);
when(artifactUrlHandlerMock.getUrl(anyString(), any(), anyObject())).thenReturn("http://mockurl");
when(artifactUrlHandlerMock.getUrl(anyString(), anyLong(), anyString(), anyString(), anyObject()))
.thenReturn("http://mockurl");
amqpMessageDispatcherService.setArtifactUrlHandler(artifactUrlHandlerMock);

View File

@@ -34,7 +34,8 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -263,7 +264,7 @@ public class AmqpMessageHandlerServiceTest {
@Description("Tests that an download request is denied for an artifact which does not exists")
public void authenticationRequestDeniedForArtifactWhichDoesNotExists() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, "123", "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);
@@ -282,7 +283,7 @@ public class AmqpMessageHandlerServiceTest {
@Description("Tests that an download request is denied for an artifact which is not assigned to the requested target")
public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, "123", "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);
@@ -306,7 +307,7 @@ public class AmqpMessageHandlerServiceTest {
@Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target")
public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException {
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
final TenantSecruityToken securityToken = new TenantSecruityToken(TENANT, "123", "12345");
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
messageProperties);

View File

@@ -15,7 +15,8 @@ import org.eclipse.hawkbit.AmqpTestConfiguration;
import org.eclipse.hawkbit.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.TestConfiguration;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.api.UrlProtocol;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -30,8 +31,7 @@ import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
*
*
* Tests for creating urls to download artifacts.
*/
@Features("Component Tests - Artifact URL Handler")
@Stories("Test to generate the artifact download URL")
@@ -45,6 +45,9 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
private TenantAware tenantAware;
private LocalArtifact localArtifact;
private final String controllerId = "Test";
private String fileName;
private Long softwareModuleId;
private String sha1Hash;
@Before
public void setup() {
@@ -53,12 +56,18 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
final SoftwareModule module = dsA.getModules().iterator().next();
localArtifact = (LocalArtifact) TestDataUtil.generateArtifacts(artifactManagement, module.getId()).stream()
.findAny().get();
softwareModuleId = localArtifact.getSoftwareModule().getId();
fileName = localArtifact.getFilename();
sha1Hash = localArtifact.getSha1Hash();
}
@Test
@Description("Tests the generation of http download url.")
public void testHttpUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTP);
final String url = urlHandlerProperties.getUrl(controllerId, softwareModuleId, fileName, sha1Hash,
UrlProtocol.HTTP);
assertEquals("http is build incorrect",
"http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
@@ -69,7 +78,8 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
@Test
@Description("Tests the generation of https download url.")
public void testHttpsUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.HTTPS);
final String url = urlHandlerProperties.getUrl(controllerId, softwareModuleId, fileName, sha1Hash,
UrlProtocol.HTTPS);
assertEquals("https is build incorrect",
"https://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/" + controllerId
+ "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/"
@@ -80,7 +90,8 @@ public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest
@Test
@Description("Tests the generation of coap download url.")
public void testCoapUrl() {
final String url = urlHandlerProperties.getUrl(controllerId, localArtifact, Artifact.UrlProtocol.COAP);
final String url = urlHandlerProperties.getUrl(controllerId, softwareModuleId, fileName, sha1Hash,
UrlProtocol.COAP);
assertEquals("coap is build incorrect", "coap://127.0.0.1:5683/fw/" + tenantAware.getCurrentTenant() + "/"
+ controllerId + "/sha1/" + localArtifact.getSha1Hash(), url);

View File

@@ -1,94 +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.dmf.json.model;
import java.util.Map;
import java.util.TreeMap;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* JSON representation to authenticate a tenant.
*
*
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TenantSecruityToken {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String COAP_AUTHORIZATION_HEADER = "Coap-Authorization";
public static final String COAP_TOKEN_VALUE = "CoapToken";
@JsonProperty
private final String tenant;
@JsonProperty
private final String controllerId;
@JsonProperty(required = false)
private Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
@JsonProperty(required = false)
private final String sha1;
/**
* Constructor.
*
* @param tenant
* the tenant for the security token
* @param controllerId
* the ID of the controller for the security token
* @param sha1
* the sha1 of authentication
*/
@JsonCreator
public TenantSecruityToken(@JsonProperty("tenant") final String tenant,
@JsonProperty("controllerId") final String controllerId, @JsonProperty("sha1") final String sha1) {
this.tenant = tenant;
this.controllerId = controllerId;
this.sha1 = sha1;
}
public String getTenant() {
return tenant;
}
public String getControllerId() {
return controllerId;
}
public Map<String, String> getHeaders() {
return headers;
}
public String getSha1() {
return sha1;
}
/**
* Gets a header value.
*
* @param name
* of header
* @return the value
*/
public String getHeader(final String name) {
return headers.get(name);
}
public void setHeaders(final Map<String, String> headers) {
this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
this.headers.putAll(headers);
}
}

View File

@@ -0,0 +1,226 @@
/**
* 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.dmf.json.model;
import java.util.Map;
import java.util.TreeMap;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* JSON representation to authenticate a tenant.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TenantSecurityToken {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String COAP_AUTHORIZATION_HEADER = "Coap-Authorization";
public static final String COAP_TOKEN_VALUE = "CoapToken";
@JsonProperty
private final String tenant;
@JsonProperty
private final String controllerId;
@JsonProperty(required = false)
private Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
@JsonProperty(required = false)
private final FileResource fileResource;
/**
* Constructor.
*
* @param tenant
* the tenant for the security token
* @param controllerId
* the ID of the controller for the security token
* @param fileResource
* the file to obtain
*/
@JsonCreator
public TenantSecurityToken(@JsonProperty("tenant") final String tenant,
@JsonProperty("controllerId") final String controllerId,
@JsonProperty("fileResource") final FileResource fileResource) {
this.tenant = tenant;
this.controllerId = controllerId;
this.fileResource = fileResource;
}
public String getTenant() {
return tenant;
}
public String getControllerId() {
return controllerId;
}
public Map<String, String> getHeaders() {
return headers;
}
public FileResource getFileResource() {
return fileResource;
}
/**
* Gets a header value.
*
* @param name
* of header
* @return the value
*/
public String getHeader(final String name) {
return headers.get(name);
}
public void setHeaders(final Map<String, String> headers) {
this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
this.headers.putAll(headers);
}
/**
* File resource descriptor which is used to ask for the resource to
* download e.g. The lookup of the file can be different e.g. by SHA1 hash
* or by filename.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public static class FileResource {
@JsonProperty(required = false)
private String sha1;
@JsonProperty(required = false)
private String filename;
@JsonProperty(required = false)
private SoftwareModuleFilenameResource softwareModuleFilenameResource;
public String getSha1() {
return sha1;
}
public void setSha1(final String sha1) {
this.sha1 = sha1;
}
public String getFilename() {
return filename;
}
public void setFilename(final String filename) {
this.filename = filename;
}
public SoftwareModuleFilenameResource getSoftwareModuleFilenameResource() {
return softwareModuleFilenameResource;
}
public void setSoftwareModuleFilenameResource(
final SoftwareModuleFilenameResource softwareModuleFilenameResource) {
this.softwareModuleFilenameResource = softwareModuleFilenameResource;
}
/**
* factory method to create a file resource for an SHA1 lookup.
*
* @param sha1
* the SHA1 key of the file to obtain
* @return the {@link FileResource} with SHA1 key set
*/
public static FileResource sha1(final String sha1) {
final FileResource resource = new FileResource();
resource.sha1 = sha1;
return resource;
}
/**
* factory method to create a file resource for an filename lookup.
*
* @param filename
* the filename of the file to obtain
* @return the {@link FileResource} with filename set
*/
public static FileResource filename(final String filename) {
final FileResource resource = new FileResource();
resource.filename = filename;
return resource;
}
/**
* factory method to create a file resource for an softwaremodule +
* filename lookup, because an filename is not globally unique but
* within a softwaremodule.
*
* @param softwareModuleId
* the ID of the software module which contains the artifact
* @param filename
* the name of file to obtain within the software module
* @return the {@link FileResource} with artifactId set
*/
public static FileResource softwareModuleFilename(final Long softwareModuleId, final String filename) {
final FileResource resource = new FileResource();
resource.softwareModuleFilenameResource = new SoftwareModuleFilenameResource(softwareModuleId, filename);
return resource;
}
@Override
public String toString() {
return "FileResource [sha1=" + sha1 + ", filename=" + filename + "]";
}
/**
* Inner class which holds the pointer to an artifact based on the
* softwaremoduleId and the filename.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public static class SoftwareModuleFilenameResource {
@JsonProperty(required = false)
private Long softwareModuleId;
@JsonProperty(required = false)
private String filename;
/**
* Constructor.
*
* @param softwareModuleId
* the ID of the softwaremodule
* @param filename
* the name of the file of the artifact within the
* softwaremodule
*/
@JsonCreator
public SoftwareModuleFilenameResource(@JsonProperty("softwareModuleId") final Long softwareModuleId,
@JsonProperty("filename") final String filename) {
this.softwareModuleId = softwareModuleId;
this.filename = filename;
}
public Long getSoftwareModuleId() {
return softwareModuleId;
}
public String getFilename() {
return filename;
}
public void setSoftwareModuleId(final Long softwareModuleId) {
this.softwareModuleId = softwareModuleId;
}
public void setFilename(final String filename) {
this.filename = filename;
}
}
}
}

View File

@@ -9,6 +9,8 @@
package org.eclipse.hawkbit.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import javax.servlet.FilterChain;
@@ -16,14 +18,19 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.AntPathMatcher;
import com.google.common.collect.Iterators;
@@ -79,14 +86,6 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
pathExtractor = new AntPathMatcher();
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.web.authentication.preauth.
* AbstractPreAuthenticatedProcessingFilter
* #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
* javax.servlet.FilterChain)
*/
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
@@ -96,7 +95,7 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
return;
}
final TenantSecruityToken secruityToken = createTenantSecruityTokenVariables((HttpServletRequest) request);
final TenantSecurityToken secruityToken = createTenantSecruityTokenVariables((HttpServletRequest) request);
if (secruityToken == null) {
chain.doFilter(request, response);
return;
@@ -112,6 +111,18 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
protected abstract PreAuthenficationFilter createControllerAuthenticationFilter();
@Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authResult) {
final Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(authResult.getAuthorities());
authorities.addAll(abstractControllerAuthenticationFilter.getSuccessfulAuthenticationAuthorities());
final PreAuthenticatedAuthenticationToken authTokenWithGrantedAuthorities = new PreAuthenticatedAuthenticationToken(
authResult.getPrincipal(), authResult.getCredentials(), authorities);
authTokenWithGrantedAuthorities.setDetails(authResult.getDetails());
super.successfulAuthentication(request, response, authTokenWithGrantedAuthorities);
}
/**
* Extracts tenant and controllerId from the request URI as path variables.
*
@@ -121,7 +132,7 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
* request does not match the pattern and no variables could be
* extracted
*/
protected TenantSecruityToken createTenantSecruityTokenVariables(final HttpServletRequest request) {
protected TenantSecurityToken createTenantSecruityTokenVariables(final HttpServletRequest request) {
final String requestURI = request.getRequestURI();
if (pathExtractor.match(request.getContextPath() + CONTROLLER_REQUEST_ANT_PATTERN, requestURI)) {
@@ -153,9 +164,9 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
}
}
private TenantSecruityToken createTenantSecruityTokenVariables(final HttpServletRequest request,
private TenantSecurityToken createTenantSecruityTokenVariables(final HttpServletRequest request,
final String tenant, final String controllerId) {
final TenantSecruityToken secruityToken = new TenantSecruityToken(tenant, controllerId, "");
final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, controllerId, FileResource.sha1(""));
final UnmodifiableIterator<String> forEnumeration = Iterators.forEnumeration(request.getHeaderNames());
forEnumeration.forEachRemaining(header -> secruityToken.getHeaders().put(header, request.getHeader(header)));
return secruityToken;
@@ -163,7 +174,7 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
@Override
protected Object getPreAuthenticatedPrincipal(final HttpServletRequest request) {
final TenantSecruityToken secruityToken = createTenantSecruityTokenVariables(request);
final TenantSecurityToken secruityToken = createTenantSecruityTokenVariables(request);
if (secruityToken == null) {
return null;
}
@@ -172,7 +183,7 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac
@Override
protected Object getPreAuthenticatedCredentials(final HttpServletRequest request) {
final TenantSecruityToken secruityToken = createTenantSecruityTokenVariables(request);
final TenantSecurityToken secruityToken = createTenantSecruityTokenVariables(request);
if (secruityToken == null) {
return null;
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
/**
* An pre-authenticated processing filter which add the
* {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE_ANONYMOUS} to the
* security context in case the anonymous download is allowed through
* configuration.
*/
public class HttpControllerPreAuthenticateAnonymousDownloadFilter extends AbstractHttpControllerAuthenticationFilter {
/**
* Constructor.
*
* @param tenantConfigurationManagement
* the system management service to retrieve configuration
* properties
* @param tenantAware
* the tenant aware service to get configuration for the specific
* tenant
* @param systemSecurityContext
* the system security context
*/
public HttpControllerPreAuthenticateAnonymousDownloadFilter(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final SystemSecurityContext systemSecurityContext) {
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
protected PreAuthenficationFilter createControllerAuthenticationFilter() {
return new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagement, tenantAware,
systemSecurityContext);
}
}

View File

@@ -321,7 +321,8 @@ public class ArtifactManagement {
}
boolean artifactIsOnlyUsedByOneSoftwareModule = true;
for (LocalArtifact lArtifact : localArtifactRepository.findByGridFsFileName(existing.getGridFsFileName())) {
for (final LocalArtifact lArtifact : localArtifactRepository
.findByGridFsFileName(existing.getGridFsFileName())) {
if (!lArtifact.getSoftwareModule().isDeleted()
&& lArtifact.getSoftwareModule().getId() != existing.getSoftwareModule().getId()) {
artifactIsOnlyUsedByOneSoftwareModule = false;
@@ -403,7 +404,7 @@ public class ArtifactManagement {
* if file could not be found in store
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD)
public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) {
final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
if (result == null) {

View File

@@ -25,6 +25,11 @@
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>

View File

@@ -18,6 +18,8 @@ import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.api.UrlProtocol;
import org.eclipse.hawkbit.controller.model.Artifact;
import org.eclipse.hawkbit.controller.model.Chunk;
import org.eclipse.hawkbit.controller.model.Config;
@@ -29,27 +31,29 @@ import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.resource.model.artifact.ArtifactHash;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link;
import com.google.common.base.Charsets;
/**
* Utility class for the Controller API.
*
*
*
*
*/
public final class DataConversionHelper {
@Autowired
ArtifactUrlHandler artifactUrlHandler;
// utility class, private constructor.
private DataConversionHelper() {
}
static List<Chunk> createChunks(final String targetid, final Action uAction, final TenantAware tenantAware) {
return uAction.getDistributionSet()
.getModules().stream().map(module -> new Chunk(mapChunkLegacyKeys(module.getType().getKey()),
module.getVersion(), module.getName(), createArtifacts(targetid, module, tenantAware)))
static List<Chunk> createChunks(final String targetid, final Action uAction,
final ArtifactUrlHandler artifactUrlHandler) {
return uAction.getDistributionSet().getModules().stream()
.map(module -> new Chunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
module.getName(), createArtifacts(targetid, module, artifactUrlHandler)))
.collect(Collectors.toList());
}
@@ -72,23 +76,25 @@ public final class DataConversionHelper {
* of the target
* @param module
* the software module
* @param tenantAware
* of the tenant
* @return a list of artifacts or a empty list. Cannot be <null>.
*/
public static List<Artifact> createArtifacts(final String targetid,
final org.eclipse.hawkbit.repository.model.SoftwareModule module, final TenantAware tenantAware) {
final org.eclipse.hawkbit.repository.model.SoftwareModule module,
final ArtifactUrlHandler artifactUrlHandler) {
final List<Artifact> files = new ArrayList<>();
module.getLocalArtifacts().forEach(artifact -> {
final Artifact file = new Artifact();
file.setHashes(new ArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash()));
file.setFilename(artifact.getFilename());
file.setSize(artifact.getSize());
file.add(linkTo(methodOn(RootController.class, tenantAware.getCurrentTenant()).downloadArtifact(targetid,
artifact.getSoftwareModule().getId(), artifact.getFilename(), null, null)).withRel("download"));
file.add(linkTo(methodOn(RootController.class, tenantAware.getCurrentTenant()).downloadArtifactMd5(targetid,
artifact.getSoftwareModule().getId(), artifact.getFilename(), null, null)).withRel("md5sum"));
final String linkHttp = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(),
artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTP);
final String linkHttps = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(),
artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTPS);
file.add(new Link(linkHttps).withRel("download"));
file.add(new Link(linkHttps + ControllerConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum"));
file.add(new Link(linkHttp).withRel("download-http"));
file.add(new Link(linkHttp + ControllerConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum-http"));
files.add(file);
});

View File

@@ -15,6 +15,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.cache.CacheWriteNotify;
import org.eclipse.hawkbit.controller.model.ActionFeedback;
@@ -84,11 +85,14 @@ public class RootController {
@Autowired
private CacheWriteNotify cacheWriteNotify;
@Autowired
private HawkbitSecurityProperties securityProperties;
@Autowired
private TenantAware tenantAware;
@Autowired
private HawkbitSecurityProperties securityProperties;
private ArtifactUrlHandler artifactUrlHandler;
/**
* Returns all artifacts of a given software module and target.
@@ -114,7 +118,7 @@ public class RootController {
}
return new ResponseEntity<>(DataConversionHelper.createArtifacts(targetid, softwareModule, tenantAware),
return new ResponseEntity<>(DataConversionHelper.createArtifacts(targetid, softwareModule, artifactUrlHandler),
HttpStatus.OK);
}
@@ -303,7 +307,7 @@ public class RootController {
if (!action.isCancelingOrCanceled()) {
final List<Chunk> chunks = DataConversionHelper.createChunks(targetid, action, tenantAware);
final List<Chunk> chunks = DataConversionHelper.createChunks(targetid, action, artifactUrlHandler);
final HandlingType handlingType = action.isForce() ? HandlingType.FORCED : HandlingType.ATTEMPT;

View File

@@ -170,15 +170,27 @@ public class DeploymentBaseTest extends AbstractIntegrationTestWithMongoDB {
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("http://localhost/" + 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/" + 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/" + 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/" + 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)))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[1].filename",
equalTo("test1.signature")))
@@ -188,11 +200,21 @@ public class DeploymentBaseTest extends AbstractIntegrationTestWithMongoDB {
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("http://localhost/" + 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/" + 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/" + 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/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
@@ -293,12 +315,12 @@ public class DeploymentBaseTest extends AbstractIntegrationTestWithMongoDB {
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("http://localhost/" + 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("http://localhost/" + tenantAware.getCurrentTenant()
equalTo("https://localhost/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.MD5SUM")))
@@ -311,11 +333,21 @@ public class DeploymentBaseTest extends AbstractIntegrationTestWithMongoDB {
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("http://localhost/" + 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/" + 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/" + 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/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
@@ -409,15 +441,25 @@ public class DeploymentBaseTest extends AbstractIntegrationTestWithMongoDB {
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0].filename", equalTo("test1")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0].hashes.md5",
equalTo(artifact.getMd5Hash())))
.andExpect(
jsonPath("$deployment.chunks[?(@.part==os)][0].artifacts[0].hashes.sha1",
equalTo(artifact.getSha1Hash())))
.andExpect(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("http://localhost/" + 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/" + 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/" + 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/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
@@ -431,15 +473,27 @@ public class DeploymentBaseTest extends AbstractIntegrationTestWithMongoDB {
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("http://localhost/" + 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/" + 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/" + 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/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$deployment.chunks[?(@.part==bApp)][0].version",
equalTo(ds.findFirstModuleByType(appType).getVersion())))
.andExpect(jsonPath("$deployment.chunks[?(@.part==bApp)][0].name",

View File

@@ -182,6 +182,12 @@ public final class SpPermission {
*/
public static final String CONTROLLER_ROLE_ANONYMOUS = "ROLE_CONTROLLER_ANONYMOUS";
/**
* The role which contains in the spring security context in case an
* controller is authenticated to download artifacts.
*/
public static final String CONTROLLER_DOWNLOAD_ROLE = "ROLE_CONTROLLER_DOWNLOAD";
/**
* The role which contains the spring security context in case the
* system is executing code which is necessary to be privileged.
@@ -275,8 +281,16 @@ public final class SpPermission {
* context contains the anoynmous role or the controller specific role
* {@link SpPermission#CONTROLLER_ROLE}.
*/
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '"
+ CONTROLLER_ROLE + "')";
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
+ "')";
/**
* Spring security eval hasAuthority expression to check if the spring
* context contains the role to allow controllers to download specific
* role {@link SpPermission#CONTROLLER_DOWNLOAD_ROLE}.
*/
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
+ HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAnyRole expression to check if the spring

View File

@@ -35,7 +35,32 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<!-- TEST -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
@@ -42,15 +42,15 @@ public abstract class AbstractControllerAuthenticationFilter implements PreAuthe
protected abstract TenantConfigurationKey getTenantConfigurationKey();
@Override
public boolean isEnable(final TenantSecruityToken secruityToken) {
public boolean isEnable(final TenantSecurityToken secruityToken) {
return tenantAware.runAsTenant(secruityToken.getTenant(), configurationKeyTenantRunner);
}
@Override
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecruityToken secruityToken);
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
@Override
public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecruityToken secruityToken);
public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<Boolean> {
@Override

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
/**
* A Filter for device which download via coap.
@@ -19,19 +19,19 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
public class CoapAnonymousPreAuthenticatedFilter implements PreAuthenficationFilter {
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecruityToken.COAP_TOKEN_VALUE);
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecurityToken.COAP_TOKEN_VALUE);
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecruityToken.COAP_TOKEN_VALUE);
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecurityToken.COAP_TOKEN_VALUE);
}
@Override
public boolean isEnable(final TenantSecruityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecruityToken.COAP_AUTHORIZATION_HEADER);
return TenantSecruityToken.COAP_TOKEN_VALUE.equals(authHeader);
public boolean isEnable(final TenantSecurityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecurityToken.COAP_AUTHORIZATION_HEADER);
return TenantSecurityToken.COAP_TOKEN_VALUE.equals(authHeader);
}
}

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -67,8 +67,8 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro
}
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecruityToken.AUTHORIZATION_HEADER);
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecurityToken.AUTHORIZATION_HEADER);
if ((authHeader != null) && authHeader.startsWith(TARGET_SECURITY_TOKEN_AUTH_SCHEME)) {
LOGGER.debug("found authorization header with scheme {} using target security token for authentication",
TARGET_SECURITY_TOKEN_AUTH_SCHEME);
@@ -81,7 +81,7 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
final String securityToken = tenantAware.runAsTenant(secruityToken.getTenant(),
new GetSecurityTokenTenantRunner(secruityToken.getTenant(), secruityToken.getControllerId()));
return new HeaderAuthentication(secruityToken.getControllerId(), securityToken);

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.security;
import java.util.Collection;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import com.google.common.collect.Lists;
/**
* An pre-authenticated processing filter which add the
* {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE_ANONYMOUS} to the
* security context in case the anonymous download is allowed through
* configuration.
*/
public class ControllerPreAuthenticatedAnonymousDownload extends AbstractControllerAuthenticationFilter {
/**
* Constructor.
*
* @param tenantConfigurationManagement
* the tenant management service to retrieve configuration
* properties
* @param tenantAware
* the tenant aware service to get configuration for the specific
* tenant
* @param systemSecurityContext
* the system security context to get access to tenant
* configuration
*/
public ControllerPreAuthenticatedAnonymousDownload(
final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware,
final SystemSecurityContext systemSecurityContext) {
super(tenantConfigurationManagement, tenantAware, systemSecurityContext);
}
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
}
@Override
protected TenantConfigurationKey getTenantConfigurationKey() {
return TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED;
}
@Override
public Collection<GrantedAuthority> getSuccessfulAuthenticationAuthorities() {
return Lists.newArrayList(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE));
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
/**
* An anonymous controller filter which is only enabled in case of anonymous
* access is granted. This should only be for development purposes.
*
* @see DdiSecurityProperties
*/
public class ControllerPreAuthenticatedAnonymousFilter implements PreAuthenficationFilter {
private final DdiSecurityProperties ddiSecurityConfiguration;
/**
* @param ddiSecurityConfiguration
* the security configuration which holds the configuration if
* anonymous is enabled or not
*/
public ControllerPreAuthenticatedAnonymousFilter(final DdiSecurityProperties ddiSecurityConfiguration) {
this.ddiSecurityConfiguration = ddiSecurityConfiguration;
}
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), secruityToken.getControllerId());
}
@Override
public boolean isEnable(final TenantSecurityToken secruityToken) {
return ddiSecurityConfiguration.getAuthentication().getAnonymous().isEnabled();
}
}

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
@@ -56,8 +56,8 @@ public class ControllerPreAuthenticatedGatewaySecurityTokenFilter extends Abstra
}
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecruityToken.AUTHORIZATION_HEADER);
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecurityToken.AUTHORIZATION_HEADER);
if ((authHeader != null) && authHeader.startsWith(GATEWAY_SECURITY_TOKEN_AUTH_SCHEME)) {
LOGGER.debug("found authorization header with scheme {} using target security token for authentication",
GATEWAY_SECURITY_TOKEN_AUTH_SCHEME);
@@ -71,7 +71,7 @@ public class ControllerPreAuthenticatedGatewaySecurityTokenFilter extends Abstra
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
final String gatewayToken = tenantAware.runAsTenant(secruityToken.getTenant(),
gatewaySecurityTokenKeyConfigRunner);
return new HeaderAuthentication(secruityToken.getControllerId(), gatewayToken);

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
@@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
/**
* An pre-authenticated processing filter which extracts the principal from a
* request URI and the credential from a request header in a the
* {@link TenantSecruityToken}.
* {@link TenantSecurityToken}.
*
*
*
@@ -75,7 +75,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
}
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
// retrieve the common name header and the authority name header from
// the http request and
// combine them together
@@ -97,7 +97,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
sslIssuerNameConfigTenantRunner);
String controllerId = secruityToken.getControllerId();
@@ -117,7 +117,7 @@ public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractCont
* It's ok if we find the the hash in any the trusted CA chain to accept
* this request for this tenant.
*/
private String getIssuerHashHeader(final TenantSecruityToken secruityToken, final String knownIssuerHash) {
private String getIssuerHashHeader(final TenantSecurityToken secruityToken, final String knownIssuerHash) {
// iterate over the headers until we get a null header.
int iHeader = 1;
String foundHash;

View File

@@ -109,6 +109,7 @@ public class PreAuthTokenSourceTrustAuthenticationProvider implements Authentica
if (successAuthentication) {
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
controllerAuthorities.add(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE));
controllerAuthorities.add(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE));
final PreAuthenticatedAuthenticationToken successToken = new PreAuthenticatedAuthenticationToken(principal,
credentials, controllerAuthorities);
successToken.setDetails(tokenDetails);

View File

@@ -8,7 +8,12 @@
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
/**
* Interface for Pre Authenfication.
@@ -25,7 +30,7 @@ public interface PreAuthenficationFilter {
* the secruity info
* @return <true> is enabled <false> diabled
*/
boolean isEnable(TenantSecruityToken secruityToken);
boolean isEnable(TenantSecurityToken secruityToken);
/**
* Extract the principal information from the current secruityToken.
@@ -34,7 +39,7 @@ public interface PreAuthenficationFilter {
* the secruityToken
* @return the extracted tenant and controller id
*/
HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecruityToken secruityToken);
HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecurityToken secruityToken);
/**
* Extract the principal credentials from the current secruityToken.
@@ -43,6 +48,18 @@ public interface PreAuthenficationFilter {
* the secruityToken
* @return the extracted tenant and controller id
*/
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecruityToken secruityToken);
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecurityToken secruityToken);
/**
* Allows to add additional authorities to the successful authenticated
* token.
*
* @return the authorities granted to the principal, or an empty collection
* if the token has not been authenticated. Never null.
* @see Authentication#getAuthorities()
*/
default Collection<GrantedAuthority> getSuccessfulAuthenticationAuthorities() {
return Collections.emptyList();
};
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.security;
import static org.fest.assertions.Assertions.assertThat;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* @author Michael Hirsch
*
*/
@Features("Unit Tests - Security")
@Stories("Exclude path aware shallow ETag filter")
@RunWith(MockitoJUnitRunner.class)
public class ControllerPreAuthenticatedAnonymousDownloadTest {
private ControllerPreAuthenticatedAnonymousDownload underTest;
@Mock
private TenantConfigurationManagement tenantConfigurationManagementMock;
@Mock
private TenantAware tenantAwareMock;
@Before
public void before() {
underTest = new ControllerPreAuthenticatedAnonymousDownload(tenantConfigurationManagementMock, tenantAwareMock,
new SystemSecurityContext(tenantAwareMock));
}
@Test
public void useCorrectTenantConfiguationKey() {
assertThat(underTest.getTenantConfigurationKey()).as("Should be using the correct tenant configuration key")
.isEqualTo(underTest.getTenantConfigurationKey());
}
@Test
public void successfulAuthenticationAdditionalAuthoritiesForDownload() {
assertThat(underTest.getSuccessfulAuthenticationAuthorities())
.as("Additional authorities should be containing the download anonymous role")
.contains(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_DOWNLOAD_ROLE));
}
}

View File

@@ -30,9 +30,6 @@ import com.vaadin.ui.VerticalLayout;
/**
* View to configure the authentication mode.
*
*
*
*/
@SpringComponent
@ViewScope

View File

@@ -14,8 +14,8 @@ import java.util.List;
import com.vaadin.ui.CustomComponent;
/**
* base class for all configuration views. This class implements the logic for
* the handling of the
* Base class for all configuration views. This class implements the logic for
* the handling of the configurations in a consistent way.
*
*/
public abstract class BaseConfigurationView extends CustomComponent implements ConfigurationGroup {

View File

@@ -11,9 +11,8 @@ package org.eclipse.hawkbit.ui.tenantconfiguration;
import com.vaadin.ui.Component;
/**
*
*
*
* Interface that all system configurations have to implement to save and undo
* their customized changes.
*/
public interface ConfigurationGroup extends Component, ConfigurationItem {

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.ui.tenantconfiguration;
import java.io.Serializable;
/**
* represents an configurationItem, which can be modified by the user
* Represents an configurationItem, which can be modified by the user
*/
public interface ConfigurationItem {

View File

@@ -33,9 +33,6 @@ import com.vaadin.ui.VerticalLayout;
/**
* Default DistributionSet Panel.
*
*
*
*/
@SpringComponent
@ViewScope

View File

@@ -0,0 +1,119 @@
/**
* 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.ui.tenantconfiguration;
import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIComponetIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
/**
* View to enable anonymous download.
*/
@SpringComponent
@ViewScope
public class DownloadAnonymousConfigurationView extends BaseConfigurationView
implements ConfigurationItem.ConfigurationItemChangeListener {
private static final String DIST_CHECKBOX_STYLE = "dist-checkbox-style";
private static final long serialVersionUID = 1L;
@Autowired
private I18N i18n;
@Autowired
private transient TenantConfigurationManagement tenantConfigurationManagement;
boolean anonymousDownloadEnabled;
private CheckBox downloadAnonymousCheckBox;
/**
* Initialize Default Download Anonymous layout.
*/
@PostConstruct
public void init() {
final TenantConfigurationValue<Boolean> value = tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED, Boolean.class);
anonymousDownloadEnabled = value.getValue();
final Panel rootPanel = new Panel();
rootPanel.setSizeFull();
rootPanel.addStyleName("config-panel");
final VerticalLayout vLayout = new VerticalLayout();
vLayout.setMargin(true);
vLayout.setSizeFull();
final Label headerDisSetType = new Label(i18n.get("enonymous.download.title"));
headerDisSetType.addStyleName("config-panel-header");
vLayout.addComponent(headerDisSetType);
final GridLayout gridLayout = new GridLayout(2, 1);
gridLayout.setSpacing(true);
gridLayout.setImmediate(true);
gridLayout.setColumnExpandRatio(1, 1.0F);
gridLayout.setSizeFull();
downloadAnonymousCheckBox = SPUIComponentProvider.getCheckBox("", DIST_CHECKBOX_STYLE, null, false, "");
downloadAnonymousCheckBox.setValue(anonymousDownloadEnabled);
downloadAnonymousCheckBox.addValueChangeListener(event -> configurationHasChanged());
downloadAnonymousCheckBox.setId(SPUIComponetIdProvider.SYSTEM_CONFIGURATION_ANONYMOUS_DOWNLOAD_CHECKBOX);
gridLayout.addComponent(downloadAnonymousCheckBox);
final Label configurationLabel = SPUIComponentProvider.getLabel(i18n.get("enonymous.download.label"),
SPUILabelDefinitions.SP_LABEL_SIMPLE);
gridLayout.addComponent(configurationLabel);
gridLayout.setComponentAlignment(configurationLabel, Alignment.MIDDLE_LEFT);
vLayout.addComponent(gridLayout);
rootPanel.setContent(vLayout);
setCompositionRoot(rootPanel);
}
@Override
public void configurationHasChanged() {
anonymousDownloadEnabled = downloadAnonymousCheckBox.getValue();
notifyConfigurationChanged();
}
@Override
public void save() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
downloadAnonymousCheckBox.getValue());
}
@Override
public void undo() {
final TenantConfigurationValue<Boolean> value = tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED, Boolean.class);
anonymousDownloadEnabled = value.getValue();
downloadAnonymousCheckBox.setValue(anonymousDownloadEnabled);
}
}

View File

@@ -29,8 +29,6 @@ import com.vaadin.ui.VerticalLayout;
/**
* View to configure the polling interval and the overdue time.
*
*
*/
@SpringComponent
@ViewScope

View File

@@ -38,9 +38,6 @@ import com.vaadin.ui.VerticalLayout;
/**
* Main UI for the system configuration view.
*
*
*
*/
@SpringView(name = TenantConfigurationDashboardView.VIEW_NAME, ui = HawkbitUI.class)
@ViewScope
@@ -58,6 +55,9 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
@Autowired
private PollingConfigurationView pollingConfigurationView;
@Autowired
private DownloadAnonymousConfigurationView downloadAnonymousConfigurationView;
@Autowired
private I18N i18n;
@@ -73,13 +73,14 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
private final List<ConfigurationGroup> configurationViews = new ArrayList<>();
/**
* init method adds all Configuration Views to the list of Views.
* Init method adds all Configuration Views to the list of Views.
*/
@PostConstruct
public void init() {
configurationViews.add(defaultDistributionSetTypeLayout);
configurationViews.add(authenticationConfigurationView);
configurationViews.add(pollingConfigurationView);
configurationViews.add(downloadAnonymousConfigurationView);
}
@Override
@@ -154,13 +155,6 @@ public class TenantConfigurationDashboardView extends CustomComponent implements
undoConfigurationBtn.setEnabled(false);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.hawkbit.server.ui.tenantconfiguration.ConfigurationGroup.
* ConfigurationGroupChangeListener #configurationChanged()
*/
@Override
public void configurationHasChanged() {
saveConfigurationBtn.setEnabled(true);

View File

@@ -21,9 +21,6 @@ import com.vaadin.server.Resource;
/**
* Menu item for system configuration view.
*
*
*
*/
@Component
@Order(700)

View File

@@ -26,16 +26,13 @@ import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;
/**
*
*
* This class represents the UI item for the certificate authenticated by an
* reverse proxy in the authentication configuration view.
*/
@SpringComponent
@ViewScope
public class CertificateAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem {
/**
*
*/
private static final long serialVersionUID = 1L;
@Autowired
@@ -59,7 +56,7 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
}
/**
* init mehotd called by spring.
* Init mehotd called by spring.
*/
@PostConstruct
public void init() {
@@ -94,12 +91,6 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.ui.tenantconfiguration.
* TenantConfigurationItem# configEnable()
*/
@Override
public void configEnable() {
if (!configurationEnabled) {
@@ -110,12 +101,6 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
setDetailVisible(true);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.ui.tenantconfiguration.
* TenantConfigurationItem# configDisable()
*/
@Override
public void configDisable() {
if (configurationEnabled) {
@@ -125,11 +110,6 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
setDetailVisible(false);
}
/*
* (non-Javadoc)
*
* @see hawkbit.server.ui.tenantconfiguration.TenantConfigurationItem#save()
*/
@Override
public void save() {
if (configurationEnabledChange) {
@@ -142,11 +122,6 @@ public class CertificateAuthenticationConfigurationItem extends AbstractAuthenti
}
}
/*
* (non-Javadoc)
*
* @see hawkbit.server.ui.tenantconfiguration.TenantConfigurationItem#undo()
*/
@Override
public void undo() {
configurationEnabledChange = false;

View File

@@ -30,16 +30,13 @@ import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;
/**
*
*
* This class represents the UI item for the gateway security token section in
* the authentication configuration view.
*/
@SpringComponent
@ViewScope
public class GatewaySecurityTokenAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem {
/**
*
*/
private static final long serialVersionUID = 1L;
@Autowired
@@ -70,7 +67,7 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
}
/**
* init mehotd called by spring.
* Init mehotd called by spring.
*/
@PostConstruct
public void init() {
@@ -135,12 +132,6 @@ public class GatewaySecurityTokenAuthenticationConfigurationItem extends Abstrac
notifyConfigurationChanged();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.ui.tenantconfiguration.
* TenantConfigurationItem# configEnable()
*/
@Override
public void configEnable() {
if (!configurationEnabled) {

View File

@@ -19,17 +19,13 @@ import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
/**
*
*
*
* This class represents the UI item for the target security token section in
* the authentication configuration view.
*/
@SpringComponent
@ViewScope
public class TargetSecurityTokenAuthenticationConfigurationItem extends AbstractAuthenticationTenantConfigurationItem {
/**
*
*/
private static final long serialVersionUID = 1L;
@Autowired
@@ -49,7 +45,7 @@ public class TargetSecurityTokenAuthenticationConfigurationItem extends Abstract
}
/**
* init mehotd called by spring.
* Init mehotd called by spring.
*/
@PostConstruct
public void init() {
@@ -57,12 +53,6 @@ public class TargetSecurityTokenAuthenticationConfigurationItem extends Abstract
configurationEnabled = isConfigEnabled();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.ui.tenantconfiguration.
* TenantConfigurationItem# configEnable()
*/
@Override
public void configEnable() {
if (!configurationEnabled) {
@@ -71,12 +61,6 @@ public class TargetSecurityTokenAuthenticationConfigurationItem extends Abstract
configurationEnabled = true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.ui.tenantconfiguration.
* TenantConfigurationItem# configDisable()
*/
@Override
public void configDisable() {
if (configurationEnabled) {

View File

@@ -501,6 +501,11 @@ public final class SPUIComponetIdProvider {
*/
public static final String SYSTEM_CONFIGURATION_CANCEL = "system.configuration.cancel";
/**
* Id of the anonymous download checkbox.
*/
public static final String SYSTEM_CONFIGURATION_ANONYMOUS_DOWNLOAD_CHECKBOX = "system.configuration.anonymous.download.checkbox";
/**
* Id of maximize/minimize icon of table - Software module table.
*/
@@ -827,7 +832,7 @@ public final class SPUIComponetIdProvider {
* Rollout status label id.
*/
public static final String ROLLOUT_STATUS_LABEL_ID = "rollout.status.id";
/**
* Rollout group status label id.
*/
@@ -867,12 +872,12 @@ public final class SPUIComponetIdProvider {
* Rollout group targets count message label.
*/
public static final String ROLLOUT_GROUP_TARGET_LABEL = "rollout.group.target.label";
/**
* Action confirmation popup id.
*/
public static final String CONFIRMATION_POPUP_ID = "action.confirmation.popup.id";
/**
* Validation status icon .
*/

View File

@@ -401,6 +401,8 @@ configuration.polling.title=Polling Configuration
configuration.polling.time=Polling Time
configuration.polling.overduetime=Polling Overdue Time
configuration.polling.custom.value=use a custom value
enonymous.download.title=Anonymous download
enonymous.download.label=Allow anonymous download. If you enable this option the download server will accept anonymous download requests.
#Calendar
calendar.year=year

View File

@@ -389,6 +389,8 @@ configuration.defaultdistributionset.select.label=Wahl des default Distribution
configuration.savebutton.tooltip=Konfigurationen speichern
configuration.cancellbutton.tooltip=Konfigurationen zur<75>cksetzen
configuration.authentication.title=Authentifikationseinstellungen
enonymous.download.title=Anonymes herunterladen
enonymous.download.label=Erlaube anonymes herunterladen. When diese option aktivert ist, wird der download server anonyme download anfragen erlauben.
#Calendar
calendar.year=Jahr
calendar.years=Jahre

View File

@@ -383,6 +383,8 @@ configuration.authentication.title=Authentication Configuration
controller.polling.title=Polling Configuration
controller.polling.time=Polling Time
controller.polling.overduetime=Polling Overdue Time
enonymous.download.title=Anonymous download
enonymous.download.label=Allow anonymous download. If you enable this option the download server will accept anonymous download requests.
#Calendar
calendar.year=year