Improvements for system notifications; add test
This commit is contained in:
parent
2a87f2ee7e
commit
c55ac0efc6
@ -182,7 +182,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
|||||||
NotificationRequest notificationRequest = NotificationRequest.builder()
|
NotificationRequest notificationRequest = NotificationRequest.builder()
|
||||||
.tenantId(tenantId)
|
.tenantId(tenantId)
|
||||||
.template(template)
|
.template(template)
|
||||||
.targets(List.of(EntityId.NULL_UUID)) // TODO: refactor
|
.targets(List.of(EntityId.NULL_UUID)) // this is temporary and will be removed when 'create from scratch' functionality is implemented for recipients
|
||||||
.status(NotificationRequestStatus.PROCESSING)
|
.status(NotificationRequestStatus.PROCESSING)
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -46,7 +46,7 @@ public class DefaultJwtSettingsService implements JwtSettingsService {
|
|||||||
private final AdminSettingsService adminSettingsService;
|
private final AdminSettingsService adminSettingsService;
|
||||||
@Lazy
|
@Lazy
|
||||||
private final Optional<TbClusterService> tbClusterService;
|
private final Optional<TbClusterService> tbClusterService;
|
||||||
private final NotificationCenter notificationCenter;
|
private final Optional<NotificationCenter> notificationCenter;
|
||||||
private final JwtSettingsValidator jwtSettingsValidator;
|
private final JwtSettingsValidator jwtSettingsValidator;
|
||||||
|
|
||||||
@Value("${security.jwt.tokenExpirationTime:9000}")
|
@Value("${security.jwt.tokenExpirationTime:9000}")
|
||||||
@ -128,7 +128,9 @@ public class DefaultJwtSettingsService implements JwtSettingsService {
|
|||||||
log.warn("WARNING: The platform is configured to use default JWT Signing Key. " +
|
log.warn("WARNING: The platform is configured to use default JWT Signing Key. " +
|
||||||
"This is a security issue that needs to be resolved. Please change the JWT Signing Key using the Web UI. " +
|
"This is a security issue that needs to be resolved. Please change the JWT Signing Key using the Web UI. " +
|
||||||
"Navigate to \"System settings -> Security settings\" while logged in as a System Administrator.");
|
"Navigate to \"System settings -> Security settings\" while logged in as a System Administrator.");
|
||||||
|
notificationCenter.ifPresent(notificationCenter -> {
|
||||||
notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(), DefaultNotifications.jwtSigningKeyIssue.toTemplate());
|
notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(), DefaultNotifications.jwtSigningKeyIssue.toTemplate());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.jwtSettings = result;
|
this.jwtSettings = result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,14 +16,22 @@
|
|||||||
package org.thingsboard.server.service.security.auth;
|
package org.thingsboard.server.service.security.auth;
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||||
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
|
import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.common.data.security.model.JwtSettings;
|
import org.thingsboard.server.common.data.security.model.JwtSettings;
|
||||||
import org.thingsboard.server.common.data.security.model.JwtToken;
|
import org.thingsboard.server.common.data.security.model.JwtToken;
|
||||||
|
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||||
|
import org.thingsboard.server.service.security.auth.jwt.settings.DefaultJwtSettingsService;
|
||||||
|
import org.thingsboard.server.service.security.auth.jwt.settings.DefaultJwtSettingsValidator;
|
||||||
import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService;
|
import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService;
|
||||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||||
import org.thingsboard.server.service.security.model.UserPrincipal;
|
import org.thingsboard.server.service.security.model.UserPrincipal;
|
||||||
@ -33,28 +41,40 @@ import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
|
|||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.BDDMockito.willReturn;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.isA;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class JwtTokenFactoryTest {
|
public class JwtTokenFactoryTest {
|
||||||
|
|
||||||
private static JwtTokenFactory tokenFactory;
|
private JwtTokenFactory tokenFactory;
|
||||||
private static JwtSettings jwtSettings;
|
private AdminSettingsService adminSettingsService;
|
||||||
|
private NotificationCenter notificationCenter;
|
||||||
|
private JwtSettingsService jwtSettingsService;
|
||||||
|
|
||||||
@BeforeClass
|
private JwtSettings jwtSettings;
|
||||||
public static void beforeAll() {
|
|
||||||
|
@Before
|
||||||
|
public void beforeEach() {
|
||||||
jwtSettings = new JwtSettings();
|
jwtSettings = new JwtSettings();
|
||||||
jwtSettings.setTokenIssuer("tb");
|
jwtSettings.setTokenIssuer("tb");
|
||||||
jwtSettings.setTokenSigningKey("abewafaf");
|
jwtSettings.setTokenSigningKey("abewafaf");
|
||||||
jwtSettings.setTokenExpirationTime((int) TimeUnit.HOURS.toSeconds(2));
|
jwtSettings.setTokenExpirationTime((int) TimeUnit.HOURS.toSeconds(2));
|
||||||
jwtSettings.setRefreshTokenExpTime((int) TimeUnit.DAYS.toSeconds(7));
|
jwtSettings.setRefreshTokenExpTime((int) TimeUnit.DAYS.toSeconds(7));
|
||||||
|
|
||||||
JwtSettingsService jwtSettingsService = mock(JwtSettingsService.class);
|
adminSettingsService = mock(AdminSettingsService.class);
|
||||||
willReturn(jwtSettings).given(jwtSettingsService).getJwtSettings();
|
notificationCenter = mock(NotificationCenter.class);
|
||||||
|
jwtSettingsService = mockJwtSettingsService();
|
||||||
|
mockJwtSettings(jwtSettings);
|
||||||
|
|
||||||
tokenFactory = new JwtTokenFactory(jwtSettingsService);
|
tokenFactory = new JwtTokenFactory(jwtSettingsService);
|
||||||
}
|
}
|
||||||
@ -150,6 +170,33 @@ public class JwtTokenFactoryTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJwtSigningKeyIssueNotification() {
|
||||||
|
JwtSettings badJwtSettings = jwtSettings;
|
||||||
|
badJwtSettings.setTokenSigningKey(JwtSettingsService.TOKEN_SIGNING_KEY_DEFAULT);
|
||||||
|
mockJwtSettings(badJwtSettings);
|
||||||
|
jwtSettingsService = mockJwtSettingsService();
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) { // to check if notification is not sent twice
|
||||||
|
jwtSettingsService.getJwtSettings();
|
||||||
|
}
|
||||||
|
verify(notificationCenter, times(1)).sendGeneralWebNotification(eq(TenantId.SYS_TENANT_ID),
|
||||||
|
isA(SystemAdministratorsFilter.class), argThat(template -> template.getConfiguration().getDeliveryMethodsTemplates().get(NotificationDeliveryMethod.WEB)
|
||||||
|
.getBody().contains("The platform is configured to use default JWT Signing Key")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockJwtSettings(JwtSettings settings) {
|
||||||
|
AdminSettings adminJwtSettings = new AdminSettings();
|
||||||
|
adminJwtSettings.setJsonValue(JacksonUtil.valueToTree(settings));
|
||||||
|
when(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, JwtSettingsService.ADMIN_SETTINGS_JWT_KEY))
|
||||||
|
.thenReturn(adminJwtSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultJwtSettingsService mockJwtSettingsService() {
|
||||||
|
return new DefaultJwtSettingsService(adminSettingsService, Optional.empty(),
|
||||||
|
Optional.of(notificationCenter), new DefaultJwtSettingsValidator());
|
||||||
|
}
|
||||||
|
|
||||||
private void checkExpirationTime(JwtToken jwtToken, int tokenLifetime) {
|
private void checkExpirationTime(JwtToken jwtToken, int tokenLifetime) {
|
||||||
Claims claims = tokenFactory.parseTokenClaims(jwtToken).getBody();
|
Claims claims = tokenFactory.parseTokenClaims(jwtToken).getBody();
|
||||||
assertThat(claims.getExpiration()).matches(actualExpirationTime -> {
|
assertThat(claims.getExpiration()).matches(actualExpirationTime -> {
|
||||||
|
|||||||
@ -66,6 +66,9 @@ import static org.thingsboard.server.dao.DaoUtil.toUUIDs;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class DefaultNotifications {
|
public class DefaultNotifications {
|
||||||
|
|
||||||
|
private static final String YELLOW_COLOR = "#F9D916";
|
||||||
|
private static final String RED_COLOR = "#e91a1a";
|
||||||
|
|
||||||
public static final DefaultNotification maintenanceWork = DefaultNotification.builder()
|
public static final DefaultNotification maintenanceWork = DefaultNotification.builder()
|
||||||
.name("Maintenance work notification")
|
.name("Maintenance work notification")
|
||||||
.subject("Infrastructure maintenance")
|
.subject("Infrastructure maintenance")
|
||||||
@ -77,7 +80,7 @@ public class DefaultNotifications {
|
|||||||
.type(NotificationType.ENTITIES_LIMIT)
|
.type(NotificationType.ENTITIES_LIMIT)
|
||||||
.subject("${entityType}s limit will be reached soon for tenant ${tenantName}")
|
.subject("${entityType}s limit will be reached soon for tenant ${tenantName}")
|
||||||
.text("${entityType}s usage: ${currentCount}/${limit} (${percents}%)")
|
.text("${entityType}s usage: ${currentCount}/${limit} (${percents}%)")
|
||||||
.icon("warning").color("#F9D916")
|
.icon("warning").color(YELLOW_COLOR)
|
||||||
.rule(DefaultRule.builder()
|
.rule(DefaultRule.builder()
|
||||||
.name("Entities count limit (sysadmin)")
|
.name("Entities count limit (sysadmin)")
|
||||||
.triggerConfig(EntitiesLimitNotificationRuleTriggerConfig.builder()
|
.triggerConfig(EntitiesLimitNotificationRuleTriggerConfig.builder()
|
||||||
@ -100,7 +103,7 @@ public class DefaultNotifications {
|
|||||||
.type(NotificationType.API_USAGE_LIMIT)
|
.type(NotificationType.API_USAGE_LIMIT)
|
||||||
.subject("${feature} feature will be disabled soon for tenant ${tenantName}")
|
.subject("${feature} feature will be disabled soon for tenant ${tenantName}")
|
||||||
.text("Usage: ${currentValue} out of ${limit} ${unitLabel}s")
|
.text("Usage: ${currentValue} out of ${limit} ${unitLabel}s")
|
||||||
.icon("warning").color("#F9D916")
|
.icon("warning").color(YELLOW_COLOR)
|
||||||
.rule(DefaultRule.builder()
|
.rule(DefaultRule.builder()
|
||||||
.name("API feature warning (sysadmin)")
|
.name("API feature warning (sysadmin)")
|
||||||
.triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder()
|
.triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder()
|
||||||
@ -123,7 +126,7 @@ public class DefaultNotifications {
|
|||||||
.type(NotificationType.API_USAGE_LIMIT)
|
.type(NotificationType.API_USAGE_LIMIT)
|
||||||
.subject("${feature} feature was disabled for tenant ${tenantName}")
|
.subject("${feature} feature was disabled for tenant ${tenantName}")
|
||||||
.text("Used ${currentValue} out of ${limit} ${unitLabel}s")
|
.text("Used ${currentValue} out of ${limit} ${unitLabel}s")
|
||||||
.icon("block").color("#e91a1a")
|
.icon("block").color(RED_COLOR)
|
||||||
.rule(DefaultRule.builder()
|
.rule(DefaultRule.builder()
|
||||||
.name("API feature disabled (sysadmin)")
|
.name("API feature disabled (sysadmin)")
|
||||||
.triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder()
|
.triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder()
|
||||||
@ -147,7 +150,7 @@ public class DefaultNotifications {
|
|||||||
.type(NotificationType.RATE_LIMITS)
|
.type(NotificationType.RATE_LIMITS)
|
||||||
.subject("Rate limits exceeded")
|
.subject("Rate limits exceeded")
|
||||||
.text("Rate limits for ${api} exceeded")
|
.text("Rate limits for ${api} exceeded")
|
||||||
.icon("block").color("#e91a1a")
|
.icon("block").color(RED_COLOR)
|
||||||
.rule(DefaultRule.builder()
|
.rule(DefaultRule.builder()
|
||||||
.name("Per-tenant rate limits exceeded")
|
.name("Per-tenant rate limits exceeded")
|
||||||
.triggerConfig(RateLimitsNotificationRuleTriggerConfig.builder()
|
.triggerConfig(RateLimitsNotificationRuleTriggerConfig.builder()
|
||||||
@ -164,7 +167,7 @@ public class DefaultNotifications {
|
|||||||
.type(NotificationType.RATE_LIMITS)
|
.type(NotificationType.RATE_LIMITS)
|
||||||
.subject("Rate limits exceeded")
|
.subject("Rate limits exceeded")
|
||||||
.text("Rate limits for ${api} exceeded for '${limitLevelEntityName}'")
|
.text("Rate limits for ${api} exceeded for '${limitLevelEntityName}'")
|
||||||
.icon("block").color("#e91a1a")
|
.icon("block").color(RED_COLOR)
|
||||||
.rule(DefaultRule.builder()
|
.rule(DefaultRule.builder()
|
||||||
.name("Per-entity rate limits exceeded")
|
.name("Per-entity rate limits exceeded")
|
||||||
.triggerConfig(RateLimitsNotificationRuleTriggerConfig.builder()
|
.triggerConfig(RateLimitsNotificationRuleTriggerConfig.builder()
|
||||||
@ -328,7 +331,7 @@ public class DefaultNotifications {
|
|||||||
.type(NotificationType.GENERAL)
|
.type(NotificationType.GENERAL)
|
||||||
.subject("WARNING: security issue")
|
.subject("WARNING: security issue")
|
||||||
.text("The platform is configured to use default JWT Signing Key. Please change it on the security settings page")
|
.text("The platform is configured to use default JWT Signing Key. Please change it on the security settings page")
|
||||||
.icon("warning").color("#F9D916")
|
.icon("warning").color(YELLOW_COLOR)
|
||||||
.button("Go to settings").link("/security-settings/general")
|
.button("Go to settings").link("/security-settings/general")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user