0 Replies Latest reply on Sep 22, 2012 1:45 PM by demetrio812

    Seam Security - Authorization doesn't work (with workaround)

    demetrio812 Apprentice

      Hello,

      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