From e8d096eec37ff936be83afeb4703a520f205e40f Mon Sep 17 00:00:00 2001 From: Radoslaw Bartosiak Date: Mon, 8 Sep 2014 13:45:22 +0200 Subject: [PATCH 01/16] Add creds configuration Configuration is used by cynara_creds_get_[client|user]_method to provide default values of [client|user] feature used in cynara-creds. Change-Id: I9a8b8e0bb009817414b9755523a60edb3d0386d0 Signed-off-by: Radoslaw Bartosiak --- conf/creds.conf | 2 ++ packaging/cynara.spec | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 conf/creds.conf diff --git a/conf/creds.conf b/conf/creds.conf new file mode 100644 index 0000000..10bce6b --- /dev/null +++ b/conf/creds.conf @@ -0,0 +1,2 @@ +client_default=smack +user_default=uid diff --git a/packaging/cynara.spec b/packaging/cynara.spec index 4d067c3..ffcccfb 100644 --- a/packaging/cynara.spec +++ b/packaging/cynara.spec @@ -31,7 +31,8 @@ BuildRequires: pkgconfig(libsystemd-journal) %global group_name %{name} %global state_path %{_localstatedir}/%{name}/ -%global tests_dir %{_datarootdir}/%{name}/tests +%global tests_dir %{_datarootdir}/%{name}/tests/ +%global conf_path %{_sysconfdir}/%{name}/ %if !%{defined build_type} %define build_type RELEASE @@ -218,7 +219,6 @@ BuildRequires: pkgconfig(gmock) Cynara tests ####################################################### - %package -n cynara-devel Summary: Cynara service (devel) Requires: cynara = %{version}-%{release} @@ -252,7 +252,8 @@ export CXXFLAGS="$CXXFLAGS -Wp,-U_FORTIFY_SOURCE" %endif export CXXFLAGS="$CXXFLAGS -DCYNARA_STATE_PATH=\\\"%{state_path}\\\" \ - -DCYNARA_TESTS_DIR=\\\"%{tests_dir}\\\"" + -DCYNARA_TESTS_DIR=\\\"%{tests_dir}\\\" \ + -DCYNARA_CONFIGURATION_DIR=\\\"%{conf_path}\\\"" export LDFLAGS+="-Wl,--rpath=%{_libdir}" %cmake . \ @@ -262,10 +263,12 @@ export LDFLAGS+="-Wl,--rpath=%{_libdir}" make %{?jobs:-j%jobs} %install - rm -rf %{buildroot} %make_install +mkdir -p %{buildroot}/%{conf_path} +cp ./conf/creds.conf %{buildroot}/%{conf_path}/creds.conf + mkdir -p %{buildroot}/usr/lib/systemd/system/sockets.target.wants mkdir -p %{buildroot}/%{state_path} mkdir -p %{buildroot}/%{tests_dir} @@ -476,6 +479,7 @@ fi %manifest libcynara-creds-commons.manifest %license LICENSE %{_libdir}/libcynara-creds-commons.so.* +%{conf_path}creds.conf %files -n libcynara-creds-commons-devel %{_includedir}/cynara/cynara-creds-commons.h -- 2.7.4 From ac02ce7589b9d88771eb180cc26552bdcd7afa9b Mon Sep 17 00:00:00 2001 From: Radoslaw Bartosiak Date: Wed, 10 Sep 2014 15:04:17 +0200 Subject: [PATCH 02/16] Implement cynara_creds_get_[client|user]_method The functions enable obtaining system default identification method for [process|user] by reading a configuration file (default /etc/cynara/creds.conf). Change-Id: I662a7681abbaa130a3d628352a13ff950a7affd3 Signed-off-by: Radoslaw Bartosiak --- src/helpers/creds-commons/CMakeLists.txt | 1 + src/helpers/creds-commons/CredsCommonsInner.cpp | 133 ++++++++++++++++++++++++ src/helpers/creds-commons/CredsCommonsInner.h | 53 ++++++++++ src/helpers/creds-commons/creds-commons.cpp | 36 +++++-- src/include/cynara-creds-commons.h | 39 ++++--- src/include/cynara-error.h | 3 + 6 files changed, 246 insertions(+), 19 deletions(-) create mode 100644 src/helpers/creds-commons/CredsCommonsInner.cpp create mode 100644 src/helpers/creds-commons/CredsCommonsInner.h diff --git a/src/helpers/creds-commons/CMakeLists.txt b/src/helpers/creds-commons/CMakeLists.txt index 8cca765..5dd5007 100644 --- a/src/helpers/creds-commons/CMakeLists.txt +++ b/src/helpers/creds-commons/CMakeLists.txt @@ -25,6 +25,7 @@ SET(LIB_CREDS_COMMONS_PATH ${CYNARA_PATH}/helpers/creds-commons) SET(LIB_CREDS_COMMONS_SOURCES ${LIB_CREDS_COMMONS_PATH}/creds-commons.cpp + ${LIB_CREDS_COMMONS_PATH}/CredsCommonsInner.cpp ) INCLUDE_DIRECTORIES( diff --git a/src/helpers/creds-commons/CredsCommonsInner.cpp b/src/helpers/creds-commons/CredsCommonsInner.cpp new file mode 100644 index 0000000..c9f0538 --- /dev/null +++ b/src/helpers/creds-commons/CredsCommonsInner.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ + /** + * @file src/helpers/creds-commons/CredsCommonsInner.cpp + * @author Radoslaw Bartosiak + * @version 1.0 + * @brief Implementation of internal credential commons functions + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace Cynara { + +std::string CredsCommonsInnerBackend::credsConfigurationFile(void) { + std::string confFile("/etc/cynara/creds.conf"); +#ifdef CYNARA_CONFIGURATION_DIR + confFile = std::string(CYNARA_CONFIGURATION_DIR) + std::string("/creds.conf"); +#else + LOGW("Cynara compiled without CYNARA_CONFIGURATION_DIR flag." + " Using default configuration directory."); +#endif + return confFile; +} +// parses stream with configuration skipping comments (from # till the end of line) +// untill a line of form = +// is found. Returns true when key and value FOUND, false when no more lines +bool CredsCommonsInnerBackend::getKeyAndValue(std::istream &f, const std::locale &loc, + std::string &key, std::string &value) { + std::string rawLine; + while (std::getline(f, rawLine)) { + std::istringstream rawLineStream(rawLine); + std::string uncommentedLine; + if (std::getline(rawLineStream, uncommentedLine, '#')) { + size_t found = uncommentedLine.find_first_of("="); + if (found != std::string::npos) { + std::string tempKey = uncommentedLine.substr(0, found); + std::string tempValue = uncommentedLine.substr(found + 1); + if (!tempKey.empty()) { + key = normalizeString(tempKey, loc); + value = normalizeString(tempValue, loc); + return true; + } + } + } + } + return false; +} + +bool CredsCommonsInnerBackend::interpretValue(const CredentialsMap &methodCodeMap, int &method, + const std::string &value, bool &occurred) { + if (occurred) //two entries in conf file are treated as error + return false; + occurred = true; + auto it = methodCodeMap.find(value); + if (it == methodCodeMap.end()) //value is not valid + return false; + + method = it->second; + return true; +} + +int CredsCommonsInnerBackend::getMethodFromConfigurationFile(const CredentialsMap &methodCodeMap, + const std::string &methodName, + int &method) { + std::ifstream f(credsConfigurationFile()); + if (!f.is_open()) + return CYNARA_API_CONFIGURATION_ERROR; + + std::locale loc = f.getloc(); + bool occurred = false; + std::string key, value; + int tmpMethod; + while (getKeyAndValue(f, loc, key, value)) + if (key == methodName && !interpretValue(methodCodeMap, tmpMethod, value, occurred)) + return CYNARA_API_CONFIGURATION_ERROR; + if (occurred) { + method = tmpMethod; + return CYNARA_API_SUCCESS; + } + return CYNARA_API_CONFIGURATION_ERROR; +} + +// trim from the start +inline std::string &CredsCommonsInnerBackend::ltrim(std::string &s, const std::locale &loc) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), + [&loc](char c) { return not(std::isspace(c, loc)); })); + return s; +} + +// trim from the end +inline std::string &CredsCommonsInnerBackend::rtrim(std::string &s, const std::locale &loc) { + s.erase(std::find_if(s.rbegin(), s.rend(), + [&loc](char c) { return not(std::isspace(c, loc)); }).base(), + s.end()); + return s; +} + +inline std::string &CredsCommonsInnerBackend::toLower(std::string &s, const std::locale &loc) { + std::transform(s.begin(), s.end(), s.begin(), [&loc](char c) { return std::tolower(c, loc); }); + return s; +} + +inline std::string &CredsCommonsInnerBackend::normalizeString(std::string &s, + const std::locale &loc) { + return toLower(ltrim(rtrim(s, loc), loc), loc); +} + +} // namespace Cynara diff --git a/src/helpers/creds-commons/CredsCommonsInner.h b/src/helpers/creds-commons/CredsCommonsInner.h new file mode 100644 index 0000000..afa3b0b --- /dev/null +++ b/src/helpers/creds-commons/CredsCommonsInner.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file src/helpers/creds-commons/CredsCommonsInner.h + * @author Radoslaw Bartosiak + * @version 1.0 + * @brief Declaration of internal credential commons functions + */ + +#ifndef SRC_HELPERS_CREDS_COMMONS_CREDSCOMMONSINNER_H_ +#define SRC_HELPERS_CREDS_COMMONS_CREDSCOMMONSINNER_H_ + +#include +#include + +namespace Cynara { + +typedef std::map CredentialsMap; + +class CredsCommonsInnerBackend { + public: + static std::string credsConfigurationFile(void); + static bool getKeyAndValue(std::istream &f, const std::locale &loc, std::string &key, + std::string &value); + static bool interpretValue(const CredentialsMap &methodCodeMap, int &method, + const std::string &value, bool &occurred); + static int getMethodFromConfigurationFile(const CredentialsMap &methodCodeMap, + const std::string &methodName, int &method); + private: + // trim from the start + static std::string <rim(std::string &s, const std::locale &loc); + // trim from the end + static std::string &rtrim(std::string &s, const std::locale &loc); + static std::string &toLower(std::string &s, const std::locale &loc); + static std::string &normalizeString(std::string &s, const std::locale &loc); +}; + +} //namespace Cynara + +#endif /* SRC_HELPERS_CREDS_COMMONS_CREDSCOMMONSINNER_H_ */ diff --git a/src/helpers/creds-commons/creds-commons.cpp b/src/helpers/creds-commons/creds-commons.cpp index 35d2ac7..973f278 100644 --- a/src/helpers/creds-commons/creds-commons.cpp +++ b/src/helpers/creds-commons/creds-commons.cpp @@ -23,22 +23,46 @@ */ #include +#include #include #include +#include "CredsCommonsInner.h" + CYNARA_API int cynara_creds_get_default_client_method(enum cynara_client_creds *method) { - //todo read from proper file and parse + return Cynara::tryCatch([&] () { + int methodCode, ret; + static const Cynara::CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + + if ((ret = Cynara::CredsCommonsInnerBackend:: + getMethodFromConfigurationFile(clientCredsMap, + "client_default", + methodCode)) + != CYNARA_API_SUCCESS) + return ret; - *method = CLIENT_METHOD_SMACK; - return CYNARA_API_SUCCESS; + *method = static_cast(methodCode); + return CYNARA_API_SUCCESS; + }); } CYNARA_API int cynara_creds_get_default_user_method(enum cynara_user_creds *method) { - //todo read from proper file and parse + return Cynara::tryCatch([&] () { + int methodCode, ret; + static const Cynara::CredentialsMap userCredsMap{{"uid", USER_METHOD_UID}, + {"gid", USER_METHOD_GID}}; + if ((ret = Cynara::CredsCommonsInnerBackend:: + getMethodFromConfigurationFile(userCredsMap, + "user_default", + methodCode)) + != CYNARA_API_SUCCESS) + return ret; - *method = USER_METHOD_UID; - return CYNARA_API_SUCCESS; + *method = static_cast(methodCode); + return CYNARA_API_SUCCESS; + }); } diff --git a/src/include/cynara-creds-commons.h b/src/include/cynara-creds-commons.h index 75e4366..813c1b9 100644 --- a/src/include/cynara-creds-commons.h +++ b/src/include/cynara-creds-commons.h @@ -26,6 +26,7 @@ #ifndef CYNARA_CREDS_COMMONS_H #define CYNARA_CREDS_COMMONS_H +#include #include enum cynara_client_creds { @@ -53,12 +54,13 @@ extern "C" { * for this parameter. * * \par Typical use case: - * The function might be called before cynara_creds_dbus_get_client() and cynara_creds_socket_get_client(), - * when functions shall be invoked with system default value of method parameter. + * The function might be called before cynara_creds_dbus_get_client() + * and cynara_creds_socket_get_client(), when functions shall be invoked with system default + * value of method parameter. * * \par Method of function operation: - * Now the function is mocked up. It sets method to CLIENT_METHOD_SMACK and returns CYNARA_API_SUCCESS. - * In the future the function will probably read the value from /etc/cynara/cynara_client_creds file. + * The function will read and return the value of parameter client_default set + * in /etc/cynara/creds.conf file (the path is determined by CYNARA_CONFIGURATION_DIR). * * \par Sync (or) Async: * This is a synchronous API. @@ -69,8 +71,14 @@ extern "C" { * \param[out] method Placeholder for system default client feature * (like CLIENT_METHOD_SMACK, CLIENT_METHOD_PID) * - * \return CYNARA_API_SUCCESS on success, negative error code on error + * \return CYNARA_API_SUCCESS on success + * CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file. + * CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation. + * CYNARA_API_UNKNOWN_ERROR if there is other error. + * */ +CYNARA_API int cynara_creds_get_default_client_method(enum cynara_client_creds *method); /** @@ -84,14 +92,13 @@ int cynara_creds_get_default_client_method(enum cynara_client_creds *method); * for this parameter. * * \par Typical use case: - * The function might be called before cynara_creds_dbus_get_user() and cynara_creds_socket_get_user(), - * when functions shall be invoked with system default value of method parameter. + * The function might be called before cynara_creds_dbus_get_user() + * and cynara_creds_socket_get_user() when functions shall be invoked with system default + * value of method parameter. * * \par Method of function operation: - * - * The function reads the value from /etc/cynara/cynara_user_creds file. - * Now the function is mocked up. It sets method to USER_METHOD_UID and returns CYNARA_API_SUCCESS. - * In the future the function will probably read the value from /etc/cynara/cynara_user_creds file. + * The function will read and return the value of parameter user_default set + * in /etc/cynara/creds.conf file (the path is determined by CYNARA_CONFIGURATION_DIR). * * \par Sync (or) Async: * This is a synchronous API. @@ -99,10 +106,16 @@ int cynara_creds_get_default_client_method(enum cynara_client_creds *method); * \par Thread safety: * This function is thread-safe. * - * \param[out] method Placeholder for system default user feature (like USER_METHOD_UID, USER_METHOD_GID) + * \param[out] method Placeholder for system default user feature + * (like USER_METHOD_UID, USER_METHOD_GID) * - * \return CYNARA_API_SUCCESS on success, negative error code on error + * \return CYNARA_API_SUCCESS on success + * CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or + * there are errors in configuration file. + * CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation. + * CYNARA_API_UNKNOWN_ERROR if there is other error. */ +CYNARA_API int cynara_creds_get_default_user_method(enum cynara_user_creds *method); #ifdef __cplusplus diff --git a/src/include/cynara-error.h b/src/include/cynara-error.h index 728fc2a..8ac7de1 100644 --- a/src/include/cynara-error.h +++ b/src/include/cynara-error.h @@ -17,6 +17,7 @@ * @file src/include/cynara-error.h * @author Lukasz Wojciechowski * @author Zofia Abramowska + * @author Radoslaw Bartosiak * @version 1.0 * @brief This file contains error codes returned by APIs of Cynara. */ @@ -70,6 +71,8 @@ /*! \brief indicating an unknown error */ #define CYNARA_API_UNKNOWN_ERROR -10 +/*! \brief indicating configuration error */ +#define CYNARA_API_CONFIGURATION_ERROR -11 /** @}*/ #endif /* CYNARA_ERROR_H */ -- 2.7.4 From 48530fdfc88229b6430c3f63640fc02bd00d0eca Mon Sep 17 00:00:00 2001 From: Radoslaw Bartosiak Date: Wed, 22 Oct 2014 22:52:50 +0200 Subject: [PATCH 03/16] Add cynara_creds_get_[client|user]_method UT Adding unit tests for functions used in implementation of cynara_creds_get_[client|user]_methods Change-Id: I3cb7b9fb03e09769dbb68fd595994cbe13956483 Signed-off-by: Radoslaw Bartosiak --- test/CMakeLists.txt | 13 +- test/credsCommons/parser/CredsCommonsInner.h | 56 +++ .../parser/FakeCredsCommonsInnerBackend.h | 42 +++ test/credsCommons/parser/Parser.cpp | 386 +++++++++++++++++++++ 4 files changed, 493 insertions(+), 4 deletions(-) create mode 100644 test/credsCommons/parser/CredsCommonsInner.h create mode 100644 test/credsCommons/parser/FakeCredsCommonsInnerBackend.h create mode 100644 test/credsCommons/parser/Parser.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 62bdbaa..ffddd87 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,6 +14,7 @@ # # @file CMakeLists.txt # @author Aleksander Zdyb +# @author Radoslaw Bartosiak # @brief Cmake for tests # PKG_CHECK_MODULES(PKGS REQUIRED gmock) @@ -27,6 +28,8 @@ SET(CYNARA_SOURCES_FOR_TESTS ${CYNARA_SRC}/common/types/PolicyKeyHelpers.cpp ${CYNARA_SRC}/common/types/PolicyResult.cpp ${CYNARA_SRC}/common/types/PolicyType.cpp + ${CYNARA_SRC}/helpers/creds-commons/CredsCommonsInner.cpp + ${CYNARA_SRC}/helpers/creds-commons/creds-commons.cpp ${CYNARA_SRC}/storage/BucketDeserializer.cpp ${CYNARA_SRC}/storage/InMemoryStorageBackend.cpp ${CYNARA_SRC}/storage/Storage.cpp @@ -35,8 +38,11 @@ SET(CYNARA_SOURCES_FOR_TESTS ) SET(CYNARA_TESTS_SOURCES + TestEventListenerProxy.cpp common/exceptions/bucketrecordcorrupted.cpp - types/policykey.cpp + common/types/policybucket.cpp + credsCommons/parser/Parser.cpp + helpers.cpp storage/performance/bucket.cpp storage/storage/policies.cpp storage/storage/check.cpp @@ -49,10 +55,8 @@ SET(CYNARA_TESTS_SOURCES storage/serializer/dump.cpp storage/serializer/dump_load.cpp storage/serializer/serialize.cpp - common/types/policybucket.cpp - helpers.cpp - TestEventListenerProxy.cpp tests.cpp + types/policykey.cpp ) INCLUDE_DIRECTORIES( @@ -60,6 +64,7 @@ INCLUDE_DIRECTORIES( ${CYNARA_SRC}/common ${CYNARA_SRC}/include ${CYNARA_SRC} + credsCommons/parser ) ADD_EXECUTABLE(${TARGET_CYNARA_TESTS} diff --git a/test/credsCommons/parser/CredsCommonsInner.h b/test/credsCommons/parser/CredsCommonsInner.h new file mode 100644 index 0000000..b7c8bf4 --- /dev/null +++ b/test/credsCommons/parser/CredsCommonsInner.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ +/** + * @file test/credsCommons/parser/CredsCommonsInner.h + * @author Radoslaw Bartosiak + * @version 1.0 + * @brief Declaration of internal credential commons functions for gmock tests + */ + +#ifndef TEST_CREDSCOMMONS_PARSER_CREDSCOMMONSINNER_H_ +#define TEST_CREDSCOMMONS_PARSER_CREDSCOMMONSINNER_H_ + +#include +#include + +namespace Cynara { + +typedef std::map CredentialsMap; + +class CredsCommonsInnerBackend { + public: + static std::string credsConfigurationFile(void); + virtual ~CredsCommonsInnerBackend() {}; + virtual bool getKeyAndValue(std::istream &f, const std::locale &loc, std::string &key, + std::string &value); + virtual bool interpretValue(const CredentialsMap &methodCodeMap, int &method, + const std::string &value, bool &occurred); + virtual int getMethodFromConfigurationFile(const CredentialsMap &methodCodeMap, + const std::string &methodName, int &method); + private: + // trim from the start + static std::string <rim(std::string &s, const std::locale &loc); + // trim from the end + static std::string &rtrim(std::string &s, const std::locale &loc); + static std::string &toLower(std::string &s, const std::locale &loc); + static std::string &normalizeString(std::string &s, const std::locale &loc); +}; + +static CredsCommonsInnerBackend credsBackend; + +} //namespace Cynara + +#endif /* TEST_CREDSCOMMONS_PARSER_CREDSCOMMONSINNER_H_ */ diff --git a/test/credsCommons/parser/FakeCredsCommonsInnerBackend.h b/test/credsCommons/parser/FakeCredsCommonsInnerBackend.h new file mode 100644 index 0000000..655e62f --- /dev/null +++ b/test/credsCommons/parser/FakeCredsCommonsInnerBackend.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ +/** + * @file test/credsCommons/parser/FakeCredsCommonsInnerBackend.h + * @author Radoslaw Bartosiak + * @version 1.0 + * @brief Mock of CredsCommonsInnerBackend + */ + +#ifndef TEST_CREDSCOMMONS_PARSER_FAKECREDSCOMMONSINNERBACKEND_H_ +#define TEST_CREDSCOMMONS_PARSER_FAKECREDSCOMMONSINNERBACKEND_H_ + +#include "CredsCommonsInner.h" + +class FakeCredsCommonsInnerBackend : public Cynara::CredsCommonsInnerBackend { +public: + + MOCK_METHOD4(getKeyAndValue, bool(std::istream &f, const std::locale &loc, std::string &key, + std::string &value)); + MOCK_METHOD4(interpretValue, bool(const Cynara::CredentialsMap &methodCodeMap, int &method, + const std::string &value, bool &occurred)); +}; + +class FakeGetKeyAndValueBackend : public Cynara::CredsCommonsInnerBackend { +public: + MOCK_METHOD4(getKeyAndValue, bool(std::istream &f, const std::locale &loc, std::string &key, + std::string &value)); +}; +#endif /* TEST_CREDSCOMMONS_PARSER_FAKECREDSCOMMONSINNERBACKEND_H_ */ diff --git a/test/credsCommons/parser/Parser.cpp b/test/credsCommons/parser/Parser.cpp new file mode 100644 index 0000000..9a92205 --- /dev/null +++ b/test/credsCommons/parser/Parser.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ +/** + * @file test/credsCommons/parser/Parser.cpp + * @author Radoslaw Bartosiak + * @version 1.0 + * @brief Tests of internal implementation of credential commons functions + */ + +#include +#include +#include +#include +#include + +#include "FakeCredsCommonsInnerBackend.h" + +#include +#include + +/* Tests */ +using namespace Cynara; +using ::testing::_; +using ::testing::Assign; +using ::testing::AtMost; +using ::testing::Eq; +using ::testing::Between; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgReferee; +using ::testing::Throw; + + +#define NOT_A_METHOD_CODE -13197 //number which is not a method code + +/*** Test for getKeyAndValue() ***/ + +struct getKeyAndValueTestEntry { + std::string testString; + std::locale loc; + bool expectedResult; + std::string key; + std::string value; +}; + +TEST(parser, getKeyAndValue) { + + static std::vector getKeyAndValueTestEntries; + // test cases initialization key and value are checked only if expectedResult is true + getKeyAndValueTestEntries.push_back({"", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"#", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"a", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"=", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"\n", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"aa", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"=#", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"=\n", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"\n=", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"==", std::locale("en_US.UTF8"), false, "", "="}); + getKeyAndValueTestEntries.push_back({"#=", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"aa", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"##", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"a=b", std::locale("en_US.UTF8"), true, "a", "b"}); + getKeyAndValueTestEntries.push_back({"=aa", std::locale("en_US.UTF8"), false, "", "aa"}); + getKeyAndValueTestEntries.push_back({"aa=", std::locale("en_US.UTF8"), true, "aa", ""}); + getKeyAndValueTestEntries.push_back({"#a=", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"a=#", std::locale("en_US.UTF8"), true, "a", ""}); + getKeyAndValueTestEntries.push_back({"=#a", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"==a", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"=a=", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"a=#\nb", std::locale("en_US.UTF8"), true, "a", ""}); + getKeyAndValueTestEntries.push_back({"a=#b\n", std::locale("en_US.UTF8"), true, "a", ""}); + getKeyAndValueTestEntries.push_back({"a\nb", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"\na=b", std::locale("en_US.UTF8"), true, "a", "b"}); + getKeyAndValueTestEntries.push_back({"a=#\nb", std::locale("en_US.UTF8"), true, "a", ""}); + getKeyAndValueTestEntries.push_back({"=a\nb", std::locale("en_US.UTF8"), false, "", ""}); + getKeyAndValueTestEntries.push_back({"a=\nb=c#", std::locale("en_US.UTF8"), true, "a", ""}); + getKeyAndValueTestEntries.push_back({"=a\nb=c#", std::locale("en_US.UTF8"), true, "b", "c"}); + getKeyAndValueTestEntries.push_back({"aa\nkey=value", std::locale("en_US.UTF8"), true, "key", + "value"}); + getKeyAndValueTestEntries.push_back({" key with spaces = value with spaces #\n", + std::locale("en_US.UTF8"), true, "key with spaces", + "value with spaces"}); + getKeyAndValueTestEntries.push_back({"VeryLongKey1111111111111111111111111111111111111111111" + "111111111111111111111111111111111111111111111111111111" + "111111111 = 1\nnoImportant", std::locale("en_US.UTF8"), + true, + "verylongkey1111111111111111111111111111111111111111111" + "111111111111111111111111111111111111111111111111111111" + "111111111", "1"}); + getKeyAndValueTestEntries.push_back({"key=value", std::locale("en_US.UTF8"), true, "key", + "value"}); + getKeyAndValueTestEntries.push_back({"CAPSON=CaPSon", std::locale("en_US.UTF8"), true, + "capson", "capson"}); + getKeyAndValueTestEntries.push_back({" soMe_spacEs_ = vaLue# ", std::locale("en_US.UTF8"), + true, "some_spaces_", "value"}); + int i = 0; + for (auto it = getKeyAndValueTestEntries.begin(); it != getKeyAndValueTestEntries.end(); ++it) + { + i++; + std::string keyAtStart("predefinedKey["+it->testString+"]"); + std::string valueAtStart("prdefinedValue["+it->testString+"]"); + std::string key(keyAtStart); + std::string value(valueAtStart); + std::stringstream ss; + ss << it->testString; + + EXPECT_EQ(it->expectedResult, credsBackend.getKeyAndValue(ss, it->loc, key, value)) + << "Result code in case no: "<< i << " [" + it->testString + "] was wrong." <expectedResult) { + //key and value shall be set if getKeyAndValue returns true + EXPECT_EQ(it->key, key) << "A in case no# " << i <value, value) << "B in case no# " << i <("client_default"), SetArgReferee<3>("smack"), Return(true))) + .WillOnce(Return(false)); + EXPECT_CALL(fakeBackend, interpretValue(_, _,"smack", Eq(false))) + .WillOnce(DoAll(SetArgReferee<1>(CLIENT_METHOD_SMACK), SetArgReferee<3>(true), + Return(true))); + + EXPECT_EQ(CYNARA_API_SUCCESS, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(CLIENT_METHOD_SMACK, method); +} + +TEST(parser, getMethodFromConfigurationFileOK3Lines) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeCredsCommonsInnerBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .WillOnce(DoAll(SetArgReferee<2>("user_default"), SetArgReferee<3>("uid"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("smack"), Return(true))) + .WillOnce(Return(false)); + EXPECT_CALL(fakeBackend, interpretValue(_, _,"smack", Eq(false))) + .WillOnce(DoAll(SetArgReferee<1>(CLIENT_METHOD_SMACK), SetArgReferee<3>(true), + Return(true))); + + EXPECT_EQ(CYNARA_API_SUCCESS, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(CLIENT_METHOD_SMACK, method); +} + +TEST(parser, getMethodFromConfigurationFileBadNoEntry) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeCredsCommonsInnerBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .Times(4) + .WillOnce(DoAll(SetArgReferee<2>("A"), SetArgReferee<3>("smack"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("B"), SetArgReferee<3>("smack"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("someWrongValue"), Return(true))) + .WillOnce(Return(false)); + //user default is not in the file (contrary to A,B, client_default) + EXPECT_EQ(CYNARA_API_CONFIGURATION_ERROR, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "user_default", + method)); + EXPECT_EQ(NOT_A_METHOD_CODE, method); +} + +TEST(parser, getMethodFromConfigurationFileBadValueInEntry) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeCredsCommonsInnerBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .Times(3) + .WillOnce(DoAll(SetArgReferee<2>("someOtherKey"), SetArgReferee<3>("smack"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("user_default"), SetArgReferee<3>("smack"), Return(true))) + //the someWrongValue does not matter this time + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("someWrongValue"), Return(true))); + EXPECT_CALL(fakeBackend, interpretValue(clientCredsMap, _, "someWrongValue", Eq(false))) + .WillOnce(Return(false)); + + EXPECT_EQ(CYNARA_API_CONFIGURATION_ERROR, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(NOT_A_METHOD_CODE, method); +} + +TEST(parser, getMethodFromConfigurationFileBadTwoEntries) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeCredsCommonsInnerBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .Times(3) + .WillOnce(DoAll(SetArgReferee<2>("someOtherKey"), SetArgReferee<3>("smack"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("smack"), Return(true))) + //the someWrongValue does not matter this time + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("someWrongValue"), Return(true))); + EXPECT_CALL(fakeBackend, interpretValue(_, _, "smack", Eq(false))) + .WillOnce(DoAll(SetArgReferee<1>(CLIENT_METHOD_SMACK), SetArgReferee<3>(true), + Return(true))); + EXPECT_CALL(fakeBackend, interpretValue(clientCredsMap, _, "someWrongValue", Eq(true))) + .WillOnce(Return(false)); + + EXPECT_EQ(CYNARA_API_CONFIGURATION_ERROR, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(NOT_A_METHOD_CODE, method); +} + +TEST(parser, getMethodFromConfigurationFileManyStrangeKeyAndValues) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method; + FakeGetKeyAndValueBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .WillOnce(DoAll(SetArgReferee<2>("key1"), SetArgReferee<3>("value1"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("key2"), SetArgReferee<3>("value2"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("key3"), SetArgReferee<3>("value3"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("key4"), SetArgReferee<3>("value4"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>(""), SetArgReferee<3>(""), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("pid"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>(""), SetArgReferee<3>(""), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("key5"), SetArgReferee<3>("value5"), Return(true))) + .WillOnce(Return(false)); + EXPECT_EQ(CYNARA_API_SUCCESS, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(CLIENT_METHOD_PID, method); +} + +TEST(parser, getMethodFromConfigurationFileFoundTheKeyAgain) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeGetKeyAndValueBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("smack"), + Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("user_default"), SetArgReferee<3>("gid"), + Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("smack"), + Return(true))); + + EXPECT_EQ(CYNARA_API_CONFIGURATION_ERROR, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(NOT_A_METHOD_CODE, method); +} + +TEST(parser, getMethodFromConfigurationFileTheKeyNotFound) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeGetKeyAndValueBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .WillOnce(DoAll(SetArgReferee<2>("something_default"), SetArgReferee<3>("smack"), + Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("user_default"), SetArgReferee<3>("gid"), + Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("other_default"), SetArgReferee<3>("smack"), + Return(true))) + .WillOnce(Return(false)); + + EXPECT_EQ(CYNARA_API_CONFIGURATION_ERROR, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(NOT_A_METHOD_CODE, method); +} + +TEST(parser, getMethodFromConfigurationFileFoundTheKeyWithWrongValue) { + static const CredentialsMap clientCredsMap{{"smack", CLIENT_METHOD_SMACK}, + {"pid", CLIENT_METHOD_PID}}; + int method = NOT_A_METHOD_CODE; + FakeGetKeyAndValueBackend fakeBackend; + EXPECT_CALL(fakeBackend, getKeyAndValue(_, _, _, _)) + .Times(2) + .WillOnce(DoAll(SetArgReferee<2>("user_default"), SetArgReferee<3>("gid"), Return(true))) + .WillOnce(DoAll(SetArgReferee<2>("client_default"), SetArgReferee<3>("noSmackNoPid"), + Return(true))); + + EXPECT_EQ(CYNARA_API_CONFIGURATION_ERROR, + fakeBackend.getMethodFromConfigurationFile(clientCredsMap, "client_default", + method)); + EXPECT_EQ(NOT_A_METHOD_CODE, method); +} -- 2.7.4 From 94c676f6a67966566072093dadf217ddd20849a1 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Mon, 6 Oct 2014 12:45:25 +0200 Subject: [PATCH 04/16] Remove PolicyBucket() constructor In some cases using parameterless constructor of PolicyBucket can result in uninitialized PolicyBucket id. Complete removal of this constructor guarantees inablity to create bucket with no id. Change-Id: Id67d7f257697078ef0d4518161ade473a983cf6b --- src/common/types/PolicyBucket.cpp | 2 +- src/common/types/PolicyBucket.h | 15 ++++++++++----- src/storage/InMemoryStorageBackend.cpp | 2 +- src/storage/StorageDeserializer.cpp | 3 ++- test/common/types/policybucket.cpp | 16 +++++++++------- test/storage/inmemorystoragebackend/buckets.cpp | 4 ++-- .../inmemeorystoragebackendfixture.h | 6 +++--- test/storage/performance/bucket.cpp | 2 +- test/storage/serializer/dump.cpp | 12 ++++++------ test/storage/serializer/dump_load.cpp | 6 +++--- test/storage/storage/check.cpp | 14 +++++++------- 11 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/common/types/PolicyBucket.cpp b/src/common/types/PolicyBucket.cpp index 4b9b508..f1a45a1 100644 --- a/src/common/types/PolicyBucket.cpp +++ b/src/common/types/PolicyBucket.cpp @@ -30,7 +30,7 @@ namespace Cynara { PolicyBucket PolicyBucket::filtered(const PolicyKey &key) const { - PolicyBucket result; + PolicyBucket result(m_id + "_filtered"); const auto &policies = m_policyCollection; const auto variants = PolicyKeyHelpers::keyVariants(key); diff --git a/src/common/types/PolicyBucket.h b/src/common/types/PolicyBucket.h index 122ece0..a2faf9c 100644 --- a/src/common/types/PolicyBucket.h +++ b/src/common/types/PolicyBucket.h @@ -49,12 +49,17 @@ public: typedef const_policy_iterator const_iterator; // TODO: Review usefulness of ctors - PolicyBucket() : m_defaultPolicy(PredefinedPolicyType::DENY) {} - PolicyBucket(const PolicyBucketId &id, const PolicyResult &defaultPolicy) - : m_defaultPolicy(defaultPolicy), m_id(id) {} - PolicyBucket(const PolicyCollection &policies) + //delete default constructor in order to prevent creation of buckets with no id + PolicyBucket() = delete; + PolicyBucket(const PolicyBucketId &id, + const PolicyResult &defaultPolicy = PredefinedPolicyType::DENY) + : m_defaultPolicy(defaultPolicy), + m_id(id) {} + PolicyBucket(const PolicyBucketId &id, + const PolicyCollection &policies) : m_policyCollection(makePolicyMap(policies)), - m_defaultPolicy(PredefinedPolicyType::DENY) {} + m_defaultPolicy(PredefinedPolicyType::DENY), + m_id(id) {} PolicyBucket(const PolicyBucketId &id, const PolicyResult &defaultPolicy, const PolicyCollection &policies) diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index ae9c624..af18926 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -72,7 +72,7 @@ void InMemoryStorageBackend::load(void) { if (!hasBucket(defaultPolicyBucketId)) { LOGN("Creating defaultBucket."); - this->buckets().insert({ defaultPolicyBucketId, PolicyBucket() }); + this->buckets().insert({ defaultPolicyBucketId, PolicyBucket(defaultPolicyBucketId) }); } } diff --git a/src/storage/StorageDeserializer.cpp b/src/storage/StorageDeserializer.cpp index 22d3231..166a406 100644 --- a/src/storage/StorageDeserializer.cpp +++ b/src/storage/StorageDeserializer.cpp @@ -56,7 +56,8 @@ void StorageDeserializer::initBuckets(Buckets &buckets) { auto policyType = parsePolicyType(line, beginToken); auto metadata = parseMetadata(line, beginToken); - buckets[bucketId] = PolicyBucket(bucketId, PolicyResult(policyType, metadata)); + //it's safe to simply insert; buckets were cleared earlier, all ids should be unique + buckets.insert({ bucketId, PolicyBucket(bucketId, PolicyResult(policyType, metadata)) }); } } diff --git a/test/common/types/policybucket.cpp b/test/common/types/policybucket.cpp index 0468782..983a040 100644 --- a/test/common/types/policybucket.cpp +++ b/test/common/types/policybucket.cpp @@ -62,7 +62,7 @@ protected: TEST_F(PolicyBucketFixture, filtered) { using ::testing::UnorderedElementsAre; - PolicyBucket bucket(pkPolicies); + PolicyBucket bucket("filtered", pkPolicies); bucket.setDefaultPolicy(PredefinedPolicyType::DENY); auto filtered = bucket.filtered(pk1); @@ -76,7 +76,7 @@ TEST_F(PolicyBucketFixture, filtered) { TEST_F(PolicyBucketFixture, filtered_other) { using ::testing::IsEmpty; - PolicyBucket bucket(pkPolicies); + PolicyBucket bucket("filtered_other", pkPolicies); bucket.setDefaultPolicy(PredefinedPolicyType::DENY); auto filtered = bucket.filtered(otherPk); @@ -93,7 +93,7 @@ TEST_F(PolicyBucketFixture, filtered_wildcard_1) { // Leave policies with given client, given user and any privilege auto policiesToStay = Helpers::pickFromCollection(wildcardPolicies, { 0, 1, 3 }); - PolicyBucket bucket(wildcardPolicies); + PolicyBucket bucket("filtered_wildcard_1", wildcardPolicies); auto filtered = bucket.filtered(PolicyKey("c1", "u1", "p2")); ASSERT_THAT(filtered, UnorderedElementsAreArray(policiesToStay)); } @@ -104,7 +104,7 @@ TEST_F(PolicyBucketFixture, filtered_wildcard_2) { // Leave policies with given client, given user and any privilege auto policiesToStay = Helpers::pickFromCollection(wildcardPolicies, { 2, 3 }); - PolicyBucket bucket(wildcardPolicies); + PolicyBucket bucket("filtered_wildcard_2", wildcardPolicies); auto filtered = bucket.filtered(PolicyKey("cccc", "u1", "p1")); ASSERT_THAT(filtered, UnorderedElementsAreArray(policiesToStay)); @@ -116,7 +116,7 @@ TEST_F(PolicyBucketFixture, filtered_wildcard_3) { // Leave policies with given client, given user and any privilege auto policiesToStay = Helpers::pickFromCollection(wildcardPolicies, { 0, 3 }); - PolicyBucket bucket(wildcardPolicies); + PolicyBucket bucket("filtered_wildcard_3", wildcardPolicies); auto filtered = bucket.filtered(PolicyKey("c1", "u1", "pppp")); ASSERT_THAT(filtered, UnorderedElementsAreArray(policiesToStay)); } @@ -127,7 +127,7 @@ TEST_F(PolicyBucketFixture, filtered_wildcard_4) { // Leave policies with given client, given user and any privilege auto policiesToStay = Helpers::pickFromCollection(wildcardPolicies, { 3 }); - PolicyBucket bucket(wildcardPolicies); + PolicyBucket bucket("filtered_wildcard_4", wildcardPolicies); auto filtered = bucket.filtered(PolicyKey("cccc", "uuuu", "pppp")); ASSERT_THAT(filtered, UnorderedElementsAreArray(policiesToStay)); } @@ -135,7 +135,9 @@ TEST_F(PolicyBucketFixture, filtered_wildcard_4) { TEST_F(PolicyBucketFixture, filtered_wildcard_none) { using ::testing::IsEmpty; - PolicyBucket bucket({ wildcardPolicies.begin(), wildcardPolicies.begin() + 3 }); + PolicyBucket bucket("filtered_wildcard_none", + PolicyCollection({ wildcardPolicies.begin(), + wildcardPolicies.begin() + 3 })); auto filtered = bucket.filtered(PolicyKey("cccc", "uuuu", "pppp")); ASSERT_THAT(filtered, IsEmpty()); } diff --git a/test/storage/inmemorystoragebackend/buckets.cpp b/test/storage/inmemorystoragebackend/buckets.cpp index b7e2bdd..f53b51c 100644 --- a/test/storage/inmemorystoragebackend/buckets.cpp +++ b/test/storage/inmemorystoragebackend/buckets.cpp @@ -86,7 +86,7 @@ TEST_F(InMemeoryStorageBackendFixture, deleteBucket) { .WillRepeatedly(ReturnRef(m_buckets)); PolicyBucketId bucketId = "delete-bucket"; - m_buckets.insert({ bucketId, PolicyBucket() }); + m_buckets.insert({ bucketId, PolicyBucket(bucketId) }); backend.deleteBucket(bucketId); @@ -102,7 +102,7 @@ TEST_F(InMemeoryStorageBackendFixture, hasBucket) { .WillRepeatedly(ReturnRef(m_buckets)); PolicyBucketId bucketId = "bucket"; - m_buckets.insert({ bucketId, PolicyBucket() }); + m_buckets.insert({ bucketId, PolicyBucket(bucketId) }); ASSERT_TRUE(backend.hasBucket(bucketId)); ASSERT_FALSE(backend.hasBucket("non-existent")); diff --git a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h index 2c0624b..3f08dea 100644 --- a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h +++ b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h @@ -36,20 +36,20 @@ class InMemeoryStorageBackendFixture : public ::testing::Test { protected: Cynara::Buckets::mapped_type & createBucket(const Cynara::PolicyBucketId &bucketId) { - auto bucket = Cynara::PolicyBucket(); + auto bucket = Cynara::PolicyBucket(bucketId); return m_buckets.insert({ bucketId, bucket }).first->second; } Cynara::Buckets::mapped_type & createBucket(const Cynara::PolicyBucketId &bucketId, const Cynara::PolicyCollection &policies) { - auto bucket = Cynara::PolicyBucket(policies); + auto bucket = Cynara::PolicyBucket(bucketId, policies); return m_buckets.insert({ bucketId, bucket }).first->second; } void addToBucket(Cynara::PolicyBucketId bucketId, const Cynara::PolicyCollection &policies) { // TODO: Consider altering PolicyMap directly for (const auto &policy : policies) { - m_buckets[bucketId].insertPolicy(policy); + m_buckets.at(bucketId).insertPolicy(policy); } } diff --git a/test/storage/performance/bucket.cpp b/test/storage/performance/bucket.cpp index 1e63966..e55da7f 100644 --- a/test/storage/performance/bucket.cpp +++ b/test/storage/performance/bucket.cpp @@ -83,7 +83,7 @@ private: TEST(Performance, bucket_filtered_100000) { using std::chrono::microseconds; - PolicyBucket bucket; + PolicyBucket bucket("test"); PolicyKeyGenerator generator(100, 10); diff --git a/test/storage/serializer/dump.cpp b/test/storage/serializer/dump.cpp index 53034ce..2eafcf7 100644 --- a/test/storage/serializer/dump.cpp +++ b/test/storage/serializer/dump.cpp @@ -49,7 +49,7 @@ static std::string expectedPolicyType(const PolicyType &type) { TEST(serializer_dump, dump_empty_bucket) { auto oss = std::make_shared(); - PolicyBucket bucket; + PolicyBucket bucket("empty"); StorageSerializer serializer(oss); serializer.dump(bucket); @@ -65,8 +65,8 @@ TEST(serializer_dump, dump_bucket) { PolicyKey pk1 = Helpers::generatePolicyKey("1"); PolicyKey pk2 = Helpers::generatePolicyKey("2"); - PolicyBucket bucket = {{ Policy::simpleWithKey(pk1, ALLOW), - Policy::simpleWithKey(pk2, DENY) }}; + PolicyBucket bucket("dump_bucket", PolicyCollection({ Policy::simpleWithKey(pk1, ALLOW), + Policy::simpleWithKey(pk2, DENY) })); auto outStream = std::make_shared(); StorageSerializer serializer(outStream); @@ -94,9 +94,9 @@ TEST(serializer_dump, dump_bucket_bucket) { PolicyKey pk3 = Helpers::generatePolicyKey("3"); PolicyBucketId bucketId = Helpers::generateBucketId(); - PolicyBucket bucket = {{ Policy::bucketWithKey(pk1, bucketId), - Policy::simpleWithKey(pk2, DENY), - Policy::bucketWithKey(pk3, bucketId) }}; + PolicyBucket bucket = {"dump_bucket_bucket", { Policy::bucketWithKey(pk1, bucketId), + Policy::simpleWithKey(pk2, DENY), + Policy::bucketWithKey(pk3, bucketId) }}; auto outStream = std::make_shared(); StorageSerializer serializer(outStream); diff --git a/test/storage/serializer/dump_load.cpp b/test/storage/serializer/dump_load.cpp index 01fff9a..f3eff9b 100644 --- a/test/storage/serializer/dump_load.cpp +++ b/test/storage/serializer/dump_load.cpp @@ -48,9 +48,9 @@ TEST(dump_load, bucket) { PolicyKey pk2 = Helpers::generatePolicyKey("2"); PolicyKey pk3 = Helpers::generatePolicyKey("3"); PolicyBucketId bucketId = Helpers::generateBucketId(); - PolicyBucket bucket = {{ Policy::bucketWithKey(pk1, bucketId), - Policy::simpleWithKey(pk2, PredefinedPolicyType::DENY), - Policy::bucketWithKey(pk3, bucketId) }}; + PolicyBucket bucket = {"bucket", { Policy::bucketWithKey(pk1, bucketId), + Policy::simpleWithKey(pk2, PredefinedPolicyType::DENY), + Policy::bucketWithKey(pk3, bucketId) }}; auto ioStream = std::make_shared(); diff --git a/test/storage/storage/check.cpp b/test/storage/storage/check.cpp index 54c16b4..4935b65 100644 --- a/test/storage/storage/check.cpp +++ b/test/storage/storage/check.cpp @@ -43,7 +43,7 @@ using namespace Cynara; TEST(storage, checkEmpty) { using ::testing::ReturnPointee; - PolicyBucket emptyBucket; + PolicyBucket emptyBucket("empty"); FakeStorageBackend backend; Cynara::Storage storage(backend); @@ -60,7 +60,7 @@ TEST(storage, checkEmpty) { TEST(storage, checkSimple) { using ::testing::ReturnPointee; - PolicyBucket bucket; + PolicyBucket bucket(defaultPolicyBucketId); FakeStorageBackend backend; Cynara::Storage storage(backend); @@ -91,12 +91,12 @@ TEST(storage, checkBucket) { Cynara::Storage storage(backend); PolicyKey pk = Helpers::generatePolicyKey(); - PolicyBucket defaultBucket(PolicyCollection({ + PolicyBucket defaultBucket(defaultPolicyBucketId, PolicyCollection({ Policy::simpleWithKey(pk, PredefinedPolicyType::ALLOW), Policy::bucketWithKey(pk, additionalBucketId) })); - PolicyBucket additionalBucket; + PolicyBucket additionalBucket("additional"); EXPECT_CALL(backend, searchBucket(defaultPolicyBucketId, pk)) .WillRepeatedly(ReturnPointee(&defaultBucket)); @@ -127,7 +127,7 @@ TEST(storage, checkBucketWildcard) { const PolicyKey defaultBucketKey = PolicyKey("c", "*", "p"); const PolicyKey checkKey = PolicyKey("c", "u1", "p"); - PolicyBucket defaultBucket(PolicyCollection({ + PolicyBucket defaultBucket(defaultPolicyBucketId, PolicyCollection({ Policy::bucketWithKey(defaultBucketKey, additionalBucketId) })); @@ -139,7 +139,7 @@ TEST(storage, checkBucketWildcard) { // Check, if next bucket is filtered with original key EXPECT_CALL(backend, searchBucket(additionalBucketId, checkKey)) - .WillRepeatedly(Return(PolicyBucket())); // additional bucket would yield no records + .WillRepeatedly(Return(PolicyBucket("id"))); // additional bucket would yield no records // Should return additional bucket's default policy ASSERT_EQ(PredefinedPolicyType::DENY, storage.checkPolicy(checkKey)); @@ -152,7 +152,7 @@ TEST(storage, checkBucketWildcardOtherDefault) { const PolicyKey defaultBucketKey = PolicyKey("c", "*", "p"); const PolicyKey checkKey = PolicyKey("c", "u1", "p"); - PolicyBucket defaultBucket(PolicyCollection({ + PolicyBucket defaultBucket(defaultPolicyBucketId, PolicyCollection({ Policy::bucketWithKey(defaultBucketKey, additionalBucketId) })); -- 2.7.4 From 5907debce03abecb8fa799e807dfa123fec116a2 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Mon, 6 Oct 2014 19:08:25 +0200 Subject: [PATCH 05/16] Add PolicyBucketId validation This patch introduces mechanism for checking whether new PolicyBucketId contains forbidden characters. Now only alphanumeric characters, hyphen and underscore can be used in PolicyBucketId. InvalidBucketIdException is thrown and OPERATION_NOT_ALLOWED error code is returned otherwise. Change-Id: I48b6e14d20cb62adc17560929055553df0ce1077 --- src/common/exceptions/InvalidBucketIdException.h | 55 ++++++++++++++++++++++++ src/common/types/PolicyBucket.cpp | 34 +++++++++++++++ src/common/types/PolicyBucket.h | 30 ++++++------- src/service/logic/Logic.cpp | 3 ++ test/common/types/policybucket.cpp | 36 ++++++++++++++++ 5 files changed, 140 insertions(+), 18 deletions(-) create mode 100644 src/common/exceptions/InvalidBucketIdException.h diff --git a/src/common/exceptions/InvalidBucketIdException.h b/src/common/exceptions/InvalidBucketIdException.h new file mode 100644 index 0000000..f8772e4 --- /dev/null +++ b/src/common/exceptions/InvalidBucketIdException.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ +/** + * @file src/common/exceptions/InvalidBucketIdException.h + * @author Pawel Wieczorek + * @version 0.1 + * @brief This file defines exception thrown when bucket ID contains forbidden characters + */ + +#ifndef SRC_COMMON_EXCEPTIONS_INVALIDBUCKETIDEXCEPTION_H_ +#define SRC_COMMON_EXCEPTIONS_INVALIDBUCKETIDEXCEPTION_H_ + +#include + +#include + +namespace Cynara { + +class InvalidBucketIdException : public Exception { +public: + InvalidBucketIdException(const std::string &bucketId) : m_bucketId(bucketId) {}; + virtual ~InvalidBucketIdException() {}; + + const std::string message(void) const { + if (m_message.empty()) { + m_message = "Bucket ID " + bucketId() + " contains forbidden characters"; + } + return m_message; + } + + const std::string &bucketId(void) const { + return m_bucketId; + } + +private: + mutable std::string m_message; + std::string m_bucketId; +}; + +} /* namespace Cynara */ + +#endif /* SRC_COMMON_EXCEPTIONS_INVALIDBUCKETIDEXCEPTION_H_ */ diff --git a/src/common/types/PolicyBucket.cpp b/src/common/types/PolicyBucket.cpp index f1a45a1..622bc05 100644 --- a/src/common/types/PolicyBucket.cpp +++ b/src/common/types/PolicyBucket.cpp @@ -22,6 +22,7 @@ #include +#include #include #include @@ -29,6 +30,25 @@ namespace Cynara { +const std::string PolicyBucket::m_idSeparators = "-_"; + +PolicyBucket::PolicyBucket(const PolicyBucketId &id, const PolicyResult &defaultPolicy) + : m_defaultPolicy(defaultPolicy), m_id(id) { + idValidator(id); +} + +PolicyBucket::PolicyBucket(const PolicyBucketId &id, const PolicyCollection &policies) + : m_policyCollection(makePolicyMap(policies)), m_defaultPolicy(PredefinedPolicyType::DENY), + m_id(id) { + idValidator(id); +} + +PolicyBucket::PolicyBucket(const PolicyBucketId &id, const PolicyResult &defaultPolicy, + const PolicyCollection &policies) + : m_policyCollection(makePolicyMap(policies)), m_defaultPolicy(defaultPolicy), m_id(id) { + idValidator(id); +} + PolicyBucket PolicyBucket::filtered(const PolicyKey &key) const { PolicyBucket result(m_id + "_filtered"); @@ -78,4 +98,18 @@ PolicyMap PolicyBucket::makePolicyMap(const PolicyCollection &policies) { return result; } +void PolicyBucket::idValidator(const PolicyBucketId &id) { + auto isCharInvalid = [] (char c) { + return !(std::isalnum(c) || isIdSeparator(c)); + }; + + if (id.end() != find_if(id.begin(), id.end(), isCharInvalid)) { + throw InvalidBucketIdException(id); + } +} + +bool PolicyBucket::isIdSeparator(char c) { + return m_idSeparators.end() != find(m_idSeparators.begin(), m_idSeparators.end(), c); +} + } // namespace Cynara diff --git a/src/common/types/PolicyBucket.h b/src/common/types/PolicyBucket.h index a2faf9c..df57d0f 100644 --- a/src/common/types/PolicyBucket.h +++ b/src/common/types/PolicyBucket.h @@ -52,20 +52,12 @@ public: //delete default constructor in order to prevent creation of buckets with no id PolicyBucket() = delete; PolicyBucket(const PolicyBucketId &id, - const PolicyResult &defaultPolicy = PredefinedPolicyType::DENY) - : m_defaultPolicy(defaultPolicy), - m_id(id) {} + const PolicyResult &defaultPolicy = PredefinedPolicyType::DENY); PolicyBucket(const PolicyBucketId &id, - const PolicyCollection &policies) - : m_policyCollection(makePolicyMap(policies)), - m_defaultPolicy(PredefinedPolicyType::DENY), - m_id(id) {} + const PolicyCollection &policies); PolicyBucket(const PolicyBucketId &id, const PolicyResult &defaultPolicy, - const PolicyCollection &policies) - : m_policyCollection(makePolicyMap(policies)), - m_defaultPolicy(defaultPolicy), - m_id(id) {} + const PolicyCollection &policies); PolicyBucket filtered(const PolicyKey &key) const; void insertPolicy(PolicyPtr policy); @@ -76,13 +68,6 @@ public: static PolicyMap makePolicyMap(const PolicyCollection &policies); -private: - PolicyMap m_policyCollection; - PolicyResult m_defaultPolicy; - PolicyBucketId m_id; - -public: - const_policy_iterator begin(void) const { return const_policy_iterator(m_policyCollection.begin()); } @@ -111,6 +96,15 @@ public: void setDefaultPolicy(const PolicyResult &defaultPolicy) { m_defaultPolicy = defaultPolicy; } + +private: + static void idValidator(const PolicyBucketId &id); + static bool isIdSeparator(char c); + + PolicyMap m_policyCollection; + PolicyResult m_defaultPolicy; + PolicyBucketId m_id; + static const std::string m_idSeparators; }; } /* namespace Cynara */ diff --git a/src/service/logic/Logic.cpp b/src/service/logic/Logic.cpp index eeed387..f76ce98 100644 --- a/src/service/logic/Logic.cpp +++ b/src/service/logic/Logic.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -134,6 +135,8 @@ void Logic::execute(RequestContextPtr context, InsertOrUpdateBucketRequestPtr re code = CodeResponse::Code::FAILED; } catch (const DefaultBucketSetNoneException &ex) { code = CodeResponse::Code::NOT_ALLOWED; + } catch (const InvalidBucketIdException &ex) { + code = CodeResponse::Code::NOT_ALLOWED; } context->returnResponse(context, std::make_shared(code, diff --git a/test/common/types/policybucket.cpp b/test/common/types/policybucket.cpp index 983a040..6c5d7a4 100644 --- a/test/common/types/policybucket.cpp +++ b/test/common/types/policybucket.cpp @@ -27,6 +27,7 @@ #include #include +#include "exceptions/InvalidBucketIdException.h" #include "types/PolicyBucket.h" #include "types/PolicyCollection.h" #include "types/PolicyKey.h" @@ -57,6 +58,15 @@ protected: Policy::simpleWithKey(PolicyKey("*", "*", "p1"), PredefinedPolicyType::ALLOW), Policy::simpleWithKey(PolicyKey("*", "*", "*"), PredefinedPolicyType::ALLOW) }; + + const std::vector goodIds = { + "_goodid", "good_id", "goodid_", "-goodid", "good-id", "goodid-" + }; + + const std::vector badIds = { + "{badid", "bad[id", "badid~", "/badid", "bad*id", "badid|", "badid;", "\tbadid", "badid\n", + " badid", "bad id", "badid " + }; }; TEST_F(PolicyBucketFixture, filtered) { @@ -141,3 +151,29 @@ TEST_F(PolicyBucketFixture, filtered_wildcard_none) { auto filtered = bucket.filtered(PolicyKey("cccc", "uuuu", "pppp")); ASSERT_THAT(filtered, IsEmpty()); } + +/** + * @brief Validate PolicyBucketIds during creation - passing bucket ids + * @test Scenario: + * - Iterate through vector of valid bucket ids and create them normally + * - PolicyBucket constructor should not throw any exception + */ +TEST_F(PolicyBucketFixture, bucket_id_validation_passes) { + for (auto it = goodIds.begin(); it != goodIds.end(); ++it) { + SCOPED_TRACE(*it); + ASSERT_NO_THROW(PolicyBucket(PolicyBucketId(*it))); + } +} + +/** + * @brief Validate PolicyBucketIds during creation - failing bucket ids + * @test Scenario: + * - Iterate through vector of bucket ids containing forbidden characters + * - PolicyBucket constructor should throw an exception every time it is called + */ +TEST_F(PolicyBucketFixture, bucket_id_validation_fails) { + for (auto it = badIds.begin(); it != badIds.end(); ++it) { + SCOPED_TRACE(*it); + ASSERT_THROW(PolicyBucket(PolicyBucketId(*it)), InvalidBucketIdException); + } +} -- 2.7.4 From bf1744e399095b89fb655787414d2d948f413281 Mon Sep 17 00:00:00 2001 From: Lukasz Wojciechowski Date: Fri, 24 Oct 2014 17:52:32 +0200 Subject: [PATCH 06/16] Treat invalid check_id as an error in async cancel If check_id passed to cynara_async_cancel_request() is invalid CYNARA_API_INVALID_PARAM will be returned. Id is invalid when: * was never generated by any previous call to cynara_async_create_request(); * response callback related to this id was already delivered. Change-Id: Iaa05fe71c752aedcb5414d162fc374f37420f36d --- src/client-async/logic/Logic.cpp | 2 +- src/include/cynara-client-async.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client-async/logic/Logic.cpp b/src/client-async/logic/Logic.cpp index 3a92b2c..8babbed 100644 --- a/src/client-async/logic/Logic.cpp +++ b/src/client-async/logic/Logic.cpp @@ -112,7 +112,7 @@ int Logic::cancelRequest(cynara_check_id checkId) { auto it = m_checks.find(checkId); if (it == m_checks.end() || it->second.cancelled()) - return CYNARA_API_SUCCESS; + return CYNARA_API_INVALID_PARAM; m_socketClient->appendRequest(std::make_shared(it->first)); diff --git a/src/include/cynara-client-async.h b/src/include/cynara-client-async.h index 9bed89d..536e5d7 100644 --- a/src/include/cynara-client-async.h +++ b/src/include/cynara-client-async.h @@ -386,6 +386,8 @@ int cynara_async_process(cynara_async *p_cynara); * cynara_status_callback callback may be triggered to be able to send cancel to cynara. * cynara_response_callback callback will be triggered with with * cynara_async_call_cause::CYNARA_CALL_CAUSE_CANCEL as cause param. + * If given id is not valid (was not requested or response callback was already delivered) + * cynara_async_cancel_request() returns CYNARA_API_INVALID_PARAM. * * \par Sync (or) Async: * This is a synchronous API. -- 2.7.4 From f7fc977dc46be0209bc54e9b40e0b3870fd34c39 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Wed, 22 Oct 2014 11:51:19 +0200 Subject: [PATCH 07/16] Add migration tool for Cynara's database This patch introduces tool for database migration if newer version of Cynara uses backward incompatible format of storing policies data. Migration tool is also used during installation of Cynara in order to initialize database with minimal contents. Change-Id: I7e6a376dad812c54f45a6a11ca559c97383d453d --- CMakeLists.txt | 6 ++ migration/CMakeLists.txt | 24 +++++ migration/cynara-db-migration.sh | 163 +++++++++++++++++++++++++++++++++ packaging/cynara-db-migration.manifest | 5 + packaging/cynara.spec | 23 +++++ 5 files changed, 221 insertions(+) create mode 100644 migration/CMakeLists.txt create mode 100644 migration/cynara-db-migration.sh create mode 100644 packaging/cynara-db-migration.manifest diff --git a/CMakeLists.txt b/CMakeLists.txt index ba5fe97..1c72d22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,11 @@ SET(BIN_INSTALL_DIR CACHE PATH "Binary installation directory") +SET(SBIN_INSTALL_DIR + "${CMAKE_INSTALL_PREFIX}/sbin" + CACHE PATH + "Administrative binary installation directory") + SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH @@ -92,6 +97,7 @@ SET(TARGET_LIB_CYNARA_STORAGE "cynara-storage") ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(pkgconfig) ADD_SUBDIRECTORY(systemd) +ADD_SUBDIRECTORY(migration) IF (BUILD_TESTS) ADD_SUBDIRECTORY(test) diff --git a/migration/CMakeLists.txt b/migration/CMakeLists.txt new file mode 100644 index 0000000..53e10d5 --- /dev/null +++ b/migration/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +# +# 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. +# +# @file CMakeLists.txt +# @author Pawel Wieczorek +# @brief CMake for migration tool +# + +INSTALL(FILES + ${CMAKE_BINARY_DIR}/migration/cynara-db-migration.sh + DESTINATION + ${SBIN_INSTALL_DIR}/cynara/ + ) diff --git a/migration/cynara-db-migration.sh b/migration/cynara-db-migration.sh new file mode 100644 index 0000000..0cf5edd --- /dev/null +++ b/migration/cynara-db-migration.sh @@ -0,0 +1,163 @@ +#!/bin/sh +# +# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +# +# 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. +# +# @file migration/cynara-db-migration.sh +# @author Pawel Wieczorek +# @brief Migration tool for Cynara's database +# + +##### Constants (these must not be modified by shell) + +STATE_PATH='/var/cynara' +DB_DIR='db' +INDEX_NAME='buckets' +DEFAULT_NAME='_' +DENY_POLICY=';0x0;' + +##### Variables, with default values (optional) + +CYNARA_USER='cynara' +CYNARA_GROUP='cynara' + +SMACK_LABEL='System' + +OLD_VERSION= +NEW_VERSION= + +##### Functions + +parse_opts() { + while getopts ":f:t:u:g:l:" opt; do + case $opt in + f ) + OLD_VERSION=${OPTARG} + ;; + t ) + NEW_VERSION=${OPTARG} + ;; + u ) + CYNARA_USER=${OPTARG} + ;; + g ) + CYNARA_GROUP=${OPTARG} + ;; + l ) + SMACK_LABEL=${OPTARG} + ;; + \? ) + echo "Invalid option: -$OPTARG" >&2 + failure + ;; + : ) + echo "Option -$OPTARG requires an argument." >&2 + failure + ;; + esac + done +} + +create_db() { + if [ -z ${NEW_VERSION} ]; then + failure + fi + + # Create Cynara's database directory: + mkdir -p ${STATE_PATH}/${DB_DIR} + + # Create contents of minimal database: first index file, then default bucket + echo ${DENY_POLICY} > ${STATE_PATH}/${DB_DIR}/${INDEX_NAME} + touch ${STATE_PATH}/${DB_DIR}/${DEFAULT_NAME} + + # Set proper permissions for newly created database + chown -R ${CYNARA_USER}:${CYNARA_GROUP} ${STATE_PATH}/${DB_DIR} + + # Set proper SMACK labels for newly created database + chsmack -a ${SMACK_LABEL} ${STATE_PATH}/${DB_DIR} + chsmack -a ${SMACK_LABEL} ${STATE_PATH}/${DB_DIR}/* +} + +migrate_db() { + if [ -z ${OLD_VERSION} -o -z ${NEW_VERSION} ]; then + failure + fi + + : + # : is a null command, as functions may not be empty. + # Actual body will be added if database structure changes. +} + +remove_db() { + if [ -z ${OLD_VERSION} ]; then + failure + fi + + rm -rf ${STATE_PATH} +} + +usage() { + cat << EOF +Usage: $0 COMMAND OPTIONS + +This script installs, migrates to another version or removes Cynara's policies database + +Commands: + upgrade (up) migrate old data to new version of database structure + install (in) create minimal database + uninstall (rm) remove database entirely + +Options: + -f from Set old version of database (mandatory for upgrade and uninstall) + -t to Set new version of database (mandatory for upgrade and install) + -u user Set database owner (default: cynara) + -g group Set database group (default: cynara) + -l label Set SMACK label for database (default: System) + -h Show this help message +EOF +} + +failure() { + usage + exit 1 +} + +##### Main + +if [ 0 -eq $# ]; then + failure +fi + +case $1 in + "up" | "upgrade" ) + shift $OPTIND + parse_opts "$@" + migrate_db + ;; + "in" | "install" ) + shift $OPTIND + parse_opts "$@" + create_db + ;; + "rm" | "uninstall" ) + shift $OPTIND + parse_opts "$@" + remove_db + ;; + "-h" | "--help" ) + usage + ;; + * ) + failure +esac diff --git a/packaging/cynara-db-migration.manifest b/packaging/cynara-db-migration.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/packaging/cynara-db-migration.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/cynara.spec b/packaging/cynara.spec index ffcccfb..35ca376 100644 --- a/packaging/cynara.spec +++ b/packaging/cynara.spec @@ -17,10 +17,13 @@ Source1009: libcynara-creds-dbus.manifest Source1010: libcynara-creds-socket.manifest Source1011: libcynara-session.manifest Source1012: libcynara-storage.manifest +Source1013: cynara-db-migration.manifest Requires: default-ac-domains Requires(pre): pwdutils +Requires(pre): cynara-db-migration >= %{version}-%{release} Requires(post): smack Requires(postun): pwdutils +Requires(postun): cynara-db-migration >= %{version}-%{release} BuildRequires: cmake BuildRequires: zip BuildRequires: pkgconfig(libsystemd-daemon) @@ -226,6 +229,13 @@ Requires: cynara = %{version}-%{release} %description -n cynara-devel service (devel version) +####################################################### +%package -n cynara-db-migration +Summary: Migration tools for Cynara's database + +%description -n cynara-db-migration +Migration tools for Cynara's database + %prep %setup -q cp -a %{SOURCE1001} . @@ -240,6 +250,7 @@ cp -a %{SOURCE1009} . cp -a %{SOURCE1010} . cp -a %{SOURCE1011} . cp -a %{SOURCE1012} . +cp -a %{SOURCE1013} . cp -a test/db/db* . %build @@ -287,6 +298,13 @@ if [ $? -eq 1 ]; then useradd -d /var/lib/empty -s /sbin/nologin -r -g %{group_name} %{user_name} > /dev/null 2>&1 fi +if [ $1 -gt 1 ] ; then + OLDVERSION="$(rpm -q --qf '%%{version}' %{name})" + %{_sbindir}/cynara/cynara-db-migration.sh upgrade -f ${OLDVERSION} -t %{version} +else + %{_sbindir}/cynara/cynara-db-migration.sh install -t %{version} +fi + %post ### Add file capabilities if needed ### setcap/getcap binary are useful. To use them you must install libcap and libcap-tools packages @@ -310,6 +328,7 @@ fi %postun if [ $1 = 0 ]; then + %{_sbindir}/cynara/cynara-db-migration.sh uninstall -f %{version} userdel -r %{user_name} > /dev/null 2>&1 groupdel %{user_name} > /dev/null 2>&1 systemctl daemon-reload @@ -515,3 +534,7 @@ fi %{_includedir}/cynara/cynara-session.h %{_libdir}/libcynara-session.so %{_libdir}/pkgconfig/cynara-session.pc + +%files -n cynara-db-migration +%manifest cynara-db-migration.manifest +%attr(744,root,root) %{_sbindir}/cynara/cynara-db-migration.sh -- 2.7.4 From 11ba3c703cfb535b7ec33b7ed23fee7c34fcc374 Mon Sep 17 00:00:00 2001 From: Lukasz Wojciechowski Date: Mon, 3 Nov 2014 08:25:37 +0100 Subject: [PATCH 08/16] Remove visibility attributes from header file Visibility attributes ar not needed in header file. Usage of them by CYNARA_API macro causes also to make internal file attributes.h published. Change-Id: I99bb84d5af96120cdc448e837601cecc05494570 --- src/include/cynara-creds-commons.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/include/cynara-creds-commons.h b/src/include/cynara-creds-commons.h index 813c1b9..ee6d2b8 100644 --- a/src/include/cynara-creds-commons.h +++ b/src/include/cynara-creds-commons.h @@ -26,7 +26,6 @@ #ifndef CYNARA_CREDS_COMMONS_H #define CYNARA_CREDS_COMMONS_H -#include #include enum cynara_client_creds { @@ -78,7 +77,6 @@ extern "C" { * CYNARA_API_UNKNOWN_ERROR if there is other error. * */ -CYNARA_API int cynara_creds_get_default_client_method(enum cynara_client_creds *method); /** @@ -115,7 +113,6 @@ int cynara_creds_get_default_client_method(enum cynara_client_creds *method); * CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation. * CYNARA_API_UNKNOWN_ERROR if there is other error. */ -CYNARA_API int cynara_creds_get_default_user_method(enum cynara_user_creds *method); #ifdef __cplusplus -- 2.7.4 From fa40410ab295da5e6847945c75f66b8a6027137d Mon Sep 17 00:00:00 2001 From: Marcin Niesluchowski Date: Tue, 4 Nov 2014 18:09:54 +0100 Subject: [PATCH 09/16] Change Group in spec file Group Security/Access Control has been removed. Cynara current group is Security/Application Privilege. Nonexistent group causes build break. Change-Id: I58d800209cb232e60e60747eb79244fb57c7b977 --- packaging/cynara.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/cynara.spec b/packaging/cynara.spec index 35ca376..e390eb7 100644 --- a/packaging/cynara.spec +++ b/packaging/cynara.spec @@ -2,7 +2,7 @@ Name: cynara Summary: Cynara service with client libraries Version: 0.3.0 Release: 1 -Group: Security/Access Control +Group: Security/Application Privilege License: Apache-2.0 Source0: %{name}-%{version}.tar.gz Source1001: cynara.manifest -- 2.7.4 From 0bbfec596d7f4072fab1bc3f29bc413736ec416a Mon Sep 17 00:00:00 2001 From: Adam Malinowski Date: Thu, 6 Nov 2014 14:47:15 +0100 Subject: [PATCH 10/16] Fix build break caused by wrong system group names cynara-rpmlintrc file was added to project in order to ignore errors related to wrong group names. File will be removed when problems with new group names is fixed. Change-Id: Ibd0ee42b707fba059f0172522cba4804c28d2cb5 --- packaging/cynara-rpmlintrc | 1 + packaging/cynara.spec | 1 + 2 files changed, 2 insertions(+) create mode 100644 packaging/cynara-rpmlintrc diff --git a/packaging/cynara-rpmlintrc b/packaging/cynara-rpmlintrc new file mode 100644 index 0000000..1fad581 --- /dev/null +++ b/packaging/cynara-rpmlintrc @@ -0,0 +1 @@ +setBadness('non-standard-group', 0) diff --git a/packaging/cynara.spec b/packaging/cynara.spec index e390eb7..213c94e 100644 --- a/packaging/cynara.spec +++ b/packaging/cynara.spec @@ -5,6 +5,7 @@ Release: 1 Group: Security/Application Privilege License: Apache-2.0 Source0: %{name}-%{version}.tar.gz +Source1000: %{name}-rpmlintrc Source1001: cynara.manifest Source1002: libcynara-client.manifest Source1003: libcynara-client-async.manifest -- 2.7.4 From 626099ee7f86f15d9d1e62826ecca418598e3a6c Mon Sep 17 00:00:00 2001 From: Marcin Niesluchowski Date: Wed, 5 Nov 2014 12:40:35 +0100 Subject: [PATCH 11/16] Fix read errno handling in Socket class Cynara Socket class treats ECONNRESET (socket closed transmiting RST instead of FIN) during read as unknown error. Handle it as disconnection. Change-Id: Iecbfa5c32c7ef8b6b5da97170269aa86e2740c22 --- src/common/sockets/Socket.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/sockets/Socket.cpp b/src/common/sockets/Socket.cpp index 0c33a7e..5a57a54 100644 --- a/src/common/sockets/Socket.cpp +++ b/src/common/sockets/Socket.cpp @@ -269,6 +269,9 @@ bool Socket::receiveFromServer(BinaryQueue &queue) { case EWOULDBLOCK: #endif return true; + case ECONNRESET: + LOGW("read returned -1 with ECONNRESET / Connection closed by server."); + return false; default: LOGE("'read' function error [%d] : <%s>", err, strerror(err)); throw UnexpectedErrorException(err, strerror(err)); -- 2.7.4 From aaf46e4b88c42b633e731d526010ebe2a5197b42 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Mon, 10 Nov 2014 13:43:59 +0100 Subject: [PATCH 12/16] Fix invocations of LOG missing format string argument MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit First argument of LOG* macros is passed to sd_journal_print() as format string. In some places these macros were used with no format string at all, simply passing e.what() from an exception. This could lead to a format string vulnerability in the code, potentially allowing arbitrary code execution. This error also caused build break: In file included from /data/src/tizen/cynara/src/client/api/client-api.cpp:27:0: /data/src/tizen/cynara/src/common/exceptions/TryCatch.h: In function ‘int Cynara::tryCatch(const std::function&)’: /data/src/tizen/cynara/src/common/exceptions/TryCatch.h:41:178: error: format not a string literal and no format arguments [-Werror=format-security] LOGE(e.what()); (... and more ...) Change-Id: I1259283cf1bd2fa0fb2d271e38a7b416e17939f7 Signed-off-by: Rafal Krypa --- src/admin/api/admin-api.cpp | 2 +- src/client-async/api/client-async-api.cpp | 4 ++-- src/client/api/client-api.cpp | 2 +- src/common/exceptions/TryCatch.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/admin/api/admin-api.cpp b/src/admin/api/admin-api.cpp index e444b7a..1e73ff7 100644 --- a/src/admin/api/admin-api.cpp +++ b/src/admin/api/admin-api.cpp @@ -197,7 +197,7 @@ int cynara_admin_check(struct cynara_admin *p_cynara_admin, userStr = user; privilegeStr = privilege; } catch (const std::length_error &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_INVALID_PARAM; } diff --git a/src/client-async/api/client-async-api.cpp b/src/client-async/api/client-async-api.cpp index d350e10..1e3479e 100644 --- a/src/client-async/api/client-async-api.cpp +++ b/src/client-async/api/client-async-api.cpp @@ -84,7 +84,7 @@ int cynara_async_check_cache(cynara_async *p_cynara, const char *client, const c userStr = user; privilegeStr = privilege; } catch (const std::length_error &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_INVALID_PARAM; } return p_cynara->impl->checkCache(clientStr, clientSessionStr, userStr, privilegeStr); @@ -113,7 +113,7 @@ int cynara_async_create_request(cynara_async *p_cynara, const char *client, userStr = user; privilegeStr = privilege; } catch (const std::length_error &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_INVALID_PARAM; } cynara_check_id checkId; diff --git a/src/client/api/client-api.cpp b/src/client/api/client-api.cpp index 4eb4625..935c6cd 100644 --- a/src/client/api/client-api.cpp +++ b/src/client/api/client-api.cpp @@ -88,7 +88,7 @@ int cynara_check(cynara *p_cynara, const char *client, const char *client_sessio userStr = user; privilegeStr = privilege; } catch (const std::length_error &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_INVALID_PARAM; } return p_cynara->impl->check(clientStr, clientSessionStr, userStr, privilegeStr); diff --git a/src/common/exceptions/TryCatch.h b/src/common/exceptions/TryCatch.h index b1ef172..3964c01 100644 --- a/src/common/exceptions/TryCatch.h +++ b/src/common/exceptions/TryCatch.h @@ -38,13 +38,13 @@ int tryCatch(const std::function &func) { try { return func(); } catch (const std::bad_alloc &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_OUT_OF_MEMORY; } catch (const NoMemoryException &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_OUT_OF_MEMORY; } catch (const std::exception &e) { - LOGE(e.what()); + LOGE("%s", e.what()); return CYNARA_API_UNKNOWN_ERROR; } catch (...) { LOGE("Unexpected exception"); -- 2.7.4 From 0ded5caa100a6802eb2f3e45e7b2073f0cef95eb Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Wed, 27 Aug 2014 09:18:02 +0200 Subject: [PATCH 13/16] Implement mechanism assuring integrity of database There is also added mechanism for cleaning up Cynara's database directory upon loading policies to memory. There is added test checking whether mechanism behaves as intended. Change-Id: I926d1aebf394c092e00731b73717e0e1c55bad0c --- packaging/cynara.spec | 3 +- src/storage/CMakeLists.txt | 1 + src/storage/InMemoryStorageBackend.cpp | 46 +++- src/storage/InMemoryStorageBackend.h | 10 +- src/storage/Integrity.cpp | 232 +++++++++++++++++++++ src/storage/Integrity.h | 71 +++++++ test/CMakeLists.txt | 1 + test/db/db6/_ | 0 test/db/db6/_additional | 1 + test/db/db6/_additional~ | 0 test/db/db6/_~ | 0 test/db/db6/buckets | 2 + test/db/db6/buckets~ | 2 + test/db/db6/guard | 0 .../fakeinmemorystoragebackend.h | 2 +- .../inmemeorystoragebackendfixture.h | 2 + .../inmemorystoragebackend.cpp | 38 +++- 17 files changed, 396 insertions(+), 15 deletions(-) create mode 100644 src/storage/Integrity.cpp create mode 100644 src/storage/Integrity.h create mode 100644 test/db/db6/_ create mode 100644 test/db/db6/_additional create mode 100644 test/db/db6/_additional~ create mode 100644 test/db/db6/_~ create mode 100644 test/db/db6/buckets create mode 100644 test/db/db6/buckets~ create mode 100644 test/db/db6/guard diff --git a/packaging/cynara.spec b/packaging/cynara.spec index 213c94e..68d52ce 100644 --- a/packaging/cynara.spec +++ b/packaging/cynara.spec @@ -283,7 +283,7 @@ cp ./conf/creds.conf %{buildroot}/%{conf_path}/creds.conf mkdir -p %{buildroot}/usr/lib/systemd/system/sockets.target.wants mkdir -p %{buildroot}/%{state_path} -mkdir -p %{buildroot}/%{tests_dir} +mkdir -p %{buildroot}/%{tests_dir}/empty_db cp -a db* %{buildroot}/%{tests_dir} ln -s ../cynara.socket %{buildroot}/usr/lib/systemd/system/sockets.target.wants/cynara.socket ln -s ../cynara-admin.socket %{buildroot}/usr/lib/systemd/system/sockets.target.wants/cynara-admin.socket @@ -494,6 +494,7 @@ fi %manifest cynara-tests.manifest %attr(755,root,root) /usr/bin/cynara-tests %attr(755,root,root) %{tests_dir}/db*/* +%dir %attr(755,root,root) %{tests_dir}/empty_db %files -n libcynara-creds-commons %manifest libcynara-creds-commons.manifest diff --git a/src/storage/CMakeLists.txt b/src/storage/CMakeLists.txt index 620dc2d..7bccc8c 100644 --- a/src/storage/CMakeLists.txt +++ b/src/storage/CMakeLists.txt @@ -25,6 +25,7 @@ SET(CYNARA_LIB_CYNARA_STORAGE_PATH ${CYNARA_PATH}/storage) SET(LIB_CYNARA_STORAGE_SOURCES ${CYNARA_LIB_CYNARA_STORAGE_PATH}/BucketDeserializer.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/InMemoryStorageBackend.cpp + ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Integrity.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Storage.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/StorageDeserializer.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/StorageSerializer.cpp diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index af18926..a2366e8 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -43,6 +42,7 @@ #include #include +#include #include #include @@ -50,17 +50,28 @@ namespace Cynara { -const std::string InMemoryStorageBackend::m_indexFileName = "buckets"; +const std::string InMemoryStorageBackend::m_indexFilename = "buckets"; +const std::string InMemoryStorageBackend::m_backupFilenameSuffix = "~"; +const std::string InMemoryStorageBackend::m_bucketFilenamePrefix = "_"; void InMemoryStorageBackend::load(void) { - std::string indexFilename = m_dbPath + m_indexFileName; + Integrity integrity(m_dbPath, m_indexFilename, m_backupFilenameSuffix, m_bucketFilenamePrefix); + bool isBackupValid = integrity.backupGuardExists(); + std::string bucketSuffix = ""; + std::string indexFilename = m_dbPath + m_indexFilename; + + if (isBackupValid) { + bucketSuffix += m_backupFilenameSuffix; + indexFilename += m_backupFilenameSuffix; + } try { auto indexStream = std::make_shared(); openFileStream(indexStream, indexFilename); StorageDeserializer storageDeserializer(indexStream, - std::bind(&InMemoryStorageBackend::bucketStreamOpener, this, std::placeholders::_1)); + std::bind(&InMemoryStorageBackend::bucketStreamOpener, this, + std::placeholders::_1, bucketSuffix)); storageDeserializer.initBuckets(buckets()); storageDeserializer.loadBuckets(buckets()); @@ -74,6 +85,13 @@ void InMemoryStorageBackend::load(void) { LOGN("Creating defaultBucket."); this->buckets().insert({ defaultPolicyBucketId, PolicyBucket(defaultPolicyBucketId) }); } + + if (isBackupValid) { + integrity.revalidatePrimaryDatabase(buckets()); + } + //in case there were unnecessary files in db directory + integrity.deleteNonIndexedFiles(std::bind(&InMemoryStorageBackend::hasBucket, this, + std::placeholders::_1)); } void InMemoryStorageBackend::save(void) { @@ -90,11 +108,19 @@ void InMemoryStorageBackend::save(void) { } auto indexStream = std::make_shared(); - openDumpFileStream(indexStream, m_dbPath + m_indexFileName); + std::string indexFilename = m_dbPath + m_indexFilename; + openDumpFileStream(indexStream, indexFilename + m_backupFilenameSuffix); StorageSerializer storageSerializer(indexStream); storageSerializer.dump(buckets(), std::bind(&InMemoryStorageBackend::bucketDumpStreamOpener, this, std::placeholders::_1)); + + Integrity integrity(m_dbPath, m_indexFilename, m_backupFilenameSuffix, m_bucketFilenamePrefix); + + integrity.syncDatabase(buckets(), true); + integrity.createBackupGuard(); + integrity.revalidatePrimaryDatabase(buckets()); + //guard is removed during revalidation } PolicyBucket InMemoryStorageBackend::searchDefaultBucket(const PolicyKey &key) { @@ -181,8 +207,9 @@ void InMemoryStorageBackend::openFileStream(std::shared_ptr strea // stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); stream->open(filename); - if (!stream->is_open()) + if (!stream->is_open()) { throw FileNotFoundException(filename); + } } void InMemoryStorageBackend::openDumpFileStream(std::shared_ptr stream, @@ -195,8 +222,8 @@ void InMemoryStorageBackend::openDumpFileStream(std::shared_ptr s } std::shared_ptr InMemoryStorageBackend::bucketStreamOpener( - const PolicyBucketId &bucketId) { - std::string bucketFilename = m_dbPath + "_" + bucketId; + const PolicyBucketId &bucketId, const std::string &filenameSuffix) { + std::string bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + filenameSuffix; auto bucketStream = std::make_shared(); try { openFileStream(bucketStream, bucketFilename); @@ -208,7 +235,8 @@ std::shared_ptr InMemoryStorageBackend::bucketStreamOpener( std::shared_ptr InMemoryStorageBackend::bucketDumpStreamOpener( const PolicyBucketId &bucketId) { - std::string bucketFilename = m_dbPath + "_" + bucketId; + std::string bucketFilename = m_dbPath + m_bucketFilenamePrefix + + bucketId + m_backupFilenameSuffix; auto bucketStream = std::make_shared(); openDumpFileStream(bucketStream, bucketFilename); diff --git a/src/storage/InMemoryStorageBackend.h b/src/storage/InMemoryStorageBackend.h index cc35f95..d03dd6c 100644 --- a/src/storage/InMemoryStorageBackend.h +++ b/src/storage/InMemoryStorageBackend.h @@ -62,15 +62,19 @@ public: protected: InMemoryStorageBackend() {} void openFileStream(std::shared_ptr stream, const std::string &filename); - std::shared_ptr bucketStreamOpener(const PolicyBucketId &bucketId); + std::shared_ptr bucketStreamOpener(const PolicyBucketId &bucketId, + const std::string &fileNameSuffix); - void openDumpFileStream(std::shared_ptr stream, const std::string &filename); + virtual void openDumpFileStream(std::shared_ptr stream, + const std::string &filename); std::shared_ptr bucketDumpStreamOpener(const PolicyBucketId &bucketId); private: std::string m_dbPath; Buckets m_buckets; - static const std::string m_indexFileName; + static const std::string m_indexFilename; + static const std::string m_backupFilenameSuffix; + static const std::string m_bucketFilenamePrefix; protected: virtual Buckets &buckets(void) { diff --git a/src/storage/Integrity.cpp b/src/storage/Integrity.cpp new file mode 100644 index 0000000..8057e9c --- /dev/null +++ b/src/storage/Integrity.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ +/** + * @file src/storage/Integrity.cpp + * @author Pawel Wieczorek + * @version 0.1 + * @brief Implementation of Cynara::Integrity + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Integrity.h" + +namespace Cynara { + +const std::string Integrity::m_guardFilename = "guard"; + +bool Integrity::backupGuardExists(void) const { + struct stat buffer; + std::string guardFilename = m_dbPath + m_guardFilename; + + int ret = stat(guardFilename.c_str(), &buffer); + + if (ret == 0) { + return true; + } else { + int err = errno; + if (err != ENOENT) { + LOGE("'stat' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + return false; + } +} + +void Integrity::createBackupGuard(void) const { + syncElement(m_dbPath + m_guardFilename, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC); + syncDirectory(m_dbPath); +} + +void Integrity::syncDatabase(const Buckets &buckets, bool syncBackup) { + std::string suffix = ""; + + if (syncBackup) { + suffix += m_backupFilenameSuffix; + } + + for (const auto &bucketIter : buckets) { + const auto &bucketId = bucketIter.first; + const auto &bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + suffix; + + syncElement(bucketFilename); + } + + syncElement(m_dbPath + m_indexFilename + suffix); + syncDirectory(m_dbPath); +} + +void Integrity::revalidatePrimaryDatabase(const Buckets &buckets) { + createPrimaryHardLinks(buckets); + syncDatabase(buckets, false); + + deleteHardLink(m_dbPath + m_guardFilename); + syncDirectory(m_dbPath); + + deleteBackupHardLinks(buckets); +} + +void Integrity::deleteNonIndexedFiles(BucketPresenceTester tester) { + DIR *dirPtr = nullptr; + struct dirent *direntPtr; + + if ((dirPtr = opendir(m_dbPath.c_str())) == nullptr) { + int err = errno; + LOGE("'opendir' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + return; + } + + while (errno = 0, (direntPtr = readdir(dirPtr)) != nullptr) { + std::string filename = direntPtr->d_name; + //ignore all special files (working dir, parent dir, index) + if ("." == filename || ".." == filename || "buckets" == filename) { + continue; + } + + std::string bucketId; + auto nameLength = filename.length(); + auto prefixLength = m_bucketFilenamePrefix.length(); + + //remove if it is impossible that it is a bucket file + if (nameLength < prefixLength) { + deleteHardLink(m_dbPath + filename); + continue; + } + + //remove if there is no bucket filename prefix + //0 is returned from string::compare() if strings are equal + if (0 != filename.compare(0, prefixLength, m_bucketFilenamePrefix)) { + deleteHardLink(m_dbPath + filename); + continue; + } + + //remove if bucket is not in index + bucketId = filename.substr(prefixLength); + if (!tester(bucketId)) { + deleteHardLink(m_dbPath + filename); + } + } + + if (errno) { + int err = errno; + LOGE("'readdir' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + return; + } +} + +void Integrity::syncElement(const std::string &filename, int flags, mode_t mode) { + int fileFd = TEMP_FAILURE_RETRY(open(filename.c_str(), flags, mode)); + + if (fileFd < 0) { + int err = errno; + if (err != EEXIST) { + LOGE("'open' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } else { + throw CannotCreateFileException(filename); + } + } + + int ret = fsync(fileFd); + + if (ret < 0) { + int err = errno; + LOGE("'fsync' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } + + ret = close(fileFd); + + if (ret < 0) { + int err = errno; + LOGE("'close' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } +} + +// from: man 2 fsync +// Calling fsync() does not necessarily ensure that the entry in the directory containing +// the file has also reached disk. For that an explicit fsync() on a file descriptor for +// the directory is also needed. +void Integrity::syncDirectory(const std::string &dirname, mode_t mode) { + syncElement(dirname, O_DIRECTORY, mode); +} + +void Integrity::createPrimaryHardLinks(const Buckets &buckets) { + for (const auto &bucketIter : buckets) { + const auto &bucketId = bucketIter.first; + const auto &bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId; + + deleteHardLink(bucketFilename); + createHardLink(bucketFilename + m_backupFilenameSuffix, bucketFilename); + } + + const auto &indexFilename = m_dbPath + m_indexFilename; + + deleteHardLink(indexFilename); + createHardLink(indexFilename + m_backupFilenameSuffix, indexFilename); +} + +void Integrity::deleteBackupHardLinks(const Buckets &buckets) { + for (const auto &bucketIter : buckets) { + const auto &bucketId = bucketIter.first; + const auto &bucketFilename = m_dbPath + m_bucketFilenamePrefix + + bucketId + m_backupFilenameSuffix; + + deleteHardLink(bucketFilename); + } + + deleteHardLink(m_dbPath + m_indexFilename + m_backupFilenameSuffix); +} + +void Integrity::createHardLink(const std::string &oldName, const std::string &newName) { + int ret = link(oldName.c_str(), newName.c_str()); + + if (ret < 0) { + int err = errno; + throw UnexpectedErrorException(err, strerror(err)); + LOGN("Trying to link to non-existent file: <%s>", oldName.c_str()); + } +} + +void Integrity::deleteHardLink(const std::string &filename) { + int ret = unlink(filename.c_str()); + + if (ret < 0) { + int err = errno; + if (err != ENOENT) { + LOGE("'unlink' function error [%d] : <%s>", err, strerror(err)); + throw UnexpectedErrorException(err, strerror(err)); + } else { + LOGN("Trying to unlink non-existent file: <%s>", filename.c_str()); + } + } +} + +} /* namespace Cynara */ diff --git a/src/storage/Integrity.h b/src/storage/Integrity.h new file mode 100644 index 0000000..4b677b0 --- /dev/null +++ b/src/storage/Integrity.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ +/** + * @file src/storage/Integrity.h + * @author Pawel Wieczorek + * @version 0.1 + * @brief Headers for Cynara::Integrity + */ + +#ifndef SRC_STORAGE_INTEGRITY_H_ +#define SRC_STORAGE_INTEGRITY_H_ + +#include +#include + +#include + +namespace Cynara { + +class Integrity +{ +public: + typedef std::function BucketPresenceTester; + Integrity(const std::string &path, const std::string &index, const std::string &backupSuffix, + const std::string &bucketPrefix) + : m_dbPath(path), m_indexFilename(index), m_backupFilenameSuffix(backupSuffix), + m_bucketFilenamePrefix(bucketPrefix) { + } + virtual ~Integrity() {}; + + virtual bool backupGuardExists(void) const; + virtual void createBackupGuard(void) const; + virtual void syncDatabase(const Buckets &buckets, bool syncBackup); + virtual void revalidatePrimaryDatabase(const Buckets &buckets); + virtual void deleteNonIndexedFiles(BucketPresenceTester tester); + +protected: + static void syncElement(const std::string &filename, int flags = O_RDONLY, + mode_t mode = S_IRUSR | S_IWUSR); + static void syncDirectory(const std::string &dirname, mode_t mode = S_IRUSR | S_IWUSR); + + void createPrimaryHardLinks(const Buckets &buckets); + void deleteBackupHardLinks(const Buckets &buckets); + + static void createHardLink(const std::string &oldName, const std::string &newName); + static void deleteHardLink(const std::string &filename); + +private: + const std::string m_dbPath; + const std::string m_indexFilename; + const std::string m_backupFilenameSuffix; + const std::string m_bucketFilenamePrefix; + static const std::string m_guardFilename; +}; + +} // namespace Cynara + +#endif /* SRC_STORAGE_INTEGRITY_H_ */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ffddd87..063098f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,6 +32,7 @@ SET(CYNARA_SOURCES_FOR_TESTS ${CYNARA_SRC}/helpers/creds-commons/creds-commons.cpp ${CYNARA_SRC}/storage/BucketDeserializer.cpp ${CYNARA_SRC}/storage/InMemoryStorageBackend.cpp + ${CYNARA_SRC}/storage/Integrity.cpp ${CYNARA_SRC}/storage/Storage.cpp ${CYNARA_SRC}/storage/StorageDeserializer.cpp ${CYNARA_SRC}/storage/StorageSerializer.cpp diff --git a/test/db/db6/_ b/test/db/db6/_ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db6/_additional b/test/db/db6/_additional new file mode 100644 index 0000000..cbeab67 --- /dev/null +++ b/test/db/db6/_additional @@ -0,0 +1 @@ +client1;user1;privilege1;0x0; diff --git a/test/db/db6/_additional~ b/test/db/db6/_additional~ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db6/_~ b/test/db/db6/_~ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db6/buckets b/test/db/db6/buckets new file mode 100644 index 0000000..19d7a93 --- /dev/null +++ b/test/db/db6/buckets @@ -0,0 +1,2 @@ +;0x0 +additional;0xFFFF diff --git a/test/db/db6/buckets~ b/test/db/db6/buckets~ new file mode 100644 index 0000000..19d7a93 --- /dev/null +++ b/test/db/db6/buckets~ @@ -0,0 +1,2 @@ +;0x0 +additional;0xFFFF diff --git a/test/db/db6/guard b/test/db/db6/guard new file mode 100644 index 0000000..e69de29 diff --git a/test/storage/inmemorystoragebackend/fakeinmemorystoragebackend.h b/test/storage/inmemorystoragebackend/fakeinmemorystoragebackend.h index 1c80dea..52b4fe3 100644 --- a/test/storage/inmemorystoragebackend/fakeinmemorystoragebackend.h +++ b/test/storage/inmemorystoragebackend/fakeinmemorystoragebackend.h @@ -29,7 +29,7 @@ class FakeInMemoryStorageBackend : public Cynara::InMemoryStorageBackend { public: using Cynara::InMemoryStorageBackend::InMemoryStorageBackend; - MOCK_METHOD0(buckets, Cynara::Buckets&()); + MOCK_METHOD0(buckets, Cynara::Buckets&(void)); }; diff --git a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h index 3f08dea..2ec2920 100644 --- a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h +++ b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h @@ -67,6 +67,8 @@ protected: // TODO: consider defaulting accessor with ON_CALL Cynara::Buckets m_buckets; + static const std::string m_indexFileName; + static const std::string m_backupFileNameSuffix; }; diff --git a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp index 11c0968..c0fd640 100644 --- a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp +++ b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp @@ -40,6 +40,9 @@ using namespace Cynara; +const std::string InMemeoryStorageBackendFixture::m_indexFileName = "buckets"; +const std::string InMemeoryStorageBackendFixture::m_backupFileNameSuffix = "~"; + TEST_F(InMemeoryStorageBackendFixture, defaultPolicyIsDeny) { using ::testing::ReturnRef; @@ -192,7 +195,7 @@ TEST_F(InMemeoryStorageBackendFixture, deletePolicyFromNonexistentBucket) { // Database dir is empty TEST_F(InMemeoryStorageBackendFixture, load_no_db) { using ::testing::ReturnRef; - auto testDbPath = std::string(CYNARA_TESTS_DIR) + "/db1/"; + auto testDbPath = std::string(CYNARA_TESTS_DIR) + "/empty_db/"; FakeInMemoryStorageBackend backend(testDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); backend.load(); @@ -252,3 +255,36 @@ TEST_F(InMemeoryStorageBackendFixture, second_bucket_corrupted) { backend.load(); ASSERT_DB_VIRGIN(m_buckets); } + +/** + * @brief Database was corrupted, restore from backup (which is present) + * @test Scenario: + * - There still is guard file in earlier prepared Cynara's policy database directory (db6) + * - Execution of load() should use backup files (present, with different contents than primaries) + * - Loaded database is checked - backup files were empty, so should recently loaded policies + */ +TEST_F(InMemeoryStorageBackendFixture, load_from_backup) { + using ::testing::ReturnRef; + using ::testing::IsEmpty; + using ::testing::InSequence; + + auto testDbPath = std::string(CYNARA_TESTS_DIR) + "/db6/"; + FakeInMemoryStorageBackend backend(testDbPath); + + { + // Calls are expected in a specific order + InSequence s; + EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); + backend.load(); + } + + std::vector bucketIds = { "", "additional" }; + for(const auto &bucketId : bucketIds) { + SCOPED_TRACE(bucketId); + const auto bucketIter = m_buckets.find(bucketId); + ASSERT_NE(m_buckets.end(), bucketIter); + + const auto &bucketPolicies = bucketIter->second; + ASSERT_THAT(bucketPolicies, IsEmpty()); + } +} -- 2.7.4 From d1b33eecd1065cf626efc5380dcfc2da6a7c1359 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Thu, 13 Nov 2014 12:07:20 +0100 Subject: [PATCH 14/16] Ensure creation of minimal database This patch changes default behaviour of migration tool during package upgrade. Previously, Cynara's state path was left untouched. Now creation of minimal database is ensured. No changes are made if it already existed. Change-Id: I25158aec7d7b436ac1446d43277afe1337bfe4e5 --- migration/cynara-db-migration.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/migration/cynara-db-migration.sh b/migration/cynara-db-migration.sh index 0cf5edd..8cbebbf 100644 --- a/migration/cynara-db-migration.sh +++ b/migration/cynara-db-migration.sh @@ -94,9 +94,10 @@ migrate_db() { failure fi - : - # : is a null command, as functions may not be empty. - # Actual body will be added if database structure changes. + # Create minimal database if there was none: + if [ ! -d "${STATE_PATH}/${DB_DIR}" ]; then + create_db + fi } remove_db() { -- 2.7.4 From 8dd72120e13a4192408b8940b85ef40d980a3fdd Mon Sep 17 00:00:00 2001 From: Zofia Abramowska Date: Thu, 13 Nov 2014 14:25:56 +0100 Subject: [PATCH 15/16] Fix segfault in dump_buckets test Change-Id: If614900c9710dc0600c48622051afb484709155f --- test/storage/serializer/serialize.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/storage/serializer/serialize.cpp b/test/storage/serializer/serialize.cpp index cceed78..938283d 100644 --- a/test/storage/serializer/serialize.cpp +++ b/test/storage/serializer/serialize.cpp @@ -48,8 +48,8 @@ public: // Fake StorageSerializer for Cynara::PolicyBucket class FakeStorageSerializer : public Cynara::StorageSerializer { public: - FakeStorageSerializer() : Cynara::StorageSerializer(outStream), - outStream(new std::ostringstream()) {} + FakeStorageSerializer(std::shared_ptr o) : Cynara::StorageSerializer(o), + outStream(o) {} MOCK_METHOD1(dump, void(const Cynara::PolicyBucket &bucket)); std::shared_ptr outStream; }; @@ -82,7 +82,8 @@ TEST_F(StorageSerializerFixture, dump_buckets) { using ::testing::UnorderedElementsAreArray; // Will be returned as serializer for buckets - auto fakeBucketSerializer = std::make_shared(); + auto fakeBucketSerializer = std::make_shared( + std::make_shared()); buckets = { { "bucket1", PolicyBucket("bucket1", PredefinedPolicyType::DENY) }, -- 2.7.4 From 656f1a804ecf28619a0f7ea91ce26b43d27ec075 Mon Sep 17 00:00:00 2001 From: Zofia Abramowska Date: Wed, 12 Nov 2014 16:30:35 +0100 Subject: [PATCH 16/16] Remove unnecessary Protocol shared pointers ProtocolFramePtr and ProtocolFrameHeaderPtr was used unnecessary (no shared ownership required). Change-Id: I71d4bf797450a46bc35e2321ff8d01a6508bcf88 --- src/common/protocol/ProtocolAdmin.cpp | 76 ++++++++++++------------- src/common/protocol/ProtocolClient.cpp | 26 ++++----- src/common/protocol/ProtocolFrame.cpp | 4 +- src/common/protocol/ProtocolFrame.h | 8 +-- src/common/protocol/ProtocolFrameHeader.h | 2 - src/common/protocol/ProtocolFrameSerializer.cpp | 16 +++--- src/common/protocol/ProtocolFrameSerializer.h | 4 +- 7 files changed, 66 insertions(+), 70 deletions(-) diff --git a/src/common/protocol/ProtocolAdmin.cpp b/src/common/protocol/ProtocolAdmin.cpp index 7d7d0f9..ca17e34 100644 --- a/src/common/protocol/ProtocolAdmin.cpp +++ b/src/common/protocol/ProtocolAdmin.cpp @@ -228,14 +228,14 @@ void ProtocolAdmin::execute(RequestContextPtr context, AdminCheckRequestPtr requ request->key().user().value().c_str(), request->key().privilege().value().c_str(), request->startBucket().c_str(), request->recursive()); - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); - ProtocolSerialization::serialize(*frame, OpAdminCheckRequest); - ProtocolSerialization::serialize(*frame, request->key().client().value()); - ProtocolSerialization::serialize(*frame, request->key().user().value()); - ProtocolSerialization::serialize(*frame, request->key().privilege().value()); - ProtocolSerialization::serialize(*frame, request->startBucket()); - ProtocolSerialization::serialize(*frame, request->recursive()); + ProtocolSerialization::serialize(frame, OpAdminCheckRequest); + ProtocolSerialization::serialize(frame, request->key().client().value()); + ProtocolSerialization::serialize(frame, request->key().user().value()); + ProtocolSerialization::serialize(frame, request->key().privilege().value()); + ProtocolSerialization::serialize(frame, request->startBucket()); + ProtocolSerialization::serialize(frame, request->recursive()); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } @@ -246,12 +246,12 @@ void ProtocolAdmin::execute(RequestContextPtr context, InsertOrUpdateBucketReque request->bucketId().c_str(), request->result().policyType(), request->result().metadata().c_str()); - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); - ProtocolSerialization::serialize(*frame, OpInsertOrUpdateBucket); - ProtocolSerialization::serialize(*frame, request->bucketId()); - ProtocolSerialization::serialize(*frame, request->result().policyType()); - ProtocolSerialization::serialize(*frame, request->result().metadata()); + ProtocolSerialization::serialize(frame, OpInsertOrUpdateBucket); + ProtocolSerialization::serialize(frame, request->bucketId()); + ProtocolSerialization::serialize(frame, request->result().policyType()); + ProtocolSerialization::serialize(frame, request->result().metadata()); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } @@ -260,10 +260,10 @@ void ProtocolAdmin::execute(RequestContextPtr context, RemoveBucketRequestPtr re LOGD("Serializing RemoveBucketRequest: sequenceNumber [%" PRIu16 "], bucketId <%s>", request->sequenceNumber(), request->bucketId().c_str()); - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); - ProtocolSerialization::serialize(*frame, OpRemoveBucket); - ProtocolSerialization::serialize(*frame, request->bucketId()); + ProtocolSerialization::serialize(frame, OpRemoveBucket); + ProtocolSerialization::serialize(frame, request->bucketId()); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } @@ -273,39 +273,39 @@ void ProtocolAdmin::execute(RequestContextPtr context, SetPoliciesRequestPtr req "insertOrUpdate count [%zu], remove count [%zu]", request->sequenceNumber(), request->policiesToBeInsertedOrUpdated().size(), request->policiesToBeRemoved().size()); - ProtocolFramePtr frame = + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); - ProtocolSerialization::serialize(*frame, OpSetPolicies); + ProtocolSerialization::serialize(frame, OpSetPolicies); - ProtocolSerialization::serialize(*frame, + ProtocolSerialization::serialize(frame, static_cast(request->policiesToBeInsertedOrUpdated().size())); for (auto policyBucket : request->policiesToBeInsertedOrUpdated()) { - ProtocolSerialization::serialize(*frame, policyBucket.first); - ProtocolSerialization::serialize(*frame, + ProtocolSerialization::serialize(frame, policyBucket.first); + ProtocolSerialization::serialize(frame, static_cast(policyBucket.second.size())); for (auto policy : policyBucket.second) { // PolicyKey - ProtocolSerialization::serialize(*frame, policy.key().client().value()); - ProtocolSerialization::serialize(*frame, policy.key().user().value()); - ProtocolSerialization::serialize(*frame, policy.key().privilege().value()); + ProtocolSerialization::serialize(frame, policy.key().client().value()); + ProtocolSerialization::serialize(frame, policy.key().user().value()); + ProtocolSerialization::serialize(frame, policy.key().privilege().value()); // PolicyResult - ProtocolSerialization::serialize(*frame, policy.result().policyType()); - ProtocolSerialization::serialize(*frame, policy.result().metadata()); + ProtocolSerialization::serialize(frame, policy.result().policyType()); + ProtocolSerialization::serialize(frame, policy.result().metadata()); } } - ProtocolSerialization::serialize(*frame, + ProtocolSerialization::serialize(frame, static_cast(request->policiesToBeRemoved().size())); for (auto policyBucket : request->policiesToBeRemoved()) { - ProtocolSerialization::serialize(*frame, policyBucket.first); - ProtocolSerialization::serialize(*frame, + ProtocolSerialization::serialize(frame, policyBucket.first); + ProtocolSerialization::serialize(frame, static_cast(policyBucket.second.size())); for (auto policyKey : policyBucket.second) { // PolicyKey - ProtocolSerialization::serialize(*frame, policyKey.client().value()); - ProtocolSerialization::serialize(*frame, policyKey.user().value()); - ProtocolSerialization::serialize(*frame, policyKey.privilege().value()); + ProtocolSerialization::serialize(frame, policyKey.client().value()); + ProtocolSerialization::serialize(frame, policyKey.user().value()); + ProtocolSerialization::serialize(frame, policyKey.privilege().value()); } } @@ -318,12 +318,12 @@ void ProtocolAdmin::execute(RequestContextPtr context, CheckResponsePtr response response->sequenceNumber(), response->m_resultRef.policyType(), response->m_resultRef.metadata().c_str()); - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization( + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization( response->sequenceNumber()); - ProtocolSerialization::serialize(*frame, OpCheckPolicyResponse); - ProtocolSerialization::serialize(*frame, response->m_resultRef.policyType()); - ProtocolSerialization::serialize(*frame, response->m_resultRef.metadata()); + ProtocolSerialization::serialize(frame, OpCheckPolicyResponse); + ProtocolSerialization::serialize(frame, response->m_resultRef.policyType()); + ProtocolSerialization::serialize(frame, response->m_resultRef.metadata()); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } @@ -332,11 +332,11 @@ void ProtocolAdmin::execute(RequestContextPtr context, CodeResponsePtr response) LOGD("Serializing CodeResponse: op [%" PRIu8 "], sequenceNumber [%" PRIu16 "], " "code [%" PRIu16 "]", OpCodeResponse, response->sequenceNumber(), response->m_code); - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization( + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization( response->sequenceNumber()); - ProtocolSerialization::serialize(*frame, OpCodeResponse); - ProtocolSerialization::serialize(*frame, static_cast(response->m_code)); + ProtocolSerialization::serialize(frame, OpCodeResponse); + ProtocolSerialization::serialize(frame, static_cast(response->m_code)); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } diff --git a/src/common/protocol/ProtocolClient.cpp b/src/common/protocol/ProtocolClient.cpp index 94811dd..c643789 100644 --- a/src/common/protocol/ProtocolClient.cpp +++ b/src/common/protocol/ProtocolClient.cpp @@ -141,52 +141,52 @@ ResponsePtr ProtocolClient::extractResponseFromBuffer(BinaryQueue &bufferQueue) } void ProtocolClient::execute(RequestContextPtr context, CancelRequestPtr request) { - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); LOGD("Serializing CancelRequest op [%" PRIu8 "]", OpCancelRequest); - ProtocolSerialization::serialize(*frame, OpCancelRequest); + ProtocolSerialization::serialize(frame, OpCancelRequest); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } void ProtocolClient::execute(RequestContextPtr context, CheckRequestPtr request) { - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(request->sequenceNumber()); LOGD("Serializing CheckRequest: client <%s>, user <%s>, privilege <%s>", request->key().client().value().c_str(), request->key().user().value().c_str(), request->key().privilege().value().c_str()); - ProtocolSerialization::serialize(*frame, OpCheckPolicyRequest); - ProtocolSerialization::serialize(*frame, request->key().client().value()); - ProtocolSerialization::serialize(*frame, request->key().user().value()); - ProtocolSerialization::serialize(*frame, request->key().privilege().value()); + ProtocolSerialization::serialize(frame, OpCheckPolicyRequest); + ProtocolSerialization::serialize(frame, request->key().client().value()); + ProtocolSerialization::serialize(frame, request->key().user().value()); + ProtocolSerialization::serialize(frame, request->key().privilege().value()); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } void ProtocolClient::execute(RequestContextPtr context, CancelResponsePtr response) { - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization( + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization( response->sequenceNumber()); LOGD("Serializing CancelResponse: op [%" PRIu8 "]", OpCancelResponse); - ProtocolSerialization::serialize(*frame, OpCancelResponse); + ProtocolSerialization::serialize(frame, OpCancelResponse); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } void ProtocolClient::execute(RequestContextPtr context, CheckResponsePtr response) { - ProtocolFramePtr frame = ProtocolFrameSerializer::startSerialization( + ProtocolFrame frame = ProtocolFrameSerializer::startSerialization( response->sequenceNumber()); LOGD("Serializing CheckResponse: op [%" PRIu8 "], policyType [%" PRIu16 "], metadata <%s>", OpCheckPolicyResponse, response->m_resultRef.policyType(), response->m_resultRef.metadata().c_str()); - ProtocolSerialization::serialize(*frame, OpCheckPolicyResponse); - ProtocolSerialization::serialize(*frame, response->m_resultRef.policyType()); - ProtocolSerialization::serialize(*frame, response->m_resultRef.metadata()); + ProtocolSerialization::serialize(frame, OpCheckPolicyResponse); + ProtocolSerialization::serialize(frame, response->m_resultRef.policyType()); + ProtocolSerialization::serialize(frame, response->m_resultRef.metadata()); ProtocolFrameSerializer::finishSerialization(frame, context->responseQueue()); } diff --git a/src/common/protocol/ProtocolFrame.cpp b/src/common/protocol/ProtocolFrame.cpp index 560217e..736be30 100644 --- a/src/common/protocol/ProtocolFrame.cpp +++ b/src/common/protocol/ProtocolFrame.cpp @@ -28,7 +28,7 @@ namespace Cynara { -ProtocolFrame::ProtocolFrame(ProtocolFrameHeaderPtr frameHeader, BinaryQueuePtr data) : +ProtocolFrame::ProtocolFrame(ProtocolFrameHeader frameHeader, BinaryQueuePtr data) : m_frameHeader(frameHeader), m_frameBodyContent(data) { } @@ -38,7 +38,7 @@ void ProtocolFrame::read(size_t num, void *bytes) { void ProtocolFrame::write(size_t num, const void *bytes) { m_frameBodyContent->appendCopy(bytes, num); - m_frameHeader->increaseFrameLength(num); + m_frameHeader.increaseFrameLength(num); } } /* namespace Cynara */ diff --git a/src/common/protocol/ProtocolFrame.h b/src/common/protocol/ProtocolFrame.h index 3d85cc0..73f22e9 100644 --- a/src/common/protocol/ProtocolFrame.h +++ b/src/common/protocol/ProtocolFrame.h @@ -39,10 +39,10 @@ class ProtocolFrameSerializer; class ProtocolFrame: public IStream { public: - ProtocolFrame(ProtocolFrameHeaderPtr frameHeader, BinaryQueuePtr headerContent); + ProtocolFrame(ProtocolFrameHeader frameHeader, BinaryQueuePtr headerContent); virtual ~ProtocolFrame() {}; - ProtocolFrameHeaderPtr frameHeader(void) { + ProtocolFrameHeader &frameHeader(void) { return m_frameHeader; } @@ -50,7 +50,7 @@ public: virtual void write(size_t num, const void *bytes); private: - ProtocolFrameHeaderPtr m_frameHeader; + ProtocolFrameHeader m_frameHeader; BinaryQueuePtr m_frameBodyContent; BinaryQueue &bodyContent(void) { @@ -60,8 +60,6 @@ private: friend class ProtocolFrameSerializer; }; -typedef std::shared_ptr ProtocolFramePtr; - } /* namespace Cynara */ #endif /* SRC_COMMON_PROTOCOL_PROTOCOLFRAME_H_ */ diff --git a/src/common/protocol/ProtocolFrameHeader.h b/src/common/protocol/ProtocolFrameHeader.h index ff92bf9..7b85465 100644 --- a/src/common/protocol/ProtocolFrameHeader.h +++ b/src/common/protocol/ProtocolFrameHeader.h @@ -107,8 +107,6 @@ private: friend class ProtocolFrameSerializer; }; -typedef std::shared_ptr ProtocolFrameHeaderPtr; - } /* namespace Cynara */ #endif /* SRC_COMMON_PROTOCOL_PROTOCOLFRAMEHEADER_H_ */ diff --git a/src/common/protocol/ProtocolFrameSerializer.cpp b/src/common/protocol/ProtocolFrameSerializer.cpp index 88a07d3..0599f02 100644 --- a/src/common/protocol/ProtocolFrameSerializer.cpp +++ b/src/common/protocol/ProtocolFrameSerializer.cpp @@ -65,19 +65,19 @@ void ProtocolFrameSerializer::deserializeHeader(ProtocolFrameHeader &frameHeader } } -ProtocolFramePtr ProtocolFrameSerializer::startSerialization(ProtocolFrameSequenceNumber sequenceNumber) { +ProtocolFrame ProtocolFrameSerializer::startSerialization(ProtocolFrameSequenceNumber sequenceNumber) { LOGD("Serialization started"); BinaryQueuePtr headerQueue = std::make_shared(); BinaryQueuePtr bodyQueue = std::make_shared(); - ProtocolFrameHeaderPtr header = std::make_shared(headerQueue); - header->setSequenceNumber(sequenceNumber); - header->increaseFrameLength(ProtocolFrameHeader::frameHeaderLength()); - return std::make_shared(header, bodyQueue); + ProtocolFrameHeader header(headerQueue); + header.setSequenceNumber(sequenceNumber); + header.increaseFrameLength(ProtocolFrameHeader::frameHeaderLength()); + return ProtocolFrame(header, bodyQueue); } -void ProtocolFrameSerializer::finishSerialization(ProtocolFramePtr frame, BinaryQueue &data) { - ProtocolFrameHeader &frameHeader = *frame->frameHeader(); +void ProtocolFrameSerializer::finishSerialization(ProtocolFrame &frame, BinaryQueue &data) { + ProtocolFrameHeader &frameHeader = frame.frameHeader(); ProtocolSerialization::serializeNoSize(frameHeader, ProtocolFrameHeader::m_signature); ProtocolSerialization::serialize(frameHeader, frameHeader.m_frameLength); ProtocolSerialization::serialize(frameHeader, frameHeader.m_sequenceNumber); @@ -87,7 +87,7 @@ void ProtocolFrameSerializer::finishSerialization(ProtocolFramePtr frame, Binary (int)frameHeader.m_sequenceNumber); data.appendMoveFrom(frameHeader.headerContent()); - data.appendMoveFrom(frame->bodyContent()); + data.appendMoveFrom(frame.bodyContent()); } } /* namespace Cynara */ diff --git a/src/common/protocol/ProtocolFrameSerializer.h b/src/common/protocol/ProtocolFrameSerializer.h index 8961e14..f2e201e 100644 --- a/src/common/protocol/ProtocolFrameSerializer.h +++ b/src/common/protocol/ProtocolFrameSerializer.h @@ -34,8 +34,8 @@ class ProtocolFrameSerializer { public: static void deserializeHeader(ProtocolFrameHeader &frameHeader, BinaryQueue &data); - static ProtocolFramePtr startSerialization(ProtocolFrameSequenceNumber sequenceNumber); - static void finishSerialization(ProtocolFramePtr frame, BinaryQueue &data); + static ProtocolFrame startSerialization(ProtocolFrameSequenceNumber sequenceNumber); + static void finishSerialization(ProtocolFrame &frame, BinaryQueue &data); }; } /* namespace Cynara */ -- 2.7.4