Hi,
I want to implement discretionary access control in an app running in
Tomcat - i.e. access controls on URLs served by Tomcat can be changed by
users. I expect to have a 1M resources each with its own ACL. Some
resources have 'public' access. No authentication should be required to
access them. Access to some resources is constrained and does require
authentication. The same resource may be access controlled one minute
and public the next. The URL of a resource may not change when its ACL
changes.
I have been approaching this by trying to use the built in Tomcat
authentication and creating my own authorization filter. The problem
I'm hitting is that getRemoteUser is returning null. I believe the
reason it is returning null is that I have no <auth-constraint> element
in my security constraint:
<security-constraint>
<display-name>Authenticate</display-name>
<web-resource-collection>
<web-resource-name>resources</web-resource-name>
<description></description>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
</security-constraint>
Despite the fact that I have arranged for the incoming request to have
an Authorization header (I send my own 401 responses), Tomcat does not
process it unless the there is an <auth-constraint> that applies to the
requested resource. This is consistent with what the servlet spec says:
[[An authorization constraint establishes a requirement for
authentication ...]]
I want to have no authorization constraint because some resources have
public access and no authentication is required for access to those
resources.
I have tried using getRequestDispatcher(...).forward(...) to forward
requests for resources that require authentication to a different URL
and defining a different security constraint for those URLs:
<security-constraint>
<display-name>Authenticate</display-name>
<web-resource-collection>
<web-resource-name>authenticated resources</web-resource-name>
<description></description>
<url-pattern>/authenticated/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>REGISTERED</role-name>
</auth-constraint>
</security-constraint>
getRemoteUser() still returns null. .forward() does not seem to be
subject to security checks. I have found nothing in the Servlet spec
that tells me what the behaviour should be.
I have three questions:
1) Is there a way I can programatically cause the authentication check?
[Currently I send my own 401 response if authentication is required.
If a call to getRemoteUser() were to cause the processing of a present
Authorization header, if that processing had not been done already,
that would support my approach. I don't yet know if/how I could compute
an appropriate challenge for the 401 responses I generate for digest
authentication, which I would want to use preference to Basic
authentication.]
2) Is there another way to implement discretionary access control, other
than implementing my own authentication mechanism? Has anyone else
solved this problem?
3) Is Tomcat's behaviour 'correct'? There may be good reason for the
current interpretation of the spec, but from my point of view allowing
.forward() to circumvent declared security constraints is questionable.
I am using Tomcat 6.0.29. Sorry its such a long winded mail.
Brian