001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.systest.http;
019:
020: import java.io.IOException;
021: import java.io.OutputStream;
022: import java.net.HttpURLConnection;
023: import java.util.Arrays;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.apache.cxf.common.util.Base64Utility;
028: import org.apache.cxf.endpoint.Endpoint;
029: import org.apache.cxf.interceptor.Fault;
030: import org.apache.cxf.message.Exchange;
031: import org.apache.cxf.message.Message;
032: import org.apache.cxf.phase.AbstractPhaseInterceptor;
033: import org.apache.cxf.phase.Phase;
034: import org.apache.cxf.transport.Conduit;
035: import org.apache.cxf.ws.addressing.EndpointReferenceType;
036:
037: /*
038: * This interceptor will issue 401s
039: * No Authorization Header --> 401 Realm=Cronus
040: * Username Mary --> 401 Realm=Andromeda
041: * Username Edward --> 401 Realm=Zorantius
042: * Username George --> 401 Realm=Cronus
043: * If the password is not "password" a 401 is issued without
044: * realm.
045: */
046: public class PushBack401 extends AbstractPhaseInterceptor {
047:
048: PushBack401() {
049: super (Phase.RECEIVE);
050: }
051:
052: /**
053: * This function extracts the user:pass token from
054: * the Authorization:Basic header. It returns a two element
055: * String array, the first being the userid, the second
056: * being the password. It returns null, if it cannot parse.
057: */
058: private String[] extractUserPass(String token) {
059: try {
060: byte[] userpass = Base64Utility.decode(token);
061: String up = new String(userpass);
062: String user = up.substring(0, up.indexOf(':'));
063: String pass = up.substring(up.indexOf(':') + 1);
064: return new String[] { user, pass };
065: } catch (Exception e) {
066: return null;
067: }
068:
069: }
070:
071: /**
072: * This function returns the realm which depends on
073: * the user name, as follows:
074: * <pre>
075: * Username Mary --> Andromeda
076: * Username Edward --> Zorantius
077: * Username George --> Cronus
078: * </pre>
079: * However, if the password is not "password" this function
080: * throws an exception, regardless.
081: */
082: private String checkUserPass(String user, String pass)
083: throws Exception {
084: //System.out.println("Got user: " + user + " pass: " + pass);
085: if (!"password".equals(pass)) {
086: throw new Exception("bad password");
087: }
088: if ("Mary".equals(user)) {
089: return "Andromeda";
090: }
091: if ("Edward".equals(user)) {
092: return "Zorantius";
093: }
094: if ("George".equals(user)) {
095: return "Cronus";
096: }
097: return null;
098: }
099:
100: @SuppressWarnings("unchecked")
101: public void handleMessage(Message message) throws Fault {
102:
103: Map<String, List<String>> headers = (Map<String, List<String>>) message
104: .get(Message.PROTOCOL_HEADERS);
105:
106: List<String> auth = headers.get("Authorization");
107: if (auth == null) {
108: // No Auth Header, respond with 401 Realm=Cronus
109: replyUnauthorized(message, "Cronus");
110: return;
111: } else {
112: for (String a : auth) {
113: if (a.startsWith("Basic ")) {
114: String[] userpass = extractUserPass(a
115: .substring("Basic ".length()));
116: if (userpass != null) {
117: try {
118: String realm = checkUserPass(userpass[0],
119: userpass[1]);
120: if (realm != null) {
121: replyUnauthorized(message, realm);
122: return;
123: } else {
124: // Password is good and no realm
125: // We just return for successful fall thru.
126: return;
127: }
128: } catch (Exception e) {
129: // Bad Password
130: replyUnauthorized(message, null);
131: return;
132: }
133: }
134: }
135: }
136: // No Authorization: Basic
137: replyUnauthorized(message, null);
138: return;
139: }
140: }
141:
142: /**
143: * This function issues a 401 response back down the conduit.
144: * If the realm is not null, a WWW-Authenticate: Basic realm=
145: * header is sent. The interceptor chain is aborted stopping
146: * the Message from going to the servant.
147: */
148: private void replyUnauthorized(Message message, String realm) {
149: Message outMessage = getOutMessage(message);
150: outMessage.put(Message.RESPONSE_CODE,
151: HttpURLConnection.HTTP_UNAUTHORIZED);
152:
153: if (realm != null) {
154: setHeader(outMessage, "WWW-Authenticate", "Basic realm="
155: + realm);
156: }
157: message.getInterceptorChain().abort();
158: try {
159: getConduit(message).prepare(outMessage);
160: close(outMessage);
161: } catch (IOException e) {
162: //System.out.println("Prepare of message not working." + e);
163: e.printStackTrace();
164: }
165: }
166:
167: /**
168: * Retrieves/creates the corresponding Outbound Message.
169: */
170: private Message getOutMessage(Message message) {
171: Exchange exchange = message.getExchange();
172: Message outMessage = exchange.getOutMessage();
173: if (outMessage == null) {
174: Endpoint endpoint = exchange.get(Endpoint.class);
175: outMessage = endpoint.getBinding().createMessage();
176: exchange.setOutMessage(outMessage);
177: }
178: outMessage.putAll(message);
179: return outMessage;
180: }
181:
182: /**
183: * This function sets the header in the PROTOCO_HEADERS of
184: * the message.
185: */
186: @SuppressWarnings("unchecked")
187: private void setHeader(Message message, String key, String value) {
188: Map<String, List<String>> responseHeaders = (Map<String, List<String>>) message
189: .get(Message.PROTOCOL_HEADERS);
190: if (responseHeaders != null) {
191: responseHeaders.put(key, Arrays
192: .asList(new String[] { value }));
193: }
194: }
195:
196: /**
197: * This method retrieves/creates the conduit for the response
198: * message.
199: */
200: private Conduit getConduit(Message message) throws IOException {
201: Exchange exchange = message.getExchange();
202: EndpointReferenceType target = exchange
203: .get(EndpointReferenceType.class);
204: Conduit conduit = exchange.getDestination().getBackChannel(
205: message, null, target);
206: exchange.setConduit(conduit);
207: return conduit;
208: }
209:
210: /**
211: * This method closes the output stream associated with the
212: * message.
213: */
214: private void close(Message message) throws IOException {
215: OutputStream os = message.getContent(OutputStream.class);
216: os.flush();
217: os.close();
218: }
219:
220: }
|