12 Replies Latest reply on May 11, 2007 10:06 PM by efabiano

    Security - Define dynamic Role in application

    stsheak

      After read through the Security Chapter in the Seam Reference, I found the Seam Security API rather rigid. What I want to do in my application is to allow user to define their own role with whatever pre-defined permissions grant to the role. The Authentication is fine for me but I am expecting something more flexible (maybe Interceptor) on the Authorization part. Anyone can advise?

        • 1. Re: Security - Define dynamic Role in application
          shane.bryzak

          What exactly do you want to achieve? The fact that authorization is rule-based means that it should provide a great deal of flexibility. Can you describe what your requirements are?

          • 2. Re: Security - Define dynamic Role in application
            markfoerstein

            Hi Shane,

            I don't know what stsheak wants exactly, but I was about to ask a similar question, so I will use this topic instead of opening a new one.

            I've successfully implemented authentication and authorization using Seam security. It works great.

            I defined my roles on the database and bound many roles to one user, and many users to one role. When the user logs in, I get his roles. That's ok and works perfectly.

            I defined permissions using JBoss Rules. Then I annotated the methods with @Restrict, configured the exceptions on pages.xml, etc... That's ok and works perfectly.

            JBoss Rules is nice, but it would be better if I could get the roles permissions from the database. How can I do that and still use Seam security annotations like @Restrict to validate authorization?

            The problem is, when creating a new role or changing permissions, I have to edit drool's security files to explicity set the permissions, which means that every new role and permission must be done changing application code.

            What I want is to have an "admin" user log in the application, access a "create/edit role" action, define its permissions and bind the roles to the users (this last one I can do already).

            That way, I don't need to change my application code, no redeploy, and no hard-coded permissions into drool's files. The "admin" user is free to do what he wants.

            Thanks for any help.

            • 3. Re: Security - Define dynamic Role in application
              shane.bryzak

              There's an outstanding JIRA issue to add this kind of functionality to Seam security, however you can easily implement it in your own application. Roughly, the steps are:

              1) Create a Permission class with a name and action property. For this example let's call it GrantedPermission.
              2) In your authentication method, assert a GrantedPermission instance into the working memory (using RuleBasedIdentity.getSecurityContext().assertObject()) for each of the permissions granted via the user's role memberships.
              3) Write a rule that matches permission checks against the granted permissions in the working memory, i.e. something like this:

              rule GrantDynamicPermission
               no-loop
               activation-group "permissions"
               salience -10
              when
               check: PermissionCheck(granted == false)
               GrantedPermission(n : name -> (n.equals(check.name)), a : action -> (a.equals(check.action)))
              then
               check.grant();
              end;
              


              You'll have to double-check the syntax of the rule, however it demonstrates in principle what you need to do.



              • 4. Re: Security - Define dynamic Role in application
                markfoerstein

                It makes sense and looks like a good solution. I was thinking that the permissions, coming from the database, would be stored in memory somehow, somewhere... but I couldn't realize how to accomplish that so I could make the checks later ;-)

                I will try to implement it and see what happens, then I will post my results here.

                Thanks for your reply.

                • 5. Re: Security - Define dynamic Role in application
                  markfoerstein

                  Hi Shane,

                  Today I finally got my hands on this. My database structure and entities are all set.

                  Step 2) tricked me, since the getSecurityContext() method is not available inside RuleBasedIdentity.instance(). So Im using the following to get the working memory:

                  WorkingMemory wm = ((RuleBasedIdentity) Identity.instance()).getSecurityContext();
                  


                  Im having trouble coding the rule, and I though maybe you could help me. This is the rule (just the same you posted):
                  package DynamicPermission
                  
                  import org.jboss.seam.security.PermissionCheck;
                  import com.sphere.consultoria.login.GrantedPermission;
                  
                  rule GrantDynamicPermission
                   no-loop
                   activation-group "permissions"
                   salience -10
                  when
                   check: PermissionCheck(granted == false)
                   GrantedPermission(n : name -> (n.equals(check.name)), a : action -> (a.equals(check.action)))
                  then
                   check.grant();
                  end;
                  


                  And here is the permission class:
                  ...
                  public class GrantedPermission implements Serializable {
                  
                   private String name;
                   private String action;
                  
                   public String getAction() {
                   return action;
                   }
                  
                   public void setAction(String action) {
                   this.action = action;
                   }
                  
                   public String getName() {
                   return name;
                   }
                  
                   public void setName(String name) {
                   this.name = name;
                   }
                  
                   @Override
                   public int hashCode() {
                   ...
                   }
                  
                   @Override
                   public boolean equals(Object obj) {
                   ...
                   }
                  }
                  


                  Im getting...
                  19:52:13,975 INFO [Lifecycle] starting up: org.jboss.seam.security.identity
                  19:52:28,867 ERROR [[/consultoria]] Session event listener threw exception
                  org.drools.rule.InvalidRulePackage: Rule Compilation error Private member cannot be accessed from type "DynamicPermission.Rule_GrantDynamicPermission_0". Private member cannot be accessed from type "DynamicPermission.Rule_GrantDynamicPermission_0".
                  
                   at org.drools.rule.Package.checkValidity(Unknown Source)
                   at org.drools.common.AbstractRuleBase.addPackage(Unknown Source)
                   at org.jboss.seam.drools.RuleBase.compileRuleBase(RuleBase.java:70)
                   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                  


                  I have no idea what's going on. The getters are public in either classes, GrantedPermission and PermissionCheck. So what private member can't be accessed?

                  Im using:
                  JBoss 4.0.5
                  JBoss Seam 1.2.1GA

                  Thanks in advance.

                  • 6. Re: Security - Define dynamic Role in application
                    shane.bryzak

                    Try check.getName() and check.getAction(). We're waiting for the next Drools milestone to be released which should include some enhancements to the rule language that will allow more EL-like expressions.

                    • 7. Re: Security - Define dynamic Role in application
                      markfoerstein

                      That did the trick, but now Im facing another problem.

                      When the user authenticates, his roles and its permissions are loaded, and a new GrantedPermission object is asserted for each permission into the working memory:

                      public boolean authenticate() {
                       ...
                      
                       for (SphGru sphGru : sphUsu.getSphGrus()) {
                       Identity.instance().addRole(sphGru.getNome()); //add the user role
                       for (SphPer sphPer : sphGru.getSphPers()) { //assert the user role permissions into the working memory
                       String name = sphPer.getSphEditor().getNomeComponente(); //get the component name
                       WorkingMemory wm = ((RuleBasedIdentity) Identity.instance()).getSecurityContext();
                      
                       if (sphPer.getFlgPermiteIncluir().toString().equals("S"))
                       wm.assertObject(new GrantedPermission("create", name)); //user can call component's create action
                      
                       if (sphPer.getFlgPermiteAlterar().toString().equals("S"))
                       wm.assertObject(new GrantedPermission("update", name)); //user can call component's update action
                      
                       if (sphPer.getFlgPermiteExcluir().toString().equals("S"))
                       wm.assertObject(new GrantedPermission("delete", name)); //user can call component's delete action
                       }
                       }
                      
                       ...
                      }
                      


                      Component has "create", "update" and "delete" methods annotated with @Restrict:
                      @Restrict
                      public String create() {
                      ...
                      }
                      
                      @Restrict
                      public String update() {
                      ...
                      }
                      
                      @Restrict
                      public String delete() {
                      ...
                      }
                      


                      This is the rule (/META-INF/security-dynamic-permission.drl):
                      package DynamicPermission
                      
                      import org.jboss.seam.security.PermissionCheck;
                      import com.sphere.consultoria.login.GrantedPermission;
                      
                      rule GrantDynamicPermission
                       no-loop
                       activation-group "permissions"
                       salience -10
                      when
                       check: PermissionCheck(granted == false)
                       GrantedPermission(n : name -> (n == check.getName()), a : action -> (a == check.getAction()))
                      then
                       System.out.println("Permission granted!!!");
                       check.grant();
                      end;
                      


                      components.xml:
                      ...
                       <security:identity authenticate-method="#{authenticator.authenticate}"/>
                      
                       <drools:rule-base name="securityRules">
                       <drools:rule-files>
                       <value>/META-INF/security-dynamic-permission.drl</value>
                       </drools:rule-files>
                       </drools:rule-base>
                      ...
                      


                      And the GrantedPermission class:
                      public class GrantedPermission implements Serializable {
                      
                       private String name;
                       private String action;
                      
                       public GrantedPermission(String action, String name) {
                       this.action = action;
                       this.name = name;
                       }
                      
                       public String getAction() {
                       return action;
                       }
                      
                       public void setAction(String action) {
                       this.action = action;
                       }
                      
                       public String getName() {
                       return name;
                       }
                      
                       public void setName(String name) {
                       this.name = name;
                       }
                      
                       @Override
                       public int hashCode() {
                       ...
                       }
                      
                       @Override
                       public boolean equals(Object obj) {
                       ...
                       }
                      }
                      


                      Still, I get an AuthorizationException when the rule is supposed to fire:
                      14:04:15,390 ERROR [ExceptionFilter] uncaught exception
                      javax.servlet.ServletException: Error calling action method of component with id _id17:_id54
                       at javax.faces.webapp.FacesServlet.service(FacesServlet.java:152)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:63)
                       at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
                       at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:57)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
                       at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:79)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
                       at org.jboss.seam.web.SeamFilter.doFilter(SeamFilter.java:84)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.ajax4jsf.framework.ajax.xmlfilter.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:96)
                       at org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter.doFilter(BaseFilter.java:220)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                       at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                       at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:175)
                       at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:74)
                       at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                       at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                       at org.jboss.web.tomcat.tc5.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:156)
                       at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                       at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                       at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
                       at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
                       at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
                       at org.apache.tomcat.util.net.MasterSlaveWorkerThread.run(MasterSlaveWorkerThread.java:112)
                       at java.lang.Thread.run(Thread.java:595)
                      14:04:15,390 ERROR [ExceptionFilter] exception root cause
                      javax.faces.FacesException: Error calling action method of component with id _id17:_id54
                       at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:74)
                       at javax.faces.component.UICommand.broadcast(UICommand.java:106)
                       at org.ajax4jsf.framework.ajax.AjaxViewRoot.processEvents(AjaxViewRoot.java:180)
                       at org.ajax4jsf.framework.ajax.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:158)
                       at org.ajax4jsf.framework.ajax.AjaxViewRoot.processApplication(AjaxViewRoot.java:329)
                       at org.apache.myfaces.lifecycle.LifecycleImpl.invokeApplication(LifecycleImpl.java:343)
                       at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:86)
                       at javax.faces.webapp.FacesServlet.service(FacesServlet.java:137)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:63)
                       at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
                       at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:57)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
                       at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:79)
                       at org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:49)
                       at org.jboss.seam.web.SeamFilter.doFilter(SeamFilter.java:84)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.ajax4jsf.framework.ajax.xmlfilter.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:96)
                       at org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter.doFilter(BaseFilter.java:220)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                       at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                       at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                       at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:175)
                       at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:74)
                       at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                       at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                       at org.jboss.web.tomcat.tc5.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:156)
                       at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                       at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                       at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
                       at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
                       at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
                       at org.apache.tomcat.util.net.MasterSlaveWorkerThread.run(MasterSlaveWorkerThread.java:112)
                       at java.lang.Thread.run(Thread.java:595)
                      Caused by: javax.faces.el.EvaluationException: /editSphAgenda.xhtml @83,159 action="#{sphagendaEditor.create}": org.jboss.seam.security.AuthorizationException: Authorization check failed for expression [#{s:hasPermission('sphagendaEditor','create', null)}]
                       at com.sun.facelets.el.LegacyMethodBinding.invoke(LegacyMethodBinding.java:73)
                       at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:63)
                       ... 40 more
                      Caused by: org.jboss.seam.security.AuthorizationException: Authorization check failed for expression [#{s:hasPermission('sphagendaEditor','create', null)}]
                       at org.jboss.seam.security.Identity.checkRestriction(Identity.java:160)
                       at org.jboss.seam.interceptors.SecurityInterceptor.aroundInvoke(SecurityInterceptor.java:35)
                       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
                       at org.jboss.seam.interceptors.RemoveInterceptor.aroundInvoke(RemoveInterceptor.java:40)
                       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
                       at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:103)
                       at org.jboss.seam.intercept.ClientSideInterceptor.invoke(ClientSideInterceptor.java:50)
                       at org.javassist.tmp.java.lang.Object_$$_javassist_54.create(Object_$$_javassist_54.java)
                       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                       at java.lang.reflect.Method.invoke(Method.java:585)
                       at com.sun.el.parser.AstValue.invoke(AstValue.java:174)
                       at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:286)
                       at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:68)
                       at com.sun.facelets.el.LegacyMethodBinding.invoke(LegacyMethodBinding.java:69)
                       ... 41 more
                      


                      Expression [#{s:hasPermission('sphagendaEditor','create', null)}] should grant the permission, since I've asserted into the working memory a GrantedPermission with action "create" and name "sphagendaEditor".

                      Im sure the rule /META-INF/security-dynamic-permission.drl is being loaded, because it was giving me that validation error before. So components.xml seems to be properly configured.

                      It seems the rule is not fired at all.

                      Any tips?

                      • 8. Re: Security - Define dynamic Role in application
                        markfoerstein

                        Hi Shane / guys,

                        I really would appreciate any help with this.

                        I have everything set. The role's permissions are on the database, I defined actions to manipulate this permissions through the UI, etc...

                        The "authenticate" method is doing his job, putting all permissions into the working memory.

                        And still I have no idea why the "hasPermission" expressions don't evaluate to true, since the permissions are in the working memory.

                        Im at that thin line between total bliss and total frustration. If this thing works, I get everything I need for permission creation and checking. If it doesn't, then my effort and time was useless, and I will have to go back to the drl files...

                        I can't find a working example doing the same thing, so Im stuck.

                        Please, give me some help. As soon as I get this working, I will be happy to post my code, so it could help others.

                        Thanks.

                        • 9. Re: Security - Define dynamic Role in application
                          shane.bryzak

                          From what I can see everything looks ok. If you could post your code (even better if it will build) to JIRA I'd be happy to look at it for you. I've got Michael Neale (from the Drools team) probably coming over next Monday so if I can't work out what's wrong I'm sure he'd be happy to take a look at it.

                          • 10. Re: Security - Define dynamic Role in application
                            efabiano

                            Hi,
                            I had the same issue and I found the problem.

                            When security system is checking this

                            GrantedPermission(n : name -> (n == check.getName()), a : action -> (a
                            == check.getAction()))


                            only the first argument (n == check.getName()) is verified.


                            best regards

                            emerson fabiano

                            • 11. Re: Security - Define dynamic Role in application
                              markfoerstein

                              Well, not quite, but what was really happening isn't far from weird.

                              I managed to solve the problem by changing from this:

                              GrantedPermission(n : name -> (n == check.getName()), a : action -> (a == check.getAction()))


                              To this:
                              GrantedPermission(n : name -> (n.equals(check.getName())), a : action -> (a.equals(check.getAction())))


                              ...which is pretty much the same Shane told me to use in his first post. The thing is, if you use the '==' operator, that line won't evaluate to true, even if you indeed have into the working memory a GrantedPermission object with the name and action being checked.

                              When you use equals() instead of '==', it works. I really don't know why, since the drools 3 docs say in all examples that you should use '==' for string comparison. There isn't a single example using equals().

                              And that's all to it. No change in any of the previous code. Just that. Im happy now!

                              I've done some testing, and permission checking is working fine.

                              I will post all the code again, so it could help others.

                              Thanks Shane!.

                              And thanks Emerson. I'm from Brazil. Are you too? Maybe we can share some personal experiences on Seam app development. Let me know if you would like that.

                              • 12. Re: Security - Define dynamic Role in application
                                efabiano

                                Hi markfoertein,

                                Thanks for your response.
                                Yes, I´m from Brasil too (Curitiba). It would be very interesting share my experience using seam with you. Please send a mail to efabiano@mpsinf.com.br so we can start a talk.

                                tks
                                emerson fabiano