15

To prevent a user role from performing an action.

  1. Example 1: The role "administrator" is the only role allowed to perform destroy action.
  2. Example 2: Any role different from "guest" can perform CREATE action.

In a real case, I have this:

public String delete() {
 if(FacesContext.getCurrentInstance().getExternalContext().isUserInRole("administrator"){
   //.....the action to perform
 }
 return "Denied";
}

I wish I could use the annotation @RolesAllowed() of EJB yet I am not using EJB but ManagedBeans. So the question is: Is there any way to use many roles at the same time? Some workaround! Example: If an action must be allowed to 3 roles (administrator, moderator, manager). I am obliged to do :

if (FacesContext.getCurrentInstance().getExternalContext().isUserInRole("administrator")
    || FacesContext.getCurrentInstance().getExternalContext().isUserInRole("manager") 
    || .....) {
  //....
}

And it is a pain to reproduce on all the methods. Something like hundreds of methods :(

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
Hanynowsky
  • 2,970
  • 5
  • 32
  • 43

3 Answers3

32

This needs to be controlled in the view side. Don't you find it by yourself very annoying when you see on some site a button for which you don't have sufficient rights to press and thus get an intimidating error page when you do so?

Just render the button in the view side only when the user has the required role, else hide it altogether.

<h:commandButton value="Delete" action="#{bean.delete}" 
    rendered="#{request.isUserInRole('administrator')}" />

This is not sensitive to (CSRF) hacks as JSF checks the condition once again during apply request values phase.

As to using multiple conditions and repeating the same over and over in a single view, consider using <c:set> to give it a short alias. You could even place it in the top of some master template so that it's available to all child templates.

<c:set var="isPowerUser" value="#{request.isUserInRole('manager') or request.isUserInRole('administrator')}" scope="request" />
...
<h:commandButton rendered="#{isPowerUser}" />
...
<h:commandButton rendered="#{isPowerUser}" />
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    smart practice as usual! Indeed, it is far annoying ti get the DENIED message. I already use the rendered attribute to display ADMIN panel if role is admin and so on. Doing in in the view has the advantages you mentioned of course, but the issue remains : I am obliged to enemurate every role seperately using or operator! Am I wrong? – Hanynowsky Jun 29 '11 at 22:30
  • 1
    It's at least more concise than `#{facesContext.externalContext.isUserInRole('administrator')}` :) If you need to repeat the same condition multiple times in the same view, then you could also use `` to give it an alias. See also the updated answer. – BalusC Jun 30 '11 at 05:56
  • Thanks BalusC! It is wiser to use the master template with ui:param to define new roles and avoid hacking the action methods in manged bean. It works fine for me now, All I have to do is apply the new role aliases where needed in the view. @Bozho method is also working fine for me if I really want to show the denied page to the non authorized user. – Hanynowsky Jun 30 '11 at 14:21
  • I use this myself allready, but this isn't real security (or am I missing something) you're just hiding a button/link for a user but the bean action can still be used! @BalusC, is there another check you do or is this it? – Rob Dec 27 '12 at 15:22
  • 2
    @Rob: It's purely server side, not client side or something (it's ab-so-lute-ly not like JavaScript/CSS display=none/block) and it's re-evaluated by JSF during processing the form submit. See also e.g. point 5 of http://stackoverflow.com/questions/2118656/hcommandlink-hcommandbutton-is-not-being-invoked/2120183#2120183 So yes, you definitely missed something :) – BalusC Dec 27 '12 at 15:26
  • I get that the link/button isn't rendered so isn't included in the output. That was not what I was trying to say. I'm trying to disable pages and menu items based on roles. Problem is that if the user knows the URL of that page then he can still navigate to it – Rob Dec 29 '12 at 09:47
  • @Rob: That has nothing to do with JSF. Use container managed authentication, or a 3rd party framework like Spring Security, or a homegrown servlet filter. – BalusC Dec 29 '12 at 11:23
  • Just noting that `c:set` doesn't accept the `name` attribute. Should be `var`. – mabi May 26 '13 at 07:32
  • @mabi: that was an unintentional leftover of original answer based on ui:param. See also edit history. – BalusC May 26 '13 at 11:39
  • Jup, just pointing it out for completeness and those (like me) that don't have the `c:set` attributes memorized. Anyway, thanks for the great suggestion! – mabi May 27 '13 at 07:12
7

You can shorten that by moving the logic to an utility method:

public class AuthorizationUtils {
    public static boolean isUserInRoles(String[] roles) {
        for (String role : roles) {
            if (FacesContext........isUserInRole(role)) {
                return true;
            }
        }

        return false;
    }
}

And then invoke it with:

if (AuthorizationUtils.isUserInRoles(new String[] {"administrator", "moderator"})) {
    ..
}

If you are using CDI, you can make an interceptor that handles the @RolesAllowed annotation

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • that is the answer to my question. While @BalusC gave a better practice. So can I use your utility method in the view instead of hacking action methods? Something like: `` I guess I am saying nonsense!! no? – Hanynowsky Jun 29 '11 at 22:45
  • I'm not sure if EL would allow this static invocation, but you can create a managed bean called `authBean` and put that method there. – Bozho Jun 29 '11 at 22:47
  • The Utility method works like a charm! Do you mind however if I accept @BalusC answer as it is the one I am adopting right now? – Hanynowsky Jun 30 '11 at 14:23
1

@Mediterran81: So basically you are looking for bean-method based authorization solution...how are you instantiating your managed beans? You may like to introduce a simple XML format:-

<bean class="">
 <method name="">
  <role> xyz</role>
 </method>
</bean>

Have this XML read by utility class and then only thing you would need to do is to call Utility's static method to determine if method is allowed to execute.

ag112
  • 5,537
  • 2
  • 23
  • 42
  • I thought about using XML before but I am ordered not to, and I am asked to use minimum coding to achieve things. in a JSF environment, I am asked to play rather on the view using JSF view capabilities. However, I voted your answer as I it describes a clean and smooth way of achieving method role authorization. Thanks ag112 – Hanynowsky Jun 30 '11 at 14:46