diff --git a/app/client/src/components/editorComponents/SelectComponent.tsx b/app/client/src/components/editorComponents/SelectComponent.tsx
index 46013203f2..305e26120e 100644
--- a/app/client/src/components/editorComponents/SelectComponent.tsx
+++ b/app/client/src/components/editorComponents/SelectComponent.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { ReactNode } from "react";
import CustomizedDropdown, {
CustomizedDropdownProps,
} from "pages/common/CustomizedDropdown/index";
@@ -8,7 +8,7 @@ type SelectComponentProps = {
value?: string;
onChange?: (value: string) => void;
};
- options?: Array<{ id: string; name: string }>;
+ options?: Array<{ id: string; name: string; content?: ReactNode }>;
placeholder?: string;
size?: "large" | "small";
outline?: boolean;
@@ -22,7 +22,7 @@ export const SelectComponent = (props: SelectComponentProps) => {
options:
props.options &&
props.options.map(option => ({
- content: option.name,
+ content: option.content ? option.content : option.name,
onSelect: () => {
props.input.onChange && props.input.onChange(option.id);
},
diff --git a/app/client/src/pages/organization/InviteUsersFromv2.tsx b/app/client/src/pages/organization/InviteUsersFromv2.tsx
index 9776258611..21e8a79731 100644
--- a/app/client/src/pages/organization/InviteUsersFromv2.tsx
+++ b/app/client/src/pages/organization/InviteUsersFromv2.tsx
@@ -35,6 +35,19 @@ const OrgInviteTitle = styled.div`
padding: 10px 0px;
`;
+const DropDownOption = styled.div`
+ padding: 10px 0;
+`;
+
+const OptionTitle = styled.div`
+ font-weight: bold;
+`;
+
+const OptionDescription = styled.div`
+ padding: 5px 0px;
+ max-width: 250px;
+`;
+
const StyledForm = styled.form`
width: 100%;
background: white;
@@ -158,6 +171,19 @@ const InviteUsersForm = (props: any) => {
fetchCurrentOrg(props.orgId);
}, [props.orgId, fetchUser, fetchAllRoles, fetchCurrentOrg]);
+ const styledRoles = props.roles.map((role: any) => {
+ return {
+ id: role.id,
+ name: role.name,
+ content: (
+
+ {role.name}
+ {role.description}
+
+ ),
+ };
+ });
+
return (
<>
{applicationId && (
@@ -199,7 +225,7 @@ const InviteUsersForm = (props: any) => {
{
return {
id: role[0],
name: role[0],
+ description: role[1],
};
});
});
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AclPermission.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AclPermission.java
index 04fa782436..61237b275a 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AclPermission.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AclPermission.java
@@ -41,10 +41,16 @@ public enum AclPermission {
ORGANIZATION_READ_APPLICATIONS("read:orgApplications", Organization.class),
ORGANIZATION_PUBLISH_APPLICATIONS("publish:orgApplications", Organization.class),
+ // Invitation related permissions
+ ORGANIZATION_INVITE_USERS("inviteUsers:organization", Organization.class),
+
MANAGE_APPLICATIONS("manage:applications", Application.class),
READ_APPLICATIONS("read:applications", Application.class),
PUBLISH_APPLICATIONS("publish:applications", Application.class),
+ // Making an application public permission at Organization level
+ MAKE_PUBLIC_APPLICATIONS("makePublic:applications", Application.class),
+
MANAGE_PAGES("manage:pages", Page.class),
READ_PAGES("read:pages", Page.class),
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java
index 99afb078df..61059a685c 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java
@@ -8,6 +8,7 @@ import java.util.Set;
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.ORGANIZATION_INVITE_USERS;
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;
@@ -19,9 +20,9 @@ public enum AppsmithRole {
APPLICATION_ADMIN("Application Administrator", "", Set.of(MANAGE_APPLICATIONS)),
APPLICATION_VIEWER("Application Viewer", "", Set.of(READ_APPLICATIONS)),
ORGANIZATION_ADMIN("Administrator", "Can edit, view applications and invite other user to organization",
- Set.of(MANAGE_ORGANIZATIONS)),
+ Set.of(MANAGE_ORGANIZATIONS, ORGANIZATION_INVITE_USERS)),
ORGANIZATION_DEVELOPER("Developer", "Can edit and view applications", Set.of(READ_ORGANIZATIONS,
- ORGANIZATION_MANAGE_APPLICATIONS, ORGANIZATION_READ_APPLICATIONS, ORGANIZATION_PUBLISH_APPLICATIONS)),
+ ORGANIZATION_MANAGE_APPLICATIONS, ORGANIZATION_READ_APPLICATIONS, ORGANIZATION_PUBLISH_APPLICATIONS, ORGANIZATION_INVITE_USERS)),
ORGANIZATION_VIEWER("App Viewer", "Can view applications", Set.of(READ_ORGANIZATIONS, ORGANIZATION_READ_APPLICATIONS));
private Set permissions;
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/PolicyGenerator.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/PolicyGenerator.java
index c7f9e918fc..3903eac04a 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/PolicyGenerator.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/PolicyGenerator.java
@@ -20,6 +20,7 @@ import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.EXECUTE_ACTIONS;
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
+import static com.appsmith.server.acl.AclPermission.MAKE_PUBLIC_APPLICATIONS;
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;
@@ -107,6 +108,7 @@ public class PolicyGenerator {
hierarchyGraph.addEdge(ORGANIZATION_MANAGE_APPLICATIONS, MANAGE_APPLICATIONS);
hierarchyGraph.addEdge(ORGANIZATION_READ_APPLICATIONS, READ_APPLICATIONS);
hierarchyGraph.addEdge(ORGANIZATION_PUBLISH_APPLICATIONS, PUBLISH_APPLICATIONS);
+ hierarchyGraph.addEdge(MANAGE_ORGANIZATIONS, MAKE_PUBLIC_APPLICATIONS);
// If the user is being given MANAGE_APPLICATION permission, they must also be given READ_APPLICATION perm
lateralGraph.addEdge(MANAGE_APPLICATIONS, READ_APPLICATIONS);
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/RoleGraph.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/RoleGraph.java
new file mode 100644
index 0000000000..5ce6d06834
--- /dev/null
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/acl/RoleGraph.java
@@ -0,0 +1,55 @@
+package com.appsmith.server.acl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jgrapht.Graph;
+import org.jgrapht.graph.DefaultEdge;
+import org.jgrapht.graph.DirectedMultigraph;
+import org.jgrapht.traverse.BreadthFirstIterator;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.appsmith.server.acl.AppsmithRole.APPLICATION_ADMIN;
+import static com.appsmith.server.acl.AppsmithRole.APPLICATION_VIEWER;
+import static com.appsmith.server.acl.AppsmithRole.ORGANIZATION_ADMIN;
+import static com.appsmith.server.acl.AppsmithRole.ORGANIZATION_DEVELOPER;
+import static com.appsmith.server.acl.AppsmithRole.ORGANIZATION_VIEWER;
+
+@Slf4j
+@Component
+public class RoleGraph {
+ /**
+ * This graph defines the hierarchy of permissions from parent objects
+ */
+ Graph hierarchyGraph = new DirectedMultigraph<>(DefaultEdge.class);
+
+ @PostConstruct
+ public void createPolicyGraph() {
+
+ // Initialization of the hierarchical and lateral graphs by adding all the vertices
+ EnumSet.allOf(AppsmithRole.class)
+ .forEach(role -> {
+ hierarchyGraph.addVertex(role);
+ });
+
+ hierarchyGraph.addEdge(ORGANIZATION_ADMIN, ORGANIZATION_DEVELOPER);
+ hierarchyGraph.addEdge(ORGANIZATION_DEVELOPER, ORGANIZATION_VIEWER);
+ hierarchyGraph.addEdge(APPLICATION_ADMIN, APPLICATION_VIEWER);
+ }
+
+ public Set generateHierarchicalRoles(String roleName) {
+ AppsmithRole role = AppsmithRole.generateAppsmithRoleFromName(roleName);
+
+ Set childrenRoles = new HashSet<>();
+ childrenRoles.add(role);
+ BreadthFirstIterator breadthFirstIterator = new BreadthFirstIterator<>(hierarchyGraph, role);
+ while(breadthFirstIterator.hasNext()) {
+ childrenRoles.add(breadthFirstIterator.next());
+ }
+
+ return childrenRoles;
+ }
+}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/OrganizationController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/OrganizationController.java
index 42058529cf..1b43ef6c8f 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/OrganizationController.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/OrganizationController.java
@@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@@ -35,8 +36,8 @@ public class OrganizationController extends BaseController>> getUserRolesForOrganization() {
- return service.getUserRolesForOrganization()
+ public Mono>> getUserRolesForOrganization(@RequestParam String organizationId) {
+ return service.getUserRolesForOrganization(organizationId)
.map(permissions -> new ResponseDTO<>(HttpStatus.OK.value(), permissions, null));
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
index d27734c659..8bf3a9decf 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
@@ -35,6 +35,7 @@ public enum AppsmithError {
DUPLICATE_KEY(409, 4024, "Duplicate key error"),
USER_ALREADY_EXISTS_SIGNUP(409, 4025, "There is already an account registered with this username {0}. Please sign in."),
UNAUTHORIZED_ACCESS(403, 4025, "Unauthorized access"),
+ ACTION_IS_NOT_AUTHORIZED(403, 4026, "Sorry. You do not have permissions to perform this action"),
INVALID_DATASOURCE_NAME(400, 4026, "Invalid datasource name. Check again."),
NO_RESOURCE_FOUND(404, 4027, "Unable to find {0} with id {1}"),
ACL_NO_RESOURCE_FOUND(404, 4028, "Unable to find {0} with id {1}. Either the asset doesn't exist or you don't have required permissions"),
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java
index 16b58b94f2..688ab8a365 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java
@@ -2,6 +2,7 @@ package com.appsmith.server.migrations;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.Policy;
+import com.appsmith.server.acl.AppsmithRole;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application;
@@ -47,6 +48,8 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.EXECUTE_ACTIONS;
+import static com.appsmith.server.acl.AclPermission.MAKE_PUBLIC_APPLICATIONS;
+import static com.appsmith.server.acl.AclPermission.ORGANIZATION_INVITE_USERS;
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
@@ -577,4 +580,68 @@ public class DatabaseChangelog {
}
}
}
+
+ @ChangeSet(order = "021", id = "invite-and-public-permissions", author = "")
+ public void giveInvitePermissionToOrganizationsAndPublicPermissionsToApplications(MongoTemplate mongoTemplate) {
+ final List organizations = mongoTemplate.find(
+ query(where("userRoles").exists(true)),
+ Organization.class
+ );
+
+ final List applications = mongoTemplate.find(
+ query(where("policies").exists(true)),
+ Application.class
+ );
+
+ for (final Organization organization : organizations) {
+ Set adminUsernames = organization.getUserRoles()
+ .stream()
+ .filter(role -> (role.getRole().equals(AppsmithRole.ORGANIZATION_ADMIN)))
+ .map(role -> role.getUsername())
+ .collect(Collectors.toSet());
+
+ Set developerUsernames = organization.getUserRoles()
+ .stream()
+ .filter(role -> (role.getRole().equals(AppsmithRole.ORGANIZATION_DEVELOPER)))
+ .map(role -> role.getUsername())
+ .collect(Collectors.toSet());
+
+ // All the developers and administrators of the organization should be allowed to get invite permissions
+ Set invitePermissionUsernames = new HashSet<>();
+ invitePermissionUsernames.addAll(developerUsernames);
+ invitePermissionUsernames.addAll(adminUsernames);
+
+ Set policies = organization.getPolicies();
+ if (policies == null) {
+ policies = new HashSet<>();
+ }
+
+ Policy inviteUserPolicy = Policy.builder().permission(ORGANIZATION_INVITE_USERS.getValue())
+ .users(invitePermissionUsernames).build();
+
+ policies.add(inviteUserPolicy);
+ organization.setPolicies(policies);
+ mongoTemplate.save(organization);
+
+ // Update the applications with public view policy for all administrators of the organization
+ Set orgApplications = applications
+ .stream()
+ .filter(application -> application.getOrganizationId().equals(organization.getId()))
+ .collect(Collectors.toSet());
+
+ for (final Application application : orgApplications) {
+ Set applicationPolicies = application.getPolicies();
+ if (applicationPolicies == null) {
+ applicationPolicies = new HashSet<>();
+ }
+
+ Policy newPublicAppPolicy = Policy.builder().permission(MAKE_PUBLIC_APPLICATIONS.getValue())
+ .users(adminUsernames).build();
+ applicationPolicies.add(newPublicAppPolicy);
+ application.setPolicies(applicationPolicies);
+
+ mongoTemplate.save(application);
+ }
+ }
+ }
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationPageServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationPageServiceImpl.java
index 348db488e3..712ecf2abd 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationPageServiceImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationPageServiceImpl.java
@@ -218,15 +218,7 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
return orgMono.map(org -> {
application.setOrganizationId(org.getId());
- // At the organization level, filter out all the application specific policies and apply them
- // to the new application that we are creating.
- Set policySet = org.getPolicies().stream()
- .filter(policy ->
- policy.getPermission().equals(ORGANIZATION_MANAGE_APPLICATIONS.getValue()) ||
- policy.getPermission().equals(ORGANIZATION_READ_APPLICATIONS.getValue())
- ).collect(Collectors.toSet());
-
- Set documentPolicies = policyGenerator.getAllChildPolicies(policySet, Organization.class, Application.class);
+ Set documentPolicies = policyGenerator.getAllChildPolicies(org.getPolicies(), Organization.class, Application.class);
application.setPolicies(documentPolicies);
return application;
});
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationServiceImpl.java
index 278c43e5d2..7231795560 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationServiceImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationServiceImpl.java
@@ -34,7 +34,7 @@ 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.MAKE_PUBLIC_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
@@ -184,7 +184,7 @@ public class ApplicationServiceImpl extends BaseService changeViewAccess(String id, ApplicationAccessDTO applicationAccessDTO) {
return repository
- .findById(id, MANAGE_APPLICATIONS)
+ .findById(id, MAKE_PUBLIC_APPLICATIONS)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.APPLICATION_ID, id)))
.flatMap(application -> {
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationService.java
index 9dff19d668..b682273306 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationService.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/OrganizationService.java
@@ -29,7 +29,7 @@ public interface OrganizationService extends CrudService {
Flux findByIdsIn(Set ids,AclPermission permission);
- Mono