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




Reply via email to