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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -30,9 +30,6 @@ import com.vaadin.ui.VerticalLayout;
|
||||
|
||||
/**
|
||||
* View to configure the authentication mode.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@SpringComponent
|
||||
@ViewScope
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -33,9 +33,6 @@ import com.vaadin.ui.VerticalLayout;
|
||||
|
||||
/**
|
||||
* Default DistributionSet Panel.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@SpringComponent
|
||||
@ViewScope
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,8 +29,6 @@ import com.vaadin.ui.VerticalLayout;
|
||||
|
||||
/**
|
||||
* View to configure the polling interval and the overdue time.
|
||||
*
|
||||
*
|
||||
*/
|
||||
@SpringComponent
|
||||
@ViewScope
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,9 +21,6 @@ import com.vaadin.server.Resource;
|
||||
|
||||
/**
|
||||
* Menu item for system configuration view.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@Order(700)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 .
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user