5 Replies Latest reply on Mar 19, 2012 12:20 AM by John Schneider

    How to combine s:viewAction and Seam 3 Security

    Lukas Camra Newbie

      Hi,

      I am using JBoss 3.1. Final and Seam 3.1 Final and I want to use the security module to enforce security restrictions. However, I'm facing following problem:

       

      On the restricted page I have following:

      <f:metadata>

        <s:viewAction phase="APPLY_REQUEST_VALUES"

        action="#{entryActionManager.selectEntryById(entryId)}" />

        </f:metadata>                    

      So before its content is rendered, it loades the entry

       

      And I define following:

              @RestrictAtPhase(PhaseIdType.INVOKE_APPLICATION)
              @ViewPattern("/entry.xhtml")
              @Leader
               ENTRY_VIEW,
      

       

      So the security check is supposed to be lauched after the viewAciton was executed. But it doesn't behave like this. Actually, the security check is executed only if some form on /entry.xhtml is submitted (for example, I want to log out).  I tried various combinations of phases and it either didn't perform the security check or failed on performing the viewAction too late, so the producer that is supposed to be called after viewAction is invoked returns null.

       

      Any suggestions how to solve this?

       

      Thanks,

      Lukas

        • 1. Re: How to combine s:viewAction and Seam 3 Security
          zeeman Novice

          I don't use @RestrictPhase on enum. On your @Leader annotation use phase there:

           

          @SecurityBindingType

          @RestrictAtPhase(PhaseIdType.RESTORE_VIEW)

          @Retention(RetentionPolicy.RUNTIME)

          @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })

          public @interface Leader{

          }

           

          For me restore_view works well on security annotation. Be sure to use correct on xhtml.

          • 2. Re: How to combine s:viewAction and Seam 3 Security
            Lukas Camra Newbie

            The problem is that RESTORE_VIEW happens before APPLY_REQUEST_VALUES when the s:viewAction is executed. And I need to execute the view action before the security check happens

            • 3. Re: How to combine s:viewAction and Seam 3 Security
              Jason Porter Master

              That's a tough one. You could try the RestrictAtPhase for a different phase and see if that works for you. That's the best I've got, sorry.

              • 4. Re: How to combine s:viewAction and Seam 3 Security
                Lukas Camra Newbie

                That didn't work for me.

                 

                I solved it in following way:

                @Secures @Leader

                    public boolean isLeader(Identity identity, @HttpParam("entryId") Entry entry)

                ....

                Where Entry is injected using GET parameter entryId

                 

                I don't like this solution, because now the GET parameters is used twice in different places (HttpParam and viewAction) and I need to load the entity twice. But it works

                • 5. Re: How to combine s:viewAction and Seam 3 Security
                  John Schneider Newbie

                  I struggled with this for hours, and finally got it working through trial and error.   First, it's very counterintuitive, but you have to set @RestrictAtPhase(PhaseIdType.RESTORE_VIEW) on your SecurityBindingType enum.  Nothing else works for this scenario.

                   

                  Second, you don't have to mess around with s:action or trying to pass in the HTTP request paramter to your authorizer method.  Seam 3 Solder now has the @RequestParam annotation that you can use to just inject your parameter value.

                   

                  If your CDI bean is RequestScoped:

                  {code}

                  @Inject

                  @RequestParam

                  private String id;

                  {code}

                   

                   

                  If your CDI bean has a scope wider than RequestScoped:

                  {code}

                  @Inject

                  @RequestParam("id")

                  private Instance<String> idResolver;

                  {code}

                   

                   

                  Then add a PostConstruct method to do whatever you need to do.  For example:

                  {code}

                  @PostConstruct

                  public void postConstruct() {

                      String id = idResolver.get();

                      meter = entityManager.find(Meter.class, Integer.valueOf(id));

                      if (meter == null) {

                          messenger.error(meterNotFoundMessageKey, id);

                       }

                  }

                  {code}

                   

                  Here's my SecurityBindingType enum:

                  {code}

                  @SecurityBindingType

                  @RestrictAtPhase(PhaseIdType.RESTORE_VIEW)

                  @Retention(RetentionPolicy.RUNTIME)

                  @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})

                  public @interface MeterOwner {

                  }

                  {code}

                   

                  And my authorizer method, which gets called twice when the page is requested:

                  {code}

                  public @Secures @MeterOwner boolean isOwner(MeterView meterView) {

                      if (meterView == null || identity.getUser() == null) {

                          return false;

                       } else {

                          if (meterView.getMeter() == null) return false; // meter is null on first invocation

                   

                          String ownerId = meterView.getMeter().getUser().getId();

                          return identity.getUser().getId().equals(ownerId);

                          }

                  }

                  {code}