Cannot get property 'id' on null object

ynnr85

New member
My code in gsp is this:

<g:link controller="user" action="show" id="${userInstance.id}">${userInstance.username.encodeAsHTML()}</g:link>

Error:

Exception Message: Cannot get property 'id' on null object
Caused by: Error evaluating expression [userInstance.id] on line [75]: Cannot get property 'id' on null object

My code in controller is:

def show = {
def userInstance = User.get(params.id)
if (!userInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), params.id])}"
redirect(action: "list")
}
else {
[userInstance: userInstance]
}
}

I´m using:
grails-1.3.5
grails-shiro-1.1-SNAPSHOT

:confused: Please I need to know what is wrong here, my goal is to create a link to update user logged in profile. Thanks
 

dave

Administrator
Staff member
Can you show the controller code that is used before showing the gsp that contains :
Code:
<g:link controller="user" action="show" id="${userInstance.id}">${userInstance.username.en  codeAsHTML()}</g:link>
 

ynnr85

New member
Can you show the controller code that is used before showing the gsp that contains :
Code:
<g:link controller="user" action="show" id="${userInstance.id}">${userInstance.username.en  codeAsHTML()}</g:link>

The page containing the link is the main layout. I think it is loaded after BootStrap, so the id can't be null. It is possible?
All I want is user may edit the profile and I need to redirect to the edit page using the id of the authenticated user, But I want the link is displayed in the layout, as many websites (Profile | Sign out)
I'm new in Grail, can you tell me which is the controller that is loaded before the main layout, so I'll show you the code

My main layout code is this:
Code:
<html>
  <head>
    <title><g:layoutTitle default="Grails"/></title>
    <link rel="stylesheet" href="${createLinkTo(dir: 'css', file: 'main.css')}"/>
    <link rel="shortcut icon" href="${createLinkTo(dir: 'images', file: 'favicon.ico')}" type="image/x-icon"/>
  <g:layoutHead/>
  <g:javascript library="application"/>
</head>
<body>
  <div id="header">
    <shiro:isLoggedIn>
      <div id="profileActions">
        <span class="signout">                                      
           
     /*Here is the problem*/
     <g:link controller="user" function="show"id="${userInstance?.id}">Profile</g:link> |

     <g:link controller="auth" action="signOut">Sign out</g:link>
        </span>
      </div>
    </shiro:isLoggedIn>

    <shiro:isNotLoggedIn>
      <div id="profileActions">
        <span class="signin">

          <g:link controller="auth" action=
                  "index">Sign in</g:link>
        </span>
      </div>
    </shiro:isNotLoggedIn>

    <h2>My_Project</h2>

  </div>
  <div id="navigationcontainer">
    <span id="navigation">

      <g:link controller="home" class="navigationitem">Home</g:link>
      
    </span>
  </div>
<g:layoutBody/>
</body>
</html>

My auth controller code (SHIRO default): (IDK if this helps)
Code:
package system.subsystem.module
import org.apache.shiro.SecurityUtils
import org.apache.shiro.authc.AuthenticationException
import org.apache.shiro.authc.UsernamePasswordToken
import org.apache.shiro.web.util.SavedRequest
import org.apache.shiro.web.util.WebUtils

class AuthController {
    def shiroSecurityManager

    def index = { redirect(action: "login", params: params) }

    def login = {
        return [ username: params.username, rememberMe: (params.rememberMe != null), targetUri: params.targetUri ]
    }

    def signIn = {
        def authToken = new UsernamePasswordToken(params.username, params.password)

        // Support for "remember me"
        if (params.rememberMe) {
            authToken.rememberMe = true
        }
        
        // If a controller redirected to this page, redirect back
        // to it. Otherwise redirect to the root URI.
        def targetUri = params.targetUri ?: "/"
        
        // Handle requests saved by Shiro filters.
        def savedRequest = WebUtils.getSavedRequest(request)
        if (savedRequest) {
            targetUri = savedRequest.requestURI - request.contextPath
            if (savedRequest.queryString) targetUri = targetUri + '?' + savedRequest.queryString
        }
        
        try{
            // Perform the actual login. An AuthenticationException
            // will be thrown if the username is unrecognised or the
            // password is incorrect.
            SecurityUtils.subject.login(authToken)

            log.info "Redirecting to '${targetUri}'."
            redirect(uri: targetUri)
        }
        catch (AuthenticationException ex){
            // Authentication failed, so display the appropriate message
            // on the login page.
            log.info "Authentication failure for user '${params.username}'."
            flash.message = message(code: "login.failed")

            // Keep the username and "remember me" setting so that the
            // user doesn't have to enter them again.
            def m = [ username: params.username ]
            if (params.rememberMe) {
                m["rememberMe"] = true
            }

            // Remember the target URI too.
            if (params.targetUri) {
                m["targetUri"] = params.targetUri
            }

            // Now redirect back to the login page.
            redirect(action: "login", params: m)
        }
    }

    def signOut = {
        // Log the user out of the application.
        SecurityUtils.subject?.logout()

        // For now, redirect back to the home page.
        redirect(uri: "/")
    }

    def unauthorized = { }


I can upload an example project if this is not enough.
Thanks for your time.
 

dave

Administrator
Staff member
HI,

I don't use Shiro myself for security but from what I'm reading, you need to look how Shiro identifiers users. Rather than using the id column of your user table, it appears you should be looking at what Shiro refers to as the 'principal' (http://shiro.apache.org/static/current/apidocs/org/apache/shiro/subject/Subject.html#getPrincipal()) as this is what Shiro uses to uniquely identify logged on users.

So firstly, as your 'prinipal' user will be bound to the session, you don't need to be using usersinstance.id in your links, just leave the action as show and leave it at that. Then in the controller, in the show action, grab the current logged in 'principal' from the session with
Code:
def principal = SecurityUtils.getSubject().getPrincipal();

Hope that helps a bit.. :)
 

ynnr85

New member
HI,

I don't use Shiro myself for security but from what I'm reading, you need to look how Shiro identifiers users. Rather than using the id column of your user table, it appears you should be looking at what Shiro refers to as the 'principal' (http://shiro.apache.org/static/current/apidocs/org/apache/shiro/subject/Subject.html#getPrincipal()) as this is what Shiro uses to uniquely identify logged on users.

So firstly, as your 'prinipal' user will be bound to the session, you don't need to be using usersinstance.id in your links, just leave the action as show and leave it at that. Then in the controller, in the show action, grab the current logged in 'principal' from the session with
Code:
def principal = SecurityUtils.getSubject().getPrincipal();

Hope that helps a bit.. :)

Thank you for your help, was very important for me. I created a service.
Code:
class UserService {
    
    def getAuthenticatedUser() {
        return User.findByUsername(SecurityUtils.getSubject().getPrincipal())
    }
}
and after that the controller
Code:
def show = {
        
        def userInstance = userService.getAuthenticatedUser()
        def session = SecurityUtils.getSubject().getSession(true)

        if (!userInstance) {
            flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), params.username])}"
            redirect(controller: "auth", action: "index")
        }
        else {
            [userInstance: userInstance]
        }
    }
Now I think my problem is solved thanks to you.
 

dave

Administrator
Staff member
No problem, glad you sorted it and given me a brief introduction to Shiro!

What made you go with Shiro rather than Ageci/Spring Security if you don't mind me ask?
 

ynnr85

New member
Of course you can ask. My first experience with Grails was through a book (Grails 1.1 Web Application Development) by Jon Dickinson,he use JSecurity, but when I was reading about JSecurity plugin for Grails, I found that:
This plugin has been deprecated due to the name change of the JSecurity project to Apache Shiro use the Grails Shiro plugin instead. However, the documentation below is applicable to the Shiro plugin (still working on migrating the docs).
So IDK about Grails and Ageci/Spring Security, I gonna read about it, and maybe we could exchange ideas.
 

dave

Administrator
Staff member
Acegi/Spring security seem to be pretty popular, as its no rarity to see them mentioned on the Nabble group. For the majority of my projects, I haven't really needed their features, I tend to just use my own security management with a simple login page & session filters. It's really another of those many things I'd like to look more into 'when I can find time' :)
 
Top