I have the following JSF 2.1 login form, running in Glassfish 3.1
I came up with the following solution to add roles programmatically after login, which works at least on GlassFish 3.1.2 build 23.
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.web.integration.PrincipalGroupFactory;
import java.security.Principal;
import java.util.Set;
import javax.security.auth.Subject;
import org.glassfish.security.common.Group;
public class GlassFishUtils {
public static void addGroupToCurrentUser(String groupName, String realmName) {
Subject subject = SecurityContext.getCurrent().getSubject();
Set<Principal> principals = subject.getPrincipals();
Group group = PrincipalGroupFactory.getGroupInstance(groupName, realmName);
if (!principals.contains(group))
principals.add(group);
}
}
You will need to add security.jar
and common-util.jar
from GlassFish to your project libraries.
And don't forget to create a <security-role>
section in your web.xml for the roles you wish to add.
Note that I am using functionality which does not appear to be part of a published stable API, so there is no guarantee that this will keep working in future releases of GlassFish.
I got the information on how to add roles from the source code of sun.appserv.security.AppservPasswordLoginModule.commit()
of GlassFish.
If a future GlassFish release breaks my code, this function would be a good place to start in order to find out how to fix it.
OK, i have figured out a workaround, which is not 100% correct in my point of view but suggestions are welcome :)
public void login() throws IOException, LoginException {
log.debug("Trying to login with username " + username);
try {
getRequest().login(username, password);
HttpSession session = getRequest().getSession(true);
Subject subject = (Subject) session
.getAttribute("javax.security.auth.subject");
if (subject == null) {
log.debug("Subject is null, creating new one");
subject = new Subject();
subject.getPrincipals().add(new PlainRolePrincipal("USER"));
subject.getPrincipals().add(new PlainRolePrincipal("ADMIN"));
}
log.debug("HAS USER " + getRequest().isUserInRole("USER"));
log.debug("HAS ADMIN " + getRequest().isUserInRole("ADMIN"));
log.debug("HAS REPORT " + getRequest().isUserInRole("REPORT"));
session.setAttribute("javax.security.auth.subject", subject);
log.debug("USER principal === " + getRequest().getUserPrincipal());
FacesContext.getCurrentInstance().getExternalContext()
.redirect("pages/home.jsf");
} catch (ServletException e) {
FacesContext.getCurrentInstance().addMessage("Login",
new FacesMessage("Invalid Username/Password combination"));
e.printStackTrace();
}
}
Also I use the following info bean to retrieve the subject and check the principals.
@ManagedBean(name = "userInfo")
@SessionScoped
public class UserInformation {
/**
* Fetches current logged in username.
*
* @return
*/
public String getUsername() {
return FacesContext.getCurrentInstance().getExternalContext()
.getRemoteUser();
}
public boolean isUserInRole(String roleName) {
Subject subject = (Subject) getRequest().getSession().getAttribute(
"javax.security.auth.subject");
for (Principal p : subject.getPrincipals()) {
if (p.getName().equals(roleName)) {
return true;
}
}
return false;
}
public static HttpServletRequest getRequest() {
Object request = FacesContext.getCurrentInstance().getExternalContext()
.getRequest();
return request instanceof HttpServletRequest ? (HttpServletRequest) request
: null;
}
}
So I workaround the isUserInRole mechanism, the real isUserInRole method returns only true on USER, because that role is set when authenticating.
From the JSF pages I can now do
<p:menuitem value="Create" action="#{menuController.XXXXXCreate}"
ajax="false" helpText="Create new XXXXX"
disabled="#{!userInfo.isUserInRole('ADMIN')}" />
Hope this helps other users, any improvement suggestions are welcome!