From baa776b7fb4066a2ee540d87ff8d76a6f443f7bc Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Wed, 18 Feb 2015 13:23:54 +0100 Subject: [PATCH 01/16] Add tests for ChecksumGenerator (chsgen) This patch adds tests for calling "cynara-db-chsgen" executable. Following call scenarios are checked: * no filename, * create checksum records for primary database contents, * create checksum records for backup database contents. Change-Id: I398f6865610598ab558b22a9cf3cdc620dc057b8 --- test/CMakeLists.txt | 6 +- test/chsgen/ChsgenCommandlineTest.h | 31 +++++++++ test/chsgen/checksumgenerator.cpp | 121 ++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 test/chsgen/ChsgenCommandlineTest.h create mode 100644 test/chsgen/checksumgenerator.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fe62294..450993f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,6 +49,7 @@ SET(CYNARA_SOURCES_FOR_TESTS ${CYNARA_SRC}/common/types/PolicyDescription.cpp ${CYNARA_SRC}/common/types/PolicyResult.cpp ${CYNARA_SRC}/common/types/PolicyType.cpp + ${CYNARA_SRC}/chsgen/ChecksumGenerator.cpp ${CYNARA_SRC}/cyad/AdminPolicyParser.cpp ${CYNARA_SRC}/cyad/CommandlineParser/CmdlineErrors.cpp ${CYNARA_SRC}/cyad/CommandlineParser/CmdlineOpts.cpp @@ -71,6 +72,7 @@ SET(CYNARA_SOURCES_FOR_TESTS SET(CYNARA_TESTS_SOURCES TestEventListenerProxy.cpp + chsgen/checksumgenerator.cpp common/exceptions/bucketrecordcorrupted.cpp common/protocols/admin/admincheckrequest.cpp common/protocols/admin/admincheckresponse.cpp @@ -121,6 +123,8 @@ ADD_EXECUTABLE(${TARGET_CYNARA_TESTS} ) TARGET_LINK_LIBRARIES(${TARGET_CYNARA_TESTS} - ${PKGS_LDFLAGS} ${PKGS_LIBRARIES} + ${PKGS_LDFLAGS} + ${PKGS_LIBRARIES} + crypt ) INSTALL(TARGETS ${TARGET_CYNARA_TESTS} DESTINATION ${BIN_INSTALL_DIR}) diff --git a/test/chsgen/ChsgenCommandlineTest.h b/test/chsgen/ChsgenCommandlineTest.h new file mode 100644 index 0000000..cbca399 --- /dev/null +++ b/test/chsgen/ChsgenCommandlineTest.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 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/chsgen/ChsgenCommandlineTest.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief Fixture for ChecksumGenerator tests + */ + +#ifndef TEST_CHSGEN_CHSGENCOMMANDLINETEST_H_ +#define TEST_CHSGEN_CHSGENCOMMANDLINETEST_H_ + +#include + +class ChsgenCommandlineTest : public QuietCommandlineTest { +}; + +#endif /* TEST_CHSGEN_CHSGENCOMMANDLINETEST_H_ */ diff --git a/test/chsgen/checksumgenerator.cpp b/test/chsgen/checksumgenerator.cpp new file mode 100644 index 0000000..1d390d6 --- /dev/null +++ b/test/chsgen/checksumgenerator.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015 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/chsgen/checksumgenerator.cpp + * @author Pawel Wieczorek + * @version 1.0 + * @brief Tests of ChecksumGenerator + */ + +#include + +#include + +#include + +#include + +#include "ChsgenCommandlineTest.h" + +namespace { + +const std::string execName("./cynara-db-chsgen"); +const std::string backupFilenameSuffix(Cynara::PathConfig::StoragePath::backupFilenameSuffix); +const char fieldSeparator(Cynara::PathConfig::StoragePath::fieldSeparator); + +} // namespace + +/** + * @brief Verify if passing no filenames to checksum generator returns error message + * @test Expected result: + * - CYNARA_API_INVALID_COMMANDLINE_PARAM returned from checksum generator + * - empty output stream + * - message from caught exception in error stream + */ +TEST_F(ChsgenCommandlineTest, noFile) { + using ::testing::StartsWith; + + std::string err; + std::string out; + + clearOutput(); + prepare_argv({ execName }); + + Cynara::ChecksumGenerator chsgen(this->argc(), this->argv()); + const auto ret = chsgen.run(); + getOutput(out, err); + + ASSERT_EQ(CYNARA_API_INVALID_COMMANDLINE_PARAM, ret); + ASSERT_TRUE(out.empty()); + ASSERT_THAT(err, StartsWith("File " + std::string() + " not found or corrupted badly")); +} + +/** + * @brief Verify if checksum generator returns valid records + * @test Expected result: + * - CYNARA_API_SUCCESS returned from checksum generator + * - valid record in output stream + * - empty error stream + */ +TEST_F(ChsgenCommandlineTest, recordGeneration) { + using ::testing::StartsWith; + + std::string err; + std::string out; + + for (const std::string &file : { "_", "buckets" }) { + clearOutput(); + prepare_argv({ execName, std::string(CYNARA_TESTS_DIR) + "/db3/" + file }); + SCOPED_TRACE(file); + + Cynara::ChecksumGenerator chsgen(this->argc(), this->argv()); + const auto ret = chsgen.run(); + getOutput(out, err); + + ASSERT_EQ(CYNARA_API_SUCCESS, ret); + ASSERT_THAT(out, StartsWith(file + fieldSeparator + "$1$")); + ASSERT_TRUE(err.empty()); + } +} + +/** + * @brief Verify if checksum generator returns valid records for backup files + * @test Expected result: + * - CYNARA_API_SUCCESS returned from checksum generator + * - valid record in output stream + * - empty error stream + */ +TEST_F(ChsgenCommandlineTest, suffixErasing) { + using ::testing::StartsWith; + + std::string err; + std::string out; + + for (const std::string &file : { "_", "_additional", "buckets" }) { + clearOutput(); + prepare_argv({ execName, + std::string(CYNARA_TESTS_DIR) + "/db6/" + file + backupFilenameSuffix }); + SCOPED_TRACE(file); + + Cynara::ChecksumGenerator chsgen(this->argc(), this->argv()); + const auto ret = chsgen.run(); + getOutput(out, err); + + ASSERT_EQ(CYNARA_API_SUCCESS, ret); + ASSERT_THAT(out, StartsWith(file + fieldSeparator + "$1$")); + ASSERT_TRUE(err.empty()); + } +} -- 2.7.4 From 0cbbe1c7b7e7ffce65d5edc3dcca0c6055109cf0 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Wed, 11 Feb 2015 16:00:12 +0100 Subject: [PATCH 02/16] Refactorize migration tool This refactoring run deals with: * unintuitive naming, * late input validation, * passing only parts of input during option parsing, * unconsistent comments. Additionally, this patch: * adds possiblity to install database in a custom directory, * removes possibility of removing whole Cynara's state directory during deinstallation - now only database is removed by migration tool. Change-Id: I59e429d93812861ca9c32f201ed0056f753cbc25 --- migration/cynara-db-migration | 55 ++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/migration/cynara-db-migration b/migration/cynara-db-migration index 5ab6409..46c6df0 100644 --- a/migration/cynara-db-migration +++ b/migration/cynara-db-migration @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +# Copyright (c) 2014-2015 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. @@ -24,7 +24,7 @@ STATE_PATH='/var/cynara' DB_DIR='db' INDEX_NAME='buckets' -DEFAULT_NAME='_' +DEFAULT_BUCKET_NAME='_' DENY_POLICY=';0x0;' ##### Variables, with default values (optional) @@ -40,22 +40,25 @@ NEW_VERSION= ##### Functions parse_opts() { - while getopts ":f:t:u:g:l:" opt; do + while getopts ":f:t:p:u:g:l:" opt; do case $opt in f ) - OLD_VERSION=${OPTARG} + OLD_VERSION="${OPTARG}" ;; t ) - NEW_VERSION=${OPTARG} + NEW_VERSION="${OPTARG}" + ;; + p ) + STATE_PATH="${OPTARG}" ;; u ) - CYNARA_USER=${OPTARG} + CYNARA_USER="${OPTARG}" ;; g ) - CYNARA_GROUP=${OPTARG} + CYNARA_GROUP="${OPTARG}" ;; l ) - SMACK_LABEL=${OPTARG} + SMACK_LABEL="${OPTARG}" ;; \? ) echo "Invalid option: -$OPTARG" >&2 @@ -69,17 +72,13 @@ parse_opts() { done } -create_db() { - if [ -z ${NEW_VERSION} ]; then - failure - fi - - # Create Cynara's database directory: +minimal_db() { + # 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} + touch ${STATE_PATH}/${DB_DIR}/${DEFAULT_BUCKET_NAME} # Set proper permissions for newly created database chown -R ${CYNARA_USER}:${CYNARA_GROUP} ${STATE_PATH}/${DB_DIR} @@ -89,6 +88,14 @@ create_db() { chsmack -a ${SMACK_LABEL} ${STATE_PATH}/${DB_DIR}/* } +install_db() { + if [ -z ${NEW_VERSION} ] ; then + failure + fi + + minimal_db +} + migrate_db() { if [ -z ${OLD_VERSION} -o -z ${NEW_VERSION} ]; then failure @@ -96,7 +103,8 @@ migrate_db() { # Create minimal database if there was none: if [ ! -d "${STATE_PATH}/${DB_DIR}" ]; then - create_db + install_db + exit_success fi } @@ -105,7 +113,7 @@ remove_db() { failure fi - rm -rf ${STATE_PATH} + rm -rf "${STATE_PATH}/${DB_DIR}" } usage() { @@ -115,13 +123,14 @@ 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 + upgrade (up) migrate data to different version of database 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) + -p path Set path for storing database (default: /var/cynara) -u user Set database owner (default: cynara) -g group Set database group (default: cynara) -l label Set SMACK label for database (default: System) @@ -131,9 +140,17 @@ EOF failure() { usage + exit_failure +} + +exit_failure() { exit 1 } +exit_success() { + exit 0 +} + ##### Main if [ 0 -eq $# ]; then @@ -149,7 +166,7 @@ case $1 in "in" | "install" ) shift $OPTIND parse_opts "$@" - create_db + install_db ;; "rm" | "uninstall" ) shift $OPTIND -- 2.7.4 From 3cdfeec14c8db2a11721aa7b71c2d689289c390a Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Mon, 9 Feb 2015 15:37:57 +0100 Subject: [PATCH 03/16] Add comparator for Semantic Versioning Change-Id: I73b273465995b6246d20179395bdff1b4d1941f2 --- migration/cynara-db-migration | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/migration/cynara-db-migration b/migration/cynara-db-migration index 46c6df0..645d831 100644 --- a/migration/cynara-db-migration +++ b/migration/cynara-db-migration @@ -27,6 +27,11 @@ INDEX_NAME='buckets' DEFAULT_BUCKET_NAME='_' DENY_POLICY=';0x0;' +# Return values for comparison +LESS=-1 +EQUAL=0 +GREATER=1 + ##### Variables, with default values (optional) CYNARA_USER='cynara' @@ -39,6 +44,47 @@ NEW_VERSION= ##### Functions +version_compare() { + local MAJOR1= + local MAJOR2= + local MINOR1= + local MINOR2= + local PATCH1= + local PATCH2= + + # Parse + i=1 + while [ 2 -ge $i ] ; do + eval "ARG=\$$i" + eval "MAJOR$i=${ARG%%.*}" + eval "TMP=${ARG#*.}" + eval "MINOR$i=${TMP%%.*}" + eval "PATCH$i=${ARG##*.}" + i=$(($i+1)) + done + + # Compare + if [ $MAJOR1 -lt $MAJOR2 ] ; then + echo "$LESS" + elif [ $MAJOR1 -eq $MAJOR2 ] ; then + if [ $MINOR1 -lt $MINOR2 ] ; then + echo "$LESS" + elif [ $MINOR1 -eq $MINOR2 ] ; then + if [ $PATCH1 -lt $PATCH2 ] ; then + echo "$LESS" + elif [ $PATCH1 -eq $PATCH2 ] ; then + echo "$EQUAL" + else + echo "$GREATER" + fi + else + echo "$GREATER" + fi + else + echo "$GREATER" + fi +} + parse_opts() { while getopts ":f:t:p:u:g:l:" opt; do case $opt in -- 2.7.4 From 0bb80318f3edc278902bc5ff54c497ac1882be57 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Fri, 16 Jan 2015 11:16:56 +0100 Subject: [PATCH 04/16] Add support for checksums to migration tool This patch also adds downgrade option to the migration tool. Change-Id: If6a443172d52a78e8bfbf732e7eca38cb37f886f --- migration/cynara-db-migration | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/migration/cynara-db-migration b/migration/cynara-db-migration index 645d831..d31897c 100644 --- a/migration/cynara-db-migration +++ b/migration/cynara-db-migration @@ -25,6 +25,8 @@ STATE_PATH='/var/cynara' DB_DIR='db' INDEX_NAME='buckets' DEFAULT_BUCKET_NAME='_' +CHECKSUM_NAME='checksum' +GUARD_NAME='guard' DENY_POLICY=';0x0;' # Return values for comparison @@ -32,6 +34,9 @@ LESS=-1 EQUAL=0 GREATER=1 +# Cynara version which introduced checksums +CHS_INTRO_VERSION='0.6.0' + ##### Variables, with default values (optional) CYNARA_USER='cynara' @@ -118,6 +123,41 @@ parse_opts() { done } +generate_checksums() { + # Output file + CHECKSUMS="${STATE_PATH}/${DB_DIR}/${CHECKSUM_NAME}" + GUARD="${STATE_PATH}/${DB_DIR}/${GUARD_NAME}" + WILDCARD="*" + + # Check if checksums should be generated from backup + if [ -e $GUARD ] ; then + WILDCARD="${WILDCARD}~" + + # Database will be read from backup at next load + CHECKSUMS="${CHECKSUMS}~" + fi + + # Mimic opening in truncate mode + echo -n "" > "${CHECKSUMS}" + + # Actual checksums generation + for FILE in $(find ${STATE_PATH}/${DB_DIR}/${WILDCARD} -type f ! -name "${CHECKSUM_NAME}*" \ + ! -name "${GUARD_NAME}"); do + CHECKSUM="$(/usr/sbin/cynara-db-chsgen ${FILE})" + if [ 0 -eq $? ] ; then + echo "${CHECKSUM}" >> ${CHECKSUMS} + else + exit_failure + fi + done + + # Set proper permissions for newly created checksum file + chown -R ${CYNARA_USER}:${CYNARA_GROUP} ${CHECKSUMS} + + # Set proper SMACK label for newly created checksum file + chsmack -a ${SMACK_LABEL} ${CHECKSUMS} +} + minimal_db() { # Create Cynara's database directory mkdir -p ${STATE_PATH}/${DB_DIR} @@ -134,12 +174,27 @@ minimal_db() { chsmack -a ${SMACK_LABEL} ${STATE_PATH}/${DB_DIR}/* } +upgrade_db() { + # Add or update checksum file if necessary + if [ 0 -ge $(version_compare ${CHS_INTRO_VERSION} ${NEW_VERSION}) ] ; then + generate_checksums + fi +} + +downgrade_db() { + # Remove checksum file if necessary + if [ 0 -lt $(version_compare ${CHS_INTRO_VERSION} ${NEW_VERSION}) ] ; then + rm "${STATE_PATH}/${DB_DIR}/${CHECKSUM_NAME}" > /dev/null 2>&1 + fi +} + install_db() { if [ -z ${NEW_VERSION} ] ; then failure fi minimal_db + upgrade_db } migrate_db() { @@ -152,6 +207,21 @@ migrate_db() { install_db exit_success fi + + case $(version_compare ${OLD_VERSION} ${NEW_VERSION}) in + -1 ) + upgrade_db + ;; + 0 ) + : + # Same version given twice; take no action + ;; + 1 ) + downgrade_db + ;; + * ) + failure + esac } remove_db() { -- 2.7.4 From df2737b7eb40da782e0e1394284866331d54fe6a Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Tue, 24 Feb 2015 15:58:42 +0100 Subject: [PATCH 05/16] Add tests for migration tool Change-Id: I2bdd88cd07646896b75ea36e7776b7fa1a449bd8 --- packaging/cynara.spec | 1 + test/CMakeLists.txt | 4 + test/db/db10_postchs_bcp/_~ | 0 test/db/db10_postchs_bcp/buckets~ | 1 + test/db/db10_postchs_bcp/checksum~ | 2 + test/db/db10_postchs_bcp/guard | 0 test/db/db7_prechs/_ | 0 test/db/db7_prechs/buckets | 1 + test/db/db8_postchs/_ | 0 test/db/db8_postchs/buckets | 1 + test/db/db8_postchs/checksum | 2 + test/db/db9_prechs_bcp/_~ | 0 test/db/db9_prechs_bcp/buckets~ | 1 + test/db/db9_prechs_bcp/guard | 0 test/tools/cynara-db-migration-tests | 218 +++++++++++++++++++++++++++++++++++ 15 files changed, 231 insertions(+) create mode 100644 test/db/db10_postchs_bcp/_~ create mode 100644 test/db/db10_postchs_bcp/buckets~ create mode 100644 test/db/db10_postchs_bcp/checksum~ create mode 100644 test/db/db10_postchs_bcp/guard create mode 100644 test/db/db7_prechs/_ create mode 100644 test/db/db7_prechs/buckets create mode 100644 test/db/db8_postchs/_ create mode 100644 test/db/db8_postchs/buckets create mode 100644 test/db/db8_postchs/checksum create mode 100644 test/db/db9_prechs_bcp/_~ create mode 100644 test/db/db9_prechs_bcp/buckets~ create mode 100644 test/db/db9_prechs_bcp/guard create mode 100644 test/tools/cynara-db-migration-tests diff --git a/packaging/cynara.spec b/packaging/cynara.spec index 48d939a..3289855 100644 --- a/packaging/cynara.spec +++ b/packaging/cynara.spec @@ -337,6 +337,7 @@ fi %files -n cynara-tests %manifest cynara-tests.manifest %attr(755,root,root) /usr/bin/cynara-tests +%attr(755,root,root) /usr/bin/cynara-db-migration-tests %attr(755,root,root) %{tests_dir}/db*/* %dir %attr(755,root,root) %{tests_dir}/empty_db diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 450993f..d937d3c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -107,6 +107,8 @@ SET(CYNARA_TESTS_SOURCES types/policykey.cpp ) +SET(CYNARA_MIGRATION_TOOL_TESTS tools/cynara-db-migration-tests) + INCLUDE_DIRECTORIES( ${PKGS_INCLUDE_DIRS} ${CYNARA_SRC}/common @@ -128,3 +130,5 @@ TARGET_LINK_LIBRARIES(${TARGET_CYNARA_TESTS} crypt ) INSTALL(TARGETS ${TARGET_CYNARA_TESTS} DESTINATION ${BIN_INSTALL_DIR}) + +INSTALL(FILES ${CYNARA_MIGRATION_TOOL_TESTS} DESTINATION ${BIN_INSTALL_DIR}) diff --git a/test/db/db10_postchs_bcp/_~ b/test/db/db10_postchs_bcp/_~ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db10_postchs_bcp/buckets~ b/test/db/db10_postchs_bcp/buckets~ new file mode 100644 index 0000000..29ab987 --- /dev/null +++ b/test/db/db10_postchs_bcp/buckets~ @@ -0,0 +1 @@ +;0x0; diff --git a/test/db/db10_postchs_bcp/checksum~ b/test/db/db10_postchs_bcp/checksum~ new file mode 100644 index 0000000..63b55e0 --- /dev/null +++ b/test/db/db10_postchs_bcp/checksum~ @@ -0,0 +1,2 @@ +_;$1$$qRPK7m23GJusamGpoGLby/ +buckets;$1$$6ZlVs5lw2nZgVmiw0BdY21 diff --git a/test/db/db10_postchs_bcp/guard b/test/db/db10_postchs_bcp/guard new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db7_prechs/_ b/test/db/db7_prechs/_ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db7_prechs/buckets b/test/db/db7_prechs/buckets new file mode 100644 index 0000000..29ab987 --- /dev/null +++ b/test/db/db7_prechs/buckets @@ -0,0 +1 @@ +;0x0; diff --git a/test/db/db8_postchs/_ b/test/db/db8_postchs/_ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db8_postchs/buckets b/test/db/db8_postchs/buckets new file mode 100644 index 0000000..29ab987 --- /dev/null +++ b/test/db/db8_postchs/buckets @@ -0,0 +1 @@ +;0x0; diff --git a/test/db/db8_postchs/checksum b/test/db/db8_postchs/checksum new file mode 100644 index 0000000..63b55e0 --- /dev/null +++ b/test/db/db8_postchs/checksum @@ -0,0 +1,2 @@ +_;$1$$qRPK7m23GJusamGpoGLby/ +buckets;$1$$6ZlVs5lw2nZgVmiw0BdY21 diff --git a/test/db/db9_prechs_bcp/_~ b/test/db/db9_prechs_bcp/_~ new file mode 100644 index 0000000..e69de29 diff --git a/test/db/db9_prechs_bcp/buckets~ b/test/db/db9_prechs_bcp/buckets~ new file mode 100644 index 0000000..29ab987 --- /dev/null +++ b/test/db/db9_prechs_bcp/buckets~ @@ -0,0 +1 @@ +;0x0; diff --git a/test/db/db9_prechs_bcp/guard b/test/db/db9_prechs_bcp/guard new file mode 100644 index 0000000..e69de29 diff --git a/test/tools/cynara-db-migration-tests b/test/tools/cynara-db-migration-tests new file mode 100644 index 0000000..c6ea3d8 --- /dev/null +++ b/test/tools/cynara-db-migration-tests @@ -0,0 +1,218 @@ +#!/bin/sh +# +# Copyright (c) 2015 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/tools/cynara-db-migration-tests +# @author Pawel Wieczorek +# @brief Tests of migration tool for Cynara's database +# + +##### Constants (these must not be modified by shell) + +# Paths +TESTS_DIR='/tmp/cynara-db-migration-tests' +PATTERNS_DIR='/usr/share/cynara/tests' +MIGRATE='/usr/sbin/cynara-db-migration' + +# Names +DB_DIR='db' +LOG_FILE='output.log' +FAIL_FILE='fail.log' + +# Various Cynara versions +CHS_INTRO_VERSION='0.6.0' +PRECHS_HIGH_VERSION='0.4.2' +PRECHS_LOW_VERSION='0.2.4' +POSTCHS_HIGH_VERSION='4.2.0' +POSTCHS_LOW_VERSION='2.4.0' + +# Messages +MIGRATE_FAIL_MSG="$MIGRATE failed." +COMPARE_FAIL_MSG="Mismatch in file" +DB_STILL_EXISTS_MSG="Database directory still exists." + +# Status indicators +SUCCESS=0 +FAILURE=1 + +##### Variables, with default values (optional) + +TEST_CASE=0 + +##### Functions + +fail_msg() { + local ID="$1" + local NAME="$2" + echo "Test $ID ($NAME) failed:" + cat "${TESTS_DIR}/${ID}/${FAIL_FILE}" +} + +success_msg() { + local ID="$1" + local NAME="$2" + echo "Test $ID ($NAME) passed." +} + +execute() { + ${MIGRATE} $1 + echo $? +} + +run() { + # Iterate test case ID + TEST_CASE=$(($TEST_CASE+1)) + + # Prepare environment + local ID=$TEST_CASE + local INITIAL="$1" + local PATTERN="$2" + local NAME="$3" + local STATE_PATH="${TESTS_DIR}/${ID}" + + mkdir -p "$STATE_PATH" + if [ 'empty' != "$INITIAL" ] ; then + cp -a "${PATTERNS_DIR}/${INITIAL}" "${STATE_PATH}/${DB_DIR}" + fi + + # Prepare error handling + local RUN_SUCCESS=$SUCCESS + local FAIL_REASON="${STATE_PATH}/${FAIL_FILE}" + echo -n "" > "$FAIL_REASON" + + # Run command + local ARG="$4 -p $STATE_PATH" + local RET=$(execute "$ARG") + + # Check results + if [ $SUCCESS -ne $RET ] ; then + # Migration tool failure + echo "$MIGRATE_FAIL_MSG" > "$FAIL_REASON" + else + # Clear logfile + local LOG="${STATE_PATH}/${LOG_FILE}" + echo -n "" > "$LOG" + + if [ 'empty' = "$PATTERN" ] ; then + # No pattern - check if database directory was removed + if [ -d "${STATE_PATH}/${DB_DIR}" ] ; then + RUN_SUCCESS=$FAILURE + echo "" >> "$FAIL_REASON" + echo "$DB_STILL_EXISTS_MSG" >> "$FAIL_REASON" + fi + else + # Pattern given - compare it to generated database + for FILE in ${PATTERNS_DIR}/${PATTERN}/* ; do + diff ${FILE} ${STATE_PATH}/${DB_DIR}/${FILE##*\/} > "$LOG" 2>&1 + if [ $SUCCESS -ne $? ] ; then + RUN_SUCCESS=$FAILURE + # Append comparison result to fail log + echo "" >> "$FAIL_REASON" + echo "${COMPARE_FAIL_MSG}: ${FILE##*\/}" >> "$FAIL_REASON" + cat "$LOG" >> "$FAIL_REASON" + fi + done + fi + fi + + # Return results + if [ $SUCCESS -eq $RUN_SUCCESS ] ; then + success_msg "$ID" "$NAME" + else + fail_msg "$ID" "$NAME" + fi + + # Clean up environment + rm -r "$STATE_PATH" +} + +##### Main + +# How to add new test cases: +# +# run INITIAL_DB PATTERN_DB TEST_CASE_NAME MIGRATION_ARGS +# +# checking if database was removed: pass 'empty' as PATTERN_DB + +### Set up tests environment +mkdir -p "$TESTS_DIR" + +# Test case 01: install minimal pre-checksum database +run empty db7_prechs "inst_min_prechs" "install -t $PRECHS_LOW_VERSION" + +# Test case 02: install minimal post-checksum database +run empty db8_postchs "inst_min_postchs" "install -t $POSTCHS_LOW_VERSION" + +# Test case 03: upgrade from pre-checksum to pre-checksum +run db7_prechs db7_prechs "up_prechs_prechs" \ + "upgrade -f $PRECHS_LOW_VERSION -t $PRECHS_HIGH_VERSION" + +# Test case 04: upgrade from pre-checksum to post-checksum +run db7_prechs db8_postchs "up_prechs_postchs" \ + "upgrade -f $PRECHS_LOW_VERSION -t $POSTCHS_HIGH_VERSION" + +# Test case 05: upgrade from post-checksum to post-checksum +run db8_postchs db8_postchs "up_postchs_postchs" \ + "upgrade -f $POSTCHS_LOW_VERSION -t $POSTCHS_HIGH_VERSION" + +# Test case 06: downgrade from pre-checksum to pre-checksum +run db7_prechs db7_prechs "down_prechs_prechs" \ + "upgrade -f $PRECHS_HIGH_VERSION -t $PRECHS_LOW_VERSION" + +# Test case 07: downgrade from post-checksum to pre-checksum +run db8_postchs db7_prechs "down_postchs_prechs" \ + "upgrade -f $POSTCHS_HIGH_VERSION -t $PRECHS_LOW_VERSION" + +# Test case 08: downgrade from post-checksum to post-checksum +run db8_postchs db8_postchs "down_postchs_postchs" \ + "upgrade -f $POSTCHS_HIGH_VERSION -t $POSTCHS_LOW_VERSION" + +# Test case 09: migrate to the same database version +run db8_postchs db8_postchs "migr_same_ver" \ + "upgrade -f $POSTCHS_HIGH_VERSION -t $POSTCHS_HIGH_VERSION" + +# Test case 10: uninstall database +run db7_prechs empty "uninst_db" "uninstall -f $PRECHS_LOW_VERSION" + +# Test case 11: upgrade from pre-checksum to pre-checksum (backups) +run db9_prechs_bcp db9_prechs_bcp "up_prechs_prechs_bcp" \ + "upgrade -f $PRECHS_LOW_VERSION -t $PRECHS_HIGH_VERSION" + +# Test case 12: upgrade from pre-checksum to post-checksum (backups) +run db9_prechs_bcp db10_postchs_bcp "up_prechs_postchs_bcp" \ + "upgrade -f $PRECHS_LOW_VERSION -t $POSTCHS_HIGH_VERSION" + +# Test case 13: upgrade from post-checksum to post-checksum (backups) +run db10_postchs_bcp db10_postchs_bcp "up_postchs_postchs_bcp" \ + "upgrade -f $POSTCHS_LOW_VERSION -t $POSTCHS_HIGH_VERSION" + +# Test case 14: downgrade from backup pre-checksum to pre-checksum (backups) +run db9_prechs_bcp db9_prechs_bcp "down_prechs_prechs_bcp" \ + "upgrade -f $PRECHS_HIGH_VERSION -t $PRECHS_LOW_VERSION" + +# Test case 15: downgrade from backup post-checksum to pre-checksum (backups) +run db10_postchs_bcp db9_prechs_bcp "down_postchs_prechs_bcp" \ + "upgrade -f $POSTCHS_HIGH_VERSION -t $PRECHS_LOW_VERSION" + +# Test case 16: downgrade from backup post-checksum to post-checksum (backups) +run db10_postchs_bcp db10_postchs_bcp "down_postchs_postchs_bcp" \ + "upgrade -f $POSTCHS_HIGH_VERSION -t $POSTCHS_LOW_VERSION" + +# Test case 17: migrate to the same database version (backups) +run db10_postchs_bcp db10_postchs_bcp "migr_same_ver_bcp" \ + "upgrade -f $POSTCHS_HIGH_VERSION -t $POSTCHS_HIGH_VERSION" + +### Clean up tests environment +rm -r "$TESTS_DIR" -- 2.7.4 From 2f2d8f10c0b96524c6380b18a89845016632a53b Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Fri, 5 Dec 2014 15:26:24 +0100 Subject: [PATCH 06/16] Extend ignored files list in integrity mechanism Integrity mechanism will not remove file containing checksums even though it is not listed in database index. Change-Id: I1e587ecdad5abff47d78362394cc0ecdb1ecd4c4 --- src/storage/Integrity.cpp | 14 +++++++++++--- src/storage/Integrity.h | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/storage/Integrity.cpp b/src/storage/Integrity.cpp index 82e8f5a..5dfa624 100644 --- a/src/storage/Integrity.cpp +++ b/src/storage/Integrity.cpp @@ -109,9 +109,8 @@ void Integrity::deleteNonIndexedFiles(BucketPresenceTester tester) { 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 || - PathConfig::StoragePath::indexFilename == filename) { + //ignore all special files (working dir, parent dir, index, checksums) + if (isSpecialDirectory(filename) || isSpecialDatabaseEntry(filename)) { continue; } @@ -236,4 +235,13 @@ void Integrity::deleteHardLink(const std::string &filename) { } } +bool Integrity::isSpecialDirectory(const std::string &filename) { + return "." == filename || ".." == filename; +} + +bool Integrity::isSpecialDatabaseEntry(const std::string &filename) { + return PathConfig::StoragePath::indexFilename == filename || + PathConfig::StoragePath::checksumFilename == filename; +} + } /* namespace Cynara */ diff --git a/src/storage/Integrity.h b/src/storage/Integrity.h index d06eada..2917fb3 100644 --- a/src/storage/Integrity.h +++ b/src/storage/Integrity.h @@ -58,6 +58,9 @@ protected: static void createHardLink(const std::string &oldName, const std::string &newName); static void deleteHardLink(const std::string &filename); + static bool isSpecialDirectory(const std::string &filename); + static bool isSpecialDatabaseEntry(const std::string &filename); + private: const std::string m_dbPath; static const std::string m_indexFilename; -- 2.7.4 From 501306b6a09f74a644e620ebcdef42a3cbc3c10b Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Fri, 27 Feb 2015 10:50:52 +0100 Subject: [PATCH 07/16] Remove unwanted pointers (InMemoryStorageBackend) Using pointers to helper classes (Integrity and, in future, ChecksumValidator) is unnecessary and undesired. However, this forces removal of InMemoryStorageBackend default constructor, as it cannot initialize its helpers with valid arguments. This patch also adjusts tests to the new constructor set and removes no longer used typedef. Change-Id: If0a41a75c16be6d55bdf3841ddb5190c388968af --- src/storage/InMemoryStorageBackend.cpp | 16 +++++------ src/storage/InMemoryStorageBackend.h | 6 ++--- src/storage/Integrity.h | 3 --- test/storage/inmemorystoragebackend/buckets.cpp | 14 +++++----- .../inmemeorystoragebackendfixture.h | 3 ++- .../inmemorystoragebackend.cpp | 31 +++++++++++----------- test/storage/inmemorystoragebackend/search.cpp | 4 +-- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index 45b7874..a5e08b1 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -57,12 +57,12 @@ const std::string InMemoryStorageBackend::m_backupFilenameSuffix( const std::string InMemoryStorageBackend::m_bucketFilenamePrefix( PathConfig::StoragePath::bucketFilenamePrefix); -InMemoryStorageBackend::InMemoryStorageBackend(const std::string &path) : m_dbPath(path) { - m_integrity.reset(new Integrity(path)); +InMemoryStorageBackend::InMemoryStorageBackend(const std::string &path) : m_dbPath(path), + m_integrity(path) { } void InMemoryStorageBackend::load(void) { - bool isBackupValid = m_integrity->backupGuardExists(); + bool isBackupValid = m_integrity.backupGuardExists(); std::string bucketSuffix = ""; std::string indexFilename = m_dbPath + m_indexFilename; @@ -104,9 +104,9 @@ void InMemoryStorageBackend::save(void) { storageSerializer.dump(buckets(), std::bind(&InMemoryStorageBackend::bucketDumpStreamOpener, this, std::placeholders::_1)); - m_integrity->syncDatabase(buckets(), true); - m_integrity->createBackupGuard(); - m_integrity->revalidatePrimaryDatabase(buckets()); + m_integrity.syncDatabase(buckets(), true); + m_integrity.createBackupGuard(); + m_integrity.revalidatePrimaryDatabase(buckets()); //guard is removed during revalidation } @@ -265,10 +265,10 @@ std::shared_ptr InMemoryStorageBackend::bucketDumpStreamOpene void InMemoryStorageBackend::postLoadCleanup(bool isBackupValid) { if (isBackupValid) { - m_integrity->revalidatePrimaryDatabase(buckets()); + m_integrity.revalidatePrimaryDatabase(buckets()); } //in case there were unnecessary files in db directory - m_integrity->deleteNonIndexedFiles(std::bind(&InMemoryStorageBackend::hasBucket, this, + m_integrity.deleteNonIndexedFiles(std::bind(&InMemoryStorageBackend::hasBucket, this, std::placeholders::_1)); } diff --git a/src/storage/InMemoryStorageBackend.h b/src/storage/InMemoryStorageBackend.h index 723c61d..c9e63c2 100644 --- a/src/storage/InMemoryStorageBackend.h +++ b/src/storage/InMemoryStorageBackend.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -43,6 +43,7 @@ namespace Cynara { class InMemoryStorageBackend : public StorageBackend { public: + InMemoryStorageBackend() = delete; InMemoryStorageBackend(const std::string &path); virtual ~InMemoryStorageBackend() {}; @@ -64,7 +65,6 @@ public: const PolicyKey &filter); protected: - InMemoryStorageBackend() {} void openFileStream(std::shared_ptr stream, const std::string &filename); std::shared_ptr bucketStreamOpener(const PolicyBucketId &bucketId, const std::string &fileNameSuffix); @@ -78,7 +78,7 @@ protected: private: std::string m_dbPath; Buckets m_buckets; - IntegrityUniquePtr m_integrity; + Integrity m_integrity; static const std::string m_indexFilename; static const std::string m_backupFilenameSuffix; static const std::string m_bucketFilenamePrefix; diff --git a/src/storage/Integrity.h b/src/storage/Integrity.h index 2917fb3..569ca85 100644 --- a/src/storage/Integrity.h +++ b/src/storage/Integrity.h @@ -31,9 +31,6 @@ namespace Cynara { -class Integrity; -typedef std::unique_ptr IntegrityUniquePtr; - class Integrity { public: diff --git a/test/storage/inmemorystoragebackend/buckets.cpp b/test/storage/inmemorystoragebackend/buckets.cpp index f53b51c..d201d69 100644 --- a/test/storage/inmemorystoragebackend/buckets.cpp +++ b/test/storage/inmemorystoragebackend/buckets.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -36,7 +36,7 @@ TEST_F(InMemeoryStorageBackendFixture, createBucket) { using ::testing::ReturnRef; using ::testing::IsEmpty; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); @@ -53,7 +53,7 @@ TEST_F(InMemeoryStorageBackendFixture, createBucket) { TEST_F(InMemeoryStorageBackendFixture, updateBucket) { using ::testing::ReturnRef; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); @@ -69,7 +69,7 @@ TEST_F(InMemeoryStorageBackendFixture, updateBucket) { TEST_F(InMemeoryStorageBackendFixture, updateNonexistentBucket) { using ::testing::ReturnRef; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); @@ -81,7 +81,7 @@ TEST_F(InMemeoryStorageBackendFixture, deleteBucket) { using ::testing::ReturnRef; using ::testing::IsEmpty; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); @@ -97,7 +97,7 @@ TEST_F(InMemeoryStorageBackendFixture, hasBucket) { using ::testing::ReturnRef; using ::testing::IsEmpty; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); @@ -111,7 +111,7 @@ TEST_F(InMemeoryStorageBackendFixture, hasBucket) { TEST_F(InMemeoryStorageBackendFixture, deleteNonexistentBucket) { using ::testing::ReturnRef; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); diff --git a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h index b671a0d..fbc523b 100644 --- a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h +++ b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -147,6 +147,7 @@ protected: Cynara::Buckets m_buckets; static const std::string m_indexFileName; static const std::string m_backupFileNameSuffix; + static const std::string m_fakeDbPath; //erase helper structures Cynara::PolicyCollection m_fullPolicyCollection; diff --git a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp index 8e19743..1c88335 100644 --- a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp +++ b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -40,13 +40,14 @@ using namespace Cynara; -const std::string InMemeoryStorageBackendFixture::m_indexFileName = "buckets"; -const std::string InMemeoryStorageBackendFixture::m_backupFileNameSuffix = "~"; +const std::string InMemeoryStorageBackendFixture::m_indexFileName("buckets"); +const std::string InMemeoryStorageBackendFixture::m_backupFileNameSuffix("~"); +const std::string InMemeoryStorageBackendFixture::m_fakeDbPath("/fake/database/path"); TEST_F(InMemeoryStorageBackendFixture, defaultPolicyIsDeny) { using ::testing::ReturnRef; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillOnce(ReturnRef(m_buckets)); @@ -65,7 +66,7 @@ TEST_F(InMemeoryStorageBackendFixture, deleteLinking) { using ::testing::IsEmpty; using ::testing::UnorderedElementsAre; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .Times(4) .WillRepeatedly(ReturnRef(m_buckets)); @@ -120,7 +121,7 @@ TEST_F(InMemeoryStorageBackendFixture, insertPolicy) { using ::testing::UnorderedElementsAre; using PredefinedPolicyType::ALLOW; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillOnce(ReturnRef(m_buckets)); @@ -136,7 +137,7 @@ TEST_F(InMemeoryStorageBackendFixture, insertPolicy) { TEST_F(InMemeoryStorageBackendFixture, insertPolicyToNonexistentBucket) { using ::testing::ReturnRef; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillOnce(ReturnRef(m_buckets)); @@ -151,7 +152,7 @@ TEST_F(InMemeoryStorageBackendFixture, deletePolicy) { using ::testing::ContainerEq; using PredefinedPolicyType::ALLOW; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillOnce(ReturnRef(m_buckets)); @@ -184,7 +185,7 @@ TEST_F(InMemeoryStorageBackendFixture, deletePolicyFromNonexistentBucket) { using ::testing::IsEmpty; using ::testing::UnorderedElementsAre; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillOnce(ReturnRef(m_buckets)); @@ -311,7 +312,7 @@ TEST_F(InMemeoryStorageBackendFixture, load_from_backup) { TEST_F(InMemeoryStorageBackendFixture, erasePoliciesEmptyBase) { using ::testing::ReturnRef; - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); @@ -345,7 +346,7 @@ TEST_F(InMemeoryStorageBackendFixture, erasePoliciesSingleBucket) { for (const auto &filter : filters()) { SCOPED_TRACE(filter.name()); - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); createBucket(bucketId); @@ -380,7 +381,7 @@ TEST_F(InMemeoryStorageBackendFixture, erasePoliciesRecursiveNotLinkedBuckets) { for (const auto &filter : filters()) { SCOPED_TRACE(filter.name()); - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); createBucket(bucketId); @@ -423,7 +424,7 @@ TEST_F(InMemeoryStorageBackendFixture, erasePoliciesRecursiveLinkedBuckets) { for (const auto &filter : filters()) { SCOPED_TRACE(filter.name()); - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); createBucket(bucketId); @@ -473,7 +474,7 @@ TEST_F(InMemeoryStorageBackendFixture, erasePoliciesRecursiveLinkedBucketsNoBack for (const auto &filter : filters()) { SCOPED_TRACE(filter.name()); - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); createBucket(bucketId); @@ -522,7 +523,7 @@ TEST_F(InMemeoryStorageBackendFixture, erasePoliciesNonRecursiveLinkedBuckets) { for (const auto &filter : filters()) { SCOPED_TRACE(filter.name()); - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); createBucket(bucketId); diff --git a/test/storage/inmemorystoragebackend/search.cpp b/test/storage/inmemorystoragebackend/search.cpp index cf6a419..46247a2 100644 --- a/test/storage/inmemorystoragebackend/search.cpp +++ b/test/storage/inmemorystoragebackend/search.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -52,7 +52,7 @@ TEST_F(InMemeoryStorageBackendFixture, searchDefault) { createBucket(defaultPolicyBucketId, policies); // Just override buckets() accessor - FakeInMemoryStorageBackend backend; + FakeInMemoryStorageBackend backend(m_fakeDbPath); EXPECT_CALL(backend, buckets()) .WillRepeatedly(ReturnRef(m_buckets)); -- 2.7.4 From ba47308b144e4321ebce79ecf837a1104b0d1d74 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Mon, 22 Dec 2014 16:18:41 +0100 Subject: [PATCH 08/16] Modify RecordCorruptedException class hierarchy So far there was only one type of record Cynara could read from its database. If any corruption was detected, BucketRecordCorruptedException was thrown. Now database will contain information about not only buckets and policies, but also some metadata for them (in this case - checksums). Need for exception superclass for handling corrupted records emerged. Patch modifies exception class hierarchy and adds new exception type: ChecksumRecordCorruptedException. Change-Id: I3af6bd20b57e17ec31d766f138595920c7a413bd --- .../exceptions/BucketRecordCorruptedException.h | 64 ++++------------ .../exceptions/ChecksumRecordCorruptedException.h | 57 ++++++++++++++ src/common/exceptions/RecordCorruptedException.h | 86 ++++++++++++++++++++++ 3 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 src/common/exceptions/ChecksumRecordCorruptedException.h create mode 100644 src/common/exceptions/RecordCorruptedException.h diff --git a/src/common/exceptions/BucketRecordCorruptedException.h b/src/common/exceptions/BucketRecordCorruptedException.h index a5ffb7d..e7ea604 100644 --- a/src/common/exceptions/BucketRecordCorruptedException.h +++ b/src/common/exceptions/BucketRecordCorruptedException.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,82 +16,48 @@ /** * @file src/common/exceptions/BucketRecordCorruptedException.h * @author Aleksander Zdyb + * @author Pawel Wieczorek * @version 1.0 * @brief Implementation of BucketRecordCorruptedException */ + #ifndef SRC_COMMON_EXCEPTIONS_BUCKETRECORDCORRUPTEDEXCEPTION_H_ #define SRC_COMMON_EXCEPTIONS_BUCKETRECORDCORRUPTEDEXCEPTION_H_ #include -#include +#include namespace Cynara { -class BucketRecordCorruptedException : public DatabaseException { +class BucketRecordCorruptedException : public RecordCorruptedException { public: - BucketRecordCorruptedException(void) = delete; + BucketRecordCorruptedException() = delete; virtual ~BucketRecordCorruptedException() {}; - BucketRecordCorruptedException(const std::string &line) - : m_lineNumber(0), m_line(line) {} + BucketRecordCorruptedException(const std::string &line) : RecordCorruptedException(line) { + setMessage(subtype()); + } BucketRecordCorruptedException withLineNumber(const size_t &lineNumber) const { BucketRecordCorruptedException copy(*this); copy.m_lineNumber = lineNumber; - copy.m_message.clear(); + copy.setMessage(copy.subtype()); return copy; } BucketRecordCorruptedException withFilename(const std::string &filename) const { BucketRecordCorruptedException copy(*this); copy.m_filename = filename; - copy.m_message.clear(); + copy.setMessage(copy.subtype()); return copy; } - virtual const std::string &message(void) const { - if (m_message.empty()) { - m_message = "Bucket record corrupted at" - + formatedFilename() - + formatedLineNumber() - + ": <" + slicedLine() + ">"; - } - return m_message; - } - - const std::string &filename(void) const { - return m_filename; - } - - const std::string &line(void) const { - return m_line; - } - - size_t lineNumber(void) const { - return m_lineNumber; - } - -protected: - inline std::string slicedLine(void) const { - return m_line.substr(0, 50) + (m_line.size() > 50 ? "..." : ""); - } - - inline std::string formatedFilename(void) const { - return m_filename.empty() ? " line" : " " + m_filename; - } - - inline std::string formatedLineNumber(void) const { - return m_lineNumber <= 0 ? "" - : (m_filename.empty() ? " " : ":") - + std::to_string(static_cast(m_lineNumber)); - } - private: - size_t m_lineNumber; - std::string m_line; - std::string m_filename; - mutable std::string m_message; + const std::string &subtype(void) { + static const std::string subtype("Bucket"); + return subtype; + } }; } /* namespace Cynara */ diff --git a/src/common/exceptions/ChecksumRecordCorruptedException.h b/src/common/exceptions/ChecksumRecordCorruptedException.h new file mode 100644 index 0000000..84b81bc --- /dev/null +++ b/src/common/exceptions/ChecksumRecordCorruptedException.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015 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/ChecksumRecordCorruptedException.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief Implementation of ChecksumRecordCorruptedException + */ + +#ifndef SRC_COMMON_EXCEPTIONS_CHECKSUMRECORDCORRUPTEDEXCEPTION_H_ +#define SRC_COMMON_EXCEPTIONS_CHECKSUMRECORDCORRUPTEDEXCEPTION_H_ + +#include + +#include + +namespace Cynara { + +class ChecksumRecordCorruptedException : public RecordCorruptedException { +public: + ChecksumRecordCorruptedException() = delete; + virtual ~ChecksumRecordCorruptedException() {}; + + ChecksumRecordCorruptedException(const std::string &line) : RecordCorruptedException(line) { + setMessage(subtype()); + } + + ChecksumRecordCorruptedException withLineNumber(const size_t &lineNumber) const { + ChecksumRecordCorruptedException copy(*this); + copy.m_lineNumber = lineNumber; + copy.setMessage(copy.subtype()); + return copy; + } + +private: + const std::string &subtype(void) { + static const std::string subtype("Checksum"); + return subtype; + } +}; + +} /* namespace Cynara */ + +#endif /* SRC_COMMON_EXCEPTIONS_CHECKSUMRECORDCORRUPTEDEXCEPTION_H_ */ diff --git a/src/common/exceptions/RecordCorruptedException.h b/src/common/exceptions/RecordCorruptedException.h new file mode 100644 index 0000000..6177aab --- /dev/null +++ b/src/common/exceptions/RecordCorruptedException.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015 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/RecordCorruptedException.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief Implementation of RecordCorruptedException + */ + +#ifndef SRC_COMMON_EXCEPTIONS_RECORDCORRUPTEDEXCEPTION_H_ +#define SRC_COMMON_EXCEPTIONS_RECORDCORRUPTEDEXCEPTION_H_ + +#include + +#include + +namespace Cynara { + +class RecordCorruptedException : public DatabaseException { +public: + RecordCorruptedException(void) = delete; + virtual ~RecordCorruptedException() {}; + + RecordCorruptedException(const std::string &line) + : m_lineNumber(0), m_line(line), m_filename(std::string()) { + setMessage(std::string()); + } + + const std::string &message(void) const { + return m_message; + } + + const std::string &filename(void) const { + return m_filename; + } + + const std::string &line(void) const { + return m_line; + } + + size_t lineNumber(void) const { + return m_lineNumber; + } + +protected: + std::string slicedLine(void) const { + return m_line.substr(0, 50) + (m_line.size() > 50 ? "..." : ""); + } + + std::string formattedMetadata(void) const { + return (m_filename.empty() ? "line" : m_filename) + + (m_lineNumber <= 0 ? "" : formattedLineNumber()); + } + + std::string formattedLineNumber(void) const { + return (m_filename.empty() ? " " : ":") + + std::to_string(static_cast(m_lineNumber)); + } + + void setMessage(const std::string &subtype) { + m_message = (subtype.empty() ? "Record" : subtype + " record") + " corrupted at " + + formattedMetadata() + ": <" + slicedLine() + ">"; + } + + size_t m_lineNumber; + std::string m_line; + std::string m_filename; + std::string m_message; +}; + +} /* namespace Cynara */ + +#endif /* SRC_COMMON_EXCEPTIONS_RECORDCORRUPTEDEXCEPTION_H_ */ -- 2.7.4 From 8a9adaa9c38a3269942181abadb5f700b7661ccb Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Thu, 26 Feb 2015 14:39:39 +0100 Subject: [PATCH 09/16] Fix basename() usage in ChecksumGenerator Contents of a string passed to basename() as an argument may be modified (it depends on implementation). In order not to perform unexpected changes, duplicate of given string is passed to basename(). Change-Id: Ib783629160f9528a6054dd0f78b9ebd5e6870fb6 --- src/chsgen/ChecksumGenerator.cpp | 32 +++++++++++++++++++++++--------- src/chsgen/ChecksumGenerator.h | 4 ++-- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/chsgen/ChecksumGenerator.cpp b/src/chsgen/ChecksumGenerator.cpp index 6255403..fcc76dc 100644 --- a/src/chsgen/ChecksumGenerator.cpp +++ b/src/chsgen/ChecksumGenerator.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -43,8 +45,10 @@ const char ChecksumGenerator::m_fieldSeparator(StorageConfig::fieldSeparator); const char ChecksumGenerator::m_recordSeparator(StorageConfig::recordSeparator); const std::string ChecksumGenerator::m_backupFilenameSuffix(StorageConfig::backupFilenameSuffix); -ChecksumGenerator::ChecksumGenerator(int argc, char * const *argv) : m_copyStream(std::string()) { - m_filename = (1 < argc ? argv[1] : std::string()); +ChecksumGenerator::ChecksumGenerator(int argc, char * const *argv) { + if (argc > 1) { + m_pathname = argv[1]; + } } int ChecksumGenerator::run(void) { @@ -65,11 +69,14 @@ int ChecksumGenerator::run(void) { } catch (const std::length_error &ex) { std::cerr << ex.what() << std::endl; return CYNARA_API_UNKNOWN_ERROR; + } catch (const std::bad_alloc &ex) { + std::cerr << ex.what() << std::endl; + return CYNARA_API_OUT_OF_MEMORY; } } const std::string ChecksumGenerator::generate(const std::string &data) { - auto checksum = crypt(data.c_str(), "$1$"); + const char *checksum = crypt(data.c_str(), "$1$"); if (nullptr != checksum) { return std::string(checksum); @@ -85,10 +92,10 @@ const std::string ChecksumGenerator::generate(const std::string &data) { }; void ChecksumGenerator::openFileStream(void) { - m_inputStream.open(m_filename); + m_inputStream.open(m_pathname); if (!m_inputStream.is_open()) { - throw FileNotFoundException(m_filename); + throw FileNotFoundException(m_pathname); } } @@ -100,14 +107,21 @@ void ChecksumGenerator::copyFileStream(void) { } void ChecksumGenerator::printRecord(void) const { - std::string filename(basename(m_filename.c_str())); - getBasicFilename(filename); + std::unique_ptr pathnameDuplicate(strdup(m_pathname.c_str()), free); + if (pathnameDuplicate == nullptr) { + LOGE("Insufficient memory available to allocate duplicate filename: <%s>", + m_pathname.c_str()); + throw std::bad_alloc(); + } + + std::string basename(::basename(pathnameDuplicate.get())); + removeBackupSuffix(basename); - std::cout << filename << m_fieldSeparator << generate(m_copyStream.str()) + std::cout << basename << m_fieldSeparator << generate(m_copyStream.str()) << m_recordSeparator; } -void ChecksumGenerator::getBasicFilename(std::string &filename) const { +void ChecksumGenerator::removeBackupSuffix(std::string &filename) const { auto backupSuffixPos = filename.rfind(m_backupFilenameSuffix); if (std::string::npos != backupSuffixPos && diff --git a/src/chsgen/ChecksumGenerator.h b/src/chsgen/ChecksumGenerator.h index a3e96e0..30e34fb 100644 --- a/src/chsgen/ChecksumGenerator.h +++ b/src/chsgen/ChecksumGenerator.h @@ -42,11 +42,11 @@ private: void openFileStream(void); void copyFileStream(void); void printRecord(void) const; - void getBasicFilename(std::string &filename) const; + void removeBackupSuffix(std::string &filename) const; std::ifstream m_inputStream; std::stringstream m_copyStream; - std::string m_filename; + std::string m_pathname; static const char m_fieldSeparator; static const char m_recordSeparator; static const std::string m_backupFilenameSuffix; -- 2.7.4 From 7832866a7fbf8b6425e180d1dbdfe7b80ab8f96d Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Tue, 23 Dec 2014 14:53:59 +0100 Subject: [PATCH 10/16] Introduce ChecksumValidator ChecksumValidator computes checksums for every file listed in checksum index equivalent using crypt(3). As a result, base64-like (custom alphabet) encoded strings are produced and compared with last known corresponding digest. Its 4-character prefix indicates used algorithm. Class will be later used as an integrity mechanism extension. Change-Id: Ibaba636bae30c747e8eac5561e9b130d4398518e --- src/storage/CMakeLists.txt | 2 + src/storage/ChecksumValidator.cpp | 142 ++++++++++++++++++++++++++++++++++++++ src/storage/ChecksumValidator.h | 65 +++++++++++++++++ test/CMakeLists.txt | 1 + 4 files changed, 210 insertions(+) create mode 100644 src/storage/ChecksumValidator.cpp create mode 100644 src/storage/ChecksumValidator.h diff --git a/src/storage/CMakeLists.txt b/src/storage/CMakeLists.txt index 7bccc8c..996b37a 100644 --- a/src/storage/CMakeLists.txt +++ b/src/storage/CMakeLists.txt @@ -24,6 +24,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}/ChecksumValidator.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/InMemoryStorageBackend.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Integrity.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Storage.cpp @@ -51,6 +52,7 @@ SET_TARGET_PROPERTIES( TARGET_LINK_LIBRARIES(${TARGET_LIB_CYNARA_STORAGE} ${CYNARA_DEP_LIBRARIES} ${TARGET_CYNARA_COMMON} + crypt ) INSTALL(TARGETS ${TARGET_LIB_CYNARA_STORAGE} DESTINATION ${LIB_INSTALL_DIR}) diff --git a/src/storage/ChecksumValidator.cpp b/src/storage/ChecksumValidator.cpp new file mode 100644 index 0000000..1354ad1 --- /dev/null +++ b/src/storage/ChecksumValidator.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2015 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/ChecksumValidator.cpp + * @author Pawel Wieczorek + * @version 1.0 + * @brief This file contains ChecksumValidator implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ChecksumValidator.h" + +namespace Cynara { + +const std::string ChecksumValidator::m_checksumFilename(PathConfig::StoragePath::checksumFilename); +const std::string ChecksumValidator::m_backupFilenameSuffix( + PathConfig::StoragePath::backupFilenameSuffix); + +void ChecksumValidator::load(std::istream &stream) { + m_sums.clear(); + + std::string line; + std::size_t lineNum = 1; + while (std::getline(stream, line, PathConfig::StoragePath::recordSeparator)) { + try { + std::size_t beginToken = 0; + std::string filename = parseFilename(line, beginToken); + std::string checksum = parseChecksum(line, beginToken); + + m_sums.insert({ filename, checksum }); + ++lineNum; + } catch (const ChecksumRecordCorruptedException &ex) { + throw ex.withLineNumber(lineNum); + } + } +}; + +const std::string ChecksumValidator::generate(const std::string &data) { + const char *checksum = crypt(data.c_str(), "$1$"); + + if (nullptr != checksum) { + return checksum; + } + + int err = errno; + if (err == ENOSYS) { + LOGE("'crypt' function was not implemented; error [%d] : <%s>", err, strerror(err)); + } else { + LOGE("'crypt' function error [%d] : <%s>", err, strerror(err)); + } + throw UnexpectedErrorException(err, strerror(err)); +}; + +void ChecksumValidator::compare(std::istream &stream, const std::string &pathname, + bool isBackupValid) { + if (isChecksumIndex(pathname)) { + return; + } + + std::unique_ptr pathnameDuplicate(strdup(pathname.c_str()), free); + if (pathnameDuplicate == nullptr) { + LOGE("Insufficient memory available to allocate duplicate filename: <%s>", + pathname.c_str()); + throw std::bad_alloc(); + } + + std::string filename(::basename(pathnameDuplicate.get())); + std::stringstream copyStream; + + if (isBackupValid) { + auto backupSuffixPos = filename.rfind(m_backupFilenameSuffix); + + if ((std::string::npos != backupSuffixPos) && + (filename.size() == (backupSuffixPos + m_backupFilenameSuffix.size()))) { + filename.erase(backupSuffixPos); + } + } + + std::copy(std::istreambuf_iterator(stream), + std::istreambuf_iterator(), + std::ostreambuf_iterator(copyStream)); + stream.seekg(0); + + if (m_sums[filename] != generate(copyStream.str())) { + throw ChecksumRecordCorruptedException(m_sums[filename]); + } +}; + +const std::string ChecksumValidator::parseFilename(const std::string &line, + std::size_t &beginToken) { + std::size_t filenameEndToken = line.find(PathConfig::StoragePath::fieldSeparator, beginToken); + if (filenameEndToken != beginToken && filenameEndToken != std::string::npos) { + auto filename = line.substr(beginToken, filenameEndToken - beginToken); + beginToken = filenameEndToken + 1; + return filename; + } + throw ChecksumRecordCorruptedException(line); +} + +const std::string ChecksumValidator::parseChecksum(const std::string &line, + std::size_t &beginToken) { + if (beginToken >= line.size()) { + throw ChecksumRecordCorruptedException(line); + } + + auto checksum = line.substr(beginToken); + beginToken = line.size(); + return checksum; +} + +bool ChecksumValidator::isChecksumIndex(const std::string &filename) const { + auto checksum = m_dbPath + m_checksumFilename; + return (filename == checksum || filename == checksum + m_backupFilenameSuffix); +} + +} // namespace Cynara diff --git a/src/storage/ChecksumValidator.h b/src/storage/ChecksumValidator.h new file mode 100644 index 0000000..bfd577b --- /dev/null +++ b/src/storage/ChecksumValidator.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 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/ChecksumValidator.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief This file contains ChecksumValidator header. + */ + +#ifndef SRC_STORAGE_CHECKSUMVALIDATOR_H_ +#define SRC_STORAGE_CHECKSUMVALIDATOR_H_ + +#include +#include +#include +#include + +namespace Cynara { + +class ChecksumValidator; +typedef std::unique_ptr ChecksumValidatorUniquePtr; + +class ChecksumValidator { +public: + ChecksumValidator(const std::string &path) : m_dbPath(path) {} + + void load(std::istream &stream); + void compare(std::istream &stream, const std::string &pathname, bool isBackupValid); + + void clear(void) { + m_sums.clear(); + } + + static const std::string generate(const std::string &data); + +protected: + typedef std::unordered_map Checksums; + + bool isChecksumIndex(const std::string &pathname) const; + + static const std::string parseFilename(const std::string &line, std::size_t &beginToken); + static const std::string parseChecksum(const std::string &line, std::size_t &beginToken); + + Checksums m_sums; + const std::string m_dbPath; + static const std::string m_checksumFilename; + static const std::string m_backupFilenameSuffix; +}; + +} // namespace Cynara + +#endif // SRC_STORAGE_CHECKSUMVALIDATOR_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d937d3c..f9b42a8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -63,6 +63,7 @@ SET(CYNARA_SOURCES_FOR_TESTS ${CYNARA_SRC}/helpers/creds-commons/creds-commons.cpp ${CYNARA_SRC}/service/main/CmdlineParser.cpp ${CYNARA_SRC}/storage/BucketDeserializer.cpp + ${CYNARA_SRC}/storage/ChecksumValidator.cpp ${CYNARA_SRC}/storage/InMemoryStorageBackend.cpp ${CYNARA_SRC}/storage/Integrity.cpp ${CYNARA_SRC}/storage/Storage.cpp -- 2.7.4 From 9f0e503b603acb3bcb7dfedd6fd27216070e6be7 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Tue, 27 Jan 2015 11:47:19 +0100 Subject: [PATCH 11/16] Add tests for ChecksumValidator Add tests checking if ChecksumValidator properly: * generates checksums, * loads them or rejects corrupted records, * supports backup files. Change-Id: I2e4222283cc0676490134819561824df6661034f --- test/CMakeLists.txt | 1 + test/storage/checksum/checksumvalidator.cpp | 154 +++++++++++++++++++++++ test/storage/checksum/checksumvalidatorfixture.h | 54 ++++++++ test/storage/checksum/fakechecksumvalidator.h | 37 ++++++ 4 files changed, 246 insertions(+) create mode 100644 test/storage/checksum/checksumvalidator.cpp create mode 100644 test/storage/checksum/checksumvalidatorfixture.h create mode 100644 test/storage/checksum/fakechecksumvalidator.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f9b42a8..5050634 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,6 +92,7 @@ SET(CYNARA_TESTS_SOURCES cyad/policy_parser.cpp helpers.cpp service/main/cmdlineparser.cpp + storage/checksum/checksumvalidator.cpp storage/performance/bucket.cpp storage/storage/policies.cpp storage/storage/check.cpp diff --git a/test/storage/checksum/checksumvalidator.cpp b/test/storage/checksum/checksumvalidator.cpp new file mode 100644 index 0000000..19918f3 --- /dev/null +++ b/test/storage/checksum/checksumvalidator.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015 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/storage/checksum/checksumvalidator.cpp + * @author Pawel Wieczorek + * @version 1.0 + * @brief Tests of ChecksumValidator + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "checksumvalidatorfixture.h" +#include "fakechecksumvalidator.h" + +using namespace Cynara; + +const std::string ChecksumValidatorFixture::m_dbPath("/fake/path/"); +const std::string ChecksumValidatorFixture::m_filename("fakeFilename"); +const std::string ChecksumValidatorFixture::m_checksum("$1$$fakeChecksum"); +const std::string ChecksumValidatorFixture::m_backupFilenameSuffix( + PathConfig::StoragePath::backupFilenameSuffix); +const char ChecksumValidatorFixture::m_fieldSeparator(PathConfig::StoragePath::fieldSeparator); +const char ChecksumValidatorFixture::m_recordSeparator(PathConfig::StoragePath::recordSeparator); +const size_t ChecksumValidatorFixture::m_firstLine(1); + +/** + * @brief Verify if generate() can use systems' crypt(3) implementation + * @test Expected result: no exceptions are thrown and non-empty string is returned + */ +TEST_F(ChecksumValidatorFixture, generateChecksum) { + for (const auto &data : { std::string("test-data"), std::string() }) { + SCOPED_TRACE(data); + + std::string returnedChecksum; + ASSERT_NO_THROW(returnedChecksum = ChecksumValidator::generate(data)); + ASSERT_NE(std::string(), returnedChecksum); + } +} + +/** + * @brief Verify if load() can successfully parse sample checksum record + * @test Expected result: + * - no exceptions are thrown from load() + * - proper checksums are stored for corresponding files + */ +TEST_F(ChecksumValidatorFixture, parseCorrectRecords) { + std::istringstream checksums(m_filename + m_fieldSeparator + m_checksum); + + FakeChecksumValidator validator(m_dbPath); + ASSERT_NO_THROW(validator.load(checksums)); + ASSERT_EQ(m_checksum, validator.sums().at(m_filename)); +} + +/** + * @brief Verify if load() rejects storing corrupted checksum records + * @test Expected result: + * - load() throws ChecksumRecordCorruptedException + * - no data is stored (there was no correct data to insert) + */ +TEST_F(ChecksumValidatorFixture, parseCorruptedRecords) { + const auto badLines = { m_filename + m_fieldSeparator, + m_filename + m_checksum, + m_fieldSeparator + m_checksum }; + + FakeChecksumValidator validator(m_dbPath); + + for (const auto &badLine : badLines) { + SCOPED_TRACE(badLine); + std::istringstream checksums(badLine); + + ASSERT_THROW(validator.load(checksums), ChecksumRecordCorruptedException); + ASSERT_TRUE(validator.sums().empty()); + } +} + +/** + * @brief Verify if compare() can successfully check sample database file + * @test Expected result: no exceptions are thrown and file contents are still readable + */ +TEST_F(ChecksumValidatorFixture, compareBasicAndBackup) { + FakeChecksumValidator validator(m_dbPath); + std::string checksumsContents; + simpleChecksumsContents(checksumsContents); + + std::istringstream checksums(checksumsContents); + validator.load(checksums); + + std::unordered_map files = { { "_", std::string() }, + { "buckets", ";0x0;\n" } }; + + for (const auto &file : files) { + const auto filename = m_dbPath + file.first; + const auto contents = file.second; + std::istringstream fakeFile(contents); + + SCOPED_TRACE(filename); + + ASSERT_NO_THROW(validator.compare(fakeFile, filename, false)); + ASSERT_NO_THROW(validator.compare(fakeFile, filename + m_backupFilenameSuffix, true)); + + ASSERT_EQ(contents, fakeFile.str()); + } +} + +/** + * @brief Verify if compare() throws an exception when checksum mismatch is detected + * @test Expected result: ChecksumRecordCorruptedException is thrown + */ +TEST_F(ChecksumValidatorFixture, checksumMismatch) { + FakeChecksumValidator validator(m_dbPath); + std::string checksumsContents; + simpleChecksumsContents(checksumsContents); + + std::istringstream checksums(checksumsContents); + validator.load(checksums); + + // Please note that default policy is set to ALLOW instead of DENY + std::unordered_map files = { { "_", "client;user;privilege;0x0;" }, + { "buckets", ";0xFFFF;\n" } }; + + for (const auto &file : files) { + const auto filename = m_dbPath + file.first; + const auto contents = file.second; + std::istringstream fakeFile(contents); + SCOPED_TRACE(filename); + + ASSERT_THROW(validator.compare(fakeFile, filename, false), + ChecksumRecordCorruptedException); + } +} diff --git a/test/storage/checksum/checksumvalidatorfixture.h b/test/storage/checksum/checksumvalidatorfixture.h new file mode 100644 index 0000000..6fb28f6 --- /dev/null +++ b/test/storage/checksum/checksumvalidatorfixture.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 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/storage/checksum/checksumvalidatorfixture.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief Fixture for ChecksumValidator tests + */ + +#ifndef CHECKSUMVALIDATORFIXTURE_H_ +#define CHECKSUMVALIDATORFIXTURE_H_ + +#include +#include + +#include +#include +#include +#include + +class ChecksumValidatorFixture : public ::testing::Test { +protected: + void simpleChecksumsContents(std::string &contents) { + std::stringstream buffer; + buffer << "_" << m_fieldSeparator << "$1$$qRPK7m23GJusamGpoGLby/" << m_recordSeparator + << "buckets" << m_fieldSeparator << "$1$$6ZlVs5lw2nZgVmiw0BdY21" + << m_recordSeparator; + + contents = buffer.str(); + } + + static const std::string m_dbPath; + static const std::string m_filename; + static const std::string m_checksum; + static const std::string m_backupFilenameSuffix; + static const char m_fieldSeparator; + static const char m_recordSeparator; + static const size_t m_firstLine; +}; + +#endif /* CHECKSUMVALIDATORFIXTURE_H_ */ diff --git a/test/storage/checksum/fakechecksumvalidator.h b/test/storage/checksum/fakechecksumvalidator.h new file mode 100644 index 0000000..af3ed82 --- /dev/null +++ b/test/storage/checksum/fakechecksumvalidator.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 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/storage/checksum/fakechecksumvalidator.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief Mock of ChecksumValidator + */ + +#ifndef FAKECHECKSUMVALIDATOR_H_ +#define FAKECHECKSUMVALIDATOR_H_ + +#include + +class FakeChecksumValidator : public Cynara::ChecksumValidator { +public: + using Cynara::ChecksumValidator::ChecksumValidator; + + const Checksums &sums(void) { + return m_sums; + } +}; + +#endif /* FAKECHECKSUMVALIDATOR_H_ */ -- 2.7.4 From 58d92c8f005be204b6d0cb990d8276aed1f3b76a Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Mon, 12 Jan 2015 15:01:54 +0100 Subject: [PATCH 12/16] Make StorageSerializer a template class This patch modifies StorageSerializer so that it will be able to use other streams than std::ostream and its derivatives. Within current class hierarchy custom output streams with overloaded insertion operator (operator<<) cannot be used, as it is non-virtual in std::ostream. Change-Id: I3e713329c55aacfbb8daa23a5c4579d4c5db9f52 --- src/storage/CMakeLists.txt | 3 +- src/storage/InMemoryStorageBackend.cpp | 6 +-- src/storage/InMemoryStorageBackend.h | 3 +- src/storage/StorageSerializer.cpp | 92 ---------------------------------- src/storage/StorageSerializer.h | 68 ++++++++++++++++++++++++- test/CMakeLists.txt | 1 - test/storage/serializer/dump.cpp | 8 +-- test/storage/serializer/dump_load.cpp | 4 +- test/storage/serializer/serialize.cpp | 27 +++++----- 9 files changed, 93 insertions(+), 119 deletions(-) delete mode 100644 src/storage/StorageSerializer.cpp diff --git a/src/storage/CMakeLists.txt b/src/storage/CMakeLists.txt index 996b37a..72f0ec2 100644 --- a/src/storage/CMakeLists.txt +++ b/src/storage/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +# Copyright (c) 2014-2015 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. @@ -29,7 +29,6 @@ SET(LIB_CYNARA_STORAGE_SOURCES ${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 ) INCLUDE_DIRECTORIES( diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index a5e08b1..d6655e2 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -100,7 +100,7 @@ void InMemoryStorageBackend::save(void) { std::string indexFilename = m_dbPath + m_indexFilename; openDumpFileStream(indexStream, indexFilename + m_backupFilenameSuffix); - StorageSerializer storageSerializer(indexStream); + StorageSerializer storageSerializer(indexStream); storageSerializer.dump(buckets(), std::bind(&InMemoryStorageBackend::bucketDumpStreamOpener, this, std::placeholders::_1)); @@ -253,14 +253,14 @@ std::shared_ptr InMemoryStorageBackend::bucketStreamOpener( } } -std::shared_ptr InMemoryStorageBackend::bucketDumpStreamOpener( +std::shared_ptr > InMemoryStorageBackend::bucketDumpStreamOpener( const PolicyBucketId &bucketId) { std::string bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + m_backupFilenameSuffix; auto bucketStream = std::make_shared(); openDumpFileStream(bucketStream, bucketFilename); - return std::make_shared(bucketStream); + return std::make_shared >(bucketStream); } void InMemoryStorageBackend::postLoadCleanup(bool isBackupValid) { diff --git a/src/storage/InMemoryStorageBackend.h b/src/storage/InMemoryStorageBackend.h index c9e63c2..10251c7 100644 --- a/src/storage/InMemoryStorageBackend.h +++ b/src/storage/InMemoryStorageBackend.h @@ -71,7 +71,8 @@ protected: virtual void openDumpFileStream(std::shared_ptr stream, const std::string &filename); - std::shared_ptr bucketDumpStreamOpener(const PolicyBucketId &bucketId); + std::shared_ptr > bucketDumpStreamOpener( + const PolicyBucketId &bucketId); virtual void postLoadCleanup(bool isBackupValid); diff --git a/src/storage/StorageSerializer.cpp b/src/storage/StorageSerializer.cpp deleted file mode 100644 index 9f701dd..0000000 --- a/src/storage/StorageSerializer.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2014-2015 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/StorageSerializer.cpp - * @author Aleksander Zdyb - * @version 1.0 - * @brief Implementation of Cynara::StorageSerializer methods - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "StorageSerializer.h" - -namespace Cynara { - -StorageSerializer::StorageSerializer(std::shared_ptr os) : m_outStream(os) { -} - -void StorageSerializer::dump(const Buckets &buckets, - BucketStreamOpener streamOpener) { - - for (const auto bucketIter : buckets) { - const auto &bucket = bucketIter.second; - - dumpFields(bucket.id(), bucket.defaultPolicy().policyType(), - bucket.defaultPolicy().metadata()); - } - - for (const auto bucketIter : buckets) { - const auto &bucketId = bucketIter.first; - const auto &bucket = bucketIter.second; - auto bucketSerializer = streamOpener(bucketId); - - if (bucketSerializer != nullptr) { - bucketSerializer->dump(bucket); - } else { - throw BucketSerializationException(bucketId); - } - } -} - -void StorageSerializer::dump(const PolicyBucket& bucket) { - for (auto it = std::begin(bucket); it != std::end(bucket); ++it) { - const auto &policy = *it; - dump(policy); - } -} - -void StorageSerializer::dump(const PolicyKeyFeature &keyFeature) { - *m_outStream << keyFeature.toString(); -} - -void StorageSerializer::dump(const PolicyType &policyType) { - auto oldFormat = m_outStream->flags(); - *m_outStream << "0x" << std::uppercase << std::hex << policyType; - m_outStream->flags(oldFormat); -} - -void StorageSerializer::dump(const PolicyResult::PolicyMetadata &metadata) { - *m_outStream << metadata; -} - -void StorageSerializer::dump(const PolicyCollection::value_type &policy) { - const auto &key = policy->key(); - const auto &result = policy->result(); - - dumpFields(key.client(), key.user(), key.privilege(), result.policyType(), result.metadata()); -} - -} /* namespace Cynara */ diff --git a/src/storage/StorageSerializer.h b/src/storage/StorageSerializer.h index 42982b2..40eb784 100644 --- a/src/storage/StorageSerializer.h +++ b/src/storage/StorageSerializer.h @@ -25,9 +25,13 @@ #include #include +#include #include +#include #include +#include +#include #include #include #include @@ -39,13 +43,14 @@ namespace Cynara { +template class StorageSerializer { public: typedef std::function(const PolicyBucketId &)> BucketStreamOpener; - StorageSerializer(std::shared_ptr os); + StorageSerializer(std::shared_ptr os); virtual ~StorageSerializer() {}; virtual void dump(const Buckets &buckets, @@ -72,9 +77,68 @@ protected: void dump(const PolicyCollection::value_type &policy); private: - std::shared_ptr m_outStream; + std::shared_ptr m_outStream; }; +template +StorageSerializer::StorageSerializer(std::shared_ptr os) : m_outStream(os) { +} + +template +void StorageSerializer::dump(const Buckets &buckets, BucketStreamOpener streamOpener) { + for (const auto bucketIter : buckets) { + const auto &bucket = bucketIter.second; + + dumpFields(bucket.id(), bucket.defaultPolicy().policyType(), + bucket.defaultPolicy().metadata()); + } + + for (const auto bucketIter : buckets) { + const auto &bucketId = bucketIter.first; + const auto &bucket = bucketIter.second; + auto bucketSerializer = streamOpener(bucketId); + + if (bucketSerializer != nullptr) { + bucketSerializer->dump(bucket); + } else { + throw BucketSerializationException(bucketId); + } + } +} + +template +void StorageSerializer::dump(const PolicyBucket &bucket) { + for (auto it = std::begin(bucket); it != std::end(bucket); ++it) { + const auto &policy = *it; + dump(policy); + } +} + +template +void StorageSerializer::dump(const PolicyKeyFeature &keyFeature) { + *m_outStream << keyFeature.toString(); +} + +template +void StorageSerializer::dump(const PolicyType &policyType) { + auto oldFormat = m_outStream->flags(); + *m_outStream << "0x" << std::uppercase << std::hex << policyType; + m_outStream->flags(oldFormat); +} + +template +void StorageSerializer::dump(const PolicyResult::PolicyMetadata &metadata) { + *m_outStream << metadata; +} + +template +void StorageSerializer::dump(const PolicyCollection::value_type &policy) { + const auto &key = policy->key(); + const auto &result = policy->result(); + + dumpFields(key.client(), key.user(), key.privilege(), result.policyType(), result.metadata()); +} + } /* namespace Cynara */ #endif /* SRC_STORAGE_STORAGESERIALIZER_H_ */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5050634..9cf89d7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -68,7 +68,6 @@ SET(CYNARA_SOURCES_FOR_TESTS ${CYNARA_SRC}/storage/Integrity.cpp ${CYNARA_SRC}/storage/Storage.cpp ${CYNARA_SRC}/storage/StorageDeserializer.cpp - ${CYNARA_SRC}/storage/StorageSerializer.cpp ) SET(CYNARA_TESTS_SOURCES diff --git a/test/storage/serializer/dump.cpp b/test/storage/serializer/dump.cpp index 2eafcf7..1daaf32 100644 --- a/test/storage/serializer/dump.cpp +++ b/test/storage/serializer/dump.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -51,7 +51,7 @@ TEST(serializer_dump, dump_empty_bucket) { auto oss = std::make_shared(); PolicyBucket bucket("empty"); - StorageSerializer serializer(oss); + StorageSerializer serializer(oss); serializer.dump(bucket); ASSERT_EQ("", oss->str()); @@ -69,7 +69,7 @@ TEST(serializer_dump, dump_bucket) { Policy::simpleWithKey(pk2, DENY) })); auto outStream = std::make_shared(); - StorageSerializer serializer(outStream); + StorageSerializer serializer(outStream); serializer.dump(bucket); // Split stream into records @@ -99,7 +99,7 @@ TEST(serializer_dump, dump_bucket_bucket) { Policy::bucketWithKey(pk3, bucketId) }}; auto outStream = std::make_shared(); - StorageSerializer serializer(outStream); + StorageSerializer serializer(outStream); serializer.dump(bucket); // Split stream into records diff --git a/test/storage/serializer/dump_load.cpp b/test/storage/serializer/dump_load.cpp index f3eff9b..cd4ce5c 100644 --- a/test/storage/serializer/dump_load.cpp +++ b/test/storage/serializer/dump_load.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -54,7 +54,7 @@ TEST(dump_load, bucket) { auto ioStream = std::make_shared(); - StorageSerializer serializer(ioStream); + StorageSerializer serializer(ioStream); serializer.dump(bucket); BucketDeserializer deserializer(ioStream); diff --git a/test/storage/serializer/serialize.cpp b/test/storage/serializer/serialize.cpp index 938283d..1681a5b 100644 --- a/test/storage/serializer/serialize.cpp +++ b/test/storage/serializer/serialize.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -37,21 +37,24 @@ class FakeStreamForBucketId { public: + typedef std::shared_ptr > + StringstreamStorageSerializerPtr; + MOCK_METHOD1(streamForBucketId, - std::shared_ptr(const Cynara::PolicyBucketId &)); + StringstreamStorageSerializerPtr(const Cynara::PolicyBucketId &)); - Cynara::StorageSerializer::BucketStreamOpener streamOpener() { + Cynara::StorageSerializer::BucketStreamOpener streamOpener() { return std::bind(&FakeStreamForBucketId::streamForBucketId, this, std::placeholders::_1); } }; // Fake StorageSerializer for Cynara::PolicyBucket -class FakeStorageSerializer : public Cynara::StorageSerializer { +class FakeStorageSerializer : public Cynara::StorageSerializer { public: - FakeStorageSerializer(std::shared_ptr o) : Cynara::StorageSerializer(o), - outStream(o) {} + FakeStorageSerializer(std::shared_ptr o) + : Cynara::StorageSerializer(o), outStream(o) {} MOCK_METHOD1(dump, void(const Cynara::PolicyBucket &bucket)); - std::shared_ptr outStream; + std::shared_ptr outStream; }; class StorageSerializerFixture : public ::testing::Test { @@ -67,8 +70,8 @@ using namespace Cynara; // Be sure no calls to streamForBucketId() are made // and output stream is not touched TEST_F(StorageSerializerFixture, dump_buckets_empty) { - auto outStream = std::make_shared(); - StorageSerializer serializer(outStream); + auto outStream = std::make_shared(); + StorageSerializer serializer(outStream); serializer.dump(Buckets(), fakeStreamOpener.streamOpener()); // Stream should be empty @@ -83,7 +86,7 @@ TEST_F(StorageSerializerFixture, dump_buckets) { // Will be returned as serializer for buckets auto fakeBucketSerializer = std::make_shared( - std::make_shared()); + std::make_shared()); buckets = { { "bucket1", PolicyBucket("bucket1", PredefinedPolicyType::DENY) }, @@ -93,7 +96,7 @@ TEST_F(StorageSerializerFixture, dump_buckets) { }; auto outStream = std::make_shared(); - StorageSerializer dbSerializer(outStream); + StorageSerializer dbSerializer(outStream); // Make sure stream was opened for each bucket EXPECT_CALL(fakeStreamOpener, streamForBucketId(_)) @@ -128,7 +131,7 @@ TEST_F(StorageSerializerFixture, dump_buckets_io_error) { }; auto outStream = std::make_shared(); - StorageSerializer dbSerializer(outStream); + StorageSerializer dbSerializer(outStream); // Make sure stream was opened for each bucket EXPECT_CALL(fakeStreamOpener, streamForBucketId(_)) -- 2.7.4 From bf8b0794dbe4d401430cae787e6bc3e6e0d09224 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Thu, 15 Jan 2015 08:19:15 +0100 Subject: [PATCH 13/16] Introduce ChecksumStream ChecksumStream will replace std::ofstream for saving database contents in storage. This way data will be not only written to the files, but also its checksums will be computed and stored in given stream (database index equivalent for storing checksums). Checksum computing is performed during stream destruction in order to be sure that all necessary data was already collected. Change-Id: I4a9ff2e29361f337cacd790d77364feca854a706 --- src/storage/CMakeLists.txt | 1 + src/storage/ChecksumStream.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++ src/storage/ChecksumStream.h | 70 +++++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + 4 files changed, 143 insertions(+) create mode 100644 src/storage/ChecksumStream.cpp create mode 100644 src/storage/ChecksumStream.h diff --git a/src/storage/CMakeLists.txt b/src/storage/CMakeLists.txt index 72f0ec2..f75bc06 100644 --- a/src/storage/CMakeLists.txt +++ b/src/storage/CMakeLists.txt @@ -24,6 +24,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}/ChecksumStream.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/ChecksumValidator.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/InMemoryStorageBackend.cpp ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Integrity.cpp diff --git a/src/storage/ChecksumStream.cpp b/src/storage/ChecksumStream.cpp new file mode 100644 index 0000000..551307e --- /dev/null +++ b/src/storage/ChecksumStream.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015 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/ChecksumStream.cpp + * @author Pawel Wieczorek + * @version 1.0 + * @brief This file contains ChecksumStream implementation. + */ + +#include +#include + +#include + +#include + +#include "ChecksumStream.h" + +namespace Cynara { + +const char ChecksumStream::m_fieldSeparator(PathConfig::StoragePath::fieldSeparator); +const char ChecksumStream::m_recordSeparator(PathConfig::StoragePath::recordSeparator); + +ChecksumStream::~ChecksumStream() { + if (!std::uncaught_exception()) { + save(); + } +} + +ChecksumStream& ChecksumStream::operator<<(std::ostream& (*manip)(std::ostream&)) { + m_bufStream << manip; + m_outStream << manip; + return *this; +} + +void ChecksumStream::open(const std::string &filename, std::ios_base::openmode mode) { + m_outStream.open(filename, mode); +} + +bool ChecksumStream::is_open(void) const { + return m_outStream.is_open(); +} + +std::ios_base::fmtflags ChecksumStream::flags(void) const { + return m_outStream.flags(); +} + +std::ios_base::fmtflags ChecksumStream::flags(std::ios_base::fmtflags flags) { + return m_outStream.flags(flags); +} + +void ChecksumStream::save() { + m_outStream.close(); + *m_chsStream << m_filename << m_fieldSeparator << ChecksumValidator::generate(m_bufStream.str()) + << m_recordSeparator; +} + +} // namespace Cynara diff --git a/src/storage/ChecksumStream.h b/src/storage/ChecksumStream.h new file mode 100644 index 0000000..ddc6e6f --- /dev/null +++ b/src/storage/ChecksumStream.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015 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/ChecksumStream.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief This file contains ChecksumStream header. + */ + +#ifndef SRC_STORAGE_CHECKSUMSTREAM_H_ +#define SRC_STORAGE_CHECKSUMSTREAM_H_ + +#include +#include +#include +#include +#include + +namespace Cynara { + +class ChecksumStream { +public: + ChecksumStream(const std::string &filename, const std::shared_ptr &stream) + : m_chsStream(stream), m_filename(filename) { + } + ~ChecksumStream(); + + template + ChecksumStream& operator<<(const Type &item); + ChecksumStream& operator<<(std::ostream& (*manip)(std::ostream&)); + + void open(const std::string &filename, std::ios_base::openmode mode); + bool is_open(void) const; + std::ios_base::fmtflags flags(void) const; + std::ios_base::fmtflags flags(std::ios_base::fmtflags flags); + +private: + void save(void); + + std::shared_ptr m_chsStream; + std::ofstream m_outStream; + std::stringstream m_bufStream; + const std::string m_filename; + static const char m_fieldSeparator; + static const char m_recordSeparator; +}; + +template +ChecksumStream& ChecksumStream::operator<<(const Type& item) { + m_bufStream << item; + m_outStream << item; + return *this; +} + +} // namespace Cynara + +#endif // SRC_STORAGE_CHECKSUMSTREAM_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9cf89d7..189268d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -63,6 +63,7 @@ SET(CYNARA_SOURCES_FOR_TESTS ${CYNARA_SRC}/helpers/creds-commons/creds-commons.cpp ${CYNARA_SRC}/service/main/CmdlineParser.cpp ${CYNARA_SRC}/storage/BucketDeserializer.cpp + ${CYNARA_SRC}/storage/ChecksumStream.cpp ${CYNARA_SRC}/storage/ChecksumValidator.cpp ${CYNARA_SRC}/storage/InMemoryStorageBackend.cpp ${CYNARA_SRC}/storage/Integrity.cpp -- 2.7.4 From f08a80ee755ba5aed0450bb04eaa235e3966ab05 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Tue, 23 Dec 2014 17:18:16 +0100 Subject: [PATCH 14/16] Adjust InMemoryStorageBackend to ChecksumValidator InMemoryStorageBackend uses ChecksumValidator as a checksum loader and comparator. This patch also includes files needed by storage unit tests to work properly. Change-Id: I541975351275bd6a30e7cf627697c9657161312f --- src/storage/InMemoryStorageBackend.cpp | 27 ++++++++++++++++++++------- src/storage/InMemoryStorageBackend.h | 9 +++++++-- src/storage/Integrity.cpp | 8 +++++++- test/db/db2/checksum | 1 + test/db/db3/checksum | 2 ++ test/db/db4/checksum | 3 +++ test/db/db5/checksum | 3 +++ test/db/db6/checksum | 3 +++ test/db/db6/checksum~ | 3 +++ 9 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 test/db/db2/checksum create mode 100644 test/db/db3/checksum create mode 100644 test/db/db4/checksum create mode 100644 test/db/db5/checksum create mode 100644 test/db/db6/checksum create mode 100644 test/db/db6/checksum~ diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index d6655e2..249eccf 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ namespace Cynara { +const std::string InMemoryStorageBackend::m_chsFilename(PathConfig::StoragePath::checksumFilename); const std::string InMemoryStorageBackend::m_indexFilename(PathConfig::StoragePath::indexFilename); const std::string InMemoryStorageBackend::m_backupFilenameSuffix( PathConfig::StoragePath::backupFilenameSuffix); @@ -58,26 +60,32 @@ const std::string InMemoryStorageBackend::m_bucketFilenamePrefix( PathConfig::StoragePath::bucketFilenamePrefix); InMemoryStorageBackend::InMemoryStorageBackend(const std::string &path) : m_dbPath(path), - m_integrity(path) { + m_checksum(path), m_integrity(path) { } void InMemoryStorageBackend::load(void) { bool isBackupValid = m_integrity.backupGuardExists(); std::string bucketSuffix = ""; std::string indexFilename = m_dbPath + m_indexFilename; + std::string chsFilename = m_dbPath + m_chsFilename; if (isBackupValid) { bucketSuffix += m_backupFilenameSuffix; indexFilename += m_backupFilenameSuffix; + chsFilename += m_backupFilenameSuffix; } try { + auto chsStream = std::make_shared(); + openFileStream(chsStream, chsFilename, isBackupValid); + m_checksum.load(*chsStream); + auto indexStream = std::make_shared(); - openFileStream(indexStream, indexFilename); + openFileStream(indexStream, indexFilename, isBackupValid); StorageDeserializer storageDeserializer(indexStream, std::bind(&InMemoryStorageBackend::bucketStreamOpener, this, - std::placeholders::_1, bucketSuffix)); + std::placeholders::_1, bucketSuffix, isBackupValid)); storageDeserializer.initBuckets(buckets()); storageDeserializer.loadBuckets(buckets()); @@ -86,6 +94,7 @@ void InMemoryStorageBackend::load(void) { buckets().clear(); // TODO: Implement emergency mode toggle } + m_checksum.clear(); if (!hasBucket(defaultPolicyBucketId)) { LOGN("Creating defaultBucket."); @@ -222,7 +231,7 @@ void InMemoryStorageBackend::erasePolicies(const PolicyBucketId &bucketId, bool } void InMemoryStorageBackend::openFileStream(std::shared_ptr stream, - const std::string &filename) { + const std::string &filename, bool isBackupValid) { // TODO: Consider adding exceptions to streams and handling them: // stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); stream->open(filename); @@ -230,6 +239,8 @@ void InMemoryStorageBackend::openFileStream(std::shared_ptr strea if (!stream->is_open()) { throw FileNotFoundException(filename); } + + m_checksum.compare(*stream, filename, isBackupValid); } void InMemoryStorageBackend::openDumpFileStream(std::shared_ptr stream, @@ -242,14 +253,16 @@ void InMemoryStorageBackend::openDumpFileStream(std::shared_ptr s } std::shared_ptr InMemoryStorageBackend::bucketStreamOpener( - const PolicyBucketId &bucketId, const std::string &filenameSuffix) { + const PolicyBucketId &bucketId, const std::string &filenameSuffix, bool isBackupValid) { std::string bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + filenameSuffix; auto bucketStream = std::make_shared(); try { - openFileStream(bucketStream, bucketFilename); + openFileStream(bucketStream, bucketFilename, isBackupValid); return std::make_shared(bucketStream); } catch (const FileNotFoundException &) { return nullptr; + } catch (const std::bad_alloc &) { + return nullptr; } } @@ -269,7 +282,7 @@ void InMemoryStorageBackend::postLoadCleanup(bool isBackupValid) { } //in case there were unnecessary files in db directory m_integrity.deleteNonIndexedFiles(std::bind(&InMemoryStorageBackend::hasBucket, this, - std::placeholders::_1)); + std::placeholders::_1)); } } /* namespace Cynara */ diff --git a/src/storage/InMemoryStorageBackend.h b/src/storage/InMemoryStorageBackend.h index 10251c7..65af96b 100644 --- a/src/storage/InMemoryStorageBackend.h +++ b/src/storage/InMemoryStorageBackend.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -65,9 +66,11 @@ public: const PolicyKey &filter); protected: - void openFileStream(std::shared_ptr stream, const std::string &filename); + void openFileStream(std::shared_ptr stream, const std::string &filename, + bool isBackupValid); std::shared_ptr bucketStreamOpener(const PolicyBucketId &bucketId, - const std::string &fileNameSuffix); + const std::string &fileNameSuffix, + bool isBackupValid); virtual void openDumpFileStream(std::shared_ptr stream, const std::string &filename); @@ -79,7 +82,9 @@ protected: private: std::string m_dbPath; Buckets m_buckets; + ChecksumValidator m_checksum; Integrity m_integrity; + static const std::string m_chsFilename; static const std::string m_indexFilename; static const std::string m_backupFilenameSuffix; static const std::string m_bucketFilenamePrefix; diff --git a/src/storage/Integrity.cpp b/src/storage/Integrity.cpp index 5dfa624..0b8e583 100644 --- a/src/storage/Integrity.cpp +++ b/src/storage/Integrity.cpp @@ -83,6 +83,7 @@ void Integrity::syncDatabase(const Buckets &buckets, bool syncBackup) { } syncElement(m_dbPath + m_indexFilename + suffix); + syncElement(m_dbPath + PathConfig::StoragePath::checksumFilename + suffix); syncDirectory(m_dbPath); } @@ -152,7 +153,8 @@ void Integrity::syncElement(const std::string &filename, int flags, mode_t mode) if (fileFd < 0) { int err = errno; if (err != EEXIST) { - LOGE("'open' function error [%d] : <%s>", err, strerror(err)); + LOGE("File <%s> : 'open' function error [%d] : <%s>", filename.c_str(), err, + strerror(err)); throw UnexpectedErrorException(err, strerror(err)); } else { throw CannotCreateFileException(filename); @@ -194,9 +196,12 @@ void Integrity::createPrimaryHardLinks(const Buckets &buckets) { } const auto &indexFilename = m_dbPath + m_indexFilename; + const auto &checksumFilename = m_dbPath + PathConfig::StoragePath::checksumFilename; deleteHardLink(indexFilename); createHardLink(indexFilename + m_backupFilenameSuffix, indexFilename); + deleteHardLink(checksumFilename); + createHardLink(checksumFilename + m_backupFilenameSuffix, checksumFilename); } void Integrity::deleteBackupHardLinks(const Buckets &buckets) { @@ -209,6 +214,7 @@ void Integrity::deleteBackupHardLinks(const Buckets &buckets) { } deleteHardLink(m_dbPath + m_indexFilename + m_backupFilenameSuffix); + deleteHardLink(m_dbPath + PathConfig::StoragePath::checksumFilename + m_backupFilenameSuffix); } void Integrity::createHardLink(const std::string &oldName, const std::string &newName) { diff --git a/test/db/db2/checksum b/test/db/db2/checksum new file mode 100644 index 0000000..a91e13a --- /dev/null +++ b/test/db/db2/checksum @@ -0,0 +1 @@ +buckets;$1$$QYBGLyef4xp18EzYIMd/U0 diff --git a/test/db/db3/checksum b/test/db/db3/checksum new file mode 100644 index 0000000..39a8d89 --- /dev/null +++ b/test/db/db3/checksum @@ -0,0 +1,2 @@ +buckets;$1$$QYBGLyef4xp18EzYIMd/U0 +_;$1$$qRPK7m23GJusamGpoGLby/ diff --git a/test/db/db4/checksum b/test/db/db4/checksum new file mode 100644 index 0000000..e2477f0 --- /dev/null +++ b/test/db/db4/checksum @@ -0,0 +1,3 @@ +buckets;$1$$Z6OUrCl62KbAaZ16Cexgm0 +_;$1$$qRPK7m23GJusamGpoGLby/ +_additional;$1$$qRPK7m23GJusamGpoGLby/ diff --git a/test/db/db5/checksum b/test/db/db5/checksum new file mode 100644 index 0000000..b578233 --- /dev/null +++ b/test/db/db5/checksum @@ -0,0 +1,3 @@ +buckets;$1$$Z6OUrCl62KbAaZ16Cexgm0 +_;$1$$qRPK7m23GJusamGpoGLby/ +_additional;$1$$NIRf1PrhNP0QOAOGJ4VDf/ diff --git a/test/db/db6/checksum b/test/db/db6/checksum new file mode 100644 index 0000000..a951841 --- /dev/null +++ b/test/db/db6/checksum @@ -0,0 +1,3 @@ +buckets;$1$$Z6OUrCl62KbAaZ16Cexgm0 +_;$1$$qRPK7m23GJusamGpoGLby/ +_additional;$1$$Shj03wOSJf.nmFjldRTO.1 diff --git a/test/db/db6/checksum~ b/test/db/db6/checksum~ new file mode 100644 index 0000000..e2477f0 --- /dev/null +++ b/test/db/db6/checksum~ @@ -0,0 +1,3 @@ +buckets;$1$$Z6OUrCl62KbAaZ16Cexgm0 +_;$1$$qRPK7m23GJusamGpoGLby/ +_additional;$1$$qRPK7m23GJusamGpoGLby/ -- 2.7.4 From e47f0b094278df45f6d38483718fafeba4d558d0 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Thu, 15 Jan 2015 10:53:18 +0100 Subject: [PATCH 15/16] Adjust InMemoryStorageBackend to ChecksumStream Now InMemoryStorageBackend uses ChecksumStream instead of std::ofstream. New member function dumpDatabase() was introduced in order to destruct database index stream before calling integrity mechanism. Change-Id: I5ea943e1ec21f02cea97699993ddbd0f3eeb0a62 --- src/storage/InMemoryStorageBackend.cpp | 43 +++++++++++++++++----------------- src/storage/InMemoryStorageBackend.h | 24 +++++++++++++++---- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index 249eccf..57a4b56 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -105,13 +104,11 @@ void InMemoryStorageBackend::load(void) { } void InMemoryStorageBackend::save(void) { - auto indexStream = std::make_shared(); - std::string indexFilename = m_dbPath + m_indexFilename; - openDumpFileStream(indexStream, indexFilename + m_backupFilenameSuffix); + std::string checksumFilename = m_dbPath + m_chsFilename; + auto chsStream = std::make_shared(); + openDumpFileStream(chsStream, checksumFilename + m_backupFilenameSuffix); - StorageSerializer storageSerializer(indexStream); - storageSerializer.dump(buckets(), std::bind(&InMemoryStorageBackend::bucketDumpStreamOpener, - this, std::placeholders::_1)); + dumpDatabase(chsStream); m_integrity.syncDatabase(buckets(), true); m_integrity.createBackupGuard(); @@ -230,7 +227,17 @@ void InMemoryStorageBackend::erasePolicies(const PolicyBucketId &bucketId, bool } } -void InMemoryStorageBackend::openFileStream(std::shared_ptr stream, +void InMemoryStorageBackend::dumpDatabase(const std::shared_ptr &chsStream) { + auto indexStream = std::make_shared(m_indexFilename, chsStream); + 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, chsStream)); +} + +void InMemoryStorageBackend::openFileStream(const std::shared_ptr &stream, const std::string &filename, bool isBackupValid) { // TODO: Consider adding exceptions to streams and handling them: // stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); @@ -243,15 +250,6 @@ void InMemoryStorageBackend::openFileStream(std::shared_ptr strea m_checksum.compare(*stream, filename, isBackupValid); } -void InMemoryStorageBackend::openDumpFileStream(std::shared_ptr stream, - const std::string &filename) { - stream->open(filename, std::ofstream::out | std::ofstream::trunc); - - if (!stream->is_open()) { - throw CannotCreateFileException(filename); - } -} - std::shared_ptr InMemoryStorageBackend::bucketStreamOpener( const PolicyBucketId &bucketId, const std::string &filenameSuffix, bool isBackupValid) { std::string bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + filenameSuffix; @@ -266,14 +264,15 @@ std::shared_ptr InMemoryStorageBackend::bucketStreamOpener( } } -std::shared_ptr > InMemoryStorageBackend::bucketDumpStreamOpener( - const PolicyBucketId &bucketId) { +std::shared_ptr > InMemoryStorageBackend::bucketDumpStreamOpener( + const PolicyBucketId &bucketId, const std::shared_ptr &chsStream) { std::string bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + m_backupFilenameSuffix; - auto bucketStream = std::make_shared(); + auto bucketStream = std::make_shared(m_bucketFilenamePrefix + bucketId, + chsStream); - openDumpFileStream(bucketStream, bucketFilename); - return std::make_shared >(bucketStream); + openDumpFileStream(bucketStream, bucketFilename); + return std::make_shared >(bucketStream); } void InMemoryStorageBackend::postLoadCleanup(bool isBackupValid) { diff --git a/src/storage/InMemoryStorageBackend.h b/src/storage/InMemoryStorageBackend.h index 65af96b..ea1f2bd 100644 --- a/src/storage/InMemoryStorageBackend.h +++ b/src/storage/InMemoryStorageBackend.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include #include +#include #include #include #include @@ -66,16 +68,18 @@ public: const PolicyKey &filter); protected: - void openFileStream(std::shared_ptr stream, const std::string &filename, + void dumpDatabase(const std::shared_ptr &chsStream); + void openFileStream(const std::shared_ptr &stream, const std::string &filename, bool isBackupValid); std::shared_ptr bucketStreamOpener(const PolicyBucketId &bucketId, const std::string &fileNameSuffix, bool isBackupValid); - virtual void openDumpFileStream(std::shared_ptr stream, - const std::string &filename); - std::shared_ptr > bucketDumpStreamOpener( - const PolicyBucketId &bucketId); + template + void openDumpFileStream(const std::shared_ptr &stream, + const std::string &filename); + std::shared_ptr > bucketDumpStreamOpener( + const PolicyBucketId &bucketId, const std::shared_ptr &chsStream); virtual void postLoadCleanup(bool isBackupValid); @@ -98,6 +102,16 @@ protected: } }; +template +void InMemoryStorageBackend::openDumpFileStream(const std::shared_ptr &stream, + const std::string &filename) { + stream->open(filename, std::ofstream::out | std::ofstream::trunc); + + if (!stream->is_open()) { + throw CannotCreateFileException(filename); + } +} + } /* namespace Cynara */ #endif /* SRC_STORAGE_INMEMORYSTORAGEBACKEND_H_ */ -- 2.7.4 From d760937d27e7c88fd61796465544810b0bdfb799 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Wed, 4 Feb 2015 15:05:30 +0100 Subject: [PATCH 16/16] Prepare service for database corruption handling When database corruption is detected, no administrative actions are allowed and all responses to check requests are DENY. Administrative API has to be informed about detected corruption. This patch modifies protocol so that this information is passed. Unit tests are also adjusted to the protocol changes this patch introduces. Change-Id: If3ab5d6ca1671167890956b986a4768cc828f3f5 --- src/common/protocol/ProtocolAdmin.cpp | 57 ++++++--- src/common/response/AdminCheckResponse.h | 13 +- src/common/response/CodeResponse.h | 4 +- src/common/response/DescriptionListResponse.h | 12 +- src/common/response/ListResponse.h | 13 +- src/service/logic/Logic.cpp | 134 +++++++++++++-------- src/service/logic/Logic.h | 2 + test/common/protocols/admin/admincheckresponse.cpp | 72 ++++++++--- .../protocols/admin/descriptionlistresponse.cpp | 69 ++++++++--- test/common/protocols/admin/listresponse.cpp | 76 ++++++++---- 10 files changed, 320 insertions(+), 132 deletions(-) diff --git a/src/common/protocol/ProtocolAdmin.cpp b/src/common/protocol/ProtocolAdmin.cpp index 238374b..35eedd8 100644 --- a/src/common/protocol/ProtocolAdmin.cpp +++ b/src/common/protocol/ProtocolAdmin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -17,6 +17,7 @@ * @file src/common/protocol/ProtocolAdmin.cpp * @author Lukasz Wojciechowski * @author Adam Malinowski + * @author Pawel Wieczorek * @version 1.0 * @brief This file implements protocol class for administration */ @@ -233,17 +234,20 @@ ResponsePtr ProtocolAdmin::deserializeAdminCheckResponse(void) { PolicyType result; PolicyResult::PolicyMetadata additionalInfo; bool bucketValid; + bool dbCorrupted; ProtocolDeserialization::deserialize(m_frameHeader, result); ProtocolDeserialization::deserialize(m_frameHeader, additionalInfo); ProtocolDeserialization::deserialize(m_frameHeader, bucketValid); + ProtocolDeserialization::deserialize(m_frameHeader, dbCorrupted); const PolicyResult policyResult(result, additionalInfo); - LOGD("Deserialized AdminCheckResponse: result [%" PRIu16 "], metadata <%s>, bucketValid [%d]", - policyResult.policyType(), policyResult.metadata().c_str(), static_cast(bucketValid)); + LOGD("Deserialized AdminCheckResponse: result [%" PRIu16 "], metadata <%s>, bucketValid [%d], " + "dbCorrupted [%d]", policyResult.policyType(), policyResult.metadata().c_str(), + static_cast(bucketValid), static_cast(dbCorrupted)); - return std::make_shared(policyResult, bucketValid, + return std::make_shared(policyResult, bucketValid, dbCorrupted, m_frameHeader.sequenceNumber()); } @@ -269,10 +273,14 @@ ResponsePtr ProtocolAdmin::deserializeDescriptionListResponse(void) { ProtocolDeserialization::deserialize(m_frameHeader, descriptions[fields].name); } - LOGD("Deserialized DescriptionListResponse: number of descriptions [%" PRIu16 "]", - descriptionsCount); + bool dbCorrupted; + ProtocolDeserialization::deserialize(m_frameHeader, dbCorrupted); - return std::make_shared(descriptions, m_frameHeader.sequenceNumber()); + LOGD("Deserialized DescriptionListResponse: number of descriptions [%" PRIu16 "], " + "dbCorrupted [%d]", descriptionsCount, static_cast(dbCorrupted)); + + return std::make_shared(descriptions, dbCorrupted, + m_frameHeader.sequenceNumber()); } ResponsePtr ProtocolAdmin::deserializeListResponse(void) { @@ -298,13 +306,18 @@ ResponsePtr ProtocolAdmin::deserializeListResponse(void) { PolicyResult(policyType, metadata))); } - bool isBucketValid; - ProtocolDeserialization::deserialize(m_frameHeader, isBucketValid); + bool bucketValid; + bool dbCorrupted; + ProtocolDeserialization::deserialize(m_frameHeader, bucketValid); + ProtocolDeserialization::deserialize(m_frameHeader, dbCorrupted); + + LOGD("Deserialized ListResponse: number of policies [%" PRIu16 "], bucketValid [%d], " + "dbCorrupted [%d]", policiesCount, static_cast(bucketValid), + static_cast(dbCorrupted)); - LOGD("Deserialized ListResponse: number of policies [%" PRIu16 "], isBucketValid [%d]", - policiesCount, isBucketValid); + return std::make_shared(policies, bucketValid, dbCorrupted, + m_frameHeader.sequenceNumber()); - return std::make_shared(policies, isBucketValid, m_frameHeader.sequenceNumber()); } ResponsePtr ProtocolAdmin::extractResponseFromBuffer(BinaryQueuePtr bufferQueue) { @@ -471,9 +484,10 @@ void ProtocolAdmin::execute(RequestContextPtr context, SetPoliciesRequestPtr req void ProtocolAdmin::execute(RequestContextPtr context, AdminCheckResponsePtr response) { LOGD("Serializing AdminCheckResponse: op [%" PRIu8 "], sequenceNumber [%" PRIu16 "], " - "policyType [%" PRIu16 "], metadata <%s>, bucketValid [%d]", OpAdminCheckPolicyResponse, - response->sequenceNumber(), response->result().policyType(), - response->result().metadata().c_str(), static_cast(response->isBucketValid())); + "policyType [%" PRIu16 "], metadata <%s>, bucketValid [%d], dbCorrupted [%d]", + OpAdminCheckPolicyResponse, response->sequenceNumber(), response->result().policyType(), + response->result().metadata().c_str(), static_cast(response->isBucketValid()), + static_cast(response->isDbCorrupted())); ProtocolFrame frame = ProtocolFrameSerializer::startSerialization( response->sequenceNumber()); @@ -482,6 +496,7 @@ void ProtocolAdmin::execute(RequestContextPtr context, AdminCheckResponsePtr res ProtocolSerialization::serialize(frame, response->result().policyType()); ProtocolSerialization::serialize(frame, response->result().metadata()); ProtocolSerialization::serialize(frame, response->isBucketValid()); + ProtocolSerialization::serialize(frame, response->isDbCorrupted()); ProtocolFrameSerializer::finishSerialization(frame, *(context->responseQueue())); } @@ -504,8 +519,8 @@ void ProtocolAdmin::execute(RequestContextPtr context, DescriptionListResponsePt = static_cast(response->descriptions().size()); LOGD("Serializing DescriptionListResponse: op [%" PRIu8 "], sequenceNumber [%" PRIu16 "], " - "number of descriptions [%" PRIu16 "]", OpDescriptionListResponse, - response->sequenceNumber(), descriptionsSize); + "number of descriptions [%" PRIu16 "], dbCorrupted [%d]", OpDescriptionListResponse, + response->sequenceNumber(), descriptionsSize, static_cast(response->isDbCorrupted())); ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(response->sequenceNumber()); @@ -515,6 +530,8 @@ void ProtocolAdmin::execute(RequestContextPtr context, DescriptionListResponsePt ProtocolSerialization::serialize(frame, desc.type); ProtocolSerialization::serialize(frame, desc.name); } + ProtocolSerialization::serialize(frame, response->isDbCorrupted()); + ProtocolFrameSerializer::finishSerialization(frame, *(context->responseQueue())); } @@ -523,8 +540,9 @@ void ProtocolAdmin::execute(RequestContextPtr context, ListResponsePtr response) = static_cast(response->policies().size()); LOGD("Serializing ListResponse: op [%" PRIu8 "], sequenceNumber [%" PRIu16 "], " - "number of policies [%" PRIu16 "], isBucketValid [%d]", OpListResponse, - response->sequenceNumber(), policiesSize, response->isBucketValid()); + "number of policies [%" PRIu16 "], bucketValid [%d], dbCorrupted [%d]", OpListResponse, + response->sequenceNumber(), policiesSize, static_cast(response->isBucketValid()), + static_cast(response->isDbCorrupted())); ProtocolFrame frame = ProtocolFrameSerializer::startSerialization(response->sequenceNumber()); @@ -540,6 +558,7 @@ void ProtocolAdmin::execute(RequestContextPtr context, ListResponsePtr response) ProtocolSerialization::serialize(frame, policy.result().metadata()); } ProtocolSerialization::serialize(frame, response->isBucketValid()); + ProtocolSerialization::serialize(frame, response->isDbCorrupted()); ProtocolFrameSerializer::finishSerialization(frame, *(context->responseQueue())); } diff --git a/src/common/response/AdminCheckResponse.h b/src/common/response/AdminCheckResponse.h index c2bbf30..d553fd9 100644 --- a/src/common/response/AdminCheckResponse.h +++ b/src/common/response/AdminCheckResponse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,6 +16,7 @@ /** * @file src/common/response/AdminCheckResponse.h * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief This file defines response class for admin check request */ @@ -33,9 +34,10 @@ namespace Cynara { class AdminCheckResponse : public Response { public: - AdminCheckResponse(const PolicyResult &result, bool bucketValid, + AdminCheckResponse(const PolicyResult &result, bool bucketValid, bool dbCorrupted, ProtocolFrameSequenceNumber sequenceNumber) : - Response(sequenceNumber), m_result(result), m_bucketValid(bucketValid) { + Response(sequenceNumber), m_result(result), m_bucketValid(bucketValid), + m_dbCorrupted(dbCorrupted) { } virtual ~AdminCheckResponse() {} @@ -51,9 +53,14 @@ public: return m_bucketValid; } + bool isDbCorrupted(void) const { + return m_dbCorrupted; + } + private: const PolicyResult m_result; bool m_bucketValid; + bool m_dbCorrupted; }; } // namespace Cynara diff --git a/src/common/response/CodeResponse.h b/src/common/response/CodeResponse.h index f5790f4..78b21c1 100644 --- a/src/common/response/CodeResponse.h +++ b/src/common/response/CodeResponse.h @@ -16,6 +16,7 @@ /** * @file src/common/response/CodeResponse.h * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief This file defines class for responding to a request with a code */ @@ -36,7 +37,8 @@ public: NO_BUCKET, NO_POLICY_TYPE, NOT_ALLOWED, - FAILED + FAILED, + DB_CORRUPTED }; const Code m_code; diff --git a/src/common/response/DescriptionListResponse.h b/src/common/response/DescriptionListResponse.h index a963775..310ee63 100644 --- a/src/common/response/DescriptionListResponse.h +++ b/src/common/response/DescriptionListResponse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,6 +16,7 @@ /** * @file src/common/response/DescriptionListResponse.h * @author Zofia Abramowska + * @author Pawel Wieczorek * @version 1.0 * @brief This file defines response class for plugins description list response */ @@ -34,21 +35,26 @@ namespace Cynara { class DescriptionListResponse : public Response { public: - DescriptionListResponse(const std::vector &descriptions, + DescriptionListResponse(const std::vector &descriptions, bool dbCorrupted, ProtocolFrameSequenceNumber sequenceNumber) : - Response(sequenceNumber), m_descriptions(descriptions) { + Response(sequenceNumber), m_descriptions(descriptions), m_dbCorrupted(dbCorrupted) { } virtual ~DescriptionListResponse() {}; virtual void execute(ResponsePtr self, ResponseTakerPtr taker, RequestContextPtr context) const; + bool isDbCorrupted(void) const { + return m_dbCorrupted; + } + const std::vector &descriptions(void) const { return m_descriptions; } private: std::vector m_descriptions; + bool m_dbCorrupted; }; } // namespace Cynara diff --git a/src/common/response/ListResponse.h b/src/common/response/ListResponse.h index ec52736..2a67225 100644 --- a/src/common/response/ListResponse.h +++ b/src/common/response/ListResponse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,6 +16,7 @@ /** * @file src/common/response/ListResponse.h * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief This file defines response class for policies list request */ @@ -35,9 +36,10 @@ namespace Cynara { class ListResponse : public Response { public: - ListResponse(const std::vector &policies, bool bucketValid, + ListResponse(const std::vector &policies, bool bucketValid, bool dbCorrupted, ProtocolFrameSequenceNumber sequenceNumber) : - Response(sequenceNumber), m_policies(policies), m_bucketValid(bucketValid) { + Response(sequenceNumber), m_policies(policies), m_bucketValid(bucketValid), + m_dbCorrupted(dbCorrupted) { } virtual ~ListResponse() {}; @@ -48,6 +50,10 @@ public: return m_bucketValid; } + bool isDbCorrupted(void) const { + return m_dbCorrupted; + } + const std::vector &policies(void) const { return m_policies; } @@ -55,6 +61,7 @@ public: private: std::vector m_policies; bool m_bucketValid; + bool m_dbCorrupted; }; } // namespace Cynara diff --git a/src/service/logic/Logic.cpp b/src/service/logic/Logic.cpp index 6a1bece..a6e3c12 100644 --- a/src/service/logic/Logic.cpp +++ b/src/service/logic/Logic.cpp @@ -17,6 +17,7 @@ * @file src/service/logic/Logic.cpp * @author Lukasz Wojciechowski * @author Zofia Abramowska + * @author Pawel Wieczorek * @version 1.0 * @brief This file implements main class of logic layer in cynara service */ @@ -76,7 +77,7 @@ namespace Cynara { -Logic::Logic() { +Logic::Logic() : m_dbCorrupted(false) { } Logic::~Logic() { @@ -96,15 +97,20 @@ void Logic::execute(RequestContextPtr context UNUSED, SignalRequestPtr request) void Logic::execute(RequestContextPtr context, AdminCheckRequestPtr request) { PolicyResult result; bool bucketValid = true; - try { - result = m_storage->checkPolicy(request->key(), request->startBucket(), - request->recursive()); - } catch (const BucketNotExistsException &ex) { + + if (m_dbCorrupted) { bucketValid = false; + } else { + try { + result = m_storage->checkPolicy(request->key(), request->startBucket(), + request->recursive()); + } catch (const BucketNotExistsException &ex) { + bucketValid = false; + } } context->returnResponse(context, std::make_shared(result, bucketValid, - request->sequenceNumber())); + m_dbCorrupted, request->sequenceNumber())); } void Logic::execute(RequestContextPtr context, AgentActionRequestPtr request) { @@ -183,7 +189,7 @@ bool Logic::check(const RequestContextPtr &context, const PolicyKey &key, return false; } - result = m_storage->checkPolicy(key); + result = (m_dbCorrupted ? PredefinedPolicyType::DENY : m_storage->checkPolicy(key)); switch (result.policyType()) { case PredefinedPolicyType::ALLOW : @@ -286,19 +292,23 @@ void Logic::execute(RequestContextPtr context, DescriptionListRequestPtr request descriptions.insert(descriptions.begin(), predefinedPolicyDescr.begin(), predefinedPolicyDescr.end()); context->returnResponse(context, std::make_shared(descriptions, - request->sequenceNumber())); + m_dbCorrupted, request->sequenceNumber())); } void Logic::execute(RequestContextPtr context, EraseRequestPtr request) { auto code = CodeResponse::Code::OK; - try { - m_storage->erasePolicies(request->startBucket(), request->recursive(), request->filter()); - onPoliciesChanged(); - } catch (const DatabaseException &ex) { - code = CodeResponse::Code::FAILED; - } catch (const BucketNotExistsException &ex) { - code = CodeResponse::Code::NO_BUCKET; + if (m_dbCorrupted) { + code = CodeResponse::Code::DB_CORRUPTED; + } else { + try { + m_storage->erasePolicies(request->startBucket(), request->recursive(), request->filter()); + onPoliciesChanged(); + } catch (const DatabaseException &ex) { + code = CodeResponse::Code::FAILED; + } catch (const BucketNotExistsException &ex) { + code = CodeResponse::Code::NO_BUCKET; + } } context->returnResponse(context, std::make_shared(code, @@ -308,18 +318,22 @@ void Logic::execute(RequestContextPtr context, EraseRequestPtr request) { void Logic::execute(RequestContextPtr context, InsertOrUpdateBucketRequestPtr request) { auto code = CodeResponse::Code::OK; - try { - checkSinglePolicyType(request->result().policyType(), true, true); - m_storage->addOrUpdateBucket(request->bucketId(), request->result()); - onPoliciesChanged(); - } catch (const DatabaseException &ex) { - code = CodeResponse::Code::FAILED; - } catch (const DefaultBucketSetNoneException &ex) { - code = CodeResponse::Code::NOT_ALLOWED; - } catch (const InvalidBucketIdException &ex) { - code = CodeResponse::Code::NOT_ALLOWED; - } catch (const UnknownPolicyTypeException &ex) { - code = CodeResponse::Code::NO_POLICY_TYPE; + if (m_dbCorrupted) { + code = CodeResponse::Code::DB_CORRUPTED; + } else { + try { + checkSinglePolicyType(request->result().policyType(), true, true); + m_storage->addOrUpdateBucket(request->bucketId(), request->result()); + onPoliciesChanged(); + } catch (const DatabaseException &ex) { + code = CodeResponse::Code::FAILED; + } catch (const DefaultBucketSetNoneException &ex) { + code = CodeResponse::Code::NOT_ALLOWED; + } catch (const InvalidBucketIdException &ex) { + code = CodeResponse::Code::NOT_ALLOWED; + } catch (const UnknownPolicyTypeException &ex) { + code = CodeResponse::Code::NO_POLICY_TYPE; + } } context->returnResponse(context, std::make_shared(code, @@ -328,48 +342,64 @@ void Logic::execute(RequestContextPtr context, InsertOrUpdateBucketRequestPtr re void Logic::execute(RequestContextPtr context, ListRequestPtr request) { bool bucketValid = true; - std::vector policies; - try { - policies = m_storage->listPolicies(request->bucket(), request->filter()); - } catch (const BucketNotExistsException &ex) { + + if (m_dbCorrupted) { bucketValid = false; + } else { + try { + policies = m_storage->listPolicies(request->bucket(), request->filter()); + } catch (const BucketNotExistsException &ex) { + bucketValid = false; + } } context->returnResponse(context, std::make_shared(policies, bucketValid, - request->sequenceNumber())); + m_dbCorrupted, request->sequenceNumber())); } void Logic::execute(RequestContextPtr context, RemoveBucketRequestPtr request) { auto code = CodeResponse::Code::OK; - try { - m_storage->deleteBucket(request->bucketId()); - onPoliciesChanged(); - } catch (const DatabaseException &ex) { - code = CodeResponse::Code::FAILED; - } catch (const BucketNotExistsException &ex) { - code = CodeResponse::Code::NO_BUCKET; - } catch (const DefaultBucketDeletionException &ex) { - code = CodeResponse::Code::NOT_ALLOWED; + + if (m_dbCorrupted) { + code = CodeResponse::Code::DB_CORRUPTED; + } else { + try { + m_storage->deleteBucket(request->bucketId()); + onPoliciesChanged(); + } catch (const DatabaseException &ex) { + code = CodeResponse::Code::FAILED; + } catch (const BucketNotExistsException &ex) { + code = CodeResponse::Code::NO_BUCKET; + } catch (const DefaultBucketDeletionException &ex) { + code = CodeResponse::Code::NOT_ALLOWED; + } } + context->returnResponse(context, std::make_shared(code, request->sequenceNumber())); } void Logic::execute(RequestContextPtr context, SetPoliciesRequestPtr request) { auto code = CodeResponse::Code::OK; - try { - checkPoliciesTypes(request->policiesToBeInsertedOrUpdated(), true, false); - m_storage->insertPolicies(request->policiesToBeInsertedOrUpdated()); - m_storage->deletePolicies(request->policiesToBeRemoved()); - onPoliciesChanged(); - } catch (const DatabaseException &ex) { - code = CodeResponse::Code::FAILED; - } catch (const BucketNotExistsException &ex) { - code = CodeResponse::Code::NO_BUCKET; - } catch (const UnknownPolicyTypeException &ex) { - code = CodeResponse::Code::NO_POLICY_TYPE; + + if (m_dbCorrupted) { + code = CodeResponse::Code::DB_CORRUPTED; + } else { + try { + checkPoliciesTypes(request->policiesToBeInsertedOrUpdated(), true, false); + m_storage->insertPolicies(request->policiesToBeInsertedOrUpdated()); + m_storage->deletePolicies(request->policiesToBeRemoved()); + onPoliciesChanged(); + } catch (const DatabaseException &ex) { + code = CodeResponse::Code::FAILED; + } catch (const BucketNotExistsException &ex) { + code = CodeResponse::Code::NO_BUCKET; + } catch (const UnknownPolicyTypeException &ex) { + code = CodeResponse::Code::NO_POLICY_TYPE; + } } + context->returnResponse(context, std::make_shared(code, request->sequenceNumber())); } diff --git a/src/service/logic/Logic.h b/src/service/logic/Logic.h index 2a4ff8a..3e8ee8c 100644 --- a/src/service/logic/Logic.h +++ b/src/service/logic/Logic.h @@ -17,6 +17,7 @@ * @file src/service/logic/Logic.h * @author Lukasz Wojciechowski * @author Zofia Abramowska + * @author Pawel Wieczorek * @version 1.0 * @brief This file defines main class of logic layer in cynara service */ @@ -95,6 +96,7 @@ private: StoragePtr m_storage; SocketManagerPtr m_socketManager; AuditLog m_auditLog; + bool m_dbCorrupted; bool check(const RequestContextPtr &context, const PolicyKey &key, ProtocolFrameSequenceNumber checkId, PolicyResult &result); diff --git a/test/common/protocols/admin/admincheckresponse.cpp b/test/common/protocols/admin/admincheckresponse.cpp index 666f65e..5aa2386 100644 --- a/test/common/protocols/admin/admincheckresponse.cpp +++ b/test/common/protocols/admin/admincheckresponse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,6 +16,7 @@ /** * @file test/common/protocols/admin/admincheckresponse.cpp * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief Tests for Cynara::AdminCheckResponse usage in Cynara::ProtocolAdmin */ @@ -34,10 +35,13 @@ template<> void compare(const Cynara::AdminCheckResponse &resp1, const Cynara::AdminCheckResponse &resp2) { EXPECT_EQ(resp1.result(), resp2.result()); EXPECT_EQ(resp1.isBucketValid(), resp2.isBucketValid()); + EXPECT_EQ(resp1.isDbCorrupted(), resp2.isDbCorrupted()); } static const bool VALID_BUCKET = true; static const bool NO_BUCKET = false; +static const bool DB_OK = false; +static const bool DB_CORRUPTED = true; } /* anonymous namespace */ @@ -48,19 +52,21 @@ using namespace TestDataCollection; /* *** compare by objects test cases *** */ TEST(ProtocolAdmin, AdminCheckResponse01) { - auto response = std::make_shared(Results::allow, VALID_BUCKET, SN::min); + auto response = std::make_shared(Results::allow, VALID_BUCKET, DB_OK, + SN::min); auto protocol = std::make_shared(); testResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponse02) { - auto response = std::make_shared(Results::deny, NO_BUCKET, SN::min_1); + auto response = std::make_shared(Results::deny, NO_BUCKET, DB_OK, + SN::min_1); auto protocol = std::make_shared(); testResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponse03) { - auto response = std::make_shared(Results::bucket_empty, VALID_BUCKET, + auto response = std::make_shared(Results::bucket_empty, VALID_BUCKET, DB_OK, SN::min_2); auto protocol = std::make_shared(); testResponse(response, protocol); @@ -68,25 +74,42 @@ TEST(ProtocolAdmin, AdminCheckResponse03) { TEST(ProtocolAdmin, AdminCheckResponse04) { auto response = std::make_shared(Results::bucket_not_empty, NO_BUCKET, - SN::max); + DB_OK, SN::max); auto protocol = std::make_shared(); testResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponse05) { - auto response = std::make_shared(Results::none, VALID_BUCKET, SN::max_1); + auto response = std::make_shared(Results::none, VALID_BUCKET, DB_OK, + SN::max_1); auto protocol = std::make_shared(); testResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponse06) { - auto response = std::make_shared(Results::plugin_1, NO_BUCKET, SN::max_2); + auto response = std::make_shared(Results::plugin_1, NO_BUCKET, DB_OK, + SN::max_2); auto protocol = std::make_shared(); testResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponse07) { - auto response = std::make_shared(Results::plugin_2, VALID_BUCKET, SN::mid); + auto response = std::make_shared(Results::plugin_2, VALID_BUCKET, DB_OK, + SN::mid); + auto protocol = std::make_shared(); + testResponse(response, protocol); +} + +/** + * @brief Verify if AdminCheckResponse is properly (de)serialized while database is corrupted + * @test Expected result: + * - PolicyResult set to DENY + * - bucketValid flag set to false (NO_BUCKET) + * - dbCorrupted flag set to true (DB_CORRUPTED) + */ +TEST(ProtocolAdmin, AdminCheckResponse08) { + auto response = std::make_shared(Results::deny, NO_BUCKET, DB_CORRUPTED, + SN::max); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -94,19 +117,21 @@ TEST(ProtocolAdmin, AdminCheckResponse07) { /* *** compare by serialized data test cases *** */ TEST(ProtocolAdmin, AdminCheckResponseBinary01) { - auto response = std::make_shared(Results::allow, VALID_BUCKET, SN::min); + auto response = std::make_shared(Results::allow, VALID_BUCKET, DB_OK, + SN::min); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponseBinary02) { - auto response = std::make_shared(Results::deny, NO_BUCKET, SN::min_1); + auto response = std::make_shared(Results::deny, NO_BUCKET, DB_OK, + SN::min_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponseBinary03) { - auto response = std::make_shared(Results::bucket_empty, VALID_BUCKET, + auto response = std::make_shared(Results::bucket_empty, VALID_BUCKET, DB_OK, SN::min_2); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); @@ -114,25 +139,42 @@ TEST(ProtocolAdmin, AdminCheckResponseBinary03) { TEST(ProtocolAdmin, AdminCheckResponseBinary04) { auto response = std::make_shared(Results::bucket_not_empty, NO_BUCKET, - SN::max); + DB_OK, SN::max); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponseBinary05) { - auto response = std::make_shared(Results::none, VALID_BUCKET, SN::max_1); + auto response = std::make_shared(Results::none, VALID_BUCKET, DB_OK, + SN::max_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponseBinary06) { - auto response = std::make_shared(Results::plugin_1, NO_BUCKET, SN::max_2); + auto response = std::make_shared(Results::plugin_1, NO_BUCKET, DB_OK, + SN::max_2); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } TEST(ProtocolAdmin, AdminCheckResponseBinary07) { - auto response = std::make_shared(Results::plugin_2, VALID_BUCKET, SN::mid); + auto response = std::make_shared(Results::plugin_2, VALID_BUCKET, DB_OK, + SN::mid); + auto protocol = std::make_shared(); + binaryTestResponse(response, protocol); +} + +/** + * @brief Verify if AdminCheckResponse is properly (de)serialized while database is corrupted + * @test Expected result: + * - PolicyResult set to DENY + * - bucketValid flag set to false (NO_BUCKET) + * - dbCorrupted flag set to true (DB_CORRUPTED) + */ +TEST(ProtocolAdmin, AdminCheckResponseBinary08) { + auto response = std::make_shared(Results::deny, NO_BUCKET, DB_CORRUPTED, + SN::max); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } diff --git a/test/common/protocols/admin/descriptionlistresponse.cpp b/test/common/protocols/admin/descriptionlistresponse.cpp index 43acbaa..42e8bae 100644 --- a/test/common/protocols/admin/descriptionlistresponse.cpp +++ b/test/common/protocols/admin/descriptionlistresponse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,6 +16,7 @@ /** * @file test/common/protocols/admin/descriptionlistresponse.cpp * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief Tests for Cynara::DescriptionListResponse usage in Cynara::ProtocolAdmin */ @@ -42,8 +43,12 @@ void compare(const Cynara::DescriptionListResponse &resp1, EXPECT_EQ(resp1.descriptions()[i].name, resp2.descriptions()[i].name); EXPECT_EQ(resp1.descriptions()[i].type, resp2.descriptions()[i].type); } + EXPECT_EQ(resp1.isDbCorrupted(), resp2.isDbCorrupted()); } +static const bool DB_OK = false; +static const bool DB_CORRUPTED = true; + } /* namespace anonymous */ using namespace Cynara; @@ -57,7 +62,7 @@ TEST(ProtocolAdmin, DescriptionListResponse01) { PolicyDescription(Types::allow, "allow"), }; - auto response = std::make_shared(descriptions, SN::min); + auto response = std::make_shared(descriptions, DB_OK, SN::min); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -67,7 +72,7 @@ TEST(ProtocolAdmin, DescriptionListResponse02) { PolicyDescription(Types::bucket, "bucket"), }; - auto response = std::make_shared(descriptions, SN::min_1); + auto response = std::make_shared(descriptions, DB_OK, SN::min_1); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -77,7 +82,7 @@ TEST(ProtocolAdmin, DescriptionListResponse03) { PolicyDescription(Types::deny, "deny"), }; - auto response = std::make_shared(descriptions, SN::max); + auto response = std::make_shared(descriptions, DB_OK, SN::max); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -87,7 +92,7 @@ TEST(ProtocolAdmin, DescriptionListResponse04) { PolicyDescription(Types::none, "none"), }; - auto response = std::make_shared(descriptions, SN::max_1); + auto response = std::make_shared(descriptions, DB_OK, SN::max_1); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -97,7 +102,7 @@ TEST(ProtocolAdmin, DescriptionListResponse05) { PolicyDescription(Types::plugin_type, "plugin"), }; - auto response = std::make_shared(descriptions, SN::mid); + auto response = std::make_shared(descriptions, DB_OK, SN::mid); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -113,7 +118,7 @@ TEST(ProtocolAdmin, DescriptionListResponseMultipleDescriptions) { PolicyDescription(Types::plugin_type, "plugin"), }; - auto response = std::make_shared(descriptions, SN::max_2); + auto response = std::make_shared(descriptions, DB_OK, SN::max_2); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -121,7 +126,24 @@ TEST(ProtocolAdmin, DescriptionListResponseMultipleDescriptions) { TEST(ProtocolAdmin, DescriptionListResponseEmptyDescriptions) { std::vector descriptions; - auto response = std::make_shared(descriptions, SN::min_2); + auto response = std::make_shared(descriptions, DB_OK, SN::min_2); + auto protocol = std::make_shared(); + testResponse(response, protocol); +} + +/** + * @brief Verify if DescriptionListResponse is properly (de)serialized while database is corrupted + * @test Expected result: + * - descriptions vector contains predefined policies ALLOW and DENY + * - dbCorrupted flag set to true (DB_CORRUPTED) + */ +TEST(ProtocolAdmin, DescriptionListResponseDatabaseCorrupted) { + std::vector descriptions = { + PolicyDescription(Types::allow, "allow"), + PolicyDescription(Types::bucket, "bucket"), + }; + + auto response = std::make_shared(descriptions, DB_CORRUPTED, SN::max); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -133,7 +155,7 @@ TEST(ProtocolAdmin, DescriptionListResponseBinary01) { PolicyDescription(Types::allow, "allow"), }; - auto response = std::make_shared(descriptions, SN::min); + auto response = std::make_shared(descriptions, DB_OK, SN::min); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -143,7 +165,7 @@ TEST(ProtocolAdmin, DescriptionListResponseBinary02) { PolicyDescription(Types::bucket, "bucket"), }; - auto response = std::make_shared(descriptions, SN::min_1); + auto response = std::make_shared(descriptions, DB_OK, SN::min_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -153,7 +175,7 @@ TEST(ProtocolAdmin, DescriptionListResponseBinary03) { PolicyDescription(Types::deny, "deny"), }; - auto response = std::make_shared(descriptions, SN::max); + auto response = std::make_shared(descriptions, DB_OK, SN::max); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -163,7 +185,7 @@ TEST(ProtocolAdmin, DescriptionListResponseBinary04) { PolicyDescription(Types::none, "none"), }; - auto response = std::make_shared(descriptions, SN::max_1); + auto response = std::make_shared(descriptions, DB_OK, SN::max_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -173,7 +195,7 @@ TEST(ProtocolAdmin, DescriptionListResponseBinary05) { PolicyDescription(Types::plugin_type, "plugin"), }; - auto response = std::make_shared(descriptions, SN::mid); + auto response = std::make_shared(descriptions, DB_OK, SN::mid); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -189,7 +211,7 @@ TEST(ProtocolAdmin, DescriptionListResponseBinaryMultipleDescriptions) { PolicyDescription(Types::plugin_type, "plugin"), }; - auto response = std::make_shared(descriptions, SN::max_2); + auto response = std::make_shared(descriptions, DB_OK, SN::max_2); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -197,7 +219,24 @@ TEST(ProtocolAdmin, DescriptionListResponseBinaryMultipleDescriptions) { TEST(ProtocolAdmin, DescriptionListResponseBinaryEmptyDescriptions) { std::vector descriptions; - auto response = std::make_shared(descriptions, SN::min_2); + auto response = std::make_shared(descriptions, DB_OK, SN::min_2); + auto protocol = std::make_shared(); + binaryTestResponse(response, protocol); +} + +/** + * @brief Verify if DescriptionListResponse is properly (de)serialized while database is corrupted + * @test Expected result: + * - descriptions vector contains predefined policies ALLOW and DENY + * - dbCorrupted flag set to true (DB_CORRUPTED) + */ +TEST(ProtocolAdmin, DescriptionListResponseBinaryDatabaseCorrupted) { + std::vector descriptions = { + PolicyDescription(Types::allow, "allow"), + PolicyDescription(Types::bucket, "bucket"), + }; + + auto response = std::make_shared(descriptions, DB_CORRUPTED, SN::max); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } diff --git a/test/common/protocols/admin/listresponse.cpp b/test/common/protocols/admin/listresponse.cpp index fddd2ca..67ea03d 100644 --- a/test/common/protocols/admin/listresponse.cpp +++ b/test/common/protocols/admin/listresponse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2015 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. @@ -16,6 +16,7 @@ /** * @file test/common/protocols/admin/listresponse.cpp * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief Tests for Cynara::ListResponse usage in Cynara::ProtocolAdmin */ @@ -37,10 +38,13 @@ template<> void compare(const Cynara::ListResponse &resp1, const Cynara::ListResponse &resp2) { EXPECT_EQ(resp1.policies(), resp2.policies()); EXPECT_EQ(resp1.isBucketValid(), resp2.isBucketValid()); + EXPECT_EQ(resp1.isDbCorrupted(), resp2.isDbCorrupted()); } static const bool VALID_BUCKET = true; static const bool NO_BUCKET = false; +static const bool DB_OK = false; +static const bool DB_CORRUPTED = true; } /* namespace anonymous */ @@ -55,7 +59,7 @@ TEST(ProtocolAdmin, ListResponse01) { Policy(Keys::k_nun, Results::allow), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -65,7 +69,7 @@ TEST(ProtocolAdmin, ListResponse02) { Policy(Keys::k_cup, Results::deny), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min_1); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min_1); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -75,7 +79,7 @@ TEST(ProtocolAdmin, ListResponse03) { Policy(Keys::k_www, Results::bucket_empty), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min_2); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min_2); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -85,7 +89,7 @@ TEST(ProtocolAdmin, ListResponse04) { Policy(Keys::k_wuw, Results::bucket_not_empty), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::max); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::max); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -95,7 +99,7 @@ TEST(ProtocolAdmin, ListResponse05) { Policy(Keys::k_aaa, Results::none), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::max_1); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::max_1); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -105,7 +109,7 @@ TEST(ProtocolAdmin, ListResponse06) { Policy(Keys::k_wua, Results::plugin_1), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::max_2); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::max_2); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -115,7 +119,7 @@ TEST(ProtocolAdmin, ListResponse07) { Policy(Keys::k_nua, Results::plugin_2), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::mid); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::mid); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -131,7 +135,7 @@ TEST(ProtocolAdmin, ListResponseMultiplePolicies) { Policy(Keys::k_nua, Results::plugin_2), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -139,7 +143,7 @@ TEST(ProtocolAdmin, ListResponseMultiplePolicies) { TEST(ProtocolAdmin, ListResponseEmptyPolicies) { std::vector policies; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min_1); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min_1); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -147,7 +151,22 @@ TEST(ProtocolAdmin, ListResponseEmptyPolicies) { TEST(ProtocolAdmin, ListResponseNoBucket) { std::vector policies; - auto response = std::make_shared(policies, NO_BUCKET, SN::min_2); + auto response = std::make_shared(policies, NO_BUCKET, DB_OK, SN::min_2); + auto protocol = std::make_shared(); + testResponse(response, protocol); +} + +/** + * @brief Verify if ListResponse is properly (de)serialized while database is corrupted + * @test Expected result: + * - policies vector is empty + * - bucketValid flag set to false (NO_BUCKET) + * - dbCorrupted flag set to true (DB_CORRUPTED) + */ +TEST(ProtocolAdmin, ListResponseDatabaseCorrupted) { + std::vector policies; + + auto response = std::make_shared(policies, NO_BUCKET, DB_CORRUPTED, SN::max); auto protocol = std::make_shared(); testResponse(response, protocol); } @@ -159,7 +178,7 @@ TEST(ProtocolAdmin, ListResponseBinary01) { Policy(Keys::k_nun, Results::allow), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -169,7 +188,7 @@ TEST(ProtocolAdmin, ListResponseBinary02) { Policy(Keys::k_cup, Results::deny), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min_1); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -179,7 +198,7 @@ TEST(ProtocolAdmin, ListResponseBinary03) { Policy(Keys::k_www, Results::bucket_empty), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min_2); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min_2); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -189,7 +208,7 @@ TEST(ProtocolAdmin, ListResponseBinary04) { Policy(Keys::k_wuw, Results::bucket_not_empty), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::max); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::max); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -199,7 +218,7 @@ TEST(ProtocolAdmin, ListResponseBinary05) { Policy(Keys::k_aaa, Results::none), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::max_1); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::max_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -209,7 +228,7 @@ TEST(ProtocolAdmin, ListResponseBinary06) { Policy(Keys::k_wua, Results::plugin_1), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::max_2); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::max_2); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -219,7 +238,7 @@ TEST(ProtocolAdmin, ListResponseBinary07) { Policy(Keys::k_nua, Results::plugin_2), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::mid); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::mid); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -235,7 +254,7 @@ TEST(ProtocolAdmin, ListResponseBinaryMultiplePolicies) { Policy(Keys::k_nua, Results::plugin_2), }; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -243,7 +262,7 @@ TEST(ProtocolAdmin, ListResponseBinaryMultiplePolicies) { TEST(ProtocolAdmin, ListResponseBinaryEmptyPolicies) { std::vector policies; - auto response = std::make_shared(policies, VALID_BUCKET, SN::min_1); + auto response = std::make_shared(policies, VALID_BUCKET, DB_OK, SN::min_1); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } @@ -251,7 +270,22 @@ TEST(ProtocolAdmin, ListResponseBinaryEmptyPolicies) { TEST(ProtocolAdmin, ListResponseBinaryNoBucket) { std::vector policies; - auto response = std::make_shared(policies, NO_BUCKET, SN::min_2); + auto response = std::make_shared(policies, NO_BUCKET, DB_OK, SN::min_2); + auto protocol = std::make_shared(); + binaryTestResponse(response, protocol); +} + +/** + * @brief Verify if ListResponse is properly (de)serialized while database is corrupted + * @test Expected result: + * - policies vector is empty + * - bucketValid flag set to false (NO_BUCKET) + * - dbCorrupted flag set to true (DB_CORRUPTED) + */ +TEST(ProtocolAdmin, ListResponseBinaryDatabaseCorrupted) { + std::vector policies; + + auto response = std::make_shared(policies, NO_BUCKET, DB_CORRUPTED, SN::max); auto protocol = std::make_shared(); binaryTestResponse(response, protocol); } -- 2.7.4