Adding the policy hierarchy graph and the lateral policy graph
These graphs help us map policies that are inherited from the parent and also lateral policies that are assigned to the users given that the user has a particular permission. Currently, the hierarchy has been defined for org & application. Need to cascade it to more documents such as pages & actions.
This commit is contained in:
parent
a892ee90b5
commit
1f35bd6a07
1
app/server/.gitignore
vendored
1
app/server/.gitignore
vendored
|
|
@ -4,3 +4,4 @@ target/**
|
|||
**/.idea
|
||||
**/target
|
||||
**/dist
|
||||
*.iml
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.appsmith.external.models;
|
|||
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
|
@ -16,6 +17,7 @@ import java.util.Set;
|
|||
@Setter
|
||||
@ToString
|
||||
@Builder
|
||||
@EqualsAndHashCode
|
||||
public class Policy implements Serializable {
|
||||
|
||||
String permission;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.appsmith.server.constants;
|
||||
package com.appsmith.server.acl;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.appsmith.server.acl;
|
||||
|
||||
import com.appsmith.server.domains.Action;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.Page;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum AclPermission {
|
||||
|
||||
// These are generic permissions created to make the transition to the new ACL format easy. They must be removed
|
||||
CREATE("create", null),
|
||||
READ("read", null),
|
||||
UPDATE("update", null),
|
||||
DELETE("delete", null),
|
||||
|
||||
MANAGE_ORGANIZATIONS("manage:organizations", Organization.class),
|
||||
READ_ORGANIZATIONS("read:organizations", Organization.class),
|
||||
ORGANIZATION_MANAGE_APPLICATIONS("manage:orgApplications", Organization.class),
|
||||
ORGANIZATION_READ_APPLICATIONS("read:orgApplications", Organization.class),
|
||||
ORGANIZATION_PUBLISH_APPLICATIONS("publish:orgApplications", Organization.class),
|
||||
|
||||
MANAGE_APPLICATIONS("manage:applications", Application.class),
|
||||
READ_APPLICATIONS("read:applications", Application.class),
|
||||
PUBLISH_APPLICATIONS("publish:applications", Application.class),
|
||||
|
||||
MANAGE_PAGES("manage:pages", Page.class),
|
||||
READ_PAGES("read:pages", Page.class),
|
||||
|
||||
MANAGE_ACTIONS("manage:actions", Action.class),
|
||||
READ_ACTIONS("read:actions", Action.class),
|
||||
EXECUTE_ACTIONS("execute:actions", Action.class);
|
||||
|
||||
private String value;
|
||||
private Class entity;
|
||||
|
||||
AclPermission(String value, Class entity) {
|
||||
this.value = value;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public static final AclPermission getPermissionByValue(String value, Class entity) {
|
||||
for (AclPermission permission : values()) {
|
||||
if (permission.getValue().equals(value) && permission.getEntity().equals(entity)) {
|
||||
return permission;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package com.appsmith.server.acl;
|
||||
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.server.domains.User;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jgrapht.Graph;
|
||||
import org.jgrapht.graph.DefaultEdge;
|
||||
import org.jgrapht.graph.DirectedMultigraph;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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_ORGANIZATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_PUBLISH_APPLICATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIONS;
|
||||
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_ORGANIZATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PolicyGenerator {
|
||||
|
||||
/**
|
||||
* This graph defines the hierarchy of permissions from parent objects
|
||||
*/
|
||||
Graph<AclPermission, DefaultEdge> hierarchyGraph = new DirectedMultigraph<>(DefaultEdge.class);
|
||||
|
||||
/**
|
||||
* This graph defines the permissions that must be given to a user given that they have another permission
|
||||
* Eg: If the user is being given MANAGE_APPLICATION permission, they must also be given READ_APPLICATION permission
|
||||
*/
|
||||
Graph<AclPermission, DefaultEdge> lateralGraph = new DirectedMultigraph<>(DefaultEdge.class);
|
||||
|
||||
@PostConstruct
|
||||
public void createPolicyGraph() {
|
||||
|
||||
EnumSet.allOf(AclPermission.class)
|
||||
.forEach(permission -> {
|
||||
hierarchyGraph.addVertex(permission);
|
||||
lateralGraph.addVertex(permission);
|
||||
});
|
||||
|
||||
createOrganizationPolicyGraph();
|
||||
createApplicationPolicyGraph();
|
||||
createPagePolicyGraph();
|
||||
createActionPolicyGraph();
|
||||
|
||||
log.debug("Successfully created the createGraph & lateralGraph");
|
||||
}
|
||||
|
||||
private void createOrganizationPolicyGraph() {
|
||||
lateralGraph.addEdge(MANAGE_ORGANIZATIONS, READ_ORGANIZATIONS);
|
||||
lateralGraph.addEdge(MANAGE_ORGANIZATIONS, ORGANIZATION_MANAGE_APPLICATIONS);
|
||||
lateralGraph.addEdge(MANAGE_ORGANIZATIONS, ORGANIZATION_READ_APPLICATIONS);
|
||||
lateralGraph.addEdge(MANAGE_ORGANIZATIONS, ORGANIZATION_PUBLISH_APPLICATIONS);
|
||||
}
|
||||
|
||||
private void createApplicationPolicyGraph() {
|
||||
hierarchyGraph.addEdge(ORGANIZATION_MANAGE_APPLICATIONS, MANAGE_APPLICATIONS);
|
||||
hierarchyGraph.addEdge(ORGANIZATION_READ_APPLICATIONS, READ_APPLICATIONS);
|
||||
hierarchyGraph.addEdge(ORGANIZATION_PUBLISH_APPLICATIONS, PUBLISH_APPLICATIONS);
|
||||
|
||||
// If the user is being given MANAGE_APPLICATION permission, they must also be given READ_APPLICATION perm
|
||||
lateralGraph.addEdge(MANAGE_APPLICATIONS, READ_APPLICATIONS);
|
||||
}
|
||||
|
||||
private void createActionPolicyGraph() {
|
||||
hierarchyGraph.addEdge(MANAGE_PAGES, MANAGE_ACTIONS);
|
||||
hierarchyGraph.addEdge(READ_PAGES, READ_ACTIONS);
|
||||
|
||||
lateralGraph.addEdge(MANAGE_PAGES, READ_PAGES);
|
||||
}
|
||||
|
||||
private void createPagePolicyGraph() {
|
||||
hierarchyGraph.addEdge(MANAGE_APPLICATIONS, MANAGE_PAGES);
|
||||
hierarchyGraph.addEdge(READ_APPLICATIONS, READ_PAGES);
|
||||
|
||||
lateralGraph.addEdge(MANAGE_PAGES, READ_PAGES);
|
||||
}
|
||||
|
||||
public Set<Policy> getLateralPoliciesForUser(AclPermission permission, User user) {
|
||||
Set<DefaultEdge> lateralEdges = lateralGraph.outgoingEdgesOf(permission);
|
||||
return lateralEdges.stream()
|
||||
.map(lateralEdge -> {
|
||||
AclPermission lateralPermission = lateralGraph.getEdgeTarget(lateralEdge);
|
||||
return Policy.builder().permission(lateralPermission.getValue())
|
||||
.users(Set.of(user.getUsername())).build();
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.authentication.handlers;
|
||||
|
||||
import com.appsmith.server.constants.AclConstants;
|
||||
import com.appsmith.server.acl.AclConstants;
|
||||
import com.appsmith.server.constants.Security;
|
||||
import com.appsmith.server.domains.LoginSource;
|
||||
import com.appsmith.server.domains.User;
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
package com.appsmith.server.constants;
|
||||
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AclComponent<T extends BaseDomain> {
|
||||
|
||||
public String getPermission(Object entity) {
|
||||
System.out.println("In the getPermission");
|
||||
return "read:applications";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.appsmith.server.constants;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum AclPermission {
|
||||
|
||||
// These are generic permissions created to make the transition to the new ACL format easy. They must be removed
|
||||
CREATE("create", null),
|
||||
READ("read", null),
|
||||
UPDATE("update", null),
|
||||
DELETE("delete", null),
|
||||
|
||||
CREATE_ORGANIZATIONS("create:organizations", null),
|
||||
READ_ORGANIZATIONS("read:organizations", null),
|
||||
UPDATE_ORGANIZATIONS("update:organizations", null),
|
||||
DELETE_ORGANIZATIONS("delete:organizations", null),
|
||||
|
||||
MANAGE_APPLICATIONS("manage:applications", null),
|
||||
READ_APPLICATIONS("read:applications", MANAGE_APPLICATIONS),
|
||||
|
||||
CREATE_PAGES("create:pages", null),
|
||||
READ_PAGES("read:pages", CREATE_PAGES),
|
||||
UPDATE_PAGES("update:pages", null),
|
||||
DELETE_PAGES("delete:pages", null);
|
||||
|
||||
|
||||
private String value;
|
||||
private AclPermission parent;
|
||||
|
||||
AclPermission(String value, AclPermission parent) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.repositories;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface AppsmithRepository<T> {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.appsmith.server.repositories;
|
|||
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.external.models.QBaseDomain;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.User;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
|
|
@ -54,13 +54,13 @@ public abstract class BaseAppsmithRepositoryImpl<T extends BaseDomain> {
|
|||
public static final Criteria userAcl(User user, AclPermission permission) {
|
||||
log.debug("Going to add userAcl for user: {} and permission: {}", user.getUsername(), permission.getValue());
|
||||
|
||||
Criteria userCriteria = Criteria.where("policies")
|
||||
Criteria userCriteria = Criteria.where(fieldName(QBaseDomain.baseDomain.policies))
|
||||
.elemMatch(Criteria.where("users").all(user.getUsername())
|
||||
.and("permission").is(permission.getValue())
|
||||
);
|
||||
log.debug("Got the userCriteria: {}", userCriteria.getCriteriaObject());
|
||||
|
||||
Criteria groupCriteria = Criteria.where("policies")
|
||||
Criteria groupCriteria = Criteria.where(fieldName(QBaseDomain.baseDomain.policies))
|
||||
.elemMatch(Criteria.where("groups").all(user.getGroupIds())
|
||||
.and("permission").is(permission.getValue()));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.appsmith.server.repositories;
|
||||
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.User;
|
||||
import lombok.NonNull;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.repositories;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.repositories;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.QApplication;
|
||||
import com.appsmith.server.domains.User;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
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.Application;
|
||||
|
|
@ -13,15 +14,20 @@ import com.appsmith.server.domains.User;
|
|||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jgrapht.graph.DefaultEdge;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ApplicationPageServiceImpl implements ApplicationPageService {
|
||||
|
|
@ -31,17 +37,20 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
|
|||
private final OrganizationService organizationService;
|
||||
|
||||
private final AnalyticsService analyticsService;
|
||||
private final PolicyGenerator policyGenerator;
|
||||
|
||||
public ApplicationPageServiceImpl(ApplicationService applicationService,
|
||||
PageService pageService,
|
||||
SessionUserService sessionUserService,
|
||||
OrganizationService organizationService,
|
||||
AnalyticsService analyticsService) {
|
||||
AnalyticsService analyticsService,
|
||||
PolicyGenerator policyGenerator) {
|
||||
this.applicationService = applicationService;
|
||||
this.pageService = pageService;
|
||||
this.sessionUserService = sessionUserService;
|
||||
this.organizationService = organizationService;
|
||||
this.analyticsService = analyticsService;
|
||||
this.policyGenerator = policyGenerator;
|
||||
}
|
||||
|
||||
public Mono<Page> createPage(Page page) {
|
||||
|
|
@ -62,7 +71,7 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
|
|||
page.setLayouts(layoutList);
|
||||
}
|
||||
|
||||
Mono<Application> applicationMono = applicationService.findById(page.getApplicationId(), AclPermission.CREATE_PAGES)
|
||||
Mono<Application> applicationMono = applicationService.findById(page.getApplicationId(), AclPermission.MANAGE_PAGES)
|
||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION_ID, page.getApplicationId())));
|
||||
|
||||
return applicationMono
|
||||
|
|
@ -199,7 +208,7 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
|
|||
.flatMap(user -> {
|
||||
String orgId = user.getCurrentOrganizationId();
|
||||
|
||||
Mono<Organization> orgMono = organizationService.findById(orgId, AclPermission.MANAGE_APPLICATIONS)
|
||||
Mono<Organization> orgMono = organizationService.findById(orgId, ORGANIZATION_MANAGE_APPLICATIONS)
|
||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ORGANIZATION, orgId)));
|
||||
|
||||
return orgMono.map(org -> {
|
||||
|
|
@ -208,18 +217,36 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
|
|||
// to the new application that we are creating.
|
||||
Set<Policy> policySet = org.getPolicies().stream()
|
||||
.filter(policy ->
|
||||
policy.getPermission().equals(AclPermission.READ_APPLICATIONS.getValue()) ||
|
||||
policy.getPermission().equals(AclPermission.MANAGE_APPLICATIONS.getValue())
|
||||
policy.getPermission().equals(ORGANIZATION_MANAGE_APPLICATIONS.getValue()) ||
|
||||
policy.getPermission().equals(ORGANIZATION_READ_APPLICATIONS.getValue())
|
||||
).collect(Collectors.toSet());
|
||||
Set<String> users = policySet.stream()
|
||||
.map(policy -> policy.getUsers())
|
||||
|
||||
Set<Policy> documentPolicies = policySet.stream()
|
||||
.map(policy -> {
|
||||
AclPermission aclPermission = AclPermission
|
||||
.getPermissionByValue(policy.getPermission(), Organization.class);
|
||||
|
||||
// Check the hierarchy graph to derive child permissions that must be given to this
|
||||
// document
|
||||
Set<Policy> childPolicySet = new HashSet<>();
|
||||
Set<DefaultEdge> edges = policyGenerator.getHierarchyGraph()
|
||||
.outgoingEdgesOf(aclPermission);
|
||||
for (DefaultEdge edge: edges) {
|
||||
AclPermission childPermission = policyGenerator.getHierarchyGraph().getEdgeTarget(edge);
|
||||
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
|
||||
// current user gets these permissions
|
||||
childPolicySet.addAll(policyGenerator.getLateralPoliciesForUser(childPermission, user));
|
||||
}
|
||||
childPolicySet.addAll(policyGenerator.getLateralPoliciesForUser(aclPermission, user));
|
||||
return childPolicySet;
|
||||
})
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
policySet.add(Policy.builder()
|
||||
.permission(AclPermission.CREATE_PAGES.getValue())
|
||||
.users(Set.of(user.getUsername())).build()
|
||||
);
|
||||
application.setPolicies(policySet);
|
||||
application.setPolicies(documentPolicies);
|
||||
return application;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.constants.AnalyticsEvents;
|
||||
import com.appsmith.server.constants.Entity;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
|
|
@ -13,15 +13,11 @@ import com.appsmith.server.exceptions.AppsmithException;
|
|||
import com.appsmith.server.repositories.ActionRepository;
|
||||
import com.appsmith.server.repositories.ApplicationRepository;
|
||||
import com.appsmith.server.repositories.PageRepository;
|
||||
import com.mongodb.DBObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
|
@ -31,7 +27,6 @@ import reactor.core.scheduler.Scheduler;
|
|||
import javax.validation.Validator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Slf4j
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.constants.AnalyticsEvents;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.AclConstants;
|
||||
import com.appsmith.server.acl.AclConstants;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Group;
|
||||
import com.appsmith.server.repositories.GroupRepository;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.User;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
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.Organization;
|
||||
|
|
@ -15,6 +16,7 @@ import com.appsmith.server.exceptions.AppsmithException;
|
|||
import com.appsmith.server.repositories.OrganizationRepository;
|
||||
import com.appsmith.server.repositories.PluginRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jgrapht.graph.DefaultEdge;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
|
|
@ -26,11 +28,14 @@ import reactor.core.scheduler.Scheduler;
|
|||
|
||||
import javax.validation.Validator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ORGANIZATIONS;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OrganizationServiceImpl extends BaseService<OrganizationRepository, Organization, String> implements OrganizationService {
|
||||
|
|
@ -41,6 +46,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
|
|||
private final PluginRepository pluginRepository;
|
||||
private final SessionUserService sessionUserService;
|
||||
private final UserOrganizationService userOrganizationService;
|
||||
private final PolicyGenerator policyGenerator;
|
||||
|
||||
@Autowired
|
||||
public OrganizationServiceImpl(Scheduler scheduler,
|
||||
|
|
@ -53,7 +59,8 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
|
|||
GroupService groupService,
|
||||
PluginRepository pluginRepository,
|
||||
SessionUserService sessionUserService,
|
||||
UserOrganizationService userOrganizationService) {
|
||||
UserOrganizationService userOrganizationService,
|
||||
PolicyGenerator policyGenerator) {
|
||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
||||
this.repository = repository;
|
||||
this.settingService = settingService;
|
||||
|
|
@ -61,6 +68,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
|
|||
this.pluginRepository = pluginRepository;
|
||||
this.sessionUserService = sessionUserService;
|
||||
this.userOrganizationService = userOrganizationService;
|
||||
this.policyGenerator = policyGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -81,41 +89,62 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
|
|||
return repository.findByName(name);
|
||||
}
|
||||
|
||||
private Set<Policy> crudAppPolicy(User user) {
|
||||
Policy readAppPolicy = Policy.builder().permission(AclPermission.READ_APPLICATIONS.getValue())
|
||||
.users(Set.of(user.getUsername()))
|
||||
.build();
|
||||
|
||||
Policy manageAppPolicy = Policy.builder().permission(AclPermission.MANAGE_APPLICATIONS.getValue())
|
||||
.users(Set.of(user.getUsername()))
|
||||
.build();
|
||||
|
||||
return Set.of(manageAppPolicy, readAppPolicy);
|
||||
}
|
||||
// private Set<Policy> crudAppPolicy(User user) {
|
||||
// Policy readAppPolicy = Policy.builder().permission(AclPermission.READ_APPLICATIONS.getValue())
|
||||
// .users(Set.of(user.getUsername()))
|
||||
// .build();
|
||||
//
|
||||
// Policy manageAppPolicy = Policy.builder().permission(AclPermission.MANAGE_APPLICATIONS.getValue())
|
||||
// .users(Set.of(user.getUsername()))
|
||||
// .build();
|
||||
//
|
||||
// return Set.of(manageAppPolicy, readAppPolicy);
|
||||
// }
|
||||
|
||||
private Set<Policy> crudOrgPolicy(User user) {
|
||||
Policy readOrgPolicy = Policy.builder().permission(AclPermission.READ_ORGANIZATIONS.getValue())
|
||||
.users(Set.of(user.getUsername()))
|
||||
.build();
|
||||
Set<Policy> policySet = user.getPolicies().stream()
|
||||
.filter(policy ->
|
||||
policy.getPermission().equals(MANAGE_ORGANIZATIONS.getValue())
|
||||
).collect(Collectors.toSet());
|
||||
|
||||
Policy updateOrgPolicy = Policy.builder().permission(AclPermission.UPDATE_ORGANIZATIONS.getValue())
|
||||
.users(Set.of(user.getUsername()))
|
||||
.build();
|
||||
Set<Policy> documentPolicies = policySet.stream()
|
||||
.map(policy -> {
|
||||
AclPermission aclPermission = AclPermission
|
||||
.getPermissionByValue(policy.getPermission(), Organization.class);
|
||||
// Check the hierarchy graph to derive child permissions that must be given to this
|
||||
// document
|
||||
Set<Policy> childPolicySet = new HashSet<>();
|
||||
Set<DefaultEdge> edges = policyGenerator.getHierarchyGraph()
|
||||
.outgoingEdgesOf(aclPermission);
|
||||
for (DefaultEdge edge : edges) {
|
||||
AclPermission childPermission = policyGenerator.getHierarchyGraph().getEdgeTarget(edge);
|
||||
childPolicySet.add(Policy.builder().permission(childPermission.getValue())
|
||||
.users(policy.getUsers()).build());
|
||||
|
||||
Policy deleteOrgPolicy = Policy.builder().permission(AclPermission.DELETE_ORGANIZATIONS.getValue())
|
||||
// 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
|
||||
// current user gets these permissions
|
||||
childPolicySet.addAll(policyGenerator.getLateralPoliciesForUser(childPermission, user));
|
||||
}
|
||||
childPolicySet.addAll(policyGenerator.getLateralPoliciesForUser(aclPermission, user));
|
||||
return childPolicySet;
|
||||
}).flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Policy manageOrgPolicy = Policy.builder().permission(MANAGE_ORGANIZATIONS.getValue())
|
||||
.users(Set.of(user.getUsername()))
|
||||
.build();
|
||||
return Set.of(readOrgPolicy, updateOrgPolicy,deleteOrgPolicy);
|
||||
documentPolicies.add(manageOrgPolicy);
|
||||
return documentPolicies;
|
||||
}
|
||||
|
||||
private Set<Policy> adminPoliciesForOrganization(User user) {
|
||||
|
||||
Set<Policy> crudAppPolicies = crudAppPolicy(user);
|
||||
Set<Policy> crudOrgPolicies = crudOrgPolicy(user);
|
||||
|
||||
Set<Policy> adminPolicies = new HashSet<>();
|
||||
adminPolicies.addAll(crudOrgPolicies);
|
||||
adminPolicies.addAll(crudAppPolicies);
|
||||
|
||||
return adminPolicies;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +167,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
|
|||
}
|
||||
|
||||
// Set the admin policies for this organization & user
|
||||
organization.setPolicies(adminPoliciesForOrganization(user));
|
||||
organization.setPolicies(crudOrgPolicy(user));
|
||||
|
||||
Mono<Organization> organizationMono = Mono.just(organization)
|
||||
.flatMap(this::validateObject)
|
||||
|
|
@ -219,7 +248,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
|
|||
|
||||
@Override
|
||||
public Mono<Organization> update(String id, Organization resource) {
|
||||
return repository.updateById(id, resource, AclPermission.UPDATE_ORGANIZATIONS)
|
||||
return repository.updateById(id, resource, MANAGE_ORGANIZATIONS)
|
||||
.flatMap(updatedObj -> analyticsService.sendEvent(AnalyticsEvents.UPDATE + "_" + updatedObj.getClass().getSimpleName().toUpperCase(), updatedObj));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.AclConstants;
|
||||
import com.appsmith.server.acl.AclConstants;
|
||||
import com.appsmith.server.domains.Group;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.User;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.User;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.appsmith.server.configurations;
|
||||
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.OrganizationPlugin;
|
||||
|
|
@ -23,9 +23,14 @@ import reactor.core.publisher.Flux;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class SeedMongoData {
|
||||
|
|
@ -38,20 +43,24 @@ public class SeedMongoData {
|
|||
PluginRepository pluginRepository) {
|
||||
|
||||
log.info("Seeding the data");
|
||||
Policy readAppPolicy = Policy.builder().permission(AclPermission.READ_APPLICATIONS.getValue())
|
||||
Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue())
|
||||
.users(Set.of("api_user"))
|
||||
.build();
|
||||
|
||||
Policy manageAppPolicy = Policy.builder().permission(AclPermission.MANAGE_APPLICATIONS.getValue())
|
||||
Policy manageAppPolicy = Policy.builder().permission(ORGANIZATION_MANAGE_APPLICATIONS.getValue())
|
||||
.users(Set.of("api_user"))
|
||||
.build();
|
||||
|
||||
Policy manageOrgPolicy = Policy.builder().permission(MANAGE_ORGANIZATIONS.getValue())
|
||||
.users(Set.of("api_user"))
|
||||
.build();
|
||||
|
||||
Object[][] userData = {
|
||||
{"user test", "usertest@usertest.com", UserState.ACTIVATED},
|
||||
{"api_user", "api_user", UserState.ACTIVATED},
|
||||
{"user test", "usertest@usertest.com", UserState.ACTIVATED, new HashSet<>()},
|
||||
{"api_user", "api_user", UserState.ACTIVATED, Set.of(manageOrgPolicy)},
|
||||
};
|
||||
Object[][] orgData = {
|
||||
{"Spring Test Organization", "appsmith-spring-test.com", "appsmith.com", Set.of(readAppPolicy, manageAppPolicy)}
|
||||
{"Spring Test Organization", "appsmith-spring-test.com", "appsmith.com", Set.of(manageAppPolicy)}
|
||||
};
|
||||
|
||||
Object[][] appData = {
|
||||
|
|
@ -107,6 +116,7 @@ public class SeedMongoData {
|
|||
user.setName((String) array[0]);
|
||||
user.setEmail((String) array[1]);
|
||||
user.setState((UserState) array[2]);
|
||||
user.setPolicies((Set<Policy>) array[3]);
|
||||
user.setCurrentOrganizationId(orgId);
|
||||
return user;
|
||||
})
|
||||
|
|
@ -122,7 +132,7 @@ public class SeedMongoData {
|
|||
return app;
|
||||
}).flatMap(applicationRepository::save)
|
||||
// Query the seed data to get the applicationId (required for page creation)
|
||||
).then(applicationRepository.findByName((String) appData[0][0], AclPermission.READ_APPLICATIONS))
|
||||
).then(applicationRepository.findByName((String) appData[0][0], READ_APPLICATIONS))
|
||||
.map(application -> application.getId())
|
||||
.flatMapMany(appId -> Flux.just(pageData)
|
||||
// Seed the page data into the DB
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
|
|
@ -15,6 +16,11 @@ import org.springframework.test.context.junit4.SpringRunner;
|
|||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
|
|
@ -49,6 +55,13 @@ public class ApplicationServiceTest {
|
|||
testApplication.setName("ApplicationServiceTest TestApp");
|
||||
Mono<Application> applicationMono = applicationPageService.createApplication(testApplication);
|
||||
|
||||
Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue())
|
||||
.users(Set.of("api_user"))
|
||||
.build();
|
||||
Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue())
|
||||
.users(Set.of("api_user"))
|
||||
.build();
|
||||
|
||||
StepVerifier
|
||||
.create(applicationMono)
|
||||
.assertNext(application -> {
|
||||
|
|
@ -56,6 +69,7 @@ public class ApplicationServiceTest {
|
|||
assertThat(application.getId()).isNotNull();
|
||||
assertThat(application.getName().equals("ApplicationServiceTest TestApp"));
|
||||
assertThat(application.getPolicies()).isNotEmpty();
|
||||
assertThat(application.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy));
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.external.models.Policy;
|
||||
import com.appsmith.server.constants.AclPermission;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
|
|
@ -20,6 +19,8 @@ import reactor.test.StepVerifier;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
|
|
@ -66,12 +67,17 @@ public class OrganizationServiceTest {
|
|||
@Test
|
||||
@WithUserDetails(value = "api_user")
|
||||
public void validCreateOrganizationTest() {
|
||||
Policy manageOrgAppPolicy = Policy.builder().permission(ORGANIZATION_MANAGE_APPLICATIONS.getValue())
|
||||
.users(Set.of("api_user"))
|
||||
.build();
|
||||
|
||||
Mono<Organization> organizationResponse = organizationService.create(organization)
|
||||
.switchIfEmpty(Mono.error(new Exception("create is returning empty!!")));
|
||||
StepVerifier.create(organizationResponse)
|
||||
.assertNext(organization1 -> {
|
||||
assertThat(organization1.getName()).isEqualTo("Test Name");
|
||||
assertThat(organization1.getPolicies()).isNotEmpty();
|
||||
assertThat(organization1.getPolicies()).containsAll(Set.of(manageOrgAppPolicy));
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user