diff options
Diffstat (limited to 'src/bin/agent/ca_response_creator.cc')
-rw-r--r-- | src/bin/agent/ca_response_creator.cc | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/bin/agent/ca_response_creator.cc b/src/bin/agent/ca_response_creator.cc new file mode 100644 index 0000000..5da0e4c --- /dev/null +++ b/src/bin/agent/ca_response_creator.cc @@ -0,0 +1,205 @@ +// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC") +// +// 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 <agent/ca_cfg_mgr.h> +#include <agent/ca_command_mgr.h> +#include <agent/ca_controller.h> +#include <agent/ca_process.h> +#include <agent/ca_response_creator.h> +#include <cc/data.h> +#include <hooks/callout_handle.h> +#include <hooks/hooks_log.h> +#include <hooks/hooks_manager.h> +#include <http/post_request_json.h> +#include <http/response_json.h> +#include <boost/pointer_cast.hpp> +#include <iostream> + +using namespace isc::data; +using namespace isc::hooks; +using namespace isc::http; + +namespace { + +/// Structure that holds registered hook indexes. +struct CtrlAgentHooks { + int hook_index_auth_; ///< index of "auth" hook point. + int hook_index_response_; ///< index of "response" hook point. + + /// Constructor that registers hook points. + CtrlAgentHooks() { + hook_index_auth_ = HooksManager::registerHook("auth"); + hook_index_response_ = HooksManager::registerHook("response"); + } +}; + +} // end of anonymous namespace. + +// Declare a Hooks object. As this is outside any function or method, it +// will be instantiated (and the constructor run) when the module is loaded. +// As a result, the hook indexes will be defined before any method in this +// module is called. +CtrlAgentHooks Hooks; + +namespace isc { +namespace agent { + +HttpRequestPtr +CtrlAgentResponseCreator::createNewHttpRequest() const { + return (HttpRequestPtr(new PostHttpRequestJson())); +} + +HttpResponsePtr +CtrlAgentResponseCreator:: +createStockHttpResponse(const HttpRequestPtr& request, + const HttpStatusCode& status_code) const { + HttpResponsePtr response = createStockHttpResponseInternal(request, status_code); + response->finalize(); + return (response); +} + +HttpResponsePtr +CtrlAgentResponseCreator:: +createStockHttpResponseInternal(const HttpRequestPtr& request, + const HttpStatusCode& status_code) const { + // The request hasn't been finalized so the request object + // doesn't contain any information about the HTTP version number + // used. But, the context should have this data (assuming the + // HTTP version is parsed ok). + HttpVersion http_version(request->context()->http_version_major_, + request->context()->http_version_minor_); + // We only accept HTTP version 1.0 or 1.1. If other version number is found + // we fall back to HTTP/1.0. + if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) { + http_version.major_ = 1; + http_version.minor_ = 0; + } + // This will generate the response holding JSON content. + HttpResponsePtr response(new HttpResponseJson(http_version, status_code)); + return (response); +} + +HttpResponsePtr +CtrlAgentResponseCreator:: +createDynamicHttpResponse(HttpRequestPtr request) { + // First check authentication. + HttpResponseJsonPtr http_response; + + // Context will hold the server configuration. + CtrlAgentCfgContextPtr ctx; + + // There is a hierarchy of the objects through which we need to pass to get + // the configuration context. We may simplify this at some point but since + // we're in the singleton we want to make sure that we're using most current + // configuration. + boost::shared_ptr<CtrlAgentController> controller = + boost::dynamic_pointer_cast<CtrlAgentController>(CtrlAgentController::instance()); + if (controller) { + CtrlAgentProcessPtr process = controller->getCtrlAgentProcess(); + if (process) { + CtrlAgentCfgMgrPtr cfgmgr = process->getCtrlAgentCfgMgr(); + if (cfgmgr) { + ctx = cfgmgr->getCtrlAgentCfgContext(); + if (ctx) { + const HttpAuthConfigPtr& auth = ctx->getAuthConfig(); + if (auth) { + // Check authentication. + http_response = auth->checkAuth(*this, request); + } + } + } + } + } + + // Callout point for "auth". + bool reset_handle = false; + if (HooksManager::calloutsPresent(Hooks.hook_index_auth_)) { + // Get callout handle. + CalloutHandlePtr callout_handle = request->getCalloutHandle(); + ScopedCalloutHandleState callout_handle_state(callout_handle); + + // Pass arguments. + callout_handle->setArgument("request", request); + callout_handle->setArgument("response", http_response); + + // Call callouts. + HooksManager::callCallouts(Hooks.hook_index_auth_, *callout_handle); + callout_handle->getArgument("request", request); + callout_handle->getArgument("response", http_response); + + // Status other than continue means 'please reset the handle'. + if (callout_handle->getStatus() != CalloutHandle::NEXT_STEP_CONTINUE) { + reset_handle = true; + } + } + + // The basic HTTP authentication check or a callout failed and + // left a response. + if (http_response) { + return (http_response); + } + + // Reset the handle when a hook asks for. + if (reset_handle) { + request->resetCalloutHandle(); + } + + // The request is always non-null, because this is verified by the + // createHttpResponse method. Let's try to convert it to the + // PostHttpRequestJson type as this is the type generated by the + // createNewHttpRequest. If the conversion result is null it means that + // the caller did not use createNewHttpRequest method to create this + // instance. This is considered an error in the server logic. + PostHttpRequestJsonPtr request_json = + boost::dynamic_pointer_cast<PostHttpRequestJson>(request); + if (!request_json) { + // Notify the client that we have a problem with our server. + return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR)); + } + + // We have already checked that the request is finalized so the call + // to getBodyAsJson must not trigger an exception. + ConstElementPtr command = request_json->getBodyAsJson(); + + // Process command doesn't generate exceptions but can possibly return + // null response, if the handler is not implemented properly. This is + // again an internal server issue. + ConstElementPtr response = CtrlAgentCommandMgr::instance().processCommand(command); + if (!response) { + // Notify the client that we have a problem with our server. + return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR)); + } + // The response is ok, so let's create new HTTP response with the status OK. + http_response = boost::dynamic_pointer_cast< + HttpResponseJson>(createStockHttpResponseInternal(request, HttpStatusCode::OK)); + http_response->setBodyAsJson(response); + http_response->finalize(); + + // Callout point for "response". + if (HooksManager::calloutsPresent(Hooks.hook_index_response_)) { + // Get callout handle. + CalloutHandlePtr callout_handle = request->getCalloutHandle(); + ScopedCalloutHandleState callout_handle_state(callout_handle); + + // Pass arguments. + callout_handle->setArgument("request", request); + callout_handle->setArgument("response", http_response); + + // Call callouts. + HooksManager::callCallouts(Hooks.hook_index_response_, + *callout_handle); + callout_handle->getArgument("response", http_response); + + // Ignore status as the HTTP response is used instead. + } + + return (http_response); +} + +} // end of namespace isc::agent +} // end of namespace isc |