aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitaly Buka <vitalybuka@google.com>2016-01-21 15:09:34 -0800
committerVitaly Buka <vitalybuka@google.com>2016-01-21 23:53:01 +0000
commit5a7c4f55a0bdbd9c9e443d5b59414d1705919626 (patch)
tree7cd9fbac4a1bf795df47a2f4f9d1155803f4bf9b
parent484b6e4b808eb57f02253fbf63f117e259471c39 (diff)
downloadlibweave-5a7c4f55a0bdbd9c9e443d5b59414d1705919626.tar.gz
Add black list manager implementation
BUG:25776798 BUG:25777806 BUG:25777658 BUG:25777810 Change-Id: I86ec09e727360c1de4ae9af63f2ff1ab5f9cea1c Reviewed-on: https://weave-review.googlesource.com/2276 Reviewed-by: Vitaly Buka <vitalybuka@google.com>
-rw-r--r--file_lists.mk2
-rw-r--r--src/access_black_list_manager_impl.cc164
-rw-r--r--src/access_black_list_manager_impl.h58
-rw-r--r--src/access_black_list_manager_impl_unittest.cc167
-rw-r--r--src/device_manager.cc6
-rw-r--r--src/device_manager.h4
6 files changed, 401 insertions, 0 deletions
diff --git a/file_lists.mk b/file_lists.mk
index 6356b2b..8dccd9c 100644
--- a/file_lists.mk
+++ b/file_lists.mk
@@ -4,6 +4,7 @@
WEAVE_SRC_FILES := \
src/access_api_handler.cc \
+ src/access_black_list_manager_impl.cc \
src/backoff_entry.cc \
src/base_api_handler.cc \
src/commands/cloud_command_proxy.cc \
@@ -50,6 +51,7 @@ WEAVE_TEST_SRC_FILES := \
WEAVE_UNITTEST_SRC_FILES := \
src/access_api_handler_unittest.cc \
+ src/access_black_list_manager_impl_unittest.cc \
src/backoff_entry_unittest.cc \
src/base_api_handler_unittest.cc \
src/commands/cloud_command_proxy_unittest.cc \
diff --git a/src/access_black_list_manager_impl.cc b/src/access_black_list_manager_impl.cc
new file mode 100644
index 0000000..e6897ba
--- /dev/null
+++ b/src/access_black_list_manager_impl.cc
@@ -0,0 +1,164 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/access_black_list_manager_impl.h"
+
+#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
+#include <base/values.h>
+
+#include "src/commands/schema_constants.h"
+#include "src/data_encoding.h"
+
+namespace weave {
+
+namespace {
+const char kConfigFileName[] = "black_list";
+
+const char kUser[] = "user";
+const char kApp[] = "app";
+const char kExpiration[] = "expiration";
+}
+
+AccessBlackListManagerImpl::AccessBlackListManagerImpl(
+ provider::ConfigStore* store,
+ size_t capacity,
+ base::Clock* clock)
+ : capacity_{capacity}, clock_{clock}, store_{store} {
+ Load();
+}
+
+void AccessBlackListManagerImpl::Load() {
+ if (!store_)
+ return;
+ if (auto list = base::ListValue::From(
+ base::JSONReader::Read(store_->LoadSettings(kConfigFileName)))) {
+ for (const auto& e : *list) {
+ const base::DictionaryValue* entry{nullptr};
+ std::string user;
+ std::string app;
+ decltype(entries_)::key_type key;
+ int expiration;
+ if (e->GetAsDictionary(&entry) && entry->GetString(kUser, &user) &&
+ Base64Decode(user, &key.first) && entry->GetString(kApp, &app) &&
+ Base64Decode(app, &key.second) &&
+ entry->GetInteger(kExpiration, &expiration)) {
+ base::Time expiration_time = base::Time::FromTimeT(expiration);
+ if (expiration_time > clock_->Now())
+ entries_[key] = expiration_time;
+ }
+ }
+ if (entries_.size() < list->GetSize()) {
+ // Save some storage space by saving without expired entries.
+ Save({});
+ }
+ }
+}
+
+void AccessBlackListManagerImpl::Save(const DoneCallback& callback) {
+ if (!store_) {
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ return;
+ }
+
+ base::ListValue list;
+ for (const auto& e : entries_) {
+ scoped_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
+ entry->SetString(kUser, Base64Encode(e.first.first));
+ entry->SetString(kApp, Base64Encode(e.first.second));
+ entry->SetInteger(kExpiration, e.second.ToTimeT());
+ list.Append(std::move(entry));
+ }
+
+ std::string json;
+ base::JSONWriter::Write(list, &json);
+ store_->SaveSettings(kConfigFileName, json, callback);
+}
+
+void AccessBlackListManagerImpl::RemoveExpired() {
+ for (auto i = begin(entries_); i != end(entries_);) {
+ if (i->second <= clock_->Now())
+ i = entries_.erase(i);
+ else
+ ++i;
+ }
+}
+
+void AccessBlackListManagerImpl::Block(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id,
+ const base::Time& expiration,
+ const DoneCallback& callback) {
+ // Iterating is OK as Save below is more expensive.
+ RemoveExpired();
+ if (expiration <= clock_->Now()) {
+ if (!callback.is_null()) {
+ ErrorPtr error;
+ Error::AddTo(&error, FROM_HERE, errors::commands::kDomain,
+ "aleady_expired", "Entry already expired");
+ callback.Run(std::move(error));
+ }
+ return;
+ }
+ if (entries_.size() >= capacity_) {
+ if (!callback.is_null()) {
+ ErrorPtr error;
+ Error::AddTo(&error, FROM_HERE, errors::commands::kDomain,
+ "blacklist_is_full", "Unable to store more entries");
+ callback.Run(std::move(error));
+ }
+ return;
+ }
+ auto& value = entries_[std::make_pair(user_id, app_id)];
+ value = std::max(value, expiration);
+ Save(callback);
+}
+
+void AccessBlackListManagerImpl::Unblock(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id,
+ const DoneCallback& callback) {
+ if (!entries_.erase(std::make_pair(user_id, app_id))) {
+ if (!callback.is_null()) {
+ ErrorPtr error;
+ Error::AddTo(&error, FROM_HERE, errors::commands::kDomain,
+ "entry_not_found", "Unknown entry");
+ callback.Run(std::move(error));
+ }
+ return;
+ }
+ // Iterating is OK as Save below is more expensive.
+ RemoveExpired();
+ Save(callback);
+}
+
+bool AccessBlackListManagerImpl::IsBlocked(
+ const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id) const {
+ for (const auto& user : {{}, user_id}) {
+ for (const auto& app : {{}, app_id}) {
+ auto both = entries_.find(std::make_pair(user, app));
+ if (both != end(entries_) && both->second > clock_->Now())
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<AccessBlackListManager::Entry>
+AccessBlackListManagerImpl::GetEntries() const {
+ std::vector<Entry> result;
+ for (const auto& e : entries_)
+ result.push_back({e.first.first, e.first.second, e.second});
+ return result;
+}
+
+size_t AccessBlackListManagerImpl::GetSize() const {
+ return entries_.size();
+}
+
+size_t AccessBlackListManagerImpl::GetCapacity() const {
+ return capacity_;
+}
+
+} // namespace weave
diff --git a/src/access_black_list_manager_impl.h b/src/access_black_list_manager_impl.h
new file mode 100644
index 0000000..1c175db
--- /dev/null
+++ b/src/access_black_list_manager_impl.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
+#define LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
+
+#include <map>
+#include <utility>
+
+#include <base/time/default_clock.h>
+#include <base/time/time.h>
+#include <weave/error.h>
+#include <weave/provider/config_store.h>
+
+#include "src/access_black_list_manager.h"
+
+namespace weave {
+
+class AccessBlackListManagerImpl : public AccessBlackListManager {
+ public:
+ explicit AccessBlackListManagerImpl(provider::ConfigStore* store,
+ size_t capacity = 1024,
+ base::Clock* clock = nullptr);
+
+ // AccessBlackListManager implementation.
+ void Block(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id,
+ const base::Time& expiration,
+ const DoneCallback& callback) override;
+ void Unblock(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id,
+ const DoneCallback& callback) override;
+ bool IsBlocked(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id) const override;
+ std::vector<Entry> GetEntries() const override;
+ size_t GetSize() const override;
+ size_t GetCapacity() const override;
+
+ private:
+ void Load();
+ void Save(const DoneCallback& callback);
+ void RemoveExpired();
+
+ const size_t capacity_{0};
+ base::DefaultClock default_clock_;
+ base::Clock* clock_{&default_clock_};
+
+ provider::ConfigStore* store_{nullptr};
+ std::map<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>, base::Time>
+ entries_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessBlackListManagerImpl);
+};
+
+} // namespace weave
+
+#endif // LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
diff --git a/src/access_black_list_manager_impl_unittest.cc b/src/access_black_list_manager_impl_unittest.cc
new file mode 100644
index 0000000..2b6d66e
--- /dev/null
+++ b/src/access_black_list_manager_impl_unittest.cc
@@ -0,0 +1,167 @@
+// Copyright 2016 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/access_black_list_manager_impl.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <weave/provider/test/mock_config_store.h>
+#include <weave/test/unittest_utils.h>
+
+#include "src/test/mock_clock.h"
+#include "src/bind_lambda.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace weave {
+
+class AccessBlackListManagerImplTest : public testing::Test {
+ protected:
+ void SetUp() {
+ std::string to_load = R"([{
+ "user": "BQID",
+ "app": "BwQF",
+ "expiration": 1410000000
+ }, {
+ "user": "AQID",
+ "app": "AwQF",
+ "expiration": 1419999999
+ }])";
+
+ EXPECT_CALL(config_store_, LoadSettings("black_list"))
+ .WillOnce(Return(to_load));
+
+ EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+ .WillOnce(testing::WithArgs<1, 2>(testing::Invoke(
+ [](const std::string& json, const DoneCallback& callback) {
+ std::string to_save = R"([{
+ "user": "AQID",
+ "app": "AwQF",
+ "expiration": 1419999999
+ }])";
+ EXPECT_JSON_EQ(to_save, *test::CreateValue(json));
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ })));
+
+ EXPECT_CALL(clock_, Now())
+ .WillRepeatedly(Return(base::Time::FromTimeT(1412121212)));
+ manager_.reset(new AccessBlackListManagerImpl{&config_store_, 10, &clock_});
+ }
+ StrictMock<test::MockClock> clock_;
+ StrictMock<provider::test::MockConfigStore> config_store_{false};
+ std::unique_ptr<AccessBlackListManagerImpl> manager_;
+};
+
+TEST_F(AccessBlackListManagerImplTest, Init) {
+ EXPECT_EQ(1u, manager_->GetSize());
+ EXPECT_EQ(10u, manager_->GetCapacity());
+ EXPECT_EQ((std::vector<AccessBlackListManagerImpl::Entry>{{
+ {1, 2, 3}, {3, 4, 5}, base::Time::FromTimeT(1419999999),
+ }}),
+ manager_->GetEntries());
+}
+
+TEST_F(AccessBlackListManagerImplTest, Block) {
+ EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+ .WillOnce(testing::WithArgs<1, 2>(testing::Invoke(
+ [](const std::string& json, const DoneCallback& callback) {
+ std::string to_save = R"([{
+ "user": "AQID",
+ "app": "AwQF",
+ "expiration": 1419999999
+ }, {
+ "app": "CAgI",
+ "user": "BwcH",
+ "expiration": 1419990000
+ }])";
+ EXPECT_JSON_EQ(to_save, *test::CreateValue(json));
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ })));
+ manager_->Block({7, 7, 7}, {8, 8, 8}, base::Time::FromTimeT(1419990000), {});
+}
+
+TEST_F(AccessBlackListManagerImplTest, BlockExpired) {
+ manager_->Block(
+ {}, {}, base::Time::FromTimeT(1400000000), base::Bind([](ErrorPtr error) {
+ EXPECT_TRUE(error->HasError("command_schema", "aleady_expired"));
+ }));
+}
+
+TEST_F(AccessBlackListManagerImplTest, BlockListIsFull) {
+ EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+ .WillRepeatedly(testing::WithArgs<1, 2>(testing::Invoke(
+ [](const std::string& json, const DoneCallback& callback) {
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ })));
+ for (size_t i = manager_->GetSize(); i < manager_->GetCapacity(); ++i) {
+ manager_->Block(
+ {99, static_cast<uint8_t>(i / 256), static_cast<uint8_t>(i % 256)},
+ {8, 8, 8}, base::Time::FromTimeT(1419990000), {});
+ EXPECT_EQ(i + 1, manager_->GetSize());
+ }
+ manager_->Block(
+ {99}, {8, 8, 8}, base::Time::FromTimeT(1419990000),
+ base::Bind([](ErrorPtr error) {
+ EXPECT_TRUE(error->HasError("command_schema", "blacklist_is_full"));
+ }));
+}
+
+TEST_F(AccessBlackListManagerImplTest, Unblock) {
+ EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+ .WillOnce(testing::WithArgs<1, 2>(testing::Invoke(
+ [](const std::string& json, const DoneCallback& callback) {
+ EXPECT_JSON_EQ("[]", *test::CreateValue(json));
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ })));
+ manager_->Unblock({1, 2, 3}, {3, 4, 5}, {});
+}
+
+TEST_F(AccessBlackListManagerImplTest, UnblockNotFound) {
+ manager_->Unblock(
+ {5, 2, 3}, {5, 4, 5}, base::Bind([](ErrorPtr error) {
+ EXPECT_TRUE(error->HasError("command_schema", "entry_not_found"));
+ }));
+}
+
+TEST_F(AccessBlackListManagerImplTest, IsBlockedFalse) {
+ EXPECT_FALSE(manager_->IsBlocked({7, 7, 7}, {8, 8, 8}));
+}
+
+class AccessBlackListManagerImplIsBlockedTest
+ : public AccessBlackListManagerImplTest,
+ public testing::WithParamInterface<
+ std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>> {
+ public:
+ void SetUp() override {
+ AccessBlackListManagerImplTest::SetUp();
+ EXPECT_CALL(config_store_, SaveSettings("black_list", _, _))
+ .WillOnce(testing::WithArgs<2>(
+ testing::Invoke([](const DoneCallback& callback) {
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ })));
+ manager_->Block(std::get<0>(GetParam()), std::get<1>(GetParam()),
+ base::Time::FromTimeT(1419990000), {});
+ }
+};
+
+TEST_P(AccessBlackListManagerImplIsBlockedTest, IsBlocked) {
+ EXPECT_TRUE(manager_->IsBlocked({7, 7, 7}, {8, 8, 8}));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ Filters,
+ AccessBlackListManagerImplIsBlockedTest,
+ testing::Combine(testing::Values(std::vector<uint8_t>{},
+ std::vector<uint8_t>{7, 7, 7}),
+ testing::Values(std::vector<uint8_t>{},
+ std::vector<uint8_t>{8, 8, 8})));
+
+} // namespace weave
diff --git a/src/device_manager.cc b/src/device_manager.cc
index 1158df7..50818ff 100644
--- a/src/device_manager.cc
+++ b/src/device_manager.cc
@@ -8,6 +8,8 @@
#include <base/bind.h>
+#include "src/access_api_handler.h"
+#include "src/access_black_list_manager_impl.h"
#include "src/base_api_handler.h"
#include "src/commands/schema_constants.h"
#include "src/component_manager_impl.h"
@@ -40,6 +42,10 @@ DeviceManager::DeviceManager(provider::ConfigStore* config_store,
network, auth_manager_.get()));
base_api_handler_.reset(new BaseApiHandler{device_info_.get(), this});
+ black_list_manager_.reset(new AccessBlackListManagerImpl{config_store});
+ access_api_handler_.reset(
+ new AccessApiHandler{this, black_list_manager_.get()});
+
device_info_->Start();
if (http_server) {
diff --git a/src/device_manager.h b/src/device_manager.h
index d40ba8e..d77bacc 100644
--- a/src/device_manager.h
+++ b/src/device_manager.h
@@ -10,6 +10,8 @@
namespace weave {
+class AccessApiHandler;
+class AccessBlackListManager;
class BaseApiHandler;
class Config;
class ComponentManager;
@@ -107,6 +109,8 @@ class DeviceManager final : public Device {
std::unique_ptr<ComponentManager> component_manager_;
std::unique_ptr<DeviceRegistrationInfo> device_info_;
std::unique_ptr<BaseApiHandler> base_api_handler_;
+ std::unique_ptr<AccessBlackListManager> black_list_manager_;
+ std::unique_ptr<AccessApiHandler> access_api_handler_;
std::unique_ptr<privet::Manager> privet_;
base::WeakPtrFactory<DeviceManager> weak_ptr_factory_{this};