I am experimenting with Grails 3 and its new concept of interceptors. Given the following interceptor/controller:
class AuthInterceptor {
// ...other stuff in here
boolean before() {
if(FizzBuzz.isFoo()) {
redirect(controller: auth, action: signin)
true
} else {
true
}
}
}
class AuthController {
AuthService authService
def signin() {
String username = params[username]
String password = params[password]
user = authService.authenticate(username, password)
if(user) {
SimpleSecurityUtils.setCurrentUser(user)
redirect(url: ??? INTENDED_DESTINATION ???)
} else {
// Auth failed.
redirect(action: failed)
}
}
}
AuthService is a Grails Service. I would like there to be one instance of AuthService per instance of AuthController. And I would like AuthController to have prototype scope such that I am never storing state, and a controller is created for each request. What do I have to change in both (AuthService/AuthController) to meet these scope requirements?
Assuming AuthController#signin is executed due to a redirect(controller:auth, action: signin) inside an interceptor, how do I redirect (yet again) the user to their intended destination (that is, the URL they wanted to go to prior to the interceptor intercepting the request) from inside the controller action above?
First, if you redirect to another URL, you must return false to cancel the rendering process, e.g.
redirect(controller: auth, action: signin)
false
Second, if you want back to previews intended URL, you must save it, may be, to session, and then you redirect to the saved URL when you finish signin process.
Related
I'm attempting a simple chat that requires the user to be logged in to use. I've gotten the login working, and can detect it in the controller with springSecurityService.isLoggedIn(). However, when calling that function inside the chat handler, it will always return false. The function in question:
#MessageMapping("/sendchat")
#SendTo("/topic/chats")
protected String sendchat(String getrequest) {
log.info "\n\npre login"
if(springSecurityService.isLoggedIn())
{
log.info "\n\n"+getrequest
//do other things if successful
}
else
{
log.info "\n\nnot logged in"
}
}
It will always execute the else statement, even when the index() function of the controller has correctly detected the user as logged in.
That is because "springSecurityService" is work only for Controller action methods, the "spring-security-core plugin" use filters to setup security-context, then the springSecurityService can retrieve the user login status.
While in your code, the method is "protected" and it is a "websocket plugin" convention, so you should do security according to the plugin document, like this:
class ExampleController {
#ControllerMethod
#MessageMapping("/hello")
#PreAuthorize("hasRole('ROLE_USER')")
#SendTo("/topic/hello")
String hello(String world) {
return "hello from secured controller, ${world}!"
}
#ControllerMethod
#MessageExceptionHandler
#SendToUser(value = "/queue/errors", broadcast = false)
String handleException(Exception e) {
return "caught ${e.message}"
}
}
More details can reference "https://github.com/zyro23/grails-spring-websocket#securing-message-handler-methods".
System has two grails apps:
the private backoffice which uses springsecurity and an Operator domain object holding the operators username, password, number of failed logins etc.
the public web front end where users signup, login, and use the system. The User domain object holds the users username, password etc.
Because we are using springsecuirty for the backoffice, I assume we cant use it again for the web (the config and db will conflict). Also, the web just needs a very basic auth (all pages require a valid session except register and the login form itself).
Setting up the login form and the interceptor are easy.
The question is, what should the login form actually do in the controller? I can check the username and password match whats in the DB, then I presumably need to create a session, with session timeouts etc. Where do I look for documentation on how to do this? http://docs.grails.org/3.1.1/ref/Servlet%20API/session.html Tells you how to logout, but not login. I presumably need to store sessions in the DB (so that the user can hit any server) etc.
By looking at some of my old java code, I have got some of the way there.
The interceptor looks like this:
class AuthInterceptor {
public AuthInterceptor() {
// allow the login form and register form to work.
matchAll().excludes(controller: 'auth')
}
boolean before() {
if(session.getAttribute("user")== null ) {
// jump to the login form if there is no user attribute.
redirect controller: 'auth', action: 'login'
return false
}
true
}
boolean after() { true }
void afterView() {
// no-op
}
The controller looks like this:
class AuthController {
def index() { }
def login() {
def email = params.email
def password = params.password
if (email != null) {
// It would be better to invalidate the old session
// but if we do, we cant get a new one...
// session.invalidate()
User user = User.findByEmail(email);
if (user != null) {
log.error("user.pass:" + user.password + " pass:" + password)
// #TODO handle password encryption
if (user.password == password) {
session.setAttribute("user", user)
redirect(controller:"dashboard")
}
}
flash.message = "email or password incorrect"
}
render (view:"login")
} // login()
However, I have not found where we can set the session timeout yet.
Laravel 5.1
I'm trying to build this functionality for every method in my controller. And this is very unpractical to do and very difficult to maintain. How can I set this that is when I registered an auth middleware in a specific route then it will redirect into a login page together with the URL that trying to view/visit.
Example:
URL: public/users
If this URL will try to visit by an unauthenticated user, then the user will be redirected to a url like public/login?url=http://localhost/myproject/public/users
Then After the user loggedin successfully, the user then will be redirected into http://localhost/myproject/public/users
What I have now: (But I think not a good one to use)
public function getLoginURL(){
if(!Input::get('location-login')){
// URL is not set, redirect user to home.
return redirect('/');
}
if(auth()->check()){
// User is logged in. Go the specified URL (depends on the middleware of the url (route))
return redirect()->to(Input::get('location-login'));
}
if(!auth()->check()){
// Redirect user to a login with the URL provided
return view('pages::auth.login.url');
}
}
You can use the next method in middleware... no need to send the url
public function handle($request, Closure $next)
{
if (Auth::user()) {
return $next($request);
}
return redirect('/login');
}
Use this in your AuthController
namespace App\Http\Controllers;
use Auth;
use Illuminate\Routing\Controller;
class AuthController extends Controller
{
/**
* Handle an authentication attempt.
*
* #return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
intended() function will redirect you to your intended location.
Check the full detail here Intended redirect
I would like to check if the user is logged in or not.I have integrated interceptor in most of the projects to check if user is logged in . But this does-not work well with AJAX request and matching all the controllers in the interceptor causes filter to be applied for all controllers including /static/** ,where in this case i will have to exclude (.excludes(uri: "/static/**")) .Is this the correct and standard way of doing?
LoginInterceptor() {
matchAll()
.excludes(controller: "login")
.excludes(controller: "signUp")
.excludes(uri: "/static/**")
.excludes(uri: "/assets/**")
}
boolean before() {
if (!springSecurityService.isLoggedIn()) {
flash.message="You need to login to access furthur pages"
redirect( controller: 'login',action: 'auth');
return false
}
true
}
boolean after() { true }
void afterView() {
// no-op
}
The above code doesnot work with the AJAX request . How do i make a generic interceptor which works well with both AJAX and WEB request? And is this a standard way of monitoring request for logged in?
If you use spring security plugin. You can do it by configuring security config. You can find more here
For ajax request you can check return code and if it is == 403, you can redirect user to the login page.
When working on my project, I found the following way to check the log-in status.
def springSecurityService
boolean canEdit() {
boolean isLoggedIn = springSecurityService.isLoggedIn()
if (!isLoggedIn) { return false }
boolean hasAdminOrCuratorRole = userService.isLoggedInUserACurator() || userService.isLoggedInUserAAdmin()
hasAdminOrCuratorRole
}
Hope this helps!
At the end of my save action I redirect to the show action like this:
redirect(action: "show", id: exampleInstance.id)
In my show action I want to be able to detect if someone came directly to this action via a url, or if they were redirected here from another action. I tried request.isRedirected() but it always returns false.
How do I detect if I am in an action as the result of a redirect from another action?
I guess you want to display a confirmation message. Grails has a built-in feature for this kind of use-case:
http://www.grails.org/doc/2.1.0/ref/Controllers/flash.html
Have a look at the example:
class BookController {
def save() {
flash.message = "Welcome!"
redirect(action: 'home')
}
}
In the view you can print or check against flash.message.
In theory, isRedirect checks request attributes. It is equivalent to
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
if(request.getAttribute(GrailsApplicationAttributes.REDIRECT_ISSUED) != null){
println "request was redirect"
}
Try it directly and tell me what happens.
It looks like in the Grails source that isRedirected() is only applicable to the save action, as it gets set in the redirect() method logic, and so wouldn't be set in the show action.
Instead, here are some manual options. One is to add a flag to the flash object, which is then tested in the redirect action. Since it is in flash scope it will be cleared at the end of the show action:
def save() {
// Do stuff
flash.redirectFrom = "save"
redirect(action:"show")
}
def show() {
if (flash.redirectFrom) {
// Respond to redirect
}
// Do other stuff
}
Another option is to issue a chain() call instead of a redirect() and test for the implicit chainModel object. The chainModel won't exist when the show action is requested from an external URL:
def save() {
// Do stuff
chain(action:"show",model:[from:'show'])
}
def show() {
if (chainModel) {
// Respond to redirect
}
// Do other stuff
}