Commit f00fa7bb authored by ipatini's avatar ipatini
Browse files

EMS: Control Service, config: Added 'WebSecurityProperties' class and moved...

EMS: Control Service, config: Added 'WebSecurityProperties' class and moved into it all @Value-annotated properties of WebSecurityConfig. Updated code to use WebSecurityProperties instance. Changed corresponding settings in 'eu.melodic.event.control.properties' (replaced '.' with '-' where needed) and rearranged them.
parent 56440f9e
......@@ -111,15 +111,22 @@ control.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip:$
### Web configuration - JWT security ###
#melodic.security.enabled=false
#web.jwt-token-authentication.enabled=false
#web.jwt-print-sample-token=false
#web.jwt-authentication.enabled=false
#web.jwt-authentication.request-parameter=jwt
#web.jwt-authentication.print-sample-token=false
### Web configuration - API key security ###
#web.api-key-authentication.enabled=false
#web.api-key.value=${random.uuid}
#web.api-key.value=1234567890
#web.api-key.header=EMS-API-KEY
#web.api-key.parameter=ems-api-key
#web.api-key-authentication.value=${random.uuid}
#web.api-key-authentication.value=1234567890
#web.api-key-authentication.request-header=EMS-API-KEY
#web.api-key-authentication.request-parameter=ems-api-key
### Web configuration - OTP security ###
#web.otp-authentication.enabled=false
#web.otp-authentication.duration=3600000
#web.otp-authentication.request-header=EMS-OTP
#web.otp-authentication.request-parameter=ems-otp
### Web configuration - User Form security ###
#web.form-authentication.enabled=false
......
......@@ -16,6 +16,7 @@ import eu.melodic.event.control.ControlServiceCoordinator;
import eu.melodic.event.control.properties.ControlServiceProperties;
import eu.melodic.event.control.properties.InfoServiceProperties;
import eu.melodic.event.control.properties.StaticResourceProperties;
import eu.melodic.event.control.properties.WebSecurityProperties;
import eu.melodic.event.translate.TranslationContext;
import eu.melodic.event.util.FunctionDefinition;
import eu.melodic.event.util.GROUPING;
......@@ -46,6 +47,7 @@ public class EmsInfoServiceImpl implements IEmsInfoService {
private final InfoServiceProperties infoServiceProperties;
private final ControlServiceCoordinator controlServiceCoordinator;
private final StaticResourceProperties staticResourceProperties;
private final WebSecurityProperties webSecurityProperties;
private final BuildInfoProvider buildInfoProvider;
private final SystemInfoProvider systemInfoProvider;
......@@ -196,6 +198,14 @@ public class EmsInfoServiceImpl implements IEmsInfoService {
authMap.put("paths-excluded", authorizationProperties.getPathsExcluded());
controlServiceInfo.put("prop-authorization", authMap);
}
if (webSecurityProperties!=null) {
Map<String,Object> authMap = new LinkedHashMap<>();
authMap.put("jwt-authentication-enabled", webSecurityProperties.getJwtAuthentication().isEnabled());
authMap.put("api-key-authentication-enabled", webSecurityProperties.getApiKeyAuthentication().isEnabled());
authMap.put("otp-authentication-enabled", webSecurityProperties.getOtpAuthentication().isEnabled());
authMap.put("form-authentication-enabled", webSecurityProperties.getFormAuthentication().isEnabled());
controlServiceInfo.put("prop-authentication-methods", authMap);
}
metrics.put(CONTROL_INFO_PROVIDER, controlServiceInfo);
// Collect Broker-CEP metrics
......
/*
* Copyright (C) 2017-2022 Institute of Communication and Computer Systems (imu.iccs.gr)
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
* If a copy of the MPL was not distributed with this file, you can obtain one at
* https://www.mozilla.org/en-US/MPL/2.0/
*/
package eu.melodic.event.control.properties;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Data
@Validated
@Configuration
@ConfigurationProperties(prefix = "web")
public class WebSecurityProperties {
// JWT Token authentication
@Valid
@NotNull
private JwtAuthentication jwtAuthentication = new JwtAuthentication();
@Data
public static class JwtAuthentication {
private boolean enabled = true;
private String requestParameter;
private boolean printSampleToken;
}
// API-Key authentication
@Valid
@NotNull
private ApiKeyAuthentication apiKeyAuthentication = new ApiKeyAuthentication();
@Data
public static class ApiKeyAuthentication {
private boolean enabled = true;
private String requestHeader = "EMS-API-KEY";
private String requestParameter = "ems-api-key";
private String value;
}
// OTP authentication
@Valid
@NotNull
private OtpAuthentication otpAuthentication = new OtpAuthentication();
@Data
public static class OtpAuthentication {
private boolean enabled = true;
@Min(1)
private long duration = 3600000;
private String requestHeader = "EMS-OTP";
private String requestParam = "ems-otp";
}
// User form authentication
@Valid
@NotNull
private FormAuthentication formAuthentication = new FormAuthentication();
@Data
public static class FormAuthentication {
private boolean enabled = true;
private String username = "admin";
private String password;
private String loginPage = "/admin/login.html";
private String loginUrl = "/login";
private String loginSuccessUrl = "/";
private String loginFailureUrl = "/admin/login.html?error=Invalid+username+or+password";
private String logoutUrl = "/logout";
private String logoutSuccessUrl = "/admin/login.html?message=Signed+out";
}
// Permitted URLs
private List<String> permittedUrls = Arrays.asList(
"/login*", "/logout*", "/favicon.ico", "/admin/login.html", "/admin/favicon.ico", "/admin/assets/**", "/resources/*");
}
......@@ -9,6 +9,8 @@
package eu.melodic.event.control.webconf;
import eu.melodic.event.control.properties.StaticResourceProperties;
import eu.melodic.event.control.properties.WebSecurityProperties;
import eu.melodic.event.util.PasswordUtil;
import eu.paasage.upperware.security.authapi.SecurityConstants;
import eu.paasage.upperware.security.authapi.properties.MelodicSecurityProperties;
......@@ -19,6 +21,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
......@@ -27,6 +30,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
......@@ -56,7 +60,7 @@ import java.util.Map;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableConfigurationProperties(MelodicSecurityProperties.class)
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean {
public final static String ROLE_USER_FORM = "ROLE_USER_FORM";
public static final String ROLE_JWT_TOKEN = "ROLE_JWT_TOKEN";
......@@ -64,68 +68,96 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String ROLE_OTP = "ROLE_OTP";
private final MelodicSecurityProperties melodicSecurityProperties;
private final StaticResourceProperties staticResourceProperties;
private final WebSecurityProperties properties;
private final PasswordUtil passwordUtil;
private final Map<String,Long> otpCache = new HashMap<>();
@Value("${melodic.security.enabled:true}")
private boolean securityEnabled;
private boolean propertiesCopied;
// JWT Token authentication fields
@Value("${web.jwt-token-authentication.enabled:true}")
private boolean jwtTokenAuthEnabled;
@Value("${web.jwt-token.parameter:#{null}}")
private String jwtTokenRequestParam;
@Value("${web.jwt-print-sample-token:false}")
private boolean jwtAuthEnabled;
private String jwtRequestParam;
private boolean printSampleJwt;
// API-Key authentication fields
@Value("${web.api-key-authentication.enabled:true}")
private boolean apiKeyAuthEnabled;
@Value("${web.api-key.header:EMS-API-KEY}")
private String apiKeyRequestHeader;
@Value("${web.api-key.parameter:ems-api-key}")
private String apiKeyRequestParam;
@Value("${web.api-key.value:#{null}}")
private String apiKeyValue;
// OTP authentication fields
@Value("${web.otp-authentication.enabled:true}")
private boolean otpAuthEnabled;
@Value("${web.otp.duration:3600000}")
private long otpDuration;
@Value("${web.otp.header:EMS-OTP}")
private String otpRequestHeader;
@Value("${web.otp.parameter:ems-otp}")
private String otpRequestParam;
private Map<String,Long> otpCache = new HashMap<>();
@Autowired
private PasswordUtil passwordUtil;
// User form authentication fields
@Value("${web.form-authentication.enabled:true}")
private boolean userFormAuthEnabled;
@Value("${web.form-authentication.username:admin}")
private String username;
@Value("${web.form-authentication.password:}")
private String password;
@Value("${web.permitted.urls:/login*,/logout*,/admin/login.html,/admin/favicon.ico,/admin/assets/**,/resources/*}")
private String[] permittedUrls;
@Value("${web.login.page:/admin/login.html}")
private String loginPage;
@Value("${web.login.url:/login}")
private String loginUrl;
@Value("${web.login.success.url:/}")
private String loginSuccessUrl;
@Value("${web.login.failure.url:/admin/login.html?error=Invalid+username+or+password}")
private String loginFailureUrl;
@Value("${web.logout.url:/logout}")
private String logoutUrl;
@Value("${web.logout.success.url:/admin/login.html?message=Signed+out}")
private String logoutSuccessUrl;
// Permitted URLs
private String[] permittedUrls;
private final static String divider = "--------------------------------------------------------------------------------";
@Override
public void afterPropertiesSet() {
copyPropertiesToLocalFields();
}
private void copyPropertiesToLocalFields() {
if (properties==null) return;
if (propertiesCopied) return;
// JWT Token authentication fields
jwtAuthEnabled = properties.getJwtAuthentication().isEnabled();
jwtRequestParam = properties.getJwtAuthentication().getRequestParameter();
printSampleJwt = properties.getJwtAuthentication().isPrintSampleToken();
// API-Key authentication fields
apiKeyAuthEnabled = properties.getApiKeyAuthentication().isEnabled();
apiKeyRequestHeader = properties.getApiKeyAuthentication().getRequestHeader();
apiKeyRequestParam = properties.getApiKeyAuthentication().getRequestParameter();
apiKeyValue = properties.getApiKeyAuthentication().getValue();
// OTP authentication fields
otpAuthEnabled = properties.getOtpAuthentication().isEnabled();
otpDuration = properties.getOtpAuthentication().getDuration();
otpRequestHeader = properties.getOtpAuthentication().getRequestHeader();
otpRequestParam = properties.getOtpAuthentication().getRequestParam();
// User form authentication fields
userFormAuthEnabled = properties.getFormAuthentication().isEnabled();
username = properties.getFormAuthentication().getUsername();
password = properties.getFormAuthentication().getPassword();
loginPage = properties.getFormAuthentication().getLoginPage();
loginUrl = properties.getFormAuthentication().getLoginUrl();
loginSuccessUrl = properties.getFormAuthentication().getLoginSuccessUrl();
loginFailureUrl = properties.getFormAuthentication().getLoginFailureUrl();
logoutUrl = properties.getFormAuthentication().getLogoutUrl();
logoutSuccessUrl = properties.getFormAuthentication().getLogoutSuccessUrl();
// Permitted URLs
permittedUrls = properties.getPermittedUrls()!=null
? properties.getPermittedUrls().toArray(new String[0])
: new String[0];
propertiesCopied = true;
}
@EventListener(ApplicationReadyEvent.class)
public void applicationReady() {
if (securityEnabled && userFormAuthEnabled && (StringUtils.isBlank(username) || password.isEmpty()))
......@@ -145,27 +177,37 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
log.info("afterPropertiesSet:\n{}\nSample JWT Token: \nBearer {}\n{}",
divider, jwtService(melodicSecurityProperties).create("USER"), divider);
log.debug("afterPropertiesSet: ---------------------");
log.debug("afterPropertiesSet: securityEnabled: {}", securityEnabled);
log.debug("afterPropertiesSet: jwtTokenAuthEnabled: {}", jwtTokenAuthEnabled);
log.debug("afterPropertiesSet: jwtTokenRequestParam: {}", jwtTokenRequestParam);
log.debug("afterPropertiesSet: ---------------------");
log.debug("afterPropertiesSet: jwtTokenAuthEnabled: {}", jwtAuthEnabled);
log.debug("afterPropertiesSet: jwtTokenRequestParam: {}", jwtRequestParam);
log.debug("afterPropertiesSet: ---------------------");
log.debug("afterPropertiesSet: apiKeyAuthEnabled: {}", apiKeyAuthEnabled);
log.debug("afterPropertiesSet: apiKeyRequestHeader: {}", apiKeyRequestHeader);
log.debug("afterPropertiesSet: apiKeyRequestParam: {}", apiKeyRequestParam);
log.debug("afterPropertiesSet: ---------------------");
log.debug("afterPropertiesSet: otpAuthEnabled: {}", otpAuthEnabled);
log.debug("afterPropertiesSet: otpDuration: {}", otpDuration);
log.debug("afterPropertiesSet: otpRequestHeader: {}", otpRequestHeader);
log.debug("afterPropertiesSet: otpRequestParam: {}", otpRequestParam);
log.debug("afterPropertiesSet: ---------------------");
log.debug("afterPropertiesSet: userFormAuthEnabled: {}", userFormAuthEnabled);
log.debug("afterPropertiesSet: permittedUrls: {}", Arrays.asList(permittedUrls));
log.debug("afterPropertiesSet: username: {}", username);
log.debug("afterPropertiesSet: loginPage: {}", loginPage);
log.debug("afterPropertiesSet: loginUrl: {}", loginUrl);
log.debug("afterPropertiesSet: loginSuccessUrl: {}", loginSuccessUrl);
log.debug("afterPropertiesSet: loginFailUrl: {}", loginFailureUrl);
log.debug("afterPropertiesSet: logoutUrl: {}", logoutUrl);
log.debug("afterPropertiesSet: logoutSuccessUrl: {}", logoutSuccessUrl);
log.debug("afterPropertiesSet: ---------------------");
log.debug("afterPropertiesSet: permittedUrls: {}", Arrays.asList(permittedUrls));
log.debug("afterPropertiesSet: ---------------------");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
copyPropertiesToLocalFields();
if (this.userFormAuthEnabled && StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
auth.inMemoryAuthentication()
.withUser(username).password(passwordEncoder().encode(password)).authorities(ROLE_USER_FORM);
......@@ -182,7 +224,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public void configure(WebSecurity web) throws Exception {
web.ignoring()
// Spring Security should completely ignore the following URLs
.antMatchers("/favicon.ico", "/health");
.antMatchers(staticResourceProperties.getFaviconContext(), "/health");
}
@Override
......@@ -193,8 +235,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// Check if authentication is disabled
log.debug("WebSecurityConfig: security-enabled={}, user-form-auth-enabled={}, jwt-token-auth-enabled={}, api-key-auth-enabled={}, otp-auth-enabled={}",
securityEnabled, userFormAuthEnabled, jwtTokenAuthEnabled, apiKeyAuthEnabled, otpAuthEnabled);
if (!securityEnabled || !userFormAuthEnabled && !jwtTokenAuthEnabled && !apiKeyAuthEnabled && !otpAuthEnabled) {
securityEnabled, userFormAuthEnabled, jwtAuthEnabled, apiKeyAuthEnabled, otpAuthEnabled);
if (!securityEnabled || !userFormAuthEnabled && !jwtAuthEnabled && !apiKeyAuthEnabled && !otpAuthEnabled) {
log.warn("WebSecurityConfig: Authentication is disabled");
// Authorize all requests
httpSecurity
......@@ -245,7 +287,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.anyRequest().authenticated();
log.debug("WebSecurityConfig: API-Key Authentication filter added");
}
if (jwtTokenAuthEnabled) {
if (jwtAuthEnabled) {
log.info("WebSecurityConfig: JWT-Token Authentication is enabled");
httpSecurity
.addFilterAfter(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
......@@ -309,10 +351,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...else get JWT token from 'jwt' query parameter
if (StringUtils.isBlank(jwtValue)) {
if (StringUtils.isNotBlank(jwtTokenRequestParam)) {
log.debug("jwtAuthorizationFilter: Authorization Header is missing. Checking for '{}' parameter", jwtTokenRequestParam);
jwtValue = req.getParameter(jwtTokenRequestParam);
log.debug("jwtAuthorizationFilter: '{}' parameter value: {}", jwtTokenRequestParam, passwordUtil.encodePassword(jwtValue));
if (StringUtils.isNotBlank(jwtRequestParam)) {
log.debug("jwtAuthorizationFilter: Authorization Header is missing. Checking for '{}' parameter", jwtRequestParam);
jwtValue = req.getParameter(jwtRequestParam);
log.debug("jwtAuthorizationFilter: '{}' parameter value: {}", jwtRequestParam, passwordUtil.encodePassword(jwtValue));
if (StringUtils.isNotBlank(jwtValue))
jwtValue = SecurityConstants.TOKEN_PREFIX + jwtValue;
} else {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment