diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java b/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java index 8ccc94502b..8f74aab7af 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,20 +31,35 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.HomeDashboardInfo; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.Views; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.mobile.LoginMobileInfo; +import org.thingsboard.server.common.data.mobile.UserMobileInfo; import org.thingsboard.server.common.data.mobile.app.MobileApp; +import org.thingsboard.server.common.data.mobile.app.MobileAppVersionInfo; +import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; +import org.thingsboard.server.common.data.mobile.layout.MobilePage; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.mobile.TbMobileAppService; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.Collections; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; +import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; @@ -58,6 +76,41 @@ public class MobileAppController extends BaseController { private final TbMobileAppService tbMobileAppService; + @ApiOperation(value = "Get mobile app login info (getLoginMobileInfo)") + @GetMapping(value = "/api/noauth/mobile") + public LoginMobileInfo getLoginMobileInfo(@Parameter(description = "Mobile application package name") + @RequestParam String pkgName, + @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) + @RequestParam PlatformType platform) { + List oauth2Clients = oAuth2ClientService.findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(pkgName, platform); + MobileApp mobileApp = mobileAppService.findMobileAppByPkgNameAndPlatformType(pkgName, platform); + return new LoginMobileInfo(oauth2Clients, mobileApp != null ? mobileApp.getVersionInfo() : null); + } + + @ApiOperation(value = "Get user mobile app basic info (getUserMobileInfo)", notes = AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/api/mobile") + public UserMobileInfo getUserMobileInfo(@Parameter(description = "Mobile application package name") + @RequestParam String pkgName, + @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) + @RequestParam PlatformType platform) throws ThingsboardException, JsonProcessingException { + SecurityUser securityUser = getCurrentUser(); + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); + HomeDashboardInfo homeDashboardInfo = securityUser.isSystemAdmin() ? null : getHomeDashboardInfo(securityUser, user.getAdditionalInfo()); + MobileAppBundle mobileAppBundle = mobileAppBundleService.findMobileAppBundleByPkgNameAndPlatform(securityUser.getTenantId(), pkgName, platform); + return new UserMobileInfo(user, homeDashboardInfo, getVisiblePages(mobileAppBundle)); + } + + @ApiOperation(value = "Get mobile app version info (getMobileVersionInfo)") + @GetMapping(value = "/api/mobile/versionInfo") + public MobileAppVersionInfo getMobileVersionInfo(@Parameter(description = "Mobile application package name") + @RequestParam String pkgName, + @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) + @RequestParam PlatformType platform) { + MobileApp mobileApp = mobileAppService.findMobileAppByPkgNameAndPlatformType(pkgName, platform); + return mobileApp != null ? mobileApp.getVersionInfo() : null; + } + @ApiOperation(value = "Save Or update Mobile app (saveMobileApp)", notes = "Create or update the Mobile app. When creating mobile app, platform generates Mobile App Id as " + UUID_WIKI_LINK + "The newly created Mobile App Id will be present in the response. " + @@ -111,4 +164,16 @@ public class MobileAppController extends BaseController { tbMobileAppService.delete(mobileApp, getCurrentUser()); } + private List getVisiblePages(MobileAppBundle mobileAppBundle) throws JsonProcessingException { + if (mobileAppBundle != null && mobileAppBundle.getLayoutConfig() != null) { + List mobilePages = mobileAppBundle.getLayoutConfig().getPages() + .stream() + .filter(MobilePage::isVisible) + .collect(Collectors.toList()); + return JacksonUtil.readValue(JacksonUtil.writeValueAsViewIgnoringNullFields(mobilePages, Views.Public.class), new TypeReference<>() {}); + } else { + return Collections.emptyList(); + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java deleted file mode 100644 index 15ff2a7346..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright © 2016-2024 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.controller; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.RequiredArgsConstructor; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.HomeDashboardInfo; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.Views; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.mobile.LoginMobileInfo; -import org.thingsboard.server.common.data.mobile.app.MobileApp; -import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; -import org.thingsboard.server.common.data.mobile.app.MobileAppVersionInfo; -import org.thingsboard.server.common.data.mobile.UserMobileInfo; -import org.thingsboard.server.common.data.mobile.layout.MobilePage; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo; -import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.config.annotations.ApiOperation; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.model.SecurityUser; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; - -@RequiredArgsConstructor -@RestController -@TbCoreComponent -public class MobileV2Controller extends BaseController { - - @ApiOperation(value = "Get mobile app login info (getLoginMobileInfo)") - @GetMapping(value = "/api/noauth/mobile") - public LoginMobileInfo getLoginMobileInfo(@Parameter(description = "Mobile application package name") - @RequestParam String pkgName, - @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) - @RequestParam PlatformType platform) { - List oauth2Clients = oAuth2ClientService.findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(pkgName, platform); - MobileApp mobileApp = mobileAppService.findMobileAppByPkgNameAndPlatformType(pkgName, platform); - return new LoginMobileInfo(oauth2Clients, mobileApp != null ? mobileApp.getVersionInfo() : null); - } - - @ApiOperation(value = "Get user mobile app basic info (getUserMobileInfo)", notes = AVAILABLE_FOR_ANY_AUTHORIZED_USER) - @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") - @GetMapping(value = "/api/mobile") - public UserMobileInfo getUserMobileInfo(@Parameter(description = "Mobile application package name") - @RequestParam String pkgName, - @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) - @RequestParam PlatformType platform) throws ThingsboardException, JsonProcessingException { - SecurityUser securityUser = getCurrentUser(); - User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); - HomeDashboardInfo homeDashboardInfo = securityUser.isSystemAdmin() ? null : getHomeDashboardInfo(securityUser, user.getAdditionalInfo()); - MobileAppBundle mobileAppBundle = mobileAppBundleService.findMobileAppBundleByPkgNameAndPlatform(securityUser.getTenantId(), pkgName, platform); - return new UserMobileInfo(user, homeDashboardInfo, getVisiblePages(mobileAppBundle)); - } - - @ApiOperation(value = "Get mobile app version info (getMobileVersionInfo)") - @GetMapping(value = "/api/mobile/versionInfo") - public MobileAppVersionInfo getMobileVersionInfo(@Parameter(description = "Mobile application package name") - @RequestParam String pkgName, - @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) - @RequestParam PlatformType platform) { - MobileApp mobileApp = mobileAppService.findMobileAppByPkgNameAndPlatformType(pkgName, platform); - return mobileApp != null ? mobileApp.getVersionInfo() : null; - } - - private List getVisiblePages(MobileAppBundle mobileAppBundle) throws JsonProcessingException { - if (mobileAppBundle != null && mobileAppBundle.getLayoutConfig() != null) { - List mobilePages = mobileAppBundle.getLayoutConfig().getPages() - .stream() - .filter(MobilePage::isVisible) - .collect(Collectors.toList()); - return JacksonUtil.readValue(JacksonUtil.writeValueAsViewIgnoringNullFields(mobilePages, Views.Public.class), new TypeReference<>() {}); - } else { - return Collections.emptyList(); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java index 0b2cb8fe8f..547cdc738f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -126,9 +126,9 @@ public class QrCodeSettingsController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PostMapping(value = "/api/mobile/qr/settings") public QrCodeSettings saveQrCodeSettings(@Parameter(description = "A JSON value representing the mobile apps configuration") - @RequestBody QrCodeSettings qrCodeSettings) throws ThingsboardException { + @RequestBody QrCodeSettings qrCodeSettings) throws ThingsboardException { SecurityUser currentUser = getCurrentUser(); - accessControlService.checkPermission(currentUser, Resource.QR_CODE_SETTINGS, Operation.WRITE); + accessControlService.checkPermission(currentUser, Resource.MOBILE_APP_SETTINGS, Operation.WRITE); qrCodeSettings.setTenantId(getTenantId()); return qrCodeSettingService.saveQrCodeSettings(currentUser.getTenantId(), qrCodeSettings); } @@ -137,9 +137,9 @@ public class QrCodeSettingsController extends BaseController { notes = "The response payload contains configuration for android/iOS applications and platform qr code widget settings." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/api/mobile/qr/settings") - public QrCodeSettings getQrAppSettings() throws ThingsboardException { + public QrCodeSettings getQrCodeSettings() throws ThingsboardException { SecurityUser currentUser = getCurrentUser(); - accessControlService.checkPermission(currentUser, Resource.QR_CODE_SETTINGS, Operation.READ); + accessControlService.checkPermission(currentUser, Resource.MOBILE_APP_SETTINGS, Operation.READ); return qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index fc50d85040..46cd1c2979 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -47,7 +47,7 @@ public class CustomerUserPermissions extends AbstractPermissions { put(Resource.DEVICE_PROFILE, profilePermissionChecker); put(Resource.ASSET_PROFILE, profilePermissionChecker); put(Resource.TB_RESOURCE, customerResourcePermissionChecker); - put(Resource.QR_CODE_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ)); + put(Resource.MOBILE_APP_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ)); } private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index dd169b5a6b..8c6f4d00e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -50,7 +50,7 @@ public enum Resource { VERSION_CONTROL, NOTIFICATION(EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE), - QR_CODE_SETTINGS; + MOBILE_APP_SETTINGS; private final Set entityTypes; Resource() { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index 9e4b7d3b1e..b3210ea68a 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -44,7 +44,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.TB_RESOURCE, systemEntityPermissionChecker); put(Resource.QUEUE, systemEntityPermissionChecker); put(Resource.NOTIFICATION, systemEntityPermissionChecker); - put(Resource.QR_CODE_SETTINGS, PermissionChecker.allowAllPermissionChecker); + put(Resource.MOBILE_APP_SETTINGS, PermissionChecker.allowAllPermissionChecker); } private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index dc77480660..a0ef8a1766 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -50,7 +50,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.QUEUE, queuePermissionChecker); put(Resource.VERSION_CONTROL, PermissionChecker.allowAllPermissionChecker); put(Resource.NOTIFICATION, tenantEntityPermissionChecker); - put(Resource.QR_CODE_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ)); + put(Resource.MOBILE_APP_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ)); put(Resource.MOBILE_APP, tenantEntityPermissionChecker); put(Resource.MOBILE_APP_BUNDLE, tenantEntityPermissionChecker); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 4eca2e93ed..d4cc6d13e3 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -610,7 +610,7 @@ cache: timeToLiveInMinutes: "${CACHE_SPECS_ALARM_TYPES_TTL:60}" # Alarm types cache TTL maxSize: "${CACHE_SPECS_ALARM_TYPES_MAX_SIZE:10000}" # 0 means the cache is disabled qrCodeSettings: - timeToLiveInMinutes: "${CACHE_SPECS_MOBILE_APP_SETTINGS_TTL:1440}" # Mobile application cache TTL + timeToLiveInMinutes: "${CACHE_SPECS_MOBILE_APP_SETTINGS_TTL:1440}" # Qr code settings cache TTL maxSize: "${CACHE_SPECS_MOBILE_APP_SETTINGS_MAX_SIZE:10000}" # 0 means the cache is disabled mobileSecretKey: timeToLiveInMinutes: "${CACHE_MOBILE_SECRET_KEY_TTL:2}" # QR secret key cache TTL diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java index bbc84e732b..87ed77c7c9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java @@ -76,9 +76,6 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements @Override public void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) { log.trace("Executing deleteMobileAppBundleById [{}]", mobileAppBundleId.getId()); - mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId()); - eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build()); - try { mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId()); eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 9d550ff341..2f3893d405 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -178,7 +178,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService { + UserMobileSessionInfo mobileInfo = findMobileSessionInfo(tenantId, userId).orElseGet(() -> { UserMobileSessionInfo newMobileInfo = new UserMobileSessionInfo(); newMobileInfo.setSessions(new HashMap<>()); return newMobileInfo; @@ -479,12 +479,12 @@ public class UserServiceImpl extends AbstractCachedEntityService findMobileSessions(TenantId tenantId, UserId userId) { - return findMobileInfo(tenantId, userId).map(UserMobileSessionInfo::getSessions).orElse(Collections.emptyMap()); + return findMobileSessionInfo(tenantId, userId).map(UserMobileSessionInfo::getSessions).orElse(Collections.emptyMap()); } @Override public MobileSessionInfo findMobileSession(TenantId tenantId, UserId userId, String mobileToken) { - return findMobileInfo(tenantId, userId).map(mobileInfo -> mobileInfo.getSessions().get(mobileToken)).orElse(null); + return findMobileSessionInfo(tenantId, userId).map(mobileInfo -> mobileInfo.getSessions().get(mobileToken)).orElse(null); } @Override @@ -495,7 +495,7 @@ public class UserServiceImpl extends AbstractCachedEntityService findMobileInfo(TenantId tenantId, UserId userId) { + private Optional findMobileSessionInfo(TenantId tenantId, UserId userId) { return Optional.ofNullable(userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.MOBILE)) .map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileSessionInfo.class)); }