tuplejump / play-yeoman

Play + Yeoman integration sbt and play plugins
Apache License 2.0
237 stars 58 forks source link

How to intercept all calls to /ui (security, Java)? #18

Closed jtammen closed 11 years ago

jtammen commented 11 years ago

I am using play-yeoman (0.5.2) in a Play! 2.1.X-based Java project and my UI app is completely non-public, which means users have to log in before accessing the app under /ui. I am using play-authenticate and Deadbolt 2 for authentication and authorization.

My question: what would be the best way to achieve that, ideally without having to modify the original Yeoman controller?

Obviously, I do have near to no experience in Play! Scala, so I was naively trying to re-use the Yeoman.index action in my Java controller (which would be equipped with the appropriate @Restrict annotation), but that did not work because I did not find a way to transform a Action<AnyContent> to a play.mvc.Result ...

Thanks for any hints!

milliondreams commented 11 years ago

We generally use "Global" to intercept requests. I am not very familiar with the Play-Java way of handling it... You can try this, http://www.playframework.com/documentation/2.1.x/JavaInterceptors

I haven't used play-authenticate, so am not sure, But from a quick look at the sample, you should be able to intercept any request in Global and check for a property that indicates if the user is authenticated or not.

jtammen commented 11 years ago

Hey Rohit, thanks for that hint! But using GlobalSettings.onRouteRequest(RequestHeader) to do this check would actually result in intercepting and inspecting every request to my application, right? Also I am not quite sure how to actually implement that method: it (optionally) returns a play.api.mvc.Handler which is a Scala trait...

Meanwhile, I am thinking of forking your module and providing a Java implementation of the Yeoman controller; then I could use Java actions composition to add my auth check. Although I fear that this would not solve my problem completely: Yeoman delegates to Play's controllers.Assets, which I would again not be able to use in my Java controller...

Other ideas are welcome! (-:

milliondreams commented 11 years ago

I think the problem here is the Play scala uses play.api.mvc.* in contrast to Java using play.mvc.*

Probably, we will need to replicate the Yeoman api using play.mvc.* to make this work in Java. Sadly I don't have much exposure to Java side of things in Play.

Will be great if you can fork and build Java thing, I will try to assist and contribute in anyway possible. Will be happy to merge if you contribute it here :)

jtammen commented 11 years ago

I would definitely contribute this, but atm I am not yet sure how to solve this problem–and whether forking and replicating the Yeoman API in Java would be the right thing to do. As I mentioned in my previous comment, that would also lead to the problem that we'd have to replicate Play's Assets controller in Java as well, because it cannot be used from a Java controller, as far as I have investigated.

The other option I am still thinking about is just adding a very simple Scala wrapper (in my applicaiton, not in play-yeoman) for the Yeoman API that then does the authentication check, maybe even by using deadbolt2-scala. Do you think it would be possible using Scala actions composition?

Another solution could be sth like this, using Global.onRouteRequest() as you have already suggested:

@Override
public Handler onRouteRequest(final RequestHeader request) {
    final boolean isAuthenticated = false;
    if (request.path().startsWith(routes.Yeoman.index().url())
            && !isAuthenticated) {
        return controllers.Default.redirect(PlayAuthenticate.getResolver()
                .login().url());
    }
    return super.onRouteRequest(request);
}
jtammen commented 11 years ago

Update: no luck with that snippet I posted – obviously, there is no possibility to get access to the current session in onRouteRequest, as we only have the request headers available at that point. And the session would be needed to do the actual authentication check...

jtammen commented 11 years ago

OK, I think I will go with this rather simple solution for now, as I think doing a Java version of the Yeoman controller would be a lot more work:

object UiApp extends Controller {
  def AuthCheck[A](action: Action[A]): Action[A] = {
    Action(action.parser) { request =>
      if (PlayAuthenticate.isLoggedIn(new Session(play.libs.Scala.asJava(request.session.data))))
        action(request)
      else
        Results.Redirect(PlayAuthenticate.getResolver().login().url())
    }
  }

  // Wrap the Yeoman action into the action that checks if the user is authenticated.
  // The /ui and /ui/ routes now point to this action, of course.
  def index = AuthCheck {
    Yeoman.index
  }
}

Not the most elegant solution, but at least it works (-; And of course this is only relevant if the whole login stuff is living outside the Angular based single page application, as it is currently the case within my application, which of course leads to some other problems like deep linking for example...

milliondreams commented 11 years ago

Great! Glad you found a solution :)