Adding test case for testDatasource

Refactoring the testDatasource code by moving the business logic to the service layer.
This commit is contained in:
Trisha Anand 2020-06-05 10:05:23 +00:00
parent c7cd46bed4
commit 87aafd6cf4
27 changed files with 381 additions and 152 deletions

View File

@ -2,6 +2,7 @@ package com.appsmith.server.acl;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.User;
@ -49,7 +50,11 @@ public enum AclPermission {
MANAGE_ACTIONS("manage:actions", Action.class),
READ_ACTIONS("read:actions", Action.class),
EXECUTE_ACTIONS("execute:actions", Action.class);
EXECUTE_ACTIONS("execute:actions", Action.class),
MANAGE_DATASOURCES("manage:datasources", Datasource.class),
READ_DATASOURCES("read:datasources", Datasource.class),
EXECUTE_DATASOURCES("execute:datasources", Datasource.class);
private String value;
private Class entity;

View File

@ -17,8 +17,10 @@ import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_ORGANIZATIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
import static com.appsmith.server.acl.AclPermission.MANAGE_USERS;
@ -28,6 +30,7 @@ import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIO
import static com.appsmith.server.acl.AclPermission.PUBLISH_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.READ_ORGANIZATIONS;
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
import static com.appsmith.server.acl.AclPermission.READ_USERS;
@ -63,11 +66,10 @@ public class PolicyGenerator {
createUserPolicyGraph();
createOrganizationPolicyGraph();
createDatasourcePolicyGraph();
createApplicationPolicyGraph();
createPagePolicyGraph();
createActionPolicyGraph();
log.debug("Successfully created the createGraph & lateralGraph");
}
/**
@ -89,6 +91,15 @@ public class PolicyGenerator {
lateralGraph.addEdge(MANAGE_ORGANIZATIONS, ORGANIZATION_PUBLISH_APPLICATIONS);
}
private void createDatasourcePolicyGraph() {
hierarchyGraph.addEdge(ORGANIZATION_MANAGE_APPLICATIONS, MANAGE_DATASOURCES);
hierarchyGraph.addEdge(ORGANIZATION_READ_APPLICATIONS, READ_DATASOURCES);
lateralGraph.addEdge(MANAGE_DATASOURCES, READ_DATASOURCES);
lateralGraph.addEdge(MANAGE_DATASOURCES, EXECUTE_DATASOURCES);
lateralGraph.addEdge(READ_DATASOURCES, EXECUTE_DATASOURCES);
}
private void createApplicationPolicyGraph() {
hierarchyGraph.addEdge(ORGANIZATION_MANAGE_APPLICATIONS, MANAGE_APPLICATIONS);
hierarchyGraph.addEdge(ORGANIZATION_READ_APPLICATIONS, READ_APPLICATIONS);
@ -131,17 +142,21 @@ public class PolicyGenerator {
* @param policy
* @param aclPermission
* @param user
* @param destinationEntity
* @return
*/
public Set<Policy> getChildPolicies(Policy policy, AclPermission aclPermission, User user) {
public Set<Policy> getChildPolicies(Policy policy, AclPermission aclPermission, User user, Class destinationEntity) {
// Check the hierarchy graph to derive child permissions that must be given to this
// document
Set<Policy> childPolicySet = new HashSet<>();
Set<DefaultEdge> edges = hierarchyGraph.outgoingEdgesOf(aclPermission);
for (DefaultEdge edge : edges) {
AclPermission childPermission = hierarchyGraph.getEdgeTarget(edge);
childPolicySet.add(Policy.builder().permission(childPermission.getValue())
.users(policy.getUsers()).build());
if (childPermission.getEntity().equals(destinationEntity)) {
childPolicySet.add(Policy.builder().permission(childPermission.getValue())
.users(policy.getUsers()).build());
}
// Get the lateral permissions that must be applied given the child permission
// This is applied at a user level and not from the parent object. Hence only the
@ -152,13 +167,13 @@ public class PolicyGenerator {
return childPolicySet;
}
public Set<Policy> getAllChildPolicies(User user, Set<Policy> policySet, Class entity) {
public Set<Policy> getAllChildPolicies(User user, Set<Policy> policySet, Class inheritingEntity, Class destinationEntity) {
return policySet.stream()
.map(policy -> {
AclPermission aclPermission = AclPermission
.getPermissionByValue(policy.getPermission(), entity);
.getPermissionByValue(policy.getPermission(), inheritingEntity);
// Get all the child policies for the given policy and aclPermission
return getChildPolicies(policy, aclPermission, user);
return getChildPolicies(policy, aclPermission, user, destinationEntity);
}).flatMap(Collection::stream)
.collect(Collectors.toSet());
}

View File

@ -5,15 +5,16 @@ import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.dtos.ResponseDTO;
import com.appsmith.server.services.DatasourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
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 reactor.core.publisher.Mono;
@Slf4j
@RestController
@RequestMapping(Url.DATASOURCE_URL)
public class DatasourceController extends BaseController<DatasourceService, Datasource, String> {
@ -25,23 +26,8 @@ public class DatasourceController extends BaseController<DatasourceService, Data
@PostMapping("/test")
public Mono<ResponseDTO<DatasourceTestResult>> testDatasource(@RequestBody Datasource datasource) {
Mono<Datasource> datasourceMono;
if (datasource.getId() != null) {
datasourceMono = service.getById(datasource.getId());
} else {
datasourceMono = Mono.just(datasource);
}
return datasourceMono
.flatMap(service::validateDatasource)
.flatMap(datasource1 -> {
if (CollectionUtils.isEmpty(datasource1.getInvalids())) {
return service.testDatasource(datasource1);
} else {
return Mono.just(new DatasourceTestResult(datasource1.getInvalids()));
}
})
log.debug("Going to test the datasource with name: {} and id: {}", datasource.getName(), datasource.getId());
return service.testDatasource(datasource)
.map(testResult -> new ResponseDTO<>(HttpStatus.OK.value(), testResult, null));
}
}

View File

@ -45,6 +45,7 @@ public class RestApiImportController {
@RequestParam RestApiImporterType type,
@RequestParam String pageId,
@RequestParam String name,
@RequestParam String organizationId,
@RequestHeader(name = "Origin", required = false) String originHeader
) {
log.debug("Going to import API");
@ -58,7 +59,7 @@ public class RestApiImportController {
throw new IllegalStateException("Unexpected value: " + type);
}
return service.importAction(input, pageId, name)
return service.importAction(input, pageId, name, organizationId)
.map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null));
}

View File

@ -6,11 +6,13 @@ import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.acl.PolicyGenerator;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.User;
import com.appsmith.server.repositories.ActionRepository;
import com.appsmith.server.repositories.ApplicationRepository;
import com.appsmith.server.repositories.DatasourceRepository;
import com.appsmith.server.repositories.PageRepository;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
@ -37,15 +39,18 @@ public class PolicyUtils<T extends BaseDomain> {
private final ApplicationRepository applicationRepository;
private final PageRepository pageRepository;
private final ActionRepository actionRepository;
private final DatasourceRepository datasourceRepository;
public PolicyUtils(PolicyGenerator policyGenerator,
ApplicationRepository applicationRepository,
PageRepository pageRepository,
ActionRepository actionRepository) {
ActionRepository actionRepository,
DatasourceRepository datasourceRepository) {
this.policyGenerator = policyGenerator;
this.applicationRepository = applicationRepository;
this.pageRepository = pageRepository;
this.actionRepository = actionRepository;
this.datasourceRepository = datasourceRepository;
}
public T addPoliciesToExistingObject(Map<String, Policy> policyMap, T obj) {
@ -112,18 +117,35 @@ public class PolicyUtils<T extends BaseDomain> {
.collect(Collectors.toMap(Policy::getPermission, Function.identity()));
}
public Map<String, Policy> generateApplicationPoliciesFromOrganizationPolicies(Map<String, Policy> orgPolicyMap, User user) {
public Map<String, Policy> generateChildrenPoliciesFromOrganizationPolicies(Map<String, Policy> orgPolicyMap, User user, Class destinationEntity) {
Set<Policy> extractedInterestingPolicySet = new HashSet<>(orgPolicyMap.values())
.stream()
.filter(policy -> policy.getPermission().equals(ORGANIZATION_MANAGE_APPLICATIONS.getValue())
|| policy.getPermission().equals(ORGANIZATION_READ_APPLICATIONS.getValue()))
.collect(Collectors.toSet());
return policyGenerator.getAllChildPolicies(user, extractedInterestingPolicySet, Organization.class)
return policyGenerator.getAllChildPolicies(user, extractedInterestingPolicySet, Organization.class, destinationEntity)
.stream()
.collect(Collectors.toMap(Policy::getPermission, Function.identity()));
}
public Flux<Datasource> updateWithNewPoliciesToDatasourcesByOrgId(String orgId, Map<String, Policy> newPoliciesMap, boolean addPolicyToObject) {
return datasourceRepository
.findAllByOrganizationId(orgId, AclPermission.MANAGE_DATASOURCES)
// In case we have come across a datasource for this organization that the current user is not allowed to manage, move on.
.switchIfEmpty(Mono.empty())
.map(datasource -> {
if (addPolicyToObject) {
return (Datasource) addPoliciesToExistingObject(newPoliciesMap, (T) datasource);
} else {
return (Datasource) removePoliciesFromExistingObject(newPoliciesMap, (T) datasource);
}
})
.collectList()
.flatMapMany(updatedDatasources -> datasourceRepository.saveAll(updatedDatasources));
}
public Flux<Application> updateWithNewPoliciesToApplicationsByOrgId(String orgId, Map<String, Policy> newAppPoliciesMap, boolean addPolicyToObject) {
return applicationRepository
@ -148,7 +170,7 @@ public class PolicyUtils<T extends BaseDomain> {
|| policy.getPermission().equals(READ_APPLICATIONS.getValue()))
.collect(Collectors.toSet());
return policyGenerator.getAllChildPolicies(user, extractedInterestingPolicySet, Application.class)
return policyGenerator.getAllChildPolicies(user, extractedInterestingPolicySet, Application.class, Page.class)
.stream()
.collect(Collectors.toMap(Policy::getPermission, Function.identity()));
}
@ -176,7 +198,7 @@ public class PolicyUtils<T extends BaseDomain> {
|| policy.getPermission().equals(READ_PAGES.getValue()))
.collect(Collectors.toSet());
return policyGenerator.getAllChildPolicies(user, extractedInterestingPolicySet, Page.class)
return policyGenerator.getAllChildPolicies(user, extractedInterestingPolicySet, Page.class, Action.class)
.stream()
.collect(Collectors.toMap(Policy::getPermission, Function.identity()));
}

View File

@ -1,6 +1,15 @@
package com.appsmith.server.repositories;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.domains.Datasource;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface CustomDatasourceRepository extends AppsmithRepository<Datasource> {
Flux<Datasource> findAllByOrganizationId(String organizationId, AclPermission permission);
Mono<Datasource> findByName(String name, AclPermission aclPermission);
Mono<Datasource> findById(String id, AclPermission aclPermission);
}

View File

@ -1,9 +1,18 @@
package com.appsmith.server.repositories;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.QDatasource;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import static org.springframework.data.mongodb.core.query.Criteria.where;
@Component
public class CustomDatasourceRepositoryImpl extends BaseAppsmithRepositoryImpl<Datasource> implements CustomDatasourceRepository {
@ -11,4 +20,16 @@ public class CustomDatasourceRepositoryImpl extends BaseAppsmithRepositoryImpl<D
public CustomDatasourceRepositoryImpl(ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter) {
super(mongoOperations, mongoConverter);
}
@Override
public Flux<Datasource> findAllByOrganizationId(String organizationId, AclPermission permission) {
Criteria orgIdCriteria = where(fieldName(QDatasource.datasource.organizationId)).is(organizationId);
return queryAll(List.of(orgIdCriteria), permission);
}
@Override
public Mono<Datasource> findByName(String name, AclPermission aclPermission) {
Criteria nameCriteria = where(fieldName(QDatasource.datasource.name)).is(name);
return queryOne(List.of(nameCriteria), aclPermission);
}
}

View File

@ -11,8 +11,6 @@ public interface CustomOrganizationRepository extends AppsmithRepository<Organiz
Mono<Organization> findByName(String name, AclPermission aclPermission);
Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId, AclPermission aclPermission);
Flux<Organization> findByIdsIn(Set<String> orgIds, AclPermission aclPermission);
}

View File

@ -32,14 +32,6 @@ public class CustomOrganizationRepositoryImpl extends BaseAppsmithRepositoryImpl
return queryOne(List.of(nameCriteria), aclPermission);
}
@Override
public Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId, AclPermission aclPermission) {
Criteria idCriteria = where(fieldName(QOrganization.organization.id)).is(organizationId);
Criteria pluginIdCriteria = where(fieldName(QOrganization.organization.plugins.any().pluginId)).is(pluginId);
return queryOne(List.of(idCriteria, pluginIdCriteria), aclPermission);
}
@Override
public Flux<Organization> findByIdsIn(Set<String> orgIds, AclPermission aclPermission) {
Criteria orgIdsCriteria = where(fieldName(QOrganization.organization.id)).in(orgIds);

View File

@ -4,16 +4,10 @@ import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Datasource;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Repository
public interface DatasourceRepository extends BaseRepository<Datasource, String>, CustomDatasourceRepository {
Mono<Datasource> findByName(String name);
Mono<Datasource> findByIdAndOrganizationId(String id, String organizationId);
Flux<Datasource> findAllByOrganizationId(String organizationId);
@Query(value = "{" + FieldName.NAME + ": {$regex: ?0}}", count = true)
Mono<Long> countNamesByPrefix(String namePrefix);

View File

@ -13,5 +13,7 @@ public interface OrganizationRepository extends BaseRepository<Organization, Str
@Query(value = "{slug: {$regex: ?0}}", count = true)
Mono<Long> countSlugsByPrefix(String keyword);
Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId);
Mono<Organization> findByName(String name);
}

View File

@ -60,7 +60,10 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
import static com.appsmith.server.helpers.MustacheHelper.extractMustacheKeysFromJson;
@ -132,7 +135,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
return pageService
.findById(action.getPageId(), READ_PAGES)
.zipWith(userMono)
.map(tuple -> {
.flatMap(tuple -> {
Page page = tuple.getT1();
User user = tuple.getT2();
@ -140,12 +143,12 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
generateAndSetActionPolicies(page, user, action);
Datasource datasource = action.getDatasource();
if (datasource != null) {
datasource.setOrganizationId(user.getCurrentOrganizationId());
action.setDatasource(datasource);
if (datasource.getOrganizationId() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION_ID));
}
action.setOrganizationId(user.getCurrentOrganizationId());
return action;
action.setOrganizationId(datasource.getOrganizationId());
return Mono.just(action);
})
.flatMap(this::validateAndSaveActionToRepository);
}
@ -191,7 +194,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
.flatMap(datasourceService::validateDatasource);
} else {
//Data source already exists. Find the same.
datasourceMono = datasourceService.findById(action.getDatasource().getId())
datasourceMono = datasourceService.findById(action.getDatasource().getId(), MANAGE_DATASOURCES)
.switchIfEmpty(Mono.defer(() -> {
action.setIsValid(false);
invalids.add(AppsmithError.NO_RESOURCE_FOUND.getMessage(FieldName.DATASOURCE, action.getDatasource().getId()));
@ -317,7 +320,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
return Mono.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
}
if (action.getDatasource() != null && action.getDatasource().getId() != null) {
return datasourceService.findById(action.getDatasource().getId())
return datasourceService.findById(action.getDatasource().getId(), EXECUTE_DATASOURCES)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "datasource")));
}
//The data source in the action has not been persisted.
@ -583,7 +586,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
if (datasource.getId() != null) {
// its a global datasource. Get the datasource from the collection
pluginIdUpdateMono = datasourceService
.findById(datasource.getId())
.findById(datasource.getId(), READ_DATASOURCES)
.map(datasource1 -> {
action.setPluginId(datasource1.getPluginId());
return action;
@ -635,7 +638,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
.filter(policy -> policy.getPermission().equals(MANAGE_PAGES.getValue())
|| policy.getPermission().equals(READ_PAGES.getValue()))
.collect(Collectors.toSet());
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Page.class);
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Page.class, Action.class);
action.setPolicies(documentPolicies);
}
}

View File

@ -65,9 +65,6 @@ public class AnalyticsService<T extends BaseDomain> {
HashMap<String, String> analyticsProperties = new HashMap<>();
analyticsProperties.put("id", ((BaseDomain) object).getId());
analyticsProperties.put("object", object.toString());
if (user.getCurrentOrganizationId() != null) {
analyticsProperties.put("organizationId", user.getCurrentOrganizationId());
}
analytics.enqueue(
TrackMessage.builder(eventTag)

View File

@ -5,6 +5,6 @@ import reactor.core.publisher.Mono;
public interface ApiImporter {
Mono<Action> importAction(Object input, String pageId, String name);
Mono<Action> importAction(Object input, String pageId, String name, String orgId);
}

View File

@ -235,7 +235,7 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
policy.getPermission().equals(ORGANIZATION_READ_APPLICATIONS.getValue())
).collect(Collectors.toSet());
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Organization.class);
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Organization.class, Application.class);
application.setPolicies(documentPolicies);
return application;
});
@ -269,7 +269,7 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
.filter(policy -> policy.getPermission().equals(MANAGE_APPLICATIONS.getValue())
|| policy.getPermission().equals(READ_APPLICATIONS.getValue()))
.collect(Collectors.toSet());
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Application.class);
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Application.class, Page.class);
page.setPolicies(documentPolicies);
}

View File

@ -5,6 +5,6 @@ import reactor.core.publisher.Mono;
public abstract class BaseApiImporter implements ApiImporter {
public abstract Mono<Action> importAction(Object input, String pageId, String name);
public abstract Mono<Action> importAction(Object input, String pageId, String name, String orgId);
}

View File

@ -47,7 +47,7 @@ public class CurlImporterService extends BaseApiImporter {
}
@Override
public Mono<Action> importAction(Object input, String pageId, String name) {
public Mono<Action> importAction(Object input, String pageId, String name, String orgId) {
Action action = curlToAction((String) input, pageId, name);
if (action == null) {
@ -62,6 +62,7 @@ public class CurlImporterService extends BaseApiImporter {
final DatasourceConfiguration datasourceConfiguration = datasource.getDatasourceConfiguration();
datasource.setName(datasourceConfiguration.getUrl());
datasource.setPluginId(plugin.getId());
datasource.setOrganizationId(orgId);
return Mono.just(action);
})
.flatMap(actionService::create);

View File

@ -13,6 +13,8 @@ import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
@Service
@Slf4j
public class DatasourceContextServiceImpl implements DatasourceContextService {
@ -47,7 +49,7 @@ public class DatasourceContextServiceImpl implements DatasourceContextService {
Mono<Datasource> datasourceMono;
if (datasource.getId() != null) {
datasourceMono = datasourceService.findById(datasourceId);
datasourceMono = datasourceService.findById(datasourceId, EXECUTE_DATASOURCES);
} else {
datasourceMono = Mono.just(datasource);
}
@ -94,7 +96,7 @@ public class DatasourceContextServiceImpl implements DatasourceContextService {
return Mono.empty();
}
Mono<Datasource> datasourceMono = datasourceService.findById(datasourceId);
Mono<Datasource> datasourceMono = datasourceService.findById(datasourceId, EXECUTE_DATASOURCES);
Mono<Plugin> pluginMono = datasourceMono
.flatMap(datasource -> pluginService.findById(datasource.getPluginId()));

View File

@ -1,6 +1,7 @@
package com.appsmith.server.services;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.domains.Datasource;
import reactor.core.publisher.Mono;
@ -12,7 +13,7 @@ public interface DatasourceService extends CrudService<Datasource, String> {
Mono<Datasource> findByName(String name);
Mono<Datasource> findById(String id);
Mono<Datasource> findById(String id, AclPermission aclPermission);
Set<String> extractKeysFromDatasource(Datasource datasource);

View File

@ -2,7 +2,10 @@ package com.appsmith.server.services;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.Policy;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.acl.PolicyGenerator;
import com.appsmith.server.constants.AnalyticsEvents;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Datasource;
@ -18,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
@ -28,7 +32,10 @@ import javax.validation.Validator;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIONS;
import static com.appsmith.server.helpers.BeanCopyUtils.copyNestedNonNullProperties;
import static com.appsmith.server.helpers.MustacheHelper.extractMustacheKeysFromJson;
@ -42,6 +49,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
private final ObjectMapper objectMapper;
private final PluginService pluginService;
private final PluginExecutorHelper pluginExecutorHelper;
private final PolicyGenerator policyGenerator;
@Autowired
public DatasourceServiceImpl(Scheduler scheduler,
@ -54,7 +62,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
SessionUserService sessionUserService,
ObjectMapper objectMapper,
PluginService pluginService,
PluginExecutorHelper pluginExecutorHelper) {
PluginExecutorHelper pluginExecutorHelper, PolicyGenerator policyGenerator) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.repository = repository;
this.organizationService = organizationService;
@ -62,6 +70,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
this.objectMapper = objectMapper;
this.pluginService = pluginService;
this.pluginExecutorHelper = pluginExecutorHelper;
this.policyGenerator = policyGenerator;
}
@Override
@ -69,6 +78,9 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
if (datasource.getId() != null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
}
if (datasource.getOrganizationId() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION_ID));
}
Mono<Datasource> datasourceMono = Mono.just(datasource);
@ -81,7 +93,30 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
});
}
return datasourceMono.flatMap(this::validateAndSaveDatasourceToRepository);
return datasourceMono
.flatMap(datasource1 ->
sessionUserService.getCurrentUser()
.flatMap(user -> {
// Create policies for this datasource -> This datasource should inherit its permissions and policies from
// the organization and this datasource should also allow the current user to crud this datasource.
return organizationService.findById(datasource1.getOrganizationId(), AclPermission.ORGANIZATION_MANAGE_APPLICATIONS)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ORGANIZATION, datasource1.getOrganizationId())))
.map(org -> {
Set<Policy> policySet = org.getPolicies().stream()
.filter(policy ->
policy.getPermission().equals(ORGANIZATION_MANAGE_APPLICATIONS.getValue()) ||
policy.getPermission().equals(ORGANIZATION_READ_APPLICATIONS.getValue())
).collect(Collectors.toSet());
Set<Policy> documentPolicies = policyGenerator.getAllChildPolicies(user, policySet, Organization.class, Datasource.class);
datasource1.setPolicies(documentPolicies);
return datasource1;
});
})
)
.flatMap(this::validateAndSaveDatasourceToRepository);
}
@Override
@ -114,39 +149,20 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
}
Mono<String> currentOrganizationMono = sessionUserService
.getCurrentUser()
.map(user -> user.getCurrentOrganizationId())
.cache();
if (datasource.getPluginId() == null) {
invalids.add(AppsmithError.PLUGIN_ID_NOT_GIVEN.getMessage());
datasource.setInvalids(invalids);
return currentOrganizationMono
.map(currentOrgId -> {
datasource.setOrganizationId(currentOrgId);
return datasource;
});
return Mono.just(datasource);
}
Mono<Organization> checkPluginInstallationAndThenReturnOrganizationMono = currentOrganizationMono
.flatMap(currentOrgId -> organizationService.findByIdAndPluginsPluginId(
currentOrgId, datasource.getPluginId()))
Mono<Organization> checkPluginInstallationAndThenReturnOrganizationMono = organizationService
.findByIdAndPluginsPluginId(datasource.getOrganizationId(), datasource.getPluginId())
.switchIfEmpty(Mono.defer(() -> {
invalids.add(AppsmithError.PLUGIN_NOT_INSTALLED.getMessage(datasource.getPluginId()));
datasource.setInvalids(invalids);
return Mono.just(new Organization());
}));
//Add organization id to the datasource.
Mono<Datasource> updatedDatasourceMono = checkPluginInstallationAndThenReturnOrganizationMono
.map(organization -> {
if (organization.getId() != null) {
datasource.setOrganizationId(organization.getId());
}
return datasource;
});
if (datasource.getDatasourceConfiguration() == null) {
invalids.add(AppsmithError.NO_CONFIGURATION_FOUND_IN_DATASOURCE.getMessage());
}
@ -154,18 +170,16 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
Mono<PluginExecutor> pluginExecutorMono = pluginExecutorHelper.getPluginExecutor(pluginService.findById(datasource.getPluginId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.PLUGIN, datasource.getPluginId())));
return Mono.zip(updatedDatasourceMono, pluginExecutorMono)
.flatMap(tuple -> {
Datasource datasource1 = tuple.getT1();
PluginExecutor pluginExecutor = tuple.getT2();
DatasourceConfiguration datasourceConfiguration = datasource1.getDatasourceConfiguration();
return checkPluginInstallationAndThenReturnOrganizationMono
.then(pluginExecutorMono)
.flatMap(pluginExecutor -> {
DatasourceConfiguration datasourceConfiguration = datasource.getDatasourceConfiguration();
if (datasourceConfiguration != null && !pluginExecutor.isDatasourceValid(datasourceConfiguration)) {
invalids.addAll(pluginExecutor.validateDatasource(datasourceConfiguration));
}
datasource1.setInvalids(invalids);
return Mono.just(datasource1);
datasource.setInvalids(invalids);
return Mono.just(datasource);
});
}
@ -177,6 +191,26 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
@Override
public Mono<DatasourceTestResult> testDatasource(Datasource datasource) {
Mono<Datasource> datasourceMono;
if (datasource.getId() != null) {
datasourceMono = getById(datasource.getId());
} else {
datasourceMono = Mono.just(datasource);
}
return datasourceMono
.flatMap(this::validateDatasource)
.flatMap(datasource1 -> {
if (CollectionUtils.isEmpty(datasource1.getInvalids())) {
return testDatasourceViaPlugin(datasource1);
} else {
return Mono.just(new DatasourceTestResult(datasource1.getInvalids()));
}
});
}
private Mono<DatasourceTestResult> testDatasourceViaPlugin(Datasource datasource) {
Mono<PluginExecutor> pluginExecutorMono = pluginExecutorHelper.getPluginExecutor(pluginService.findById(datasource.getPluginId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.PLUGIN, datasource.getPluginId())));
@ -186,12 +220,12 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
@Override
public Mono<Datasource> findByName(String name) {
return repository.findByName(name);
return repository.findByName(name, AclPermission.READ_DATASOURCES);
}
@Override
public Mono<Datasource> findById(String id) {
return repository.findById(id);
public Mono<Datasource> findById(String id, AclPermission aclPermission) {
return repository.findById(id, aclPermission);
}
@Override
@ -214,19 +248,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
return sessionUserService
.getCurrentUser()
.flatMapMany(user -> repository.findAllByOrganizationId(user.getCurrentOrganizationId()));
}
@Override
public Mono<Datasource> getById(String id) {
if (id == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
}
return sessionUserService
.getCurrentUser()
.flatMap(user -> repository.findByIdAndOrganizationId(id, user.getCurrentOrganizationId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.DATASOURCE, id)));
.flatMapMany(user -> repository.findAllByOrganizationId(user.getCurrentOrganizationId(), AclPermission.READ_DATASOURCES));
}
@Override

View File

@ -1,6 +1,5 @@
package com.appsmith.server.services;
import com.appsmith.external.models.Policy;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.acl.AppsmithRole;
import com.appsmith.server.acl.PolicyGenerator;
@ -101,15 +100,6 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
.map(max -> initialSlug + (max == 0 ? "" : (max + 1)));
}
private Set<Policy> crudOrgPolicy(User user) {
Set<Policy> policySet = user.getPolicies().stream()
.filter(policy ->
policy.getPermission().equals(USER_MANAGE_ORGANIZATIONS.getValue())
).collect(Collectors.toSet());
return policyGenerator.getAllChildPolicies(user, policySet, User.class);
}
/**
* This function does the following:
* 1. Creates the organization for the user
@ -258,7 +248,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
@Override
public Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId) {
return repository.findByIdAndPluginsPluginId(organizationId, pluginId, AclPermission.READ_ORGANIZATIONS);
return repository.findByIdAndPluginsPluginId(organizationId, pluginId);
}
@Override

View File

@ -19,7 +19,7 @@ import java.util.List;
@Slf4j
public class PostmanImporterService extends BaseApiImporter {
@Override
public Mono<Action> importAction(Object input, String pageId, String name) {
public Mono<Action> importAction(Object input, String pageId, String name, String orgId) {
Action action = new Action();
ActionConfiguration actionConfiguration = new ActionConfiguration();
Datasource datasource = new Datasource();

View File

@ -6,6 +6,7 @@ import com.appsmith.server.acl.AppsmithRole;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.User;
@ -135,7 +136,8 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
// Generate all the policies for Organization, Application, Page and Actions
Set<AclPermission> rolePermissions = role.getPermissions();
Map<String, Policy> orgPolicyMap = policyUtils.generatePolicyFromPermission(rolePermissions, user);
Map<String, Policy> applicationPolicyMap = policyUtils.generateApplicationPoliciesFromOrganizationPolicies(orgPolicyMap, user);
Map<String, Policy> applicationPolicyMap = policyUtils.generateChildrenPoliciesFromOrganizationPolicies(orgPolicyMap, user, Application.class);
Map<String, Policy> datasourcePolicyMap = policyUtils.generateChildrenPoliciesFromOrganizationPolicies(orgPolicyMap, user, Datasource.class);
Map<String, Policy> pagePolicyMap = policyUtils.generatePagePoliciesFromApplicationPolicies(applicationPolicyMap, user);
Map<String, Policy> actionPolicyMap = policyUtils.generateActionPoliciesFromPagePolicies(pagePolicyMap, user);
@ -144,16 +146,17 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
updatedOrganization.setUserRoles(userRoles);
// Update the underlying application/page/action
Flux<Datasource> updatedDatasourcesFlux = policyUtils.updateWithNewPoliciesToDatasourcesByOrgId(updatedOrganization.getId(), datasourcePolicyMap, true);
Flux<Application> updatedApplicationsFlux = policyUtils.updateWithNewPoliciesToApplicationsByOrgId(updatedOrganization.getId(), applicationPolicyMap, true);
Flux<Page> updatedPagesFlux = updatedApplicationsFlux
.flatMap(application -> policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, true));
Flux<Action> updatedActionsFlux = updatedPagesFlux
.flatMap(page -> policyUtils.updateWithPagePermissionsToAllItsActions(page.getId(), actionPolicyMap, true));
return Mono.zip(updatedActionsFlux.collectList(), Mono.just(updatedOrganization))
return Mono.zip(updatedDatasourcesFlux.collectList(), updatedActionsFlux.collectList(), Mono.just(updatedOrganization))
.flatMap(tuple -> {
//By now all the applications/pages/actions have been updated. Just save the organization now
Organization updatedOrgBeforeSave = tuple.getT2();
//By now all the datasources/applications/pages/actions have been updated. Just save the organization now
Organization updatedOrgBeforeSave = tuple.getT3();
return organizationRepository.save(updatedOrgBeforeSave);
});
}
@ -198,7 +201,8 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
// Generate all the policies for Organization, Application, Page and Actions
Set<AclPermission> rolePermissions = role.getPermissions();
Map<String, Policy> orgPolicyMap = policyUtils.generatePolicyFromPermission(rolePermissions, user);
Map<String, Policy> applicationPolicyMap = policyUtils.generateApplicationPoliciesFromOrganizationPolicies(orgPolicyMap, user);
Map<String, Policy> applicationPolicyMap = policyUtils.generateChildrenPoliciesFromOrganizationPolicies(orgPolicyMap, user, Application.class);
Map<String, Policy> datasourcePolicyMap = policyUtils.generateChildrenPoliciesFromOrganizationPolicies(orgPolicyMap, user, Datasource.class);
Map<String, Policy> pagePolicyMap = policyUtils.generatePagePoliciesFromApplicationPolicies(applicationPolicyMap, user);
Map<String, Policy> actionPolicyMap = policyUtils.generateActionPoliciesFromPagePolicies(pagePolicyMap, user);
@ -207,16 +211,17 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
updatedOrganization.setUserRoles(userRoles);
// Update the underlying application/page/action
Flux<Datasource> updatedDatasourcesFlux = policyUtils.updateWithNewPoliciesToDatasourcesByOrgId(updatedOrganization.getId(), datasourcePolicyMap, false);
Flux<Application> updatedApplicationsFlux = policyUtils.updateWithNewPoliciesToApplicationsByOrgId(updatedOrganization.getId(), applicationPolicyMap, false);
Flux<Page> updatedPagesFlux = updatedApplicationsFlux
.flatMap(application -> policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, false));
Flux<Action> updatedActionsFlux = updatedPagesFlux
.flatMap(page -> policyUtils.updateWithPagePermissionsToAllItsActions(page.getId(), actionPolicyMap, false));
return Mono.zip(updatedActionsFlux.collectList(), Mono.just(updatedOrganization))
return Mono.zip(updatedDatasourcesFlux.collectList(), updatedActionsFlux.collectList(), Mono.just(updatedOrganization))
.flatMap(tuple -> {
//By now all the applications/pages/actions have been updated. Just save the organization now
Organization updatedOrgBeforeSave = tuple.getT2();
//By now all the datasources/applications/pages/actions have been updated. Just save the organization now
Organization updatedOrgBeforeSave = tuple.getT3();
return organizationRepository.save(updatedOrgBeforeSave);
});
}
@ -229,15 +234,29 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
Mono<Organization> organizationMono = organizationRepository.findById(orgId, MANAGE_ORGANIZATIONS);
Mono<User> userMono = userRepository.findByEmail(userRole.getUsername());
Mono<User> currentUserMono = sessionUserService.getCurrentUser();
return organizationMono.zipWith(userMono)
return Mono.zip(organizationMono, userMono, currentUserMono)
.flatMap(tuple -> {
Organization organization = tuple.getT1();
User user = tuple.getT2();
User currentUser = tuple.getT3();
if (user.getUsername().equals(currentUser.getUsername())) {
// The user is trying to change its own role. Disallow the same.
return Mono.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
}
List<UserRole> userRoles = organization.getUserRoles();
for (UserRole role : userRoles) {
if (role.getUsername().equals(userRole.getUsername())) {
// User found in the organization.
if (role.getRoleName().equals(userRole.getRoleName())) {
// No change in the role. Do nothing.
Mono.just(userRole);
}
// Step 1. Remove the existing role of the user from the organization
Mono<Organization> userRemovedOrganizationMono = this.removeUserRoleFromOrganization(organization.getId(), userRole);
// Step 2. Add the new role (if present) to the organization for the user

View File

@ -2,21 +2,29 @@ package com.appsmith.server.services;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.Policy;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PluginExecutorHelper;
import com.appsmith.server.repositories.OrganizationRepository;
import com.appsmith.server.repositories.PluginRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpMethod;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.annotation.DirtiesContext;
@ -51,12 +59,23 @@ public class ActionServiceTest {
@Autowired
OrganizationService organizationService;
@Autowired
OrganizationRepository organizationRepository;
@Autowired
PluginRepository pluginRepository;
@MockBean
PluginExecutorHelper pluginExecutorHelper;
Application testApp = null;
Page testPage = null;
int i = 0;
Datasource datasource;
@Before
@WithUserDetails(value = "api_user")
public void setup() {
@ -73,6 +92,14 @@ public class ActionServiceTest {
testApp = applicationPageService.createApplication(application, organization.getId()).block();
testPage = pageService.getById(testApp.getPages().get(0).getId()).block();
}
Organization testOrg = organizationRepository.findByName("Another Test Organization", AclPermission.READ_ORGANIZATIONS).block();
orgId = testOrg.getId();
datasource = new Datasource();
datasource.setName("Default Database");
datasource.setOrganizationId(orgId);
Plugin installed_plugin = pluginRepository.findByPackageName("installed-plugin").block();
datasource.setPluginId(installed_plugin.getId());
}
@After
@ -87,6 +114,8 @@ public class ActionServiceTest {
@Test
@WithUserDetails(value = "api_user")
public void createValidActionAndCheckPermissions() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new DatasourceServiceTest.TestPluginExecutor()));
Policy manageActionPolicy = Policy.builder().permission(MANAGE_ACTIONS.getValue())
.users(Set.of("api_user"))
.build();
@ -100,6 +129,7 @@ public class ActionServiceTest {
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setHttpMethod(HttpMethod.GET);
action.setActionConfiguration(actionConfiguration);
action.setDatasource(datasource);
Mono<Action> actionMono = actionService.create(action);
@ -117,12 +147,15 @@ public class ActionServiceTest {
@Test
@WithUserDetails(value = "api_user")
public void createValidActionWithJustName() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new DatasourceServiceTest.TestPluginExecutor()));
Action action = new Action();
action.setName("randomActionName");
action.setPageId(testPage.getId());
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setHttpMethod(HttpMethod.GET);
action.setActionConfiguration(actionConfiguration);
action.setDatasource(datasource);
Mono<Action> actionMono = Mono.just(action)
.flatMap(actionService::create);
StepVerifier
@ -130,8 +163,7 @@ public class ActionServiceTest {
.assertNext(createdAction -> {
assertThat(createdAction.getId()).isNotEmpty();
assertThat(createdAction.getName()).isEqualTo(action.getName());
assertThat(createdAction.getIsValid()).isFalse();
assertThat(createdAction.getInvalids().size()).isEqualTo(1);
assertThat(createdAction.getIsValid()).isTrue();
})
.verifyComplete();
}
@ -139,9 +171,12 @@ public class ActionServiceTest {
@Test
@WithUserDetails(value = "api_user")
public void createValidActionNullActionConfiguration() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new DatasourceServiceTest.TestPluginExecutor()));
Action action = new Action();
action.setName("randomActionName2");
action.setPageId(testPage.getId());
action.setDatasource(datasource);
Mono<Action> actionMono = Mono.just(action)
.flatMap(actionService::create);
StepVerifier
@ -163,6 +198,7 @@ public class ActionServiceTest {
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setHttpMethod(HttpMethod.GET);
action.setActionConfiguration(actionConfiguration);
action.setDatasource(datasource);
Mono<Action> actionMono = Mono.just(action)
.flatMap(actionService::create);
StepVerifier

View File

@ -112,7 +112,7 @@ public class CurlImporterServiceTest {
Page page = pageService.findById(application.getPages().get(0).getId(), AclPermission.MANAGE_PAGES).block();
String command = "curl -X GET http://localhost:8080/api/v1/actions?name=something -H 'Accept: */*' -H 'Accept-Encoding: gzip, deflate' -H 'Authorization: Basic YXBpX3VzZXI6OHVBQDsmbUI6Y252Tn57Iw==' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Content-Type: application/json' -H 'Cookie: SESSION=97c5def4-4f72-45aa-96fe-e8a9f5ade0b5,SESSION=97c5def4-4f72-45aa-96fe-e8a9f5ade0b5; SESSION=' -H 'Host: localhost:8080' -H 'Postman-Token: 16e4b6bc-2c7a-4ab1-a127-bca382dfc0f0,a6655daa-db07-4c5e-aca3-3fd505bd230d' -H 'User-Agent: PostmanRuntime/7.20.1' -H 'cache-control: no-cache' -d '{someJson}'";
Mono<Action> action = curlImporterService.importAction(command, page.getId(), "actionName");
Mono<Action> action = curlImporterService.importAction(command, page.getId(), "actionName", orgId);
StepVerifier
.create(action)
.assertNext(action1 -> {
@ -542,7 +542,7 @@ public class CurlImporterServiceTest {
public void importInvalidCurlCommand() {
String command = "invalid curl command here";
Mono<Action> actionMono = curlImporterService.importAction(command, "pageId", "actionName");
Mono<Action> actionMono = curlImporterService.importAction(command, "pageId", "actionName", orgId);
StepVerifier
.create(actionMono)

View File

@ -4,15 +4,19 @@ import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.Connection;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.Policy;
import com.appsmith.external.models.SSLDetails;
import com.appsmith.external.models.UploadedFile;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PluginExecutorHelper;
import com.appsmith.server.repositories.OrganizationRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
@ -31,6 +35,9 @@ import reactor.util.function.Tuple2;
import java.util.HashSet;
import java.util.Set;
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@ -45,10 +52,13 @@ public class DatasourceServiceTest {
@Autowired
PluginService pluginService;
@Autowired
OrganizationRepository organizationRepository;
@MockBean
PluginExecutorHelper pluginExecutorHelper;
class TestPluginExecutor implements PluginExecutor {
static class TestPluginExecutor implements PluginExecutor {
@Override
public Mono<Object> execute(Object connection, DatasourceConfiguration datasourceConfiguration, ActionConfiguration actionConfiguration) {
@ -80,9 +90,13 @@ public class DatasourceServiceTest {
}
}
@Before
public void setup() {
String orgId = "";
@Before
@WithUserDetails(value = "api_user")
public void setup() {
Organization testOrg = organizationRepository.findByName("Another Test Organization", AclPermission.READ_ORGANIZATIONS).block();
orgId = testOrg.getId();
}
@Test
@ -90,6 +104,7 @@ public class DatasourceServiceTest {
public void createDatasourceWithNullPluginId() {
Datasource datasource = new Datasource();
datasource.setName("DS-with-null-pluginId");
datasource.setOrganizationId(orgId);
Mono<Datasource> datasourceMono = Mono.just(datasource)
.flatMap(datasourceService::create);
StepVerifier
@ -108,6 +123,7 @@ public class DatasourceServiceTest {
public void createDatasourceWithId() {
Datasource datasource = new Datasource();
datasource.setId("randomId");
datasource.setOrganizationId(orgId);
Mono<Datasource> datasourceMono = Mono.just(datasource)
.flatMap(datasourceService::create);
StepVerifier
@ -125,6 +141,7 @@ public class DatasourceServiceTest {
Mono<Plugin> pluginMono = pluginService.findByName("Not Installed Plugin Name");
Datasource datasource = new Datasource();
datasource.setName("DS-with-uninstalled-plugin");
datasource.setOrganizationId(orgId);
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
datasourceConfiguration.setUrl("http://test.com");
datasource.setDatasourceConfiguration(datasourceConfiguration);
@ -155,6 +172,7 @@ public class DatasourceServiceTest {
Mono<Plugin> pluginMono = pluginService.findByName("Installed Plugin Name");
Datasource datasource = new Datasource();
datasource.setName("test datasource name");
datasource.setOrganizationId(orgId);
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
datasourceConfiguration.setUrl("http://test.com");
datasource.setDatasourceConfiguration(datasourceConfiguration);
@ -169,6 +187,18 @@ public class DatasourceServiceTest {
assertThat(createdDatasource.getId()).isNotEmpty();
assertThat(createdDatasource.getPluginId()).isEqualTo(datasource.getPluginId());
assertThat(createdDatasource.getName()).isEqualTo(datasource.getName());
Policy manageDatasourcePolicy = Policy.builder().permission(MANAGE_DATASOURCES.getValue())
.users(Set.of("api_user"))
.build();
Policy readDatasourcePolicy = Policy.builder().permission(READ_DATASOURCES.getValue())
.users(Set.of("api_user"))
.build();
Policy executeDatasourcePolicy = Policy.builder().permission(EXECUTE_DATASOURCES.getValue())
.users(Set.of("api_user"))
.build();
assertThat(createdDatasource.getPolicies()).isNotEmpty();
assertThat(createdDatasource.getPolicies()).containsAll(Set.of(manageDatasourcePolicy, readDatasourcePolicy, executeDatasourcePolicy));
})
.verifyComplete();
}
@ -180,6 +210,7 @@ public class DatasourceServiceTest {
Datasource datasource = new Datasource();
datasource.setName("test db datasource");
datasource.setOrganizationId(orgId);
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
Connection connection = new Connection();
connection.setMode(Connection.Mode.READ_ONLY);
@ -192,7 +223,7 @@ public class DatasourceServiceTest {
datasourceConfiguration.setConnection(connection);
datasource.setDatasourceConfiguration(datasourceConfiguration);
datasource.setOrganizationId("fixme-put-valid-org-id-here");
datasource.setOrganizationId(orgId);
Mono<Plugin> pluginMono = pluginService.findByName("Installed Plugin Name");
@ -233,10 +264,12 @@ public class DatasourceServiceTest {
Datasource datasource1 = new Datasource();
datasource1.setDatasourceConfiguration(new DatasourceConfiguration());
datasource1.setOrganizationId(orgId);
datasource1.getDatasourceConfiguration().setUrl("http://test.com");
Datasource datasource2 = new Datasource();
datasource2.setDatasourceConfiguration(new DatasourceConfiguration());
datasource2.setOrganizationId(orgId);
datasource2.getDatasourceConfiguration().setUrl("http://test.com");
final Mono<Tuple2<Datasource, Datasource>> datasourcesMono = pluginMono
@ -262,4 +295,35 @@ public class DatasourceServiceTest {
})
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void testDatasourceValid() {
Mono<Plugin> pluginMono = pluginService.findByName("Installed Plugin Name");
Datasource datasource = new Datasource();
datasource.setName("test datasource name for test");
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
datasourceConfiguration.setUrl("http://test.com");
datasource.setDatasourceConfiguration(datasourceConfiguration);
datasource.setOrganizationId(orgId);
Mono<Datasource> datasourceMono = pluginMono.map(plugin -> {
datasource.setPluginId(plugin.getId());
return datasource;
}).flatMap(datasourceService::create);
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new TestPluginExecutor()));
Mono<DatasourceTestResult> testResultMono = datasourceMono.flatMap(datasource1 -> datasourceService.testDatasource(datasource1));
StepVerifier
.create(testResultMono)
.assertNext(testResult -> {
assertThat(testResult).isNotNull();
assertThat(testResult.getInvalids()).isEmpty();
})
.verifyComplete();
}
}

View File

@ -5,12 +5,14 @@ import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.acl.AppsmithRole;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.User;
import com.appsmith.server.domains.UserRole;
import com.appsmith.server.dtos.InviteUserDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.DatasourceRepository;
import com.appsmith.server.repositories.OrganizationRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
@ -25,15 +27,19 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuple3;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.MANAGE_ORGANIZATIONS;
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.READ_ORGANIZATIONS;
import static org.assertj.core.api.Assertions.assertThat;
@ -61,6 +67,12 @@ public class OrganizationServiceTest {
@Autowired
UserService userService;
@Autowired
DatasourceService datasourceService;
@Autowired
DatasourceRepository datasourceRepository;
Organization organization;
@Before
@ -386,13 +398,13 @@ public class OrganizationServiceTest {
}
/**
* This test checks for application permissions if a user is invited to the organization as an Admin.
* This test checks for application and datasource permissions if a user is invited to the organization as an Admin.
* The existing applications in the organization should now have the new user be included in both
* manage:applications and read:applications policies.
*/
@Test
@WithUserDetails(value = "api_user")
public void addUserToOrganizationAsAdminAndCheckApplicationPermissions() {
public void addUserToOrganizationAsAdminAndCheckApplicationAndDatasourcePermissions() {
Organization organization = new Organization();
organization.setName("Member Management Admin Test Organization");
organization.setDomain("example.com");
@ -410,6 +422,15 @@ public class OrganizationServiceTest {
return applicationPageService.createApplication(application, org.getId());
});
// Create datasource for this organization
Mono<Datasource> datasourceMono = organizationMono
.flatMap(org -> {
Datasource datasource = new Datasource();
datasource.setName("test datasource");
datasource.setOrganizationId(org.getId());
return datasourceService.create(datasource);
});
Mono<Organization> userAddedToOrgMono = organizationMono
.flatMap(organization1 -> {
// Add user to organization
@ -417,6 +438,10 @@ public class OrganizationServiceTest {
userRole.setRoleName(AppsmithRole.ORGANIZATION_ADMIN.getName());
userRole.setUsername("usertest@usertest.com");
return userOrganizationService.addUserRoleToOrganization(organization1.getId(), userRole);
})
.map(organization1 -> {
log.debug("Organization policies after adding user is : {}", organization1.getPolicies());
return organization1;
});
Mono<Application> readApplicationByNameMono = applicationService.findByName("User Management Admin Test Application",
@ -426,16 +451,23 @@ public class OrganizationServiceTest {
Mono<Organization> readOrganizationByNameMono = organizationRepository.findByName("Member Management Admin Test Organization")
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "organization by name")));
Mono<Tuple2<Application, Organization>> testMono = organizationMono
.then(applicationMono)
Mono<Datasource> readDatasourceByNameMono = datasourceRepository.findByName("test datasource", READ_DATASOURCES)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "Datasource")));
Mono<Tuple3<Application, Organization, Datasource>> testMono = organizationMono
// create application and datasource
.then(Mono.zip(applicationMono, datasourceMono))
// Now add the user
.then(userAddedToOrgMono)
.then(Mono.zip(readApplicationByNameMono, readOrganizationByNameMono));
// Read application, organization and datasource now to confirm the policies.
.then(Mono.zip(readApplicationByNameMono, readOrganizationByNameMono, readDatasourceByNameMono));
StepVerifier
.create(testMono)
.assertNext(tuple -> {
Application application = tuple.getT1();
Organization org = tuple.getT2();
Datasource datasource = tuple.getT3();
assertThat(org).isNotNull();
assertThat(org.getUserRoles().get(1).getUsername()).isEqualTo("usertest@usertest.com");
@ -449,6 +481,23 @@ public class OrganizationServiceTest {
assertThat(application.getPolicies()).isNotEmpty();
assertThat(application.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy));
/*
* Check for datasource permissions after the user addition
*/
Policy manageDatasourcePolicy = Policy.builder().permission(MANAGE_DATASOURCES.getValue())
.users(Set.of("api_user", "usertest@usertest.com"))
.build();
Policy readDatasourcePolicy = Policy.builder().permission(READ_DATASOURCES.getValue())
.users(Set.of("api_user", "usertest@usertest.com"))
.build();
Policy executeDatasourcePolicy = Policy.builder().permission(EXECUTE_DATASOURCES.getValue())
.users(Set.of("api_user", "usertest@usertest.com"))
.build();
assertThat(datasource.getPolicies()).isNotEmpty();
assertThat(datasource.getPolicies()).containsAll(Set.of(manageDatasourcePolicy, readDatasourcePolicy,
executeDatasourcePolicy));
})
.verifyComplete();
}