diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/aspects/AclAspect.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/aspects/AclAspect.java new file mode 100644 index 0000000000..36bb7ed26e --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/aspects/AclAspect.java @@ -0,0 +1,71 @@ +package com.appsmith.server.aspects; + +import com.appsmith.server.domains.User; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.services.AclEntity; +import com.appsmith.server.services.AclPermission; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; + +@Aspect +@Component +@Slf4j +public class AclAspect { + + @Around("execution(reactor.core.publisher.Mono+ com.appsmith.server.services.CrudService.*(..))") + public Mono checkAuthorization(ProceedingJoinPoint joinPoint) { + try { + return ReactiveSecurityContextHolder.getContext() + .map(ctx -> ctx.getAuthentication()) + .map(auth -> auth.getPrincipal()) + .filter(principal -> { + User user = (User) principal; + + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + log.debug("Checking the authorization for user: {} for function: {}", user.getEmail(), method.getName()); + + // Get the auth permission to be applied + AclPermission aclPermissionAnnotation = AnnotationUtils.findAnnotation(method, AclPermission.class); + if (aclPermissionAnnotation == null) { + // There are no permission annotations on the method. Continue + return true; + } + String[] authPermission = aclPermissionAnnotation.values(); + + // Get the entity on which permission needs to be applied + String authEntity = joinPoint.getTarget().getClass().getAnnotation(AclEntity.class).value(); + + Set actualPermissions = new HashSet<>(); + if (authPermission != null && authPermission.length > 0) { + for (int i = 0; i < authPermission.length; i++) { + actualPermissions.add(new SimpleGrantedAuthority(authPermission[i] + ":" + authEntity)); + } + } + + log.debug("Permissions required to execute function: {} are: {}", method.getName(), actualPermissions); + return user.getAuthorities().containsAll(actualPermissions); + }) + // If the user is not authorized, the filter will not emit. Hence, we return "Unauthorized" + .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS))) + // The user is authorized to proceed to function execution + .then((Mono) joinPoint.proceed()); + } catch (Throwable throwable) { + return Mono.error(throwable); + } + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/aspects/ContextAspect.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/aspects/ContextAspect.java deleted file mode 100644 index 89121d1f63..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/aspects/ContextAspect.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.appsmith.server.aspects; - -import com.appsmith.server.domains.User; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; -import org.springframework.stereotype.Component; -import reactor.core.publisher.Mono; - -@Aspect -@Component -@Slf4j -public class ContextAspect { - -// @Around("execution(reactor.core.publisher.Mono+ com.appsmith.server.services.CrudService+") - @Around("execution(reactor.core.publisher.Mono+ com.appsmith.server.services.CrudService.*(..))") - public Mono addAuthorization(ProceedingJoinPoint joinPoint) { - try { - log.debug("In the custom aspect"); - return ReactiveSecurityContextHolder.getContext() - .map(ctx -> ctx.getAuthentication()) - .map(auth -> auth.getPrincipal()) - .map(principal -> { - User user = (User) principal; - log.debug("{}", user.getAuthorities()); - if(user.getAuthorities().contains(new SimpleGrantedAuthority("read:applications"))) { - log.debug("Got the permission"); - } - return principal; - }) - .then((Mono) joinPoint.proceed()); -// return Mono.just(true); -// return ((Mono) joinPoint.proceed()); -// .subscriberContext(Context.of(UserRepository.CONTEXT_CLIENT_KEY, getClient())); - } catch (Throwable throwable) { - return Mono.error(throwable); - } - } -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java index 783d855c05..460a59afec 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java @@ -136,13 +136,4 @@ public class SecurityConfig { .logoutSuccessHandler(new LogoutSuccessHandler(objectMapper)) .and().build(); } - - @Bean - public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() { - DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); - expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator()); - - return expressionHandler; - } - } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AclConstants.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AclConstants.java index 22fbc34409..f7321d67d9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AclConstants.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AclConstants.java @@ -18,9 +18,11 @@ public interface AclConstants { "read:users" ); - String READ_APPLICATION_PERMISSION = "read:applications"; - String CREATE_APPLICATION_PERMISSION = "create:applications"; - String DELETE_APPLICATION_PERMISSION = "delete:applications"; - String UPDATE_APPLICATION_PERMISSION = "update:applications"; - String PUBLISH_APPLICATION_PERMISSION = "publish:applications"; + String READ_PERMISSION = "read"; + String CREATE_PERMISSION = "create"; + String DELETE_PERMISSION = "delete"; + String UPDATE_PERMISSION = "update"; + String PUBLISH_PERMISSION = "publish"; + String ARCHIVE_PERMISSION = "archive"; } + diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Entity.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Entity.java new file mode 100644 index 0000000000..3e2c6fe1b3 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Entity.java @@ -0,0 +1,6 @@ +package com.appsmith.server.constants; + +public interface Entity { + String APPLICATIONS = "applications"; + String PAGES = "pages"; +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Url.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Url.java index cb82f24ed6..923a3f0864 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Url.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/Url.java @@ -13,8 +13,8 @@ public interface Url { String DATASOURCE_URL = BASE_URL + VERSION + "/datasources"; String ACTION_URL = BASE_URL + VERSION + "/actions"; String USER_URL = BASE_URL + VERSION + "/users"; - String APPLICATION_URL = BASE_URL + VERSION + "/applications"; - String PAGE_URL = BASE_URL + VERSION + "/pages"; + String APPLICATION_URL = BASE_URL + VERSION + "/" + Entity.APPLICATIONS; + String PAGE_URL = BASE_URL + VERSION + "/" + Entity.PAGES; String PROPERTY_URL = BASE_URL + VERSION + "/properties"; String CONFIG_URL = BASE_URL + VERSION + "/configs"; String TEAM_URL = BASE_URL + VERSION + "/teams"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AclEntity.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AclEntity.java new file mode 100644 index 0000000000..172976593a --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AclEntity.java @@ -0,0 +1,16 @@ +package com.appsmith.server.services; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AclEntity { + String value(); +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AclPermission.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AclPermission.java new file mode 100644 index 0000000000..80b30285f9 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AclPermission.java @@ -0,0 +1,18 @@ +package com.appsmith.server.services; + +import org.checkerframework.framework.qual.InheritedAnnotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AclPermission { + String[] values(); +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationService.java index e4790cc579..74af0e1cd0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ApplicationService.java @@ -1,31 +1,26 @@ package com.appsmith.server.services; +import com.appsmith.server.constants.AclConstants; import com.appsmith.server.domains.Application; -import org.springframework.security.access.prepost.PreAuthorize; import reactor.core.publisher.Mono; -//@Domain("applications") public interface ApplicationService extends CrudService { -// @Override -// @PreAuthorize("hasPermission('someValue', T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)") -// Mono getById(String id); - - @PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)") + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findById(String id); - @PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)") + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findByIdAndOrganizationId(String id, String organizationId); - @PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)") + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findByName(String name); - @PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).PUBLISH_APPLICATION_PERMISSION)") + @AclPermission(values = AclConstants.PUBLISH_PERMISSION) Mono publish(String applicationId); - @PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).CREATE_APPLICATION_PERMISSION)") + @AclPermission(values = AclConstants.READ_PERMISSION) Mono save(Application application); - @PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).DELETE_APPLICATION_PERMISSION)") + @AclPermission(values = {AclConstants.ARCHIVE_PERMISSION, AclConstants.DELETE_PERMISSION}) Mono archive(Application 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 58ae93a8f4..605a62dddf 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 @@ -1,5 +1,6 @@ package com.appsmith.server.services; +import com.appsmith.server.constants.Entity; import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Application; import com.appsmith.server.domains.ApplicationPage; @@ -28,6 +29,7 @@ import java.util.List; @Slf4j @Service +@AclEntity(Entity.APPLICATIONS) public class ApplicationServiceImpl extends BaseService implements ApplicationService { private final SessionUserService sessionUserService; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CrudService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CrudService.java index d77657591f..f9f2fa4077 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CrudService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CrudService.java @@ -1,20 +1,25 @@ package com.appsmith.server.services; import com.appsmith.external.models.BaseDomain; +import com.appsmith.server.constants.AclConstants; import org.springframework.util.MultiValueMap; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface CrudService { + @AclPermission(values = AclConstants.READ_PERMISSION) Flux get(MultiValueMap params); + @AclPermission(values = AclConstants.CREATE_PERMISSION) Mono create(T resource); + @AclPermission(values = AclConstants.UPDATE_PERMISSION) Mono update(ID id, T resource); -// @PreAuthorize("hasPermission('someValue', @aclComponent.getPermission(#returnObject))") + @AclPermission(values = AclConstants.READ_PERMISSION) Mono getById(ID id); + @AclPermission(values = AclConstants.DELETE_PERMISSION) Mono delete(ID id); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/Domain.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/Domain.java deleted file mode 100644 index e1d7f8fd43..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/Domain.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.appsmith.server.services; - -//public @interface Domain { -//} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageService.java index a45d636c2b..060ca1a247 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageService.java @@ -1,5 +1,6 @@ package com.appsmith.server.services; +import com.appsmith.server.constants.AclConstants; import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Page; import com.appsmith.server.dtos.PageNameIdDTO; @@ -8,23 +9,33 @@ import reactor.core.publisher.Mono; public interface PageService extends CrudService { + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findById(String pageId); + @AclPermission(values = AclConstants.READ_PERMISSION) Flux findByApplicationId(String applicationId); + @AclPermission(values = {AclConstants.CREATE_PERMISSION, AclConstants.UPDATE_PERMISSION}) Mono save(Page page); + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findByIdAndLayoutsId(String pageId, String layoutId); + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findByName(String name); + @AclPermission(values = AclConstants.DELETE_PERMISSION) Mono deleteAll(); + @AclPermission(values = AclConstants.READ_PERMISSION) Flux findNamesByApplicationId(String applicationId); + @AclPermission(values = AclConstants.READ_PERMISSION) Layout createDefaultLayout(); + @AclPermission(values = AclConstants.READ_PERMISSION) Flux findNamesByApplicationName(String applicationName); + @AclPermission(values = AclConstants.READ_PERMISSION) Mono findByNameAndApplicationId(String name, String applicationId); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageServiceImpl.java index f6048dd19d..9ba448c6aa 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PageServiceImpl.java @@ -1,6 +1,7 @@ package com.appsmith.server.services; import com.appsmith.server.constants.AnalyticsEvents; +import com.appsmith.server.constants.Entity; import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Action; import com.appsmith.server.domains.Application; @@ -27,6 +28,7 @@ import java.util.List; @Service @Slf4j +@AclEntity(Entity.PAGES) public class PageServiceImpl extends BaseService implements PageService { private final ApplicationService applicationService;