The situation is that I have a layout application.gsp that defines the layout for all the pages except the login page. Inside the layout is a header that should display the name of the current logged in user (Spring Security plugin). The problem is that if the user just registers or uses oAuth for the first time (same as registering) then when he logs in he will see and empty spot instead of a username because the layout was rendered before the username was available. If he logs out/logs in again he will of course see his username.
P.S. Moving the header outside of the layout is not an option, because it will cause massive code duplication.
Is there a way around this?
I will answer my own question for future generations trying to solve the same problem.
The trick is in using a decorator design pattern and the badly documented g:pageProperty gsp tag.
views/layouts/application.gsp:
....
<li class="dropdown menu">
<a href="#" class="dropdown-toggle"
data-toggle="dropdown"><g:pageProperty name="page.username"/></a>
<ul class="dropdown-menu">
<li>Preferences</li>
<li>
<a href="${resource(file: 'j_spring_security_logout')}"
class="navbar-btn btn-danger btn">Logout</a></li>
</ul>
</li>
....
views/index.gsp:
....
<body>
<content tag="username">
<g:username username="${username}"/>
</content>
....
taglib/UsernameTagLib.groovy:
class UsernameTagLib {
def springSecurityService
/**
* Check if the username is already available, else inject newly created username into the layout
*
* #attr username REQUIRED that was received from the newly registered user
*/
def username = { attrs ->
String currentUsername = springSecurityService.getCurrentUser()?.username
if (currentUsername) {
out << currentUsername
} else {
out << attrs.username
}
}
}
The newly created username is passed to views/index.gsp when the user is finished through the whole oAuth process.
controllers/OauthCallBackController.groovy
....
def facebook() {
Token facebookAccessToken = (Token) session[oauthService.findSessionKeyForAccessToken('facebook')]
if (facebookAccessToken) {
String username = oauthCallBackService.facebook(facebookAccessToken)
render view: '/index', model: [username: username]
} else failure()
}
....
Basically the username travels upwards until it reaches the layout, which allows me to use the username in the layout header and propogate it on all the pages that need the layout.
You can check it with SecurityTagLib helper of Spring Security Core.
In your application.gsp you can have:
...
<sec:ifLoggedIn>
<!-- Display user field with sec:loggedInUserInfo tag -->
Welcome Back <sec:loggedInUserInfo field="fullName"/>
<!-- Check if user have all specified privileges -->
<sec:ifAllGranted roles="ROLE_ADMIN,ROLE_SUPERVISOR">
You've ROLE_ADMIN AND ROLE_SUPERVISOR privileges.
</sec:ifAllGranted>
<!-- Check if user have at least one specified privileges -->
<sec:ifAnyGranted roles="ROLE_ADMIN,ROLE_SUPERVISOR">
You've ROLE_ADMIN OR ROLE_SUPERVISOR privileges.
</sec:ifAnyGranted>
</sec:ifLoggedIn>
<sec:ifNotLoggedIn>
It's public content, anonymous user.
</sec:ifNotLoggedIn>
...
You can also use: <sec:ifNotGranted roles="ROLE_USER"></sec:ifNotGranted>
If you are using Spring Security Core plugin, you can reauthenticate user in your controller.
springSecurityService.reauthenticate user.username
Related
I am doing a two level menu and I want to show some entries if the user has certain permissions.
I dont want to show the first level of the menu if the user doesn't have permissions to any other submenu because it would display an empty list of results.
I'll put some code to explain it better. This could be an example of the menu:
<ul>
<shiro:hasPermission permission="foo">
<li> foo</li>
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<li> bar</li>
</shiro:hasPermission>
</ul>
Is it there a way to wrap the <ul> somehow to ask if the user has foo or bar permission?
I'm using Grails 2.4.4 and Shiro plugin 1.2.1
UPDATE WITH SOLUTION:
Finally I've created a new tagLib that extends the ShiroTagLib and has the desired function:
class ShiroTagLib extends org.apache.shiro.grails.ShiroTagLib {
/**
* This tag only writes its body to the output if the current user
* have any of the given permissions provided in a separated comma String.
*/
def hasAnyPermission = { attrs, body ->
def permissions = attrs["permissions"]
if (!permissions){
throwTagError("Tag [hasAnyPermission] must have [permissions] attribute")
}
def permissionsList = permissions.split(',')
for (permission in permissionsList) {
if (checkPermission([permission: permission], "hasPermission")) {
// Output the body text.
out << body()
return out
}
}
}
}
with this you can do <shiro:hasAnyPermission permissions="foo,bar">
There are several ways to approach this but the hasAnyRole tag from the Shiro plugin is likely the easiest way.
<shiro:hasAnyRole in="foo,bar">
<ul>
<shiro:hasPermission permission="foo">
<li> foo</li>
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<li> bar</li>
</shiro:hasPermission>
</ul>
</shiro:hasAnyRole>
You can take a look at the source code for the tag library for more information.
Update
Based on the comment provided there is another option which is based on permissions.
<g:set var="hasSomePermission" value="${false}" />
<shiro:hasPermission permission="foo">
<g:set var="hasSomePermission" value="${true}" />
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<g:set var="hasSomePermission" value="${true}" />
</shiro:hasPermission>
...
<g:if test="${hasSomePermission}">
...
</g:if>
The Shiro plugin doesn't provide a logical OR tag for permissions like it does with roles, so the above is another approach. Using this same idea you could DRY it up with your own tag library.
I'm sure there must be a more elegant way of implementing if/else logic blocks in a GSP based on user info.
Consider the following:
<g:if test="(user logged in) && (this users post) && (post replied to)">
Show options for this post if replied to
</g:if>
<g:elseif test="(user logged in) && (this users post)">
show options if this users post
</g:elseif>
<g:elseif test="(user logged in)">
show options if not this users post
</g:elseif>
<g:else>
show options if no one is logged in
</g:else>
I'm having to use the following to determine if a user is logged in (sec.loggedInUserInfo(field: 'id') != "") I know, I hate it!
I cannot use the nice Spring Security tags as they do not satisfy my requirements and I was trying to implement custom tags but would not be able to implement if/else functionality (or could I)?
Any help would be much appreciated.
EDITED:
The tag I've created:
<g:isOwnerAndStatusEquals post="${post}" status="${Post.REPLIED_TO}">
My post and is replied to
</g:isOwnerAndStatusEquals>
The Tag lib implementation:
class AuthTagLib {
def springSecurityService
def isOwnerAndStatusEquals = { attrs, body ->
def loggedInUser = springSecurityService.currentUser
def post = attrs?.post
def postUser = post?.user
def postStatus = attrs?.status
if(loggedInUser?.id == postUser?.id && job?.status?.equals(thisStatus)) {
out << body()
}
}
}
This above is all fine, but I've no idea, at a high level, how to add if/else to the same tag?
The following appears to be equivalent to your GSP code and does not require you to write any custom tags:
<sec:ifLoggedIn>
<g:if test="(this users post) && (post replied to)">
Show options for this post if replied to
</g:if>
<g:elseif test="(this users post)">
show options if this users post
</g:elseif>
<g:else>
show options if not this users post
</g:else>
</sec:ifLoggedIn>
<sec:ifNotLoggedIn>
show options if no one is logged in
</sec:ifNotLoggedIn>
You can use the SpringSecurityUtils inside the test criteria of the g:if tag. Like this:
<g:if test="${grails.plugin.springsecurity.SpringSecurityUtils.ifAllGranted('ROLE_MYROLE')}"
It will solve the problem since you can combine it with other test. The down-side is that the code becomes somewhat bloated. You can make it look a little bit nicer by adding an import for grails.plugin.springsecurity.SpringSecurityUtils if you use it at multiple places.
I have a Grails app that has a Domain for People, Location, Pictures and Users. Controllers and Views have been generated. I have my index page sec:tagged so that the admin can see the controllers to upload files, create data, and users.
Users have a username, password and location. When a user logs in I want them to see the files that pertain to their location. There can be multiple users attached to a location. But I want every user with that location to see the associated files. I stared with the #Secure but moved to the sec: tags. Now I just need to limit the display of files based on location. Any advice?
Here is the code I have so far for the user display
<sec:access expression="hasRole('ROLE_USER')">
<% def user = springSecurityService.getCurrentlyLoggedInUser()
def aPerson = user.person
def locations = Location.findAllByPerson(aPerson)
locations.each ${it.picture} %>
<g:select name="picture" from="${aPerson.all()}"/>
</sec:access>
This displays nothing on the User page.
First thing, avoid <% %> at all costs. Secondly, I have no idea what you think this code is supposed to be doing:
<g:select name="picture" from="${aPerson.all()}"/>
Lastly, it would help to see your Domain code to understand the actual relationship between all of them. But moving forward with what you have provided, I'd approach it like this:
controller
def someAction() {
def user = springSecurityService.getCurrentlyLoggedInUser()
def aPerson = user.person
def locations = Location.findAllByPerson(aPerson)
[aPerson: aPerson, locations: locations]
}
view
<!-- only use the expression tag if you actually need an expression -->
<sec:ifAllGranted roles='ROLE_USER'>
<g:each in="${locations}" var="location">
<!-- do something with each location -->
</g:each>
<g:select name="picture" from="${aPerson.all()}"/>
</sec:ifAllGranted>
I am currently developing a web app in Grails and I am looking for a way to hide a menu based on the current user logged into the solution.
To give you a bit of background this is what I have setup
A web app with a User Model and Roles model that are mapped
Login functionality that restricts certain controllers based on the users access.
I have menus that are display on each of the pages.
I know how to restrict a controller to only allow users with access to view it but I want to now restrict a menu like the one below from being seen unless the right user is logged in, how can I do this? Does it have something to do with rendering that element from the controller??
<div class="nav">
<ul class"nav">
<li>
<g:link class="Tester" controller="Testing" action="test">
<g:message code="Tester" args"[entityName]" />
</g:link>
</li>
<li>
<g:link class="Tester2" controller="Testing" action="test2">
<g:message code="Tester2" args"[entityName]" />
</g:link>
</li>
</ul>
</div>
The spring-security-core plugin provides a taglib that may help you here
<sec:ifAnyGranted roles="ROLE_TESTER">
<div class="nav">
...
</div>
</sec:ifAnyGranted>
Ian answered your question well but we should add here to secure the server side controller/actions as well such as:
// At the controller level
#Secured(["hasRole('User')"])
class Testing
// action specific
#Secured(["hasAnyRole('SuperUser', 'Support', 'InternalUser')"])
def test() {
...
}
Otherwise the links are just hidden from view but could still be executed by anyone.
HTH
If you are not using spring-security-core plugin following can be implemented
<g:if test="${userHaveRightRole}">
<div class="nav">
...
</div>
</g:if>
i want to show/hide certain parts of a View based on Authentication-status or Roles. For my controller actions I have extended ActionFilterAttribute so I can attribute certain Actions.
<RequiresRole(Role:="Admin")> _
Function Action() as ActionResult
Return View()
End Function
Is there a similar way (attributing) which I can use in the Views? (so not like this: How can I create a view that has different displays according to the role the user is in?)
You can access the user's logged-in roles from the view like this:
<% if (Page.User.IsInRole("Admin")) { %>
<td>
<%= Html.DeleteButton("delete", model.ID) %>
</td>
<% } %>
and maybe your extension method with something like:
public static string DeleteButton(this HtmlHelper html,
string linkText, int id)
{
return html.RouteLink(linkText,
new { ID = id, action = "Delete" },
new { onclick = "$.delete(this.href, deleteCompleted()); return false;" });
}
Obviously, I'm using JavaScript to perform an HTTP DELETE to my controller action, to prevent page crawlers from accidentally deleting data from getting my pages. In my case I'm extending JQuery with a delete() method to supplement the HTTP verb.
I new this existed, but took a while to find. Here's what I am using:
<asp:LoginView runat="server">
<AnonymousTemplate>
You are not logged in yet. Please log in.
</AnonymousTemplate>
<RoleGroups>
<asp:RoleGroup Roles="Admin">
<ContentTemplate>
You are an Admin.
</ContentTemplate>
</asp:RoleGroup>
<asp:RoleGroup Roles="Customers">
<ContentTemplate>
You are a customer.
</ContentTemplate>
</asp:RoleGroup>
</RoleGroups>
<LoggedInTemplate>
Simple Log in check
</LoggedInTemplate>
</asp:LoginView>
This allows you to show different content to different users based on their login state or credentials.