I've got problem that SpEL is evaluating entity argument as null reference in the second method of this repository. This first method works well and id is correctly evaluated to Long as should be.
@NoRepositoryBean
public interface SecuredPagingAndSortingRepository<T extends AuditedEntity, ID extends Serializable>
extends PagingAndSortingRepository<T, ID> {
@Override
@RestResource(exported = false)
@PreAuthorize("hasPermission(#id, null, 'owner')")
void delete(ID id);
@Override
@PreAuthorize("hasPermission(#entity, 'owner')")
void delete(T entity);
}
This is my custom PermissionEvaluator:
@Slf4j
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
private final PermissionResolverFactory permissionResolverFactory;
@Autowired
public CustomPermissionEvaluator(PermissionResolverFactory permissionResolverFactory) {
this.permissionResolverFactory = permissionResolverFactory;
}
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Assert.notNull(userDetails, "User details cannot be null");
Assert.notNull(targetDomainObject, "Target object cannot be null");
log.debug("Permmission: " + permission + " check on: " + targetDomainObject + " for user: " + userDetails.getUsername());
PermissionType permissionType = PermissionType.valueOf(((String) permission).toUpperCase());
return permissionResolverFactory.getPermissionResolver(permissionType).resolve(targetDomainObject.getClass(), authentication, (AuditedEntity) targetDomainObject);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
// TODO
return false;
}
}
This test doesn't pass because of assert that target object cannot be null in CustomPermissionEvaluator.
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
@ContextConfiguration(classes = SqapApiApplication.class)
public class PermissionsIT {
@Autowired
private TestGroupRepository testGroupRepository;
@Autowired
private UserRepository userRepository;
UserEntity user;
@Before
public void before() {
user = new UserEntity("user", "password1", true, Sets.newHashSet(RoleType.ROLE_USER));
user = userRepository.save(user);
}
@Test
@WithMockUser(username="user")
public void shouldDeleteWhenIsOwner() throws Exception {
TestGroupEntity testGroupEntity = new TestGroupEntity("testGroup", "testdesc", Sets.newHashSet(new AbxTestEntity(1, "abx", "desc", null)));
user.addTestGroup(testGroupEntity);
user = userRepository.save(user);
TestGroupEntity createdEntity = testGroupRepository.findAll().iterator().next();
testGroupRepository.delete(createdEntity);
}
}
When referencing method parameters from spel in interfaces it pays to annotate them with Spring Data's
@Param
to explicitly name them:If the parameters aren't annotated Spring has to use reflection to discover the parameter names. This is only possible for interface methods if
-parameters
flag was specifiedFor class methods Spring has another option—it can use debug information. This works in Spring 3 and earlier versions of Java, but again it relies on a compile time flag to work (i.e.
-g
).For portability it's better to annotate all the parameters you need to reference.
Reference: Access Control using @PreAuthorize and @PostAuthorize.