11

Grails 2.4.5 here. I am trying to implement the following UX behavior for my GSPs:

  • If a user has permission to click a button, then they may do so; however
  • If the user doesn't have permission to click a button, then when they click the button, a banner message (flash?) appears across the top of the screen with an rose/pinkish/red background stating 'You don't have permission to take this action'

To determine whether the user has the required permission, I have access to functionality from both the Groovy and GSP/taglib layers.

From the Groovy/controller layer:

SecurityUtils.hasPermission(String permission)
Ex: SecurityUtils.hasPermission('UPDATE_BUZZ')

From the GSP/taglib layer:

<sec:hasPermission permission="<permission name>">???</sec:hasPermission>
Ex: <sec:hasPermission permission="UPDATE_BUZZ">???</sec:hasPermission>

So, given those two available access checking mechanisms, and given the following controller:

class FizzController {
    BuzzService BuzzService

    def buzz() {
        SomeData dataModel = buzzService.getModel(params)
        render(view: 'buzz', model: [ dataModel: dataModel ])
    }
}

...where buzz.gsp is:

<!-- Lots of HTML/GSP here -->
<g:submitButton name="update" value="Update" />
<!-- Lots more HTML/GSP down here -->

Given all that, my question is: How/where should I: (1) respond to the 'update' button's click handler, (2) perform the access check, and (3) render the error/banner/flash message? Code example (even pseudo-code) would be most awesome!

smeeb
  • 27,777
  • 57
  • 250
  • 447

4 Answers4

3

Here is what I would suggest:

  1. Make sure your controller method is role based
  2. Remove permission check in GSP, if the button should be visible to everyone
  3. Create an AJAX call upon submission, if the response status is 403, display the banner.
Armaiti
  • 766
  • 3
  • 11
  • A comment would be valuable for voting down. – Armaiti Jan 21 '16 at 19:19
  • 1
    It's the best solution. Permissions checks must be made on server side during model validation. With Grails it is not nessesary to return 403 status, you can return just a message source string (instead of JSON) and for backbone model it will be treated as an error on save(). On client side you cat catch all 403 and 500 also and use smth like toastr.error(message). So it's very easy to pass grails validation errors to client side service. – Alex Shwarc Jan 21 '16 at 23:43
  • I'd rather make filtering instead. It seems much cleaner and universal solution. – Michal_Szulc Feb 01 '16 at 08:21
3

If I were you, I'd probably try to use before-action filter in this case. In this filter I'd make a checking if current user has permissions to such an action (checking permissions should always be done in server-side, because of security reasons) and than:

  • if security check passes - just return true (controller will continue it's flow)

  • if security check fails - you can use default flash.message and return false (with propriate css style flasheed message could appears across the top of the screen with an rose/pinkish/red background)

Example code:

class UserFilters {
   def filters = {
      // ... other filters ...
      permissionAllCheck(controller: '*', action: '*') {
         before = {
            doPermissionCheck(delegate)
         }
      }
   }

   private boolean doPermissionCheck(filters) {
         if (! YourService.checkForPermissionForCurrentlyLoggedUser()) {
            filters.flash.message = "You don't have permission to take this action"
            return false
         }
      true
   }
}

If you want to use filter only to specific controller/action, check applying section. Remember also, that you can use invert rule filter.

More info about different filterTypes.

You should also remember to add flash.message section to your layout (or selected views). You can always specify type of flash message and it's css style.

Community
  • 1
  • 1
Michal_Szulc
  • 4,097
  • 6
  • 32
  • 59
2

Best way would be to hide the button if the user has no permission to use it. That could be easily achieve through <sec:hasPermission permission="UPDATE_BUZZ">button</sec:hasPermission>

If you want button to be displayed even for user without permission, you could use the same hasPermission to add an extra class to the button. Now capture the clock event for that class using jQuery and show your message.

Ashish Joseph
  • 1,103
  • 3
  • 12
  • 35
  • Thanks @ashipj (+1) - yes, unfortunately I still need to display the button to the user, **even** if they don't have permission to click + execute it. **Would you mind updating your answer with some code/pseudo-code examples, specifically: (1) adding the extra class to the button and (2) capturing the "clock event" for that class?** I'm just not understanding for some reason...thanks again so much! – smeeb Jan 18 '16 at 12:19
2

I am assuming by your question that you don't want a page refresh and perhaps not even an ajax call. Because if you did that then showing a banner is not difficult. You just want this to behave like JavaScript client-side validation (UX wise). If this assumption is wrong then don't read and use Aramiti's solution. Otherwise go ahead.

First solution

You can create a tag which takes a permission as input. Something like

<myTagLib:flashOnNoPermission permission="PERM" name="name" value="value">
</myTagLib:flashOnNoPermission>

This tag's definition can check the permission using sec:hasPermission. Then this tag can just render a template containing something like this

<hidden flash message>
<g:submitButton name="name" value="value" onclick="<unhide flash if no permission>"/>

Basically create a wrapper over grails button so that you can have flash messages alongwith buttons.

Problem

  • What if user's permissions are changed when user is on screen? Ajax takes care of that but this does not. Once screen is loaded then everything is fixed.
  • The banner is alongwith the button

Second solution

Add a common div at the top of your layout for displaying flash messages. Then create a tag similar to the above. Just don't add the flash message in the rendered template. Something like

    <g:submitButton name="name" value="value" onclick="<unhide flash at top of layout if no permission>"/>

Problem

  • What if user's permissions are changed when he is on the screen?

Not sure why you need things onclick handler but if there is no reason just Aramiti's solution.

Aseem Bansal
  • 6,722
  • 13
  • 46
  • 84