Merge branch 'develop/3.5.2' into feature/widget-bundles
This commit is contained in:
commit
7f47472092
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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 org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class CryptoConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
protected BCryptPasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -188,11 +188,6 @@ public class ThingsboardSecurityConfiguration {
|
|||||||
return auth.build();
|
return auth.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
protected BCryptPasswordEncoder passwordEncoder() {
|
|
||||||
return new BCryptPasswordEncoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver;
|
private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver;
|
||||||
|
|
||||||
|
|||||||
@ -23,18 +23,18 @@ import com.google.api.client.auth.oauth2.TokenResponse;
|
|||||||
import com.google.api.client.http.GenericUrl;
|
import com.google.api.client.http.GenericUrl;
|
||||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||||
import com.google.api.client.json.gson.GsonFactory;
|
import com.google.api.client.json.gson.GsonFactory;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.mail.MailException;
|
||||||
|
import org.springframework.mail.MailSendException;
|
||||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||||
import org.thingsboard.server.common.data.AdminSettings;
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
|
||||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.mail.MailOauth2Provider;
|
import org.thingsboard.server.common.data.mail.MailOauth2Provider;
|
||||||
import org.thingsboard.server.dao.exception.IncorrectParameterException;
|
import org.thingsboard.server.dao.exception.IncorrectParameterException;
|
||||||
|
|
||||||
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -50,6 +50,7 @@ public class TbMailSender extends JavaMailSenderImpl {
|
|||||||
private static final String MAIL_PROP = "mail.";
|
private static final String MAIL_PROP = "mail.";
|
||||||
private final TbMailContextComponent ctx;
|
private final TbMailContextComponent ctx;
|
||||||
private final Lock lock;
|
private final Lock lock;
|
||||||
|
|
||||||
private final Boolean oauth2Enabled;
|
private final Boolean oauth2Enabled;
|
||||||
private volatile String accessToken;
|
private volatile String accessToken;
|
||||||
private volatile long tokenExpires;
|
private volatile long tokenExpires;
|
||||||
@ -70,14 +71,39 @@ public class TbMailSender extends JavaMailSenderImpl {
|
|||||||
setJavaMailProperties(createJavaMailProperties(jsonConfig));
|
setJavaMailProperties(createJavaMailProperties(jsonConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
public Boolean getOauth2Enabled() {
|
||||||
|
return oauth2Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTokenExpires() {
|
||||||
|
return tokenExpires;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) {
|
protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
|
||||||
if (oauth2Enabled && (System.currentTimeMillis() > tokenExpires)){
|
updateOauth2PasswordIfExpired();
|
||||||
|
doSendSuper(mimeMessages, originalMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doSendSuper(MimeMessage[] mimeMessages, Object[] originalMessages) {
|
||||||
|
super.doSend(mimeMessages, originalMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testConnection() throws MessagingException {
|
||||||
|
updateOauth2PasswordIfExpired();
|
||||||
|
testConnectionSuper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConnectionSuper() throws MessagingException {
|
||||||
|
super.testConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateOauth2PasswordIfExpired() {
|
||||||
|
if (getOauth2Enabled() && (System.currentTimeMillis() > getTokenExpires())){
|
||||||
refreshAccessToken();
|
refreshAccessToken();
|
||||||
setPassword(accessToken);
|
setPassword(accessToken);
|
||||||
}
|
}
|
||||||
super.doSend(mimeMessages, originalMessages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Properties createJavaMailProperties(JsonNode jsonConfig) {
|
private Properties createJavaMailProperties(JsonNode jsonConfig) {
|
||||||
@ -125,10 +151,10 @@ public class TbMailSender extends JavaMailSenderImpl {
|
|||||||
return javaMailProperties;
|
return javaMailProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshAccessToken() throws ThingsboardException {
|
public void refreshAccessToken() {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
if (System.currentTimeMillis() > tokenExpires) {
|
if (System.currentTimeMillis() > getTokenExpires()) {
|
||||||
AdminSettings settings = ctx.getAdminSettingsService().findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
|
AdminSettings settings = ctx.getAdminSettingsService().findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
|
||||||
JsonNode jsonValue = settings.getJsonValue();
|
JsonNode jsonValue = settings.getJsonValue();
|
||||||
|
|
||||||
@ -151,8 +177,8 @@ public class TbMailSender extends JavaMailSenderImpl {
|
|||||||
tokenExpires = System.currentTimeMillis() + (tokenResponse.getExpiresInSeconds().intValue() * 1000);
|
tokenExpires = System.currentTimeMillis() + (tokenResponse.getExpiresInSeconds().intValue() * 1000);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Unable to retrieve access token: {}", e.getMessage());
|
log.error("Unable to retrieve access token: {}", e.getMessage());
|
||||||
throw new ThingsboardException("Error while retrieving access token: " + e.getMessage(), ThingsboardErrorCode.GENERAL);
|
throw new RuntimeException("Error while retrieving access token: " + e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.thingsboard.rule.engine.api.NotificationCenter;
|
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.User;
|
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.NotificationId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationRuleId;
|
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.NotificationStatus;
|
||||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
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.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.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.NotificationRecipient;
|
||||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
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.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.targets.slack.SlackNotificationTargetConfig;
|
||||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||||
@ -165,16 +167,54 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
|||||||
.settings(settings)
|
.settings(settings)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
notificationExecutor.submit(() -> {
|
processNotificationRequestAsync(ctx, targets, callback);
|
||||||
for (NotificationTarget target : targets) {
|
return request;
|
||||||
processForTarget(target, ctx);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@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)) // this is temporary and will be removed when 'create from scratch' functionality is implemented for recipients
|
||||||
|
.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();
|
NotificationRequestId requestId = ctx.getRequest().getId();
|
||||||
|
for (NotificationTarget target : targets) {
|
||||||
|
try {
|
||||||
|
processForTarget(target, ctx);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[{}] Failed to process notification request for target {}", requestId, target.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
log.debug("[{}] Notification request processing is finished", requestId);
|
log.debug("[{}] Notification request processing is finished", requestId);
|
||||||
|
|
||||||
NotificationRequestStats stats = ctx.getStats();
|
NotificationRequestStats stats = ctx.getStats();
|
||||||
try {
|
try {
|
||||||
notificationRequestService.updateNotificationRequest(tenantId, requestId, NotificationRequestStatus.SENT, stats);
|
notificationRequestService.updateNotificationRequest(ctx.getTenantId(), requestId, NotificationRequestStatus.SENT, stats);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[{}] Failed to update stats for notification request", requestId, 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) {
|
private void processForTarget(NotificationTarget target, NotificationProcessingContext ctx) {
|
||||||
|
|||||||
@ -22,11 +22,14 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||||
import org.thingsboard.server.cluster.TbClusterService;
|
import org.thingsboard.server.cluster.TbClusterService;
|
||||||
import org.thingsboard.server.common.data.AdminSettings;
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
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.plugin.ComponentLifecycleEvent;
|
||||||
import org.thingsboard.server.common.data.security.model.JwtSettings;
|
import org.thingsboard.server.common.data.security.model.JwtSettings;
|
||||||
|
import org.thingsboard.server.dao.notification.DefaultNotifications;
|
||||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -43,6 +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 Optional<NotificationCenter> notificationCenter;
|
||||||
private final JwtSettingsValidator jwtSettingsValidator;
|
private final JwtSettingsValidator jwtSettingsValidator;
|
||||||
|
|
||||||
@Value("${security.jwt.tokenExpirationTime:9000}")
|
@Value("${security.jwt.tokenExpirationTime:9000}")
|
||||||
@ -124,6 +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());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.jwtSettings = result;
|
this.jwtSettings = result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,6 @@ import org.thingsboard.server.dao.exception.DataValidationException;
|
|||||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
import org.thingsboard.server.dao.user.UserServiceImpl;
|
import org.thingsboard.server.dao.user.UserServiceImpl;
|
||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
|
||||||
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
|
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
|
||||||
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
|
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
|
||||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||||
@ -73,7 +72,6 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@TbCoreComponent
|
|
||||||
public class DefaultSystemSecurityService implements SystemSecurityService {
|
public class DefaultSystemSecurityService implements SystemSecurityService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.service.mail;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.Session;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.willCallRealMethod;
|
||||||
|
import static org.mockito.BDDMockito.willReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
|
||||||
|
public class TbMailSenderTest {
|
||||||
|
|
||||||
|
private TbMailSender tbMailSender;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
tbMailSender = mock(TbMailSender.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoSendSendMail() {
|
||||||
|
MimeMessage mimeMsg = new MimeMessage(Session.getInstance(new Properties()));
|
||||||
|
List<MimeMessage> mimeMessages = new ArrayList<>(1);
|
||||||
|
mimeMessages.add(mimeMsg);
|
||||||
|
|
||||||
|
willCallRealMethod().given(tbMailSender).doSend(any(), any());
|
||||||
|
tbMailSender.doSend(mimeMessages.toArray(new MimeMessage[0]), null);
|
||||||
|
|
||||||
|
Mockito.verify(tbMailSender, times(1)).updateOauth2PasswordIfExpired();
|
||||||
|
Mockito.verify(tbMailSender, times(1)).doSendSuper(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTestConnection() throws MessagingException {
|
||||||
|
willCallRealMethod().given(tbMailSender).testConnection();
|
||||||
|
tbMailSender.testConnection();
|
||||||
|
|
||||||
|
Mockito.verify(tbMailSender, times(1)).updateOauth2PasswordIfExpired();
|
||||||
|
Mockito.verify(tbMailSender, times(1)).testConnectionSuper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideSenderConfiguration")
|
||||||
|
public void testUpdateOauth2PasswordIfExpiredIfOauth2Enabled(boolean oauth2, long expiresIn, boolean passwordUpdateNeeded) {
|
||||||
|
willReturn(oauth2).given(tbMailSender).getOauth2Enabled();
|
||||||
|
willReturn(expiresIn).given(tbMailSender).getTokenExpires();
|
||||||
|
|
||||||
|
willCallRealMethod().given(tbMailSender).updateOauth2PasswordIfExpired();
|
||||||
|
tbMailSender.updateOauth2PasswordIfExpired();
|
||||||
|
|
||||||
|
if (passwordUpdateNeeded) {
|
||||||
|
Mockito.verify(tbMailSender, times(1)).refreshAccessToken();
|
||||||
|
Mockito.verify(tbMailSender, times(1)).setPassword(any());
|
||||||
|
} else {
|
||||||
|
Mockito.verify(tbMailSender, Mockito.never()).refreshAccessToken();
|
||||||
|
Mockito.verify(tbMailSender, Mockito.never()).setPassword(any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> provideSenderConfiguration() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(true, 0L, true),
|
||||||
|
Arguments.of(true, System.currentTimeMillis() + 5000, false),
|
||||||
|
Arguments.of(false, 0L, false),
|
||||||
|
Arguments.of(false, System.currentTimeMillis() + 5000, false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -265,4 +265,8 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
|
|||||||
return (NotificationApiWsClient) super.getWsClient();
|
return (NotificationApiWsClient) super.getWsClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationApiWsClient getAnotherWsClient() {
|
||||||
|
return (NotificationApiWsClient) super.getAnotherWsClient();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.DeviceId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationRuleId;
|
import org.thingsboard.server.common.data.id.NotificationRuleId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
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.Notification;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
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.NotificationTarget;
|
||||||
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
|
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.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.platform.UserListFilter;
|
||||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
|
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
|
||||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
|
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.SmsDeliveryMethodNotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
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.notification.NotificationDao;
|
||||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||||
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
||||||
@ -601,6 +604,23 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
assertThat(stats.getErrors().get(NotificationDeliveryMethod.SLACK).values()).containsExactly(errorMessage);
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMicrosoftTeamsNotifications() throws Exception {
|
public void testMicrosoftTeamsNotifications() throws Exception {
|
||||||
RestTemplate restTemplate = mock(RestTemplate.class);
|
RestTemplate restTemplate = mock(RestTemplate.class);
|
||||||
@ -688,7 +708,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
|
|
||||||
protected void connectOtherWsClient() throws Exception {
|
protected void connectOtherWsClient() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
otherWsClient = (NotificationApiWsClient) super.getAnotherWsClient();
|
otherWsClient = super.getAnotherWsClient();
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 -> {
|
||||||
|
|||||||
@ -42,6 +42,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
|||||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||||
import org.thingsboard.server.queue.discovery.QueueKey;
|
import org.thingsboard.server.queue.discovery.QueueKey;
|
||||||
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
|
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
|
||||||
|
import org.thingsboard.server.queue.usagestats.DefaultTbApiUsageReportClient;
|
||||||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -86,7 +87,7 @@ public class DefaultDeviceStateServiceTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
service = spy(new DefaultDeviceStateService(deviceService, attributesService, tsService, clusterService, partitionService, entityQueryRepository, null, null, mock(NotificationRuleProcessor.class)));
|
service = spy(new DefaultDeviceStateService(deviceService, attributesService, tsService, clusterService, partitionService, entityQueryRepository, null, mock(DefaultTbApiUsageReportClient.class), mock(NotificationRuleProcessor.class)));
|
||||||
telemetrySubscriptionService = Mockito.mock(TelemetrySubscriptionService.class);
|
telemetrySubscriptionService = Mockito.mock(TelemetrySubscriptionService.class);
|
||||||
ReflectionTestUtils.setField(service, "tsSubService", telemetrySubscriptionService);
|
ReflectionTestUtils.setField(service, "tsSubService", telemetrySubscriptionService);
|
||||||
ReflectionTestUtils.setField(service, "defaultStateCheckIntervalInSec", 60);
|
ReflectionTestUtils.setField(service, "defaultStateCheckIntervalInSec", 60);
|
||||||
|
|||||||
@ -28,6 +28,10 @@
|
|||||||
<logger name="org.thingsboard.server.transport.lwm2m.server" level="INFO"/>
|
<logger name="org.thingsboard.server.transport.lwm2m.server" level="INFO"/>
|
||||||
<logger name="org.eclipse.californium.core" level="INFO"/>
|
<logger name="org.eclipse.californium.core" level="INFO"/>
|
||||||
|
|
||||||
|
<!-- To reduce logs -->
|
||||||
|
<logger name="org.apache.catalina.loader.WebappClassLoaderBase" level="ERROR" />
|
||||||
|
<logger name="org.thingsboard.server.service.queue.DefaultTbClusterService" level="ERROR" />
|
||||||
|
|
||||||
<!-- Coap client context debug for the test scope -->
|
<!-- Coap client context debug for the test scope -->
|
||||||
<!-- <logger name="org.thingsboard.server.transport.coap.client.DefaultCoapClientContext" level="TRACE" />-->
|
<!-- <logger name="org.thingsboard.server.transport.coap.client.DefaultCoapClientContext" level="TRACE" />-->
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
@ -323,6 +326,15 @@ public class DefaultNotifications {
|
|||||||
.build())
|
.build())
|
||||||
.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(YELLOW_COLOR)
|
||||||
|
.button("Go to settings").link("/security-settings/general")
|
||||||
|
.build();
|
||||||
|
|
||||||
private final NotificationTemplateService templateService;
|
private final NotificationTemplateService templateService;
|
||||||
private final NotificationRuleService ruleService;
|
private final NotificationRuleService ruleService;
|
||||||
|
|
||||||
|
|||||||
@ -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.NotificationDeliveryMethod;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
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.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -30,6 +32,8 @@ public interface NotificationCenter {
|
|||||||
|
|
||||||
NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, Consumer<NotificationRequestStats> callback);
|
NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, Consumer<NotificationRequestStats> callback);
|
||||||
|
|
||||||
|
void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template);
|
||||||
|
|
||||||
void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId);
|
void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId);
|
||||||
|
|
||||||
void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId);
|
void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId);
|
||||||
|
|||||||
@ -33,8 +33,8 @@ public class TbMathNodeConfiguration implements NodeConfiguration<TbMathNodeConf
|
|||||||
public TbMathNodeConfiguration defaultConfiguration() {
|
public TbMathNodeConfiguration defaultConfiguration() {
|
||||||
TbMathNodeConfiguration configuration = new TbMathNodeConfiguration();
|
TbMathNodeConfiguration configuration = new TbMathNodeConfiguration();
|
||||||
configuration.setOperation(TbRuleNodeMathFunctionType.CUSTOM);
|
configuration.setOperation(TbRuleNodeMathFunctionType.CUSTOM);
|
||||||
configuration.setCustomFunction("(t - 32) / 1.8");
|
configuration.setCustomFunction("(x - 32) / 1.8");
|
||||||
configuration.setArguments(List.of(new TbMathArgument("t", TbMathArgumentType.MESSAGE_BODY, "temperature")));
|
configuration.setArguments(List.of(new TbMathArgument("x", TbMathArgumentType.MESSAGE_BODY, "temperature")));
|
||||||
configuration.setResult(new TbMathResult(TbMathArgumentType.MESSAGE_BODY, "temperatureCelsius", 2, false, false, null));
|
configuration.setResult(new TbMathResult(TbMathArgumentType.MESSAGE_BODY, "temperatureCelsius", 2, false, false, null));
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user