Spring Security and super class - spring

fIn my app I'm using the Spring Security and have defined next classes.
public abstract class AbstractService {
public void save(){
.....
}
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS)
publist class UserService extends AbstractService {
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS_X)
publist class XService extends AbstractService{
}
I need #PreAuthorize annotation from the child class to be applied to the super class methods( for example: save()).Is there any way to achieve it by avoiding to override super class methods?
AbstractService will have more than one child( > 10) wherein each have own #PreAuthorize value.

You can try to use SPEL for that.
Because AFAIK, you must annotate methods or the superclass or the superclass itself, and the annotation must be a plain string (or a static final which is the same). But the string may contain SPEL expressions that will reference the target object. Example if only roles were used :
#PreAuthorize("hasAnyRole(#root.target.requiredRoles)")
public abstract class AbstractService {
public abstract String getRequiredRoles();
public void save(){
.....
}
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS)
publix class UserService extends AbstractService {
#Override
public String getRequiredRoles() {
return "ROLE_USER, ROLE_CLIENT";
}
....
}
As the condition is evaluated by SPEL at runtime, it will use the overriden getter and the list of roles can be defined in child class.
Without knowing what are your requirements for authorization expressions, I cannot be sure if that will do the trick, but I successfully use that for caching methods in a superclass, with keys depending on values in child classes.

Related

Generic type reverse lookup with Spring Boot #Service autowirings

Spring Boot & Java 11 here. I have an abstract base class:
public abstract class AbstractBurninator {
// ...
}
And some subclasses, such as:
public class FizzBurninator extends AbstractBurninator {}
public class BuzzBurninator extends AbstractBurninator {}
public class FoobazBurninator extends AbstractBurninator {}
But there are many more subclasses of it besides those three. I also have an interface:
public interface DoesThings<B extends AbstractBurninator> {
void doAllTheThings(B burninator, String payload);
}
So each implementation of the interface must specify the AbstractBurninator subclass it operates on. Hence I have:
public class DoesFizzThings implements DoesThings<FizzBurninator> {}
public class DoesBuzzThings implements DoesThings<BuzzBurninator> {}
public class DoesFoobazThings imlpements DoesThings<FoobazBurninator> {}
etc.
I now have a Spring Boot service (annotated with #Service) that gets autowired with a list of all List<DoesThings>. Inside that service I have a method that will infer (from certain logic) and instantiate an AbstractBurninator subclass, and it then needs to look up the DoesThings implementation associated with it. Hence if it infers an instance of FizzBurninator, I want it to select the DoesFizzThings instance from the autowired list:
#Service
public class BurninationService {
#Autowired
private List<DoesThings> thingDoers;
public void hahaha(Whistlefeather wf) {
// use 'wf' and other stateful data to infer a subclassed instance of 'AbstractBurninator':
AbstractBurninator burninator = inferSomehow();
// TODO: how to figure out which item of 'thingDoers' matches 'burninator'?
}
What's an easy and elegant way of doing this TBD lookup? I could inject a map instead:
private Map<AbstractBurninator,DoesThings> thingDoers;
But that seems unnecessary since each DoesThing has 1-and-only-1 corresponding AbstractBurninator. Any ideas? It's possible this can be done with straight Java generics, but I'm guessing Spring has some nifty utility that can help here.
If you are comfortable with wiring your Spring context into your service, you could do something like this (inspired by this SO accepted answer)
private <T extends AbstractBurninator> DoesThings<T> getSomeBurn(Class<T> clazz) {
String[] arr = ctx.getBeanNamesForType(ResolvableType.forClassWithGenerics(DoesThings.class, clazz));
if (arr.length == 1) {
return (DoesThings<T>) ctx.getBean(arr[0]);
} else {
throw new IllegalArgumentException("No burninator found");
}
}
This comes with a beautiful "unchecked cast" warning. Also, in my experience, wiring the application context indicates a design problem and definitely complicates testing.

Is there a way for #SpyBean to create spies for all types based on the interface type in a Spring Boot test?

I have a Spring Boot application where I would like to ensure that a list of decorators are verified to be executed. These decorators all extend from the same Abstract class, which in turn extend from the same interface, and they are autowired into a service class as a list of decorators. I would have thought that providing the #SpyBean(MyDecorator.class) at the class level of the test would have done the trick, but I got the error specifying that the decorator is not a spy. It looks like the MockitoPostProcessor class expects that we provide the individual concrete classes in the annotation as so #SpyBean(classes = {decorator1.class,decorator2.class}). I tried the latter, and it worked.
However, the issue that I have with this is that we have to add to this list every time we create a new decorator, which is not ideal. This is why I thought it makes sense to have the interface type be checked as well. Please let me know if there is a better way of doing this, or if I missed something. A thought that crossed my mind was to define my own post processor to wrap any bean from a defined type in a mockito spy, but I would like to check here first. Here is a skeleton definition of the classes to help you understand my dilemma.
MyDecorator.java
public interface MyDecorator{
public void decorate(SomeObject obj);
}
AbstractDecorator.java
public class AbstractDecorator implements MyDecorator{
//common decorator logic
}
Decorator1.java
#Component
public class Decorator1 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
Decorator2.java
#Component
public class Decorator2 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
DecorationService.java
#Service
public class DecorationService implements Service{
#Autowired
private List<MyDecorator> decoratorList;
public void processDecorators(){
//go through list of decorators and process some object
}
}
DecoratorServiceTest.java
#Runwith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
//#SpyBean(MyDecorator.class) //<-- This doesn't wrap the classes in a spy and errors out
#SpyBean(classes = {Decorator1.class, Decorator2.class}) //<-- This works
public class DecoratorServiceTest{
#Autowired
private List<MyDecorator> decoratorList;
#Test
public void testProcessDecorator(){
//verify that each decorator was processed
}
}
I posted a spring boot github issue here. Hopefully we would either see an improvement on it or we get an explanation as to why it is designed in this way.
I have a workaround in place that I'm using which is I've created a class that implements Spring's BeanPostProcessor interface, and I override the postProcessAfterInitialization method, and I check if the class is what I'm expecting, then I would wrap it in a mockito spy. Also, you would need to define the spring bean.
Here is a snippet of the class that I created.
public class SpyBeanPostProcessor<T> implements BeanPostProcessor{
/**
* The class type to spy on.
*/
private Class<T> typeToSpy;
/**
* Construct a SpyBeanPostProcessor with a class type to wrap
* as a {#link org.mockito.Spy}
* #param typeToSpy The class type to spy on.
*/
public SpyBeanPostProcessor(Class<T> typeToSpy) {
this.typeToSpy = typeToSpy;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (typeToSpy.isAssignableFrom(bean.getClass())){
return Mockito.spy(bean);
}else{
return bean;
}
}
}
I also needed to create a new spring bean that loads the BeanPostProcessor as shown below.
#Bean
public static SpyBeanPostProcessor decoratorSpyBeanPostProcessor(){
return new SpyBeanPostProcessor(MyDecorator.class);
}

How can I create a Pointcut or Around for extended classes with shared interface?

I have an abstract service class.
abstract class AbstractService<T> {
public void saveNew(T entity) {
}
}
And I have two more abstract classes extends AbstractService and implement a shared interface.
abstract class MoreAbstractService2<T extends Some2>
extends AbstractService<T>
implements SharedInterface {
}
abstract class MoreAbstractService3<T extends Some3>
extends AbstractService<T>
implements SharedInterface {
}
Now I want to validate the entity argument on these two extending services' saveNew(T) method.
How can I define a #Pointcut and (or) an #Around for following conditions?
extends the AbstractService class
implements the SharedInterface interface
you can use within as following:
within(com.somepackage.Super+)
where com.somepackage.Super is the fully qualified base class name and + means "all subclasses". Other pointcut is
execution(* com.somepackage.Super+.*(..))
R.G's solution has a few disadvantages:
The pointcut matches too many joinpoints.
Thus it needs reflection in order to filter out the unnecessary ones.
I am going to show you a stand-alone AspectJ solution (no Spring, I am not a Spring user), but the aspect would look just the same in Spring, you only need to make it a #Component or declare a #Bean factory in your configuration. But the same applies to all the classes you want to intercept, of course.
Because I prefer a full MCVE with all necessary dependency classes in order for you to be able to copy, compile and run it, and because I also added negative test cases (sub-classes only extending the abstract base class or only implementing the interface), this is a lot of code. So please bear with me:
Abstract classes, interface and helper classes:
package de.scrum_master.app;
public abstract class AbstractService<T> {
public void saveNew(T entity) {
System.out.println("Saving new entity " + entity);
}
}
package de.scrum_master.app;
public class Some2 {}
package de.scrum_master.app;
public class Some3 {}
package de.scrum_master.app;
public abstract class MoreAbstractService2<T extends Some2>
extends AbstractService<T>
implements SharedInterface {}
package de.scrum_master.app;
public abstract class MoreAbstractService3<T extends Some3>
extends AbstractService<T>
implements SharedInterface {}
package de.scrum_master.app;
public interface SharedInterface {
void doSomething();
}
Driver application (AspectJ + POJOs, not Spring):
This driver class contains some static inner classes subclassing the given base classes and/or implementing the shared interface. Two are used for positive tests (should be intercepted), two for negative tests (should not be intercepted). Each class also contains an additional method as another negative test case which should not be matched - better safe than sorry.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
// Should be intercepted
InterceptMe1 interceptMe1 = new InterceptMe1();
interceptMe1.saveNew(new Some2());
interceptMe1.doSomething();
interceptMe1.additional();
printSeparator();
// Should be intercepted
InterceptMe2 interceptMe2 = new InterceptMe2();
interceptMe2.saveNew(new Some3());
interceptMe2.doSomething();
interceptMe2.additional();
printSeparator();
// Should NOT be intercepted
DontInterceptMe1 dontInterceptMe1 = new DontInterceptMe1();
dontInterceptMe1.saveNew(new Some2());
dontInterceptMe1.additional();
printSeparator();
// Should NOT be intercepted
DontInterceptMe2 dontInterceptMe2 = new DontInterceptMe2();
dontInterceptMe2.additional();
printSeparator();
}
private static void printSeparator() {
System.out.println("\n----------------------------------------\n");
}
static class InterceptMe1 extends MoreAbstractService2<Some2> {
#Override
public void doSomething() {
System.out.println("Doing something in MoreAbstractService2<Some2>");
}
public void additional() {
System.out.println("Additional method in MoreAbstractService2<Some2>");
}
}
static class InterceptMe2 extends MoreAbstractService3<Some3> {
#Override
public void doSomething() {
System.out.println("Doing something in MoreAbstractService3<Some3>");
}
public void additional() {
System.out.println("Additional method in MoreAbstractService3<Some3>");
}
}
static class DontInterceptMe1 extends AbstractService<Some2> {
public void additional() {
System.out.println("Additional method in AbstractService<Some2>");
}
}
static class DontInterceptMe2 implements SharedInterface {
#Override
public void doSomething() {
System.out.println("Doing something in SharedInterface"); }
public void additional() {
System.out.println("Additional method in SharedInterface");
}
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class EntityValidationAspect {
#Before(
"execution(* saveNew(*)) && " +
"args(entity) && " +
"target(de.scrum_master.app.SharedInterface) && " +
"target(de.scrum_master.app.AbstractService)"
)
public void validateEntity(JoinPoint thisJoinPoint, Object entity) {
System.out.println("-> Pre-save entity validation: " + entity);
}
}
As you can see, the aspect uses two target() pointcuts which must both match. It also specifically targets any saveNew method with a single argument saveNew(*), binding that argument as an advice method parameter via args().
For demonstration's sake I do not validate anything (I don't know how you want to do that) but just print the entity. Thus, a #Before advice is sufficient. If in case of negative validation you want to throw an exception, this advice type is also okay. If you need to do more, such as manipulate the entity's state or replace it before passing it on to the target method, call an alternative target method instead or none at all, return a specific result (in case of non-void methods, here not applicable), handle exceptions from the target method etc., you ought to use an #Around advice instead.
Console log:
-> Pre-save entity validation: de.scrum_master.app.Some2#28a418fc
Saving new entity de.scrum_master.app.Some2#28a418fc
Doing something in MoreAbstractService2<Some2>
Additional method in MoreAbstractService2<Some2>
----------------------------------------
-> Pre-save entity validation: de.scrum_master.app.Some3#5305068a
Saving new entity de.scrum_master.app.Some3#5305068a
Doing something in MoreAbstractService3<Some3>
Additional method in MoreAbstractService3<Some3>
----------------------------------------
Saving new entity de.scrum_master.app.Some2#1f32e575
Additional method in AbstractService<Some2>
----------------------------------------
Additional method in SharedInterface
----------------------------------------
Et voilĂ  - the aspect does exactly what you asked for, as far as I understand your requirement. :-) More specifically, it does not get triggered in the third case when saveNew is being called, but the class does not implement the interface.
Following code can be used for the validation mentioned.
The point cut is to intercept on the execution of a specific method for the subclasses of AbstractService and the code logic is to only validate if SharedInterface is a superinterface of the target bean.
The use of isAssignableFrom() is required as the interfaces proxied by the AOP proxy does not include SharedInterface. As per my understanding , a pointcut expression to match the second criteria will not be possible for the same reason and hence handled the requirement in the code logic.
Hope this helps
#Aspect
#Component
public class ValidationAspect {
#Pointcut("execution(* package.to.AbstractService+.saveNew(..))")
public void isAbstractServiceType() {
}
#Around("isAbstractServiceType() && args(entity) && target(bean)")
public void validateEntityArugments(ProceedingJoinPoint pjp, Object entity, Object bean) throws Throwable {
if (SharedInterface.class.isAssignableFrom(bean.getClass())) {
System.out.println(entity);
// ..validate
}
pjp.proceed();
}
}

Spring Security: get controller class in Preauthorize annotation

Many of the controllers I use in a Spring application extend a common abstract class. The methods are thus declared in the abstract class.
I would like to apply a 'PreAuthorize' condition in the abstract method, but I need to get the name of the actual controller class being invoked to be passed to the EL evaluator.
Is there a way to do so?
In abstract class implement BeanNameAware interface.
public abstract class MyController implements BeanNameAware{
String beanName;
#Override
public void setBeanName(final String beanName) {
this.beanName = beanName;
}
#Override
public String getBeanName() {
return beanName;
}
}
You will get a hold of actual bean name instead regular proxy. If you need further customization implement BeanFactoryAware. You can than use that getBeanName method in SpEl of your preauthorize

Inject a list of classes that extends an abstract class with Spring

Hy,
I have the next code:
public abstract class MyClass{
protected abstract void method1();
}
Classes that extend the first one:
#Component
public class MyClass1 extends MyClass{
.....
}
#Component
public class MyClass2 extends MyClass{
.....
}
My class where I try to inject list of classes that extends an abstract class
#Component
public class SpringClass{
#Autowired **//It doesnt work, nothing is inyected!**
List<MyClass> classes
}
My problem is it doesnt work, it doesnt inject the list of classes that extend MyClass in property classes. Why?
Thanks
You can remove #Autowired annotation for the instance variable and add it to the setter method. After doing this your spring class would be
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class SpringClass {
List<MyClass> classes;
#Autowired
public void setClasses(List<MyClass> classes) {
this.classes = classes;
}
}
Hope this helps.
#Component
public class SpringClass {
#Autowired
private Map<String, YourInterface> map;
}
String in map will be contain all classes name that implements the YourInterface as String.
if you want to get all instances - use map.values()
if you want to get specific instance - get it by class name.
in additional, you can customize the key by another recognize such as Enum.
in this case - you need to configure this as #Bean
as bellow :
#Configuration
public class CalculationHandlerConfig {
#Bean
public HashMap<OperatorTypeEnum, CalculatorService> CalculationHandlers(Map<String, CalculatorService> beansMap) {
HashMap<OperatorTypeEnum, CalculatorService> map = new HashMap<>();
for (CalculatorService bean : beansMap.values()) {
map.put(bean.getOperatorType(), bean);
}
return map;
}
}
It looks like it could be dependent on the version of Spring being used, however, you may also want to try using an interface that all the desired classes implement, and injecting the list referencing the interface.
In other words, instead of this..
List<AbstractOrConcreteBaseClass>
use...
List<Interface>

Resources