Skip to content

Commit

Permalink
Merge pull request #2778 from atmire/w2p-71143_preAuthorize-annotations
Browse files Browse the repository at this point in the history
Pre authorize annotations for subresources
Merging since we have 2 approvals
  • Loading branch information
benbosman committed Jun 18, 2020
2 parents 1896d73 + 559001c commit 124117c
Show file tree
Hide file tree
Showing 43 changed files with 279 additions and 1,059 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
*/
package org.dspace.app.rest.converter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
Expand All @@ -17,6 +19,7 @@
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.app.rest.link.HalLinkFactory;
import org.dspace.app.rest.link.HalLinkService;
Expand All @@ -26,18 +29,22 @@
import org.dspace.app.rest.model.hateoas.HALResource;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.security.DSpacePermissionEvaluator;
import org.dspace.app.rest.security.WebSecurityExpressionEvaluator;
import org.dspace.app.rest.utils.Utils;
import org.dspace.services.RequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -71,6 +78,12 @@ public class ConverterService {
@Autowired
private DSpacePermissionEvaluator dSpacePermissionEvaluator;

@Autowired
private WebSecurityExpressionEvaluator webSecurityExpressionEvaluator;

@Autowired
private RequestService requestService;

/**
* Converts the given model object to a rest object, using the appropriate {@link DSpaceConverter} and
* the given projection.
Expand All @@ -94,8 +107,15 @@ public <M, R> R toRest(M modelObject, Projection projection) {
DSpaceConverter<M, R> converter = requireConverter(modelObject.getClass());
R restObject = converter.convert(transformedModel, projection);
if (restObject instanceof BaseObjectRest) {
if (!dSpacePermissionEvaluator.hasPermission(SecurityContextHolder.getContext().getAuthentication(),
restObject, "READ")) {
BaseObjectRest baseObjectRest = (BaseObjectRest) restObject;
// This section will verify whether the current user has permissions to retrieve the
// rest object. It'll only return the REST object if the permission is granted.
// If permission isn't granted, it'll return null
String preAuthorizeValue = getPreAuthorizeAnnotationForBaseObject(baseObjectRest);
if (!webSecurityExpressionEvaluator
.evaluate(preAuthorizeValue, requestService.getCurrentRequest().getHttpServletRequest(),
requestService.getCurrentRequest().getHttpServletResponse(),
String.valueOf(baseObjectRest.getId()))) {
log.debug("Access denied on " + restObject.getClass() + " with id: " +
((BaseObjectRest) restObject).getId());
return null;
Expand All @@ -107,6 +127,48 @@ public <M, R> R toRest(M modelObject, Projection projection) {
return restObject;
}

private String getPreAuthorizeAnnotationForBaseObject(BaseObjectRest restObject) {
Annotation preAuthorize = getAnnotationForRestObject(restObject);
if (preAuthorize == null) {
preAuthorize = getDefaultFindOnePreAuthorize();

}
return parseAnnotation(preAuthorize);

}

private String parseAnnotation(Annotation preAuthorize) {
if (preAuthorize != null) {
return (String) AnnotationUtils.getValue(preAuthorize);
}
return null;
}

private Annotation getAnnotationForRestObject(BaseObjectRest restObject) {
BaseObjectRest baseObjectRest = restObject;
DSpaceRestRepository repositoryToUse = utils
.getResourceRepositoryByCategoryAndModel(baseObjectRest.getCategory(), baseObjectRest.getType());
Annotation preAuthorize = null;
for (Method m : repositoryToUse.getClass().getMethods()) {
if (StringUtils.equalsIgnoreCase(m.getName(), "findOne")) {
preAuthorize = AnnotationUtils.findAnnotation(m, PreAuthorize.class);
}
}
return preAuthorize;
}

private Annotation getDefaultFindOnePreAuthorize() {
for (Method m : DSpaceRestRepository.class.getMethods()) {
if (StringUtils.equalsIgnoreCase(m.getName(), "findOne")) {
Annotation annotation = AnnotationUtils.findAnnotation(m, PreAuthorize.class);
if (annotation != null) {
return annotation;
}
}
}
return null;
}

/**
* Converts a list of model objects to a page of rest objects using the given {@link Projection}.
*
Expand Down Expand Up @@ -328,19 +390,19 @@ private void initialize() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(EntityModel.class));
Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents(
HALResource.class.getPackage().getName().replaceAll("\\.", "/"));
HALResource.class.getPackage().getName().replaceAll("\\.", "/"));
for (BeanDefinition beanDefinition : beanDefinitions) {
String resourceClassName = beanDefinition.getBeanClassName();
String resourceClassSimpleName = resourceClassName.substring(resourceClassName.lastIndexOf(".") + 1);
String restClassSimpleName = resourceClassSimpleName
.replaceAll("ResourceWrapper$", "RestWrapper")
.replaceAll("Resource$", "Rest");
.replaceAll("ResourceWrapper$", "RestWrapper")
.replaceAll("Resource$", "Rest");
String restClassName = RestModel.class.getPackage().getName() + "." + restClassSimpleName;
try {
Class<? extends RestModel> restClass =
(Class<? extends RestModel>) Class.forName(restClassName);
(Class<? extends RestModel>) Class.forName(restClassName);
Class<HALResource<? extends RestModel>> resourceClass =
(Class<HALResource<? extends RestModel>>) Class.forName(resourceClassName);
(Class<HALResource<? extends RestModel>>) Class.forName(resourceClassName);
Constructor compatibleConstructor = null;
for (Constructor constructor : resourceClass.getDeclaredConstructors()) {
if (constructor.getParameterCount() == 2 && constructor.getParameterTypes()[1] == Utils.class) {
Expand All @@ -354,11 +416,11 @@ private void initialize() {
resourceConstructors.put(restClass, compatibleConstructor);
} else {
log.warn("Skipping registration of resource class " + resourceClassName
+ "; compatible constructor not found");
+ "; compatible constructor not found");
}
} catch (ClassNotFoundException e) {
log.warn("Skipping registration of resource class " + resourceClassName
+ "; rest class not found: " + restClassName);
+ "; rest class not found: " + restClassName);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/**
* This class serves as a REST representation for an entry of external data
*/
public class ExternalSourceEntryRest extends BaseObjectRest<String> {
public class ExternalSourceEntryRest extends RestAddressableModel {

public static final String NAME = "externalSourceEntry";
public static final String PLURAL_NAME = "externalSourceEntries";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public Page<AuthorizationFeatureRest> findAll(Context context, Pageable pageable
return converter.toRestPage(authorizationFeatureService.findAll(), pageable, utils.obtainProjection());
}

@PreAuthorize("hasAuthority('ADMIN')")
@PreAuthorize("permitAll()")
@Override
public AuthorizationFeatureRest findOne(Context context, String id) {
AuthorizationFeature authzFeature = authorizationFeatureService.find(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ public BundleRestRepository(BundleService dsoService) {
this.bundleService = dsoService;
}

@PreAuthorize("hasPermission(#uuid, 'BUNDLE', 'READ')")
public BundleRest findOne(Context context, UUID uuid) {
@PreAuthorize("hasPermission(#id, 'BUNDLE', 'READ')")
public BundleRest findOne(Context context, UUID id) {
Bundle bundle = null;
try {
bundle = bundleService.find(context, uuid);
bundle = bundleService.find(context, id);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class EntityTypeRestRepository extends DSpaceRestRepository<EntityTypeRes
@Autowired
private EntityTypeService entityTypeService;

@Override
@PreAuthorize("permitAll()")
public EntityTypeRest findOne(Context context, Integer integer) {
try {
Expand All @@ -43,6 +44,7 @@ public EntityTypeRest findOne(Context context, Integer integer) {
}
}

@Override
public Page<EntityTypeRest> findAll(Context context, Pageable pageable) {
try {
List<EntityType> entityTypes = entityTypeService.findAll(context);
Expand All @@ -52,6 +54,7 @@ public Page<EntityTypeRest> findAll(Context context, Pageable pageable) {
}
}

@Override
public Class<EntityTypeRest> getDomainClass() {
return EntityTypeRest.class;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;

/**
Expand All @@ -49,6 +50,7 @@ public class TemplateItemRestRepository extends DSpaceRestRepository<TemplateIte
ResourcePatch<Item> resourcePatch;

@Override
@PreAuthorize("permitAll()")
public TemplateItemRest findOne(Context context, UUID uuid) {
Item item = null;
try {
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 124117c

Please sign in to comment.