You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Alexander Klimetschek <al...@mindquarry.com> on 2006/11/21 16:35:35 UTC

Original request attributes in internal block servlet call

Currently internal BlockServlet calls do not contain the original 
request parameters or attributes, i.e. the BlockCallHttpServletRequest 
has none of them and no reference to the original request (when called 
from a BlockSource, but this is currently the only scenario anyway).

We are using the request attributes to store the name of the logged-in 
user, because the authentication is done by a servlet filter (which sits 
before the DispatcherServlet). Now when you make an internal call to 
another block this username request attribute gets lost which is bad, 
because you are forced to put all user-related functionality (eg. 
authorisation) in the front-end block sitemap, which is often not possible.

There is also no possibility to make pipelines only available to other 
blocks in the same cocoon webapp, but not for external HTTP requests, 
like you can define an internal-only="true" for pipelines inside a 
sitemap. That would be at least a workaround for having working 
authorization.

My idea would be to include the original request in the 
BlockCallHttpServletRequest, either by referencing it, or by completely 
including all request params and attributes of the original request.

Technically this could be implemented by storing the original 
HttpRequest in the BlockCallStack (in a ThreadLocalStorage, one request 
per thread) and then provide a static method 
BlockCallStack.getOriginalRequest() that returns that request object, 
which can be called inside BlockConnection where the 
BlockCallHttpServletRequest is created. That one should hold a reference 
and include the params and attributes.

The main question is: is it feasible to always include all request 
parameters and attributes and other information (HTTP method, original 
URI...) or do you sometimes want a "clean" call to another BlockServlet? 
That could be made configurable for the Block sources by having two 
schemes, eg. "block:" and "block-with-params:" (well, that should have a 
better name ;-)

Hey, and if you finally got to this point, thanks for reading all that 
stuff!

Alex

-- 
Alexander Klimetschek
http://www.mindquarry.com


Re: Original request attributes in internal block servlet call

Posted by Daniel Fagerstrom <da...@nada.kth.se>.
Alexander Klimetschek skrev:
> Daniel Fagerstrom schrieb:
...
>> This could be complemented by giving access to the original request 
>> parameters. For this case we need a call stack where each block 
>> protocol call pushes a new request object, and where all parameter 
>> lookup is done through the stack.
>>
>> For request attributes the situation is more complicated, we need a 
>> call stack where the local attribute context is pushed. Otherwise 
>> your block will aways risk that some other block in the call chain 
>> happen to use the same parameter name and affect your block. But you 
>> probably also need to be able to set the global attribute to 
>> communicate state information between blocks. For the Cocoon protocol 
>> this is done by having booth a global and a local scope for 
>> parameters (see o.a.c.environment.Request). But this solution 
>> requires an extension to the standard HttpServletRequest, and I 
>> preferred to avoid that. But if we have a need for it we could extend 
>> the current design.
>>
>> For the rest of the request (and response) object we would need to 
>> evaluate what should be available everywhere and what should be 
>> specific for the current block call.
>
> I see, it's difficult. My case is attribute-readonly after the 
> listener, so it is quite simple. The distinction between global and 
> local parameters would be important, since otherwise you'd only had 
> the attributes of the previous block call at hand (if there is more 
> than 1 call to different blocks).
It wouldn't need to be that bad. What I have in mind for local access is 
a construction like this:

There is a call stack for request attributes consisting of hash tables. 
Each time a block call is made an empty hash table is pushed on the 
stack and each time a block call leave the stack is poped.

A setAttribute will affect the *topmost* element of the stack and thus 
only have an effect during the ongoing block call.

A removeAttribute will but a special "empty element" for that attribute 
into the topmost hash table.

A getAttribute will search the stack top down until it find the 
attribute, if it has the "empty" value it will return null otherwise it 
will return the value of the attribute. If the attribute is not 
available in any of the hash tables it will return null.

This will work in most scenarios, attributes are available for blocks 
further down the call chain. But blocks cannot communicate to cannot 
communicate through attributes to blocks higher in the call chain or to 
"siblings" in the call chain.

The global scope would always affect the original request attribute. 
Which always is highest in the call stack.

>> There should be a simple possibility, just remove the block path 
>> property from a managed servlet and then the dispatcher servlet will 
>> not use it. And it will still be available as a component and thus 
>> possible to connect to and use through the block protocol. Now this 
>> will not be usable for the block servlet as it has a getBlockPath 
>> method and therefore always will be connected. If we strengthen the 
>> condition in the dispatcher servlet to only connect to servlet that 
>> has a *non null* block path property, it would be enough to not 
>> configure the block path property to make the block servlet internal..
>
> I currently only have the BlockServlet in place. Apart from that, 
> there are blocks that contain both external matchers (open to the 
> script kiddies) and internal ones, so deciding at the block level 
> would no help as everything in that sitemap would be internal. What 
> about extending the pipeline parameter internal-only="true" to accept 
> the values (true|false|block), where block means visible for blocks 
> but not for external requests?
OK, I see. It seem like a useful feature. Any idea about how to 
implement it ?

/Daniel


Re: Original request attributes in internal block servlet call

Posted by Alexander Klimetschek <al...@mindquarry.com>.
Daniel Fagerstrom schrieb:
> I solved it that way to not need to answer lots of complicated questions 
> about exactly how this should work.
> 
> For request parameters you can in the current solution just resend the 
> parameters that you need as query parameters in the block protocol.

Yup, that's what I am already doing at a different place. But it creates 
long URIs in your sitemap... :-(

> This could be complemented by giving access to the original request 
> parameters. For this case we need a call stack where each block protocol 
> call pushes a new request object, and where all parameter lookup is done 
> through the stack.
> 
> For request attributes the situation is more complicated, we need a call 
> stack where the local attribute context is pushed. Otherwise your block 
> will aways risk that some other block in the call chain happen to use 
> the same parameter name and affect your block. But you probably also 
> need to be able to set the global attribute to communicate state 
> information between blocks. For the Cocoon protocol this is done by 
> having booth a global and a local scope for parameters (see 
> o.a.c.environment.Request). But this solution requires an extension to 
> the standard HttpServletRequest, and I preferred to avoid that. But if 
> we have a need for it we could extend the current design.
> 
> For the rest of the request (and response) object we would need to 
> evaluate what should be available everywhere and what should be specific 
> for the current block call.

I see, it's difficult. My case is attribute-readonly after the listener, 
so it is quite simple. The distinction between global and local 
parameters would be important, since otherwise you'd only had the 
attributes of the previous block call at hand (if there is more than 1 
call to different blocks).


> There should be a simple possibility, just remove the block path 
> property from a managed servlet and then the dispatcher servlet will not 
> use it. And it will still be available as a component and thus possible 
> to connect to and use through the block protocol. Now this will not be 
> usable for the block servlet as it has a getBlockPath method and 
> therefore always will be connected. If we strengthen the condition in 
> the dispatcher servlet to only connect to servlet that has a *non null* 
> block path property, it would be enough to not configure the block path 
> property to make the block servlet internal..

I currently only have the BlockServlet in place. Apart from that, there 
are blocks that contain both external matchers (open to the script 
kiddies) and internal ones, so deciding at the block level would no help 
as everything in that sitemap would be internal. What about extending 
the pipeline parameter internal-only="true" to accept the values 
(true|false|block), where block means visible for blocks but not for 
external requests?

> Would probably work, but as indicated above it should probably be stack 
> based.

Yes, makes sense.

> Thinking further about it, you could take a look at the request scope 
> for Spring managed beans 
> (http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-factory-scopes-request). 
> Using that you could put your login information in a request scoped 
> bean, and use that from the different blocks.

Good hint, thanks! I will look into it.


Alex

-- 
Alexander Klimetschek
http://www.mindquarry.com


Re: Original request attributes in internal block servlet call

Posted by Daniel Fagerstrom <da...@nada.kth.se>.
Alexander Klimetschek skrev:
> Currently internal BlockServlet calls do not contain the original 
> request parameters or attributes, i.e. the BlockCallHttpServletRequest 
> has none of them and no reference to the original request (when called 
> from a BlockSource, but this is currently the only scenario anyway).
> We are using the request attributes to store the name of the logged-in 
> user, because the authentication is done by a servlet filter (which 
> sits before the DispatcherServlet). Now when you make an internal call 
> to another block this username request attribute gets lost which is 
> bad, because you are forced to put all user-related functionality (eg. 
> authorisation) in the front-end block sitemap, which is often not 
> possible.

I solved it that way to not need to answer lots of complicated questions 
about exactly how this should work.

For request parameters you can in the current solution just resend the 
parameters that you need as query parameters in the block protocol.

This could be complemented by giving access to the original request 
parameters. For this case we need a call stack where each block protocol 
call pushes a new request object, and where all parameter lookup is done 
through the stack.

For request parameters the situation is more complicated, we need a call 
stack where the local attribute context is pushed. Otherwise your block 
will aways risk that some other block in the call chain happen to use 
the same parameter name and affect your block. But you probably also 
need to be able to set the global attribute to communicate state 
information between blocks. For the Cocoon protocol this is done by 
having booth a global and a local scope for parameters (see 
o.a.c.environment.Request). But this solution requires an extension to 
the standard HttpServletRequest, and I preferred to avoid that. But if 
we have a need for it we could extend the current design.

For the rest of the request (and response) object we would need to 
evaluate what should be available everywhere and what should be specific 
for the current block call.

> There is also no possibility to make pipelines only available to other 
> blocks in the same cocoon webapp, but not for external HTTP requests, 
> like you can define an internal-only="true" for pipelines inside a 
> sitemap. That would be at least a workaround for having working 
> authorization.
There should be a simple possibility, just remove the block path 
property from a managed servlet and then the dispatcher servlet will not 
use it. And it will still be available as a component and thus possible 
to connect to and use through the block protocol. Now this will not be 
usable for the block servlet as it has a getBlockPath method and 
therefore always will be connected. If we strengthen the condition in 
the dispatcher servlet to only connect to servlet that has a *non null* 
block path property, it would be enough to not configure the block path 
property to make the block servlet internal..

> My idea would be to include the original request in the 
> BlockCallHttpServletRequest, either by referencing it, or by 
> completely including all request params and attributes of the original 
> request.
As described above there is a little bit more one have to think about.

> Technically this could be implemented by storing the original 
> HttpRequest in the BlockCallStack (in a ThreadLocalStorage, one 
> request per thread) and then provide a static method 
> BlockCallStack.getOriginalRequest() that returns that request object, 
> which can be called inside BlockConnection where the 
> BlockCallHttpServletRequest is created. That one should hold a 
> reference and include the params and attributes.
Would probably work, but as indicated above it should probably be stack 
based.

Thinking further about it, you could take a look at the request scope 
for Spring managed beans 
(http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-factory-scopes-request). 
Using that you could put your login information in a request scoped 
bean, and use that from the different blocks.

> The main question is: is it feasible to always include all request 
> parameters and attributes and other information (HTTP method, original 
> URI...) 
> or do you sometimes want a "clean" call to another BlockServlet?
The clean call is probably the default scenario.

/Daniel