Allowing user to define multiple AclPermissions for a given permission
Also adding proper Acl permissions on ApplicationService and PageService functions
This commit is contained in:
parent
9f3197792a
commit
bd1c390402
|
|
@ -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<GrantedAuthority> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package com.appsmith.server.constants;
|
||||
|
||||
public interface Entity {
|
||||
String APPLICATIONS = "applications";
|
||||
String PAGES = "pages";
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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<Application, String> {
|
||||
|
||||
// @Override
|
||||
// @PreAuthorize("hasPermission('someValue', T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)")
|
||||
// Mono<Application> getById(String id);
|
||||
|
||||
@PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)")
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Application> findById(String id);
|
||||
|
||||
@PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)")
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Application> findByIdAndOrganizationId(String id, String organizationId);
|
||||
|
||||
@PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).READ_APPLICATION_PERMISSION)")
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Application> findByName(String name);
|
||||
|
||||
@PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).PUBLISH_APPLICATION_PERMISSION)")
|
||||
@AclPermission(values = AclConstants.PUBLISH_PERMISSION)
|
||||
Mono<Boolean> publish(String applicationId);
|
||||
|
||||
@PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).CREATE_APPLICATION_PERMISSION)")
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Application> save(Application application);
|
||||
|
||||
@PreAuthorize("hasPermission(#user, T(com.appsmith.server.constants.AclConstants).DELETE_APPLICATION_PERMISSION)")
|
||||
@AclPermission(values = {AclConstants.ARCHIVE_PERMISSION, AclConstants.DELETE_PERMISSION})
|
||||
Mono<Application> archive(Application application);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ApplicationRepository, Application, String> implements ApplicationService {
|
||||
|
||||
private final SessionUserService sessionUserService;
|
||||
|
|
|
|||
|
|
@ -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<T extends BaseDomain, ID> {
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Flux<T> get(MultiValueMap<String, String> params);
|
||||
|
||||
@AclPermission(values = AclConstants.CREATE_PERMISSION)
|
||||
Mono<T> create(T resource);
|
||||
|
||||
@AclPermission(values = AclConstants.UPDATE_PERMISSION)
|
||||
Mono<T> update(ID id, T resource);
|
||||
|
||||
// @PreAuthorize("hasPermission('someValue', @aclComponent.getPermission(#returnObject))")
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<T> getById(ID id);
|
||||
|
||||
@AclPermission(values = AclConstants.DELETE_PERMISSION)
|
||||
Mono<T> delete(ID id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
//public @interface Domain {
|
||||
//}
|
||||
|
|
@ -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<Page, String> {
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Page> findById(String pageId);
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Flux<Page> findByApplicationId(String applicationId);
|
||||
|
||||
@AclPermission(values = {AclConstants.CREATE_PERMISSION, AclConstants.UPDATE_PERMISSION})
|
||||
Mono<Page> save(Page page);
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Page> findByIdAndLayoutsId(String pageId, String layoutId);
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Page> findByName(String name);
|
||||
|
||||
@AclPermission(values = AclConstants.DELETE_PERMISSION)
|
||||
Mono<Void> deleteAll();
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Flux<PageNameIdDTO> findNamesByApplicationId(String applicationId);
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Layout createDefaultLayout();
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Flux<PageNameIdDTO> findNamesByApplicationName(String applicationName);
|
||||
|
||||
@AclPermission(values = AclConstants.READ_PERMISSION)
|
||||
Mono<Page> findByNameAndApplicationId(String name, String applicationId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PageRepository, Page, String> implements PageService {
|
||||
|
||||
private final ApplicationService applicationService;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user