jwt settings added message on base64 validation exception, default key is to be validated ok, tests added

This commit is contained in:
Sergey Matvienko 2022-11-14 13:43:56 +01:00
parent 335d88c82e
commit 46b2adeb2c
4 changed files with 101 additions and 38 deletions

View File

@ -15,15 +15,22 @@
*/
package org.thingsboard.server.config.jwt;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.security.model.JwtToken;
@Component
@ConfigurationProperties(prefix = "security.jwt")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JwtSettings {
static final String ADMIN_SETTINGS_JWT_KEY = "jwt";
static final String TOKEN_SIGNING_KEY_DEFAULT = "thingsboardDefaultSigningKey";
/**
* {@link JwtToken} will expire after this time.
*/

View File

@ -37,13 +37,14 @@ import java.util.Base64;
import java.util.Objects;
import java.util.Optional;
import static org.thingsboard.server.config.jwt.JwtSettings.ADMIN_SETTINGS_JWT_KEY;
import static org.thingsboard.server.config.jwt.JwtSettings.TOKEN_SIGNING_KEY_DEFAULT;
@Service
@RequiredArgsConstructor
@Slf4j
public class JwtSettingsServiceDefault implements JwtSettingsService {
static final String ADMIN_SETTINGS_JWT_KEY = "jwt";
static final String TOKEN_SIGNING_KEY_DEFAULT = "thingsboardDefaultSigningKey";
@Lazy
private final AdminSettingsService adminSettingsService;
@Lazy

View File

@ -26,6 +26,8 @@ import java.util.Base64;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.config.jwt.JwtSettings.TOKEN_SIGNING_KEY_DEFAULT;
@Component
@RequiredArgsConstructor
public class JwtSettingsValidatorDefault implements JwtSettingsValidator {
@ -58,7 +60,7 @@ public class JwtSettingsValidatorDefault implements JwtSettingsValidator {
if (Arrays.isNullOrEmpty(decodedKey)) {
throw new DataValidationException("JWT token signing key should be non-empty after Base64 decoding!");
}
if (decodedKey.length * Byte.SIZE < 256) {
if (decodedKey.length * Byte.SIZE < 256 && !TOKEN_SIGNING_KEY_DEFAULT.equals(jwtSettings.getTokenSigningKey())) {
throw new DataValidationException("JWT token signing key should be a Base64 encoded string representing at least 256 bits of data!");
}

View File

@ -17,14 +17,22 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.config.jwt.JwtSettings;
import org.thingsboard.server.config.jwt.JwtSettingsService;
import org.thingsboard.server.service.mail.DefaultMailService;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@ -32,8 +40,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
public abstract class BaseAdminControllerTest extends AbstractControllerTest {
final JwtSettings defaultJwtSettings = new JwtSettings(9000, "thingsboard.io", "thingsboardDefaultSigningKey", 604800);
@Autowired
MailService mailService;
@ -139,4 +148,48 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest {
doPost("/api/admin/settings/testMail", adminSettings).andExpect(status().is5xxServerError());
Mockito.doNothing().when(mailService).sendTestMail(Mockito.any(), Mockito.any());
}
void resetJwtSettingsToDefault() throws Exception {
loginSysAdmin();
doPost("/api/admin/jwtSettings", defaultJwtSettings).andExpect(status().isOk()); // jwt test scenarios are always started from
loginTenantAdmin();
}
@Test
public void testGetAndSaveDefaultJwtSettings() throws Exception {
JwtSettings jwtSettings;
loginSysAdmin();
jwtSettings = doGet("/api/admin/jwtSettings", JwtSettings.class);
assertThat(jwtSettings).isEqualTo(defaultJwtSettings);
doPost("/api/admin/jwtSettings", jwtSettings).andExpect(status().isOk());
jwtSettings = doGet("/api/admin/jwtSettings", JwtSettings.class);
assertThat(jwtSettings).isEqualTo(defaultJwtSettings);
resetJwtSettingsToDefault();
}
@Test
public void testCreateJwtSettings() throws Exception {
loginSysAdmin();
JwtSettings jwtSettings = doGet("/api/admin/jwtSettings", JwtSettings.class);
assertThat(jwtSettings).isEqualTo(defaultJwtSettings);
jwtSettings.setTokenSigningKey(Base64.getEncoder().encodeToString(
RandomStringUtils.randomAlphanumeric(256 / Byte.SIZE).getBytes(StandardCharsets.UTF_8)));
doPost("/api/admin/jwtSettings", jwtSettings).andExpect(status().isOk());
doGet("/api/admin/jwtSettings").andExpect(status().isUnauthorized()); //the old JWT token does not work after signing key was changed!
loginSysAdmin();
JwtSettings newJwtSettings = doGet("/api/admin/jwtSettings", JwtSettings.class);
assertThat(jwtSettings).isEqualTo(newJwtSettings);
resetJwtSettingsToDefault();
}
}