18

How do I render JSF components based on a logged in user's role? I know the external context exposes the principals, but how should I do the rendering properly in JSF? In JSP it would be something like

<% isUserInRole(Roles.ADMIN) { %>
<button>Edit!</button>
<% } %>

How do I write this in JSF the best possible way? My best guess is the rendered attribute tied to a backing bean's method that returns a boolean, but that would introduce an irrelevant backing bean if I have to render some navigation items only for admins...

Glassfish V3.1, JSF 2.x

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
TC1
  • 1
  • 3
  • 20
  • 31

3 Answers3

42

If your web.xml is declared as Servlet 3.0 (which implicitly relates to JSP/EL 2.2)

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
    version="3.0">

then you can take benefit of being able to invoke methods with arguments in EL like as ExternalContext#isUserInRole():

rendered="#{facesContext.externalContext.isUserInRole('ADMIN')}"

Note that this requires a Servlet 3.0 capable container, but since you're using Glassfish 3 (which supports Servlet 3.0), it should work without any issues.

Also note that if you're using Facelets instead of JSP, then you've the HttpServletRequest available as #{request} in EL, allowing you the following shorter expression:

rendered="#{request.isUserInRole('ADMIN')}"
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • It is indeed Servlet 3.0, but somehow among all this fuss of the exam session I'd forgotten they added method calls to JSF... Thanks very much :) – TC1 Jan 20 '11 at 14:03
  • 1
    It's not specifically JSF, it's EL (those `#{}` things) which counts. If you run JSF 2.0 on for example Servlet 2.5 (which implies JSP/EL 2.1), it won't work. It's introduced in JSP/EL 2.2. – BalusC Jan 20 '11 at 14:04
  • I've defined roles for each user using JDBC Realm. But the above expression always returns "false" for any user. Do I need to add some other thing to make it work? – wasimbhalli Apr 13 '11 at 07:27
5

In response to @wasimbhalli, there are two reasons I have found that the expression would always return false:

  1. The role name is case sensitive. rendered="#{facesContext.externalContext.isUserInRole('ADMIN')}" may return false, but try rendered="#{facesContext.externalContext.isUserInRole('admin')}", or rendered="#{facesContext.externalContext.isUserInRole('Admin')}".

  2. You have to define your roles in both web.xml (or as annotations) and map it in glassfish-web.xml.

The following is how to specify a role in web.ml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <security-role>
    <role-name>admin</role-name>
  </security-role>
</web-app>

The following is how to map the authentication group to the role in glassfish-web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
  <security-role-mapping>
    <role-name>admin</role-name> <!-- name defined in web.xml or annotations -->
    <group-name>admin</group-name><!-- name from authentication mechanism -->
  </security-role-mapping>
</glassfish-web-app>

In my testing it was necessary to do the mapping even when the names were the same, as I show in my example code. Also in my testing, I tried to only define the mapping and only to define the role in web.xml, and neither worked. I needed both, as specifying the role name in the correct case.

pgreen2
  • 3,601
  • 3
  • 32
  • 59
0

Store role in session attribute and just compare that using rendered attribute.

e.g. rendered="#{yoursessionbean.userRole == Roles.ADMIN}"

niksvp
  • 5,545
  • 2
  • 24
  • 41
  • That was one of my ideas at first, but the user can have an infinite number of roles based on his groups. Would only really work if the user only had one role... – TC1 Jan 20 '11 at 13:31
  • @TC1 - agree, then you will have to go for a method call or wait if any other solution comes. – niksvp Jan 20 '11 at 13:33