diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/GitServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/GitServiceImpl.java index 7289424b7c..370b02e314 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/GitServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/GitServiceImpl.java @@ -18,6 +18,7 @@ import com.appsmith.server.repositories.GitDeployKeysRepository; import com.appsmith.server.services.ce_compatible.GitServiceCECompatibleImpl; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; +import com.appsmith.server.solutions.WorkspacePermission; import io.micrometer.observation.ObservationRegistry; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Import; @@ -48,6 +49,7 @@ public class GitServiceImpl extends GitServiceCECompatibleImpl implements GitSer PluginService pluginService, DatasourcePermission datasourcePermission, ApplicationPermission applicationPermission, + WorkspacePermission workspacePermission, WorkspaceService workspaceService, RedisUtils redisUtils, ObservationRegistry observationRegistry, @@ -73,6 +75,7 @@ public class GitServiceImpl extends GitServiceCECompatibleImpl implements GitSer pluginService, datasourcePermission, applicationPermission, + workspacePermission, workspaceService, redisUtils, observationRegistry, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java index 38afba0432..f284a97c98 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java @@ -61,6 +61,7 @@ import com.appsmith.server.services.UserService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; +import com.appsmith.server.solutions.WorkspacePermission; import io.micrometer.observation.ObservationRegistry; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -144,6 +145,7 @@ public class GitServiceCEImpl implements GitServiceCE { private final PluginService pluginService; private final DatasourcePermission datasourcePermission; private final ApplicationPermission applicationPermission; + private final WorkspacePermission workspacePermission; private final WorkspaceService workspaceService; private final RedisUtils redisUtils; private final ObservationRegistry observationRegistry; @@ -744,7 +746,15 @@ public class GitServiceCEImpl implements GitServiceCE { GitUtils.isRepoPrivate(browserSupportedUrl).cache(); Mono connectApplicationMono = profileMono - .then(getApplicationById(defaultApplicationId).zipWith(isPrivateRepoMono)) + .then(getApplicationById(defaultApplicationId)) + .flatMap(application -> + // Check if the user has permission to create app on the workspace, if yes then proceed + checkPermissionOnWorkspace( + application.getWorkspaceId(), + workspacePermission.getApplicationCreatePermission(), + "Connect to Git") + .thenReturn(application)) + .zipWith(isPrivateRepoMono) .flatMap(tuple -> { Application application = tuple.getT1(); boolean isRepoPrivate = tuple.getT2(); @@ -1158,6 +1168,11 @@ public class GitServiceCEImpl implements GitServiceCE { public Mono detachRemote(String defaultApplicationId) { Mono disconnectMono = getApplicationById(defaultApplicationId) + .flatMap(application -> checkPermissionOnWorkspace( + application.getWorkspaceId(), + workspacePermission.getApplicationCreatePermission(), + "Disconnect from Git") + .thenReturn(application)) .flatMap(defaultApplication -> { if (Optional.ofNullable(defaultApplication.getGitApplicationMetadata()) .isEmpty() @@ -1578,6 +1593,14 @@ public class GitServiceCEImpl implements GitServiceCE { AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION_ID, applicationId))); } + protected Mono checkPermissionOnWorkspace( + String workspaceId, AclPermission aclPermission, String operationName) { + return workspaceService + .findById(workspaceId, aclPermission) + .switchIfEmpty( + Mono.error(new AppsmithException(AppsmithError.ACTION_IS_NOT_AUTHORIZED, operationName))); + } + /** * Method to pull application json files from remote repo, make a commit with the changes present in local DB and * make a system commit to remote repo diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java index 3f051f5fc9..91b04333f6 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java @@ -24,6 +24,7 @@ import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.services.ce.GitServiceCEImpl; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.DatasourcePermission; +import com.appsmith.server.solutions.WorkspacePermission; import io.micrometer.observation.ObservationRegistry; import org.springframework.stereotype.Service; @@ -51,6 +52,7 @@ public class GitServiceCECompatibleImpl extends GitServiceCEImpl implements GitS PluginService pluginService, DatasourcePermission datasourcePermission, ApplicationPermission applicationPermission, + WorkspacePermission workspacePermission, WorkspaceService workspaceService, RedisUtils redisUtils, ObservationRegistry observationRegistry, @@ -76,6 +78,7 @@ public class GitServiceCECompatibleImpl extends GitServiceCEImpl implements GitS pluginService, datasourcePermission, applicationPermission, + workspacePermission, workspaceService, redisUtils, observationRegistry, diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java index 78573e9ad2..4cf20bb940 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCETest.java @@ -12,6 +12,7 @@ import com.appsmith.external.models.DatasourceStorageDTO; import com.appsmith.external.models.DefaultResources; import com.appsmith.external.models.JSValue; import com.appsmith.external.models.PluginType; +import com.appsmith.external.models.Policy; import com.appsmith.server.actioncollections.base.ActionCollectionService; import com.appsmith.server.constants.FieldName; import com.appsmith.server.datasources.base.DatasourceService; @@ -59,6 +60,7 @@ import com.appsmith.server.services.UserService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.EnvironmentPermission; +import com.appsmith.server.solutions.WorkspacePermission; import com.appsmith.server.themes.base.ThemeService; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -109,6 +111,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties; import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS; @@ -206,6 +209,9 @@ public class GitServiceCETest { @Autowired ApplicationPermission applicationPermission; + @Autowired + WorkspacePermission workspacePermission; + @BeforeEach public void setup() throws IOException, GitAPIException { @@ -4099,4 +4105,61 @@ public class GitServiceCETest { }) .verifyComplete(); } + + /** + * This method creates an workspace, creates an application in the workspace and removes the + * create application permission from the workspace for the api_user. + * @return Created Application + */ + private Application createApplicationAndRemoveCreateAppPermissionFromWorkspace() { + User apiUser = userService.findByEmail("api_user").block(); + + Workspace toCreate = new Workspace(); + toCreate.setName("Workspace_" + UUID.randomUUID()); + Workspace workspace = + workspaceService.create(toCreate, apiUser, Boolean.FALSE).block(); + + Application testApplication = new Application(); + testApplication.setWorkspaceId(workspace.getId()); + testApplication.setName("Test App"); + Application application1 = + applicationPageService.createApplication(testApplication).block(); + + // remove create application permission from the workspace for the api user + Set existingPolicies = workspace.getPolicies(); + Set newPoliciesWithoutExport = existingPolicies.stream() + .filter(policy -> !policy.getPermission() + .equals(workspacePermission + .getApplicationCreatePermission() + .getValue())) + .collect(Collectors.toSet()); + workspace.setPolicies(newPoliciesWithoutExport); + workspaceRepository.save(workspace).block(); + return application1; + } + + @WithUserDetails("api_user") + @Test + public void ConnectApplicationToGit_WhenUserDoesNotHaveRequiredPermission_OperationFails() { + Application application = createApplicationAndRemoveCreateAppPermissionFromWorkspace(); + + GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); + Mono applicationMono = + gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "baseUrl"); + + StepVerifier.create(applicationMono) + .expectErrorMessage(AppsmithError.ACTION_IS_NOT_AUTHORIZED.getMessage("Connect to Git")) + .verify(); + } + + @WithUserDetails("api_user") + @Test + public void detachRemote_WhenUserDoesNotHaveRequiredPermission_OperationFails() { + Application application = createApplicationAndRemoveCreateAppPermissionFromWorkspace(); + Mono applicationMono = gitService.detachRemote(application.getId()); + + StepVerifier.create(applicationMono) + .expectErrorMessage(AppsmithError.ACTION_IS_NOT_AUTHORIZED.getMessage("Disconnect from Git")) + .verify(); + } }