espinchi
espinchi

Reputation: 9212

Protect access to Java methods depending on custom conditions

In short: I want to allow/forbid the execution of Java methods depending on certain conditions. What is the best available solution/mechanism/technique?

Long question, via an (admittedly dumb) example:

Say I have several methods in a vehicle controller, like void openWindow(Window w), void openRoof(), void keepSpeed(double speedKmh). If it rains, the openRoof should not be called, and we want to put in place a mechanism to ensure it. Similarly, if the speed is below 60 km/h, keepSpeed is forbidden, like openWindow if it rains a lot or if the speed if above 100 km/h.

Since accessing the rain/speed sensors requires several lines of code, and these conditions are used everywhere, I don't want to use use assertions or conditions in the body of the methods, but I'd rather make them easily used by domain developers. Besides, I'd like the security concern to be separated from the actual logic of opening the windows, etc. More complex, custom conditions should be easy to configure too.

For instance, I'd like to have this:

@ForbidIf('default:rain') openWindow();
@ForbidIf('default:speedBelow(60)') keepSpeed();
@ForbidIf('default:speedAbove(100)', 'custom:rainsALot') openWindow();

If it helps, this application is a Spring-powered client-server application.

Upvotes: 2

Views: 367

Answers (5)

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 298828

You can use a simple Spring AOP aspect, something like this (untested):

@Aspect
public class SecurityAspect{

    @Pointcut("execution(@ForbidIf * *(*))")
    public void annotatedMethodCalled(){}

    @Before("annotatedMethodCalled() && @target(annotation) && target(bean)")
    public void beforeRestrictedMethodCall(
        final ForbidIf annotation, final Object bean){
        final Expression expression =
            new SpelExpressionParser().parseExpression(annotation.value());
        if(!Boolean.TRUE.equals(expression.getValue(bean))){
            throw new IllegalArgumentException();
        }
    }

}

Upvotes: 2

user638455
user638455

Reputation:

Do you need AOP here? the Decorator pattern can be a lot simpler and requires a lot less magic. Just wrap the implementation with:

class ProtectedWindowOpener implements WindowOpener {

   WindowOpener delegate ...

    void openWindow(Window w) {
        if (!allowOpen(w)) {
          throw new PermissionDeniedException...
        }
        delegate.openWindow(w);
    }

    boolean allowOpen(Window w) {
      // security logic here

    }
}

Which keeps the security logic separate and also has the advantage of not embedding code in a string, which means eclipse can do its business.

Upvotes: 0

cthulhu
cthulhu

Reputation: 3169

OP, commenting to Andreas_D's answer: "If the execution is forbidden, I want a runtime exception to be raised. In this case, if the condition is false, it means this method should have never been called under the current circumstances."

What's wrong with:

public void openWindow() {
    if (itsRaining()) {
        throw new IllegalStateException("Window should not open if it's raining");
    }
}

? I mean, doing an annotation is effectively the same thing, only more complex (especially if your conditions for the method grow in complexity, for example if the window should not be opened if it's raining, snowing, vehicle speed > 100 KPh, wind speed > 6, temperature below freezing, etc).

Of course, I could also just be missing the point. Just wanted to give the hint that overthinking, -engineering or -complicating the problem aren't the only paths to take. Don't want to say that using AOP or annotations are overthinking/engineering/complication a problem, of course. Probably basing this post on the simplified example, too.

Upvotes: 1

axtavt
axtavt

Reputation: 242686

Something similar is implemented in Spring Security as expression based access control, but I think it's not suitable in your case.

However, it should be easy to implement similar functionality from scratch by creating an aspect that can evaluate SpEL expressions against a certain context.

Upvotes: 1

Andreas Dolk
Andreas Dolk

Reputation: 114757

This is an interesting idea, although I see some real problems. You may annotate a method and refer to some environment settings. But if execution is forbidden, what should be done instead?

Consider this example:

 @ForbidIf('default:durationIs(0))') double getSpeed(double distance);

and use it like

 double speed = getSpeed(distance);   // the duration is set globally in this example

What should happen if the duration value is set to 0 - what should be assigned to speed in that case? Or do you want to raise a runtime exception?

In your case, we already have two ways to implement conditional execution:

 // check before calling
 if (isSaveToOpenWindow())
   openWindow();

and

 public void openWindow() {
    if (!isSaveToOpenWindow())
      return;

    // open window
 }

Upvotes: 0

Related Questions