You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Taavi Tiirik <tt...@ibs.ee> on 2001/10/17 22:47:53 UTC

blocking access using filter

Hey,

I need more sophisticated access control for certain
documents than JDBCRealm provides. These documents
reside in separate directory tree and they are
served by standalone tomcat 4. I have mapped an
'access control filter' to listen to all requests to
this directory. Filter should behave like this:

1. If user is not logged in or if the session has
timed out then it should open login page and after
successful login it should try to access the very
same request (ie. the same document).

/ I am using getRemoteUser() to determine if user
is logged in. /

2. Filter performs a database lookup and decides if
given user can access this document.

/ This step is easy and I can live with extra
overhead needed for database query. /

3. If user does not have rights to access this
document then filter should send error 404 (no such
document).

/ This can be achieved using:
  response.sendError( HttpServletResponse.SC_NOT_FOUND ); /

4. Do nothing... ie. user will get this document.

/ 'do nothing' actually means calling next filter in
  chain like this:
  chain.doFilter( request, response ); /


I have figured out all steps but the very first one.
What should I do in filter in order to make tomcat
to use it's standard authentication mechanism
(JDBCRealm, form based login in my case) so
the user could log in and still get required document?

Please, any help is appreciated. I will happily donate
this filter back to the group if I get it working and
if there is interest.

thanks in advance,
Taavi


RE: blocking access using filter

Posted by Taavi Tiirik <ta...@ibs.ee>.
Thanks, Craig!

> > 1. If user is not logged in or if the session has
> > timed out then it should open login page and after
> > successful login it should try to access the very
> > same request (ie. the same document).

> I don't quite see why you need to modify the standard
> form-based login mechanisms, either.  Can't you just use
> the standard form based login for triggering authentication?

No, I did not want to modify standard login mechanism by
any means :-). I simply had this (wrong) impression that
filters get called before checking security constraints.
How stupid of me :-). Creating security constraint like
you suggested covered the first step and now I have this
filter purring like a kitten.

Just in case anybody is interested... this is what I did.
doFilter looks like this:

public void doFilter (
	ServletRequest request,
	ServletResponse response,
	FilterChain chain
)
throws IOException, ServletException
{
	HttpServletRequest httpRequest = null;
	HttpServletResponse httpResponse = null;

	if( request instanceof HttpServletRequest )
		httpRequest = (HttpServletRequest)request;

	if( response instanceof HttpServletResponse )
		httpResponse = (HttpServletResponse)response;

	boolean authorized = false;
	String user = httpRequest.getRemoteUser();

	// Is this really necessary? Could it be that requests
	// other than HttpServletRequest are passed to
	// this filter? Can they be harmful by any means?
	// Or should I let them through?
	if( httpRequest == null || httpResponse == null || user == null
){
		httpResponse.sendError( HttpServletResponse.SC_NOT_FOUND
);
		return;
	}

	try {
		// At this point we have user name in 'user' and request
URI
		// in 'requestURI'. Make sure that this user has rights
to
		// get this document and set authorized to true, if
(s)he has.

		authorized = ...

	} catch( Exception e ){
	}

	if( !authorized ){
		httpResponse.sendError( HttpServletResponse.SC_NOT_FOUND
);
		return;
	}

	// Pass control on to the next filter
	chain.doFilter( request, response );
}

with best wishes,
Taavi



Re: blocking access using filter

Posted by Dmitri Colebatch <di...@bigpond.net.au>.
On Wed, 17 Oct 2001, Craig R. McClanahan wrote:

> The "get new users" remark was mine.  It's always fun to think outside the
> box, even if the ideas are not always practical :-).

(o:  yes... I recall having a chuckle...

> In servlet 2.3 there is a new <run-as> feature that defines what identity
> to use for executing EJBs for unauthenticated web users (from the
> container's perspective, which is what we would be talking about here).
> This is configured on a per-servlet basis (i.e. it's nested inside the
> <servlet> definition).
> 
> If that is not good enough for your application requirements, then I'm
> afraid you're back to needing to use container-managed security.

bugger )o:  

thanks for your input here... much appreciated.

cheers
dim


Re: blocking access using filter

Posted by "Craig R. McClanahan" <cr...@apache.org>.
On Thu, 18 Oct 2001, Dmitri Colebatch wrote:

> Date: Thu, 18 Oct 2001 10:28:05 +1000
> From: Dmitri Colebatch <di...@bigpond.net.au>
> Reply-To: tomcat-user@jakarta.apache.org
> To: tomcat-user@jakarta.apache.org
> Subject: Re: blocking access using filter
>
> On Wed, 17 Oct 2001, Craig R. McClanahan wrote:
> >
> > Personally, I don't really care about bookmarking the login page.  I train
> > my users to just ask for the page they want -- if login is necessary, the
> > server will "pop up" the login page (just like BASIC authentication
> > works).
>
> was the "get new users" suggestion for people having problems with users
> bookmarking the login page yours or someone elses?  (o:  Educating users
> (as I'm sure you know) can be very hard work... and that's assuming that
> you've educated your management first (o:

The "get new users" remark was mine.  It's always fun to think outside the
box, even if the ideas are not always practical :-).

Actually, the simplest way to do this is to use BASIC authentication
during alpha and beta testing, and then switch to form-based just before
production.  Then, the habits that they learned are already the correct
ones.

>
> > But it would be feasible to completely dispense with container-managed
> > security (by having no <security-constraint> or <login-config> element in
> > your web.xml file, and do *all* of it in a Filter.  As you point out, then
> > you could make this behave any way you want, but still be portable to any
> > container that compiles with Servlet 2.3.
>
> yeah, I suppose thats what I was suggesting...
>
> > Basically, you implement a class that extends
> > javax.servlet.http.HttpServletRequestWrapper.  Then, in your filter, you
> > do something like this in the doFilter() method:
> >
> >   MyWrapper wrapper = new MyWrapper(request);  // Pass the original request
> >   chain.doFilter(wrapper, response);
> >
> > Now, when your servlet calls getRemoteUser(), it sees the value provided
> > by your wrapper class rather than the one set by Tomcat.  But, the beauty
> > of this is, it doesn't need to know that something special is going on.
>
> thanks, hadn't even thought that far ahead... but thinking ahead
> now... this essentially means that we're not using a realm right?

Yes, in the sense that you would not be configuring a <Realm> entry in
server.xml.  You would still want to be able to configure how your Filter
looks up users somehow, but that would most easily be done through
initialization parameters to the Filter (in web.xml).

>  or
> wrong?  (not sure)... reason I ask that is what does this mean for
> security context propagation into an ejb container?
>

In servlet 2.3 there is a new <run-as> feature that defines what identity
to use for executing EJBs for unauthenticated web users (from the
container's perspective, which is what we would be talking about here).
This is configured on a per-servlet basis (i.e. it's nested inside the
<servlet> definition).

If that is not good enough for your application requirements, then I'm
afraid you're back to needing to use container-managed security.

> cheesr
> dim
>
>

Craig



Re: blocking access using filter

Posted by Dmitri Colebatch <di...@bigpond.net.au>.
On Wed, 17 Oct 2001, Craig R. McClanahan wrote:
> 
> Personally, I don't really care about bookmarking the login page.  I train
> my users to just ask for the page they want -- if login is necessary, the
> server will "pop up" the login page (just like BASIC authentication
> works).

was the "get new users" suggestion for people having problems with users
bookmarking the login page yours or someone elses?  (o:  Educating users
(as I'm sure you know) can be very hard work... and that's assuming that
you've educated your management first (o:

> But it would be feasible to completely dispense with container-managed
> security (by having no <security-constraint> or <login-config> element in
> your web.xml file, and do *all* of it in a Filter.  As you point out, then
> you could make this behave any way you want, but still be portable to any
> container that compiles with Servlet 2.3.

yeah, I suppose thats what I was suggesting...

> Basically, you implement a class that extends
> javax.servlet.http.HttpServletRequestWrapper.  Then, in your filter, you
> do something like this in the doFilter() method:
> 
>   MyWrapper wrapper = new MyWrapper(request);  // Pass the original request
>   chain.doFilter(wrapper, response);
> 
> Now, when your servlet calls getRemoteUser(), it sees the value provided
> by your wrapper class rather than the one set by Tomcat.  But, the beauty
> of this is, it doesn't need to know that something special is going on.

thanks, hadn't even thought that far ahead... but thinking ahead
now... this essentially means that we're not using a realm right?  or
wrong?  (not sure)... reason I ask that is what does this mean for
security context propagation into an ejb container?  

cheesr
dim


Re: blocking access using filter

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Thu, 18 Oct 2001, Dmitri Colebatch wrote:

> Date: Thu, 18 Oct 2001 10:06:41 +1000
> From: Dmitri Colebatch <di...@bigpond.net.au>
> Reply-To: tomcat-user@jakarta.apache.org
> To: tomcat-user@jakarta.apache.org
> Subject: Re: blocking access using filter
>
> On Wed, 17 Oct 2001, Craig R. McClanahan wrote:
>
> > I don't quite see why you need to modify the standard form-based login
> > mechanisms, either.  Can't you just use the standard form based login for
> > triggering authentication?  This would be as simple as a security
>
> ... snip ...
>
> >
> > Form based login already does the rest of what your step (1) includes - it
> > remembers the page that the user asks for (if it switches them to the
> > login screen), and automatically "replays" it once authentication is done.
>
> I've been thinking of something similar and would love to see something
> worked on in for the community (I'd be more than willing to put time
> in).  The advantages I see of this approach are:
>   1. Users can bookmark the login page (which please correct me if I am
> wrong, they cant in form based login)
>   2. It is easier to customise, as customisations will onyl be of a
> filter, and hence there wont be any spec compliance issues.
>
> are they valid arguments?
>

Personally, I don't really care about bookmarking the login page.  I train
my users to just ask for the page they want -- if login is necessary, the
server will "pop up" the login page (just like BASIC authentication
works).

But it would be feasible to completely dispense with container-managed
security (by having no <security-constraint> or <login-config> element in
your web.xml file, and do *all* of it in a Filter.  As you point out, then
you could make this behave any way you want, but still be portable to any
container that compiles with Servlet 2.3.

The only "magic" part of this would be, how do you make it possible for
your servlets and JSP pages to depend on request.getRemoteUser() and
request.isUserInRole() anyway?  The answer to this is that a Filter can
"wrap" the HttpServletRequest before passing it on to the rest of the
chain.  In your wrapper, you simply override getRemoteUser() and
isUserInRole() to do your own lookups -- or whatever is appropriate.

Basically, you implement a class that extends
javax.servlet.http.HttpServletRequestWrapper.  Then, in your filter, you
do something like this in the doFilter() method:

  MyWrapper wrapper = new MyWrapper(request);  // Pass the original request
  chain.doFilter(wrapper, response);

Now, when your servlet calls getRemoteUser(), it sees the value provided
by your wrapper class rather than the one set by Tomcat.  But, the beauty
of this is, it doesn't need to know that something special is going on.

> cheers
> dim
>
>

Craig



Re: blocking access using filter

Posted by Dmitri Colebatch <di...@bigpond.net.au>.
On Wed, 17 Oct 2001, Craig R. McClanahan wrote:

> I don't quite see why you need to modify the standard form-based login
> mechanisms, either.  Can't you just use the standard form based login for
> triggering authentication?  This would be as simple as a security

... snip ...

> 
> Form based login already does the rest of what your step (1) includes - it
> remembers the page that the user asks for (if it switches them to the
> login screen), and automatically "replays" it once authentication is done.

I've been thinking of something similar and would love to see something
worked on in for the community (I'd be more than willing to put time
in).  The advantages I see of this approach are:
  1. Users can bookmark the login page (which please correct me if I am
wrong, they cant in form based login)
  2. It is easier to customise, as customisations will onyl be of a
filter, and hence there wont be any spec compliance issues.

are they valid arguments?

cheers
dim


Re: blocking access using filter

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Wed, 17 Oct 2001, Taavi Tiirik wrote:

> Date: Wed, 17 Oct 2001 22:47:53 +0200
> From: Taavi Tiirik <tt...@ibs.ee>
> Reply-To: tomcat-user@jakarta.apache.org
> To: tomcat-user@jakarta.apache.org
> Subject: blocking access using filter
>
> Hey,
>
> I need more sophisticated access control for certain
> documents than JDBCRealm provides. These documents
> reside in separate directory tree and they are
> served by standalone tomcat 4. I have mapped an
> 'access control filter' to listen to all requests to
> this directory. Filter should behave like this:
>
> 1. If user is not logged in or if the session has
> timed out then it should open login page and after
> successful login it should try to access the very
> same request (ie. the same document).
>
> / I am using getRemoteUser() to determine if user
> is logged in. /
>
> 2. Filter performs a database lookup and decides if
> given user can access this document.
>
> / This step is easy and I can live with extra
> overhead needed for database query. /
>
> 3. If user does not have rights to access this
> document then filter should send error 404 (no such
> document).
>
> / This can be achieved using:
>   response.sendError( HttpServletResponse.SC_NOT_FOUND ); /
>
> 4. Do nothing... ie. user will get this document.
>
> / 'do nothing' actually means calling next filter in
>   chain like this:
>   chain.doFilter( request, response ); /
>

That's a pretty impressive amount of learning-by-doing!

>
> I have figured out all steps but the very first one.
> What should I do in filter in order to make tomcat
> to use it's standard authentication mechanism
> (JDBCRealm, form based login in my case) so
> the user could log in and still get required document?
>

What you've got so far doesn't really correspond to JDBCRealm, which is
just a way to tell Tomcat how to look up users.

I don't quite see why you need to modify the standard form-based login
mechanisms, either.  Can't you just use the standard form based login for
triggering authentication?  This would be as simple as a security
constraint that looks like this:

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>The Entire Webapp</web-resource-name>
      <url-pattern> /* </url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name> * </role-name>
    </auth-constraint>
  </security-constraint>

The "/*" URL pattern means that every single URL inside this webapp is
protected by the constraint.  The "*" in role names mean that it doesn't
matter what role(s) the user has -- every request requires an
authenticated user.  Therefore, by the time your filter is invoked,
getRemoteUser() will return the username you are looking for, and you can
impose any *additional* constraints that you need to.

Form based login already does the rest of what your step (1) includes - it
remembers the page that the user asks for (if it switches them to the
login screen), and automatically "replays" it once authentication is done.

If you really did want to modify the Tomcat authenticator, you would need
to turn this filter into an implementation of the
"org.apache.catalina.Valve" interface, which is conceptually pretty
similar to a Filter.  Valves (including the standard one used for
authentication) are invoked before the Filters (and the ultimate servlet)
belonging to your web application are invoked.

> Please, any help is appreciated. I will happily donate
> this filter back to the group if I get it working and
> if there is interest.
>
> thanks in advance,
> Taavi
>
>

Craig