All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kawanfw.file.servlet.ServerFileDispatch Maven / Gradle / Ivy

Go to download

Awake FILE is a secure Open Source framework that allows to program very easily file uploads/downloads and RPC through http. File transfers include powerful features like file chunking and automatic recovery mechanism. Security has been taken into account from the design: server side allows to specify strong security rules in order to protect the files and to secure the RPC calls.

The newest version!
/*
 * This file is part of Awake FILE. 
 * Awake file: Easy file upload & download over HTTP with Java.                                    
 * Copyright (C) 2015,  KawanSoft SAS
 * (http://www.kawansoft.com). All rights reserved.                                
 *                                                                               
 * Awake FILE is free software; you can redistribute it and/or                 
 * modify it under the terms of the GNU Lesser General Public                    
 * License as published by the Free Software Foundation; either                  
 * version 2.1 of the License, or (at your option) any later version.            
 *                                                                               
 * Awake FILE is distributed in the hope that it will be useful,               
 * but WITHOUT ANY WARRANTY; without even the implied warranty of                
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             
 * Lesser General Public License for more details.                               
 *                                                                               
 * You should have received a copy of the GNU Lesser General Public              
 * License along with this library; if not, write to the Free Software           
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
 * 02110-1301  USA
 *
 * Any modifications to this file must keep this entire header
 * intact.
 */
package org.kawanfw.file.servlet;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.logging.Level;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.kawanfw.commons.api.server.CommonsConfigurator;
import org.kawanfw.commons.json.ListOfStringTransport;
import org.kawanfw.commons.server.util.ServerLogger;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.commons.util.HtmlConverter;
import org.kawanfw.commons.util.StringUtil;
import org.kawanfw.commons.util.Tag;
import org.kawanfw.commons.util.TransferStatus;
import org.kawanfw.file.api.server.FileConfigurator;
import org.kawanfw.file.reflection.ClassPathUtil;
import org.kawanfw.file.servlet.nio.FileListAction;
import org.kawanfw.file.servlet.nio.FileListFilesAction;
import org.kawanfw.file.servlet.nio.FileMethodOneReturnAction;
import org.kawanfw.file.servlet.nio.KawanfwSecurityManager;
import org.kawanfw.file.servlet.util.CallUtil;
import org.kawanfw.file.servlet.util.FileTransferManager;
import org.kawanfw.file.servlet.util.HttpConfigurationUtil;
import org.kawanfw.file.util.parms.Action;
import org.kawanfw.file.util.parms.Parameter;
import org.kawanfw.file.util.parms.ReturnCode;
import org.kawanfw.file.version.FileVersionValues;

/**
 * @author Nicolas de Pomereu
 * 
 *         The method executeRequest() is to to be called from the
 *         ServerCallerRecv Servlet and Class. 
* It will execute a client side request with a ServerCaller.call() * instance. * */ public class ServerFileDispatch { private static boolean DEBUG = FrameworkDebug .isSet(ServerFileDispatch.class); // A space public static final String SPACE = " "; public static String CR_LF = System.getProperty("line.separator"); public static KawanfwSecurityManager securityManager = null; /** * Constructor */ public ServerFileDispatch() { } /** * * Execute the dispatched request * * @param request * the http request * @param response * the http response * @param servletContextTempDir * The temp dir used by Servlets * @param commonsConfigurator * the client commons configurator * @param fileConfigurator * the client configurator for files * @throws IOException * if any Servlet Exception occurs */ public void executeRequest(HttpServletRequest request, HttpServletResponse response, File servletContextTempDir, CommonsConfigurator commonsConfigurator, FileConfigurator fileConfigurator) throws IOException { OutputStream out = null; try { // Immediate catch if we are asking a file upload, because // parameters are // in unknown sequence. We know it's a upload action if it's mime // multipart if (ServletFileUpload.isMultipartContent(request)) { ServerFileUploadAction serverFileUploadAction = new ServerFileUploadAction(); serverFileUploadAction.executeAction(request, response, servletContextTempDir, commonsConfigurator, fileConfigurator); return; } debug("ServerFileDispatch begin 2"); // The action & filename (for file size ask) String action = null; // We must trap the IllegalArgumentException to rethrow properly to // client // This happens if there is an encryption problem try { action = request.getParameter(Parameter.ACTION); } catch (IllegalArgumentException e) { out = response.getOutputStream(); throw e; } action = StringUtil.getTrimValue(action); debug("ACTION : " + action); // Special action for Login, because Token does not exists and must // be built if (action.equals(Action.LOGIN_ACTION) || action.equals(Action.BEFORE_LOGIN_ACTION)) { ServerLoginAction serverLoginAction = new ServerLoginAction(); serverLoginAction.executeAction(request, response, commonsConfigurator, action); return; } out = response.getOutputStream(); // Only if there is a call action, we may execute authorized classes // without authentication/login if (action.equals(Action.CALL_ACTION) || action.equals(Action.CALL_ACTION_HTML_ENCODED)) { // The class name String methodName = request.getParameter(Parameter.METHOD_NAME); methodName = StringUtil.getTrimValue(methodName); String className = StringUtils.substringBeforeLast(methodName, "."); Class c = Class.forName(className); CallUtil callUtil = new CallUtil(c, fileConfigurator); boolean callAllowed = callUtil.isCallableNotAuthenticated(); if (callAllowed) { if (action.equals(Action.CALL_ACTION) || action.equals(Action.CALL_ACTION_HTML_ENCODED)) { ServerCallAction serverCallAction = new ServerCallAction(); serverCallAction.call(request, commonsConfigurator, fileConfigurator, out, null); } return; } } // For all other actions, we check the parameters // The username (used for token re-compilation) String username = request.getParameter(Parameter.USERNAME); username = StringUtil.getTrimValue(username); debug("username : " + username); // // For old call(): // try { // username = StringUtil.fromBase64(username); // } catch (Exception e) { // } // The login may be in clear // Authentication Token with SHA-1(login + secret value) String token = request.getParameter(Parameter.TOKEN); token = StringUtil.getTrimValue(token); if (!ServerFileDispatch.isTokenValid(username, token, commonsConfigurator)) { debug("invalid token!"); debug("username: " + username); debug("token : " + token); writeLine(out, TransferStatus.SEND_OK); writeLine(out, ReturnCode.INVALID_LOGIN_OR_PASSWORD); return; } // Notify to Kawan in async mode using a secured Thread that // the user has successfully logged (done once in JVM session per // username). // No notification is done if user.home/.kawansoft/no_notify.txt // exists // or web server name is localhost or 127.0.0.1 if (!KawanNotifier.existsNoNotifyTxt() && !KawanNotifier.usernameAlreadyLogged(username) && !KawanNotifier.serverNameIsLocalhost()) { KawanNotifier kawanNotifier = new KawanNotifier(username, "AwakeFile_" + FileVersionValues.VERSION); kawanNotifier.start(); } // Ok, install our security manager //installSecurityManager(fileConfigurator); // Displays class path if (DEBUG) ClassPathUtil.displayClasspath(); // The filename String filename = request.getParameter(Parameter.FILENAME); filename = StringUtil.getTrimValue(filename); // Call to a File method that returns one result (no list return) if (action.equals(Action.FILE_METHOD_ONE_RETURN_ACTION)) { FileMethodOneReturnAction fileMethodOneReturnAction = new FileMethodOneReturnAction(); fileMethodOneReturnAction.call(request, commonsConfigurator, fileConfigurator, out, username, filename); return; } // Call to a File.list() or File.list(FilenameFilter) else if (action.equals(Action.FILE_LIST_ACTION)) { FileListAction fileListAction = new FileListAction(); fileListAction.list(request, commonsConfigurator, fileConfigurator, out, username, filename); return; } // Call to a File.listFiles() or File.listFiles(FileFilter) // or File.listFiles(FilenameFilter) else if (action.equals(Action.FILE_LIST_FILES_ACTION)) { FileListFilesAction fileListFilesAction = new FileListFilesAction(); fileListFilesAction.listFiles(request, commonsConfigurator, fileConfigurator, out, username, filename); return; } else if (action.equals(Action.CALL_ACTION) || action.equals(Action.CALL_ACTION_HTML_ENCODED)) { ServerCallAction serverCallAction = new ServerCallAction(); serverCallAction.call(request, commonsConfigurator, fileConfigurator, out, username); return; } else if (action.equals(Action.GET_FILE_LENGTH_ACTION)) { long result = actionGetListFileLength(fileConfigurator, username, filename); writeLine(out, TransferStatus.SEND_OK); writeLine(out, Long.toString(result)); } else if (action.equals(Action.GET_JAVA_VERSION)) { String javaVersion = System.getProperty("java.version"); writeLine(out, TransferStatus.SEND_OK); writeLine(out, javaVersion); } else if (action.equals(Action.DOWNLOAD_FILE_ACTION)) { String chunkLengtgStr = request .getParameter(Parameter.CHUNKLENGTH); long chunkLength = Long.parseLong(chunkLengtgStr); boolean result = new FileTransferManager().download(out, fileConfigurator, username, filename, chunkLength); if (!result) { // Impossible to find the file on server writeLine(out, TransferStatus.SEND_OK); writeLine(out, Tag.FileNotFoundException); // throw new FileNotFoundException( // "File not found on remote server: " + filename); } } else { throw new IllegalArgumentException("Invalid Client Action: " + action); } return; } catch (Throwable throwable) { if (DEBUG) throwable.printStackTrace(System.out); Throwable finalThrowable = getFinalThrowable(throwable); writeLine(out, TransferStatus.SEND_FAILED); writeLine(out, finalThrowable.getClass().getName()); // Exception class name writeLine(out, ServerUserThrowable.getMessage(finalThrowable)); // Exception // message writeLine(out, ExceptionUtils.getStackTrace(finalThrowable)); // stack trace try { ServerLogger.getLogger().log( Level.WARNING, Tag.PRODUCT_EXCEPTION_RAISED + " " + ServerUserThrowable.getMessage(finalThrowable)); ServerLogger.getLogger().log( Level.WARNING, Tag.PRODUCT_EXCEPTION_RAISED + " " + ExceptionUtils.getStackTrace(finalThrowable)); } catch (Exception e1) { e1.printStackTrace(); e1.printStackTrace(System.out); } } } /** * Analyse the throwable and build the final Exception/Throwable * @param throwable the input throwable thrown * @return the new rewritten Throwable */ public static Throwable getFinalThrowable(Throwable throwable) { Throwable finalThrowable = null; Throwable cause = throwable.getCause(); if (cause != null && cause instanceof ClassNotFoundException || cause != null && cause instanceof NoClassDefFoundError) { finalThrowable = new ClassNotFoundException(throwable.getMessage()); } else if (cause != null && cause instanceof UnsupportedClassVersionError) { finalThrowable = new UnsupportedClassVersionError( throwable.getMessage()); } else { if (cause != null) { finalThrowable = cause; } else { finalThrowable = throwable; } } return finalThrowable; } /** * NOT USED ANYMORE * Install the Security Manager that restricts FileFilter and FilenameFilter * to write/delete files. * @param fileConfigurator the file configurator in use */ @SuppressWarnings("unused") private void installSecurityManager(FileConfigurator fileConfigurator) { // Ok, install our security manager if (System.getSecurityManager() == null) { securityManager = new KawanfwSecurityManager(); System.setSecurityManager(securityManager); } } /** * Write a line of string on the servlet output stream. Will add the * necessary CR_LF * * @param out * the servlet output stream * @param s * the string to write * @throws IOException */ private void writeLine(OutputStream out, String s) throws IOException { out.write((s + CR_LF).getBytes()); } /** * Check the validity of the (username, token) pair
* Will return false if the operation is now allowed! * * @param username * the username to check * @param token * the associated token with the username * @param commonsConfigurator * the user configuration * * @return true if the pair (username, token) is verified and ok. * @throws Exception */ public static boolean isTokenValid(String username, String token, CommonsConfigurator commonsConfigurator) throws Exception { // OK! Now build a token with SHA-1(username + secretValue) String tokenRecomputed = CommonsConfiguratorCall.computeAuthToken( commonsConfigurator, username); if (token == null || !token.equals(tokenRecomputed)) { debug("username : " + username + ":"); debug("token : " + token + ":"); debug("tokenRecomputed: " + tokenRecomputed + ":"); return false; } return true; } /** * Action: get a file list length * * @param fileConfigurator * @param filename * the filelist * @return the length of file list */ private long actionGetListFileLength(FileConfigurator fileConfigurator, String username, String filename) throws IOException { debug("Action.GET_FILE_LENGTH_ACTION"); long result = 0; // We have in fact a list of files List files = ListOfStringTransport.fromJson(filename); // actionGetListFileLength: We must convert each element of List // files from Html files = HtmlConverter.fromHtml(files); for (String theFilename : files) { // result += fileActionManager.length(fileConfigurator, // username, theFilename); theFilename = HttpConfigurationUtil.addRootPath(fileConfigurator, username, theFilename); File file = new File(theFilename); result += file.length(); } return result; } private static void debug(String s) { if (DEBUG) { ServerLogger.getLogger().log(Level.WARNING, s); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy