loolwsd/Admin.cpp | 2 loolwsd/DocumentBroker.hpp | 2 loolwsd/FileServer.cpp | 245 +++++++++++++++++++++++++++++++++++++++++++++ loolwsd/FileServer.hpp | 232 +----------------------------------------- loolwsd/LOOLWSD.cpp | 1 loolwsd/Makefile.am | 2 6 files changed, 257 insertions(+), 227 deletions(-)
New commits: commit f9b86d749d4e10c314431a180d212f63e551c52a Author: Pranav Kant <[email protected]> Date: Tue Jul 19 22:44:32 2016 +0530 loolwsd: Split FileServer into header/implementation Change-Id: Idf0d2cb92028a79b8b32e0225ce5be1a1156542e diff --git a/loolwsd/FileServer.cpp b/loolwsd/FileServer.cpp new file mode 100644 index 0000000..507f512 --- /dev/null +++ b/loolwsd/FileServer.cpp @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "config.h" + +#include <string> +#include <vector> + +#include <Poco/FileStream.h> +#include <Poco/Net/HTTPCookie.h> +#include <Poco/Net/HTTPBasicCredentials.h> +#include <Poco/Net/HTMLForm.h> +#include <Poco/Net/HTTPRequest.h> +#include <Poco/Net/HTTPRequestHandler.h> +#include <Poco/Net/HTTPServer.h> +#include <Poco/Net/HTTPServerParams.h> +#include <Poco/Net/HTTPServerRequest.h> +#include <Poco/Net/HTTPServerResponse.h> +#include <Poco/Net/NetException.h> +#include <Poco/Net/SecureServerSocket.h> +#include <Poco/Net/WebSocket.h> +#include <Poco/RegularExpression.h> +#include <Poco/Runnable.h> +#include <Poco/StreamCopier.h> +#include <Poco/StringTokenizer.h> +#include <Poco/URI.h> +#include <Poco/Util/ServerApplication.h> +#include <Poco/Util/Timer.h> + +#include "Common.hpp" +#include "FileServer.hpp" +#include "LOOLWSD.hpp" + +using Poco::FileInputStream; +using Poco::Net::HTMLForm; +using Poco::Net::HTTPRequest; +using Poco::Net::HTTPRequestHandler; +using Poco::Net::HTTPRequestHandlerFactory; +using Poco::Net::HTTPResponse; +using Poco::Net::HTTPServerParams; +using Poco::Net::HTTPServerRequest; +using Poco::Net::HTTPServerResponse; +using Poco::Net::SecureServerSocket; +using Poco::Net::HTTPBasicCredentials; +using Poco::StreamCopier; +using Poco::Util::Application; + +bool FileServerRequestHandler::isAdminLoggedIn(HTTPServerRequest& request, HTTPServerResponse& response) +{ + const auto& config = Application::instance().config(); + const auto sslKeyPath = config.getString("ssl.key_file_path", ""); + + if (request.find("Cookie") != request.end()) + { + // FIXME: Handle other cookie params like '; httponly; secure' + const std::size_t pos = request["Cookie"].find_first_of("="); + if (pos == std::string::npos) + throw Poco::Net::NotAuthenticatedException("Missing JWT"); + + const std::string jwtToken = request["Cookie"].substr(pos + 1); + Log::info("Verifying JWT token: " + jwtToken); + JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin"); + if (authAgent.verify(jwtToken)) + { + Log::trace("JWT token is valid"); + return true; + } + + Log::info("Invalid JWT token, let the administrator re-login"); + } + + const auto user = config.getString("admin_console.username", ""); + const auto pass = config.getString("admin_console.password", ""); + if (user.empty() || pass.empty()) + { + Log::error("Admin Console credentials missing. Denying access until set."); + return false; + } + + HTTPBasicCredentials credentials(request); + if (credentials.getUsername() == user && + credentials.getPassword() == pass) + { + const std::string htmlMimeType = "text/html"; + // generate and set the cookie + JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin"); + const std::string jwtToken = authAgent.getAccessToken(); + Poco::Net::HTTPCookie cookie("jwt", jwtToken); + cookie.setPath("/lool/adminws/"); + cookie.setSecure(true); + cookie.setHttpOnly(true); + response.addCookie(cookie); + + return true; + } + + Log::info("Wrong admin credentials."); + return false; +} + +void FileServerRequestHandler::handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) +{ + try + { + Poco::URI requestUri(request.getURI()); + requestUri.normalize(); // avoid .'s and ..'s + + std::vector<std::string> requestSegments; + requestUri.getPathSegments(requestSegments); + if (requestSegments.size() < 1) + { + throw Poco::FileNotFoundException("Invalid URI request: [" + requestUri.toString() + "]."); + } + + const auto& config = Application::instance().config(); + const std::string loleafletHtml = config.getString("loleaflet_html", "loleaflet.html"); + const std::string endPoint = requestSegments[requestSegments.size() - 1]; + if (endPoint == loleafletHtml) + { + preprocessFile(request, response); + return; + } + + if (request.getMethod() == HTTPRequest::HTTP_GET) + { + if (endPoint == "admin.html" || + endPoint == "adminSettings.html" || + endPoint == "adminAnalytics.html") + { + if (!FileServerRequestHandler::isAdminLoggedIn(request, response)) + throw Poco::Net::NotAuthenticatedException("Invalid admin login"); + } + + const auto path = Poco::Path(LOOLWSD::FileServerRoot, getRequestPathname(request)); + const auto filepath = path.absolute().toString(); + if (filepath.find(LOOLWSD::FileServerRoot) != 0) + { + // Accessing unauthorized path. + throw Poco::FileAccessDeniedException("Invalid or forbidden file path: [" + filepath + "]."); + } + + const std::size_t extPoint = endPoint.find_last_of("."); + if (extPoint == std::string::npos) + throw Poco::FileNotFoundException("Invalid file."); + + const std::string fileType = endPoint.substr(extPoint + 1); + std::string mimeType; + if (fileType == "js") + mimeType = "application/javascript"; + else if (fileType == "css") + mimeType = "text/css"; + else if (fileType == "html") + mimeType = "text/html"; + else if (fileType == "svg") + mimeType = "image/svg+xml"; + else + mimeType = "text/plain"; + + response.setContentType(mimeType); + response.sendFile(filepath, mimeType); + } + } + catch (const Poco::Net::NotAuthenticatedException& exc) + { + Log::error("FileServerRequestHandler::NotAuthenticated: " + exc.displayText()); + response.set("WWW-Authenticate", "Basic realm=\"online\""); + response.setStatusAndReason(HTTPResponse::HTTP_UNAUTHORIZED); + response.setContentLength(0); + response.send(); + } + catch (const Poco::FileAccessDeniedException& exc) + { + Log::error("FileServerRequestHandler: " + exc.displayText()); + response.setStatusAndReason(HTTPResponse::HTTP_FORBIDDEN); + response.setContentLength(0); // TODO return some 403 page? + response.send(); + } + catch (const Poco::FileNotFoundException& exc) + { + Log::error("FileServerRequestHandler: " + exc.displayText()); + response.setStatusAndReason(HTTPResponse::HTTP_NOT_FOUND); + response.setContentLength(0); // TODO return some 404 page? + response.send(); + } +} + +std::string FileServerRequestHandler::getRequestPathname(const HTTPServerRequest& request) +{ + Poco::URI requestUri(request.getURI()); + // avoid .'s and ..'s + requestUri.normalize(); + + std::string path(requestUri.getPath()); + + // convert version back to a real file name + Poco::replaceInPlace(path, std::string("/loleaflet/" LOOLWSD_VERSION_HASH "/"), std::string("/loleaflet/dist/")); + + return path; +} + +void FileServerRequestHandler::preprocessFile(HTTPServerRequest& request, HTTPServerResponse& response) throw(Poco::FileAccessDeniedException) +{ + HTMLForm form(request, request.stream()); + + const auto host = (LOOLWSD::isSSLEnabled() ? "wss://" : "ws://") + (LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName); + const auto path = Poco::Path(LOOLWSD::FileServerRoot, getRequestPathname(request)); + + Log::debug("Preprocessing file: " + path.toString()); + + std::string preprocess; + FileInputStream file(path.toString()); + StreamCopier::copyToString(file, preprocess); + file.close(); + + const std::string& accessToken = form.get("access_token", ""); + const std::string& accessTokenTtl = form.get("access_token_ttl", ""); + + // As of now only alphanumeric characters are allowed in access token + // Sanitize user input before replacing + Poco::RegularExpression re("[a-zA-Z0-9_]*", Poco::RegularExpression::RE_ANCHORED); + if (!re.match(accessToken, 0, 0) || !re.match(accessTokenTtl, 0, 0)) + { + throw Poco::FileAccessDeniedException("Invalid access token provided. Only alphanumeric and _ are allowed "); + } + + Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN%"), accessToken); + Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN_TTL%"), accessTokenTtl); + Poco::replaceInPlace(preprocess, std::string("%HOST%"), host); + Poco::replaceInPlace(preprocess, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH)); + + response.setContentType("text/html"); + response.setContentLength(preprocess.length()); + response.setChunkedTransferEncoding(false); + + std::ostream& ostr = response.send(); + ostr << preprocess; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/loolwsd/FileServer.hpp b/loolwsd/FileServer.hpp index 6903b0d..ac8d44d 100644 --- a/loolwsd/FileServer.hpp +++ b/loolwsd/FileServer.hpp @@ -15,241 +15,26 @@ #include <string> #include <vector> -#include <Poco/FileStream.h> -#include <Poco/Net/HTTPCookie.h> -#include <Poco/Net/HTTPBasicCredentials.h> -#include <Poco/Net/HTMLForm.h> #include <Poco/Net/HTTPRequest.h> #include <Poco/Net/HTTPRequestHandler.h> #include <Poco/Net/HTTPServer.h> -#include <Poco/Net/HTTPServerParams.h> #include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPServerResponse.h> -#include <Poco/Net/NetException.h> #include <Poco/Net/SecureServerSocket.h> -#include <Poco/Net/WebSocket.h> -#include <Poco/RegularExpression.h> -#include <Poco/Runnable.h> -#include <Poco/StreamCopier.h> -#include <Poco/StringTokenizer.h> -#include <Poco/URI.h> -#include <Poco/Util/ServerApplication.h> -#include <Poco/Util/Timer.h> -#include "Common.hpp" -#include "LOOLWSD.hpp" +#include "Log.hpp" -using Poco::FileInputStream; -using Poco::Net::HTMLForm; -using Poco::Net::HTTPRequest; -using Poco::Net::HTTPRequestHandler; -using Poco::Net::HTTPRequestHandlerFactory; -using Poco::Net::HTTPResponse; -using Poco::Net::HTTPServerParams; -using Poco::Net::HTTPServerRequest; -using Poco::Net::HTTPServerResponse; -using Poco::Net::SecureServerSocket; -using Poco::Net::HTTPBasicCredentials; -using Poco::StreamCopier; -using Poco::Util::Application; - -class FileServerRequestHandler: public HTTPRequestHandler +class FileServerRequestHandler: public Poco::Net::HTTPRequestHandler { -public: - - /// Evaluate if the cookie exists, and if not, ask for the credentials. - static bool isAdminLoggedIn(HTTPServerRequest& request, HTTPServerResponse& response) - { - const auto& config = Application::instance().config(); - const auto sslKeyPath = config.getString("ssl.key_file_path", ""); - - if (request.find("Cookie") != request.end()) - { - // FIXME: Handle other cookie params like '; httponly; secure' - const std::size_t pos = request["Cookie"].find_first_of("="); - if (pos == std::string::npos) - throw Poco::Net::NotAuthenticatedException("Missing JWT"); - - const std::string jwtToken = request["Cookie"].substr(pos + 1); - Log::info("Verifying JWT token: " + jwtToken); - JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin"); - if (authAgent.verify(jwtToken)) - { - Log::trace("JWT token is valid"); - return true; - } - - Log::info("Invalid JWT token, let the administrator re-login"); - } - - const auto user = config.getString("admin_console.username", ""); - const auto pass = config.getString("admin_console.password", ""); - if (user.empty() || pass.empty()) - { - Log::error("Admin Console credentials missing. Denying access until set."); - return false; - } - - HTTPBasicCredentials credentials(request); - if (credentials.getUsername() == user && - credentials.getPassword() == pass) - { - const std::string htmlMimeType = "text/html"; - // generate and set the cookie - JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin"); - const std::string jwtToken = authAgent.getAccessToken(); - Poco::Net::HTTPCookie cookie("jwt", jwtToken); - cookie.setPath("/lool/adminws/"); - cookie.setSecure(true); - cookie.setHttpOnly(true); - response.addCookie(cookie); - - return true; - } - - Log::info("Wrong admin credentials."); - return false; - } - - void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) override - { - try - { - Poco::URI requestUri(request.getURI()); - requestUri.normalize(); // avoid .'s and ..'s - - std::vector<std::string> requestSegments; - requestUri.getPathSegments(requestSegments); - if (requestSegments.size() < 1) - { - throw Poco::FileNotFoundException("Invalid URI request: [" + requestUri.toString() + "]."); - } - - const auto& config = Application::instance().config(); - const std::string loleafletHtml = config.getString("loleaflet_html", "loleaflet.html"); - const std::string endPoint = requestSegments[requestSegments.size() - 1]; - if (endPoint == loleafletHtml) - { - preprocessFile(request, response); - return; - } - - if (request.getMethod() == HTTPRequest::HTTP_GET) - { - if (endPoint == "admin.html" || - endPoint == "adminSettings.html" || - endPoint == "adminAnalytics.html") - { - if (!FileServerRequestHandler::isAdminLoggedIn(request, response)) - throw Poco::Net::NotAuthenticatedException("Invalid admin login"); - } - - const auto path = Poco::Path(LOOLWSD::FileServerRoot, getRequestPathname(request)); - const auto filepath = path.absolute().toString(); - if (filepath.find(LOOLWSD::FileServerRoot) != 0) - { - // Accessing unauthorized path. - throw Poco::FileAccessDeniedException("Invalid or forbidden file path: [" + filepath + "]."); - } - - const std::size_t extPoint = endPoint.find_last_of("."); - if (extPoint == std::string::npos) - throw Poco::FileNotFoundException("Invalid file."); + std::string getRequestPathname(const Poco::Net::HTTPServerRequest& request); - const std::string fileType = endPoint.substr(extPoint + 1); - std::string mimeType; - if (fileType == "js") - mimeType = "application/javascript"; - else if (fileType == "css") - mimeType = "text/css"; - else if (fileType == "html") - mimeType = "text/html"; - else if (fileType == "svg") - mimeType = "image/svg+xml"; - else - mimeType = "text/plain"; + void preprocessFile(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) throw(Poco::FileAccessDeniedException); - response.setContentType(mimeType); - response.sendFile(filepath, mimeType); - } - } - catch (const Poco::Net::NotAuthenticatedException& exc) - { - Log::error("FileServerRequestHandler::NotAuthenticated: " + exc.displayText()); - response.set("WWW-Authenticate", "Basic realm=\"online\""); - response.setStatusAndReason(HTTPResponse::HTTP_UNAUTHORIZED); - response.setContentLength(0); - response.send(); - } - catch (const Poco::FileAccessDeniedException& exc) - { - Log::error("FileServerRequestHandler: " + exc.displayText()); - response.setStatusAndReason(HTTPResponse::HTTP_FORBIDDEN); - response.setContentLength(0); // TODO return some 403 page? - response.send(); - } - catch (const Poco::FileNotFoundException& exc) - { - Log::error("FileServerRequestHandler: " + exc.displayText()); - response.setStatusAndReason(HTTPResponse::HTTP_NOT_FOUND); - response.setContentLength(0); // TODO return some 404 page? - response.send(); - } - } - -private: - - std::string getRequestPathname(const HTTPServerRequest& request) - { - Poco::URI requestUri(request.getURI()); - // avoid .'s and ..'s - requestUri.normalize(); - - std::string path(requestUri.getPath()); - - // convert version back to a real file name - Poco::replaceInPlace(path, std::string("/loleaflet/" LOOLWSD_VERSION_HASH "/"), std::string("/loleaflet/dist/")); - - return path; - } - - void preprocessFile(HTTPServerRequest& request, HTTPServerResponse& response) throw(Poco::FileAccessDeniedException) - { - HTMLForm form(request, request.stream()); - - const auto host = (LOOLWSD::isSSLEnabled() ? "wss://" : "ws://") + (LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName); - const auto path = Poco::Path(LOOLWSD::FileServerRoot, getRequestPathname(request)); - - Log::debug("Preprocessing file: " + path.toString()); - - std::string preprocess; - FileInputStream file(path.toString()); - StreamCopier::copyToString(file, preprocess); - file.close(); - - const std::string& accessToken = form.get("access_token", ""); - const std::string& accessTokenTtl = form.get("access_token_ttl", ""); - - // As of now only alphanumeric characters are allowed in access token - // Sanitize user input before replacing - Poco::RegularExpression re("[a-zA-Z0-9_]*", Poco::RegularExpression::RE_ANCHORED); - if (!re.match(accessToken, 0, 0) || !re.match(accessTokenTtl, 0, 0)) - { - throw Poco::FileAccessDeniedException("Invalid access token provided. Only alphanumeric and _ are allowed "); - } - - Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN%"), accessToken); - Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN_TTL%"), accessTokenTtl); - Poco::replaceInPlace(preprocess, std::string("%HOST%"), host); - Poco::replaceInPlace(preprocess, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH)); - - response.setContentType("text/html"); - response.setContentLength(preprocess.length()); - response.setChunkedTransferEncoding(false); +public: + /// Evaluate if the cookie exists, and if not, ask for the credentials. + static bool isAdminLoggedIn(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response); - std::ostream& ostr = response.send(); - ostr << preprocess; - } + void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) override; }; class FileServer diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am index 5e7186d..148e9c9 100644 --- a/loolwsd/Makefile.am +++ b/loolwsd/Makefile.am @@ -44,6 +44,7 @@ loolwsd_SOURCES = Admin.cpp \ DocumentBroker.cpp \ LOOLWSD.cpp \ ClientSession.cpp \ + FileServer.cpp \ PrisonerSession.cpp \ Storage.cpp \ TileCache.cpp \ @@ -175,4 +176,3 @@ all-local: loolforkit @JAILS_PATH@ $(SYSTEM_STAMP) else \ echo "Skipping capability setting"; \ fi - commit ff5704a3cf02eed2687a4604ce9a29be79cc3b59 Author: Pranav Kant <[email protected]> Date: Tue Jul 19 22:40:14 2016 +0530 loolwsd: Drop forward decl. and add missing include ... instead of including Storage.hpp through some funny inclusion of header files. Change-Id: I7b6d63b687ef92c4523c01455172ad9fa08fe14a diff --git a/loolwsd/DocumentBroker.hpp b/loolwsd/DocumentBroker.hpp index a589143..1324005 100644 --- a/loolwsd/DocumentBroker.hpp +++ b/loolwsd/DocumentBroker.hpp @@ -26,11 +26,11 @@ #include "IoUtil.hpp" #include "Log.hpp" +#include "Storage.hpp" #include "TileCache.hpp" #include "Util.hpp" // Forwards. -class StorageBase; class DocumentBroker; /// Represents a new LOK child that is read commit 478879f1812b22811884acacaca4f475c06aebfe Author: Pranav Kant <[email protected]> Date: Tue Jul 19 22:14:29 2016 +0530 loolwsd: Reorder header files alphabetically Change-Id: Ic83155ddf596c7d5168ef37721417d211b85313d diff --git a/loolwsd/Admin.cpp b/loolwsd/Admin.cpp index 540d36b..952544b 100644 --- a/loolwsd/Admin.cpp +++ b/loolwsd/Admin.cpp @@ -7,7 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "Admin.hpp" #include "config.h" #include <cassert> @@ -27,6 +26,7 @@ #include <Poco/Util/ServerApplication.h> #include <Poco/Util/Timer.h> +#include "Admin.hpp" #include "AdminModel.hpp" #include "Auth.hpp" #include "Common.hpp" diff --git a/loolwsd/FileServer.hpp b/loolwsd/FileServer.hpp index 574d0b0..6903b0d 100644 --- a/loolwsd/FileServer.hpp +++ b/loolwsd/FileServer.hpp @@ -15,8 +15,7 @@ #include <string> #include <vector> -#include <Poco/Net/NetException.h> - +#include <Poco/FileStream.h> #include <Poco/Net/HTTPCookie.h> #include <Poco/Net/HTTPBasicCredentials.h> #include <Poco/Net/HTMLForm.h> @@ -26,20 +25,21 @@ #include <Poco/Net/HTTPServerParams.h> #include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPServerResponse.h> +#include <Poco/Net/NetException.h> #include <Poco/Net/SecureServerSocket.h> #include <Poco/Net/WebSocket.h> +#include <Poco/RegularExpression.h> #include <Poco/Runnable.h> +#include <Poco/StreamCopier.h> #include <Poco/StringTokenizer.h> #include <Poco/URI.h> -#include <Poco/FileStream.h> -#include <Poco/RegularExpression.h> -#include <Poco/StreamCopier.h> #include <Poco/Util/ServerApplication.h> #include <Poco/Util/Timer.h> #include "Common.hpp" #include "LOOLWSD.hpp" +using Poco::FileInputStream; using Poco::Net::HTMLForm; using Poco::Net::HTTPRequest; using Poco::Net::HTTPRequestHandler; @@ -50,7 +50,6 @@ using Poco::Net::HTTPServerRequest; using Poco::Net::HTTPServerResponse; using Poco::Net::SecureServerSocket; using Poco::Net::HTTPBasicCredentials; -using Poco::FileInputStream; using Poco::StreamCopier; using Poco::Util::Application; commit c94d2b79fbc6c6687f3f5757c3e11082b4a0dbdd Author: Pranav Kant <[email protected]> Date: Tue Jul 19 22:10:17 2016 +0530 loolwsd: Missing include Though it still works without it through some other indirect include. Better explicitly include it. Change-Id: Ia0783cd1b1116d5269248ff29f7e7239c3826644 diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp index cbd3fca..3fe3c96 100644 --- a/loolwsd/LOOLWSD.cpp +++ b/loolwsd/LOOLWSD.cpp @@ -69,6 +69,7 @@ #include <Poco/Net/InvalidCertificateHandler.h> #include <Poco/Net/KeyConsoleHandler.h> #include <Poco/Net/MessageHeader.h> +#include <Poco/Net/NameValueCollection.h> #include <Poco/Net/Net.h> #include <Poco/Net/NetException.h> #include <Poco/Net/PartHandler.h> _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
