Browse Source

Notification to sysadmin when default JWT signing key is used

pull/9104/head
ViacheslavKlimov 3 years ago
parent
commit
ba2ded02b1
  1. 52
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
  2. 5
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java
  3. 4
      application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java
  4. 23
      application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
  5. 9
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java
  6. 4
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java

52
application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.NotificationRuleId;
@ -39,11 +40,12 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus
import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings;
import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
@ -165,16 +167,54 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
.settings(settings)
.build();
processNotificationRequestAsync(ctx, targets, callback);
return request;
}
@Override
public void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template) {
NotificationTarget target = new NotificationTarget();
target.setTenantId(tenantId);
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
targetConfig.setUsersFilter(recipients);
target.setConfiguration(targetConfig);
NotificationRequest notificationRequest = NotificationRequest.builder()
.tenantId(tenantId)
.template(template)
.targets(List.of(EntityId.NULL_UUID)) // TODO: refactor
.status(NotificationRequestStatus.PROCESSING)
.build();
try {
notificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest);
NotificationProcessingContext ctx = NotificationProcessingContext.builder()
.tenantId(tenantId)
.request(notificationRequest)
.deliveryMethods(Set.of(NotificationDeliveryMethod.WEB))
.template(template)
.build();
processNotificationRequestAsync(ctx, List.of(target), null);
} catch (Exception e) {
log.error("Failed to process notification request for recipients {} for template '{}'", recipients, template.getName(), e);
}
}
private void processNotificationRequestAsync(NotificationProcessingContext ctx, List<NotificationTarget> targets, Consumer<NotificationRequestStats> callback) {
notificationExecutor.submit(() -> {
NotificationRequestId requestId = ctx.getRequest().getId();
for (NotificationTarget target : targets) {
processForTarget(target, ctx);
try {
processForTarget(target, ctx);
} catch (Exception e) {
log.error("[{}] Failed to process notification request for target {}", requestId, target.getId());
}
}
NotificationRequestId requestId = ctx.getRequest().getId();
log.debug("[{}] Notification request processing is finished", requestId);
NotificationRequestStats stats = ctx.getStats();
try {
notificationRequestService.updateNotificationRequest(tenantId, requestId, NotificationRequestStatus.SENT, stats);
notificationRequestService.updateNotificationRequest(ctx.getTenantId(), requestId, NotificationRequestStatus.SENT, stats);
} catch (Exception e) {
log.error("[{}] Failed to update stats for notification request", requestId, e);
}
@ -187,8 +227,6 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
}
});
return request;
}
private void processForTarget(NotificationTarget target, NotificationProcessingContext ctx) {

5
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java

@ -22,11 +22,14 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.security.model.JwtSettings;
import org.thingsboard.server.dao.notification.DefaultNotifications;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import java.nio.charset.StandardCharsets;
@ -43,6 +46,7 @@ public class DefaultJwtSettingsService implements JwtSettingsService {
private final AdminSettingsService adminSettingsService;
@Lazy
private final Optional<TbClusterService> tbClusterService;
private final NotificationCenter notificationCenter;
private final JwtSettingsValidator jwtSettingsValidator;
@Value("${security.jwt.tokenExpirationTime:9000}")
@ -124,6 +128,7 @@ public class DefaultJwtSettingsService implements JwtSettingsService {
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. " +
"Navigate to \"System settings -> Security settings\" while logged in as a System Administrator.");
notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(), DefaultNotifications.jwtSigningKeyIssue.toTemplate());
}
this.jwtSettings = result;
}

4
application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java

@ -265,4 +265,8 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
return (NotificationApiWsClient) super.getWsClient();
}
@Override
public NotificationApiWsClient getAnotherWsClient() {
return (NotificationApiWsClient) super.getAnotherWsClient();
}
}

23
application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.NotificationRuleId;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.Notification;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.NotificationRequest;
@ -47,6 +48,7 @@ import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNot
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
@ -61,6 +63,7 @@ import org.thingsboard.server.common.data.notification.template.SlackDeliveryMet
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.notification.DefaultNotifications;
import org.thingsboard.server.dao.notification.NotificationDao;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
@ -601,6 +604,24 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
assertThat(stats.getErrors().get(NotificationDeliveryMethod.SLACK).values()).containsExactly(errorMessage);
}
@Test
public void testInternalGeneralWebNotifications() throws Exception {
loginSysAdmin();
getAnotherWsClient().subscribeForUnreadNotifications(10).waitForReply(true);
getAnotherWsClient().registerWaitForUpdate();
DefaultNotifications.DefaultNotification expectedNotification = DefaultNotifications.maintenanceWork;
notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(),
expectedNotification.toTemplate());
getAnotherWsClient().waitForUpdate(true);
Notification notification = getAnotherWsClient().getLastDataUpdate().getUpdate();
assertThat(notification.getSubject()).isEqualTo(expectedNotification.getSubject());
assertThat(notification.getText()).isEqualTo(expectedNotification.getText());
System.err.println(notification);
}
@Test
public void testMicrosoftTeamsNotifications() throws Exception {
RestTemplate restTemplate = mock(RestTemplate.class);
@ -688,7 +709,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
protected void connectOtherWsClient() throws Exception {
loginCustomerUser();
otherWsClient = (NotificationApiWsClient) super.getAnotherWsClient();
otherWsClient = super.getAnotherWsClient();
loginTenantAdmin();
}

9
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java

@ -323,6 +323,15 @@ public class DefaultNotifications {
.build())
.build();
public static final DefaultNotification jwtSigningKeyIssue = DefaultNotification.builder()
.name("JWT Signing Key issue notification")
.type(NotificationType.GENERAL)
.subject("WARNING: security issue")
.text("The platform is configured to use default JWT Signing Key. Please change it on the security settings page")
.icon("warning").color("#F9D916")
.button("Go to settings").link("/security-settings/general")
.build();
private final NotificationTemplateService templateService;
private final NotificationRuleService ruleService;

4
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java

@ -22,6 +22,8 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import java.util.Set;
import java.util.function.Consumer;
@ -30,6 +32,8 @@ public interface NotificationCenter {
NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, Consumer<NotificationRequestStats> callback);
void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template);
void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId);
void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId);

Loading…
Cancel
Save