001: /*
002: * CoadunationLib: The coaduntion implementation library.
003: * Copyright (C) 2006 Rift IT Contracting
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
018: *
019: * HttpServiceHandler.java
020: *
021: * This object is responsible for handling the HTTP service requests.
022: * It handles both WebService requests and the RMI class loader requests.
023: *
024: */
025:
026: // package path
027: package com.rift.coad.lib.httpd;
028:
029: // java imports
030: import java.io.ByteArrayOutputStream;
031: import java.io.File;
032: import java.io.IOException;
033: import java.net.Socket;
034: import java.net.URL;
035: import java.net.URLDecoder;
036: import java.util.Iterator;
037: import java.lang.reflect.Constructor;
038:
039: // logging import
040: import org.apache.log4j.Logger;
041:
042: // apache imports
043: import org.apache.http.HttpServerConnection;
044: import org.apache.http.protocol.HttpService;
045: import org.apache.http.HttpException;
046: import org.apache.http.HttpRequest;
047: import org.apache.http.HttpResponse;
048: import org.apache.http.Header;
049: import org.apache.http.HttpStatus;
050: import org.apache.http.HttpEntity;
051: import org.apache.http.entity.StringEntity;
052: import org.apache.http.entity.FileEntity;
053: import org.apache.http.message.BasicHttpEntityEnclosingRequest;
054: import org.apache.http.message.HttpPost;
055: import org.apache.http.params.HttpParams;
056:
057: // axis libraries
058: import org.apache.axis.configuration.EngineConfigurationFactoryFinder;
059: import org.apache.axis.EngineConfiguration;
060: import org.apache.axis.AxisEngine;
061: import org.apache.axis.server.AxisServer;
062: import org.apache.axis.management.ServiceAdmin;
063: import org.apache.axis.Message;
064: import org.apache.axis.MessageContext;
065: import org.apache.axis.handlers.JAXRPCHandler;
066: import org.w3c.dom.Document;
067: import org.apache.axis.utils.XMLUtils;
068: import org.apache.axis.handlers.soap.SOAPService;
069: import org.apache.axis.providers.java.RPCProvider;
070: import org.apache.axis.constants.Scope;
071: import org.apache.http.Header;
072: import javax.xml.soap.MimeHeader;
073: import javax.xml.soap.MimeHeaders;
074: import javax.xml.soap.SOAPMessage;
075: import org.apache.axis.Constants;
076: import org.apache.axis.transport.http.NonBlockingBufferedInputStream;
077:
078: // coadunation imports
079: import com.rift.coad.lib.configuration.Configuration;
080: import com.rift.coad.lib.configuration.ConfigurationFactory;
081: import com.rift.coad.lib.deployment.webservice.WebServiceConnector;
082: import com.rift.coad.lib.security.AuthorizationException;
083: import com.rift.coad.lib.security.UserSession;
084: import com.rift.coad.lib.security.Validator;
085: import com.rift.coad.lib.security.login.AuthenticationException;
086: import com.rift.coad.lib.security.login.SessionLogin;
087: import com.rift.coad.lib.security.login.handlers.PasswordInfoHandler;
088: import com.rift.coad.lib.security.sudo.Sudo;
089: import com.rift.coad.lib.security.sudo.SudoCallbackHandler;
090: import com.rift.coad.lib.security.user.UserSessionManager;
091: import com.rift.coad.lib.security.user.UserSessionManagerAccessor;
092: import com.rift.coad.lib.thread.BasicThread;
093: import com.rift.coad.lib.thirdparty.axis.AxisManager;
094: import com.rift.coad.lib.thirdparty.base64.Base64;
095: import com.rift.coad.lib.webservice.WebServiceWrapper;
096: import com.rift.coad.lib.webservice.WebServiceException;
097:
098: /**
099: * This object is responsible for handling the HTTP service requests.
100: * It handles both the WebService requests and the RMI class loader requests.
101: *
102: * @author Brett Chaldecott
103: */
104: public class HttpServiceHandler extends HttpService implements
105: SudoCallbackHandler, RequestInterface {
106:
107: // class static member variables
108: private final static String AUTH_HEADER = "Authorization";
109: private final static String CHALLENGE_HEADER = "WWW-Authenticate";
110: private final static String BASIC = "Basic";
111: private final static String SESSION_ID = "sessionid";
112: private final static String CODE_BASE = "/codebase/";
113:
114: // the private member variables
115: private Logger log = Logger.getLogger(HttpServiceHandler.class
116: .getName());
117: private Socket socket = null;
118: private HttpRequestCookieManager httpRequestCookieManager = null;
119: private String realm = null;
120: private HttpRequest request = null;
121: private HttpResponse response = null;
122: private String clientStubDir = null;
123:
124: /**
125: * Creates a new instance of HttpServiceHandler
126: *
127: * @param conn The http server connection object.
128: * @param socket The socket that all the information is retrieved from.
129: * @param realm The security realm for the challange
130: */
131: public HttpServiceHandler(HttpServerConnection conn, Socket socket,
132: String realm) throws HttpException {
133: super (conn);
134: this .socket = socket;
135: this .realm = realm;
136: try {
137: Configuration config = ConfigurationFactory.getInstance()
138: .getConfig(HttpServiceHandler.class);
139: clientStubDir = config.getString("Client_Stub");
140: } catch (Exception ex) {
141: throw new HttpException(
142: "Failed to init the http service handler : "
143: + ex.getMessage(), ex);
144: }
145:
146: }
147:
148: /**
149: * This method is responsible for responding to the http server requests.
150: *
151: * @param request The object containing the request value.
152: * @param response The method that encloses the http response value.
153: * @exception HttpException
154: * @exception IOException
155: */
156: protected void doService(HttpRequest request, HttpResponse response)
157: throws HttpException, IOException {
158: try {
159: this .request = request;
160: this .response = response;
161:
162: // Here we check for a client stub code request.
163: log.debug("Received a requests");
164: String target = request.getRequestLine().getUri();
165: String filePath = URLDecoder
166: .decode(target, MimeTypes.UTF_8);
167: if (filePath.indexOf(CODE_BASE) == 0) {
168: returnClientStubCode(request, response, filePath);
169: return;
170: }
171:
172: // instanciate the HttpRequestCookieManager
173: httpRequestCookieManager = new HttpRequestCookieManager(
174: request, response);
175:
176: // authenticate the user
177: if (sudoAndProcess() == false) {
178: response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
179: response.addHeader(new Header(CHALLENGE_HEADER, BASIC
180: + " realm=" + "\"" + realm + "\""));
181: return;
182: }
183:
184: } catch (Exception ex) {
185: log.error("Failed to respond to the request : "
186: + ex.getMessage(), ex);
187: response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
188: response.setEntity(new StringEntity(
189: "Internal server error [" + ex.getMessage() + "].",
190: "UTF-8"));
191: }
192: }
193:
194: /**
195: * This method returns the client stub code.
196: *
197: * @param request The request on the client.
198: * @param response The response to the client request.
199: * @param path The path to make the request on.
200: */
201: private void returnClientStubCode(HttpRequest request,
202: HttpResponse response, String path) throws HttpException {
203: try {
204: File file = new File(clientStubDir, path
205: .substring(CODE_BASE.length() - 1));
206: if (!file.exists()) {
207: response.setStatusCode(HttpStatus.SC_NOT_FOUND);
208: StringEntity body = new StringEntity("File not found ["
209: + file.getPath() + "]", "UTF-8");
210: response.setEntity(body);
211: return;
212: } else if (!file.canRead() || file.isDirectory()) {
213: response.setStatusCode(HttpStatus.SC_FORBIDDEN);
214: StringEntity body = new StringEntity("Access Denied",
215: "UTF-8");
216: response.setEntity(body);
217: return;
218: }
219: response.setStatusCode(HttpStatus.SC_OK);
220: FileEntity fileEntity = new FileEntity(file,
221: "application/x-compressed");
222: response.setEntity(fileEntity);
223: log.info("Return the file [" + file.getPath() + "]");
224: } catch (Exception ex) {
225: log.error("Failed to retrieve the file : "
226: + ex.getMessage(), ex);
227: throw new HttpException("Failed to retrieve the file : "
228: + ex.getMessage(), ex);
229: }
230: }
231:
232: /**
233: * This method is called to authenticate a user
234: *
235: * @exception HttpdException
236: */
237: private boolean sudoAndProcess() throws HttpdException {
238:
239: try {
240: // check for an active session
241: CookieWrapper cookieWrapper = httpRequestCookieManager
242: .getCookie(SESSION_ID);
243:
244: // check if the session is valid
245: log.debug("Check for cookie [" + cookieWrapper + "]");
246: if (cookieWrapper != null) {
247: String sessionId = cookieWrapper.getValue();
248: log.info("Retrieve the session id [" + sessionId
249: + "] from cookie.");
250: try {
251: UserSessionManagerAccessor.getInstance()
252: .getUserSessionManager().getSessionById(
253: sessionId);
254: Sudo.sudoThreadBySessionId(sessionId, this );
255: return true;
256: } catch (com.rift.coad.lib.security.user.UserException ex) {
257: // ignore
258: }
259: }
260:
261: // check for auth
262: log.debug("Check for an auth header.");
263: if (request.containsHeader(AUTH_HEADER)) {
264: log.info("Auth user.");
265: Header header = request.getFirstHeader(AUTH_HEADER);
266: if (!checkForBasic(header)) {
267: return false;
268: }
269: String decodedValue = decodeHeader(header);
270: String sessionId = authenticate(decodedValue);
271: log.debug("Session id is [" + sessionId + "]");
272: httpRequestCookieManager.addCookie(new CookieWrapper(
273: SESSION_ID, sessionId));
274: Sudo.sudoThreadBySessionId(sessionId, this );
275: return true;
276: }
277:
278: // assuming that the user will run as a guest.
279: process();
280: return true;
281: } catch (AuthorizationException ex) {
282: log.error("Insufficiant permission [" + ex.getMessage()
283: + "]", ex);
284: return false;
285: } catch (AuthenticationException ex) {
286: log.info("Authentication failed [" + ex.getMessage() + "]",
287: ex);
288: return false;
289: } catch (Exception ex) {
290: throw new HttpdException(
291: "Failed to auth the user because : "
292: + ex.getMessage(), ex);
293: }
294: }
295:
296: /**
297: * This method performs the actuall processing of the request.
298: */
299: public void process() throws Exception {
300:
301: try {
302: // retrieve the file path
303: String target = request.getRequestLine().getUri();
304: String filePath = URLDecoder
305: .decode(target, MimeTypes.UTF_8);
306:
307: // strip the file and look for question mark
308: String strippedFile = filePath;
309: int getParam = filePath.indexOf('?');
310: if (getParam != -1) {
311: strippedFile = filePath.substring(0, getParam);
312: }
313:
314: // retrieve the web service
315: log.info("Find the file [" + strippedFile + "]");
316: WebServiceWrapper webServiceWrapper = (WebServiceWrapper) WebServiceConnector
317: .getInstance().getService(strippedFile);
318: if (webServiceWrapper == null) {
319: response.setStatusCode(HttpStatus.SC_NOT_FOUND);
320: response.setEntity(new StringEntity("The Web Service ["
321: + strippedFile + "] does not exist.",
322: MimeTypes.UTF_8));
323: return;
324: }
325:
326: // validate that we can call it
327: Validator.validate(this .getClass(), webServiceWrapper
328: .getRole());
329:
330: // retrieve the request type
331: String method = request.getRequestLine().getMethod();
332:
333: if (method.equalsIgnoreCase("GET")
334: && getParam != -1
335: && (filePath.substring(getParam + 1)
336: .equalsIgnoreCase("wsdl"))) {
337: String result = webServiceWrapper.generateWSDL();
338: StringEntity stringEntity = new StringEntity(result);
339: stringEntity.setContentType(MimeTypes.XML);
340: response.setEntity(stringEntity);
341: return;
342: } else if (request instanceof BasicHttpEntityEnclosingRequest) {
343: BasicHttpEntityEnclosingRequest post = (BasicHttpEntityEnclosingRequest) request;
344: HttpEntity entity = post.getEntity();
345:
346: // setup the mime headers
347: MimeHeaders mimeHeaders = new MimeHeaders();
348: Header[] headerList = request.getAllHeaders();
349: for (int i = 0; i < headerList.length; i++) {
350: mimeHeaders.addHeader(headerList[i].getName(),
351: headerList[i].getValue());
352: }
353:
354: String result = webServiceWrapper.processRequest(entity
355: .getContent(), mimeHeaders);
356: StringEntity stringEntity = new StringEntity(result);
357: stringEntity.setContentType(MimeTypes.XML);
358: response.setEntity(stringEntity);
359: return;
360: }
361: response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
362: response.setEntity(new StringEntity("Unrecognised request",
363: MimeTypes.UTF_8));
364: } catch (AuthorizationException ex) {
365: log.error("In sufficiant privaleges : " + ex.getMessage(),
366: ex);
367: throw ex;
368: } catch (WebServiceException ex) {
369: log.error("Failed to process the soap request : "
370: + ex.getMessage(), ex);
371: response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
372: StringEntity stringEntity = new StringEntity(ex
373: .getMessage(), MimeTypes.UTF_8);
374: stringEntity.setContentEncoding(ex.getEncoding());
375: response.setEntity(stringEntity);
376:
377: } catch (Exception ex) {
378: log.error("Failed to process the soap request : "
379: + ex.getMessage(), ex);
380: response.setStatusCode(HttpStatus.SC_NOT_FOUND);
381: response.setEntity(new StringEntity(
382: "Failed to invoke the request " + "because : "
383: + ex.getMessage(), MimeTypes.UTF_8));
384: }
385: }
386:
387: /**
388: * This method validates that a basic auth request has been made.
389: *
390: * @return TRUE if a basic auth request has been made, FALSE if not.
391: * @param authHeader The auth header to retrieve.
392: */
393: private boolean checkForBasic(Header authHeader) {
394: if (authHeader.getValue().toLowerCase().contains(
395: BASIC.toLowerCase())) {
396: return true;
397: }
398: return false;
399: }
400:
401: /**
402: * This method decodes the header.
403: *
404: * @return The string value of the decoded header.
405: * @param authHeader The header to containing the auth information.
406: */
407: private String decodeHeader(Header authHeader) {
408: String encodedValue = authHeader.getValue().trim();
409: String fullValue = encodedValue;
410: encodedValue = encodedValue
411: .substring(encodedValue.indexOf(" ")).trim();
412: String decodedValue = new String(Base64.decode(encodedValue));
413: log.debug("Full value [" + fullValue + "] Encoded value ["
414: + encodedValue + "] decoded value [" + decodedValue
415: + "]");
416: return decodedValue;
417: }
418:
419: /**
420: * This method uses the decoded header authenticate the user.
421: *
422: * @return The string containing the new session id.
423: * @param decodedHeaderValue The decoded header value containing the user
424: * name and password.
425: */
426: private String authenticate(String decodedHeaderValue)
427: throws AuthenticationException, HttpdException {
428: try {
429: log.debug("Decoded header value [" + decodedHeaderValue
430: + "] colon value ["
431: + decodedHeaderValue.indexOf(':') + "]");
432: // check for a traditional = seperator
433: String username = null;
434: String password = null;
435: int pos = decodedHeaderValue.indexOf('=');
436: if (pos != -1) {
437: username = decodedHeaderValue.substring(0, pos);
438: password = decodedHeaderValue.substring(pos + 1);
439:
440: }
441: // check for a colon seperator
442: else if ((pos = decodedHeaderValue.indexOf(':')) != -1) {
443: username = decodedHeaderValue.substring(0, pos).trim();
444: password = decodedHeaderValue.substring(pos + 1).trim();
445: } else {
446: throw new HttpdException(
447: "Authentication not recognised.");
448: }
449:
450: // authenticate
451: log.debug("User name [" + username + "] password ["
452: + password + "]");
453: SessionLogin sessionLogin = new SessionLogin(
454: new PasswordInfoHandler(username, password));
455: sessionLogin.login();
456: return sessionLogin.getUser().getSessionId();
457: } catch (AuthenticationException ex) {
458: throw ex;
459: } catch (Exception ex) {
460: throw new HttpdException(
461: "Failed to authenticate the user : "
462: + ex.getMessage(), ex);
463: }
464: }
465: }
|