Initial check in accordance with Parallel IP
This commit is contained in:
41
hawkbit-security-integration/pom.xml
Executable file
41
hawkbit-security-integration/pom.xml
Executable file
@@ -0,0 +1,41 @@
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>hawkbit-security-integration</artifactId>
|
||||
<name>hawkBit :: Security Integration</name>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-dmf-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -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 org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An abstraction for all controller based security. Check if the tenant
|
||||
* configuration is enabled.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractControllerAuthenticationFilter implements PreAuthenficationFilter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractControllerAuthenticationFilter.class);
|
||||
|
||||
protected final SystemManagement systemManagement;
|
||||
protected final TenantAware tenantAware;
|
||||
private final SecurityConfigurationKeyTenantRunner configurationKeyTenantRunner;
|
||||
|
||||
protected AbstractControllerAuthenticationFilter(final SystemManagement systemManagement,
|
||||
final TenantAware tenantAware) {
|
||||
this.systemManagement = systemManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.configurationKeyTenantRunner = new SecurityConfigurationKeyTenantRunner();
|
||||
}
|
||||
|
||||
protected abstract TenantConfigurationKey getTenantConfigurationKey();
|
||||
|
||||
@Override
|
||||
public boolean isEnable(final TenantSecruityToken secruityToken) {
|
||||
return tenantAware.runAsTenant(secruityToken.getTenant(), configurationKeyTenantRunner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecruityToken secruityToken);
|
||||
|
||||
@Override
|
||||
public abstract HeaderAuthentication getPreAuthenticatedCredentials(TenantSecruityToken secruityToken);
|
||||
|
||||
private final class SecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<Boolean> {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
LOGGER.trace("retrieving configuration value for configuration key {}", getTenantConfigurationKey());
|
||||
return systemManagement.getConfigurationValue(getTenantConfigurationKey(), Boolean.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.security;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecruityToken;
|
||||
|
||||
/**
|
||||
* A Filter for device which download via coap.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class CoapAnonymousPreAuthenticatedFilter implements PreAuthenficationFilter {
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
|
||||
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecruityToken.COAP_TOKEN_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
|
||||
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecruityToken.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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.TenantSecruityToken;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
/**
|
||||
* An pre-authenticated processing filter which extracts (if enabled through
|
||||
* configuration) the possibility to authenticate a target based on its target
|
||||
* security-token with the {@code Authorization} HTTP header.
|
||||
* {@code Example Header: Authorization: TargetToken
|
||||
* 5d8fSD54fdsFG98DDsa.}
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractControllerAuthenticationFilter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerPreAuthenticateSecurityTokenFilter.class);
|
||||
private static final String TARGET_SECURITY_TOKEN_AUTH_SCHEME = "TargetToken ";
|
||||
private static final int OFFSET_TARGET_TOKEN = TARGET_SECURITY_TOKEN_AUTH_SCHEME.length();
|
||||
|
||||
private final ControllerManagement controllerManagement;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param systemManagement
|
||||
* the system management service to retrieve configuration
|
||||
* properties
|
||||
* @param controllerManagement
|
||||
* the controller management to retrieve the specific target
|
||||
* security token to verify
|
||||
* @param tenantAware
|
||||
* the tenant aware service to get configuration for the specific
|
||||
* tenant
|
||||
*/
|
||||
public ControllerPreAuthenticateSecurityTokenFilter(final SystemManagement systemManagement,
|
||||
final ControllerManagement controllerManagement, final TenantAware tenantAware) {
|
||||
super(systemManagement, tenantAware);
|
||||
this.controllerManagement = controllerManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
|
||||
final String authHeader = secruityToken.getHeader(TenantSecruityToken.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);
|
||||
return new HeaderAuthentication(secruityToken.getControllerId(), authHeader.substring(OFFSET_TARGET_TOKEN));
|
||||
}
|
||||
LOGGER.debug(
|
||||
"security token filter is enabled but requst does not contain either the necessary path variables {} or the authorization header with scheme {}",
|
||||
secruityToken, TARGET_SECURITY_TOKEN_AUTH_SCHEME);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
|
||||
final String securityToken = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||
new GetSecurityTokenTenantRunner(secruityToken.getTenant(), secruityToken.getControllerId()));
|
||||
return new HeaderAuthentication(secruityToken.getControllerId(), securityToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TenantConfigurationKey getTenantConfigurationKey() {
|
||||
return TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED;
|
||||
}
|
||||
|
||||
private final class GetSecurityTokenTenantRunner implements TenantAware.TenantRunner<String> {
|
||||
|
||||
private final String controllerId;
|
||||
private final String tenant;
|
||||
|
||||
private GetSecurityTokenTenantRunner(final String tenant, final String controllerId) {
|
||||
this.tenant = tenant;
|
||||
this.controllerId = controllerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String run() {
|
||||
LOGGER.trace("retrieving security token for controllerId {}", controllerId);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
try {
|
||||
SecurityContextHolder.setContext(getSecurityTokenReadContext());
|
||||
return controllerManagement.getSecurityTokenByControllerId(controllerId);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityContext getSecurityTokenReadContext() {
|
||||
final SecurityContextImpl securityContextImpl = new SecurityContextImpl();
|
||||
securityContextImpl.setAuthentication(getSecurityTokenReadAuthentication());
|
||||
return securityContextImpl;
|
||||
}
|
||||
|
||||
private Authentication getSecurityTokenReadAuthentication() {
|
||||
final AnonymousAuthenticationToken anonymousAuthenticationToken = new AnonymousAuthenticationToken(
|
||||
"anonymous-read-security-token", "anonymous", com.google.common.collect.Lists
|
||||
.newArrayList(new SimpleGrantedAuthority(SpPermission.READ_TARGET_SEC_TOKEN)));
|
||||
anonymousAuthenticationToken.setDetails(new TenantAwareAuthenticationDetails(tenant, true));
|
||||
return anonymousAuthenticationToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.TenantSecruityToken;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An pre-authenticated processing filter which extracts (if enabled through
|
||||
* configuration) the possibility to authenticate a target based through a
|
||||
* gateway security token. This is commonly used for targets connected
|
||||
* indirectly via a gateway. This gateway controls multiple targets under the
|
||||
* gateway security token which can be set via the {@code TenantSecruityToken}
|
||||
* header. {@code Example Header: Authorization: GatewayToken
|
||||
* 5d8fSD54fdsFG98DDsa.}
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ControllerPreAuthenticatedGatewaySecurityTokenFilter extends AbstractControllerAuthenticationFilter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory
|
||||
.getLogger(ControllerPreAuthenticatedGatewaySecurityTokenFilter.class);
|
||||
private static final String GATEWAY_SECURITY_TOKEN_AUTH_SCHEME = "GatewayToken ";
|
||||
private static final int OFFSET_GATEWAY_TOKEN = GATEWAY_SECURITY_TOKEN_AUTH_SCHEME.length();
|
||||
|
||||
private final GetGatewaySecurityConfigurationKeyTenantRunner gatewaySecurityTokenKeyConfigRunner = new GetGatewaySecurityConfigurationKeyTenantRunner();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param systemManagement
|
||||
* the system management service to retrieve configuration
|
||||
* properties
|
||||
* @param tenantAware
|
||||
* the tenant aware service to get configuration for the specific
|
||||
* tenant
|
||||
*/
|
||||
public ControllerPreAuthenticatedGatewaySecurityTokenFilter(final SystemManagement systemManagement,
|
||||
final TenantAware tenantAware) {
|
||||
super(systemManagement, tenantAware);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
|
||||
final String authHeader = secruityToken.getHeader(TenantSecruityToken.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);
|
||||
return new HeaderAuthentication(secruityToken.getControllerId(),
|
||||
authHeader.substring(OFFSET_GATEWAY_TOKEN));
|
||||
}
|
||||
LOGGER.debug(
|
||||
"security token filter is enabled but request does not contain either the necessary secruity token {} or the authorization header with scheme {}",
|
||||
secruityToken, GATEWAY_SECURITY_TOKEN_AUTH_SCHEME);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
|
||||
final String gatewayToken = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||
gatewaySecurityTokenKeyConfigRunner);
|
||||
return new HeaderAuthentication(secruityToken.getControllerId(), gatewayToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TenantConfigurationKey getTenantConfigurationKey() {
|
||||
return TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED;
|
||||
}
|
||||
|
||||
private final class GetGatewaySecurityConfigurationKeyTenantRunner implements TenantAware.TenantRunner<String> {
|
||||
|
||||
@Override
|
||||
public String run() {
|
||||
LOGGER.trace("retrieving configuration value for configuration key {}",
|
||||
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY);
|
||||
return systemManagement.getConfigurationValue(
|
||||
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* 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.TenantSecruityToken;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.slf4j.Logger;
|
||||
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}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ControllerPreAuthenticatedSecurityHeaderFilter extends AbstractControllerAuthenticationFilter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerPreAuthenticatedSecurityHeaderFilter.class);
|
||||
private static final Logger LOG_SECURITY_AUTH = LoggerFactory.getLogger("server-security.authentication");
|
||||
|
||||
private final GetSecurityAuthorityNameTenantRunner sslIssuerNameConfigTenantRunner = new GetSecurityAuthorityNameTenantRunner();
|
||||
// Example Headers with Cert Information
|
||||
// Clientip: 217.24.201.180
|
||||
// X-Forwarded-Proto: https
|
||||
// X-Ssl-Client-Cn: my.name
|
||||
// X-Ssl-Client-Dn:
|
||||
// CN=my.name,CN=O,CN=R,CN=DE,CN=BOSCH,CN=pki,DC=bosch,DC=com
|
||||
// X-Ssl-Client-Hash: 7f:87:cb:b5:9c:e0:c5:0a:1a:a6:57:69:0f:ca:0a:95
|
||||
// X-Ssl-Client-Notafter: Dec 18 08:02:45 2017 GMT
|
||||
// X-Ssl-Client-Notbefore: Dec 18 07:32:45 2014 GMT
|
||||
// X-Ssl-Client-Verify: ok
|
||||
// X-Ssl-Issuer: CN=Bosch-CA1-DE,CN=PKI,DC=Bosch,DC=com
|
||||
// X-Ssl-Issuer-Dn-1: CN=Bosch-CA-DE,CN=PKI,DC=Bosch,DC=com
|
||||
// X-Ssl-Issuer-Hash-1: ae:11:f5:6a:0a:e8:74:50:81:0e:0c:37:ec:c5:22:fc
|
||||
private final String caCommonNameHeader;
|
||||
// the X-Ssl-Issuer-Hash basic header header which contains the x509
|
||||
// fingerprint hash, this
|
||||
// header exists multiple in the request for all trusted chain.
|
||||
private final String sslIssuerHashBasicHeader;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param caCommonNameHeader
|
||||
* the http-header which holds the common-name of the certificate
|
||||
* @param caAuthorityNameHeader
|
||||
* the http-header which holds the ca-authority name of the
|
||||
* certificate
|
||||
* @param systemManagement
|
||||
* the system management service to retrieve configuration
|
||||
* properties to check if the header authentication is enabled
|
||||
* for this tenant
|
||||
* @param tenantAware
|
||||
* the tenant aware service to get configuration for the specific
|
||||
* tenant
|
||||
*/
|
||||
public ControllerPreAuthenticatedSecurityHeaderFilter(final String caCommonNameHeader,
|
||||
final String caAuthorityNameHeader, final SystemManagement systemManagement,
|
||||
final TenantAware tenantAware) {
|
||||
super(systemManagement, tenantAware);
|
||||
this.caCommonNameHeader = caCommonNameHeader;
|
||||
this.sslIssuerHashBasicHeader = caAuthorityNameHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecruityToken secruityToken) {
|
||||
// retrieve the common name header and the authority name header from
|
||||
// the http request and
|
||||
// combine them together
|
||||
final String commonNameValue = secruityToken.getHeader(caCommonNameHeader);
|
||||
final String knownSslIssuerConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||
sslIssuerNameConfigTenantRunner);
|
||||
final String sslIssuerHashValue = getIssuerHashHeader(secruityToken, knownSslIssuerConfigurationValue);
|
||||
if (commonNameValue != null && LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Found commonNameHeader {}={}, using as credentials", caCommonNameHeader, commonNameValue);
|
||||
}
|
||||
if (sslIssuerHashValue != null && LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Found sslIssuerHash ****, using as credentials for tenant {}", secruityToken.getTenant());
|
||||
}
|
||||
|
||||
if (commonNameValue != null && sslIssuerHashValue != null) {
|
||||
return new HeaderAuthentication(commonNameValue, sslIssuerHashValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecruityToken secruityToken) {
|
||||
final String authorityNameConfigurationValue = tenantAware.runAsTenant(secruityToken.getTenant(),
|
||||
sslIssuerNameConfigTenantRunner);
|
||||
String controllerId = secruityToken.getControllerId();
|
||||
// in case of legacy download artifact, the controller ID is not in the
|
||||
// URL path, so then
|
||||
// we just use the common name header
|
||||
if (controllerId == null || "anonymous".equals(controllerId)) {
|
||||
controllerId = secruityToken.getHeader(caCommonNameHeader);
|
||||
}
|
||||
|
||||
return new HeaderAuthentication(controllerId, authorityNameConfigurationValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the {@link #sslIssuerHashBasicHeader} basic header
|
||||
* {@code X-Ssl-Issuer-Hash-%d} and try to finds the same hash as known.
|
||||
* 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) {
|
||||
// iterate over the headers until we get a null header.
|
||||
int iHeader = 1;
|
||||
String foundHash = null;
|
||||
while ((foundHash = secruityToken.getHeader(String.format(sslIssuerHashBasicHeader, iHeader))) != null) {
|
||||
if (foundHash.equals(knownIssuerHash)) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Found matching ssl issuer hash at position {}", iHeader);
|
||||
}
|
||||
return foundHash;
|
||||
}
|
||||
iHeader++;
|
||||
}
|
||||
LOG_SECURITY_AUTH.debug(
|
||||
"Certifacte request but no matching hash found in headers {} for common name {} in request",
|
||||
sslIssuerHashBasicHeader, secruityToken.getHeader(caCommonNameHeader));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TenantConfigurationKey getTenantConfigurationKey() {
|
||||
return TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED;
|
||||
}
|
||||
|
||||
private final class GetSecurityAuthorityNameTenantRunner implements TenantAware.TenantRunner<String> {
|
||||
@Override
|
||||
public String run() {
|
||||
return systemManagement.getConfigurationValue(
|
||||
TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_AUTHORITY_NAME, String.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The authentication principal and credentials object which holds the
|
||||
* controller-id and the authority name from the http-headers as principal or
|
||||
* from the http-url and tenant configuration for the credentials.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class HeaderAuthentication {
|
||||
private final String controllerId;
|
||||
private final String headerAuth;
|
||||
|
||||
HeaderAuthentication(final String controllerId, final String headerAuth) {
|
||||
this.controllerId = controllerId;
|
||||
this.headerAuth = headerAuth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {// NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((controllerId == null) ? 0 : controllerId.hashCode());
|
||||
result = prime * result + ((headerAuth == null) ? 0 : headerAuth.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {// NOSONAR - as this is generated
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final HeaderAuthentication other = (HeaderAuthentication) obj;
|
||||
if (controllerId == null) {
|
||||
if (other.controllerId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!controllerId.equals(other.controllerId)) {
|
||||
return false;
|
||||
}
|
||||
if (headerAuth == null) {
|
||||
if (other.headerAuth != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!headerAuth.equals(other.headerAuth)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// only the controller ID because the principal is stored as string for
|
||||
// audit information
|
||||
// etc.
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
|
||||
/**
|
||||
* An spring authentication provider which supportes authentication tokens of
|
||||
* type {@link PreAuthenticatedAuthenticationToken} created by the
|
||||
* {@link ControllerPreAuthenticatedSecurityHeaderFilter}.
|
||||
*
|
||||
* Addtionally to the authentication token providing the principal and the
|
||||
* credentials which must be match, this authentication provider can also check
|
||||
* the remote IP address of the request.
|
||||
*
|
||||
* E.g. The request path is /controller/v1/{controllerId} then the controllerId
|
||||
* in the path is the principal. The credentials are the extracted information
|
||||
* from e.g. a certificate provided by an reverse proxy. Due this request is
|
||||
* only allowed from a specific source address this authentication manager can
|
||||
* also check the remote IP address of the request.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class PreAuthTokenSourceTrustAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PreAuthenticatedAuthenticationToken.class);
|
||||
private final List<String> authorizedSourceIps;
|
||||
|
||||
/**
|
||||
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider without
|
||||
* source IPs, which disables the source IP check.
|
||||
*/
|
||||
public PreAuthTokenSourceTrustAuthenticationProvider() {
|
||||
authorizedSourceIps = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
|
||||
* source IP addresses which are trusted and should be checked against the
|
||||
* request remote IP address.
|
||||
*
|
||||
* @param authorizedSourceIps
|
||||
* a list of IP addresses.
|
||||
*/
|
||||
public PreAuthTokenSourceTrustAuthenticationProvider(final List<String> authorizedSourceIps) {
|
||||
this.authorizedSourceIps = authorizedSourceIps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PreAuthTokenSourceTrustAuthenticationProvider with given
|
||||
* source IP addresses which are trusted and should be checked against the
|
||||
* request remote IP address.
|
||||
*
|
||||
* @param authorizedSourceIps
|
||||
* a list of IP addresses.
|
||||
*/
|
||||
public PreAuthTokenSourceTrustAuthenticationProvider(final String... authorizedSourceIps) {
|
||||
this.authorizedSourceIps = new ArrayList<>();
|
||||
for (final String ip : authorizedSourceIps) {
|
||||
this.authorizedSourceIps.add(ip);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(final Authentication authentication) {
|
||||
if (!supports(authentication.getClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean successAuthentication = false;
|
||||
final PreAuthenticatedAuthenticationToken token = (PreAuthenticatedAuthenticationToken) authentication;
|
||||
final Object credentials = token.getCredentials();
|
||||
final Object principal = token.getPrincipal();
|
||||
final Object tokenDetails = token.getDetails();
|
||||
|
||||
if (principal == null) {
|
||||
throw new BadCredentialsException("The provided principal and credentials are not match");
|
||||
}
|
||||
|
||||
// check if principal equals credentials because we want to check if
|
||||
// e.g. controllerId
|
||||
// containing in the URL equals the controllerId in the special header
|
||||
// set by the reverse
|
||||
// proxy which extracted the CN from the certificate
|
||||
if (principal.equals(credentials)) {
|
||||
successAuthentication = checkSourceIPAddressIfNeccessary(tokenDetails);
|
||||
}
|
||||
|
||||
if (successAuthentication) {
|
||||
final Collection<GrantedAuthority> controllerAuthorities = new ArrayList<>();
|
||||
controllerAuthorities.add(new SimpleGrantedAuthority(SpringEvalExpressions.CONTROLLER_ROLE));
|
||||
final PreAuthenticatedAuthenticationToken successToken = new PreAuthenticatedAuthenticationToken(principal,
|
||||
credentials, controllerAuthorities);
|
||||
successToken.setDetails(tokenDetails);
|
||||
return successToken;
|
||||
}
|
||||
|
||||
throw new BadCredentialsException("The provided principal and credentials are not match");
|
||||
}
|
||||
|
||||
private boolean checkSourceIPAddressIfNeccessary(final Object tokenDetails) {
|
||||
boolean success = authorizedSourceIps == null;
|
||||
String remoteAddress = null;
|
||||
// controllerIds in URL path and request header are the same but is the
|
||||
// request coming
|
||||
// from a trustful source, like the reverse proxy.
|
||||
if (authorizedSourceIps != null) {
|
||||
if (!(tokenDetails instanceof TenantAwareWebAuthenticationDetails)) {
|
||||
// is not of type WebAuthenticationDetails, then we cannot
|
||||
// determine the remote address!
|
||||
LOGGER.error(
|
||||
"Cannot determine the controller remote-ip-address based on the given authentication token - {} , token details are not TenantAwareWebAuthenticationDetails! ",
|
||||
tokenDetails);
|
||||
success = false;
|
||||
} else {
|
||||
remoteAddress = ((TenantAwareWebAuthenticationDetails) tokenDetails).getRemoteAddress();
|
||||
if (authorizedSourceIps.contains(remoteAddress)) {
|
||||
// source ip matches the given pattern -> authenticated
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new InsufficientAuthenticationException("The remote source IP address " + remoteAddress
|
||||
+ " is not in the list of trusted IP addresses " + authorizedSourceIps);
|
||||
}
|
||||
|
||||
// no trusted IP check, because no authorizedSourceIPs configuration
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> authentication) {
|
||||
return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.TenantSecruityToken;
|
||||
|
||||
/**
|
||||
* Interface for Pre Authenfication.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface PreAuthenficationFilter {
|
||||
|
||||
/**
|
||||
* Check if the filter is enabled.
|
||||
*
|
||||
* @param secruityToken
|
||||
* the secruity info
|
||||
* @return <true> is enabled <false> diabled
|
||||
*/
|
||||
boolean isEnable(TenantSecruityToken secruityToken);
|
||||
|
||||
/**
|
||||
* Extract the principal information from the current secruityToken.
|
||||
*
|
||||
* @param secruityToken
|
||||
* the secruityToken
|
||||
* @return the extracted tenant and controller id
|
||||
*/
|
||||
HeaderAuthentication getPreAuthenticatedPrincipal(TenantSecruityToken secruityToken);
|
||||
|
||||
/**
|
||||
* Extract the principal credentials from the current secruityToken.
|
||||
*
|
||||
* @param secruityToken
|
||||
* the secruityToken
|
||||
* @return the extracted tenant and controller id
|
||||
*/
|
||||
HeaderAuthentication getPreAuthenticatedCredentials(TenantSecruityToken secruityToken);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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 javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
|
||||
/**
|
||||
* Extends the {@link TenantAwareAuthenticationDetails} to web information to
|
||||
* retrieve also e.g. the remoteAddress of the {@link HttpServletRequest} when
|
||||
* authenticating the requested controller e.g. based on the security header and
|
||||
* trusted IP address we need the remote address of the http request to verify
|
||||
* the e.g. the reverse proxy is trusted and allowed to set the header.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TenantAwareWebAuthenticationDetails extends TenantAwareAuthenticationDetails {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final String remoteAddress;
|
||||
|
||||
/**
|
||||
* @param tenant
|
||||
* the current tenant
|
||||
* @param remoteAddress
|
||||
* the remote address of this web request
|
||||
* @param controller
|
||||
* {@code true} indicates this is an controller HTTP request
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
public TenantAwareWebAuthenticationDetails(final String tenant, final String remoteAddress,
|
||||
final boolean controller) {
|
||||
super(tenant, controller);
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the remoteAddress
|
||||
*/
|
||||
public String getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user