chore: Impose permission on Git operations (#27954)

## Description
This PR checks whether user has permission to create and application in
a workspace before doing git operations - Git connect and disconnect
from Git.

#### PR fixes following issue(s)
Fixes #26878
This commit is contained in:
Nayan 2023-10-20 10:59:22 +06:00 committed by GitHub
parent 41ee6473a8
commit 17eae14dfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 1 deletions

View File

@ -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,

View File

@ -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<Application> 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<Application> detachRemote(String defaultApplicationId) {
Mono<Application> 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<Workspace> 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

View File

@ -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,

View File

@ -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<Policy> existingPolicies = workspace.getPolicies();
Set<Policy> 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<Application> 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<Application> applicationMono = gitService.detachRemote(application.getId());
StepVerifier.create(applicationMono)
.expectErrorMessage(AppsmithError.ACTION_IS_NOT_AUTHORIZED.getMessage("Disconnect from Git"))
.verify();
}
}