From 72e607fbb64c97179afe6862c1ed7770ec8f8799 Mon Sep 17 00:00:00 2001 From: Lukasz Wojciechowski Date: Thu, 18 Dec 2014 14:04:09 +0100 Subject: [PATCH] Implement erasePolicies() in Storage Implementation add StorageBackend::erasePolicies() in base class and its implementation in InMemoryStorageBackend. In Storage class erasePolicies() just passes this request to backend. PolicyBucket class was enhanced with getSubBuckets() method returning set of all IDs of subbuckets. Subbucket is a bucket that can be reached with policy (type==BUCKET) from current bucket. Change-Id: I90598aa916857a917d911068da1a1c18c69391a5 --- src/common/types/PolicyBucket.cpp | 10 + src/common/types/PolicyBucket.h | 3 + src/storage/InMemoryStorageBackend.cpp | 23 ++ src/storage/InMemoryStorageBackend.h | 2 + src/storage/Storage.cpp | 5 + src/storage/Storage.h | 2 + src/storage/StorageBackend.h | 2 + test/common/protocols/TestDataCollection.h | 12 +- .../inmemeorystoragebackendfixture.h | 83 +++++++ .../inmemorystoragebackend.cpp | 245 +++++++++++++++++++++ test/storage/storage/fakestoragebackend.h | 2 + 11 files changed, 388 insertions(+), 1 deletion(-) diff --git a/src/common/types/PolicyBucket.cpp b/src/common/types/PolicyBucket.cpp index 052ac35..a25c274 100644 --- a/src/common/types/PolicyBucket.cpp +++ b/src/common/types/PolicyBucket.cpp @@ -99,6 +99,16 @@ PolicyBucket::Policies PolicyBucket::listPolicies(const PolicyKey &filter) const return policies; } +PolicyBucket::BucketIds PolicyBucket::getSubBuckets(void) const { + PolicyBucket::BucketIds buckets; + for (auto iter = m_policyCollection.begin(); iter != m_policyCollection.end(); ++iter) { + auto &policyPtr = iter->second; + if (policyPtr->result().policyType() == PredefinedPolicyType::BUCKET) + buckets.insert(policyPtr->result().metadata()); + } + return buckets; +} + PolicyMap PolicyBucket::makePolicyMap(const PolicyCollection &policies) { PolicyMap result; for (const auto &policy : policies) { diff --git a/src/common/types/PolicyBucket.h b/src/common/types/PolicyBucket.h index 262c7d3..62cdf5f 100644 --- a/src/common/types/PolicyBucket.h +++ b/src/common/types/PolicyBucket.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -49,6 +50,7 @@ public: typedef PolicyCollection::value_type value_type; typedef const_policy_iterator const_iterator; typedef std::vector Policies; + typedef std::set BucketIds; // TODO: Review usefulness of ctors //delete default constructor in order to prevent creation of buckets with no id @@ -65,6 +67,7 @@ public: void insertPolicy(PolicyPtr policy); void deletePolicy(const PolicyKey &key); Policies listPolicies(const PolicyKey &filter) const; + BucketIds getSubBuckets(void) const; // TODO: Try to change interface, so this method is not needed void deletePolicy(std::function predicate); diff --git a/src/storage/InMemoryStorageBackend.cpp b/src/storage/InMemoryStorageBackend.cpp index bc3d01a..f5bd505 100644 --- a/src/storage/InMemoryStorageBackend.cpp +++ b/src/storage/InMemoryStorageBackend.cpp @@ -211,6 +211,29 @@ PolicyBucket::Policies InMemoryStorageBackend::listPolicies(const PolicyBucketId } } +void InMemoryStorageBackend::erasePolicies(const PolicyBucketId &bucketId, bool recursive, + const PolicyKey &filter) { + PolicyBucket::BucketIds bucketIds = {bucketId}; + + while (!bucketIds.empty()) { + auto it = bucketIds.begin(); + PolicyBucketId bucketId = *it; + bucketIds.erase(it); + try { + auto &policyBucket = buckets().at(bucketId); + if (recursive) { + auto subBuckets = policyBucket.getSubBuckets(); + bucketIds.insert(subBuckets.begin(), subBuckets.end()); + } + policyBucket.deletePolicy([&filter] (PolicyPtr policy) -> bool { + return policy->key().matchFilter(filter); + }); + } catch (const std::out_of_range &) { + throw BucketNotExistsException(bucketId); + } + } +} + void InMemoryStorageBackend::openFileStream(std::shared_ptr stream, const std::string &filename) { // TODO: Consider adding exceptions to streams and handling them: diff --git a/src/storage/InMemoryStorageBackend.h b/src/storage/InMemoryStorageBackend.h index 07f7fcd..872486b 100644 --- a/src/storage/InMemoryStorageBackend.h +++ b/src/storage/InMemoryStorageBackend.h @@ -60,6 +60,8 @@ public: virtual void deleteLinking(const PolicyBucketId &bucketId); virtual PolicyBucket::Policies listPolicies(const PolicyBucketId &bucketId, const PolicyKey &filter) const; + virtual void erasePolicies(const PolicyBucketId &bucketId, bool recursive, + const PolicyKey &filter); protected: InMemoryStorageBackend() {} diff --git a/src/storage/Storage.cpp b/src/storage/Storage.cpp index fafb374..3fe5c7b 100644 --- a/src/storage/Storage.cpp +++ b/src/storage/Storage.cpp @@ -159,6 +159,11 @@ PolicyBucket::Policies Storage::listPolicies(const PolicyBucketId &bucketId, return m_backend.listPolicies(bucketId, filter); } +void Storage::erasePolicies(const PolicyBucketId &bucketId, bool recursive, + const PolicyKey &filter) { + return m_backend.erasePolicies(bucketId, recursive, filter); +} + void Storage::load(void) { m_backend.load(); } diff --git a/src/storage/Storage.h b/src/storage/Storage.h index 838bdb7..0893974 100644 --- a/src/storage/Storage.h +++ b/src/storage/Storage.h @@ -56,6 +56,8 @@ public: PolicyBucket::Policies listPolicies(const PolicyBucketId &bucketId, const PolicyKey &filter) const; + void erasePolicies(const PolicyBucketId &bucketId, bool recursive, const PolicyKey &filter); + void load(void); void save(void); diff --git a/src/storage/StorageBackend.h b/src/storage/StorageBackend.h index 5bb4e61..10430e2 100644 --- a/src/storage/StorageBackend.h +++ b/src/storage/StorageBackend.h @@ -54,6 +54,8 @@ public: virtual void deleteLinking(const PolicyBucketId &bucket) = 0; virtual PolicyBucket::Policies listPolicies(const PolicyBucketId &bucketId, const PolicyKey &filter) const = 0; + virtual void erasePolicies(const PolicyBucketId &bucketId, bool recursive, + const PolicyKey &filter) = 0; virtual void load(void) = 0; virtual void save(void) = 0; }; diff --git a/test/common/protocols/TestDataCollection.h b/test/common/protocols/TestDataCollection.h index cf7c323..bb0bc13 100644 --- a/test/common/protocols/TestDataCollection.h +++ b/test/common/protocols/TestDataCollection.h @@ -39,13 +39,23 @@ typedef Cynara::PolicyKeyFeature PKF; namespace Keys { static const Cynara::PolicyKey k_nun(PKF::create(""), PKF::create("u"), PKF::create("")); static const Cynara::PolicyKey k_cup(PKF::create("c"), PKF::create("u"), PKF::create("p")); - static const Cynara::PolicyKey k_www(PKF::createWildcard(), PKF::createWildcard(), + static const Cynara::PolicyKey k_wup(PKF::createWildcard(), PKF::create("u"), PKF::create("p")); + static const Cynara::PolicyKey k_cwp(PKF::create("c"), PKF::createWildcard(), PKF::create("p")); + static const Cynara::PolicyKey k_cuw(PKF::create("c"), PKF::create("u"), PKF::createWildcard()); + static const Cynara::PolicyKey k_cww(PKF::create("c"), PKF::createWildcard(), PKF::createWildcard()); static const Cynara::PolicyKey k_wuw(PKF::createWildcard(), PKF::create("u"), PKF::createWildcard()); + static const Cynara::PolicyKey k_wwp(PKF::createWildcard(), PKF::createWildcard(), + PKF::create("p")); + static const Cynara::PolicyKey k_www(PKF::createWildcard(), PKF::createWildcard(), + PKF::createWildcard()); static const Cynara::PolicyKey k_aaa(PKF::createAny(), PKF::createAny(), PKF::createAny()); static const Cynara::PolicyKey k_wua(PKF::createWildcard(), PKF::create("u"), PKF::createAny()); static const Cynara::PolicyKey k_nua(PKF::create(""), PKF::create("u"), PKF::createAny()); + static const Cynara::PolicyKey k_wwa(PKF::createWildcard(), PKF::createWildcard(), + PKF::createAny()); + static const Cynara::PolicyKey k_aup(PKF::createAny(), PKF::create("u"), PKF::create("p")); } /* namespace Keys */ namespace SN { diff --git a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h index 2ec2920..b671a0d 100644 --- a/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h +++ b/test/storage/inmemorystoragebackend/inmemeorystoragebackendfixture.h @@ -26,12 +26,38 @@ #include #include +#include +#include + #include #include +#include #include #include +#include + class InMemeoryStorageBackendFixture : public ::testing::Test { +public: + class Filter { + public: + Filter(const std::string &name, const Cynara::PolicyKey &key) : + m_name(name), m_key(key) {} + + const std::string &name(void) const { + return m_name; + } + + const Cynara::PolicyKey &key(void) const { + return m_key; + } + + private: + std::string m_name; + Cynara::PolicyKey m_key; + }; + + typedef std::vector Filters; protected: Cynara::Buckets::mapped_type & @@ -65,10 +91,67 @@ protected: virtual ~InMemeoryStorageBackendFixture() {} + const Cynara::PolicyCollection &fullPoliciesCollection(void) { + using Cynara::PredefinedPolicyType::ALLOW; + if (m_fullPolicyCollection.empty()) { + m_fullPolicyCollection = { + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_cup, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_wup, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_cwp, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_cuw, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_cww, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_wuw, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_wwp, ALLOW), + Cynara::Policy::simpleWithKey(TestDataCollection::Keys::k_www, ALLOW), + }; + } + return m_fullPolicyCollection; + } + + const Filters &filters(void) { + if (m_filters.empty()) { + m_filters.push_back(Filter("cup", TestDataCollection::Keys::k_cup)); + m_filters.push_back(Filter("www", TestDataCollection::Keys::k_www)); + m_filters.push_back(Filter("wuw", TestDataCollection::Keys::k_wuw)); + m_filters.push_back(Filter("aaa", TestDataCollection::Keys::k_aaa)); + m_filters.push_back(Filter("wua", TestDataCollection::Keys::k_wua)); + m_filters.push_back(Filter("wwa", TestDataCollection::Keys::k_wwa)); + m_filters.push_back(Filter("aup", TestDataCollection::Keys::k_aup)); + } + return m_filters; + } + + const Cynara::PolicyCollection &expectedEraseResult(const std::string &filterName) { + if (m_expectedResults.empty()) { + const Cynara::PolicyCollection &pc = fullPoliciesCollection(); + // "cup", "wup", "cwp", "cuw", "cww", "wuw", "wwp", "www" + m_expectedResults["cup"] = { pc[1], pc[2], pc[3], pc[4], pc[5], pc[6], pc[7]}; + m_expectedResults["www"] = {pc[0], pc[1], pc[2], pc[3], pc[4], pc[5], pc[6], }; + m_expectedResults["wuw"] = {pc[0], pc[1], pc[2], pc[3], pc[4], pc[6], pc[7]}; + m_expectedResults["aaa"] = { }; + m_expectedResults["wua"] = {pc[0], pc[2], pc[3], pc[4], pc[6], pc[7]}; + m_expectedResults["wwa"] = {pc[0], pc[1], pc[2], pc[3], pc[4], pc[5], }; + m_expectedResults["aup"] = { pc[2], pc[3], pc[4], pc[5], pc[6], pc[7]}; + } + + return m_expectedResults[filterName]; + } + + bool isLinkPolicyErased(const std::string &filterName) { + if (filterName == "aaa") + return true; + return false; + } + // TODO: consider defaulting accessor with ON_CALL Cynara::Buckets m_buckets; static const std::string m_indexFileName; static const std::string m_backupFileNameSuffix; + + //erase helper structures + Cynara::PolicyCollection m_fullPolicyCollection; + Filters m_filters; + std::map m_expectedResults; }; diff --git a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp index c0fd640..a684b22 100644 --- a/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp +++ b/test/storage/inmemorystoragebackend/inmemorystoragebackend.cpp @@ -288,3 +288,248 @@ TEST_F(InMemeoryStorageBackendFixture, load_from_backup) { ASSERT_THAT(bucketPolicies, IsEmpty()); } } + +/** + * @brief Erase from non-exiting bucket should throw BucketNotExistsException + * @test Scenario: + * - For different filter keys: + * - try erase policies from "non-existing-bucket" + * - expect BucketNotExistsException is thrown + */ +TEST_F(InMemeoryStorageBackendFixture, erasePoliciesEmptyBase) { + using ::testing::ReturnRef; + + FakeInMemoryStorageBackend backend; + EXPECT_CALL(backend, buckets()) + .WillRepeatedly(ReturnRef(m_buckets)); + + PolicyBucketId bucketId = "non-existing-bucket"; + const bool NON_RECURSIVE = false; + + for (const auto &filter : filters()) { + SCOPED_TRACE(filter.name()); + + EXPECT_THROW(backend.erasePolicies(bucketId, NON_RECURSIVE, filter.key()), + BucketNotExistsException); + } +} + +/** + * @brief Erase from single bucket + * @test Scenario: + * - For different filter keys: + * - create "test-bucket" + * - add all types of policies (fullPoliciesCollection) into "test-bucket" + * - erase policies from "test-bucket" + * - check if received results match expected + */ +TEST_F(InMemeoryStorageBackendFixture, erasePoliciesSingleBucket) { + using ::testing::ReturnRef; + using ::testing::UnorderedElementsAreArray; + + PolicyBucketId bucketId = "test-bucket"; + const bool NON_RECURSIVE = false; + + for (const auto &filter : filters()) { + SCOPED_TRACE(filter.name()); + + FakeInMemoryStorageBackend backend; + EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); + + createBucket(bucketId); + addToBucket(bucketId, fullPoliciesCollection()); + + backend.erasePolicies(bucketId, NON_RECURSIVE, filter.key()); + ASSERT_THAT(m_buckets.at(bucketId), + UnorderedElementsAreArray(expectedEraseResult(filter.name()))); + } +} + +/** + * @brief Verify if recursive erase doesn't propagate to not linked bucket + * @test Scenario: + * - For different filter keys: + * - create "test-bucket" + * - add all types of policies (fullPoliciesCollection) into "test-bucket" + * - create "test-bucket2" + * - add all types of policies (fullPoliciesCollection) into "test-bucket2" + * - erase policies from "test-bucket" + * - check if received results match expected in case of "test-bucket" + * - check if policies in "test-bucket2" remain unaffected + */ +TEST_F(InMemeoryStorageBackendFixture, erasePoliciesRecursiveNotLinkedBuckets) { + using ::testing::ReturnRef; + using ::testing::UnorderedElementsAreArray; + + PolicyBucketId bucketId = "test-bucket"; + PolicyBucketId bucketId2 = "test-bucket2"; + const bool RECURSIVE = true; + + for (const auto &filter : filters()) { + SCOPED_TRACE(filter.name()); + + FakeInMemoryStorageBackend backend; + EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); + + createBucket(bucketId); + addToBucket(bucketId, fullPoliciesCollection()); + + createBucket(bucketId2); + addToBucket(bucketId2, fullPoliciesCollection()); + + backend.erasePolicies(bucketId, RECURSIVE, filter.key()); + ASSERT_THAT(m_buckets.at(bucketId), + UnorderedElementsAreArray(expectedEraseResult(filter.name()))); + ASSERT_THAT(m_buckets.at(bucketId2), + UnorderedElementsAreArray(fullPoliciesCollection())); + } +} + +/** + * @brief Verify if recursive erase does propagate to linked bucket + * @test Scenario: + * - For different filter keys: + * - create "test-bucket" + * - add all types of policies (fullPoliciesCollection) into "test-bucket" + * - create "test-bucket2" + * - add all types of policies (fullPoliciesCollection) into "test-bucket2" + * - add policy linking test buckets + * - erase policies from "test-bucket" + * - check if received results match expected in case of "test-bucket" + * - check if received results match expected in case of "test-bucket2" + */ +TEST_F(InMemeoryStorageBackendFixture, erasePoliciesRecursiveLinkedBuckets) { + using ::testing::ReturnRef; + using ::testing::UnorderedElementsAreArray; + + PolicyBucketId bucketId = "test-bucket"; + PolicyBucketId bucketId2 = "test-bucket2"; + PolicyKey linkKey = Helpers::generatePolicyKey("link"); + PolicyCollection linkPolicy = {Policy::bucketWithKey(linkKey, bucketId2)}; + const bool RECURSIVE = true; + + for (const auto &filter : filters()) { + SCOPED_TRACE(filter.name()); + + FakeInMemoryStorageBackend backend; + EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); + + createBucket(bucketId); + addToBucket(bucketId, fullPoliciesCollection()); + + createBucket(bucketId2); + addToBucket(bucketId2, fullPoliciesCollection()); + + addToBucket(bucketId, linkPolicy); + + backend.erasePolicies(bucketId, RECURSIVE, filter.key()); + + PolicyCollection expectedResult = expectedEraseResult(filter.name()); + if (!isLinkPolicyErased(filter.name())) { + expectedResult.insert(expectedResult.end(), linkPolicy.begin(), linkPolicy.end()); + } + ASSERT_THAT(m_buckets.at(bucketId), + UnorderedElementsAreArray(expectedResult)); + ASSERT_THAT(m_buckets.at(bucketId2), + UnorderedElementsAreArray(expectedEraseResult(filter.name()))); + } +} + +/** + * @brief Verify if recursive erase doesn't propagate backwards to linked bucket + * @test Scenario: + * - For different filter keys: + * - create "test-bucket" + * - add all types of policies (fullPoliciesCollection) into "test-bucket" + * - create "test-bucket2" + * - add all types of policies (fullPoliciesCollection) into "test-bucket2" + * - add policy linking test buckets + * - erase policies from "test-bucket2" + * - check if policies in "test-bucket" remain unaffected + * - check if received results match expected in case of "test-bucket2" + */ +TEST_F(InMemeoryStorageBackendFixture, erasePoliciesRecursiveLinkedBucketsNoBackwardPropagation) { + using ::testing::ReturnRef; + using ::testing::UnorderedElementsAreArray; + + PolicyBucketId bucketId = "test-bucket"; + PolicyBucketId bucketId2 = "test-bucket2"; + PolicyKey linkKey = Helpers::generatePolicyKey("link"); + PolicyCollection linkPolicy = {Policy::bucketWithKey(linkKey, bucketId2)}; + const bool RECURSIVE = true; + + for (const auto &filter : filters()) { + SCOPED_TRACE(filter.name()); + + FakeInMemoryStorageBackend backend; + EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); + + createBucket(bucketId); + addToBucket(bucketId, fullPoliciesCollection()); + + createBucket(bucketId2); + addToBucket(bucketId2, fullPoliciesCollection()); + + addToBucket(bucketId, linkPolicy); + + backend.erasePolicies(bucketId2, RECURSIVE, filter.key()); + + PolicyCollection expectedResult = fullPoliciesCollection(); + expectedResult.insert(expectedResult.end(), linkPolicy.begin(), linkPolicy.end()); + + ASSERT_THAT(m_buckets.at(bucketId), + UnorderedElementsAreArray(expectedResult)); + ASSERT_THAT(m_buckets.at(bucketId2), + UnorderedElementsAreArray(expectedEraseResult(filter.name()))); + } +} + +/** + * @brief Verify if non-recursive erase doesn't propagate to linked bucket + * @test Scenario: + * - For different filter keys: + * - create "test-bucket" + * - add all types of policies (fullPoliciesCollection) into "test-bucket" + * - create "test-bucket2" + * - add all types of policies (fullPoliciesCollection) into "test-bucket2" + * - add policy linking test buckets + * - erase policies from "test-bucket" + * - check if received results match expected in case of "test-bucket" + * - check if policies in "test-bucket2" remain unaffected + */ +TEST_F(InMemeoryStorageBackendFixture, erasePoliciesNonRecursiveLinkedBuckets) { + using ::testing::ReturnRef; + using ::testing::UnorderedElementsAreArray; + + PolicyBucketId bucketId = "test-bucket"; + PolicyBucketId bucketId2 = "test-bucket2"; + PolicyKey linkKey = Helpers::generatePolicyKey("link"); + PolicyCollection linkPolicy = {Policy::bucketWithKey(linkKey, bucketId2)}; + const bool NON_RECURSIVE = false; + + for (const auto &filter : filters()) { + SCOPED_TRACE(filter.name()); + + FakeInMemoryStorageBackend backend; + EXPECT_CALL(backend, buckets()).WillRepeatedly(ReturnRef(m_buckets)); + + createBucket(bucketId); + addToBucket(bucketId, fullPoliciesCollection()); + + createBucket(bucketId2); + addToBucket(bucketId2, fullPoliciesCollection()); + + addToBucket(bucketId, linkPolicy); + + backend.erasePolicies(bucketId, NON_RECURSIVE, filter.key()); + + PolicyCollection expectedResult = expectedEraseResult(filter.name()); + if (!isLinkPolicyErased(filter.name())) { + expectedResult.insert(expectedResult.end(), linkPolicy.begin(), linkPolicy.end()); + } + ASSERT_THAT(m_buckets.at(bucketId), + UnorderedElementsAreArray(expectedResult)); + ASSERT_THAT(m_buckets.at(bucketId2), + UnorderedElementsAreArray(fullPoliciesCollection())); + } +} diff --git a/test/storage/storage/fakestoragebackend.h b/test/storage/storage/fakestoragebackend.h index 86bfd30..0aefd60 100644 --- a/test/storage/storage/fakestoragebackend.h +++ b/test/storage/storage/fakestoragebackend.h @@ -44,6 +44,8 @@ public: MOCK_METHOD2(insertPolicy, void(const PolicyBucketId &bucketId, PolicyPtr policy)); MOCK_CONST_METHOD2(listPolicies, PolicyBucket::Policies(const PolicyBucketId &bucketId, const PolicyKey &filter)); + MOCK_METHOD3(erasePolicies, void(const PolicyBucketId &bucketId, bool recursive, + const PolicyKey &filter)); }; #endif /* FAKESTORAGEBACKEND_H_ */ -- 2.7.4