You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@shiro.apache.org by davison <da...@davisononline.org> on 2013/09/26 01:45:01 UTC

REST API permissions with anonymous usage

Hi everyone,

I've been struggling with what I think should be a trivial security issue
for
well over a week (which is almost as long as it took me to write this
simple app in its entireity!) Having spent quite a long time now going
through
the source code for Shiro and the docs, I think I've concluded that what I 
want to do isn't possible without re-implementing core parts of the 
framework.

So I hope someone can prove me wrong :)

My app is basically a simple server-side API for some REST clients,
principally
an HMTL5 front end.  I need some of the resources to be accessible to an
anonymous user for reading, but not for create/update/delete.

For example;

GET /api/foo (ok for UNauthenticated/anonymous user)
PUT /api/foo (requires admin role)

There are some secondary requirements which are also problematic, but they
are
moot if this one can't be solved easily.

Should be simple, but here's why I think this looks impossible..

To base my permissions on the HTTP method, I need to use the 'rest' filter
(HttpMethodPermissionFilter).  However, this has no way to specify that some
permissions are accessible for the anonymous user - because ANY set of
permissions requires an authenticated subject (see the various
DelegatingSubject.isPermitted() methods that all start with a requirement
that
current principals exist).  

I even created my own filter that would programatically "login" a user named
"anonymous" (defined in my realm) that could be given suitable permissions,
and
this works in isolation;

public class AnonymousRoleAuthenticationFilter extends OncePerRequestFilter
{
    @Override
    public void doFilterInternal(
            ServletRequest request, 
            ServletResponse response, 
            FilterChain chain) throws IOException, ServletException 
    {
        Subject subject = SecurityUtils.getSubject();
        if (subject.getPrincipals() == null ||
subject.getPrincipals().isEmpty()) 
        {
            subject.login(new UsernamePasswordToken("anonymous",
"anonymous"));
        }
        chain.doFilter(request, response);
    }
}


// realm config below...
user.admin=s3cr3t,admin
user.anonymous=anonymous,anonymous
role.admin=*
role.anonymous=*:read


But if this filter sits before any other (like authcBasic) that I use to
gather
credentials from an admin user, then the admin credentials will never be
requested because there is already an authenticated subject.. he just
doesn't have permission.  If I reverse the order of those filters, then I
will
incorrectly be asked for credentials even for resources that anonymous
should
be able to read - because without the subject, the framework can't decide if
permissions apply or not.  It's this that seems like a fundamental use case
is
not being catered for and which would need a lot of core framework
changes/overrides.

Can anyone help me out?

Best wishes,
Darren.




--
View this message in context: http://shiro-user.582556.n2.nabble.com/REST-API-permissions-with-anonymous-usage-tp7579176.html
Sent from the Shiro User mailing list archive at Nabble.com.

Re: REST API permissions with anonymous usage

Posted by Jared Bunting <ja...@peachjean.com>.
Another thought is that you could modify the rest filter at this point and
skip the AOP.  I think the key in your case was "permissive".
On Sep 26, 2013 6:06 PM, "Jared Bunting" <ja...@peachjean.com>
wrote:

> Are you sure that spring is instantiating the annotated beans?  (As
> opposed to your jaxrs provider) - that's probably the next thing that I
> would check.
> On Sep 26, 2013 4:25 PM, "davison" <da...@davisononline.org> wrote:
>
>> Thanks for the reply!
>>
>> I hadn't come across the "[permissive]" bit in the filter, but looking
>> through the code there it seems that it pretty much unconditionally allows
>> everything with this mapping.  I added it to my setup, and also added the
>> Spring beans to my context that according to the Shiro docs are required
>> to
>> make the annotations work.  But it just doesn't work for me.  Anonymous is
>> permitted to execute the methods protected with the annotation and no
>> password is requested.
>>
>> Here's my security context now:
>>
>>     <bean id="shiroFilter"
>> class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
>>         <property name="securityManager" ref="securityManager"/>
>>         <property name="filterChainDefinitions">
>>             <value>
>>                 /index.* = anon
>>                 /static/* = anon
>>                 /api/** = authcBasic[permissive]
>>             </value>
>>         </property>
>>     </bean>
>>
>>     <bean id="securityManager"
>> class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
>>         <property name="realm">
>>             <bean class="org.apache.shiro.realm.text.PropertiesRealm">
>>                 <property name="resourcePath"
>> value="classpath:shiro-realm.properties"></property>
>>             </bean>
>>         </property>
>>     </bean>
>>
>>     <bean id="lifecycleBeanPostProcessor"
>> class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
>>     <bean
>>
>> class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
>> depends-on="lifecycleBeanPostProcessor"/>
>>     <bean
>>
>> class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
>>         <property name="securityManager" ref="securityManager"/>
>>     </bean>
>>
>>
>> Is there something else I'm missing?
>>
>> Best wishes,
>>
>>
>>
>> --
>> View this message in context:
>> http://shiro-user.582556.n2.nabble.com/REST-API-permissions-with-anonymous-usage-tp7579176p7579186.html
>> Sent from the Shiro User mailing list archive at Nabble.com.
>>
>

Re: REST API permissions with anonymous usage

Posted by Jared Bunting <ja...@peachjean.com>.
Are you sure that spring is instantiating the annotated beans?  (As opposed
to your jaxrs provider) - that's probably the next thing that I would check.
On Sep 26, 2013 4:25 PM, "davison" <da...@davisononline.org> wrote:

> Thanks for the reply!
>
> I hadn't come across the "[permissive]" bit in the filter, but looking
> through the code there it seems that it pretty much unconditionally allows
> everything with this mapping.  I added it to my setup, and also added the
> Spring beans to my context that according to the Shiro docs are required to
> make the annotations work.  But it just doesn't work for me.  Anonymous is
> permitted to execute the methods protected with the annotation and no
> password is requested.
>
> Here's my security context now:
>
>     <bean id="shiroFilter"
> class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
>         <property name="securityManager" ref="securityManager"/>
>         <property name="filterChainDefinitions">
>             <value>
>                 /index.* = anon
>                 /static/* = anon
>                 /api/** = authcBasic[permissive]
>             </value>
>         </property>
>     </bean>
>
>     <bean id="securityManager"
> class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
>         <property name="realm">
>             <bean class="org.apache.shiro.realm.text.PropertiesRealm">
>                 <property name="resourcePath"
> value="classpath:shiro-realm.properties"></property>
>             </bean>
>         </property>
>     </bean>
>
>     <bean id="lifecycleBeanPostProcessor"
> class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
>     <bean
>
> class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
> depends-on="lifecycleBeanPostProcessor"/>
>     <bean
>
> class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
>         <property name="securityManager" ref="securityManager"/>
>     </bean>
>
>
> Is there something else I'm missing?
>
> Best wishes,
>
>
>
> --
> View this message in context:
> http://shiro-user.582556.n2.nabble.com/REST-API-permissions-with-anonymous-usage-tp7579176p7579186.html
> Sent from the Shiro User mailing list archive at Nabble.com.
>

Re: REST API permissions with anonymous usage

Posted by davison <da...@davisononline.org>.
Thanks for the reply!

I hadn't come across the "[permissive]" bit in the filter, but looking
through the code there it seems that it pretty much unconditionally allows
everything with this mapping.  I added it to my setup, and also added the
Spring beans to my context that according to the Shiro docs are required to
make the annotations work.  But it just doesn't work for me.  Anonymous is
permitted to execute the methods protected with the annotation and no
password is requested.

Here's my security context now:

    <bean id="shiroFilter"
class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="filterChainDefinitions">
            <value>
                /index.* = anon
                /static/* = anon
                /api/** = authcBasic[permissive]
            </value>
        </property>
    </bean>

    <bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm">
            <bean class="org.apache.shiro.realm.text.PropertiesRealm">
                <property name="resourcePath"
value="classpath:shiro-realm.properties"></property>
            </bean>
        </property>
    </bean>

    <bean id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
    <bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>


Is there something else I'm missing?

Best wishes,



--
View this message in context: http://shiro-user.582556.n2.nabble.com/REST-API-permissions-with-anonymous-usage-tp7579176p7579186.html
Sent from the Shiro User mailing list archive at Nabble.com.

Re: REST API permissions with anonymous usage

Posted by Jared Bunting <ja...@peachjean.com>.
I have similar requirements.  I'd be happy to share with you how I go about
it.  To start with, I use JAX-RS (1.0) for my REST API.

In my shiro.ini (or, in my case, Guice module), I have the filter setup:

/** = authcBasic[permissive]

Then, I use @RequiresPermission on my JAX-RS http methods.


@GET
@Produces("application/json")
public List<Item> listItems();

@POST
@Produces("application/json")
@Consumes("application/json")
@RequiresPermissions("item:create")
public Item createItem(Item item);

@GET
@Produces("application/json")
@Path("/{id}")
public Item getItem(@PathParam("id") String itemId);


This works fine for anonymous users - the two @GET methods don't require a
login, but the @POST method does.  The "permissive" is the key to anonymous
users here - and I'm not sure if that's documented. If not, that's
primarily because it sort of smells.  The BasicHttpAuthenticationFilter
extends AuthenticatingFilter, which says "An AuthenticationFilter that is
capable of automatically performing an authentication attempt based on the
incoming request.". That's cool, that's what we want.  Unfortunately,
AuthenticatingFilter extends AuthenticationFilter, which says "Base class
for all Filters that require the current user to be authenticated.".  We
don't want this for your (and my) usecase, and permissive basically removes
that guarantee.

Hope that helps.

Thanks,

Jared
On Sep 25, 2013 6:45 PM, "davison" <da...@davisononline.org> wrote:

> Hi everyone,
>
> I've been struggling with what I think should be a trivial security issue
> for
> well over a week (which is almost as long as it took me to write this
> simple app in its entireity!) Having spent quite a long time now going
> through
> the source code for Shiro and the docs, I think I've concluded that what I
> want to do isn't possible without re-implementing core parts of the
> framework.
>
> So I hope someone can prove me wrong :)
>
> My app is basically a simple server-side API for some REST clients,
> principally
> an HMTL5 front end.  I need some of the resources to be accessible to an
> anonymous user for reading, but not for create/update/delete.
>
> For example;
>
> GET /api/foo (ok for UNauthenticated/anonymous user)
> PUT /api/foo (requires admin role)
>
> There are some secondary requirements which are also problematic, but they
> are
> moot if this one can't be solved easily.
>
> Should be simple, but here's why I think this looks impossible..
>
> To base my permissions on the HTTP method, I need to use the 'rest' filter
> (HttpMethodPermissionFilter).  However, this has no way to specify that
> some
> permissions are accessible for the anonymous user - because ANY set of
> permissions requires an authenticated subject (see the various
> DelegatingSubject.isPermitted() methods that all start with a requirement
> that
> current principals exist).
>
> I even created my own filter that would programatically "login" a user
> named
> "anonymous" (defined in my realm) that could be given suitable permissions,
> and
> this works in isolation;
>
> public class AnonymousRoleAuthenticationFilter extends OncePerRequestFilter
> {
>     @Override
>     public void doFilterInternal(
>             ServletRequest request,
>             ServletResponse response,
>             FilterChain chain) throws IOException, ServletException
>     {
>         Subject subject = SecurityUtils.getSubject();
>         if (subject.getPrincipals() == null ||
> subject.getPrincipals().isEmpty())
>         {
>             subject.login(new UsernamePasswordToken("anonymous",
> "anonymous"));
>         }
>         chain.doFilter(request, response);
>     }
> }
>
>
> // realm config below...
> user.admin=s3cr3t,admin
> user.anonymous=anonymous,anonymous
> role.admin=*
> role.anonymous=*:read
>
>
> But if this filter sits before any other (like authcBasic) that I use to
> gather
> credentials from an admin user, then the admin credentials will never be
> requested because there is already an authenticated subject.. he just
> doesn't have permission.  If I reverse the order of those filters, then I
> will
> incorrectly be asked for credentials even for resources that anonymous
> should
> be able to read - because without the subject, the framework can't decide
> if
> permissions apply or not.  It's this that seems like a fundamental use case
> is
> not being catered for and which would need a lot of core framework
> changes/overrides.
>
> Can anyone help me out?
>
> Best wishes,
> Darren.
>
>
>
>
> --
> View this message in context:
> http://shiro-user.582556.n2.nabble.com/REST-API-permissions-with-anonymous-usage-tp7579176.html
> Sent from the Shiro User mailing list archive at Nabble.com.
>

Re: REST API permissions with anonymous usage

Posted by Stephen McCants <st...@hcs.us.com>.
Hi Darren,

I had a couple of thoughts.  I've needed something similar and achieved 
it by implementing a custom rest filter and using that.

Instead of logging anonymous users in, I've left them anonymous and in 
the custom rest filter checked the HTTP Method they are using and then 
as appropriate their role/permission.

For example (in pseudo code):

if(HTTP Method == get)
     return okay;
else
     if(subject.isAuthenitcated() == false)
         return not okay;
     if(subject.hasRole(role))
         return okay;
return not okay;

That lacks the flexibility of the default rest filter but that 
flexibility can be achieved (more or less) by doing something like 
Daniel describes in the main part of 
https://issues.apache.org/jira/browse/SHIRO-459.  You can use the 
arguments to a filter to define HTTP Method and required 
role/permission.  My custom filter recognizes an "anon" that lets anyone 
access through that method.

My shiro.ini looks like:

rest = com.hcs.webtl.shiro.filter.RESTAuthorizationFilter
/users/** = noSessionCreation, rest[POST-anon,PUT-anon,DELETE-admin]

Rest is my custom filter (not the default rest filter) and allows 
anonymous POST and PUT, but you must have admin role/permission to 
execute a DELETE.  GET is forbidden for everyone.

Hope that helps!

Sincerely,
Stephen McCants

On 9/25/2013 6:45 PM, davison wrote:
> Hi everyone,
>
> I've been struggling with what I think should be a trivial security issue
> for
> well over a week (which is almost as long as it took me to write this
> simple app in its entireity!) Having spent quite a long time now going
> through
> the source code for Shiro and the docs, I think I've concluded that what I
> want to do isn't possible without re-implementing core parts of the
> framework.
>
> So I hope someone can prove me wrong :)
>
> My app is basically a simple server-side API for some REST clients,
> principally
> an HMTL5 front end.  I need some of the resources to be accessible to an
> anonymous user for reading, but not for create/update/delete.
>
> For example;
>
> GET /api/foo (ok for UNauthenticated/anonymous user)
> PUT /api/foo (requires admin role)
>
> There are some secondary requirements which are also problematic, but they
> are
> moot if this one can't be solved easily.
>
> Should be simple, but here's why I think this looks impossible..
>
> To base my permissions on the HTTP method, I need to use the 'rest' filter
> (HttpMethodPermissionFilter).  However, this has no way to specify that some
> permissions are accessible for the anonymous user - because ANY set of
> permissions requires an authenticated subject (see the various
> DelegatingSubject.isPermitted() methods that all start with a requirement
> that
> current principals exist).
>
> I even created my own filter that would programatically "login" a user named
> "anonymous" (defined in my realm) that could be given suitable permissions,
> and
> this works in isolation;
>
> public class AnonymousRoleAuthenticationFilter extends OncePerRequestFilter
> {
>      @Override
>      public void doFilterInternal(
>              ServletRequest request,
>              ServletResponse response,
>              FilterChain chain) throws IOException, ServletException
>      {
>          Subject subject = SecurityUtils.getSubject();
>          if (subject.getPrincipals() == null ||
> subject.getPrincipals().isEmpty())
>          {
>              subject.login(new UsernamePasswordToken("anonymous",
> "anonymous"));
>          }
>          chain.doFilter(request, response);
>      }
> }
>
>
> // realm config below...
> user.admin=s3cr3t,admin
> user.anonymous=anonymous,anonymous
> role.admin=*
> role.anonymous=*:read
>
>
> But if this filter sits before any other (like authcBasic) that I use to
> gather
> credentials from an admin user, then the admin credentials will never be
> requested because there is already an authenticated subject.. he just
> doesn't have permission.  If I reverse the order of those filters, then I
> will
> incorrectly be asked for credentials even for resources that anonymous
> should
> be able to read - because without the subject, the framework can't decide if
> permissions apply or not.  It's this that seems like a fundamental use case
> is
> not being catered for and which would need a lot of core framework
> changes/overrides.
>
> Can anyone help me out?
>
> Best wishes,
> Darren.
>
>
>
>
> --
> View this message in context: http://shiro-user.582556.n2.nabble.com/REST-API-permissions-with-anonymous-usage-tp7579176.html
> Sent from the Shiro User mailing list archive at Nabble.com.


-- 
Stephen McCants
Senior Software Engineer
Healthcare Control Systems
1-877-877-8795 x116