Eoghan,
Thanks. I originally threw a fault, because I didn't know how to prevent
the message from continuing to the end of the chain (to the
application). Also, the Fault, seems to use the system to create the
return "fault" message, and I had thought that is the proper way to go.
Creating a corresponding "out" message to match the "in" message seems
like the proper way to go since you have the correlation right there.
But, you seem to usurp the OutInterceptor chain, if not the
OutFaultInterceptor chain, (which incidentally leads me to doubt the
need for "Fault" messages. They are just messages, aren't they?) I
guess you can just set up the interceptor chains, and call them.
However, I'm still concerned about the fact that that some interceptors
don't get notified that the chain did not complete.
Cheers,
-Polar
Glynn, Eoghan wrote:
Hi Polar,
It struck me that sending a 401 has got to be simpler than what you did,
so I had a crack at knocking up a single interceptor to do the job as
directly as possible, say sending a 401 with "realm=foo" for the first
incoming request. I've appended the code below as the Apache mail server
strips out attachements. Obvioiusly this code needs extending, but you
could use it as a basic template.
The first mistake you made was to raise a fault. A 401 is not in my view
a fault, i.e. it wouldn't translate into a HTTP entity-body containing a
<SOAP:Fault>. In fact what we need is an *empty* response, as all the
relevant info is carried in the HTTP headers (i.e. the 401 response code
and WWW-Authenticate header). Similarly we don't want traverse the
out-fault-chain, rather it would be more appropriate to abort the
traversal of the in-chain and allow the transport get straight back with
the challenge.
Cheers,
Eoghan
-----Original Message-----
From: Polar Humenn [mailto:[EMAIL PROTECTED]
Sent: 12 February 2007 19:13
To: [email protected]
Subject: Re: HTTP Basic Authentication Is there hope?
Glynn, Eoghan wrote:
I can construct a complex graph of interceptors on the
server side to
send the 401 and the proper realm information.
A complex graph of interceptors just to send a 401?
Can you describe what server-side interceptors chains you needed to
achieve this? If something so simple as rejecting an
incoming request
with an auth challenge requires some complex choreography in the
server-side interceptors chains, then we are surely doing something
very wrong in our interceptor/dispatch architecture
Well, I tell you what I did. I am still somewhat naive about
this so, please tell me if I have gone astray. This is where
I was learning about interceptors, and when handleMessage and
handleFault gets called. I think I'm better informed at that
now, but I still may be lacking in some aspects.
I set up one Inbound interceptor on the RECEIVE phase to
check for the authorization information if present and
validate it. If it wasn't there or validated I throw a Fault,
which was subclassed to HTTPBAFault, which held the realm identifier.
This Inbound InterceptorChain unwinds through the InboundChain. In
handleFault() I tried setting the response code, but that was
ineffective, as I realized I was not really manipulating a
response here. so I know I couldn't do this entirely in one
interceptor. Fair enough.
Throwing the Fault, I found, automatically generates a
response message, which seems to know its a"fault" message
with a response code of 500 and heads out in an Outbound
Fault Interceptor Chain using handleMessage() calls.
I set up a second interceptor that gets installed
(dynamically?) on the OutFaultInterceptor chain on the
USER_STREAM phase. I discoverd some state saved on the
message in the previous interceptor chain, which was the
actual HTTPBAFault I threw, which I discovered is available
through a Message.getContent(Exception.class) call. Then I
changed the response code from 500 to a 401 and had to add
the Authorization header with the realm information.
I am not sure if I got the phases right for this sort of
thing (I played with these with varying degrees of success
and failure), or even if I took the right approach. I may be
relying on some technique or information that is coincidental
and not guaranteed.
Cheers,
-Polar
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.interceptor;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.ws.addressing.EndpointReferenceType;
public class ChallengeInterceptor extends
AbstractPhaseInterceptor<Message> {
boolean challenged;
public ChallengeInterceptor() {
super();
setPhase(Phase.RECEIVE);
}
public void handleMessage(Message inMessage) {
if (!challenged) {
Message outMessage = getOutMessage(inMessage);
outMessage.put(Message.RESPONSE_CODE,
HttpURLConnection.HTTP_UNAUTHORIZED);
setHeader(outMessage, "WWW-Authenticate", "Basic
realm=foo");
inMessage.getInterceptorChain().abort();
try {
getConduit(inMessage).send(outMessage);
close(outMessage);
} catch (IOException ioe) {
// REVISIT log etc...
ioe.printStackTrace();
}
challenged = true;
}
}
private Message getOutMessage(Message inMessage) {
Exchange exchange = inMessage.getExchange();
Message outMessage = exchange.getOutMessage();
if (outMessage == null) {
Endpoint endpoint = exchange.get(Endpoint.class);
outMessage = endpoint.getBinding().createMessage();
exchange.setOutMessage(outMessage);
}
outMessage.putAll(inMessage);
return outMessage;
}
@SuppressWarnings("unchecked")
private void setHeader(Message message, String name, String value) {
Map<String, List<String>> responseHeaders =
(Map<String,
List<String>>)message.get(Message.PROTOCOL_HEADERS);
if (responseHeaders != null) {
responseHeaders.put(name, Arrays.asList(new
String[]{value}));
}
}
private Conduit getConduit(Message inMessage) throws IOException {
Exchange exchange = inMessage.getExchange();
EndpointReferenceType target =
exchange.get(EndpointReferenceType.class);
Conduit conduit =
exchange.getDestination().getBackChannel(inMessage, null,
target);
exchange.setConduit(conduit);
return conduit;
}
private void close(Message outMessage) throws IOException {
OutputStream os = outMessage.getContent(OutputStream.class);
os.flush();
os.close();
}
}