You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomee.apache.org by Marcin Kwapisz <mk...@zsk.p.lodz.pl> on 2009/01/08 02:17:52 UTC

Cannot override security annotations

Hi,
I cannot override security annotations in OpenEJB3 (the same concerns OpenEJB3.1). In the following example I cannot call createStudent with role SysAdmin. When I remove security annotation I can call createStudent only with role SysAdmin. That means that I can set method permissions in ejb-jar.xml but not override them.

--LOG--
INFO: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.ejb.EJBAccessException : Unauthorized Access by Principal Denied
--LOG--

Is something wrong with my code or with OpenEjb? Can you help me to solve this problem?

I use OpenEjb for unit testing. Application is deployed on Glassfish server and method permissions are correctly set there.

--EJB--
@RolesAllowed({"StudentAdmin"})
    public void createStudent....

--sun-ejb.jar--
<assembly-descriptor>
        <security-role>
            <role-name>SysAdmin</role-name>
        </security-role>
        <method-permission>
            <role-name>SysAdmin</role-name>
            <method>
                <ejb-name>StudentEndpoint</ejb-name>
                <method-name>createStudent</method-name>
            </method>
        </method-permission>


Thanks in advance
-- 
Marcin Kwapisz
Division of Computer Networks
Technical Univeristy of Lodz, Poland



Re: Cannot override security annotations

Posted by David Blevins <da...@visi.com>.
On Jan 12, 2009, at 1:09 AM, Marcin Kwapisz wrote:

> Hi,
>
>>> I think this property should be set in System, not in  
>>> InitialContext.
>>>
>>> System
>>> .setProperty("log4j.category.OpenEJB.startup.attributes","DEBUG");
>>
>> I'm not sure if you're stating a preference or reporting that setting
>> via InitialContext didn't work.  Both approaches should work in the
>> current release.
>
> [Marcin Kwapisz]
> p.setProperty("log4j.category.OpenEJB.startup.attributes", "DEBUG");
> ctx = new InitialContext(p);
>
> It does not work. That’s why I have set that value as a system  
> property.

Thanks for reporting that.  I thought I had testing it as both.  Looks  
like the logging initialization was happening just before the  
InitialContext properties were getting seen.  I've fixed things up so  
that logging is reinitialized and the logging overrides in the  
InitialContext properties get picked up as they should.

The fix should be in the 3.1.1-SNAPSHOT jars.

>> We achieve this by translating the annotation into their *exact* xml
>> equivalent and processing them together as if everything had been
>> declared in xml.
>
> [Marcin Kwapisz]
> Thanks David for very extensive and exhaustive explanation. More  
> precise method description helps to override the security  
> annotation. Glassfish accepts such deployment descriptor also so  
> there is no problem now.

No problem, it needed to be documented.  Just need to get that text  
into the wiki.

> I am curious how other application servers process security  
> annotations and if OpenEJB is distinguished by that.

It seems to be the case as far as I can tell.  The same rules apply to  
the way we process transaction annotations and the new singleton @Lock  
annotation which have identical rules for annotation inheritance.

> I take as a common rule that any value set in a deployment  
> descriptor overrides an annotation value. I think, that rule comes  
> from JEE specification so I was a little surprised when it did not  
> work in OpenEJB.

Right, it does in concept, but the actual semantics of overriding are  
not covered in detail.  The goal in EJB 3.0 was to create annotations  
that could express what the xml could do in the goal of replacing the  
xml.  In the case of the transaction and security annotations, the  
annotations actually have *more* expressive power than the equivalent  
xml as the annotations can be applied to super classes resulting in  
pretty complex layering that you can't do the same way in xml.  We  
made it possible in our impl, but as far as I know were the only one  
to do so.

-David


RE: Cannot override security annotations

Posted by Marcin Kwapisz <mk...@zsk.p.lodz.pl>.
Hi,

> > I think this property should be set in System, not in InitialContext.
> >
> > System
> > .setProperty("log4j.category.OpenEJB.startup.attributes","DEBUG");
> 
> I'm not sure if you're stating a preference or reporting that setting
> via InitialContext didn't work.  Both approaches should work in the
> current release.

[Marcin Kwapisz] 
p.setProperty("log4j.category.OpenEJB.startup.attributes", "DEBUG");
ctx = new InitialContext(p);

It does not work. That’s why I have set that value as a system property.

> We achieve this by translating the annotation into their *exact* xml
> equivalent and processing them together as if everything had been
> declared in xml.

[Marcin Kwapisz] 
Thanks David for very extensive and exhaustive explanation. More precise method description helps to override the security annotation. Glassfish accepts such deployment descriptor also so there is no problem now. I am curious how other application servers process security annotations and if OpenEJB is distinguished by that.
I take as a common rule that any value set in a deployment descriptor overrides an annotation value. I think, that rule comes from JEE specification so I was a little surprised when it did not work in OpenEJB.

Best regards
-- 
Marcin Kwapisz
Division of Computer Networks
Technical Univeristy of Lodz, Poland




Re: Cannot override security annotations

Posted by David Blevins <da...@visi.com>.
On Jan 8, 2009, at 12:30 AM, Marcin Kwapisz wrote:

> I think this property should be set in System, not in InitialContext.
>
> System
> .setProperty("log4j.category.OpenEJB.startup.attributes","DEBUG");

I'm not sure if you're stating a preference or reporting that setting  
via InitialContext didn't work.  Both approaches should work in the  
current release.

> Scenario 1.
> --EJB--
> @RolesAllowed({"StudentAdmin"})
>    public void createStudent
> --EJB--
>
> --ejb-jar.xml--
> <assembly-descriptor>
>        <security-role>
>            <role-name>SysAdmin</role-name>
>        </security-role>
>        <method-permission>
>            <role-name>SysAdmin</role-name>
>            <method>
>                <ejb-name>StudentEndpoint</ejb-name>
>                <method-name>createStudent</method-name>
>            </method>
>        </method-permission>
> --ejb-jar.xml--
>
> DEBUG - Security Attribute: public void  
> pl 
> .zsk 
> .sos 
> .ejb 
> .endpoint 
> .StudentEndpointBean 
> .createStudent(pl.zsk.sos.dto.StudentDTO,pl.zsk.sos.dto.AccountDTO)  
> throws pl.zsk.sos.exception.StudentException -- StudentEndpoint :  
> * : pl.zsk.sos.ejb.endpoint.StudentEndpointBean :  
> createStudent(pl.zsk.sos.dto.StudentDTO, pl.zsk.sos.dto.AccountDTO)  
> StudentAdmin
>
> Scenario 2.
> ejb-jar.xml unchanged
> RolesAllowed annotation is commented in createStudent as follows
> --EJB--
> //@RolesAllowed({"StudentAdmin"})
>    public void createStudent
> --EJB--
>
> DEBUG - Security Attribute: public void  
> pl 
> .zsk 
> .sos 
> .ejb 
> .endpoint 
> .StudentEndpointBean 
> .createStudent(pl.zsk.sos.dto.StudentDTO,pl.zsk.sos.dto.AccountDTO)  
> throws pl.zsk.sos.exception.StudentException -- StudentEndpoint :  
> * : * : createStudent(*) SysAdmin

Ok, it's all coming back to me.  I recall what is going on here.  Two  
things first:

   1.  If you modify your override to be more specific (i.e. include  
the exact method signature) it will work:
        <method-permission>
            <role-name>SysAdmin</role-name>
            <method>
                <ejb-name>StudentEndpoint</ejb-name>
                <method-name>createStudent</method-name>
                <method-params>
                   <method-param>pl.zsk.sos.dto.StudentDTO</method- 
param>
                   <method-param>pl.zsk.sos.dto.AccountDTO</method- 
param>
                </method-params>
            </method>
        </method-permission>

   2.  If you'd rather not have to do that and want it to work like  
Glassfish we can totally do that -- it's not there now but we can add  
it.


-- FOR THE CURIOUS ---

That said here is the logic behind how it works.  Say you have an EJB  
like this (same applies to transaction annotations):

     @RolesAllowed("ADMIN")
     public static class ColorBean implements ColorLocal, ColorRemote {

         @RolesAllowed("USER")
         public void color(){}

         @RolesAllowed("MANAGER")
         public void color(Object o){}

         public void color(String s){}
         public void color(Boolean b){}
         public void color(Integer i){}
     }

Which yields this:

    - color() : USER
    - color(Object o) : MANAGER
    - color(String s) : ADMIN
    - color(Boolean b) : ADMIN
    - color(Integer i) : ADMIN

Now say you want to override just that usage of ADMIN at the class  
level to use say SUPERUSER instead and have it work *exactly* as it  
would as if you had used @RolesAllowed("SUPERUSER") on the class  
instead of @RolesAllowed("SUPERUSER").  With OpenEJB, you simply do  
this:

        <method-permission>
            <role-name>SUPERUSER</role-name>
             <method>
                 <ejb-name>ColorBean</ejb-name>
             </method>
        </method-permission>

Then we will apply the override to the class level just as if you had  
used @RolesAllowed("SUPERUSER") which yields:

    - color() : USER
    - color(Object o) : MANAGER
    - color(String s) : SUPERUSER
    - color(Boolean b) : SUPERUSER
    - color(Integer i) : SUPERUSER

We achieve this by translating the annotation into their *exact* xml  
equivalent and processing them together as if everything had been  
declared in xml.  So this annotation data ...

     @RolesAllowed("ADMIN")
     public static class ColorBean implements ColorLocal, ColorRemote {

         @RolesAllowed("USER")
         public void color(){}

         @RolesAllowed("MANAGER")
         public void color(Object o){}

         public void color(String s){}
         public void color(Boolean b){}
         public void color(Integer i){}
     }

... becomes this xml (added ids for descriptive purposes, they have no  
affect on anything)...

        <method-permission id="A">
            <role-name>ADMIN</role-name>
             <method>
                 <ejb-name>ColorBean</ejb-name>
             </method>
        </method-permission>
        <method-permission id="B">
            <role-name>USER</role-name>
             <method>
                 <ejb-name>ColorBean</ejb-name>
                 <method-name>color</method-name>
                 <method-params/>
             </method>
        </method-permission>
        <method-permission id="C">
            <role-name>MANAGER</role-name>
             <method>
                 <ejb-name>ColorBean</ejb-name>
                 <method-name>color</method-name>
                 <method-params>
                    <method-param>java.lang.Object</method-param>
                 </method-params>
             </method>
        </method-permission>

The rules for processing <method-permission> or <container- 
transaction> attributes in xml dictate that more specific declarations  
win over the more general ones.  So for the method "color(Object o)"  
the declaration A, which applies to the bean, looses out to  
declaration C, which applies to an exact method of the bean.  So (the  
part you've been waiting for), if say this xml declaration was added  
(notice no params specified).

        <method-permission id="D">
            <role-name>SUPERUSER</role-name>
             <method>
                 <ejb-name>ColorBean</ejb-name>
                 <method-name>color</method-name>
             </method>
        </method-permission>

This style declaration per spec rules would be considered more  
specific declaration A as it specifies the method name but still less  
specific than declarations B and C as it does not specify the exact  
method arguments.  This is why when you added the xml declaration it  
did not affect methods which had more specific declarations in xml  
(albeit automatically created from annotation data rather than typed  
in by you).  This is something the spec does not really go into detail  
on, unfortunately, though I have brought it up as a candidate for  
clarification.  I should really do that again before EJB 3.1 goes out  
the door.


-- FOR THE REALLY CURIOUS ---

That said, there are some very interesting rules for annotation  
inheritance in the spec for which there is no clear xml overriding  
support.  Read on only if you are interested, otherwise feel free to  
stop here :)

Given this inheritance chain (switching to transaction annotations for  
simplicity).

     @TransactionAttribute(MANDATORY)
     public static class Color implements ColorLocal, ColorRemote {

         @TransactionAttribute(NEVER)
         public void color(){}

         @TransactionAttribute(REQUIRES_NEW)
         public void color(Object o){}

         public void color(String s){}
         public void color(Boolean b){}
         public void color(Integer i){}
     }

     @Stateless
     public static class Red extends Color {

         public void color(Object o){super.color(o);}

         @TransactionAttribute(REQUIRES_NEW)
         public void red(){}

         public void red(Object o){}
         public void red(String s){}

     }

Per spec rules, the resulting attributes for the methods of the Red  
ejb will be as follows:

    - color() : NEVER
    - color(String s) : MANDATORY
    - color(Boolean b) : MANDATORY
    - color(Integer i) : MANDATORY
    - color(Object o) : REQUIRED
    - red() : REQUIRES_NEW
    - red(Object o) : REQUIRED
    - red(String s) : REQUIRED

This methods "red(Object)" and "red(String)" are tx REQUIRED because  
the usage of @TransactionAttribute(MANDATORY) in the parent class  
Color is *not* inherited by Red. Additionally since Red did not  
specify a class-level @TransactionAttribute annotation the default of  
REQUIRED is use which affects only the methods which are declared in  
the Red class -- the methods of the parent class Color still use  
@TransactionAttribute(MANDATORY) unless otherwise specified.  The  
method "color()" was annotated in the parent class as  
@TransactionAttribute(NEVER), but has been overridden in the child  
class Red where an @TransactionAttribute annotation was not explicitly  
supplied, therefor the tx attribute of "color()" becomes that of the  
Red class and is now @TransactionAttribute(REQUIRED) and no longer  
@TransactionAttribute(NEVER).

Since annotations can be declared in a "layered cake" style with each  
parent class in the inheritance chain having the ability to specify  
its own class-level annotation which affects only it's methods, you  
need some way to do this in xml.  Unfortunately, in this regard there  
was not enough standard xml elements to do so.  We didn't stop there  
of course and added new <class> level element that allows you target  
your override to a specific class just as you can with an annotation.   
It has also been proposed in EJB 3.1 -- one more thing I need to  
follow up on before the spec goes final.

It works like this.  Say you want to override the default transaction  
attribute for the Red class to be MANDATORY and the default for the  
class Color to be REQUIRED just as if you had applied  
@TransactionAttribute(MANDATORY) to the Red class and  
@TransactionAttribute(REQUIRED) to the Color class.  You could do so  
with an xml declaration like this:

         <container-transaction>
             <trans-attribute>Required</trans-attribute>
             <method>
                 <ejb-name>Red</ejb-name>
                 <class>org.superbiz.Color</class>  <!-- not in EJB  
3.0 schema -->
             </method>
         </container-transaction>
         <container-transaction>
             <trans-attribute>Mandatory</trans-attribute>
             <method>
                 <ejb-name>Red</ejb-name>
                 <class>org.superbiz.Red</class>  <!-- not in EJB 3.0  
schema -->
             </method>
         </container-transaction>

That would give you these transaction attributes:

    - color() : NEVER
    - color(String s) : REQUIRED
    - color(Boolean b) : REQUIRED
    - color(Integer i) : REQUIRED
    - color(Object o) : MANDATORY
    - red() : REQUIRES_NEW
    - red(Object o) : MANDATORY
    - red(String s) : MANDATORY

An alternative proposal would be to allow the existing <method-inf> to  
specify the class name rather than adding a new <class> element.  The  
long and short of it is that we do support xml and xml overrides with  
the exact same expressive power as annotations have.  Definitely  
something we'd like to see in the spec.

Congrats to those who made it this far :)  Feel free to as questions  
if you have them.

-David


RE: Cannot override security annotations

Posted by Marcin Kwapisz <mk...@zsk.p.lodz.pl>.
Hi David,

I think this property should be set in System, not in InitialContext.

System.setProperty("log4j.category.OpenEJB.startup.attributes","DEBUG");

Scenario 1.
--EJB--
@RolesAllowed({"StudentAdmin"})
    public void createStudent
--EJB--

--ejb-jar.xml--
<assembly-descriptor>
        <security-role>
            <role-name>SysAdmin</role-name>
        </security-role>
        <method-permission>
            <role-name>SysAdmin</role-name>
            <method>
                <ejb-name>StudentEndpoint</ejb-name>
                <method-name>createStudent</method-name>
            </method>
        </method-permission>
--ejb-jar.xml--

DEBUG - Security Attribute: public void pl.zsk.sos.ejb.endpoint.StudentEndpointBean.createStudent(pl.zsk.sos.dto.StudentDTO,pl.zsk.sos.dto.AccountDTO) throws pl.zsk.sos.exception.StudentException -- StudentEndpoint : * : pl.zsk.sos.ejb.endpoint.StudentEndpointBean : createStudent(pl.zsk.sos.dto.StudentDTO, pl.zsk.sos.dto.AccountDTO) StudentAdmin  

Scenario 2.
ejb-jar.xml unchanged
RolesAllowed annotation is commented in createStudent as follows
--EJB--
//@RolesAllowed({"StudentAdmin"})
    public void createStudent
--EJB--

DEBUG - Security Attribute: public void pl.zsk.sos.ejb.endpoint.StudentEndpointBean.createStudent(pl.zsk.sos.dto.StudentDTO,pl.zsk.sos.dto.AccountDTO) throws pl.zsk.sos.exception.StudentException -- StudentEndpoint : * : * : createStudent(*) SysAdmin

Regards
-- 
Marcin Kwapisz
Division of Computer Networks
Technical Univeristy of Lodz, Poland


Re: Cannot override security annotations

Posted by David Blevins <da...@visi.com>.
On Jan 7, 2009, at 5:17 PM, Marcin Kwapisz wrote:

> Hi,
> I cannot override security annotations in OpenEJB3 (the same  
> concerns OpenEJB3.1). In the following example I cannot call  
> createStudent with role SysAdmin. When I remove security annotation  
> I can call createStudent only with role SysAdmin. That means that I  
> can set method permissions in ejb-jar.xml but not override them.
>
> --LOG--
> INFO: The transaction has been marked rollback only because the bean  
> encountered a non-application  
> exception :javax.ejb.EJBAccessException : Unauthorized Access by  
> Principal Denied
> --LOG--
>
> Is something wrong with my code or with OpenEjb? Can you help me to  
> solve this problem?
>
> I use OpenEjb for unit testing. Application is deployed on Glassfish  
> server and method permissions are correctly set there.
>
> --EJB--
> @RolesAllowed({"StudentAdmin"})
>    public void createStudent....
>
> --sun-ejb.jar--
> <assembly-descriptor>
>        <security-role>
>            <role-name>SysAdmin</role-name>
>        </security-role>
>        <method-permission>
>            <role-name>SysAdmin</role-name>
>            <method>
>                <ejb-name>StudentEndpoint</ejb-name>
>                <method-name>createStudent</method-name>
>            </method>
>        </method-permission>

Hi Marcin,

Just as a quick way to verify there isn't something else going on,  
paste the debug info for your security attributes.  In OpenEJB 3.1  
there's a new log category where we print all of the method attributes  
on debug.

In your test case you can turn it on by adding this to your  
InitialContext properties:

   properties.setProperty("log4j.category.OpenEJB.startup.attributes",  
"DEBUG");

Then you should see some log lines in the output that start with  
"Security Attribute: ...".  Paste all of them or the ones that apply  
to StudentEndpoint.  Should help us see exactly what the attributes  
are after all the overriding, etc.

-David