From 0bb80318f3edc278902bc5ff54c497ac1882be57 Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Fri, 16 Jan 2015 11:16:56 +0100 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 13/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 From afb5c6f014f2d5b8250b0d0b65377eb8a3fca67c Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Tue, 3 Mar 2015 12:35:48 +0100 Subject: [PATCH 14/16] Handle information about database corruption DatabaseCorruptedException will be thrown to inform about database corruption. This may be handled directly (OfflineLogic) or through responses returned from service logic (OnlineLogic). In both cases proper error code should be returned: CYNARA_API_DATABASE_CORRUPTED (introduced in this patch). Change-Id: Idbafcf241c6689dadd3c5a7f25adc4629ea7cffd --- src/admin/logic/OfflineLogic.cpp | 14 +++++++ src/admin/logic/OnlineLogic.cpp | 19 +++++++++ src/common/error/api.cpp | 3 ++ src/common/exceptions/DatabaseCorruptedException.h | 47 ++++++++++++++++++++++ src/include/cynara-error.h | 3 ++ 5 files changed, 86 insertions(+) create mode 100644 src/common/exceptions/DatabaseCorruptedException.h diff --git a/src/admin/logic/OfflineLogic.cpp b/src/admin/logic/OfflineLogic.cpp index 2a7bc89..98721bc 100644 --- a/src/admin/logic/OfflineLogic.cpp +++ b/src/admin/logic/OfflineLogic.cpp @@ -17,6 +17,7 @@ * @file src/admin/logic/OfflineLogic.cpp * @author Aleksander Zdyb * @author Lukasz Wojciechowski + * @author Pawel Wieczorek * @version 1.0 * @brief This file contains implementation of OfflineLogic class */ @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +92,8 @@ int OfflineLogic::setPolicies(const ApiInterface::PoliciesByBucket &insertOrUpda return CYNARA_API_BUCKET_NOT_FOUND; } catch (const DatabaseException &) { return CYNARA_API_OPERATION_FAILED; + } catch (const DatabaseCorruptedException &) { + return CYNARA_API_DATABASE_CORRUPTED; } catch (const UnknownPolicyTypeException &ex) { return CYNARA_API_INVALID_PARAM; } @@ -111,6 +115,8 @@ int OfflineLogic::insertOrUpdateBucket(const PolicyBucketId &bucket, return CYNARA_API_OPERATION_NOT_ALLOWED; } catch (const DatabaseException &) { return CYNARA_API_OPERATION_FAILED; + } catch (const DatabaseCorruptedException &) { + return CYNARA_API_DATABASE_CORRUPTED; } catch (const UnknownPolicyTypeException &ex) { return CYNARA_API_INVALID_PARAM; } @@ -129,6 +135,8 @@ int OfflineLogic::removeBucket(const PolicyBucketId &bucket) { return CYNARA_API_OPERATION_NOT_ALLOWED; } catch (const DatabaseException &) { return CYNARA_API_OPERATION_FAILED; + } catch (const DatabaseCorruptedException &) { + return CYNARA_API_DATABASE_CORRUPTED; } return CYNARA_API_SUCCESS; @@ -141,6 +149,8 @@ int OfflineLogic::adminCheck(const PolicyBucketId &startBucket, bool recursive, result = m_storage->checkPolicy(key, startBucket, recursive); } catch (const BucketNotExistsException &ex) { return CYNARA_API_BUCKET_NOT_FOUND; + } catch (const DatabaseCorruptedException &) { + return CYNARA_API_DATABASE_CORRUPTED; } return CYNARA_API_SUCCESS; @@ -161,6 +171,8 @@ int OfflineLogic::listPolicies(const PolicyBucketId &bucket, const PolicyKey &fi policies = m_storage->listPolicies(bucket, filter); } catch (const BucketNotExistsException &ex) { return CYNARA_API_BUCKET_NOT_FOUND; + } catch (const DatabaseCorruptedException &) { + return CYNARA_API_DATABASE_CORRUPTED; } return CYNARA_API_SUCCESS; @@ -174,6 +186,8 @@ int OfflineLogic::erasePolicies(const PolicyBucketId &startBucket, bool recursiv onPoliciesChanged(); } catch (const BucketNotExistsException &) { return CYNARA_API_BUCKET_NOT_FOUND; + } catch (const DatabaseCorruptedException &) { + return CYNARA_API_DATABASE_CORRUPTED; } return CYNARA_API_SUCCESS; diff --git a/src/admin/logic/OnlineLogic.cpp b/src/admin/logic/OnlineLogic.cpp index 9671111..e8964b0 100644 --- a/src/admin/logic/OnlineLogic.cpp +++ b/src/admin/logic/OnlineLogic.cpp @@ -18,6 +18,7 @@ * @author Lukasz Wojciechowski * @author Aleksander Zdyb * @author Zofia Abramowska + * @author Pawel Wieczorek * @version 1.0 * @brief This file contains implementation of online version of Logic class */ @@ -110,6 +111,9 @@ static int interpretCodeResponse(const CodeResponse::Code &code) { case CodeResponse::Code::FAILED: LOGC("Cynara service answered: Operation failed."); return CYNARA_API_OPERATION_FAILED; + case CodeResponse::Code::DB_CORRUPTED: + LOGC("Cynara service answered: Database is corrupted."); + return CYNARA_API_DATABASE_CORRUPTED; default: LOGE("Unexpected response code from server: [%d]", static_cast(code)); @@ -157,6 +161,11 @@ int OnlineLogic::adminCheck(const PolicyBucketId &startBucket, bool recursive, c return ret; } + if (adminCheckResponse->isDbCorrupted()) { + LOGC("Cynara service answered: Database is corrupted."); + return CYNARA_API_DATABASE_CORRUPTED; + } + LOGD("AdminCheckResponse: policyType [%" PRIu16 "], metadata <%s>, bucketValid [%d]", adminCheckResponse->result().policyType(), adminCheckResponse->result().metadata().c_str(), static_cast(adminCheckResponse->isBucketValid())); @@ -179,6 +188,11 @@ int OnlineLogic::listPolicies(const PolicyBucketId &bucket, const PolicyKey &fil return ret; } + if (listResponse->isDbCorrupted()) { + LOGC("Cynara service answered: Database is corrupted."); + return CYNARA_API_DATABASE_CORRUPTED; + } + LOGD("listResponse: number of policies [%zu], bucketValid [%d]", listResponse->policies().size(), listResponse->isBucketValid()); @@ -208,6 +222,11 @@ int OnlineLogic::listDescriptions(std::vector &descriptions) return ret; } + if (descrResponse->isDbCorrupted()) { + LOGC("Cynara service answered: Database is corrupted."); + return CYNARA_API_DATABASE_CORRUPTED; + } + LOGD("descriptionListResponse: number of plugin descriptions [%zu]", descrResponse->descriptions().size()); diff --git a/src/common/error/api.cpp b/src/common/error/api.cpp index d8243b4..802787c 100644 --- a/src/common/error/api.cpp +++ b/src/common/error/api.cpp @@ -82,6 +82,9 @@ int cynara_strerror(int errnum, char *buf, size_t buflen) { case CYNARA_API_BUFFER_TOO_SHORT: message = "Buffer too short"; break; + case CYNARA_API_DATABASE_CORRUPTED: + message = "Database corrupted"; + break; } if (message == nullptr) diff --git a/src/common/exceptions/DatabaseCorruptedException.h b/src/common/exceptions/DatabaseCorruptedException.h new file mode 100644 index 0000000..960125a --- /dev/null +++ b/src/common/exceptions/DatabaseCorruptedException.h @@ -0,0 +1,47 @@ +/* + * 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/DatabaseCorruptedException.h + * @author Pawel Wieczorek + * @version 1.0 + * @brief Implementation of DatabaseCorruptedException + */ + +#ifndef SRC_COMMON_EXCEPTIONS_DATABASECORRUPTEDEXCEPTION_H_ +#define SRC_COMMON_EXCEPTIONS_DATABASECORRUPTEDEXCEPTION_H_ + +#include + +#include "Exception.h" + +namespace Cynara { + +class DatabaseCorruptedException : public Exception { +public: + DatabaseCorruptedException() : m_message("DatabaseCorruptedException") {} + virtual ~DatabaseCorruptedException() {}; + + virtual const std::string &message(void) const { + return m_message; + } + +private: + std::string m_message; +}; + +} /* namespace Cynara */ + +#endif /* SRC_COMMON_EXCEPTIONS_DATABASECORRUPTEDEXCEPTION_H_ */ diff --git a/src/include/cynara-error.h b/src/include/cynara-error.h index 875d877..988b462 100644 --- a/src/include/cynara-error.h +++ b/src/include/cynara-error.h @@ -84,6 +84,9 @@ /*! \brief indicating that provided buffer is too short */ #define CYNARA_API_BUFFER_TOO_SHORT -13 + +/*! \brief indicating that database is corrupted */ +#define CYNARA_API_DATABASE_CORRUPTED -14 /** @}*/ #ifdef __cplusplus -- 2.7.4 From f61768e10f54e40dda71834037155bd9a3b42a4b Mon Sep 17 00:00:00 2001 From: Pawel Wieczorek Date: Tue, 3 Mar 2015 12:36:13 +0100 Subject: [PATCH 15/16] Handle database corruption Loading database from storage is now moved to logic layer. InMemoryStorageBackend throws DatabaseCorruptedException to trigger corrupted state toggle when database corruption is detected. Tests which involved database to enter corrupted state were updated. Change-Id: I0df8c38322b4478abb8c6d18c0805f97cfa45161 --- src/service/logic/Logic.cpp | 9 +++++++++ src/service/logic/Logic.h | 1 + src/service/main/Cynara.cpp | 6 ++++-- src/storage/InMemoryStorageBackend.cpp | 4 +++- .../inmemeorystoragebackendfixture.h | 7 +++++++ .../inmemorystoragebackend/inmemorystoragebackend.cpp | 19 +++++++------------ 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/service/logic/Logic.cpp b/src/service/logic/Logic.cpp index a6e3c12..44fcd97 100644 --- a/src/service/logic/Logic.cpp +++ b/src/service/logic/Logic.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -526,4 +527,12 @@ void Logic::handleClientDisconnection(const CheckContextPtr &checkContextPtr) { } } +void Logic::loadDb(void) { + try { + m_storage->load(); + } catch (const DatabaseCorruptedException &) { + m_dbCorrupted = true; + } +} + } // namespace Cynara diff --git a/src/service/logic/Logic.h b/src/service/logic/Logic.h index 3e8ee8c..4f95d30 100644 --- a/src/service/logic/Logic.h +++ b/src/service/logic/Logic.h @@ -88,6 +88,7 @@ public: virtual void execute(RequestContextPtr context, SimpleCheckRequestPtr request); virtual void contextClosed(RequestContextPtr context); + virtual void loadDb(void); private: AgentManagerPtr m_agentManager; diff --git a/src/service/main/Cynara.cpp b/src/service/main/Cynara.cpp index 15ebd99..d43e4a4 100644 --- a/src/service/main/Cynara.cpp +++ b/src/service/main/Cynara.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/service/main/Cynara.cpp * @author Lukasz Wojciechowski * @author Aleksander Zdyb + * @author Pawel Wieczorek * @version 1.0 * @brief This file implements main class of cynara service */ @@ -65,7 +66,8 @@ void Cynara::init(void) { m_socketManager->bindLogic(m_logic); m_databaseLock.lock(); // Wait until database lock can be acquired - m_storage->load(); + m_logic->loadDb(); + m_pluginManager->loadPlugins(); } diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index 57a4b56..141f006 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -16,6 +16,7 @@ /** * @file src/storage/InMemoryStorageBackend.cpp * @author Aleksander Zdyb + * @author Pawel Wieczorek * @version 1.0 * @brief Implementation of InMemoryStorageBackend */ @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -91,7 +93,7 @@ void InMemoryStorageBackend::load(void) { } catch (const DatabaseException &) { LOGC("Reading cynara database failed."); buckets().clear(); - // TODO: Implement emergency mode toggle + throw DatabaseCorruptedException(); } m_checksum.clear(); diff --git a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h index fbc523b..4bcd05d 100644 --- a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h +++ b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h @@ -16,6 +16,7 @@ /** * @file test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h * @author Aleksander Zdyb + * @author Pawel Wieczorek * @version 1.0 * @brief Fixture for InMemeoryStorageBackend tests */ @@ -89,6 +90,12 @@ protected: ASSERT_EQ(Cynara::PredefinedPolicyType::DENY, defaultBucket.defaultPolicy()); } + static void ASSERT_DB_EMPTY(Cynara::Buckets &buckets) { + using ::testing::IsEmpty; + ASSERT_EQ(0, buckets.size()); + ASSERT_THAT(buckets, IsEmpty()); + } + virtual ~InMemeoryStorageBackendFixture() {} const Cynara::PolicyCollection &fullPoliciesCollection(void) { diff --git a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp index 1c88335..64091b6 100644 --- a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp +++ b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp @@ -25,6 +25,7 @@ #include "exceptions/BucketNotExistsException.h" #include "exceptions/BucketDeserializationException.h" +#include "exceptions/DatabaseCorruptedException.h" #include "exceptions/DefaultBucketDeletionException.h" #include "exceptions/FileNotFoundException.h" #include "storage/InMemoryStorageBackend.h" @@ -196,25 +197,21 @@ TEST_F(InMemeoryStorageBackendFixture, deletePolicyFromNonexistentBucket) { // Database dir is empty TEST_F(InMemeoryStorageBackendFixture, load_no_db) { using ::testing::ReturnRef; - using ::testing::Return; auto testDbPath = std::string(CYNARA_TESTS_DIR) + "/empty_db/"; FakeInMemoryStorageBackend backend(testDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); - EXPECT_CALL(backend, postLoadCleanup(false)).WillOnce(Return()); - backend.load(); - ASSERT_DB_VIRGIN(m_buckets); + EXPECT_THROW(backend.load(), DatabaseCorruptedException); + ASSERT_DB_EMPTY(m_buckets); } // Database dir contains index with default bucket, but no file for this bucket TEST_F(InMemeoryStorageBackendFixture, load_no_default) { using ::testing::ReturnRef; - using ::testing::Return; auto testDbPath = std::string(CYNARA_TESTS_DIR) + "/db2/"; FakeInMemoryStorageBackend backend(testDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); - EXPECT_CALL(backend, postLoadCleanup(false)).WillOnce(Return()); - backend.load(); - ASSERT_DB_VIRGIN(m_buckets); + EXPECT_THROW(backend.load(), DatabaseCorruptedException); + ASSERT_DB_EMPTY(m_buckets); } // Database contains index with default bucket and an empty bucket file @@ -258,13 +255,11 @@ TEST_F(InMemeoryStorageBackendFixture, load_2_buckets) { // Database contains index with 2 buckets; 1st bucket is valid, but second is corrupted TEST_F(InMemeoryStorageBackendFixture, second_bucket_corrupted) { using ::testing::ReturnRef; - using ::testing::Return; auto testDbPath = std::string(CYNARA_TESTS_DIR) + "/db5/"; FakeInMemoryStorageBackend backend(testDbPath); EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); - EXPECT_CALL(backend, postLoadCleanup(false)).WillOnce(Return()); - backend.load(); - ASSERT_DB_VIRGIN(m_buckets); + EXPECT_THROW(backend.load(), DatabaseCorruptedException); + ASSERT_DB_EMPTY(m_buckets); } /** -- 2.7.4 From f33f7bec830b3c4492d556cd54f63af95ad715c1 Mon Sep 17 00:00:00 2001 From: Aleksander Zdyb Date: Thu, 15 Jan 2015 11:27:42 +0100 Subject: [PATCH 16/16] Add tests utilizing short command-line options The tests are automagically generated from their longer sisters' bodies. Change-Id: I1e50314ddee74aaaba42dc29a04c26f237128996 --- test/CMakeLists.txt | 1 + test/cyad/commandline_short.cpp | 253 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 test/cyad/commandline_short.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 189268d..25a70db 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,6 +86,7 @@ SET(CYNARA_TESTS_SOURCES credsCommons/parser/Parser.cpp cyad/commandline.cpp cyad/commandline_errors.cpp + cyad/commandline_short.cpp cyad/commands_dispatcher.cpp cyad/helpers.cpp cyad/policy_collection.cpp diff --git a/test/cyad/commandline_short.cpp b/test/cyad/commandline_short.cpp new file mode 100644 index 0000000..357a3dc --- /dev/null +++ b/test/cyad/commandline_short.cpp @@ -0,0 +1,253 @@ +/* + * 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/cyad/commandline_short.cpp + * @author Aleksander Zdyb + * @version 1.0 + * @brief Tests for CyadCommandlineParser (short opts) + */ + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "CyadCommandlineTest.h" + +TEST_F(CyadCommandlineTest, help_short) { + prepare_argv({ "./cyad", "-h" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); +} + +TEST_F(CyadCommandlineTest, deleteBucket_short) { + prepare_argv({ "./cyad", "-d", "bucket" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("bucket", result->bucketId()); +} + +TEST_F(CyadCommandlineTest, setBucket_short) { + prepare_argv({ "./cyad", "-b", "bucket", "-t", "42" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("bucket", result->bucketId()); + ASSERT_EQ("42", result->policyResult().policyType()); + ASSERT_TRUE(result->policyResult().metadata().empty()); +} + +TEST_F(CyadCommandlineTest, setBucketWithMetadata_short) { + const std::string ultimateAnswer = "Answer to The Ultimate Question of Life," + " the Universe, and Everything"; + + prepare_argv({ "./cyad", "-b", "adams", "-t", "42", "-m", ultimateAnswer }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("adams", result->bucketId()); + ASSERT_EQ("42", result->policyResult().policyType()); + ASSERT_EQ(ultimateAnswer, result->policyResult().metadata()); +} + +TEST_F(CyadCommandlineTest, setPolicy_short) { + prepare_argv({ "./cyad", "-s", "-k", "some-bucket", + "-c", "client", "-u", "user", "-p", "privilege", + "-t", "42" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("some-bucket", result->bucketId()); + + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); + ASSERT_EQ("42", result->policyResult().policyType()); + ASSERT_TRUE(result->policyResult().metadata().empty()); +} + +TEST_F(CyadCommandlineTest, setPolicyWithMetadata_short) { + prepare_argv({ "./cyad", "-s", "-k", "some-bucket", + "-c", "client", "-u", "user", "-p", "privilege", + "-t", "42", "-m", "some-metadata" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("some-bucket", result->bucketId()); + + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); + ASSERT_EQ("42", result->policyResult().policyType()); + ASSERT_EQ("some-metadata", result->policyResult().metadata()); +} + +TEST_F(CyadCommandlineTest, setPolicyInDefaultBucket_short) { + prepare_argv({ "./cyad", "-s", "-k", "", + "-c", "client", "-u", "user", "-p", "privilege", + "-t", "ALLOW", "-m", "some-metadata" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("", result->bucketId()); + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); + ASSERT_EQ("ALLOW", result->policyResult().policyType()); + ASSERT_EQ("some-metadata", result->policyResult().metadata()); +} + +TEST_F(CyadCommandlineTest, setPolicyInDefaultBucketNoOption_short) { + prepare_argv({ "./cyad", "-s", + "-c", "client", "-u", "user", "-p", "privilege", + "-t", "ALLOW", "-m", "some-metadata" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("", result->bucketId()); + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); + ASSERT_EQ("ALLOW", result->policyResult().policyType()); + ASSERT_EQ("some-metadata", result->policyResult().metadata()); +} + +TEST_F(CyadCommandlineTest, parsePolicyTypeBase10_short) { + auto parsePolicyType = [] (const char *rawPolicy) -> Cynara::PolicyType { + return Cynara::HumanReadableParser::policyType(rawPolicy); + }; + + ASSERT_EQ(CYNARA_ADMIN_DENY, parsePolicyType("0")); + ASSERT_EQ(CYNARA_ADMIN_NONE, parsePolicyType("1")); + ASSERT_EQ(CYNARA_ADMIN_BUCKET, parsePolicyType("65534")); + ASSERT_EQ(CYNARA_ADMIN_ALLOW, parsePolicyType("65535")); +} + +TEST_F(CyadCommandlineTest, parsePolicyTypeBase16_short) { + auto parsePolicyType = [] (const char *rawPolicy) -> Cynara::PolicyType { + return Cynara::HumanReadableParser::policyType(rawPolicy); + }; + + ASSERT_EQ(CYNARA_ADMIN_DENY, parsePolicyType("0x0")); + ASSERT_EQ(CYNARA_ADMIN_NONE, parsePolicyType("0x1")); + ASSERT_EQ(CYNARA_ADMIN_BUCKET, parsePolicyType("0xFFFE")); + ASSERT_EQ(CYNARA_ADMIN_ALLOW, parsePolicyType("0xFFFF")); +} + +TEST_F(CyadCommandlineTest, parsePolicyTypeHuman_short) { + auto parsePolicyType = [] (const char *rawPolicy) -> Cynara::PolicyType { + return Cynara::HumanReadableParser::policyType(rawPolicy); + }; + + ASSERT_EQ(CYNARA_ADMIN_DENY, parsePolicyType("DENY")); + ASSERT_EQ(CYNARA_ADMIN_NONE, parsePolicyType("NONE")); + ASSERT_EQ(CYNARA_ADMIN_BUCKET, parsePolicyType("BUCKET")); + ASSERT_EQ(CYNARA_ADMIN_ALLOW, parsePolicyType("ALLOW")); +} + +TEST_F(CyadCommandlineTest, setPoliciesBulkFilename_short) { + prepare_argv({ "./cyad", "-s", "-f", "/tmp/input_file" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("/tmp/input_file", result->filename()); +} + +TEST_F(CyadCommandlineTest, setPoliciesBulkStdin_short) { + prepare_argv({ "./cyad", "-s", "-f", "-" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("-", result->filename()); +} + +TEST_F(CyadCommandlineTest, eraseRecursive_short) { + prepare_argv({ "./cyad", "-e", "bucket", "-r", "yes", + "-c", "client", "-u", "user", "-p", "privilege" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("bucket", result->bucketId()); + ASSERT_TRUE(result->recursive()); + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); +} + +TEST_F(CyadCommandlineTest, eraseNonrecursive_short) { + prepare_argv({ "./cyad", "-e", "bucket", "-r", "no", + "-c", "client", "-u", "user", "-p", "privilege" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("bucket", result->bucketId()); + ASSERT_FALSE(result->recursive()); + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); +} + +TEST_F(CyadCommandlineTest, checkDefaultRecursive_short) { + prepare_argv({ "./cyad", "-a", "-r", "no", + "-c", "client", "-u", "user", "-p", "privilege" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ(CYNARA_ADMIN_DEFAULT_BUCKET, result->bucketId()); + ASSERT_FALSE(result->recursive()); + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); +} + +TEST_F(CyadCommandlineTest, listPoliciesDefault_short) { + prepare_argv({ "./cyad", "-l", "", + "-c", "client", "-u", "user", "-p", "privilege" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ(CYNARA_ADMIN_DEFAULT_BUCKET, result->bucketId()); + ASSERT_EQ(Cynara::PolicyKey("client", "user", "privilege"), result->policyKey()); +} + +TEST_F(CyadCommandlineTest, listPoliciesOtherBucket_short) { + prepare_argv({ "./cyad", "-l", "some-bucket", + "-c", "c", "-u", "u", "-p", "p" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast(parser.parseMain()); + ASSERT_NE(nullptr, result); + ASSERT_EQ("some-bucket", result->bucketId()); + ASSERT_EQ(Cynara::PolicyKey("c", "u", "p"), result->policyKey()); +} + +TEST_F(CyadCommandlineTest, listPoliciesDesc_short) { + prepare_argv({ "./cyad", "-g" }); + Cynara::CyadCommandlineParser parser(this->argc(), this->argv()); + + auto result = std::dynamic_pointer_cast( + parser.parseMain()); + ASSERT_NE(nullptr, result); +} -- 2.7.4