diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index a8eee51f17..8436463afb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -39,6 +39,9 @@ import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.FeaturesInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.SystemInfo; import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -403,4 +406,14 @@ public class AdminController extends BaseController { return systemInfoService.getSystemInfo(); } + @ApiOperation(value = "Get features info (getFeaturesInfo)", + notes = "Get information about enabled/disabled features. " + + SYSTEM_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('SYS_ADMIN')") + @RequestMapping(value = "/featuresInfo", method = RequestMethod.GET) + @ResponseBody + public FeaturesInfo getFeaturesInfo() { + return systemInfoService.getFeaturesInfo(); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 9b251687f4..e6f98af4a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.system; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.ProtocolStringList; import lombok.RequiredArgsConstructor; @@ -22,7 +23,9 @@ import lombok.extern.slf4j.Slf4j; 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.ApiUsageState; +import org.thingsboard.server.common.data.FeaturesInfo; import org.thingsboard.server.common.data.SystemInfo; import org.thingsboard.server.common.data.SystemInfoData; import org.thingsboard.server.common.data.id.TenantId; @@ -33,6 +36,9 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.stats.TbApiUsageStateClient; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.queue.discovery.DiscoveryService; import org.thingsboard.server.queue.discovery.PartitionService; @@ -81,6 +87,8 @@ public class DefaultSystemInfoService extends TbApplicationEventListener clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfoWithCurrentSystemInfo()); diff --git a/application/src/main/java/org/thingsboard/server/service/system/SystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/SystemInfoService.java index 3b48d809b2..1ca0fb8fe0 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/SystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/SystemInfoService.java @@ -15,8 +15,11 @@ */ package org.thingsboard.server.service.system; +import org.thingsboard.server.common.data.FeaturesInfo; import org.thingsboard.server.common.data.SystemInfo; public interface SystemInfoService { SystemInfo getSystemInfo(); + + FeaturesInfo getFeaturesInfo(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseHomePageApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseHomePageApiTest.java index 07fa2ddaad..c2bf7b253c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseHomePageApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseHomePageApiTest.java @@ -15,19 +15,33 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.FeaturesInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; @@ -41,10 +55,14 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.concurrent.TimeUnit; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + @Slf4j public abstract class BaseHomePageApiTest extends AbstractControllerTest { @@ -238,4 +256,120 @@ public abstract class BaseHomePageApiTest extends AbstractControllerTest { } } + @Test + public void testGetFeaturesInfo() throws Exception { + loginSysAdmin(); + + FeaturesInfo featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); + Assert.assertNotNull(featuresInfo); + Assert.assertFalse(featuresInfo.isEmailEnabled()); + Assert.assertFalse(featuresInfo.isSmsEnabled()); + Assert.assertFalse(featuresInfo.isTwoFaEnabled()); + Assert.assertFalse(featuresInfo.isNotificationEnabled()); + Assert.assertFalse(featuresInfo.isOauthEnabled()); + + AdminSettings mailSettings = doGet("/api/admin/settings/mail", AdminSettings.class); + + JsonNode jsonValue = mailSettings.getJsonValue(); + ((ObjectNode) jsonValue).put("mailFrom", "test@thingsboard.org"); + mailSettings.setJsonValue(jsonValue); + + doPost("/api/admin/settings", mailSettings).andExpect(status().isOk()); + + featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); + Assert.assertTrue(featuresInfo.isEmailEnabled()); + Assert.assertFalse(featuresInfo.isSmsEnabled()); + Assert.assertFalse(featuresInfo.isTwoFaEnabled()); + Assert.assertFalse(featuresInfo.isNotificationEnabled()); + Assert.assertFalse(featuresInfo.isOauthEnabled()); + + AdminSettings smsSettings = new AdminSettings(); + smsSettings.setKey("sms"); + smsSettings.setJsonValue(JacksonUtil.newObjectNode()); + doPost("/api/admin/settings", smsSettings).andExpect(status().isOk()); + + featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); + Assert.assertTrue(featuresInfo.isEmailEnabled()); + Assert.assertTrue(featuresInfo.isSmsEnabled()); + Assert.assertFalse(featuresInfo.isTwoFaEnabled()); + Assert.assertFalse(featuresInfo.isNotificationEnabled()); + Assert.assertFalse(featuresInfo.isOauthEnabled()); + + AdminSettings twoFaSettingsSettings = new AdminSettings(); + twoFaSettingsSettings.setKey("twoFaSettings"); + twoFaSettingsSettings.setJsonValue(JacksonUtil.newObjectNode()); + doPost("/api/admin/settings", twoFaSettingsSettings).andExpect(status().isOk()); + + featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); + Assert.assertTrue(featuresInfo.isEmailEnabled()); + Assert.assertTrue(featuresInfo.isSmsEnabled()); + Assert.assertTrue(featuresInfo.isTwoFaEnabled()); + Assert.assertFalse(featuresInfo.isNotificationEnabled()); + Assert.assertFalse(featuresInfo.isOauthEnabled()); + + AdminSettings notificationsSettings = new AdminSettings(); + notificationsSettings.setKey("notifications"); + notificationsSettings.setJsonValue(JacksonUtil.newObjectNode()); + doPost("/api/admin/settings", notificationsSettings).andExpect(status().isOk()); + + featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); + Assert.assertTrue(featuresInfo.isEmailEnabled()); + Assert.assertTrue(featuresInfo.isSmsEnabled()); + Assert.assertTrue(featuresInfo.isTwoFaEnabled()); + Assert.assertTrue(featuresInfo.isNotificationEnabled()); + Assert.assertFalse(featuresInfo.isOauthEnabled()); + + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + + doPost("/api/oauth2/config", oAuth2Info).andExpect(status().isOk()); + + featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); + Assert.assertNotNull(featuresInfo); + Assert.assertTrue(featuresInfo.isEmailEnabled()); + Assert.assertTrue(featuresInfo.isSmsEnabled()); + Assert.assertTrue(featuresInfo.isTwoFaEnabled()); + Assert.assertTrue(featuresInfo.isNotificationEnabled()); + Assert.assertTrue(featuresInfo.isOauthEnabled()); + } + + + private OAuth2Info createDefaultOAuth2Info() { + return new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build() + )) + .mobileInfos(Collections.emptyList()) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo() + )) + .build() + )); + } + + private OAuth2RegistrationInfo validRegistrationInfo() { + return OAuth2RegistrationInfo.builder() + .clientId(UUID.randomUUID().toString()) + .clientSecret(UUID.randomUUID().toString()) + .authorizationUri(UUID.randomUUID().toString()) + .accessTokenUri(UUID.randomUUID().toString()) + .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) + .platforms(Collections.emptyList()) + .userInfoUri(UUID.randomUUID().toString()) + .userNameAttributeName(UUID.randomUUID().toString()) + .jwkSetUri(UUID.randomUUID().toString()) + .clientAuthenticationMethod(UUID.randomUUID().toString()) + .loginButtonLabel(UUID.randomUUID().toString()) + .mapperConfig( + OAuth2MapperConfig.builder() + .type(MapperType.CUSTOM) + .custom( + OAuth2CustomMapperConfig.builder() + .url(UUID.randomUUID().toString()) + .build() + ) + .build() + ) + .build(); + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/FeaturesInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/FeaturesInfo.java new file mode 100644 index 0000000000..480e29a6a4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/FeaturesInfo.java @@ -0,0 +1,27 @@ +/** + * 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.common.data; + +import lombok.Data; + +@Data +public class FeaturesInfo { + boolean isEmailEnabled; + boolean isSmsEnabled; + boolean isNotificationEnabled; + boolean isOauthEnabled; + boolean isTwoFaEnabled; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index e95fee79ae..0a75e07bd1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -94,7 +94,7 @@ public abstract class DataValidator> { } } - private static boolean doValidateEmail(String email) { + public static boolean doValidateEmail(String email) { if (email == null) { return false; }