From 0b6de71768c6ff356224c177b222d5b50a1e66d1 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 19 May 2022 12:01:06 +0300 Subject: [PATCH 1/4] Implement version control ssh access with private key. Improve VC settings REST methods. Add tenantId field to admin settings. Add DB upgrade. --- application/pom.xml | 4 + .../main/data/upgrade/3.3.4/schema_update.sql | 3 + .../server/controller/AdminController.java | 61 +++++++- .../EntitiesVersionControlController.java | 47 +----- .../install/ThingsboardInstallService.java | 1 + .../fetch/AdminSettingsEdgeEventFetcher.java | 7 +- .../DefaultSystemDataLoaderService.java | 2 + .../install/SqlDatabaseUpgradeService.java | 12 ++ .../permission/TenantAdminPermissions.java | 1 + .../system/DefaultSystemSecurityService.java | 1 + .../DefaultEntitiesVersionControlService.java | 145 ++++++++++-------- .../vc/EntitiesVersionControlService.java | 12 +- .../server/utils/GitRepository.java | 131 +++++++++++++--- .../server/common/data/AdminSettings.java | 19 ++- .../server/common/data/StringUtils.java | 42 +++++ .../vc}/EntitiesVersionControlSettings.java | 6 +- .../data/vc/VersionControlAuthMethod.java | 21 +++ .../server/dao/model/ModelConstants.java | 2 + .../dao/model/sql/AdminSettingsEntity.java | 9 ++ .../server/dao/settings/AdminSettingsDao.java | 4 +- .../settings/AdminSettingsServiceImpl.java | 22 ++- .../sql/settings/AdminSettingsRepository.java | 3 +- .../dao/sql/settings/JpaAdminSettingsDao.java | 4 +- .../main/resources/sql/schema-entities.sql | 1 + pom.xml | 5 + 25 files changed, 411 insertions(+), 154 deletions(-) rename {application/src/main/java/org/thingsboard/server/service/sync/vc/data => common/data/src/main/java/org/thingsboard/server/common/data/vc}/EntitiesVersionControlSettings.java (79%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/vc/VersionControlAuthMethod.java diff --git a/application/pom.xml b/application/pom.xml index 211d22fb04..83d36c2b42 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -345,6 +345,10 @@ org.eclipse.jgit org.eclipse.jgit + + org.eclipse.jgit + org.eclipse.jgit.ssh.apache + diff --git a/application/src/main/data/upgrade/3.3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql index ad307a6576..f537acfb36 100644 --- a/application/src/main/data/upgrade/3.3.4/schema_update.sql +++ b/application/src/main/data/upgrade/3.3.4/schema_update.sql @@ -26,3 +26,6 @@ ALTER TABLE dashboard ADD COLUMN IF NOT EXISTS external_id UUID; ALTER TABLE customer ADD COLUMN IF NOT EXISTS external_id UUID; + +ALTER TABLE admin_settings + ADD COLUMN IF NOT EXISTS tenant_id uuid NOT NULL DEFAULT '13814000-1dd2-11b2-8080-808080808080'; 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 e5934cbf26..3868476491 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -20,12 +20,7 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.AdminSettings; @@ -34,14 +29,17 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.sms.config.TestSmsRequest; +import org.thingsboard.server.common.data.vc.EntitiesVersionControlSettings; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.system.SystemSecurityService; +import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.update.UpdateService; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; @RestController @TbCoreComponent @@ -60,6 +58,9 @@ public class AdminController extends BaseController { @Autowired private SystemSecurityService systemSecurityService; + @Autowired + private EntitiesVersionControlService versionControlService; + @Autowired private UpdateService updateService; @@ -96,6 +97,7 @@ public class AdminController extends BaseController { @RequestBody AdminSettings adminSettings) throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); + adminSettings.setTenantId(getTenantId()); adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); if (adminSettings.getKey().equals("mail")) { mailService.updateMailConfiguration(); @@ -180,6 +182,53 @@ public class AdminController extends BaseController { } } + @ApiOperation(value = "Get version control settings (getVersionControlSettings)", + notes = "Get the version control settings object. " + TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping("/vcSettings") + @ResponseBody + public EntitiesVersionControlSettings getVersionControlSettings() throws ThingsboardException { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + EntitiesVersionControlSettings versionControlSettings = checkNotNull(versionControlService.getVersionControlSettings(getTenantId())); + versionControlSettings.setPassword(null); + versionControlSettings.setPrivateKey(null); + versionControlSettings.setPrivateKeyPassword(null); + return versionControlSettings; + } catch (Exception e) { + throw handleException(e); + } + } + + @ApiOperation(value = "Creates or Updates the version control settings (saveVersionControlSettings)", + notes = "Creates or Updates the version control settings object. " + TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @PostMapping("/vsSettings") + public void saveVersionControlSettings(@RequestBody EntitiesVersionControlSettings settings) throws ThingsboardException { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); + versionControlService.saveVersionControlSettings(getTenantId(), settings); + } catch (Exception e) { + throw handleException(e); + } + } + + @ApiOperation(value = "Check version control access (checkVersionControlAccess)", + notes = "Attempts to check version control access. " + TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/vcSettings/checkAccess", method = RequestMethod.POST) + public void checkVersionControlAccess( + @ApiParam(value = "A JSON value representing the Entities Version Control Settings.") + @RequestBody EntitiesVersionControlSettings settings) throws ThingsboardException { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); + settings = checkNotNull(settings); + versionControlService.checkVersionControlAccess(getTenantId(), settings); + } catch (Exception e) { + throw handleException(e); + } + } + @ApiOperation(value = "Check for new Platform Releases (checkUpdates)", notes = "Check notifications about new platform releases. " + SYSTEM_AUTHORITY_PARAGRAPH) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index e7df5acdc3..599149402f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -15,25 +15,18 @@ */ package org.thingsboard.server.controller; -import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiOperation; import lombok.Data; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; -import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; import org.thingsboard.server.service.sync.vc.data.EntityVersion; import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; @@ -280,7 +273,7 @@ public class EntitiesVersionControlController extends BaseController { List remoteBranches = versionControlService.listBranches(getTenantId()); List infos = new ArrayList<>(); - String defaultBranch = getSettings().getDefaultBranch(); + String defaultBranch = versionControlService.getVersionControlSettings(getTenantId()).getDefaultBranch(); if (StringUtils.isNotEmpty(defaultBranch)) { remoteBranches.remove(defaultBranch); infos.add(new BranchInfo(defaultBranch, true)); @@ -293,40 +286,6 @@ public class EntitiesVersionControlController extends BaseController { } } - - @ApiOperation(value = "", notes = "" + - "```\n{\n" + - " \"repositoryUri\": \"https://github.com/User/repo.git\",\n" + - " \"username\": \"User\",\n" + - " \"password\": \"api_key\",\n" + - " \"defaultBranch\": \"master\"\n" + - "}\n```") - @GetMapping("/settings") - public EntitiesVersionControlSettings getSettings() throws ThingsboardException { - try { - return versionControlService.getSettings(getTenantId()); - } catch (Exception e) { - throw handleException(e); - } - } - - @ApiOperation(value = "", notes = "" + - "```\n{\n" + - " \"repositoryUri\": \"https://github.com/User/repo.git\",\n" + - " \"username\": \"User\",\n" + - " \"password\": \"api_key\",\n" + - " \"defaultBranch\": \"master\"\n" + - "}\n```") - @PostMapping("/settings") - public void saveSettings(@RequestBody EntitiesVersionControlSettings settings) throws ThingsboardException { - try { - versionControlService.saveSettings(getTenantId(), settings); - } catch (Exception e) { - throw handleException(e); - } - } - - @Data public static class BranchInfo { private final String name; diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index ebab403149..e728c004c0 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -219,6 +219,7 @@ public class ThingsboardInstallService { databaseEntitiesUpgradeService.upgradeDatabase("3.3.3"); case "3.3.4": log.info("Upgrading ThingsBoard from version 3.3.4 to 3.4.0 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.3.4"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); break; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java index 0f4ecf7e30..ee10c7859b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java @@ -83,7 +83,7 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher { result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings))); - AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); + AdminSettings tenantMailSettings = convertToTenantAdminSettings(tenantId, systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings))); @@ -91,7 +91,7 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher { result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates))); - AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); + AdminSettings tenantMailTemplates = convertToTenantAdminSettings(tenantId, systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates))); @@ -151,8 +151,9 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher { } } - private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) { + private AdminSettings convertToTenantAdminSettings(TenantId tenantId, String key, ObjectNode jsonValue) { AdminSettings tenantMailSettings = new AdminSettings(); + tenantMailSettings.setTenantId(tenantId); jsonValue.put("useSystemMailSettings", true); tenantMailSettings.setJsonValue(jsonValue); tenantMailSettings.setKey(key); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 9e7636879f..696ac50c3d 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -231,6 +231,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Override public void createAdminSettings() throws Exception { AdminSettings generalSettings = new AdminSettings(); + generalSettings.setTenantId(TenantId.SYS_TENANT_ID); generalSettings.setKey("general"); ObjectNode node = objectMapper.createObjectNode(); node.put("baseUrl", "http://localhost:8080"); @@ -239,6 +240,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings); AdminSettings mailSettings = new AdminSettings(); + mailSettings.setTenantId(TenantId.SYS_TENANT_ID); mailSettings.setKey("mail"); node = objectMapper.createObjectNode(); node.put("mailFrom", "ThingsBoard "); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index f7b9e5cad1..1f9dcf15a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -534,6 +534,18 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.error("Failed updating schema!!!", e); } break; + case "3.3.4": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + log.info("Updating schema settings..."); + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); + log.info("Schema updated."); + } catch (Exception e) { + log.error("Failed updating schema!!!", e); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } 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 4ff884f0e1..8f821f5051 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 @@ -28,6 +28,7 @@ public class TenantAdminPermissions extends AbstractPermissions { public TenantAdminPermissions() { super(); + put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker); put(Resource.ALARM, tenantEntityPermissionChecker); put(Resource.ASSET, tenantEntityPermissionChecker); put(Resource.DEVICE, tenantEntityPermissionChecker); diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index 9a998544f3..2449d7fbc0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -107,6 +107,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); if (adminSettings == null) { adminSettings = new AdminSettings(); + adminSettings.setTenantId(tenantId); adminSettings.setKey("securitySettings"); } adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings)); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 8919f7d46e..77a5586a8c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -19,34 +19,29 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; +import org.eclipse.jgit.api.errors.GitAPIException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +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.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.query.EntityDataPageLink; -import org.thingsboard.server.common.data.query.EntityDataQuery; -import org.thingsboard.server.common.data.query.EntityDataSortOrder; -import org.thingsboard.server.common.data.query.EntityKey; -import org.thingsboard.server.common.data.query.EntityKeyType; -import org.thingsboard.server.common.data.query.EntityTypeFilter; +import org.thingsboard.server.common.data.query.*; +import org.thingsboard.server.common.data.vc.EntitiesVersionControlSettings; +import org.thingsboard.server.common.data.vc.VersionControlAuthMethod; import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -58,17 +53,11 @@ import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExp import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; -import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; import org.thingsboard.server.service.sync.vc.data.EntityVersion; import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; -import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateRequest; -import org.thingsboard.server.service.sync.vc.data.request.create.ComplexVersionCreateRequest; -import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateRequest; -import org.thingsboard.server.service.sync.vc.data.request.create.SyncStrategy; -import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.*; import org.thingsboard.server.service.sync.vc.data.request.load.EntityTypeVersionLoadRequest; import org.thingsboard.server.service.sync.vc.data.request.load.SingleEntityVersionLoadRequest; import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadConfig; @@ -81,13 +70,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -104,7 +87,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private final EntitiesExportImportService exportImportService; private final ExportableEntitiesService exportableEntitiesService; - private final AttributesService attributesService; + private final AdminSettingsService adminSettingsService; private final EntityService entityService; private final TenantDao tenantDao; private final TransactionTemplate transactionTemplate; @@ -114,14 +97,14 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Value("${java.io.tmpdir}/repositories") private String repositoriesFolder; - private static final String SETTINGS_KEY = "vc"; + private static final String SETTINGS_KEY = "entitiesVersionControl"; private final ObjectWriter jsonWriter = new ObjectMapper().writer(SerializationFeature.INDENT_OUTPUT); @AfterStartUp public void init() { DaoUtil.processInBatches(tenantDao::findTenantsIds, 100, tenantId -> { - EntitiesVersionControlSettings settings = getSettings(tenantId); + EntitiesVersionControlSettings settings = getVersionControlSettings(tenantId); if (settings != null) { try { initRepository(tenantId, settings); @@ -412,6 +395,73 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont return repository.listBranches(); } + @Override + public EntitiesVersionControlSettings getVersionControlSettings(TenantId tenantId) { + AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, SETTINGS_KEY); + if (adminSettings != null) { + try { + return JacksonUtil.convertValue(adminSettings.getJsonValue(), EntitiesVersionControlSettings.class); + } catch (Exception e) { + throw new RuntimeException("Failed to load version control settings!", e); + } + } + return null; + } + + @Override + public EntitiesVersionControlSettings saveVersionControlSettings(TenantId tenantId, EntitiesVersionControlSettings versionControlSettings) { + EntitiesVersionControlSettings storedSettings = getVersionControlSettings(tenantId); + versionControlSettings = this.restoreCredentials(versionControlSettings, storedSettings); + AdminSettings adminSettings = new AdminSettings(); + adminSettings.setTenantId(tenantId); + adminSettings.setKey(SETTINGS_KEY); + adminSettings.setJsonValue(JacksonUtil.valueToTree(versionControlSettings)); + AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings); + EntitiesVersionControlSettings savedVersionControlSettings; + try { + savedVersionControlSettings = JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), EntitiesVersionControlSettings.class); + } catch (Exception e) { + throw new RuntimeException("Failed to load version control settings!", e); + } + try { + clearRepository(tenantId); + initRepository(tenantId, savedVersionControlSettings); + } catch (Exception e) { + throw new RuntimeException("Failed to init repository!", e); + } + return savedVersionControlSettings; + } + + @Override + public void checkVersionControlAccess(TenantId tenantId, EntitiesVersionControlSettings settings) throws ThingsboardException { + EntitiesVersionControlSettings storedSettings = getVersionControlSettings(tenantId); + settings = this.restoreCredentials(settings, storedSettings); + Path repositoryDirectory = Path.of(repositoriesFolder, tenantId.getId().toString()); + try { + GitRepository.test(settings, repositoryDirectory.toFile()); + } catch (GitAPIException e) { + throw new ThingsboardException(String.format("Unable to access repository: %s", e.getMessage()), + ThingsboardErrorCode.GENERAL); + } + } + + private EntitiesVersionControlSettings restoreCredentials(EntitiesVersionControlSettings settings, EntitiesVersionControlSettings storedSettings) { + VersionControlAuthMethod authMethod = settings.getAuthMethod(); + if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(authMethod) && settings.getPassword() == null) { + if (storedSettings != null) { + settings.setPassword(storedSettings.getPassword()); + } + } else if (VersionControlAuthMethod.PRIVATE_KEY.equals(authMethod) && settings.getPrivateKey() == null) { + if (storedSettings != null) { + settings.setPrivateKey(storedSettings.getPrivateKey()); + if (StringUtils.isEmpty(settings.getPrivateKeyPassword()) && + StringUtils.isNotEmpty(storedSettings.getPrivateKeyPassword())) { + settings.setPrivateKeyPassword(storedSettings.getPrivateKeyPassword()); + } + } + } + return settings; + } private EntityVersion checkVersion(TenantId tenantId, String branch, String versionId) throws Exception { return listVersions(tenantId, branch, null).stream() @@ -426,11 +476,12 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private void initRepository(TenantId tenantId, EntitiesVersionControlSettings settings) throws Exception { Path repositoryDirectory = Path.of(repositoriesFolder, tenantId.getId().toString()); - GitRepository repository; - FileUtils.forceDelete(repositoryDirectory.toFile()); + FileUtils.forceDelete(repositoryDirectory.toFile()); Files.createDirectories(repositoryDirectory); - repository = GitRepository.clone(settings.getRepositoryUri(), settings.getUsername(), settings.getPassword(), repositoryDirectory.toFile()); + + GitRepository repository = GitRepository.clone(settings, repositoryDirectory.toFile()); + repositories.put(tenantId, repository); } @@ -442,34 +493,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } } - - @SneakyThrows - @Override - public void saveSettings(TenantId tenantId, EntitiesVersionControlSettings settings) { - attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, List.of( - new BaseAttributeKvEntry(System.currentTimeMillis(), new JsonDataEntry(SETTINGS_KEY, JacksonUtil.toString(settings))) - )).get(); - - clearRepository(tenantId); - initRepository(tenantId, settings); - } - - @SneakyThrows - @Override - public EntitiesVersionControlSettings getSettings(TenantId tenantId) { - return attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE, SETTINGS_KEY).get() - .flatMap(KvEntry::getJsonValue) - .map(json -> { - try { - return JacksonUtil.fromString(json, EntitiesVersionControlSettings.class); - } catch (IllegalArgumentException e) { - return null; - } - }) - .orElse(null); - } - - private EntityVersion toVersion(GitRepository.Commit commit) { return new EntityVersion(commit.getId(), commit.getMessage()); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java index 03343ec6f5..2854ec1d05 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -16,16 +16,17 @@ package org.thingsboard.server.service.sync.vc; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.vc.EntitiesVersionControlSettings; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; import org.thingsboard.server.service.sync.vc.data.EntityVersion; import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; -import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; import java.util.List; @@ -51,9 +52,12 @@ public interface EntitiesVersionControlService { List listBranches(TenantId tenantId) throws Exception; + EntitiesVersionControlSettings getVersionControlSettings(TenantId tenantId); + + EntitiesVersionControlSettings saveVersionControlSettings(TenantId tenantId, EntitiesVersionControlSettings versionControlSettings); + + void checkVersionControlAccess(TenantId tenantId, EntitiesVersionControlSettings settings) throws ThingsboardException; - void saveSettings(TenantId tenantId, EntitiesVersionControlSettings settings); - EntitiesVersionControlSettings getSettings(TenantId tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/utils/GitRepository.java b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java index 2cb01e6a8b..e207c30fa2 100644 --- a/application/src/main/java/org/thingsboard/server/utils/GitRepository.java +++ b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java @@ -18,14 +18,8 @@ package org.thingsboard.server.utils; import com.google.common.collect.Streams; import lombok.Data; import lombok.Getter; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.GitCommand; -import org.eclipse.jgit.api.ListBranchCommand; -import org.eclipse.jgit.api.LogCommand; -import org.eclipse.jgit.api.RmCommand; -import org.eclipse.jgit.api.Status; -import org.eclipse.jgit.api.TransportCommand; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.eclipse.jgit.api.*; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; @@ -34,50 +28,88 @@ import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshTransport; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.eclipse.jgit.transport.sshd.JGitKeyCache; +import org.eclipse.jgit.transport.sshd.ServerKeyDatabase; +import org.eclipse.jgit.transport.sshd.SshdSessionFactory; +import org.eclipse.jgit.transport.sshd.SshdSessionFactoryBuilder; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.vc.EntitiesVersionControlSettings; +import org.thingsboard.server.common.data.vc.VersionControlAuthMethod; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.PublicKey; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; public class GitRepository { private final Git git; private final CredentialsProvider credentialsProvider; + private final SshdSessionFactory sshSessionFactory; @Getter private final String directory; - private GitRepository(Git git, CredentialsProvider credentialsProvider, String directory) { + private GitRepository(Git git, CredentialsProvider credentialsProvider, SshdSessionFactory sshSessionFactory, String directory) { this.git = git; this.credentialsProvider = credentialsProvider; + this.sshSessionFactory = sshSessionFactory; this.directory = directory; } - public static GitRepository clone(String uri, String username, String password, File directory) throws GitAPIException { - CredentialsProvider credentialsProvider = newCredentialsProvider(username, password); - Git git = Git.cloneRepository() - .setURI(uri) + public static GitRepository clone(EntitiesVersionControlSettings settings, File directory) throws GitAPIException { + CredentialsProvider credentialsProvider = null; + SshdSessionFactory sshSessionFactory = null; + if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { + credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); + } else if (VersionControlAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { + sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); + } + CloneCommand cloneCommand = Git.cloneRepository() + .setURI(settings.getRepositoryUri()) .setDirectory(directory) - .setNoCheckout(true) - .setCredentialsProvider(credentialsProvider) - .call(); - return new GitRepository(git, credentialsProvider, directory.getAbsolutePath()); + .setNoCheckout(true); + configureTransportCommand(cloneCommand, credentialsProvider, sshSessionFactory); + Git git = cloneCommand.call(); + return new GitRepository(git, credentialsProvider, sshSessionFactory, directory.getAbsolutePath()); } - public static GitRepository open(File directory, String username, String password) throws IOException { + public static GitRepository open(File directory, EntitiesVersionControlSettings settings) throws IOException { Git git = Git.open(directory); - return new GitRepository(git, newCredentialsProvider(username, password), directory.getAbsolutePath()); + CredentialsProvider credentialsProvider = null; + SshdSessionFactory sshSessionFactory = null; + if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { + credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); + } else if (VersionControlAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { + sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); + } + return new GitRepository(git, credentialsProvider, sshSessionFactory, directory.getAbsolutePath()); } + public static void test(EntitiesVersionControlSettings settings, File directory) throws GitAPIException { + CredentialsProvider credentialsProvider = null; + SshdSessionFactory sshSessionFactory = null; + if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { + credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); + } else if (VersionControlAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { + sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); + } + LsRemoteCommand lsRemoteCommand = Git.lsRemoteRepository().setRemote(settings.getRepositoryUri()); + configureTransportCommand(lsRemoteCommand, credentialsProvider, sshSessionFactory); + lsRemoteCommand.call(); + } public void fetch() throws GitAPIException { execute(git.fetch() @@ -108,7 +140,6 @@ public class GitRepository { .distinct().collect(Collectors.toList()); } - public List listCommits(String branch, int limit) throws IOException, GitAPIException { return listCommits(branch, null, limit); } @@ -246,16 +277,66 @@ public class GitRepository { } private , T> T execute(C command) throws GitAPIException { - if (command instanceof TransportCommand && credentialsProvider != null) { - ((TransportCommand) command).setCredentialsProvider(credentialsProvider); + if (command instanceof TransportCommand) { + configureTransportCommand((TransportCommand) command, credentialsProvider, sshSessionFactory); } return command.call(); } + private static void configureTransportCommand(TransportCommand transportCommand, CredentialsProvider credentialsProvider, SshdSessionFactory sshSessionFactory) { + if (credentialsProvider != null) { + transportCommand.setCredentialsProvider(credentialsProvider); + } + if (sshSessionFactory != null) { + transportCommand.setTransportConfigCallback(transport -> { + if (transport instanceof SshTransport) { + SshTransport sshTransport = (SshTransport) transport; + sshTransport.setSshSessionFactory(sshSessionFactory); + } + }); + } + } + private static CredentialsProvider newCredentialsProvider(String username, String password) { - return new UsernamePasswordCredentialsProvider(username, password); + return new UsernamePasswordCredentialsProvider(username, password == null ? "" : password); } + private static SshdSessionFactory newSshdSessionFactory(String privateKey, String password, File directory) { + SshdSessionFactory sshSessionFactory = null; + if (StringUtils.isNotBlank(privateKey)) { + Iterable keyPairs = loadKeyPairs(privateKey, password); + sshSessionFactory = new SshdSessionFactoryBuilder() + .setPreferredAuthentications("publickey") + .setDefaultKeysProvider(file -> keyPairs) + .setHomeDirectory(directory) + .setSshDirectory(directory) + .setServerKeyDatabase((file, file2) -> new ServerKeyDatabase() { + @Override + public List lookup(String connectAddress, InetSocketAddress remoteAddress, Configuration config) { + return Collections.emptyList(); + } + + @Override + public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey, Configuration config, CredentialsProvider provider) { + return true; + } + }) + .build(new JGitKeyCache()); + } + return sshSessionFactory; + } + + private static Iterable loadKeyPairs(String privateKeyContent, String password) { + Iterable keyPairs = null; + try { + keyPairs = SecurityUtils.loadKeyPairIdentities(null, + null, new ByteArrayInputStream(privateKeyContent.getBytes()), (session, resourceKey, retryIndex) -> password); + } catch (Exception e) {} + if (keyPairs == null) { + throw new IllegalArgumentException("Failed to load ssh private key"); + } + return keyPairs; + } @Data public static class Commit { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java index 3b639659c8..d88fabc052 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java @@ -21,14 +21,17 @@ import org.thingsboard.server.common.data.id.AdminSettingsId; import com.fasterxml.jackson.databind.JsonNode; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @ApiModel -public class AdminSettings extends BaseData { +public class AdminSettings extends BaseData implements HasTenantId { private static final long serialVersionUID = -7670322981725511892L; + private TenantId tenantId; + @NoXss @Length(fieldName = "key") private String key; @@ -44,6 +47,7 @@ public class AdminSettings extends BaseData { public AdminSettings(AdminSettings adminSettings) { super(adminSettings); + this.tenantId = adminSettings.getTenantId(); this.key = adminSettings.getKey(); this.jsonValue = adminSettings.getJsonValue(); } @@ -60,7 +64,16 @@ public class AdminSettings extends BaseData { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "The Administration Settings key, (e.g. 'general' or 'mail')", example = "mail") + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + public TenantId getTenantId() { + return tenantId; + } + + public void setTenantId(TenantId tenantId) { + this.tenantId = tenantId; + } + + @ApiModelProperty(position = 4, value = "The Administration Settings key, (e.g. 'general' or 'mail')", example = "mail") public String getKey() { return key; } @@ -69,7 +82,7 @@ public class AdminSettings extends BaseData { this.key = key; } - @ApiModelProperty(position = 4, value = "JSON representation of the Administration Settings value") + @ApiModelProperty(position = 5, value = "JSON representation of the Administration Settings value") public JsonNode getJsonValue() { return jsonValue; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java index f1391555b0..52ad469ce1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java @@ -17,6 +17,10 @@ package org.thingsboard.server.common.data; public class StringUtils { + public static final String EMPTY = ""; + + public static final int INDEX_NOT_FOUND = -1; + public static boolean isEmpty(String source) { return source == null || source.isEmpty(); } @@ -32,4 +36,42 @@ public class StringUtils { public static boolean isNotBlank(String source) { return source != null && !source.isEmpty() && !source.trim().isEmpty(); } + + public static String removeStart(final String str, final String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.startsWith(remove)){ + return str.substring(remove.length()); + } + return str; + } + + public static String substringBefore(final String str, final String separator) { + if (isEmpty(str) || separator == null) { + return str; + } + if (separator.isEmpty()) { + return EMPTY; + } + final int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return str; + } + return str.substring(0, pos); + } + + public static String substringBetween(final String str, final String open, final String close) { + if (str == null || open == null || close == null) { + return null; + } + final int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) { + final int end = str.indexOf(close, start + open.length()); + if (end != INDEX_NOT_FOUND) { + return str.substring(start + open.length(), end); + } + } + return null; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/vc/EntitiesVersionControlSettings.java similarity index 79% rename from application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java rename to common/data/src/main/java/org/thingsboard/server/common/data/vc/EntitiesVersionControlSettings.java index 602f5dba4f..ce4f8e71d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/vc/EntitiesVersionControlSettings.java @@ -13,14 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.vc.data; +package org.thingsboard.server.common.data.vc; import lombok.Data; @Data public class EntitiesVersionControlSettings { private String repositoryUri; + private VersionControlAuthMethod authMethod; private String username; private String password; + private String privateKeyFileName; + private String privateKey; + private String privateKeyPassword; private String defaultBranch; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/vc/VersionControlAuthMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/vc/VersionControlAuthMethod.java new file mode 100644 index 0000000000..9d2dfa048e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/vc/VersionControlAuthMethod.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2022 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.vc; + +public enum VersionControlAuthMethod { + USERNAME_PASSWORD, + PRIVATE_KEY +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 0b74e79f2b..2bb9ef6bdb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -90,6 +90,8 @@ public class ModelConstants { * Cassandra admin_settings constants. */ public static final String ADMIN_SETTINGS_COLUMN_FAMILY_NAME = "admin_settings"; + + public static final String ADMIN_SETTINGS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String ADMIN_SETTINGS_KEY_PROPERTY = "key"; public static final String ADMIN_SETTINGS_JSON_VALUE_PROPERTY = "json_value"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java index 49d5002af2..4da17d2c34 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java @@ -22,14 +22,18 @@ import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.AdminSettingsId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import java.util.UUID; + import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_JSON_VALUE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_KEY_PROPERTY; @@ -41,6 +45,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_KEY @Table(name = ADMIN_SETTINGS_COLUMN_FAMILY_NAME) public final class AdminSettingsEntity extends BaseSqlEntity implements BaseEntity { + @Column(name = ModelConstants.ADMIN_SETTINGS_TENANT_ID_PROPERTY) + private UUID tenantId; + @Column(name = ADMIN_SETTINGS_KEY_PROPERTY) private String key; @@ -57,6 +64,7 @@ public final class AdminSettingsEntity extends BaseSqlEntity impl this.setUuid(adminSettings.getId().getId()); } this.setCreatedTime(adminSettings.getCreatedTime()); + this.tenantId = adminSettings.getTenantId().getId(); this.key = adminSettings.getKey(); this.jsonValue = adminSettings.getJsonValue(); } @@ -65,6 +73,7 @@ public final class AdminSettingsEntity extends BaseSqlEntity impl public AdminSettings toData() { AdminSettings adminSettings = new AdminSettings(new AdminSettingsId(id)); adminSettings.setCreatedTime(createdTime); + adminSettings.setTenantId(TenantId.fromUUID(tenantId)); adminSettings.setKey(key); adminSettings.setJsonValue(jsonValue); return adminSettings; diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java index faea2cc34e..14455cddd4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java @@ -19,6 +19,8 @@ import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; +import java.util.UUID; + public interface AdminSettingsDao extends Dao { /** @@ -35,6 +37,6 @@ public interface AdminSettingsDao extends Dao { * @param key the key * @return the admin settings object */ - AdminSettings findByKey(TenantId tenantId, String key); + AdminSettings findByTenantIdAndKey(UUID tenantId, String key); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index bf4c71d296..0462cc38de 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -22,6 +22,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.AdminSettingsId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.vc.VersionControlAuthMethod; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.Validator; @@ -46,20 +47,35 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { public AdminSettings findAdminSettingsByKey(TenantId tenantId, String key) { log.trace("Executing findAdminSettingsByKey [{}]", key); Validator.validateString(key, "Incorrect key " + key); - return adminSettingsDao.findByKey(tenantId, key); + return adminSettingsDao.findByTenantIdAndKey(tenantId.getId(), key); } @Override public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) { log.trace("Executing saveAdminSettings [{}]", adminSettings); adminSettingsValidator.validate(adminSettings, data -> tenantId); - if(adminSettings.getKey().equals("mail") && !adminSettings.getJsonValue().has("password")) { + if (adminSettings.getKey().equals("mail") && !adminSettings.getJsonValue().has("password")) { AdminSettings mailSettings = findAdminSettingsByKey(tenantId, "mail"); if (mailSettings != null) { ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText()); } + } else if (adminSettings.getKey().equals("entitiesVersionControl")) { + VersionControlAuthMethod authMethod = VersionControlAuthMethod.valueOf(adminSettings.getJsonValue().get("authMethod").asText()); + if (VersionControlAuthMethod.USERNAME_PASSWORD.equals(authMethod) && !adminSettings.getJsonValue().has("password")) { + AdminSettings vcSettings = findAdminSettingsByKey(tenantId, "entitiesVersionControl"); + if (vcSettings != null) { + ((ObjectNode) adminSettings.getJsonValue()).put("password", vcSettings.getJsonValue().get("password").asText()); + } + } else if (VersionControlAuthMethod.PRIVATE_KEY.equals(authMethod) && !adminSettings.getJsonValue().has("privateKey")) { + AdminSettings vcSettings = findAdminSettingsByKey(tenantId, "entitiesVersionControl"); + if (vcSettings != null) { + ((ObjectNode) adminSettings.getJsonValue()).put("privateKey", vcSettings.getJsonValue().get("privateKey").asText()); + if (!adminSettings.getJsonValue().has("privateKeyPassword") && vcSettings.getJsonValue().has("privateKeyPassword")) { + ((ObjectNode) adminSettings.getJsonValue()).put("privateKeyPassword", vcSettings.getJsonValue().get("privateKeyPassword").asText()); + } + } + } } - return adminSettingsDao.save(tenantId, adminSettings); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java index 58423645b1..10275a3c1a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java @@ -25,5 +25,6 @@ import java.util.UUID; */ public interface AdminSettingsRepository extends JpaRepository { - AdminSettingsEntity findByKey(String key); + AdminSettingsEntity findByTenantIdAndKey(UUID tenantId, String key); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/settings/JpaAdminSettingsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/settings/JpaAdminSettingsDao.java index 438274e3cc..4a4f7e5440 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/settings/JpaAdminSettingsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/settings/JpaAdminSettingsDao.java @@ -46,7 +46,7 @@ public class JpaAdminSettingsDao extends JpaAbstractDaoorg.eclipse.jgit ${jgit.version} + + org.eclipse.jgit + org.eclipse.jgit.ssh.apache + ${jgit.version} + From 2eb94fd95e5eca1973a689802af0279124f61a7d Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 19 May 2022 12:38:06 +0300 Subject: [PATCH 2/4] Fix beans cycle dependency --- .../DefaultEntitiesVersionControlService.java | 2 -- .../sync/vc/EntitiesVersionControlService.java | 2 ++ .../sync/vc/LocalGitVersionControlService.java | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 1496882031..70bf5b0d5b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -79,8 +79,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private final EntityService entityService; private final TransactionTemplate transactionTemplate; - public static final String SETTINGS_KEY = "entitiesVersionControl"; - @Override public VersionCreationResult saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception { var commit = gitService.prepareCommit(user.getTenantId(), request); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java index 8c18aeb32d..d24fa1411e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -32,6 +32,8 @@ import java.util.List; public interface EntitiesVersionControlService { + String SETTINGS_KEY = "entitiesVersionControl"; + VersionCreationResult saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java index aa51fc3ea1..94976438e9 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java @@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.sync.vc.VersionedEntityInfo; import org.thingsboard.server.common.data.sync.vc.request.create.VersionCreateRequest; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.queue.util.AfterStartUp; @@ -63,14 +65,14 @@ public class LocalGitVersionControlService implements GitVersionControlService { private final ObjectWriter jsonWriter = new ObjectMapper().writer(SerializationFeature.INDENT_OUTPUT); private final GitRepositoryService gitRepositoryService; private final TenantDao tenantDao; - private final EntitiesVersionControlService entitiesVersionControlService; + private final AdminSettingsService adminSettingsService; private final ConcurrentMap tenantRepoLocks = new ConcurrentHashMap<>(); private final Map pendingCommitMap = new HashMap<>(); @AfterStartUp public void init() { DaoUtil.processInBatches(tenantDao::findTenantsIds, 100, tenantId -> { - EntitiesVersionControlSettings settings = entitiesVersionControlService.getVersionControlSettings(tenantId); + EntitiesVersionControlSettings settings = getVersionControlSettings(tenantId); if (settings != null) { try { gitRepositoryService.initRepository(tenantId, settings); @@ -213,6 +215,18 @@ public class LocalGitVersionControlService implements GitVersionControlService { } } + private EntitiesVersionControlSettings getVersionControlSettings(TenantId tenantId) { + AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, EntitiesVersionControlService.SETTINGS_KEY); + if (adminSettings != null) { + try { + return JacksonUtil.convertValue(adminSettings.getJsonValue(), EntitiesVersionControlSettings.class); + } catch (Exception e) { + throw new RuntimeException("Failed to load version control settings!", e); + } + } + return null; + } + private List listVersions(TenantId tenantId, String branch, String path) { try { return gitRepositoryService.listVersions(tenantId, branch, path); From 587066cfd32b17e6b0259f147b32987a68c94a63 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 19 May 2022 13:19:43 +0300 Subject: [PATCH 3/4] Ability to remove version control settings --- .../server/controller/AdminController.java | 24 ++++++++++++++++--- .../DefaultEntitiesVersionControlService.java | 7 ++++++ .../vc/EntitiesVersionControlService.java | 2 ++ .../vc/LocalGitVersionControlService.java | 14 +++++++++++ .../dao/settings/AdminSettingsService.java | 2 ++ .../sync/vc/DefaultGitRepositoryService.java | 3 ++- .../service/sync/vc/GitRepositoryService.java | 2 ++ .../sync/vc/GitVersionControlService.java | 2 ++ .../server/dao/settings/AdminSettingsDao.java | 2 ++ .../settings/AdminSettingsServiceImpl.java | 6 +++++ .../sql/settings/AdminSettingsRepository.java | 4 ++++ .../dao/sql/settings/JpaAdminSettingsDao.java | 9 +++++++ 12 files changed, 73 insertions(+), 4 deletions(-) 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 237f681819..bf8c656257 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -19,13 +19,16 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; 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.Device; import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.sms.config.TestSmsRequest; @@ -38,8 +41,8 @@ import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.update.UpdateService; -import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; -import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.*; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID; @RestController @TbCoreComponent @@ -203,7 +206,7 @@ public class AdminController extends BaseController { @ApiOperation(value = "Creates or Updates the version control settings (saveVersionControlSettings)", notes = "Creates or Updates the version control settings object. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @PostMapping("/vsSettings") + @PostMapping("/vcSettings") public void saveVersionControlSettings(@RequestBody EntitiesVersionControlSettings settings) throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); @@ -213,6 +216,21 @@ public class AdminController extends BaseController { } } + @ApiOperation(value = "Delete version control settings (deleteVersionControlSettings)", + notes = "Deletes the version control settings." + + TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/vcSettings", method = RequestMethod.DELETE) + @ResponseStatus(value = HttpStatus.OK) + public void deleteVersionControlSettings() throws ThingsboardException { + try { + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.DELETE); + versionControlService.deleteVersionControlSettings(getTenantId()); + } catch (Exception e) { + throw handleException(e); + } + } + @ApiOperation(value = "Check version control access (checkVersionControlAccess)", notes = "Attempts to check version control access. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 70bf5b0d5b..c69ff94630 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -312,6 +312,13 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont return savedVersionControlSettings; } + @Override + public void deleteVersionControlSettings(TenantId tenantId) { + if (adminSettingsService.deleteAdminSettings(tenantId, SETTINGS_KEY)) { + gitService.clearRepository(tenantId); + } + } + @Override public void checkVersionControlAccess(TenantId tenantId, EntitiesVersionControlSettings settings) throws ThingsboardException { EntitiesVersionControlSettings storedSettings = getVersionControlSettings(tenantId); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java index d24fa1411e..28f095123c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -58,6 +58,8 @@ public interface EntitiesVersionControlService { EntitiesVersionControlSettings saveVersionControlSettings(TenantId tenantId, EntitiesVersionControlSettings versionControlSettings); + void deleteVersionControlSettings(TenantId tenantId); + void checkVersionControlAccess(TenantId tenantId, EntitiesVersionControlSettings settings) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java index 94976438e9..d73157d256 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/LocalGitVersionControlService.java @@ -111,6 +111,20 @@ public class LocalGitVersionControlService implements GitVersionControlService { } } + @Override + public void clearRepository(TenantId tenantId) { + var lock = getRepoLock(tenantId); + lock.lock(); + try { + gitRepositoryService.clearRepository(tenantId); + } catch (Exception e) { + //TODO: analyze and return meaningful exceptions that we can show to the client; + throw new RuntimeException(e); + } finally { + lock.unlock(); + } + } + @Override public PendingCommit prepareCommit(TenantId tenantId, VersionCreateRequest request) { var lock = getRepoLock(tenantId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java index d9238c9d7b..77f542ccc3 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java @@ -27,4 +27,6 @@ public interface AdminSettingsService { AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings); + boolean deleteAdminSettings(TenantId tenantId, String key); + } diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java index 56ca38ffe4..aac40e0dcb 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java @@ -224,7 +224,8 @@ public class DefaultGitRepositoryService implements GitRepositoryService { repositories.put(tenantId, repository); } - private void clearRepository(TenantId tenantId) throws IOException { + @Override + public void clearRepository(TenantId tenantId) throws IOException { GitRepository repository = repositories.get(tenantId); if (repository != null) { FileUtils.deleteDirectory(new File(repository.getDirectory())); diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepositoryService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepositoryService.java index 4b1843e3c7..19754750c2 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepositoryService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepositoryService.java @@ -36,6 +36,8 @@ public interface GitRepositoryService { void initRepository(TenantId tenantId, EntitiesVersionControlSettings settings) throws Exception; + void clearRepository(TenantId tenantId) throws IOException; + void add(PendingCommit commit, String relativePath, String entityDataJson) throws IOException; void deleteFolderContent(PendingCommit commit, String relativePath) throws IOException; diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlService.java index 8345375e4f..f28584c418 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlService.java @@ -34,6 +34,8 @@ public interface GitVersionControlService { void initRepository(TenantId tenantId, EntitiesVersionControlSettings settings); + void clearRepository(TenantId tenantId); + PendingCommit prepareCommit(TenantId tenantId, VersionCreateRequest request); void addToCommit(PendingCommit commit, EntityExportData> entityData); diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java index 14455cddd4..d211a8340f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsDao.java @@ -39,4 +39,6 @@ public interface AdminSettingsDao extends Dao { */ AdminSettings findByTenantIdAndKey(UUID tenantId, String key); + boolean removeByTenantIdAndKey(UUID tenantId, String key); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index 0bb5564fa6..433d056186 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -62,4 +62,10 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { return adminSettingsDao.save(tenantId, adminSettings); } + @Override + public boolean deleteAdminSettings(TenantId tenantId, String key) { + log.trace("Executing deleteAdminSettings, tenantId [{}], key [{}]", tenantId, key); + Validator.validateString(key, "Incorrect key " + key); + return adminSettingsDao.removeByTenantIdAndKey(tenantId.getId(), key); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java index 10275a3c1a..0e68327fdf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/settings/AdminSettingsRepository.java @@ -27,4 +27,8 @@ public interface AdminSettingsRepository extends JpaRepository Date: Thu, 19 May 2022 13:21:19 +0300 Subject: [PATCH 4/4] Clear repository on settings update --- .../service/sync/vc/DefaultEntitiesVersionControlService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index c69ff94630..dcc96b7d17 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -305,6 +305,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont throw new RuntimeException("Failed to load version control settings!", e); } try { + gitService.clearRepository(tenantId); gitService.initRepository(tenantId, savedVersionControlSettings); } catch (Exception e) { throw new RuntimeException("Failed to init repository!", e);