Seam Security - Authorization doesn't work (with workaround)
demetrio812 Sep 22, 2012 1:45 PMHello,
I've followed the Seam 3 guide to add Authorization check using Seam Security, basically I have a custom @Admin annotation and I applied in Pages.java to avoid unauthorized access to the administration by users without the "admin" role.
If in the page I want to protect I add a viewAction, the system works as expected, if in the page I don't have any "page action" the system doesn't work.
Basically I debugged the Seam Security code and I have noticed that it correctly intercepts the annotation, calls the authorizer and correctly calls the redirectToAccessDeniedView() method in SecurityPhaseListener, the problems start here:
{code}
private void redirectToAccessDeniedView(FacesContext context, UIViewRoot viewRoot) {
// If a user has already done a redirect and rendered the response (possibly in an observer) we cannot do this output
final PhaseId currentPhase = context.getCurrentPhaseId();
if (!context.getResponseComplete() && !PhaseId.RENDER_RESPONSE.equals(currentPhase)) {
AccessDeniedView accessDeniedView = viewConfigStore.getAnnotationData(viewRoot.getViewId(), AccessDeniedView.class);
if (accessDeniedView == null || accessDeniedView.value() == null || accessDeniedView.value().isEmpty()) {
log.warn("No AccessDeniedView is configured, returning 401 response (access denied). Please configure an AccessDeniedView in the ViewConfig.");
context.getExternalContext().setResponseStatus(401);
context.responseComplete();
return;
}
String accessDeniedViewId = accessDeniedView.value();
log.debugf("Redirecting to configured AccessDenied %s", accessDeniedViewId);
NavigationHandler navHandler = context.getApplication().getNavigationHandler();
navHandler.handleNavigation(context, "", accessDeniedViewId);
context.renderResponse();
}
}
{code}
As you can see it goes on only if getResponseComplete()==false and we aren't in the RENDER_RESPONSE phase. If I directly access that page by writing it to the browser URL (or with a redirect) JSF goes directly to the RENDER_RESPONSE phase so the method above skips the redirect. In case I add the viewAction the INVOKE_APPLICATION view is called and everything works.
My workaround is to add @RestrictAtPhase(PhaseIdType.RESTORE_VIEW) to the annotation so that it gets called at the RESTORE_VIEW phase anyway, so that's the code:
{code}
@SecurityBindingType
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@RestrictAtPhase(PhaseIdType.RESTORE_VIEW)
public @interface Admin { }
{code}
I don't think there are problems with this approach except if we need to run some action before the check...
Any hints?
Btw I think that Seam 3 need at least other 2 minor version updates to address at least the problems fixed by the community, I'm going to open another post about that topic.
Demetrio Filocamo