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:
parent
41ee6473a8
commit
17eae14dfc
|
|
@ -18,6 +18,7 @@ import com.appsmith.server.repositories.GitDeployKeysRepository;
|
||||||
import com.appsmith.server.services.ce_compatible.GitServiceCECompatibleImpl;
|
import com.appsmith.server.services.ce_compatible.GitServiceCECompatibleImpl;
|
||||||
import com.appsmith.server.solutions.ApplicationPermission;
|
import com.appsmith.server.solutions.ApplicationPermission;
|
||||||
import com.appsmith.server.solutions.DatasourcePermission;
|
import com.appsmith.server.solutions.DatasourcePermission;
|
||||||
|
import com.appsmith.server.solutions.WorkspacePermission;
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
@ -48,6 +49,7 @@ public class GitServiceImpl extends GitServiceCECompatibleImpl implements GitSer
|
||||||
PluginService pluginService,
|
PluginService pluginService,
|
||||||
DatasourcePermission datasourcePermission,
|
DatasourcePermission datasourcePermission,
|
||||||
ApplicationPermission applicationPermission,
|
ApplicationPermission applicationPermission,
|
||||||
|
WorkspacePermission workspacePermission,
|
||||||
WorkspaceService workspaceService,
|
WorkspaceService workspaceService,
|
||||||
RedisUtils redisUtils,
|
RedisUtils redisUtils,
|
||||||
ObservationRegistry observationRegistry,
|
ObservationRegistry observationRegistry,
|
||||||
|
|
@ -73,6 +75,7 @@ public class GitServiceImpl extends GitServiceCECompatibleImpl implements GitSer
|
||||||
pluginService,
|
pluginService,
|
||||||
datasourcePermission,
|
datasourcePermission,
|
||||||
applicationPermission,
|
applicationPermission,
|
||||||
|
workspacePermission,
|
||||||
workspaceService,
|
workspaceService,
|
||||||
redisUtils,
|
redisUtils,
|
||||||
observationRegistry,
|
observationRegistry,
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ import com.appsmith.server.services.UserService;
|
||||||
import com.appsmith.server.services.WorkspaceService;
|
import com.appsmith.server.services.WorkspaceService;
|
||||||
import com.appsmith.server.solutions.ApplicationPermission;
|
import com.appsmith.server.solutions.ApplicationPermission;
|
||||||
import com.appsmith.server.solutions.DatasourcePermission;
|
import com.appsmith.server.solutions.DatasourcePermission;
|
||||||
|
import com.appsmith.server.solutions.WorkspacePermission;
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -144,6 +145,7 @@ public class GitServiceCEImpl implements GitServiceCE {
|
||||||
private final PluginService pluginService;
|
private final PluginService pluginService;
|
||||||
private final DatasourcePermission datasourcePermission;
|
private final DatasourcePermission datasourcePermission;
|
||||||
private final ApplicationPermission applicationPermission;
|
private final ApplicationPermission applicationPermission;
|
||||||
|
private final WorkspacePermission workspacePermission;
|
||||||
private final WorkspaceService workspaceService;
|
private final WorkspaceService workspaceService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private final ObservationRegistry observationRegistry;
|
private final ObservationRegistry observationRegistry;
|
||||||
|
|
@ -744,7 +746,15 @@ public class GitServiceCEImpl implements GitServiceCE {
|
||||||
GitUtils.isRepoPrivate(browserSupportedUrl).cache();
|
GitUtils.isRepoPrivate(browserSupportedUrl).cache();
|
||||||
|
|
||||||
Mono<Application> connectApplicationMono = profileMono
|
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 -> {
|
.flatMap(tuple -> {
|
||||||
Application application = tuple.getT1();
|
Application application = tuple.getT1();
|
||||||
boolean isRepoPrivate = tuple.getT2();
|
boolean isRepoPrivate = tuple.getT2();
|
||||||
|
|
@ -1158,6 +1168,11 @@ public class GitServiceCEImpl implements GitServiceCE {
|
||||||
public Mono<Application> detachRemote(String defaultApplicationId) {
|
public Mono<Application> detachRemote(String defaultApplicationId) {
|
||||||
|
|
||||||
Mono<Application> disconnectMono = getApplicationById(defaultApplicationId)
|
Mono<Application> disconnectMono = getApplicationById(defaultApplicationId)
|
||||||
|
.flatMap(application -> checkPermissionOnWorkspace(
|
||||||
|
application.getWorkspaceId(),
|
||||||
|
workspacePermission.getApplicationCreatePermission(),
|
||||||
|
"Disconnect from Git")
|
||||||
|
.thenReturn(application))
|
||||||
.flatMap(defaultApplication -> {
|
.flatMap(defaultApplication -> {
|
||||||
if (Optional.ofNullable(defaultApplication.getGitApplicationMetadata())
|
if (Optional.ofNullable(defaultApplication.getGitApplicationMetadata())
|
||||||
.isEmpty()
|
.isEmpty()
|
||||||
|
|
@ -1578,6 +1593,14 @@ public class GitServiceCEImpl implements GitServiceCE {
|
||||||
AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION_ID, applicationId)));
|
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
|
* 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
|
* make a system commit to remote repo
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import com.appsmith.server.services.WorkspaceService;
|
||||||
import com.appsmith.server.services.ce.GitServiceCEImpl;
|
import com.appsmith.server.services.ce.GitServiceCEImpl;
|
||||||
import com.appsmith.server.solutions.ApplicationPermission;
|
import com.appsmith.server.solutions.ApplicationPermission;
|
||||||
import com.appsmith.server.solutions.DatasourcePermission;
|
import com.appsmith.server.solutions.DatasourcePermission;
|
||||||
|
import com.appsmith.server.solutions.WorkspacePermission;
|
||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
@ -51,6 +52,7 @@ public class GitServiceCECompatibleImpl extends GitServiceCEImpl implements GitS
|
||||||
PluginService pluginService,
|
PluginService pluginService,
|
||||||
DatasourcePermission datasourcePermission,
|
DatasourcePermission datasourcePermission,
|
||||||
ApplicationPermission applicationPermission,
|
ApplicationPermission applicationPermission,
|
||||||
|
WorkspacePermission workspacePermission,
|
||||||
WorkspaceService workspaceService,
|
WorkspaceService workspaceService,
|
||||||
RedisUtils redisUtils,
|
RedisUtils redisUtils,
|
||||||
ObservationRegistry observationRegistry,
|
ObservationRegistry observationRegistry,
|
||||||
|
|
@ -76,6 +78,7 @@ public class GitServiceCECompatibleImpl extends GitServiceCEImpl implements GitS
|
||||||
pluginService,
|
pluginService,
|
||||||
datasourcePermission,
|
datasourcePermission,
|
||||||
applicationPermission,
|
applicationPermission,
|
||||||
|
workspacePermission,
|
||||||
workspaceService,
|
workspaceService,
|
||||||
redisUtils,
|
redisUtils,
|
||||||
observationRegistry,
|
observationRegistry,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.appsmith.external.models.DatasourceStorageDTO;
|
||||||
import com.appsmith.external.models.DefaultResources;
|
import com.appsmith.external.models.DefaultResources;
|
||||||
import com.appsmith.external.models.JSValue;
|
import com.appsmith.external.models.JSValue;
|
||||||
import com.appsmith.external.models.PluginType;
|
import com.appsmith.external.models.PluginType;
|
||||||
|
import com.appsmith.external.models.Policy;
|
||||||
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.datasources.base.DatasourceService;
|
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.services.WorkspaceService;
|
||||||
import com.appsmith.server.solutions.ApplicationPermission;
|
import com.appsmith.server.solutions.ApplicationPermission;
|
||||||
import com.appsmith.server.solutions.EnvironmentPermission;
|
import com.appsmith.server.solutions.EnvironmentPermission;
|
||||||
|
import com.appsmith.server.solutions.WorkspacePermission;
|
||||||
import com.appsmith.server.themes.base.ThemeService;
|
import com.appsmith.server.themes.base.ThemeService;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
@ -109,6 +111,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties;
|
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
|
|
@ -206,6 +209,9 @@ public class GitServiceCETest {
|
||||||
@Autowired
|
@Autowired
|
||||||
ApplicationPermission applicationPermission;
|
ApplicationPermission applicationPermission;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
WorkspacePermission workspacePermission;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() throws IOException, GitAPIException {
|
public void setup() throws IOException, GitAPIException {
|
||||||
|
|
||||||
|
|
@ -4099,4 +4105,61 @@ public class GitServiceCETest {
|
||||||
})
|
})
|
||||||
.verifyComplete();
|
.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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user