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



 

Reply via email to