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(); } }
