Initial check in accordance with Parallel IP

This commit is contained in:
Kai Zimmermann
2016-01-21 13:18:55 +01:00
parent c5e4f1139f
commit 7497ab61ed
763 changed files with 114508 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
/**
* 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;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
/**
* An {@link ShallowEtagHeaderFilter} with exclusion paths to exclude some paths
* where no ETag header should be generated due that calculating the ETag is an
* expensive operation and the response output need to be copied in memory which
* should be excluded in case of artifact downloads which could be big of size.
*
*
*
*
*/
public class ExcludePathAwareShallowETagFilter extends ShallowEtagHeaderFilter {
private final String[] excludeAntPaths;
private final AntPathMatcher antMatcher;
/**
* @param excludeAntPaths
*/
public ExcludePathAwareShallowETagFilter(final String... excludeAntPaths) {
this.excludeAntPaths = excludeAntPaths;
this.antMatcher = new AntPathMatcher();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.filter.ShallowEtagHeaderFilter#doFilterInternal(
* javax.servlet.http .HttpServletRequest,
* javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
*/
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException {
final boolean shouldExclude = shouldExclude(request);
if (shouldExclude) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
private boolean shouldExclude(final HttpServletRequest request) {
for (final String pattern : excludeAntPaths) {
if (antMatcher.match(request.getContextPath() + pattern, request.getRequestURI())) {
// exclude this request from eTag filter
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.im.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
/**
* Indicates if the SP server runs in multi-tenancy mode. By means e.g. if a
* login screen needs to allow to specifiy the tenant to login.
*
* This can defere e.g. in case if the {@link AuthenticationProvider} allows
* {@link TenantUserPasswordAuthenticationToken} tokens or not.
*
*
*
*/
public interface MultitenancyIndicator {
/**
* @return {@code true} if multi-tenancy is supported, otherwise
* {@code false}.
*/
boolean isMultiTenancySupported();
}

View File

@@ -0,0 +1,98 @@
/**
* 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.im.authentication;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
/**
*
*
*/
public class PermissionService {
/**
* Checks if the given {@code permission} contains in the. In case no
* {@code context} is available {@code false} will be returned.
*
* @param permission
* the permission to check against the
* @return {@code true} if a is available and contains the given
* {@code permission}, otherwise {@code false}.
* @see SpPermission
*/
public boolean hasPermission(final String permission) {
final SecurityContext context = SecurityContextHolder.getContext();
if (context == null) {
return false;
}
final Authentication authentication = context.getAuthentication();
if (authentication == null) {
return false;
}
for (final GrantedAuthority authority : authentication.getAuthorities()) {
if (authority.getAuthority().equals(permission)) {
return true;
}
}
return false;
}
public List<String> getAllPermission() {
final List<String> permissions = new ArrayList<String>();
final SecurityContext context = SecurityContextHolder.getContext();
if (context == null) {
return permissions;
}
final Authentication authentication = context.getAuthentication();
if (authentication == null) {
return permissions;
}
authentication.getAuthorities().stream().forEach(authority -> permissions.add(authority.getAuthority()));
return permissions;
}
/**
* Checks if at least on permission of the given {@code permissions}
* contains in the . In case no {@code context} is available {@code false}
* will be returned.
*
* @param permissions
* the permissions to check against the
* @return {@code true} if a is available and contains the given
* {@code permission}, otherwise {@code false}.
* @see SpPermission
*/
public boolean hasAtLeastOnePermission(final List<String> permissions) {
final SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
final Authentication authentication = context.getAuthentication();
if (authentication != null) {
for (final GrantedAuthority authority : authentication.getAuthorities()) {
for (final String permission : permissions) {
if (authority.getAuthority().equals(permission)) {
return true;
}
}
}
}
}
return false;
}
}

View File

@@ -0,0 +1,283 @@
/**
* 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.im.authentication;
import java.lang.annotation.Target;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
/**
* <p>
* Software provisioning permissions that are technically available as
* {@link GrantedAuthority} based on the authenticated users identity context.
* </p>
*
* <p>
* The Permissions cover CRUD for two data areas of SP:<br/>
* <br/>
* XX_Target_CRUD which covers the following entities: {@link Target} entities
* including metadata, {@link TargetTag}s, {@link Action}s,
* {@link TargetRegistrationRule}s<br/>
* XX_Repository CRUD which covers: {@link DistributionSet}s,
* {@link SoftwareModule}s, DS Tags<br/>
* </p>
*
*
*
*
*
*/
public final class SpPermission {
/**
* Permission to read the targets from the
* {@link ProvisioningTargetRepository} including their meta information,
* {@link ProvisioningTargetFilter}s and target changing entities (
* {@link DistributionSetApplier} and {@link TargetRegistrationRule}). That
* corresponds in REST API to GET.
*/
public static final String READ_TARGET = "READ_TARGET";
/**
* Permission to read the target security token. The security token is
* security concerned and should be protected. So the combination
* {@link #READ_TARGET} and {@link #READ_TARGET_SEC_TOKEN} is necessary to
* able to read the security token of an target.
*/
public static final String READ_TARGET_SEC_TOKEN = "READ_TARGET_SECURITY_TOKEN";
/**
* Permission to change/edit/update targets in the
* {@link ProvisioningTargetRepository} including their meta information and
* or/relations or {@link DistributionSet} assignment,
* {@link ProvisioningTargetFilter}s and target changing entities (
* {@link DistributionSetApplier} and {@link TargetRegistrationRule}). That
* corresponds in REST API to POST.
*/
public static final String UPDATE_TARGET = "UPDATE_TARGET";
/**
* Permission to add new targets to the {@link ProvisioningTargetRepository}
* including their meta information and or/relations or
* {@link DistributionSet} assignment.That corresponds in REST API to PUT.
*/
public static final String CREATE_TARGET = "CREATE_TARGET";
/**
* Permission to delete targets in the {@link ProvisioningTargetRepository},
* {@link ProvisioningTargetFilter}s and target changing entities (
* {@link DistributionSetApplier} and {@link TargetRegistrationRule}). That
* corresponds in REST API to DELETE.
*/
public static final String DELETE_TARGET = "DELETE_TARGET";
/**
* Permission to read {@link DistributionSet}s and/or {@link OsPackage}s.
* That corresponds in REST API to GET.
*/
public static final String READ_REPOSITORY = "READ_REPOSITORY";
/**
* Permission to edit/update {@link DistributionSet}s including their
* {@link OsPackage} assignment and/or {@link OsPackage}s. That corresponds
* in REST API to POST.
*/
public static final String UPDATE_REPOSITORY = "UPDATE_REPOSITORY";
/**
* Permission to add {@link DistributionSet}s and/or {@link OsPackage}s to
* the repository. That corresponds in REST API to PUT.
*/
public static final String CREATE_REPOSITORY = "CREATE_REPOSITORY";
/**
* Permission to delete {@link DistributionSet}s and/or {@link OsPackage}s
* from the repository. That corresponds in REST API to DELETE.
*/
public static final String DELETE_REPOSITORY = "DELETE_REPOSITORY";
/**
* Permission to monitor the SP system. E.g. retrieving health, monitor
* checks through REST API provided by the spring actuator.
*/
public static final String SYSTEM_MONITOR = "SYSTEM_MONITOR";
/**
* Permission to retrieve diagnosis of the SP system. E.g. retrieving
* metrics, configuration through REST API provided by the spring actuator.
*/
public static final String SYSTEM_DIAG = "SYSTEM_DIAG";
/**
* Permission to administrate the system on a global, i.e. tenant
* independent scale. Thta inlcuds the deletion of tenants.
*/
public static final String SYSTEM_ADMIN = "SYSTEM_ADMIN";
/**
* Permission to download repository artifact of an software module.
*/
public static final String DOWNLOAD_REPOSITORY_ARTIFACT = "DOWNLOAD_REPOSITORY_ARTIFACT";
/**
* Permission to administrate the tenant settings.
*/
public static final String TENANT_CONFIGURATION = "TENANT_CONFIGURATION";
private SpPermission() {
// Constants only
}
/**
* Contains all the spring security evaluation expressions for the
* {@link PreAuthorize} annotation for method security.
* <p/>
* Examples:
* <p/>
* {@code
* hasRole([role]) Returns true if the current principal has the specified role.
* hasAnyRole([role1,role2]) Returns true if the current principal has any of the supplied roles (given as a comma-separated list of strings)
* principal Allows direct access to the principal object representing the current user
* authentication Allows direct access to the current Authentication object obtained from the SecurityContext
* permitAll Always evaluates to true
* denyAll Always evaluates to false
* isAnonymous() Returns true if the current principal is an anonymous user
* isRememberMe() Returns true if the current principal is a remember-me user
* isAuthenticated() Returns true if the user is not anonymous
* isFullyAuthenticated() Returns true if the user is not an anonymous or a remember-me user
* }
*
*
*
*
*/
public static final class SpringEvalExpressions {
/*
* Spring security eval expressions.
*/
private static final String HAS_AUTH_PREFIX = "hasAuthority('";
private static final String HAS_AUTH_SUFFIX = "')";
private static final String HAS_AUTH_AND = " and ";
/**
* The role which contains in the spring security context in case an
* controller is authenticated.
*/
public static final String CONTROLLER_ROLE = "ROLE_CONTROLLER";
/**
* The role which contains in the spring security context in case an
* controller is authenticated but only as anonymous.
*/
public static final String CONTROLLER_ROLE_ANONYMOUS = "ROLE_CONTROLLER_ANONYMOUS";
/**
* The spring security eval expression operator {@code or}.
*/
public static final String HAS_AUTH_OR = " or ";
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#UPDATE_TARGET}.
*/
public static final String HAS_AUTH_UPDATE_TARGET = HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#SYSTEM_ADMIN}.
*/
public static final String HAS_AUTH_SYSTEM_ADMIN = HAS_AUTH_PREFIX + SYSTEM_ADMIN + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_TARGET}.
*/
public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_TARGET}.
*/
public static final String HAS_AUTH_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DELETE_TARGET}.
*/
public static final String HAS_AUTH_DELETE_TARGET = HAS_AUTH_PREFIX + DELETE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} and
* {@link SpPermission#UPDATE_TARGET}.
*/
public static final String HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET = HAS_AUTH_PREFIX + READ_REPOSITORY
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY}.
*/
public static final String HAS_AUTH_CREATE_REPOSITORY = HAS_AUTH_PREFIX + CREATE_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DELETE_REPOSITORY}.
*/
public static final String HAS_AUTH_DELETE_REPOSITORY = HAS_AUTH_PREFIX + DELETE_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY}.
*/
public static final String HAS_AUTH_READ_REPOSITORY = HAS_AUTH_PREFIX + READ_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#UPDATE_REPOSITORY}.
*/
public static final String HAS_AUTH_UPDATE_REPOSITORY = HAS_AUTH_PREFIX + UPDATE_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} and
* {@link SpPermission#READ_TARGET}.
*/
public static final String HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET = HAS_AUTH_PREFIX + READ_REPOSITORY
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DOWNLOAD_REPOSITORY_ARTIFACT}.
*/
public static final String HAS_AUTH_DOWNLOAD_ARTIFACT = HAS_AUTH_PREFIX + DOWNLOAD_REPOSITORY_ARTIFACT
+ HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAnyRole expression to check if the spring
* 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
+ "')";
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY} and
* {@link SpPermission#CREATE_TARGET}.
*/
public static final String HAS_AUTH_CREATE_REPOSITORY_AND_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_REPOSITORY
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX;
private SpringEvalExpressions() {
// utility class
}
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.im.authentication;
import java.io.Serializable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
/**
* An authentication details object
* {@link AbstractAuthenticationToken#getDetails()} which is stored in the
* spring security authentication token details to transport the principal and
* tenant in the security context session.
*
*
*
*
*/
public class TenantAwareAuthenticationDetails implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String tenant;
private final boolean controller;
/**
* @param tenant
* the current tenant
* @param controller
* boolean flag to indicate if this authenticated token is a
* controller authentication. {@code true} in case of
* authenticated controller otherwise {@code false}
*/
public TenantAwareAuthenticationDetails(final String tenant, final boolean controller) {
this.tenant = tenant;
this.controller = controller;
}
/**
* @return the tenant
*/
public String getTenant() {
return tenant;
}
/**
* @return the controller
*/
public boolean isController() {
return controller;
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.im.authentication;
import java.util.List;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* The authentication token which transports the username, password and the
* tenant information for authentication.
*
*
*
*
*/
public class TenantUserPasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
/**
*
*/
private static final long serialVersionUID = 1L;
final Object tenant;
/**
*
* Creating a new {@link TenantUserPasswordAuthenticationToken} as
* {@link #isAuthenticated()} will return {@code false}.
*
* @param tenant
* the tenant to authenticate against
* @param principal
* the principal to authenticate
* @param credentials
* the credentials of the principal
*/
public TenantUserPasswordAuthenticationToken(final Object tenant, final Object principal,
final Object credentials) {
super(principal, credentials);
this.tenant = tenant;
}
/**
* Creating a new {@link TenantUserPasswordAuthenticationToken} as
* {@link #isAuthenticated()} will return {@code true}.
*
* @param tenant
* the tenant to authenticate against
* @param principal
* the principal to authenticate
* @param credentials
* the credentials of the principal
* @param authorities
* the principal's authorities
*/
public TenantUserPasswordAuthenticationToken(final Object tenant, final Object principal, final Object credentials,
final List<GrantedAuthority> authorities) {
super(principal, credentials, authorities);
this.tenant = tenant;
}
/**
* @return the tenant
*/
public Object getTenant() {
return tenant;
}
}

View File

@@ -0,0 +1,63 @@
/**
* 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.im.authentication;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Filter to integrate into the SP security filter-chain. The filter is called
* in any remote call through HTTP except the SP login screen. E.g. using the SP
* REST-API. To authenticate user e.g. using Basic-Authentication implement the
* {@link #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}
* method.
*
*
*
*/
public interface UserAuthenticationFilter {
/**
* @see Filter#init(FilterConfig)
*
* @param filterConfig
* the filter config
*/
void init(FilterConfig filterConfig) throws ServletException;
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*
* @param request
* the servlet request
* @param response
* the servlet response
* @param chain
* the filterchain
* @throws IOException
* cannot read from request
* @throws ServletException
* servlet exception
*/
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException;
/**
* @see Filter#destroy()
*/
void destroy();
}

View File

@@ -0,0 +1,163 @@
/**
* 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.im.authentication;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.UserDetails;
/**
* A software provisioning user principal definition stored in the
* {@link SecurityContext} which contains the user specific attributes.
*
*
*
*
*/
public class UserPrincipal implements UserDetails, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String username;
private final String firstname;
private final String lastname;
private final String loginname;
private final String tenant;
/**
* @param username
* the user name of the user
* @param firstname
* the first name of the user
* @param lastname
* the last name of the user
* @param loginname
* the login name of user
* @param tenant
* the tenant of the user
*/
public UserPrincipal(final String username, final String firstname, final String lastname, final String loginname,
final String tenant) {
this.username = username;
this.firstname = firstname;
this.lastname = lastname;
this.loginname = loginname;
this.tenant = tenant;
}
/**
* @return the username
*/
@Override
public String getUsername() {
return username;
}
/**
* @return the firstname
*/
public String getFirstname() {
return firstname;
}
/**
* @return the lastname
*/
public String getLastname() {
return lastname;
}
/**
* @return the loginname
*/
public String getLoginname() {
return loginname;
}
/**
* @return the tenant
*/
public String getTenant() {
return tenant;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getAuthorities(
* )
*/
@Override
public Collection<SimpleGrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getPassword()
*/
@Override
public String getPassword() {
return null;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetails#
* isAccountNonExpired()
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetails#
* isAccountNonLocked()
*/
@Override
public boolean isAccountNonLocked() {
return false;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetails#
* isCredentialsNonExpired()
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#isEnabled()
*/
@Override
public boolean isEnabled() {
return true;
}
}

View File

@@ -0,0 +1,193 @@
/**
* 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.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* Filter for protection against denial of service attacks. It reduces the
* maximum number of request per seconds which can be separately configured for
* read (GET) and write (PUT/POST/DELETE) requests. requests
*
*
*
*
*/
public class DosFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(DosFilter.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
private static final Logger LOG_BLACKLIST = LoggerFactory.getLogger("server-security.blacklist");
private final Pattern ipAdressBlacklist;
private final Cache<String, AtomicInteger> readCountCache = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.SECONDS).build();
private final Cache<String, AtomicInteger> writeCountCache = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.SECONDS).build();
private final Integer maxRead;
private final Integer maxWrite;
private final Pattern whitelist;
private final String forwardHeader;
/**
* Filter constructor including configuration.
*
* @param maxRead
* Maximum number of allowed REST read/GET requests per second
* per client
* @param maxWrite
* Maximum number of allowed REST write/(PUT/POST/etc.) requests
* per second per client
* @param ipDosWhiteListPattern
* {@link Pattern} with with white list of peer IP addresses for
* DOS filter
* @param ipBlackListPattern
* {@link Pattern} with black listed IP addresses
* @param forwardHeader
* the header containing the forwarded IP address e.g.
* {@code x-forwarded-for}
*/
public DosFilter(final Integer maxRead, final Integer maxWrite, final String ipDosWhiteListPattern,
final String ipBlackListPattern, final String forwardHeader) {
super();
this.maxRead = maxRead;
this.maxWrite = maxWrite;
this.forwardHeader = forwardHeader;
if (ipBlackListPattern != null && !ipBlackListPattern.isEmpty()) {
ipAdressBlacklist = Pattern.compile(ipBlackListPattern);
} else {
ipAdressBlacklist = null;
}
if (ipDosWhiteListPattern != null && !ipDosWhiteListPattern.isEmpty()) {
whitelist = Pattern.compile(ipDosWhiteListPattern);
} else {
whitelist = null;
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(
* javax.servlet.http. HttpServletRequest,
* javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
*/
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException {
boolean processChain = true;
final String ip = IpUtil.getClientIpFromRequest(request, forwardHeader).getHost();
if (checkIpFails(ip)) {
processChain = handleMissingIpAddress(response);
} else {
processChain = checkAgainstBlacklist(response, ip);
if (processChain && (whitelist == null || !whitelist.matcher(ip).find())) {
// read request
if (HttpMethod.valueOf(request.getMethod()) == HttpMethod.GET) {
processChain = handleReadRequest(response, ip);
}
// write request
else {
processChain = handleWriteRequest(response, ip);
}
}
}
if (processChain) {
filterChain.doFilter(request, response);
}
}
/**
* @return false if the given ip address is on the blacklist and further
* processing of the request if forbidden
*/
private boolean checkAgainstBlacklist(final HttpServletResponse response, final String ip) {
if (ipAdressBlacklist != null && ipAdressBlacklist.matcher(ip).find()) {
LOG_BLACKLIST.info("Blacklisted client ({}) tries to access the server!", ip);
response.setStatus(HttpStatus.FORBIDDEN.value());
return false;
}
return true;
}
private boolean checkIpFails(final String ip) {
return ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip);
}
private boolean handleMissingIpAddress(final HttpServletResponse response) {
boolean processChain;
LOG.error("Failed to get peer IP adress");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
processChain = false;
return processChain;
}
private boolean handleWriteRequest(final HttpServletResponse response, final String ip) {
boolean processChain = true;
final AtomicInteger count = writeCountCache.getIfPresent(ip);
if (count == null) {
writeCountCache.put(ip, new AtomicInteger());
} else if (count.getAndIncrement() > maxWrite) {
LOG_DOS.info("Registered DOS attack! Client {} is above configured WRITE request threshold ({})!", ip,
maxWrite);
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
processChain = false;
}
return processChain;
}
private boolean handleReadRequest(final HttpServletResponse response, final String ip) {
boolean processChain = true;
final AtomicInteger count = readCountCache.getIfPresent(ip);
if (count == null) {
readCountCache.put(ip, new AtomicInteger());
} else if (count.getAndIncrement() > maxRead) {
LOG_DOS.info("Registered DOS attack! Client {} is above configured READ request threshold ({})!", ip,
maxRead);
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
processChain = false;
}
return processChain;
}
}

View File

@@ -0,0 +1,66 @@
/**
* 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.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* A {@link TenantAware} implemenation which retrieves the ID of the tenant from
* the {@link SecurityContext#getAuthentication()}
* {@link Authentication#getDetails()} which holds the
* {@link TenantAwareAuthenticationDetails} object.
*
*
*
*
*/
public class SecurityContextTenantAware implements TenantAware {
private static final ThreadLocal<String> TENANT_THREAD_LOCAL = new ThreadLocal<>();
/*
* (non-Javadoc)
*
* @see org.eclipse.hawkbit.server.tenancy.TenantAware#getCurrentTenantId()
*/
@Override
public String getCurrentTenant() {
if (TENANT_THREAD_LOCAL.get() != null) {
return TENANT_THREAD_LOCAL.get();
}
final SecurityContext context = SecurityContextHolder.getContext();
if (context.getAuthentication() != null) {
final Object authDetails = context.getAuthentication().getDetails();
if (authDetails instanceof TenantAwareAuthenticationDetails) {
return ((TenantAwareAuthenticationDetails) authDetails).getTenant();
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see hawkbit.server.tenancy.TenantAware#runAsTenant(java.lang.String,
* java.util.concurrent.Callable)
*/
@Override
public <T> T runAsTenant(final String tenant, final TenantRunner<T> callable) {
TENANT_THREAD_LOCAL.set(tenant);
try {
return callable.run();
} finally {
TENANT_THREAD_LOCAL.remove();
}
}
}

View File

@@ -0,0 +1,68 @@
/**
* 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.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* The common properties for security.
*
*
*
*/
@ConfigurationProperties
public class SecurityProperties {
@Value("${hawkbit.server.controller.security.rp.cnHeader:X-Ssl-Client-Cn}")
private String rpCnHeader;
@Value("${hawkbit.server.controller.security.rp.sslIssuerHashHeader:X-Ssl-Issuer-Hash-%d}")
private String rpSslIssuerHashHeader;
@Value("${hawkbit.server.controller.security.rp.trustedIPs:#{null}}")
private List<String> rpTrustedIPs;
@Value("${hawkbit.server.controller.security.authentication.anonymous.enabled:false}")
private Boolean anonymousEnabled;
public String getRpCnHeader() {
return rpCnHeader;
}
public String getRpSslIssuerHashHeader() {
return rpSslIssuerHashHeader;
}
public List<String> getRpTrustedIPs() {
return rpTrustedIPs;
}
public Boolean getAnonymousEnabled() {
return anonymousEnabled;
}
public void setRpCnHeader(final String rpCnHeader) {
this.rpCnHeader = rpCnHeader;
}
public void setRpSslIssuerHashHeader(final String rpSslIssuerHashHeader) {
this.rpSslIssuerHashHeader = rpSslIssuerHashHeader;
}
public void setRpTrustedIPs(final List<String> rpTrustedIPs) {
this.rpTrustedIPs = rpTrustedIPs;
}
public void setAnonymousEnabled(final Boolean anonymousEnabled) {
this.anonymousEnabled = anonymousEnabled;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.security;
import java.security.SecureRandom;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Service;
/**
* A security token generator service which can be used to generate security
* tokens for e.g. target or gateway tokens which are valid for authenticates
* against SP.
*
*
*
*
*/
@Service
public class SecurityTokenGenerator {
private static final boolean LETTERS_GENERATION = true;
private static final boolean NUMBER_GENERATION = true;
private static final int TOKEN_LENGTH = 32;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* Generates a random secure token of length {@link #TOKEN_LENGTH}
* characters with alphanumeric characters {@code A-Z_a-z_0-9}.
*
* @return a new generated random alphanumeric string.
*/
public String generateToken() {
return RandomStringUtils.random(TOKEN_LENGTH, 0, 0, LETTERS_GENERATION, NUMBER_GENERATION, null, SECURE_RANDOM);
}
}

View File

@@ -0,0 +1,44 @@
/**
* 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.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Auditor class that allows {@link BaseEntity}s to insert currenlt logged in
* user for repository changes.
*
*
*
*
*
*/
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public String getCurrentAuditor() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
if (authentication.getPrincipal() != null) {
if (authentication.getPrincipal() instanceof UserDetails) {
return ((UserDetails) authentication.getPrincipal()).getUsername();
}
return authentication.getPrincipal().toString();
}
return null;
}
}

View File

@@ -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.util;
import java.lang.annotation.Target;
import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import com.google.common.net.HttpHeaders;
/**
* A utility which determines the correct IP of a connected {@link Target}. E.g
* from a {@link HttpServletRequest}.
*
*
*
*
*/
public final class IpUtil {
private static final String SCHEME_SEPERATOR = "://";
private static final String HTTP_SCHEME = "http";
private static final String AMPQP_SCHEME = "amqp";
private static final Pattern IPV4_ADDRESS_PATTERN = Pattern
.compile("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
private static final Pattern IPV6_ADDRESS_PATTERN = Pattern.compile("([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}");
private IpUtil() {
}
/**
* Retrieves the string based IP address from a given
* {@link HttpServletRequest} by either the
* {@link HttpHeaders#X_FORWARDED_FOR} or by the
* {@link HttpServletRequest#getRemoteAddr()} methods.
*
* @param request
* the {@link HttpServletRequest} to determine the IP address
* where this request has been sent from
* @param forwardHeader
* the header name containing the IP address e.g. forwarded by a
* proxy {@code x-forwarded-for}
* @return the {@link URI} based IP address from the client which sent the
* request
*/
public static URI getClientIpFromRequest(final HttpServletRequest request, final String forwardHeader) {
String ip = request.getHeader(forwardHeader);
if (ip == null || (ip = findClientIpAddress(ip)) == null) {
ip = request.getRemoteAddr();
}
return createHttpUri(ip);
}
private static String findClientIpAddress(final String s) {
final Matcher matcherv4 = IPV4_ADDRESS_PATTERN.matcher(s);
if (matcherv4.find()) {
return matcherv4.group(0);
}
final Matcher matcherv6 = IPV6_ADDRESS_PATTERN.matcher(s);
if (matcherv6.find()) {
return matcherv6.group(0);
}
return null;
}
/**
* Create a {@link URI} with scheme and host.
*
* @param scheme
* the scheme
* @param host
* the host
* @return the {@link URI}
* @throws IllegalArgumentException
* If the given string not parsable
*/
public static URI createUri(final String scheme, final String host) {
final boolean isIpV6 = host.indexOf(':') >= 0 && host.charAt(0) != '[';
if (isIpV6) {
return URI.create(scheme + SCHEME_SEPERATOR + "[" + host + "]");
}
return URI.create(scheme + SCHEME_SEPERATOR + host);
}
/**
* Create a {@link URI} with amqp scheme and host.
*
* @param host
* the host
* @return the {@link URI}
* @throws IllegalArgumentException
* If the given string not parsable
*/
public static URI createAmqpUri(final String host) {
return createUri(AMPQP_SCHEME, host);
}
/**
* Create a {@link URI} with http scheme and host.
*
* @param host
* the host
* @return the {@link URI}
* @throws IllegalArgumentException
* If the given string not parsable
*/
public static URI createHttpUri(final String host) {
return createUri(HTTP_SCHEME, host);
}
/**
* Check if scheme contains http and uri ist not <code>null</code>.
*
* @param uri
* the uri
* @return true = is http host false = not
*/
public static boolean isHttpUri(final URI uri) {
return uri != null && HTTP_SCHEME.equals(uri.getScheme());
}
/**
* Check if host scheme amqp and uri ist not <code>null</code>.
*
* @param uri
* the uri
* @return true = is http host false = not
*/
public static boolean isAmqpUri(final URI uri) {
return uri != null && AMPQP_SCHEME.equals(uri.getScheme());
}
}

View File

@@ -0,0 +1,105 @@
/**
* 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 javax.servlet.MultipartConfigElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* Bean which contains all informations about the SP software, e.g. like
* version, built time etc. from the environment.
*
*
*
*
*/
@Component
public class SPInfo implements EnvironmentAware {
// package private for testing purposes
static final String UNKNOWN_VERSION = "unknown";
static final String UNKNOWN_CREDENTIAL = "unknown credential";
private Environment environmentData;
@Autowired
private MultipartConfigElement configElement;
/*
* (non-Javadoc)
*
* @see org.springframework.context.EnvironmentAware#setEnvironment(org.
* springframework.core.env. Environment)
*/
@Override
public void setEnvironment(final Environment environment) {
this.environmentData = environment;
}
/**
* @return the version in string format, e.g. 1.0.0 or {@code "UNKNOWN"} in
* case the SP version info cannot be determined.
*/
public String getVersion() {
if (environmentData != null) {
return environmentData.getProperty("info.build.version", UNKNOWN_VERSION);
}
return UNKNOWN_VERSION;
}
public String getSupportEmail() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.email.support");
}
return "";
}
public String getRequestAccountEmail() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.email.request.account");
}
return "";
}
public String getDemoTenant() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.demo.tenant");
}
return UNKNOWN_CREDENTIAL;
}
public String getDemoUser() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.demo.user");
}
return UNKNOWN_CREDENTIAL;
}
public String getDemoPassword() {
if (environmentData != null) {
return environmentData.getProperty("hawkbit.server.demo.password");
}
return UNKNOWN_CREDENTIAL;
}
/**
* @return the max file size to upload artifact files in bytes which has
* been configured.
*/
public long getMaxArtifactFileSize() {
return configElement.getMaxFileSize();
}
}

View File

@@ -0,0 +1,86 @@
/**
* 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;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ExcludePathAwareShallowETagFilterTest {
@Mock
private HttpServletRequest servletRequestMock;
@Mock
private HttpServletResponse servletResponseMock;
@Mock
private FilterChain filterChainMock;
@Test
public void excludePathDoesNotCalculateETag() throws ServletException, IOException {
final String knownContextPath = "/bumlux/test";
final String knownUri = knownContextPath + "/exclude/download";
final String antPathExclusion = "/exclude/**";
// mock
when(servletRequestMock.getContextPath()).thenReturn(knownContextPath);
when(servletRequestMock.getRequestURI()).thenReturn(knownUri);
final ExcludePathAwareShallowETagFilter filterUnderTest = new ExcludePathAwareShallowETagFilter(
antPathExclusion);
filterUnderTest.doFilterInternal(servletRequestMock, servletResponseMock, filterChainMock);
// verify no eTag header is set and response has not been changed
assertThat(servletResponseMock.getHeader("ETag")).isNull();
// the servlet response must be the same mock!
verify(filterChainMock, times(1)).doFilter(servletRequestMock, servletResponseMock);
}
@Test
public void pathNotExcludedETagIsCalculated() throws ServletException, IOException {
final String knownContextPath = "/bumlux/test";
final String knownUri = knownContextPath + "/include/download";
final String antPathExclusion = "/exclude/**";
// mock
when(servletRequestMock.getContextPath()).thenReturn(knownContextPath);
when(servletRequestMock.getRequestURI()).thenReturn(knownUri);
final ExcludePathAwareShallowETagFilter filterUnderTest = new ExcludePathAwareShallowETagFilter(
antPathExclusion);
final ArgumentCaptor<HttpServletResponse> responseArgumentCaptor = ArgumentCaptor
.forClass(HttpServletResponse.class);
filterUnderTest.doFilterInternal(servletRequestMock, servletResponseMock, filterChainMock);
// the servlet response must be the same mock!
verify(filterChainMock, times(1)).doFilter(Mockito.eq(servletRequestMock), responseArgumentCaptor.capture());
assertThat(mockingDetails(responseArgumentCaptor.getValue()).isMock()).isFalse();
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import org.junit.Test;
public class SecurityTokenGeneratorTest {
@Test
public void test() throws NoSuchAlgorithmException, UnsupportedEncodingException {
final SecurityTokenGenerator securityTokenGenerator = new SecurityTokenGenerator();
for (int index = 0; index < 1; index++) {
System.out.println(securityTokenGenerator.generateToken());
}
}
}

View File

@@ -0,0 +1,138 @@
/**
* 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 static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.net.URI;
import javax.servlet.http.HttpServletRequest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import com.google.common.net.HttpHeaders;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@RunWith(MockitoJUnitRunner.class)
@Features("IpUtil Test")
@Stories("Tests the created uris")
public class IpUtilTest {
@Mock
private HttpServletRequest requestMock;
@Test
@Description("Tests create uri from request")
public void getRemoteAddrFromRequestIfForwaredHeaderNotPresent() {
// known values
final URI knownRemoteClientIP = IpUtil.createHttpUri("127.0.0.1");
// mock
when(requestMock.getHeader(HttpHeaders.X_FORWARDED_FOR)).thenReturn(null);
when(requestMock.getRemoteAddr()).thenReturn(knownRemoteClientIP.getHost());
// test
final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "bumlux");
// verify
assertThat(remoteAddr).isEqualTo(knownRemoteClientIP);
verify(requestMock, times(1)).getHeader("bumlux");
verify(requestMock, times(1)).getRemoteAddr();
}
@Test
@Description("Tests create uri from x forward header")
public void getRemoteAddrFromXForwardedForHeader() {
// known values
final URI knownRemoteClientIP = IpUtil.createHttpUri("10.99.99.1");
// mock
when(requestMock.getHeader(HttpHeaders.X_FORWARDED_FOR)).thenReturn(knownRemoteClientIP.getHost());
when(requestMock.getRemoteAddr()).thenReturn(null);
// test
final URI remoteAddr = IpUtil.getClientIpFromRequest(requestMock, "X-Forwarded-For");
// verify
assertThat(remoteAddr).isEqualTo(knownRemoteClientIP);
verify(requestMock, times(1)).getHeader(HttpHeaders.X_FORWARDED_FOR);
verify(requestMock, times(0)).getRemoteAddr();
}
@Test
@Description("Tests create http uri ipv4 and ipv6")
public void testCreateHttpUri() {
final String ipv4 = "10.99.99.1";
URI httpUri = IpUtil.createHttpUri(ipv4);
assertHttpUri(ipv4, httpUri);
final String host = "myhost";
httpUri = IpUtil.createHttpUri(host);
assertHttpUri(host, httpUri);
final String ipv6 = "0:0:0:0:0:0:0:1";
httpUri = IpUtil.createHttpUri(ipv6);
assertHttpUri("[" + ipv6 + "]", httpUri);
}
private void assertHttpUri(final String host, final URI httpUri) {
assertTrue(IpUtil.isHttpUri(httpUri));
assertFalse(IpUtil.isAmqpUri(httpUri));
assertEquals(host, httpUri.getHost());
assertEquals("http", httpUri.getScheme());
}
@Test
@Description("Tests create amqp uri ipv4 and ipv6")
public void testCreateAmqpUri() {
final String ipv4 = "10.99.99.1";
URI amqpUri = IpUtil.createAmqpUri(ipv4);
assertAmqpUri(ipv4, amqpUri);
final String host = "myhost";
amqpUri = IpUtil.createAmqpUri(host);
assertAmqpUri(host, amqpUri);
final String ipv6 = "0:0:0:0:0:0:0:1";
amqpUri = IpUtil.createAmqpUri(ipv6);
assertAmqpUri("[" + ipv6 + "]", amqpUri);
}
private void assertAmqpUri(final String host, final URI httpUri) {
assertTrue(IpUtil.isAmqpUri(httpUri));
assertFalse(IpUtil.isHttpUri(httpUri));
assertEquals(host, httpUri.getHost());
assertEquals("amqp", httpUri.getScheme());
}
@Test(expected = IllegalArgumentException.class)
@Description("Tests create invalid uri")
public void testCreateInvalidUri() {
final String host = "10.99.99.1";
final URI testUri = IpUtil.createUri("test", host);
assertFalse(IpUtil.isAmqpUri(testUri));
assertFalse(IpUtil.isHttpUri(testUri));
assertEquals(host, testUri.getHost());
IpUtil.createUri(":/", host);
fail();
}
}