jwt settings service implementation
This commit is contained in:
parent
9b519d33a1
commit
7c8db6cac7
@ -15,27 +15,16 @@
|
||||
*/
|
||||
package org.thingsboard.server.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.security.model.JwtToken;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "security.jwt")
|
||||
@Data
|
||||
@Slf4j
|
||||
public class JwtSettings {
|
||||
public static final String ADMIN_SETTINGS_JWT_KEY = "jwt";
|
||||
static final String TOKEN_SIGNING_KEY_DEFAULT = "thingsboardDefaultSigningKey";
|
||||
|
||||
/**
|
||||
* {@link JwtToken} will expire after this time.
|
||||
*/
|
||||
@ -57,29 +46,4 @@ public class JwtSettings {
|
||||
*/
|
||||
private Integer refreshTokenExpTime;
|
||||
|
||||
@JsonIgnore
|
||||
@Autowired
|
||||
private AdminSettingsService adminSettingsService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
AdminSettings adminJwtSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, ADMIN_SETTINGS_JWT_KEY);
|
||||
if (adminJwtSettings != null) {
|
||||
log.debug("Loading the JWT admin settings from database");
|
||||
JwtSettings jwtSettings = JacksonUtil.treeToValue(adminJwtSettings.getJsonValue(), JwtSettings.class);
|
||||
this.setRefreshTokenExpTime(jwtSettings.getRefreshTokenExpTime());
|
||||
this.setTokenExpirationTime(jwtSettings.getTokenExpirationTime());
|
||||
this.setTokenIssuer(jwtSettings.getTokenIssuer());
|
||||
this.setTokenSigningKey(jwtSettings.getTokenSigningKey());
|
||||
}
|
||||
|
||||
if (hasDefaultTokenSigningKey()) {
|
||||
log.warn("JWT token signing key is default. This is a security issue. Please, consider to set unique value");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasDefaultTokenSigningKey() {
|
||||
return TOKEN_SIGNING_KEY_DEFAULT.equals(tokenSigningKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright © 2016-2022 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.validation.ValidationException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class JwtSettingsService {
|
||||
|
||||
static final String ADMIN_SETTINGS_JWT_KEY = "jwt";
|
||||
static final String TOKEN_SIGNING_KEY_DEFAULT = "thingsboardDefaultSigningKey";
|
||||
static final String TB_ALLOW_DEFAULT_JWT_SIGNING_KEY = "TB_ALLOW_DEFAULT_JWT_SIGNING_KEY";
|
||||
|
||||
private final AdminSettingsService adminSettingsService;
|
||||
|
||||
@Getter
|
||||
private final JwtSettings jwtSettings;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
AdminSettings adminJwtSettings = findJwtAdminSettings();
|
||||
if (adminJwtSettings != null) {
|
||||
log.debug("Loading the JWT admin settings from database");
|
||||
JwtSettings jwtLoaded = JacksonUtil.treeToValue(adminJwtSettings.getJsonValue(), JwtSettings.class);
|
||||
jwtSettings.setRefreshTokenExpTime(jwtLoaded.getRefreshTokenExpTime());
|
||||
jwtSettings.setTokenExpirationTime(jwtLoaded.getTokenExpirationTime());
|
||||
jwtSettings.setTokenIssuer(jwtLoaded.getTokenIssuer());
|
||||
jwtSettings.setTokenSigningKey(jwtLoaded.getTokenSigningKey());
|
||||
}
|
||||
|
||||
if (hasDefaultTokenSigningKey()) {
|
||||
log.warn("JWT token signing key is default. This is a security issue. Please, consider to set unique value");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasDefaultTokenSigningKey() {
|
||||
return TOKEN_SIGNING_KEY_DEFAULT.equals(jwtSettings.getTokenSigningKey());
|
||||
}
|
||||
|
||||
public void createJwtAdminSettings() {
|
||||
Objects.requireNonNull(jwtSettings, "JWT settings is null");
|
||||
if (!isJwtAdminSettingsExists()) {
|
||||
if (hasDefaultTokenSigningKey()) {
|
||||
if (!isAllowedDefaultJwtSigningKey()) {
|
||||
log.warn("JWT token signing key is default. Generating a new random key");
|
||||
jwtSettings.setTokenSigningKey(Base64.getEncoder().encodeToString(RandomStringUtils.randomAlphanumeric(64).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
AdminSettings adminJwtSettings = new AdminSettings();
|
||||
adminJwtSettings.setTenantId(TenantId.SYS_TENANT_ID);
|
||||
adminJwtSettings.setKey(ADMIN_SETTINGS_JWT_KEY);
|
||||
adminJwtSettings.setJsonValue(JacksonUtil.valueToTree(jwtSettings));
|
||||
log.info("Saving new JWT admin settings. From this moment, the JWT parameters from YAML and ENV will be ignored");
|
||||
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminJwtSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isJwtAdminSettingsExists() {
|
||||
return findJwtAdminSettings() == null;
|
||||
}
|
||||
|
||||
AdminSettings findJwtAdminSettings() {
|
||||
return adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, ADMIN_SETTINGS_JWT_KEY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowing default JWT signing key is not secure
|
||||
* */
|
||||
public boolean isAllowedDefaultJwtSigningKey() {
|
||||
String allowDefaultJwtSigningKey = System.getenv(TB_ALLOW_DEFAULT_JWT_SIGNING_KEY);
|
||||
return "true".equalsIgnoreCase(allowDefaultJwtSigningKey);
|
||||
}
|
||||
|
||||
public void validateJwtTokenSigningKey() {
|
||||
if (!isJwtAdminSettingsExists()) {
|
||||
if (hasDefaultTokenSigningKey()) {
|
||||
if (isAllowedDefaultJwtSigningKey()) {
|
||||
log.warn("Default JWT signing key is allowed. This is a security issue. Please, consider to set a strong key in admin settings");
|
||||
} else {
|
||||
String message = "Please, set a unique signing key with env variable JWT_TOKEN_SIGNING_KEY. Key is a Base64 encoded phrase. This will require to generate new tokens for all users and API that uses JWT tokens. To allow insecure JWS use TB_ALLOW_DEFAULT_JWT_SIGNING_KEY=true";
|
||||
log.error(message);
|
||||
throw new ValidationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -19,14 +19,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.config.JwtSettings;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
import static org.thingsboard.server.config.JwtSettings.ADMIN_SETTINGS_JWT_KEY;
|
||||
import org.thingsboard.server.config.JwtSettingsService;
|
||||
|
||||
@Service
|
||||
@Profile("install")
|
||||
@ -34,30 +27,12 @@ import static org.thingsboard.server.config.JwtSettings.ADMIN_SETTINGS_JWT_KEY;
|
||||
@Slf4j
|
||||
public class ConditionValidatorUpgradeServiceImpl implements ConditionValidatorUpgradeService {
|
||||
|
||||
private final AdminSettingsService adminSettingsService;
|
||||
|
||||
private final JwtSettings jwtSettings;
|
||||
private final JwtSettingsService jwtSettingsService;
|
||||
|
||||
@Override
|
||||
public void validateConditionsBeforeUpgrade(String fromVersion) throws Exception {
|
||||
log.info("Validating conditions before upgrade..");
|
||||
validateJwtTokenSigningKey();
|
||||
}
|
||||
|
||||
void validateJwtTokenSigningKey() {
|
||||
AdminSettings adminJwtSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, ADMIN_SETTINGS_JWT_KEY);
|
||||
if (adminJwtSettings == null) {
|
||||
if (jwtSettings.hasDefaultTokenSigningKey()) {
|
||||
String allowDefaultJwtSigningKey = System.getenv("TB_ALLOW_DEFAULT_JWT_SIGNING_KEY");
|
||||
if ("true".equalsIgnoreCase(allowDefaultJwtSigningKey)) {
|
||||
log.warn("Default JWT signing key is allowed. This is a security issue. Please, consider to set a strong key in admin settings");
|
||||
} else {
|
||||
String message = "Please, set a unique signing key with env variable JWT_TOKEN_SIGNING_KEY. Key is a Base64 encoded phrase. This will require to generate new tokens for all users and API that uses JWT tokens. To allow insecure JWS use TB_ALLOW_DEFAULT_JWT_SIGNING_KEY=true";
|
||||
log.error(message);
|
||||
throw new ValidationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
jwtSettingsService.validateJwtTokenSigningKey();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -22,7 +22,6 @@ import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -30,7 +29,6 @@ import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.Customer;
|
||||
@ -84,7 +82,7 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
|
||||
import org.thingsboard.server.common.data.widget.WidgetsBundle;
|
||||
import org.thingsboard.server.config.JwtSettings;
|
||||
import org.thingsboard.server.config.JwtSettingsService;
|
||||
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||
import org.thingsboard.server.dao.customer.CustomerService;
|
||||
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
||||
@ -103,18 +101,13 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static org.thingsboard.server.config.JwtSettings.ADMIN_SETTINGS_JWT_KEY;
|
||||
|
||||
@Service
|
||||
@Profile("install")
|
||||
@Slf4j
|
||||
@ -176,7 +169,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
private QueueService queueService;
|
||||
|
||||
@Autowired
|
||||
private JwtSettings jwtSettings;
|
||||
private JwtSettingsService jwtSettingsService;
|
||||
|
||||
@Bean
|
||||
protected BCryptPasswordEncoder passwordEncoder() {
|
||||
@ -274,6 +267,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createJwtAdminSettings() throws Exception {
|
||||
jwtSettingsService.createJwtAdminSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOAuth2Templates() throws Exception {
|
||||
installScripts.createOAuth2Templates();
|
||||
@ -668,25 +666,4 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createJwtAdminSettings() throws Exception {
|
||||
Objects.requireNonNull(jwtSettings,"JWT settings is null");
|
||||
AdminSettings adminJwtSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, ADMIN_SETTINGS_JWT_KEY);
|
||||
if (adminJwtSettings == null) {
|
||||
if (jwtSettings.hasDefaultTokenSigningKey()) {
|
||||
String allowDefaultJwtSigningKey = System.getenv("TB_ALLOW_DEFAULT_JWT_SIGNING_KEY");
|
||||
if (!"true".equalsIgnoreCase(allowDefaultJwtSigningKey)) {
|
||||
log.warn("JWT token signing key is default. Generating a new random key");
|
||||
jwtSettings.setTokenSigningKey(Base64.getEncoder().encodeToString(RandomStringUtils.randomAlphanumeric(64).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
adminJwtSettings = new AdminSettings();
|
||||
adminJwtSettings.setTenantId(TenantId.SYS_TENANT_ID);
|
||||
adminJwtSettings.setKey(ADMIN_SETTINGS_JWT_KEY);
|
||||
adminJwtSettings.setJsonValue(JacksonUtil.valueToTree(jwtSettings));
|
||||
log.info("Saving new JWT admin settings. From this moment, the JWT parameters from YAML and ENV will be ignored");
|
||||
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminJwtSettings);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user