diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/RepositoryHelperMethods.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/RepositoryHelperMethods.java index 90cd888ad45..cd5b121dbd3 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/RepositoryHelperMethods.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/RepositoryHelperMethods.java @@ -4,6 +4,7 @@ import com.appsmith.server.domains.Config; import com.appsmith.server.domains.PermissionGroup; import com.appsmith.server.domains.Tenant; +import com.appsmith.server.domains.Theme; import com.appsmith.server.domains.User; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -19,6 +20,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -214,4 +216,58 @@ public Tenant saveTenant(Tenant tenant) throws JsonProcessingException { id); return tenant; } + + public Theme getTheme(String name, boolean isSystemTheme) { + String sqlQuery = "SELECT * FROM theme WHERE name = ? AND is_system_theme = ?"; + RowMapper rowMapper = (rs, rowNum) -> { + Theme theme = new Theme(); + theme.setId(rs.getString("id")); + theme.setName(rs.getString("name")); + theme.setDisplayName(rs.getString("display_name")); + theme.setStylesheet(mapObject(rs.getString("stylesheet"), new TypeReference>() {})); + theme.setProperties(mapObject(rs.getString("properties"), new TypeReference>() {})); + theme.setConfig(mapObject(rs.getString("config"), new TypeReference>() {})); + theme.setSystemTheme(rs.getBoolean("is_system_theme")); + return theme; + }; + try { + return jdbcTemplate.queryForObject(sqlQuery, rowMapper, name, isSystemTheme); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public Theme saveTheme(Theme theme) throws JsonProcessingException { + String id = theme.getId(); + String updateThemeQuery = + "UPDATE theme SET name = ?, display_name = ?, config = cast(? as jsonb), properties = cast(? as jsonb), stylesheet = cast(? as jsonb), policies = cast(? as jsonb), updated_at = now() WHERE id = ?"; + jdbcTemplate.update( + updateThemeQuery, + theme.getName(), + theme.getDisplayName(), + JsonHelper.convertToString(theme.getConfig()), + JsonHelper.convertToString(theme.getProperties()), + JsonHelper.convertToString(theme.getStylesheet()), + JsonHelper.convertToString(theme.getPolicies()), + id); + return theme; + } + + public Theme createTheme(Theme theme) throws JsonProcessingException { + String id = UUID.randomUUID().toString(); + String insertInstanceConfigurationQuery = + "INSERT INTO theme (id, name, display_name, config, properties, stylesheet, is_system_theme, policies, created_at, updated_at) VALUES (?, ?, ?, cast(? as jsonb), cast(? as jsonb), cast(? as jsonb), ?, cast(? as jsonb), now(), now())"; + jdbcTemplate.update( + insertInstanceConfigurationQuery, + id, + theme.getName(), + theme.getDisplayName(), + JsonHelper.convertToString(theme.getConfig()), + JsonHelper.convertToString(theme.getProperties()), + JsonHelper.convertToString(theme.getStylesheet()), + theme.isSystemTheme(), + JsonHelper.convertToString(theme.getPolicies())); + theme.setId(id); + return theme; + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V10__createSystemThemes.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V10__createSystemThemes.java new file mode 100644 index 00000000000..4455d3a7cf7 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V10__createSystemThemes.java @@ -0,0 +1,101 @@ +package com.appsmith.server.migrations.ce; + +import com.appsmith.external.models.Policy; +import com.appsmith.server.domains.Config; +import com.appsmith.server.domains.PermissionGroup; +import com.appsmith.server.domains.Theme; +import com.appsmith.server.dtos.Permission; +import com.appsmith.server.migrations.AppsmithJavaMigration; +import com.appsmith.server.migrations.RepositoryHelperMethods; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.StreamUtils; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.time.Instant; +import java.util.HashSet; +import java.util.Set; + +import static com.appsmith.server.acl.AclPermission.READ_THEMES; +import static com.appsmith.server.constants.ce.FieldNameCE.PERMISSION_GROUP_ID; +import static com.appsmith.server.constants.ce.FieldNameCE.PUBLIC_PERMISSION_GROUP; + +public class V10__createSystemThemes extends AppsmithJavaMigration { + private RepositoryHelperMethods repositoryHelperMethods; + + @Override + public void migrate(JdbcTemplate jdbcTemplate) throws Exception { + repositoryHelperMethods = new RepositoryHelperMethods(jdbcTemplate); + createSystemTheme(); + } + + private void createSystemTheme() throws IOException { + final String themesJson = StreamUtils.copyToString( + new DefaultResourceLoader().getResource("system-themes.json").getInputStream(), + Charset.defaultCharset()); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + Theme[] themes = mapper.readValue(themesJson, Theme[].class); + + Config publicPermissionGroupConfig = repositoryHelperMethods.getConfig(PUBLIC_PERMISSION_GROUP); + if (publicPermissionGroupConfig == null) { + throw new IllegalStateException("Public permission group not found in the database."); + } + + String permissionGroupId = publicPermissionGroupConfig.getConfig().getAsString(PERMISSION_GROUP_ID); + + PermissionGroup publicPermissionGroup = repositoryHelperMethods.getPermissionGroup(permissionGroupId); + + // Initialize the permissions for the role + HashSet permissions = new HashSet<>(); + if (publicPermissionGroup.getPermissions() != null) { + permissions.addAll(publicPermissionGroup.getPermissions()); + } + + Policy policyWithCurrentPermission = Policy.builder() + .permission(READ_THEMES.getValue()) + .permissionGroups(Set.of(publicPermissionGroup.getId())) + .build(); + + for (Theme theme : themes) { + theme.setSystemTheme(true); + theme.setCreatedAt(Instant.now()); + theme.setPolicies(new HashSet<>(Set.of(policyWithCurrentPermission))); + + Theme savedTheme = repositoryHelperMethods.getTheme(theme.getName(), true); + if (savedTheme == null) { // this theme does not exist, create it + savedTheme = repositoryHelperMethods.createTheme(theme); + } else { // theme already found, update + savedTheme.setDisplayName(theme.getDisplayName()); + savedTheme.setPolicies(theme.getPolicies()); + savedTheme.setConfig(theme.getConfig()); + savedTheme.setProperties(theme.getProperties()); + savedTheme.setStylesheet(theme.getStylesheet()); + if (savedTheme.getCreatedAt() == null) { + savedTheme.setCreatedAt(Instant.now()); + } + repositoryHelperMethods.saveTheme(savedTheme); + } + + // Add the access to this theme to the public permission group + Theme finalSavedTheme = savedTheme; + boolean isThemePermissionPresent = permissions.stream() + .filter(p -> p.getAclPermission().equals(READ_THEMES) + && p.getDocumentId().equals(finalSavedTheme.getId())) + .findFirst() + .isPresent(); + if (!isThemePermissionPresent) { + permissions.add(new Permission(finalSavedTheme.getId(), READ_THEMES)); + } + } + + // Finally save the role which gives access to all the system themes to the anonymous user. + publicPermissionGroup.setPermissions(permissions); + repositoryHelperMethods.savePermissionGroup(publicPermissionGroup); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V10__cleanPermissionsInPermissionGroups.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V11__cleanPermissionsInPermissionGroups.java similarity index 85% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V10__cleanPermissionsInPermissionGroups.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V11__cleanPermissionsInPermissionGroups.java index 2f1b5aad9f4..a032e9bf907 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V10__cleanPermissionsInPermissionGroups.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/ce/V11__cleanPermissionsInPermissionGroups.java @@ -3,7 +3,7 @@ import com.appsmith.server.migrations.AppsmithJavaMigration; import org.springframework.jdbc.core.JdbcTemplate; -public class V10__cleanPermissionsInPermissionGroups extends AppsmithJavaMigration { +public class V11__cleanPermissionsInPermissionGroups extends AppsmithJavaMigration { @Override public void migrate(JdbcTemplate jdbcTemplate) throws Exception { String sql = "UPDATE permission_group SET permissions = NULL";