/** * 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.systest.https.conduit; 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.common.util.Base64Utility; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.message.Exchange; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.apache.cxf.transport.Conduit; import org.apache.cxf.transport.http.Headers; /* * This interceptor will issue 401s * No Authorization Header --> 401 Realm=Cronus * Username Mary --> 401 Realm=Andromeda * Username Edward --> 401 Realm=Zorantius * Username George --> 401 Realm=Cronus * If the password is not "password" a 401 is issued without * realm. */ public class PushBack401 extends AbstractPhaseInterceptor<Message> { PushBack401() { super(Phase.RECEIVE); } /** * This function extracts the user:pass token from * the Authorization:Basic header. It returns a two element * String array, the first being the userid, the second * being the password. It returns null, if it cannot parse. */ private String[] extractUserPass(String token) { try { byte[] userpass = Base64Utility.decode(token); String up = IOUtils.newStringFromBytes(userpass); String user = up.substring(0, up.indexOf(':')); String pass = up.substring(up.indexOf(':') + 1); return new String[] {user, pass}; } catch (Exception e) { return null; } } /** * This function returns the realm which depends on * the user name, as follows: * <pre> * Username Mary --> Andromeda * Username Edward --> Zorantius * Username George --> Cronus * </pre> * However, if the password is not "password" this function * throws an exception, regardless. */ private String checkUserPass( String user, String pass ) throws Exception { //System.out.println("Got user: " + user + " pass: " + pass); if (!"password".equals(pass)) { throw new Exception("bad password"); } if ("Mary".equals(user)) { return "Andromeda"; } if ("Edward".equals(user)) { return "Zorantius"; } if ("George".equals(user)) { return "Cronus"; } return null; } @SuppressWarnings("unchecked") public void handleMessage(Message message) throws Fault { Map<String, List<String>> headers = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS); List<String> auth = headers.get("Authorization"); if (auth == null) { // No Auth Header, respond with 401 Realm=Cronus replyUnauthorized(message, "Cronus"); return; } else { for (String a : auth) { if (a.startsWith("Basic ")) { String[] userpass = extractUserPass(a.substring("Basic ".length())); if (userpass != null) { try { String realm = checkUserPass(userpass[0], userpass[1]); if (realm != null) { replyUnauthorized(message, realm); return; } else { // Password is good and no realm // We just return for successful fall thru. return; } } catch (Exception e) { // Bad Password replyUnauthorized(message, null); return; } } } } // No Authorization: Basic replyUnauthorized(message, null); return; } } /** * This function issues a 401 response back down the conduit. * If the realm is not null, a WWW-Authenticate: Basic realm= * header is sent. The interceptor chain is aborted stopping * the Message from going to the servant. */ private void replyUnauthorized(Message message, String realm) { Message outMessage = getOutMessage(message); outMessage.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_UNAUTHORIZED); if (realm != null) { setHeader(outMessage, "WWW-Authenticate", "Basic realm=" + realm); } message.getInterceptorChain().abort(); try { getConduit(message).prepare(outMessage); close(outMessage); } catch (IOException e) { //System.out.println("Prepare of message not working." + e); e.printStackTrace(); } } /** * Retrieves/creates the corresponding Outbound Message. */ private Message getOutMessage(Message message) { Exchange exchange = message.getExchange(); Message outMessage = exchange.getOutMessage(); if (outMessage == null) { Endpoint endpoint = exchange.getEndpoint(); outMessage = new MessageImpl(); outMessage.putAll(message); outMessage.remove(Message.PROTOCOL_HEADERS); outMessage.setExchange(exchange); outMessage = endpoint.getBinding().createMessage(outMessage); exchange.setOutMessage(outMessage); } return outMessage; } /** * This function sets the header in the PROTOCO_HEADERS of * the message. */ private void setHeader(Message message, String key, String value) { Map<String, List<String>> responseHeaders = Headers.getSetProtocolHeaders(message); responseHeaders.put(key, Arrays.asList(new String[] {value})); } /** * This method retrieves/creates the conduit for the response * message. */ private Conduit getConduit(Message message) throws IOException { Exchange exchange = message.getExchange(); Conduit conduit = exchange.getDestination().getBackChannel(message); exchange.setConduit(conduit); return conduit; } /** * This method closes the output stream associated with the * message. */ private void close(Message message) throws IOException { OutputStream os = message.getContent(OutputStream.class); os.flush(); os.close(); } }