From dac4cec12f6aeb5ee82c03bf1c13e6d2d655a34d Mon Sep 17 00:00:00 2001 From: cpatel129 Date: Sat, 31 Jul 2021 00:57:25 +0100 Subject: [PATCH] Implement Authintercept interface to communicate with configured http service --- CMakeLists.txt | 3 + README.md | 1 + amqpprox/amqpprox.m.cpp | 5 +- authproto/CMakeLists.txt | 8 + authproto/README.md | 24 ++ .../authrequest.proto | 25 +- .../authresponse.proto | 27 +- authproto/sasl.proto | 24 ++ buildfiles/conan/conanfile.txt | 1 + docs/config.md | 19 ++ libamqpprox/CMakeLists.txt | 9 +- libamqpprox/amqpprox_authcontrolcommand.cpp | 106 +++++++ libamqpprox/amqpprox_authcontrolcommand.h | 63 ++++ libamqpprox/amqpprox_authinterceptinterface.h | 15 +- libamqpprox/amqpprox_authrequestdata.h | 51 ---- libamqpprox/amqpprox_authresponsedata.h | 105 ------- libamqpprox/amqpprox_connector.cpp | 2 +- libamqpprox/amqpprox_connector.h | 2 +- libamqpprox/amqpprox_defaultauthintercept.cpp | 14 +- libamqpprox/amqpprox_defaultauthintercept.h | 7 +- libamqpprox/amqpprox_httpauthintercept.cpp | 289 ++++++++++++++++++ libamqpprox/amqpprox_httpauthintercept.h | 110 +++++++ libamqpprox/amqpprox_server.cpp | 22 ++ libamqpprox/amqpprox_server.h | 24 ++ libamqpprox/amqpprox_session.cpp | 48 +-- tests/CMakeLists.txt | 5 +- tests/amqpprox_defaultauthintercept.t.cpp | 20 +- tests/amqpprox_httpauthintercept.t.cpp | 50 +++ tests/amqpprox_session.t.cpp | 30 +- 29 files changed, 848 insertions(+), 261 deletions(-) create mode 100644 authproto/CMakeLists.txt create mode 100644 authproto/README.md rename libamqpprox/amqpprox_authrequestdata.cpp => authproto/authrequest.proto (57%) rename libamqpprox/amqpprox_authresponsedata.cpp => authproto/authresponse.proto (56%) create mode 100644 authproto/sasl.proto create mode 100644 libamqpprox/amqpprox_authcontrolcommand.cpp create mode 100644 libamqpprox/amqpprox_authcontrolcommand.h delete mode 100644 libamqpprox/amqpprox_authrequestdata.h delete mode 100644 libamqpprox/amqpprox_authresponsedata.h create mode 100644 libamqpprox/amqpprox_httpauthintercept.cpp create mode 100644 libamqpprox/amqpprox_httpauthintercept.h create mode 100644 tests/amqpprox_httpauthintercept.t.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 37a5d55..48903e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,9 @@ include("${BUILD_FLAVOUR_DIR}/main.pre.cmake") # Find threads find_package(Threads) +# Proto files to serialize/deserialize auth request/response data +add_subdirectory(authproto) + # Main library add_subdirectory(libamqpprox) diff --git a/README.md b/README.md index 0b43192..c5c1d00 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ Usage: amqpprox_ctl ARGS ``` $ amqpprox_ctl /tmp/amqpprox HELP +AUTH (SERVICE hostname port target | ALWAYS_ALLOW | PRINT) - Change authentication mechanism for connecting clients BACKEND (ADD name datacenter host port [SEND-PROXY] [TLS] | ADD_DNS name datacenter address port [SEND-PROXY] [TLS] | DELETE name | PRINT) - Change backend servers CONN Print the connected sessions DATACENTER SET name | PRINT diff --git a/amqpprox/amqpprox.m.cpp b/amqpprox/amqpprox.m.cpp index e43e3f5..48de1f2 100644 --- a/amqpprox/amqpprox.m.cpp +++ b/amqpprox/amqpprox.m.cpp @@ -42,6 +42,7 @@ #include // Control commands +#include #include #include #include @@ -77,7 +78,6 @@ Although most configuration is injected by the amqpprox_ctl program, the logging )helptext"; } - int main(int argc, char *argv[]) { using namespace std::string_literals; @@ -234,7 +234,8 @@ int main(int argc, char *argv[]) CommandPtr(new LoggingControlCommand), CommandPtr(new StatControlCommand(&eventSource)), CommandPtr(new MapHostnameControlCommand()), - CommandPtr(new TlsControlCommand)}; + CommandPtr(new TlsControlCommand), + CommandPtr(new AuthControlCommand)}; for (auto &&command : commands) { control.addControlCommand(std::move(command)); diff --git a/authproto/CMakeLists.txt b/authproto/CMakeLists.txt new file mode 100644 index 0000000..6fe3ef2 --- /dev/null +++ b/authproto/CMakeLists.txt @@ -0,0 +1,8 @@ +include(FindProtobuf) +find_package(Protobuf REQUIRED) + +include_directories(${Protobuf_INCLUDE_DIRS}) +protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS sasl.proto authrequest.proto authresponse.proto) +add_library(authproto ${PROTO_SRCS} ${PROTO_HDRS}) +set(PROTO_HDR_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) +target_link_libraries(authproto PUBLIC ${Protobuf_LIBRARIES}) diff --git a/authproto/README.md b/authproto/README.md new file mode 100644 index 0000000..fa94d48 --- /dev/null +++ b/authproto/README.md @@ -0,0 +1,24 @@ +## amqpprox HTTP Auth service + +### Introduction +Out of the box, amqpprox will perform no authentication of incoming connections - they only need to present a valid vhost. Using the `AUTH SERVICE ...` ctl command amqpprox can be configured to query an http service to auth inbound connections - a bit like [RabbitMQ's http auth backend](https://github.com/rabbitmq/rabbitmq-server/tree/master/deps/rabbitmq_auth_backend_http). +This document describes what information is sent to the service, and how the service should respond. + +### Request +The service will receive POST request from amqpprox proxy with following fields in [protobuf format](./authrequest.proto) +- Vhost - Vhost name for connecting clients +- SASL auth data + - Auth mechanism - Authentication mechanism field extracted from START-OK AMQP connection method, sent by client during handshake. E.g. PLAIN + - Credentials - Response field extracted from START-OK AMQP connection method, sent by client during handshake. E.g. '\0user\0password' + +### Response +The service should respond to amqpprox proxy with following fields in [protobuf format](./authresponse.proto) +- Auth Result - This will decide, whether to allow or deny clients. It is a enum type, takes ALLOW or DENY value only. +- Reason - Reason for the returned auth result. It is an optional field, but good to specify. +- SASL auth data - It is an optional field. In case of absence, the START-OK AMQP connection method, received from clients will be sent to the broker without any modification during handshake. + - Broker mechanism - Authentication mechanism field for START-OK AMQP connection method. This will be injected into START-OK AMQP connection method to send to the broker during handshake. E.g. PLAIN + - Credentials - Response field for START-OK AMQP connection method. This will also be injectd into START-OK AMQP connection method to send to the broker during handshake. E.g.'\0user\0password' + +The external HTTP auth service is used to authenticate clients during the handshake before connecting to the destination RabbitMQ broker. The service receives SASL fields (mechanism and credentials) to authenticate and replies with Allow/Deny, and (optionally) with overridden SASL fields for the outbound connection to the broker. + +The service is called once for each client connection, with a 30 seconds default timeout for each service request. diff --git a/libamqpprox/amqpprox_authrequestdata.cpp b/authproto/authrequest.proto similarity index 57% rename from libamqpprox/amqpprox_authrequestdata.cpp rename to authproto/authrequest.proto index bda003f..047f7f7 100644 --- a/libamqpprox/amqpprox_authrequestdata.cpp +++ b/authproto/authrequest.proto @@ -14,26 +14,13 @@ ** limitations under the License. */ -#include +syntax = "proto3"; -namespace Bloomberg { -namespace amqpprox { +import "sasl.proto"; -AuthRequestData::AuthRequestData(std::string_view vhostName, - std::string_view authMechanism, - std::string_view credentials) -: d_vhostName(vhostName) -, d_authMechanism(authMechanism) -, d_credentials(credentials) -{ -} - -AuthRequestData::AuthRequestData() -: d_vhostName() -, d_authMechanism() -, d_credentials() -{ -} +package Bloomberg.amqpprox.authproto; -} +message AuthRequest { + string vhostName = 1; + SASL authData = 2; } diff --git a/libamqpprox/amqpprox_authresponsedata.cpp b/authproto/authresponse.proto similarity index 56% rename from libamqpprox/amqpprox_authresponsedata.cpp rename to authproto/authresponse.proto index 813778e..ae9dd81 100644 --- a/libamqpprox/amqpprox_authresponsedata.cpp +++ b/authproto/authresponse.proto @@ -14,23 +14,18 @@ ** limitations under the License. */ -#include +syntax = "proto3"; -#include +import "sasl.proto"; -namespace Bloomberg { -namespace amqpprox { +package Bloomberg.amqpprox.authproto; -AuthResponseData::AuthResponseData(const AuthResult &authResult, - std::string_view reason, - std::string_view authMechanism, - std::string_view credentials) -: d_authResult(authResult) -, d_reason(reason) -, d_authMechanism(authMechanism) -, d_credentials(credentials) -{ -} - -} +message AuthResponse { + enum AuthResult { + ALLOW = 0; + DENY = 1; + } + AuthResult result = 1; + string reason = 2; + SASL authData = 3; } diff --git a/authproto/sasl.proto b/authproto/sasl.proto new file mode 100644 index 0000000..d9a6003 --- /dev/null +++ b/authproto/sasl.proto @@ -0,0 +1,24 @@ +/* +** Copyright 2021 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +syntax = "proto3"; + +package Bloomberg.amqpprox.authproto; + +message SASL { + string authMechanism = 1; + bytes credentials = 2; +} diff --git a/buildfiles/conan/conanfile.txt b/buildfiles/conan/conanfile.txt index f446291..2de0c75 100644 --- a/buildfiles/conan/conanfile.txt +++ b/buildfiles/conan/conanfile.txt @@ -2,6 +2,7 @@ boost/1.76.0 openssl/1.1.1k gtest/1.10.0 +protobuf/3.17.1 [generators] cmake diff --git a/docs/config.md b/docs/config.md index c323fde..e27d947 100644 --- a/docs/config.md +++ b/docs/config.md @@ -14,6 +14,7 @@ ``` $ amqpprox_ctl /tmp/amqpprox HELP +AUTH (SERVICE hostname port target | ALWAYS_ALLOW | PRINT) - Change authentication mechanism for connecting clients BACKEND (ADD name datacenter host port [SEND-PROXY] [TLS] | ADD_DNS name datacenter address port [SEND-PROXY] [TLS] | DELETE name | PRINT) - Change backend servers CONN Print the connected sessions DATACENTER SET name | PRINT @@ -30,6 +31,24 @@ TLS (INGRESS | EGRESS) (KEY_FILE file | CERT_CHAIN_FILE file | RSA_KEY_FILE file ``` +## AUTH commands + +The command represents authentication mechanism for connecting clients. By default all the clients will be allowed to connect to broker without any authentication. With the help of this command, one can set up an external HTTP auth service to authenticate clients, before allowing them to start communicating with broker. The schema structure for the HTTP auth service is defined [here](../authproto) + +#### AUTH SERVICE hostname port target + +Configures the external HTTP auth service to authenticate connecting clients. + +For example: `AUTH SERVICE localhost 1234 /auth?tier=dev` will trigger queries to http://localhost:1234/auth?tier=dev when a client connects. + +#### AUTH ALWAYS_ALLOW + +Stops authentication for connecting clients. So all clients will be allowed to connect to broker. + +#### AUTH PRINT + +Prints information about current configured auth mechanism. + ## BACKEND commands A backend represents an instance of an AMQP broker. diff --git a/libamqpprox/CMakeLists.txt b/libamqpprox/CMakeLists.txt index 1ec2ff6..da2f586 100644 --- a/libamqpprox/CMakeLists.txt +++ b/libamqpprox/CMakeLists.txt @@ -82,10 +82,11 @@ add_library(libamqpprox STATIC amqpprox_methods_start.cpp amqpprox_methods_startok.cpp amqpprox_authinterceptinterface.cpp - amqpprox_authrequestdata.cpp - amqpprox_authresponsedata.cpp - amqpprox_defaultauthintercept.cpp) + amqpprox_defaultauthintercept.cpp + amqpprox_httpauthintercept.cpp + amqpprox_authcontrolcommand.cpp) -target_link_libraries(libamqpprox LINK_PUBLIC ${LIBAMQPPROX_LIBS}) +target_include_directories(libamqpprox PRIVATE ${PROTO_HDR_PATH}) +target_link_libraries(libamqpprox LINK_PUBLIC authproto ${LIBAMQPPROX_LIBS}) include("${BUILD_FLAVOUR_DIR}/libamqpprox.post.cmake" OPTIONAL) diff --git a/libamqpprox/amqpprox_authcontrolcommand.cpp b/libamqpprox/amqpprox_authcontrolcommand.cpp new file mode 100644 index 0000000..46a2a09 --- /dev/null +++ b/libamqpprox/amqpprox_authcontrolcommand.cpp @@ -0,0 +1,106 @@ +/* +** Copyright 2021 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include + +namespace Bloomberg { +namespace amqpprox { + +AuthControlCommand::AuthControlCommand() +{ +} + +std::string AuthControlCommand::commandVerb() const +{ + return "AUTH"; +} + +std::string AuthControlCommand::helpText() const +{ + return "(SERVICE hostname port target | ALWAYS_ALLOW | PRINT) - " + "Change authentication mechanism for connecting clients"; +} + +void AuthControlCommand::handleCommand(const std::string & /* command */, + const std::string & restOfCommand, + const OutputFunctor &outputFunctor, + Server * serverHandle, + Control * /* controlHandle */) +{ + ControlCommandOutput output(outputFunctor); + + std::istringstream iss(restOfCommand); + std::string subcommand; + if (iss >> subcommand) { + boost::to_upper(subcommand); + + if (subcommand == "SERVICE") { + std::string hostname; + int port = -1; + std::string target; + if (!(iss >> hostname)) { + output << "No hostname specified.\n"; + return; + } + if (!(iss >> port && port > 0 && port <= 65535)) { + output << "Invalid port provided.\n"; + return; + } + if (!(iss >> target)) { + output << "No http target specified.\n"; + return; + } + + serverHandle->setAuthIntercept(std::make_shared( + serverHandle->ioService(), + hostname, + std::to_string(port), + target, + serverHandle->getDNSResolverPtr())); + + serverHandle->getAuthIntercept()->print(output); + } + else if (subcommand == "ALWAYS_ALLOW") { + serverHandle->setAuthIntercept( + std::make_shared( + serverHandle->ioService())); + + serverHandle->getAuthIntercept()->print(output); + } + else if (subcommand == "PRINT") { + std::ostringstream oss; + serverHandle->getAuthIntercept()->print(output); + } + else { + output << "Unknown subcommand.\n"; + } + } + else { + output << "No subcommand provided.\n"; + } +} + +} +} diff --git a/libamqpprox/amqpprox_authcontrolcommand.h b/libamqpprox/amqpprox_authcontrolcommand.h new file mode 100644 index 0000000..5b4408a --- /dev/null +++ b/libamqpprox/amqpprox_authcontrolcommand.h @@ -0,0 +1,63 @@ +/* +** Copyright 2021 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#ifndef BLOOMBERG_AMQPPROX_AUTHCONTROLCOMMAND +#define BLOOMBERG_AMQPPROX_AUTHCONTROLCOMMAND + +#include +#include + +#include + +namespace Bloomberg { +namespace amqpprox { + +class AuthControlCommand : public ControlCommand { + public: + // CREATORS + AuthControlCommand(); + + virtual ~AuthControlCommand() override = default; + + // MANIPULATORS + /** + * \return the command verb this handle + */ + virtual std::string commandVerb() const override; + + /** + * \return a string of the help text for this command + */ + virtual std::string helpText() const override; + + /** + * \brief Execute a command, providing any output to the provided functor + * \param command to execute + * \param restOfCommand parameters for the command + * \param outputFunctor is called back with the output + * \param serverHandle access to the Server object + * \param controlHandle access to the Control object + */ + virtual void handleCommand(const std::string & command, + const std::string & restOfCommand, + const OutputFunctor &outputFunctor, + Server * serverHandle, + Control * controlHandle) override; +}; + +} +} + +#endif diff --git a/libamqpprox/amqpprox_authinterceptinterface.h b/libamqpprox/amqpprox_authinterceptinterface.h index 1f81aee..24ba8e3 100644 --- a/libamqpprox/amqpprox_authinterceptinterface.h +++ b/libamqpprox/amqpprox_authinterceptinterface.h @@ -16,9 +16,6 @@ #ifndef BLOOMBERG_AMQPPROX_AUTHINTERCEPTINTERFACE #define BLOOMBERG_AMQPPROX_AUTHINTERCEPTINTERFACE -#include -#include - #include #include @@ -27,6 +24,11 @@ namespace Bloomberg { namespace amqpprox { +namespace authproto { +class AuthRequest; +class AuthResponse; +} + /** * \brief Provide a pure virtual interface for authn/authz operations */ @@ -39,7 +41,8 @@ class AuthInterceptInterface { * \brief Callback function to return response allow/deny with reason after * authenticating client connection. */ - typedef std::function ReceiveResponseCb; + typedef std::function + ReceiveResponseCb; // CREATORS explicit AuthInterceptInterface(boost::asio::io_service &ioService); @@ -54,8 +57,8 @@ class AuthInterceptInterface { * \param authRequestData auth request data payload * \param responseCb Callbak function with response values */ - virtual void authenticate(const AuthRequestData authRequestData, - const ReceiveResponseCb &responseCb) = 0; + virtual void authenticate(const authproto::AuthRequest authRequestData, + const ReceiveResponseCb & responseCb) = 0; // ACCESSORS /** diff --git a/libamqpprox/amqpprox_authrequestdata.h b/libamqpprox/amqpprox_authrequestdata.h deleted file mode 100644 index 81b3574..0000000 --- a/libamqpprox/amqpprox_authrequestdata.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -** Copyright 2021 Bloomberg Finance L.P. -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ -#ifndef BLOOMBERG_AMQPPROX_AUTHREQUESTDATA -#define BLOOMBERG_AMQPPROX_AUTHREQUESTDATA - -#include -#include - -namespace Bloomberg { -namespace amqpprox { - -/** - * \brief Provide a class to hold request data for authn/authz operations - */ -class AuthRequestData { - std::string d_vhostName; - std::string d_authMechanism; - std::string d_credentials; - - public: - /** - * \brief Create and initialize object of AuthRequestData class - * \param vhostName vhost name - * \param authMechanism authentication mechanism field for START-OK - * connection method - * \param credentials response field for START-OK connection method - */ - AuthRequestData(std::string_view vhostName, - std::string_view authMechanism, - std::string_view credentials); - - AuthRequestData(); -}; - -} -} - -#endif diff --git a/libamqpprox/amqpprox_authresponsedata.h b/libamqpprox/amqpprox_authresponsedata.h deleted file mode 100644 index 91c4089..0000000 --- a/libamqpprox/amqpprox_authresponsedata.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -** Copyright 2021 Bloomberg Finance L.P. -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ -#ifndef BLOOMBERG_AMQPPROX_AUTHRESPONSEDATA -#define BLOOMBERG_AMQPPROX_AUTHRESPONSEDATA - -#include -#include - -namespace Bloomberg { -namespace amqpprox { - -/** - * \brief Provide a class to hold response data for authn/authz operations - */ -class AuthResponseData { - public: - /** - * The enum class will represents authn/authz result for specific auth - * client request for particular AMQP connection - */ - enum class AuthResult { ALLOW, DENY }; - - private: - AuthResult d_authResult; - std::string d_reason; - std::string d_authMechanism; - std::string d_credentials; - - public: - /** - * \brief Create and initialize object of AuthResponseData class - * \param authResult represents authn/authz result for connecting AMQP - * client to proxy - * \param reason more detailed information related to auth result - * \param authMechanism authentication mechanism field for START-OK - * connection method - * \param credentials response field for START-OK connection method - */ - explicit AuthResponseData(const AuthResult &authResult, - std::string_view reason = "", - std::string_view authMechanism = "", - std::string_view credentials = ""); - - /** - * \return authn/authz result for connecting AMQP client to proxy - */ - inline const AuthResult getAuthResult() const; - - /** - * \return more detailed information related to auth result - */ - inline const std::string getReason() const; - - /** - * \return authentication mechanism field for START-OK connection method. - * This field will be injected to START-OK connection method to send to the - * broker. - */ - inline const std::string getAuthMechanism() const; - - /** - * \return response field for START-OK connection method. This field will - * be injected to START-OK connection method to send to the broker. - */ - inline const std::string getCredentials() const; -}; - -inline const AuthResponseData::AuthResult -AuthResponseData::getAuthResult() const -{ - return d_authResult; -} - -inline const std::string AuthResponseData::getReason() const -{ - return d_reason; -} - -inline const std::string AuthResponseData::getAuthMechanism() const -{ - return d_authMechanism; -} - -inline const std::string AuthResponseData::getCredentials() const -{ - return d_credentials; -} - -} -} - -#endif diff --git a/libamqpprox/amqpprox_connector.cpp b/libamqpprox/amqpprox_connector.cpp index a41d995..beca1cf 100644 --- a/libamqpprox/amqpprox_connector.cpp +++ b/libamqpprox/amqpprox_connector.cpp @@ -433,7 +433,7 @@ const FieldTable Connector::getClientProperties() const return d_startOk.properties(); } -const std::pair +const std::pair Connector::getAuthMechanismCredentials() const { return std::make_pair(d_startOk.mechanism(), d_startOk.response()); diff --git a/libamqpprox/amqpprox_connector.h b/libamqpprox/amqpprox_connector.h index 4db5d28..ec9da75 100644 --- a/libamqpprox/amqpprox_connector.h +++ b/libamqpprox/amqpprox_connector.h @@ -207,7 +207,7 @@ class Connector { * information from that method fields. * \return pair of AMQP authentication mechanism, AMQP response field */ - const std::pair + const std::pair getAuthMechanismCredentials() const; /** diff --git a/libamqpprox/amqpprox_defaultauthintercept.cpp b/libamqpprox/amqpprox_defaultauthintercept.cpp index b496efd..6230967 100644 --- a/libamqpprox/amqpprox_defaultauthintercept.cpp +++ b/libamqpprox/amqpprox_defaultauthintercept.cpp @@ -17,8 +17,8 @@ #include #include -#include -#include +#include +#include #include @@ -32,13 +32,13 @@ DefaultAuthIntercept::DefaultAuthIntercept(boost::asio::io_service &ioService) { } -void DefaultAuthIntercept::authenticate(const AuthRequestData, +void DefaultAuthIntercept::authenticate(const authproto::AuthRequest, const ReceiveResponseCb &responseCb) { auto cb = [responseCb] { - AuthResponseData authResponseData( - AuthResponseData::AuthResult::ALLOW, - "Default route auth used - always allow"); + authproto::AuthResponse authResponseData; + authResponseData.set_result(authproto::AuthResponse::ALLOW); + authResponseData.set_reason("Default route auth used - always allow"); responseCb(authResponseData); }; boost::asio::post(d_ioService, cb); @@ -47,7 +47,7 @@ void DefaultAuthIntercept::authenticate(const AuthRequestData, void DefaultAuthIntercept::print(std::ostream &os) const { os << "All connections are authorised to route to any vhost. No auth " - "service requests are made.\n"; + "service requests will be made.\n"; } } diff --git a/libamqpprox/amqpprox_defaultauthintercept.h b/libamqpprox/amqpprox_defaultauthintercept.h index 3ae0eb2..8ce00cd 100644 --- a/libamqpprox/amqpprox_defaultauthintercept.h +++ b/libamqpprox/amqpprox_defaultauthintercept.h @@ -17,7 +17,6 @@ #define BLOOMBERG_AMQPPROX_DEFAULTAUTHINTERCEPT #include -#include #include @@ -26,6 +25,10 @@ namespace Bloomberg { namespace amqpprox { +namespace authproto { +class AuthRequest; +} + /** * \brief Performs authn/authz operations for incoming clients, implements the * AuthInterceptInterface interface @@ -45,7 +48,7 @@ class DefaultAuthIntercept : public AuthInterceptInterface { * \param authRequestData auth request data payload * \param responseCb Callbak function with response values */ - virtual void authenticate(const AuthRequestData authRequestData, + virtual void authenticate(const authproto::AuthRequest authRequestData, const ReceiveResponseCb &responseCb) override; // ACCESSORS diff --git a/libamqpprox/amqpprox_httpauthintercept.cpp b/libamqpprox/amqpprox_httpauthintercept.cpp new file mode 100644 index 0000000..3ce23f3 --- /dev/null +++ b/libamqpprox/amqpprox_httpauthintercept.cpp @@ -0,0 +1,289 @@ +/* +** Copyright 2021 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Bloomberg { +namespace amqpprox { + +namespace { +namespace beast = boost::beast; +using tcp = boost::asio::ip::tcp; + +const int TIMEOUT_SECONDS = 30; +int HTTP_VERSION = 11; // HTTP/1.1 version +} + +HttpAuthIntercept::HttpAuthIntercept(boost::asio::io_service &ioService, + const std::string & hostname, + const std::string & port, + const std::string & target, + DNSResolver * dnsResolver) +: AuthInterceptInterface(ioService) +, d_ioService(ioService) +, d_hostname(hostname) +, d_port(port) +, d_target(target) +, d_dnsResolver_p(dnsResolver) +, d_mutex() +{ +} + +void HttpAuthIntercept::authenticate( + const authproto::AuthRequest authRequestData, + const ReceiveResponseCb & responseCb) +{ + std::shared_ptr> request = + std::make_shared>(); + request->version(HTTP_VERSION); + request->method(beast::http::verb::post); + request->target(d_target); + request->set(beast::http::field::host, d_hostname + ":" + d_port); + request->set(beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING); + request->set(beast::http::field::content_type, "application/octet-stream"); + std::string serializedRequestBody; + if (!authRequestData.SerializeToString(&serializedRequestBody)) { + const std::string errorMsg = + "Unable to serialize auth request data for http service."; + LOG_ERROR << errorMsg; + authproto::AuthResponse errorResponseData; + errorResponseData.set_result(authproto::AuthResponse::DENY); + errorResponseData.set_reason(errorMsg); + responseCb(errorResponseData); + return; + } + request->body() = serializedRequestBody; + request->prepare_payload(); + + // Look up the domain name + d_dnsResolver_p->resolve(d_hostname, + d_port, + std::bind(&HttpAuthIntercept::onResolve, + shared_from_this(), + request, + responseCb, + std::placeholders::_1, + std::placeholders::_2)); +} + +void HttpAuthIntercept::onResolve( + std::shared_ptr> request, + const ReceiveResponseCb & responseCb, + const boost::system::error_code & ec, + std::vector results) +{ + if (ec) { + const std::string errorMsg = + "Unable to resolve hostname " + d_hostname + + ", Error: {category: " + ec.category().name() + + ", message: " + ec.message() + + ", value: " + std::to_string(ec.value()) + "}"; + LOG_ERROR << errorMsg; + authproto::AuthResponse errorResponseData; + errorResponseData.set_result(authproto::AuthResponse::DENY); + errorResponseData.set_reason(errorMsg); + responseCb(errorResponseData); + return; + } + + std::shared_ptr stream = + std::make_shared( + boost::asio::make_strand(d_ioService)); + // Set a timeout on the operation + stream->expires_after(std::chrono::seconds(TIMEOUT_SECONDS)); + + // Make the connection on the IP address we get from a lookup + stream->async_connect( + results, + beast::bind_front_handler(&HttpAuthIntercept::onConnect, + shared_from_this(), + stream, + request, + responseCb)); +} + +void HttpAuthIntercept::onConnect( + std::shared_ptr stream, + std::shared_ptr> request, + const ReceiveResponseCb & responseCb, + beast::error_code ec, + tcp::resolver::results_type::endpoint_type) +{ + if (ec) { + const std::string errorMsg = + "Unable to connect hostname " + d_hostname + " on port " + d_port + + ", Error: {category: " + ec.category().name() + + ", message: " + ec.message() + + ", value: " + std::to_string(ec.value()) + "}"; + LOG_ERROR << errorMsg; + authproto::AuthResponse errorResponseData; + errorResponseData.set_result(authproto::AuthResponse::DENY); + errorResponseData.set_reason(errorMsg); + responseCb(errorResponseData); + return; + } + + // Set a timeout on the operation + stream->expires_after(std::chrono::seconds(TIMEOUT_SECONDS)); + + // Send the HTTP request to the remote host + beast::http::async_write( + *stream, + *request, + beast::bind_front_handler(&HttpAuthIntercept::onWrite, + shared_from_this(), + stream, + request, + responseCb)); +} + +void HttpAuthIntercept::onWrite( + std::shared_ptr stream, + std::shared_ptr>, // Extend the lifetime of request object + const ReceiveResponseCb &responseCb, + beast::error_code ec, + std::size_t bytes_transferred) +{ + boost::ignore_unused(bytes_transferred); + + if (ec) { + const std::string errorMsg = + "Unable to send http request to hostname " + d_hostname + + " on port " + d_port + + ", Error: {category: " + ec.category().name() + + ", message: " + ec.message() + + ", value: " + std::to_string(ec.value()) + "}"; + LOG_ERROR << errorMsg; + authproto::AuthResponse errorResponseData; + errorResponseData.set_result(authproto::AuthResponse::DENY); + errorResponseData.set_reason(errorMsg); + responseCb(errorResponseData); + return; + } + + std::shared_ptr> response = + std::make_shared>(); + + std::shared_ptr buffer = + std::make_shared(); + + // Receive the HTTP response + beast::http::async_read( + *stream, + *buffer, + *response, + beast::bind_front_handler(&HttpAuthIntercept::onRead, + shared_from_this(), + buffer, + stream, + response, + responseCb)); +} + +void HttpAuthIntercept::onRead( + std::shared_ptr, // Buffer must persists between reads + std::shared_ptr stream, + std::shared_ptr> response, + const ReceiveResponseCb &responseCb, + beast::error_code ec, + std::size_t bytes_transferred) +{ + boost::ignore_unused(bytes_transferred); + + if (ec) { + const std::string errorMsg = + "Unable to receive http response from hostname " + d_hostname + + " on port " + d_port + + ", Error: {category: " + ec.category().name() + + ", message: " + ec.message() + + ", value: " + std::to_string(ec.value()) + "}"; + LOG_ERROR << errorMsg; + authproto::AuthResponse errorResponseData; + errorResponseData.set_result(authproto::AuthResponse::DENY); + errorResponseData.set_reason(errorMsg); + responseCb(errorResponseData); + return; + } + + authproto::AuthResponse authResponseData; + if (!authResponseData.ParseFromString(response->body())) { + const std::string errorMsg = "Unable to deserialize auth response " + "data received from http service."; + LOG_ERROR << errorMsg; + authproto::AuthResponse errorResponseData; + errorResponseData.set_result(authproto::AuthResponse::DENY); + errorResponseData.set_reason(errorMsg); + responseCb(errorResponseData); + return; + } + LOG_TRACE << "Response from auth route gate service at " << d_hostname + << ":" << d_port << d_target << ": " + << "[ Auth Result: " + << ((authResponseData.result() == authproto::AuthResponse::ALLOW) + ? "ALLOW" + : "DENY") + << ", Reason: " << authResponseData.reason() + << ((authResponseData.has_authdata()) + ? (", Auth mechanism: " + + authResponseData.authdata().authmechanism()) + : "") + << " ]"; + responseCb(authResponseData); + + // Gracefully close the socket + stream->socket().shutdown(tcp::socket::shutdown_both, ec); + + // not_connected happens sometimes so don't bother reporting it. + if (ec && ec != beast::errc::not_connected) { + const std::string errorMsg = + "Unable to close socket and shutdown connection gracefully for " + "auth route gate service at " + + d_hostname + ":" + d_port + d_target + + ", Error: {category: " + ec.category().name() + + ", message: " + ec.message() + + ", value: " + std::to_string(ec.value()) + "}"; + LOG_WARN << errorMsg; + return; + } + + // If we get here then the connection is closed gracefully +} + +void HttpAuthIntercept::print(std::ostream &os) const +{ + std::lock_guard lg(d_mutex); + os << "HTTP Auth service will be used to authn/authz client connections: " + "http://" + << d_hostname << ":" << d_port << d_target << "\n"; +} +} +} diff --git a/libamqpprox/amqpprox_httpauthintercept.h b/libamqpprox/amqpprox_httpauthintercept.h new file mode 100644 index 0000000..683dbef --- /dev/null +++ b/libamqpprox/amqpprox_httpauthintercept.h @@ -0,0 +1,110 @@ +/* +** Copyright 2021 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#ifndef BLOOMBERG_AMQPPROX_HTTPAUTHINTERCEPT +#define BLOOMBERG_AMQPPROX_HTTPAUTHINTERCEPT + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace Bloomberg { +namespace amqpprox { + +namespace { +namespace beast = boost::beast; +using tcp = boost::asio::ip::tcp; +} + +class HttpAuthIntercept +: public AuthInterceptInterface, + public std::enable_shared_from_this { + boost::asio::io_service &d_ioService; + std::string d_hostname; + std::string d_port; + std::string d_target; + DNSResolver * d_dnsResolver_p; + mutable std::mutex d_mutex; + + void + onResolve(std::shared_ptr> + request, + const ReceiveResponseCb & responseCb, + const boost::system::error_code &ec, + std::vector results); + void + onConnect(std::shared_ptr stream, + std::shared_ptr> + request, + const ReceiveResponseCb &responseCb, + beast::error_code ec, + tcp::resolver::results_type::endpoint_type); + void + onWrite(std::shared_ptr stream, + std::shared_ptr> + request, + const ReceiveResponseCb &responseCb, + beast::error_code ec, + std::size_t bytes_transferred); + void + onRead(std::shared_ptr buffer, + std::shared_ptr stream, + std::shared_ptr> + response, + const ReceiveResponseCb &responseCb, + beast::error_code ec, + std::size_t bytes_transferred); + + public: + // CREATORS + HttpAuthIntercept(boost::asio::io_service &ioService, + const std::string & hostname, + const std::string & port, + const std::string & target, + DNSResolver * dnsResolver); + + virtual ~HttpAuthIntercept() override = default; + + // MANIPULATORS + /** + * \brief It gets all the information required to authenticate from client + * in requestBody parameter and invoke callback function to provide + * response. + * \param requestBody request data payload + * \param responseCb Callbak function with response values + */ + virtual void authenticate(const authproto::AuthRequest authRequestData, + const ReceiveResponseCb &responseCb) override; + + // ACCESSORS + /** + * \brief Print information about route auth gate service + * \param os output stream object + */ + virtual void print(std::ostream &os) const override; +}; + +} +} + +#endif diff --git a/libamqpprox/amqpprox_server.cpp b/libamqpprox/amqpprox_server.cpp index 3c61d41..a174998 100644 --- a/libamqpprox/amqpprox_server.cpp +++ b/libamqpprox/amqpprox_server.cpp @@ -270,6 +270,13 @@ void Server::setHostnameMapper( d_hostnameMapper = hostnameMapper; } +void Server::setAuthIntercept( + const std::shared_ptr &authIntercept) +{ + std::lock_guard lg(d_mutex); + d_authIntercept = authIntercept; +} + std::shared_ptr Server::getSession(uint64_t identifier) { std::lock_guard lg(d_mutex); @@ -319,5 +326,20 @@ boost::asio::ssl::context &Server::egressTlsContext() return d_egressTlsContext; } +boost::asio::io_service &Server::ioService() +{ + return d_ioService; +} + +std::shared_ptr Server::getAuthIntercept() const +{ + return d_authIntercept; +} + +DNSResolver *Server::getDNSResolverPtr() +{ + return &d_dnsResolver; +} + } // namespace amqpprox } // namespace Bloomberg diff --git a/libamqpprox/amqpprox_server.h b/libamqpprox/amqpprox_server.h index b3fdf03..336506c 100644 --- a/libamqpprox/amqpprox_server.h +++ b/libamqpprox/amqpprox_server.h @@ -120,6 +120,15 @@ class Server { void setHostnameMapper(const std::shared_ptr &hostnameMapper); + /** + * \brief Set a different AuthIntercept mechanism. Note that this does not + * update any AuthIntercept mechanism that are currently set on any + * sessions. + * \param authIntercept to authenticate connecting clients + */ + void setAuthIntercept( + const std::shared_ptr &authIntercept); + // ACCESSORS /** * \brief Get a particular session for a specified ID @@ -150,6 +159,21 @@ class Server { */ boost::asio::ssl::context &egressTlsContext(); + /** + * \return the boost::asio io service object + */ + boost::asio::io_service &ioService(); + + /** + * \return the current AuthIntercept mechanism applied on each session + */ + std::shared_ptr getAuthIntercept() const; + + /** + * \return the DNS resolver pointer, being used for each sessions + */ + DNSResolver *getDNSResolverPtr(); + private: void doAccept(int port, bool secure); void doTimer(); diff --git a/libamqpprox/amqpprox_session.cpp b/libamqpprox/amqpprox_session.cpp index 819f7a8..af7089e 100644 --- a/libamqpprox/amqpprox_session.cpp +++ b/libamqpprox/amqpprox_session.cpp @@ -16,8 +16,6 @@ #include #include -#include -#include #include #include #include @@ -37,12 +35,16 @@ #include #include #include +#include +#include +#include #include #include #include #include +#include #include #include @@ -413,24 +415,22 @@ void Session::establishConnection() auto self(shared_from_this()); auto authResponseCb = [this, self, connectionManager]( - const AuthResponseData &authResponseData) { - if (authResponseData.getAuthResult() == - AuthResponseData::AuthResult::DENY) { + const authproto::AuthResponse + &authResponseData) { + if (authResponseData.result() == authproto::AuthResponse::DENY) { LOG_ERROR << "Disconnecting unauthenticated/unauthorized client, " "reason: " - << authResponseData.getReason(); + << authResponseData.reason(); disconnectUnauthClient(d_connector.getClientProperties()); return; } - else if (authResponseData.getAuthResult() == - AuthResponseData::AuthResult::ALLOW) { + else if (authResponseData.result() == authproto::AuthResponse::ALLOW) { LOG_TRACE << "Authenticated/Authorized client, reason: " - << authResponseData.getReason(); - if (!authResponseData.getAuthMechanism().empty() && - !authResponseData.getCredentials().empty()) { - d_connector.setAuthMechanismCredentials( - authResponseData.getAuthMechanism(), - authResponseData.getCredentials()); + << authResponseData.reason(); + if (authResponseData.has_authdata()) { + authproto::SASL sasl = authResponseData.authdata(); + d_connector.setAuthMechanismCredentials(sasl.authmechanism(), + sasl.credentials()); } attemptConnection(connectionManager); } @@ -438,20 +438,24 @@ void Session::establishConnection() LOG_FATAL << "Not able to authn/authz client. Disconnecting " "client. Invalid response values from auth gate " "service, isAllowed: " - << static_cast(authResponseData.getAuthResult()) - << ", reason: " << authResponseData.getReason(); + << static_cast(authResponseData.result()) + << ", reason: " << authResponseData.reason(); disconnectUnauthClient(d_connector.getClientProperties()); return; } }; - const std::pair credentials = + const std::pair sasl = d_connector.getAuthMechanismCredentials(); - d_authIntercept->authenticate( - AuthRequestData(d_sessionState.getVirtualHost(), - credentials.first, - credentials.second), - authResponseCb); + // Initialize auth request data + authproto::AuthRequest authRequestData; + authRequestData.set_vhostname(d_sessionState.getVirtualHost()); + authproto::SASL *saslPtr = authRequestData.mutable_authdata(); + saslPtr->set_authmechanism(sasl.first); + saslPtr->set_credentials(sasl.second); + + // Authenticate connecting clients + d_authIntercept->authenticate(authRequestData, authResponseCb); } void Session::print(std::ostream &os) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 836a9ed..6bf4f9c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,9 +37,12 @@ add_executable(amqpprox_tests amqpprox_statsnapshot.t.cpp amqpprox_types.t.cpp amqpprox_vhoststate.t.cpp - amqpprox_defaultauthintercept.t.cpp) + amqpprox_defaultauthintercept.t.cpp + amqpprox_httpauthintercept.t.cpp) +target_include_directories(amqpprox_tests PRIVATE ${PROTO_HDR_PATH}) target_link_libraries(amqpprox_tests LINK_PUBLIC + authproto libamqpprox amqpprox_testapparatus gtest diff --git a/tests/amqpprox_defaultauthintercept.t.cpp b/tests/amqpprox_defaultauthintercept.t.cpp index 8a03cdb..df29caa 100644 --- a/tests/amqpprox_defaultauthintercept.t.cpp +++ b/tests/amqpprox_defaultauthintercept.t.cpp @@ -16,8 +16,8 @@ #include -#include -#include +#include +#include #include @@ -25,9 +25,8 @@ #include -using Bloomberg::amqpprox::AuthInterceptInterface; -using Bloomberg::amqpprox::AuthRequestData; -using Bloomberg::amqpprox::AuthResponseData; +using namespace Bloomberg; +using namespace amqpprox; using Bloomberg::amqpprox::DefaultAuthIntercept; TEST(DefaultAuthIntercept, Breathing) @@ -41,13 +40,12 @@ TEST(DefaultAuthIntercept, Authenticate) { boost::asio::io_service ioService; DefaultAuthIntercept defaultAuth(ioService); - auto responseCb = [](const AuthResponseData &authResponseData) { - ASSERT_EQ(authResponseData.getAuthResult(), - AuthResponseData::AuthResult::ALLOW); - ASSERT_EQ(authResponseData.getReason(), + auto responseCb = [](const authproto::AuthResponse &authResponseData) { + ASSERT_EQ(authResponseData.result(), authproto::AuthResponse::ALLOW); + ASSERT_EQ(authResponseData.reason(), "Default route auth used - always allow"); }; - defaultAuth.authenticate(AuthRequestData(), responseCb); + defaultAuth.authenticate(authproto::AuthRequest(), responseCb); ioService.run(); } @@ -60,5 +58,5 @@ TEST(DefaultAuthIntercept, Print) defaultAuth.print(oss); EXPECT_EQ(oss.str(), "All connections are authorised to route to any vhost. No auth " - "service requests are made.\n"); + "service requests will be made.\n"); } diff --git a/tests/amqpprox_httpauthintercept.t.cpp b/tests/amqpprox_httpauthintercept.t.cpp new file mode 100644 index 0000000..cd18175 --- /dev/null +++ b/tests/amqpprox_httpauthintercept.t.cpp @@ -0,0 +1,50 @@ +/* +** Copyright 2021 Bloomberg Finance L.P. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include + +#include + +#include + +#include + +using namespace Bloomberg; +using namespace amqpprox; +using Bloomberg::amqpprox::HttpAuthIntercept; + +TEST(HttpAuthIntercept, Breathing) +{ + boost::asio::io_service ioService; + DNSResolver dnsResolver(ioService); + HttpAuthIntercept authIntercept( + ioService, "localhost", "8080", "/target", &dnsResolver); + ioService.run(); +} + +TEST(HttpAuthIntercept, Print) +{ + boost::asio::io_service ioService; + DNSResolver dnsResolver(ioService); + HttpAuthIntercept authIntercept( + ioService, "localhost", "8080", "/target", &dnsResolver); + ioService.run(); + std::ostringstream oss; + authIntercept.print(oss); + EXPECT_EQ(oss.str(), + "HTTP Auth service will be used to authn/authz client " + "connections: http://localhost:8080/target\n"); +} diff --git a/tests/amqpprox_session.t.cpp b/tests/amqpprox_session.t.cpp index 0cfff5d..2b5d238 100644 --- a/tests/amqpprox_session.t.cpp +++ b/tests/amqpprox_session.t.cpp @@ -39,8 +39,6 @@ #define SOCKET_TESTING 1 #include -#include -#include #include #include #include @@ -62,6 +60,8 @@ #include #include #include +#include +#include #include #include @@ -114,7 +114,7 @@ struct AuthInterceptInterfaceMock : public AuthInterceptInterface { MOCK_CONST_METHOD1(print, void(std::ostream &)); MOCK_METHOD2(authenticate, - void(const AuthRequestData, + void(const authproto::AuthRequest, const AuthInterceptInterface::ReceiveResponseCb &)); }; @@ -1297,12 +1297,14 @@ TEST_F(SessionTest, Authorized_Client_Test) EXPECT_CALL(d_selector, acquireConnection(_, _)) .WillOnce(DoAll(SetArgPointee<0>(d_cm), Return(0))); - std::string modifiedMechanism = "TEST_MECHANISM"; - std::string modifiedCredentials = "credentials"; - AuthResponseData authResponseData(AuthResponseData::AuthResult::ALLOW, - "Authorized test client", - modifiedMechanism, - modifiedCredentials); + std::string modifiedMechanism = "TEST_MECHANISM"; + std::string modifiedCredentials = "credentials"; + authproto::AuthResponse authResponseData; + authResponseData.set_result(authproto::AuthResponse::ALLOW); + authResponseData.set_reason("Authorized test client"); + authproto::SASL *saslPtr = authResponseData.mutable_authdata(); + saslPtr->set_authmechanism(modifiedMechanism); + saslPtr->set_credentials(modifiedCredentials); EXPECT_CALL(*d_mockAuthIntercept, authenticate(_, _)) .WillOnce(InvokeArgument<1>(authResponseData)); @@ -1388,8 +1390,9 @@ TEST_F( EXPECT_CALL(d_selector, acquireConnection(_, _)) .WillOnce(DoAll(SetArgPointee<0>(d_cm), Return(0))); - AuthResponseData authResponseData(AuthResponseData::AuthResult::DENY, - "Unauthorized test client"); + authproto::AuthResponse authResponseData; + authResponseData.set_result(authproto::AuthResponse::DENY); + authResponseData.set_reason("Unauthorized test client"); EXPECT_CALL(*d_mockAuthIntercept, authenticate(_, _)) .WillOnce(InvokeArgument<1>(authResponseData)); @@ -1440,8 +1443,9 @@ TEST_F(SessionTest, EXPECT_CALL(d_selector, acquireConnection(_, _)) .WillOnce(DoAll(SetArgPointee<0>(d_cm), Return(0))); - AuthResponseData authResponseData(AuthResponseData::AuthResult::DENY, - "Unauthorized test client"); + authproto::AuthResponse authResponseData; + authResponseData.set_result(authproto::AuthResponse::DENY); + authResponseData.set_reason("Unauthorized test client"); EXPECT_CALL(*d_mockAuthIntercept, authenticate(_, _)) .WillOnce(InvokeArgument<1>(authResponseData));