Merge remote-tracking branch 'weave/master' into 'weave/dev'
ec47eb0 examples/provider/wifi_manager: find iface name
43bf6b7 Update gtest
e03c094 Include of gtest_prod.h only building unittests
0dbbf60 AddTo will return AddToTypeProxy for convenience
48a8669 Remove domain from weave::Error
50a147a Enforce printf format literals checking
diff --git a/examples/daemon/common/daemon.h b/examples/daemon/common/daemon.h
index 4cccff3..6dc021d 100644
--- a/examples/daemon/common/daemon.h
+++ b/examples/daemon/common/daemon.h
@@ -69,10 +69,11 @@
};
Daemon(const Options& opts)
- : config_store_{new weave::examples::FileConfigStore(
- opts.disable_security_,
- opts.model_id_)},
- task_runner_{new weave::examples::EventTaskRunner},
+ : task_runner_{new weave::examples::EventTaskRunner},
+ config_store_{
+ new weave::examples::FileConfigStore(opts.disable_security_,
+ opts.model_id_,
+ task_runner_.get())},
http_client_{new weave::examples::CurlHttpClient(task_runner_.get())},
network_{new weave::examples::EventNetworkImpl(task_runner_.get())},
bluetooth_{new weave::examples::BluetoothImpl} {
@@ -114,8 +115,8 @@
LOG(INFO) << "Device registered: " << device->GetSettings().cloud_id;
}
- std::unique_ptr<weave::examples::FileConfigStore> config_store_;
std::unique_ptr<weave::examples::EventTaskRunner> task_runner_;
+ std::unique_ptr<weave::examples::FileConfigStore> config_store_;
std::unique_ptr<weave::examples::CurlHttpClient> http_client_;
std::unique_ptr<weave::examples::EventNetworkImpl> network_;
std::unique_ptr<weave::examples::BluetoothImpl> bluetooth_;
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc
index 6faa242..31efaa7 100644
--- a/examples/provider/file_config_store.cc
+++ b/examples/provider/file_config_store.cc
@@ -12,16 +12,27 @@
#include <string>
#include <vector>
+#include <base/bind.h>
+
namespace weave {
namespace examples {
const char kSettingsDir[] = "/var/lib/weave/";
FileConfigStore::FileConfigStore(bool disable_security,
- const std::string& model_id)
+ const std::string& model_id,
+ provider::TaskRunner* task_runner)
: disable_security_{disable_security},
model_id_{model_id},
- settings_path_{"/var/lib/weave/weave_settings_" + model_id + ".json"} {}
+ task_runner_{task_runner} {}
+
+std::string FileConfigStore::GetPath(const std::string& name) const {
+ std::string path{kSettingsDir};
+ path += path + "weave_settings_" + model_id_;
+ if (!name.empty())
+ path += "_" + name;
+ return path + ".json";
+}
bool FileConfigStore::LoadDefaults(Settings* settings) {
char host_name[HOST_NAME_MAX] = {};
@@ -55,17 +66,25 @@
}
std::string FileConfigStore::LoadSettings() {
- LOG(INFO) << "Loading settings from " << settings_path_;
- std::ifstream str(settings_path_);
+ return LoadSettings("");
+}
+
+std::string FileConfigStore::LoadSettings(const std::string& name) {
+ LOG(INFO) << "Loading settings from " << GetPath(name);
+ std::ifstream str(GetPath(name));
return std::string(std::istreambuf_iterator<char>(str),
std::istreambuf_iterator<char>());
}
-void FileConfigStore::SaveSettings(const std::string& settings) {
+void FileConfigStore::SaveSettings(const std::string& name,
+ const std::string& settings,
+ const DoneCallback& callback) {
CHECK(mkdir(kSettingsDir, S_IRWXU) == 0 || errno == EEXIST);
- LOG(INFO) << "Saving settings to " << settings_path_;
- std::ofstream str(settings_path_);
+ LOG(INFO) << "Saving settings to " << GetPath(name);
+ std::ofstream str(GetPath(name));
str << settings;
+ if (!callback.is_null())
+ task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {});
}
} // namespace examples
diff --git a/examples/provider/file_config_store.h b/examples/provider/file_config_store.h
index 578f940..e7398d1 100644
--- a/examples/provider/file_config_store.h
+++ b/examples/provider/file_config_store.h
@@ -10,22 +10,30 @@
#include <vector>
#include <weave/provider/config_store.h>
+#include <weave/provider/task_runner.h>
namespace weave {
namespace examples {
class FileConfigStore : public provider::ConfigStore {
public:
- FileConfigStore(bool disable_security, const std::string& model_id);
+ FileConfigStore(bool disable_security,
+ const std::string& model_id,
+ provider::TaskRunner* task_runner);
bool LoadDefaults(Settings* settings) override;
+ std::string LoadSettings(const std::string& name) override;
+ void SaveSettings(const std::string& name,
+ const std::string& settings,
+ const DoneCallback& callback) override;
+
std::string LoadSettings() override;
- void SaveSettings(const std::string& settings) override;
private:
+ std::string GetPath(const std::string& name) const;
const bool disable_security_;
const std::string model_id_;
- const std::string settings_path_;
+ provider::TaskRunner* task_runner_{nullptr};
};
} // namespace examples
diff --git a/file_lists.mk b/file_lists.mk
index a018178..8dccd9c 100644
--- a/file_lists.mk
+++ b/file_lists.mk
@@ -3,6 +3,8 @@
# found in the LICENSE file.
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 \
@@ -48,6 +50,8 @@
src/test/unittest_utils.cc
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 \
@@ -162,4 +166,3 @@
third_party/libuweave/src/macaroon_caveat.c \
third_party/libuweave/src/macaroon_context.c \
third_party/libuweave/src/macaroon_encoding.c
-
diff --git a/include/weave/provider/config_store.h b/include/weave/provider/config_store.h
index 1b7988f..128eccc 100644
--- a/include/weave/provider/config_store.h
+++ b/include/weave/provider/config_store.h
@@ -13,6 +13,7 @@
#include <base/callback.h>
#include <base/time/time.h>
#include <weave/enum_to_string.h>
+#include <weave/error.h>
#include <weave/settings.h>
namespace weave {
@@ -36,8 +37,8 @@
// Implementation of LoadSettings() method should load previously
// stored settings from the persistent storage (file, flash, etc).
// For example:
-// std::string FileConfigStore::LoadSettings() {
-// std::ifstream str("/var/lib/weave/weave_settings.json");
+// std::string FileConfigStore::LoadSettings(const std::string& name) {
+// std::ifstream str("/var/lib/weave/weave_" + name + ".json");
// return std::string(std::istreambuf_iterator<char>(str),
// std::istreambuf_iterator<char>());
// }
@@ -47,9 +48,14 @@
// Implementation of SaveSettings(...) method should store data in the
// persistent storage (file, flash, etc).
// For example:
-// void FileConfigStore::SaveSettings(const std::string& settings) {
-// std::ofstream str(kSettingsPath);
+// void FileConfigStore::SaveSettings(const std::string& name,
+// const std::string& settings,
+// const DoneCallback& callback) {
+// std::ofstream str("/var/lib/weave/weave_" + name + ".json");
// str << settings;
+// if (!callback.is_null())
+// task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr),
+// {});
// }
// It is highly recommended to protected data using encryption with
// hardware backed key.
@@ -67,12 +73,20 @@
// Returns settings saved by SaveSettings during last run of libweave.
// Implementation should return data as-is without parsing or modifications.
- virtual std::string LoadSettings() = 0;
+ // |name| is the name of settings blob. Could be used as filename.
+ virtual std::string LoadSettings(const std::string& name) = 0;
// Saves settings. Implementation should save data as-is without parsing or
// modifications. Data stored in settings can be sensitive, so it's highly
// recommended to protect data, e.g. using encryption.
- virtual void SaveSettings(const std::string& settings) = 0;
+ // |name| is the name of settings blob. Could be used as filename.
+ // Implementation must call or post callback
+ virtual void SaveSettings(const std::string& name,
+ const std::string& settings,
+ const DoneCallback& callback) = 0;
+
+ // Deprecated: only for migration of old configs to version with |name|.
+ virtual std::string LoadSettings() = 0;
protected:
virtual ~ConfigStore() {}
diff --git a/include/weave/provider/test/mock_config_store.h b/include/weave/provider/test/mock_config_store.h
index 3873251..a7eb374 100644
--- a/include/weave/provider/test/mock_config_store.h
+++ b/include/weave/provider/test/mock_config_store.h
@@ -18,10 +18,13 @@
class MockConfigStore : public ConfigStore {
public:
- MockConfigStore() {
+ explicit MockConfigStore(bool set_expectations = true) {
using testing::_;
using testing::Return;
+ if (!set_expectations)
+ return;
+
EXPECT_CALL(*this, LoadDefaults(_))
.WillRepeatedly(testing::Invoke([](Settings* settings) {
settings->firmware_version = "TEST_FIRMWARE";
@@ -39,11 +42,21 @@
"version": 1,
"device_id": "TEST_DEVICE_ID"
})"));
- EXPECT_CALL(*this, SaveSettings(_)).WillRepeatedly(Return());
+ EXPECT_CALL(*this, LoadSettings(_)).WillRepeatedly(Return(""));
+ EXPECT_CALL(*this, SaveSettings(_, _, _))
+ .WillRepeatedly(testing::WithArgs<1, 2>(testing::Invoke(
+ [](const std::string& json, const DoneCallback& callback) {
+ if (!callback.is_null())
+ callback.Run(nullptr);
+ })));
}
MOCK_METHOD1(LoadDefaults, bool(Settings*));
+ MOCK_METHOD1(LoadSettings, std::string(const std::string&));
+ MOCK_METHOD3(SaveSettings,
+ void(const std::string&,
+ const std::string&,
+ const DoneCallback&));
MOCK_METHOD0(LoadSettings, std::string());
- MOCK_METHOD1(SaveSettings, void(const std::string&));
};
} // namespace test
diff --git a/src/access_api_handler.cc b/src/access_api_handler.cc
new file mode 100644
index 0000000..9fa6df2
--- /dev/null
+++ b/src/access_api_handler.cc
@@ -0,0 +1,229 @@
+// 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_api_handler.h"
+
+#include <base/bind.h>
+#include <weave/device.h>
+
+#include "src/access_black_list_manager.h"
+#include "src/commands/schema_constants.h"
+#include "src/data_encoding.h"
+#include "src/json_error_codes.h"
+
+namespace weave {
+
+namespace {
+
+const char kComponent[] = "accessControl";
+const char kTrait[] = "_accessControlBlackList";
+const char kStateSize[] = "_accessControlBlackList.size";
+const char kStateCapacity[] = "_accessControlBlackList.capacity";
+const char kUserId[] = "userId";
+const char kApplicationId[] = "applicationId";
+const char kExpirationTimeout[] = "expirationTimeoutSec";
+const char kBlackList[] = "blackList";
+
+bool GetIds(const base::DictionaryValue& parameters,
+ std::vector<uint8_t>* user_id_decoded,
+ std::vector<uint8_t>* app_id_decoded,
+ ErrorPtr* error) {
+ std::string user_id;
+ parameters.GetString(kUserId, &user_id);
+ if (!Base64Decode(user_id, user_id_decoded)) {
+ Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+ errors::commands::kInvalidPropValue,
+ "Invalid user id '%s'", user_id.c_str());
+ return false;
+ }
+
+ std::string app_id;
+ parameters.GetString(kApplicationId, &app_id);
+ if (!Base64Decode(app_id, app_id_decoded)) {
+ Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+ errors::commands::kInvalidPropValue,
+ "Invalid app id '%s'", user_id.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+AccessApiHandler::AccessApiHandler(Device* device,
+ AccessBlackListManager* manager)
+ : device_{device}, manager_{manager} {
+ device_->AddTraitDefinitionsFromJson(R"({
+ "_accessControlBlackList": {
+ "commands": {
+ "block": {
+ "minimalRole": "owner",
+ "parameters": {
+ "userId": {
+ "type": "string"
+ },
+ "applicationId": {
+ "type": "string"
+ },
+ "expirationTimeoutSec": {
+ "type": "integer"
+ }
+ }
+ },
+ "unblock": {
+ "minimalRole": "owner",
+ "parameters": {
+ "userId": {
+ "type": "string"
+ },
+ "applicationId": {
+ "type": "string"
+ }
+ }
+ },
+ "list": {
+ "minimalRole": "owner",
+ "parameters": {},
+ "results": {
+ "blackList": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "userId": {
+ "type": "string"
+ },
+ "applicationId": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+ }
+ }
+ },
+ "state": {
+ "size": {
+ "type": "integer",
+ "isRequired": true
+ },
+ "capacity": {
+ "type": "integer",
+ "isRequired": true
+ }
+ }
+ }
+ })");
+ CHECK(device_->AddComponent(kComponent, {kTrait}, nullptr));
+ UpdateState();
+
+ device_->AddCommandHandler(
+ kComponent, "_accessControlBlackList.block",
+ base::Bind(&AccessApiHandler::Block, weak_ptr_factory_.GetWeakPtr()));
+ device_->AddCommandHandler(
+ kComponent, "_accessControlBlackList.unblock",
+ base::Bind(&AccessApiHandler::Unblock, weak_ptr_factory_.GetWeakPtr()));
+ device_->AddCommandHandler(
+ kComponent, "_accessControlBlackList.list",
+ base::Bind(&AccessApiHandler::List, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AccessApiHandler::Block(const std::weak_ptr<Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ CHECK(command->GetState() == Command::State::kQueued)
+ << EnumToString(command->GetState());
+ command->SetProgress(base::DictionaryValue{}, nullptr);
+
+ const auto& parameters = command->GetParameters();
+ std::vector<uint8_t> user_id;
+ std::vector<uint8_t> app_id;
+ ErrorPtr error;
+ if (!GetIds(parameters, &user_id, &app_id, &error)) {
+ command->Abort(error.get(), nullptr);
+ return;
+ }
+
+ int timeout_sec = 0;
+ parameters.GetInteger(kExpirationTimeout, &timeout_sec);
+
+ base::Time expiration =
+ base::Time::Now() + base::TimeDelta::FromSeconds(timeout_sec);
+
+ manager_->Block(user_id, app_id, expiration,
+ base::Bind(&AccessApiHandler::OnCommandDone,
+ weak_ptr_factory_.GetWeakPtr(), cmd));
+}
+
+void AccessApiHandler::Unblock(const std::weak_ptr<Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ CHECK(command->GetState() == Command::State::kQueued)
+ << EnumToString(command->GetState());
+ command->SetProgress(base::DictionaryValue{}, nullptr);
+
+ const auto& parameters = command->GetParameters();
+ std::vector<uint8_t> user_id;
+ std::vector<uint8_t> app_id;
+ ErrorPtr error;
+ if (!GetIds(parameters, &user_id, &app_id, &error)) {
+ command->Abort(error.get(), nullptr);
+ return;
+ }
+
+ manager_->Unblock(user_id, app_id,
+ base::Bind(&AccessApiHandler::OnCommandDone,
+ weak_ptr_factory_.GetWeakPtr(), cmd));
+}
+
+void AccessApiHandler::List(const std::weak_ptr<Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ CHECK(command->GetState() == Command::State::kQueued)
+ << EnumToString(command->GetState());
+ command->SetProgress(base::DictionaryValue{}, nullptr);
+
+ std::unique_ptr<base::ListValue> entries{new base::ListValue};
+ for (const auto& e : manager_->GetEntries()) {
+ std::unique_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
+ entry->SetString(kUserId, Base64Encode(e.user_id));
+ entry->SetString(kApplicationId, Base64Encode(e.app_id));
+ entries->Append(entry.release());
+ }
+
+ base::DictionaryValue result;
+ result.Set(kBlackList, entries.release());
+
+ command->Complete(result, nullptr);
+}
+
+void AccessApiHandler::OnCommandDone(const std::weak_ptr<Command>& cmd,
+ ErrorPtr error) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+ UpdateState();
+ if (error) {
+ command->Abort(error.get(), nullptr);
+ return;
+ }
+ command->Complete({}, nullptr);
+}
+
+void AccessApiHandler::UpdateState() {
+ base::DictionaryValue state;
+ state.SetInteger(kStateSize, manager_->GetSize());
+ state.SetInteger(kStateCapacity, manager_->GetCapacity());
+ device_->SetStateProperties(kComponent, state, nullptr);
+}
+
+} // namespace weave
diff --git a/src/access_api_handler.h b/src/access_api_handler.h
new file mode 100644
index 0000000..821ce02
--- /dev/null
+++ b/src/access_api_handler.h
@@ -0,0 +1,47 @@
+// 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_API_HANDLER_H_
+#define LIBWEAVE_SRC_ACCESS_API_HANDLER_H_
+
+#include <memory>
+
+#include <base/memory/weak_ptr.h>
+#include <weave/error.h>
+
+namespace weave {
+
+class AccessBlackListManager;
+class Command;
+class Device;
+
+// Handles commands for 'accessControlBlackList' trait.
+// Objects of the class subscribe for notification from CommandManager and
+// execute incoming commands.
+// Handled commands:
+// accessControlBlackList.block
+// accessControlBlackList.unblock
+// accessControlBlackList.list
+class AccessApiHandler final {
+ public:
+ AccessApiHandler(Device* device, AccessBlackListManager* manager);
+
+ private:
+ void Block(const std::weak_ptr<Command>& command);
+ void Unblock(const std::weak_ptr<Command>& command);
+ void List(const std::weak_ptr<Command>& command);
+ void UpdateState();
+
+ void OnCommandDone(const std::weak_ptr<Command>& command, ErrorPtr error);
+
+ Device* device_{nullptr};
+ AccessBlackListManager* manager_{nullptr};
+
+ base::WeakPtrFactory<AccessApiHandler> weak_ptr_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(AccessApiHandler);
+};
+
+} // namespace weave
+
+#endif // LIBWEAVE_SRC_ACCESS_API_HANDLER_H_
diff --git a/src/access_api_handler_unittest.cc b/src/access_api_handler_unittest.cc
new file mode 100644
index 0000000..a142735
--- /dev/null
+++ b/src/access_api_handler_unittest.cc
@@ -0,0 +1,257 @@
+// 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_api_handler.h"
+
+#include <gtest/gtest.h>
+#include <weave/test/mock_device.h>
+#include <weave/test/unittest_utils.h>
+
+#include "src/component_manager_impl.h"
+#include "src/access_black_list_manager.h"
+#include "src/data_encoding.h"
+
+using testing::_;
+using testing::AnyOf;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace weave {
+
+class MockAccessBlackListManager : public AccessBlackListManager {
+ public:
+ MOCK_METHOD4(Block,
+ void(const std::vector<uint8_t>&,
+ const std::vector<uint8_t>&,
+ const base::Time&,
+ const DoneCallback&));
+ MOCK_METHOD3(Unblock,
+ void(const std::vector<uint8_t>&,
+ const std::vector<uint8_t>&,
+ const DoneCallback&));
+ MOCK_CONST_METHOD2(IsBlocked,
+ bool(const std::vector<uint8_t>&,
+ const std::vector<uint8_t>&));
+ MOCK_CONST_METHOD0(GetEntries, std::vector<Entry>());
+ MOCK_CONST_METHOD0(GetSize, size_t());
+ MOCK_CONST_METHOD0(GetCapacity, size_t());
+};
+
+class AccessApiHandlerTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ EXPECT_CALL(device_, AddTraitDefinitionsFromJson(_))
+ .WillRepeatedly(Invoke([this](const std::string& json) {
+ EXPECT_TRUE(component_manager_.LoadTraits(json, nullptr));
+ }));
+ EXPECT_CALL(device_, SetStateProperties(_, _, _))
+ .WillRepeatedly(
+ Invoke(&component_manager_, &ComponentManager::SetStateProperties));
+ EXPECT_CALL(device_, SetStateProperty(_, _, _, _))
+ .WillRepeatedly(
+ Invoke(&component_manager_, &ComponentManager::SetStateProperty));
+ EXPECT_CALL(device_, AddComponent(_, _, _))
+ .WillRepeatedly(Invoke([this](const std::string& name,
+ const std::vector<std::string>& traits,
+ ErrorPtr* error) {
+ return component_manager_.AddComponent("", name, traits, error);
+ }));
+
+ EXPECT_CALL(device_,
+ AddCommandHandler(_, AnyOf("_accessControlBlackList.block",
+ "_accessControlBlackList.unblock",
+ "_accessControlBlackList.list"),
+ _))
+ .WillRepeatedly(
+ Invoke(&component_manager_, &ComponentManager::AddCommandHandler));
+
+ EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(0));
+
+ EXPECT_CALL(access_manager_, GetCapacity()).WillRepeatedly(Return(10));
+
+ handler_.reset(new AccessApiHandler{&device_, &access_manager_});
+ }
+
+ const base::DictionaryValue& AddCommand(const std::string& command) {
+ std::string id;
+ auto command_instance = component_manager_.ParseCommandInstance(
+ *test::CreateDictionaryValue(command.c_str()), Command::Origin::kLocal,
+ UserRole::kOwner, &id, nullptr);
+ EXPECT_NE(nullptr, command_instance.get());
+ component_manager_.AddCommand(std::move(command_instance));
+ EXPECT_EQ(Command::State::kDone,
+ component_manager_.FindCommand(id)->GetState());
+ return component_manager_.FindCommand(id)->GetResults();
+ }
+
+ std::unique_ptr<base::DictionaryValue> GetState() {
+ std::string path =
+ component_manager_.FindComponentWithTrait("_accessControlBlackList");
+ EXPECT_FALSE(path.empty());
+ const auto* component = component_manager_.FindComponent(path, nullptr);
+ EXPECT_TRUE(component);
+ const base::DictionaryValue* state = nullptr;
+ EXPECT_TRUE(
+ component->GetDictionary("state._accessControlBlackList", &state));
+ return std::unique_ptr<base::DictionaryValue>{state->DeepCopy()};
+ }
+
+ ComponentManagerImpl component_manager_;
+ StrictMock<test::MockDevice> device_;
+ StrictMock<MockAccessBlackListManager> access_manager_;
+ std::unique_ptr<AccessApiHandler> handler_;
+};
+
+TEST_F(AccessApiHandlerTest, Initialization) {
+ const base::DictionaryValue* trait = nullptr;
+ ASSERT_TRUE(component_manager_.GetTraits().GetDictionary(
+ "_accessControlBlackList", &trait));
+
+ auto expected = R"({
+ "commands": {
+ "block": {
+ "minimalRole": "owner",
+ "parameters": {
+ "userId": {
+ "type": "string"
+ },
+ "applicationId": {
+ "type": "string"
+ },
+ "expirationTimeoutSec": {
+ "type": "integer"
+ }
+ }
+ },
+ "unblock": {
+ "minimalRole": "owner",
+ "parameters": {
+ "userId": {
+ "type": "string"
+ },
+ "applicationId": {
+ "type": "string"
+ }
+ }
+ },
+ "list": {
+ "minimalRole": "owner",
+ "parameters": {},
+ "results": {
+ "blackList": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "userId": {
+ "type": "string"
+ },
+ "applicationId": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+ }
+ }
+ },
+ "state": {
+ "size": {
+ "type": "integer",
+ "isRequired": true
+ },
+ "capacity": {
+ "type": "integer",
+ "isRequired": true
+ }
+ }
+ })";
+ EXPECT_JSON_EQ(expected, *trait);
+
+ expected = R"({
+ "capacity": 10,
+ "size": 0
+ })";
+ EXPECT_JSON_EQ(expected, *GetState());
+}
+
+TEST_F(AccessApiHandlerTest, Block) {
+ EXPECT_CALL(access_manager_, Block(std::vector<uint8_t>{1, 2, 3},
+ std::vector<uint8_t>{3, 4, 5}, _, _))
+ .WillOnce(WithArgs<3>(
+ Invoke([](const DoneCallback& callback) { callback.Run(nullptr); })));
+ EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(1));
+
+ AddCommand(R"({
+ 'name' : '_accessControlBlackList.block',
+ 'component': 'accessControl',
+ 'parameters': {
+ 'userId': 'AQID',
+ 'applicationId': 'AwQF',
+ 'expirationTimeoutSec': 1234
+ }
+ })");
+
+ auto expected = R"({
+ "capacity": 10,
+ "size": 1
+ })";
+ EXPECT_JSON_EQ(expected, *GetState());
+}
+
+TEST_F(AccessApiHandlerTest, Unblock) {
+ EXPECT_CALL(access_manager_, Unblock(std::vector<uint8_t>{1, 2, 3},
+ std::vector<uint8_t>{3, 4, 5}, _))
+ .WillOnce(WithArgs<2>(
+ Invoke([](const DoneCallback& callback) { callback.Run(nullptr); })));
+ EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(4));
+
+ AddCommand(R"({
+ 'name' : '_accessControlBlackList.unblock',
+ 'component': 'accessControl',
+ 'parameters': {
+ 'userId': 'AQID',
+ 'applicationId': 'AwQF',
+ 'expirationTimeoutSec': 1234
+ }
+ })");
+
+ auto expected = R"({
+ "capacity": 10,
+ "size": 4
+ })";
+ EXPECT_JSON_EQ(expected, *GetState());
+}
+
+TEST_F(AccessApiHandlerTest, List) {
+ std::vector<AccessBlackListManager::Entry> entries{
+ {{11, 12, 13}, {21, 22, 23}, base::Time::FromTimeT(1410000000)},
+ {{31, 32, 33}, {41, 42, 43}, base::Time::FromTimeT(1420000000)},
+ };
+ EXPECT_CALL(access_manager_, GetEntries()).WillOnce(Return(entries));
+ EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(4));
+
+ auto expected = R"({
+ "blackList": [ {
+ "applicationId": "FRYX",
+ "userId": "CwwN"
+ }, {
+ "applicationId": "KSor",
+ "userId": "HyAh"
+ } ]
+ })";
+
+ const auto& results = AddCommand(R"({
+ 'name' : '_accessControlBlackList.list',
+ 'component': 'accessControl',
+ 'parameters': {
+ }
+ })");
+
+ EXPECT_JSON_EQ(expected, results);
+}
+} // namespace weave
diff --git a/src/access_black_list_manager.h b/src/access_black_list_manager.h
new file mode 100644
index 0000000..b56226a
--- /dev/null
+++ b/src/access_black_list_manager.h
@@ -0,0 +1,56 @@
+// 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_H_
+#define LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_
+
+#include <vector>
+
+#include <base/time/time.h>
+
+namespace weave {
+
+class AccessBlackListManager {
+ public:
+ struct Entry {
+ // user_id is empty, app_id is empty: block everything.
+ // user_id is not empty, app_id is empty: block if user_id matches.
+ // user_id is empty, app_id is not empty: block if app_id matches.
+ // user_id is not empty, app_id is not empty: block if both match.
+ std::vector<uint8_t> user_id;
+ std::vector<uint8_t> app_id;
+
+ // Time after which to discard the rule.
+ base::Time expiration;
+ };
+ virtual ~AccessBlackListManager() = default;
+
+ virtual void Block(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id,
+ const base::Time& expiration,
+ const DoneCallback& callback) = 0;
+ virtual void Unblock(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id,
+ const DoneCallback& callback) = 0;
+ virtual bool IsBlocked(const std::vector<uint8_t>& user_id,
+ const std::vector<uint8_t>& app_id) const = 0;
+ virtual std::vector<Entry> GetEntries() const = 0;
+ virtual size_t GetSize() const = 0;
+ virtual size_t GetCapacity() const = 0;
+};
+
+inline bool operator==(const AccessBlackListManager::Entry& l,
+ const AccessBlackListManager::Entry& r) {
+ return l.user_id == r.user_id && l.app_id == r.app_id &&
+ l.expiration == r.expiration;
+}
+
+inline bool operator!=(const AccessBlackListManager::Entry& l,
+ const AccessBlackListManager::Entry& r) {
+ return !(l == r);
+}
+
+} // namespace weave
+
+#endif // LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_
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/base_api_handler.h b/src/base_api_handler.h
index 1dbbac8..6eebfca 100644
--- a/src/base_api_handler.h
+++ b/src/base_api_handler.h
@@ -33,7 +33,7 @@
void OnConfigChanged(const Settings& settings);
DeviceRegistrationInfo* device_info_;
- Device* device_;
+ Device* device_{nullptr};
base::WeakPtrFactory<BaseApiHandler> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BaseApiHandler);
diff --git a/src/config.cc b/src/config.cc
index 76be205..44d20dd 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -18,9 +18,12 @@
#include "src/data_encoding.h"
#include "src/privet/privet_types.h"
#include "src/string_utils.h"
+#include "src/bind_lambda.h"
namespace weave {
+const char kConfigName[] = "config";
+
namespace config_keys {
const char kVersion[] = "version";
@@ -139,9 +142,12 @@
void Config::Transaction::LoadState() {
if (!config_->config_store_)
return;
- std::string json_string = config_->config_store_->LoadSettings();
- if (json_string.empty())
- return;
+ std::string json_string = config_->config_store_->LoadSettings(kConfigName);
+ if (json_string.empty()) {
+ json_string = config_->config_store_->LoadSettings();
+ if (json_string.empty())
+ return;
+ }
auto value = base::JSONReader::Read(json_string);
base::DictionaryValue* dict = nullptr;
@@ -266,7 +272,9 @@
base::JSONWriter::WriteWithOptions(
dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
- config_store_->SaveSettings(json_string);
+ config_store_->SaveSettings(
+ kConfigName, json_string,
+ base::Bind([](ErrorPtr error) { CHECK(!error); }));
}
Config::Transaction::~Transaction() {
diff --git a/src/config_unittest.cc b/src/config_unittest.cc
index 0367516..fbb558a 100644
--- a/src/config_unittest.cc
+++ b/src/config_unittest.cc
@@ -17,18 +17,20 @@
using testing::_;
using testing::Invoke;
using testing::Return;
+using testing::WithArgs;
namespace weave {
+const char kConfigName[] = "config";
+
class ConfigTest : public ::testing::Test {
protected:
void SetUp() override {
- EXPECT_CALL(*this, OnConfigChanged(_))
- .Times(1); // Called from AddOnChangedCallback
Reload();
}
void Reload() {
+ EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
config_.reset(new Config{&config_store_});
config_->AddOnChangedCallback(
base::Bind(&ConfigTest::OnConfigChanged, base::Unretained(this)));
@@ -86,31 +88,45 @@
}
TEST_F(ConfigTest, LoadStateV0) {
- EXPECT_CALL(config_store_, LoadSettings())
+ EXPECT_CALL(config_store_, LoadSettings(kConfigName))
.WillOnce(Return(R"({
"device_id": "state_device_id"
})"));
- EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
Reload();
EXPECT_EQ("state_device_id", GetSettings().cloud_id);
EXPECT_FALSE(GetSettings().device_id.empty());
EXPECT_NE(GetSettings().cloud_id, GetSettings().device_id);
- EXPECT_CALL(config_store_, LoadSettings())
+ EXPECT_CALL(config_store_, LoadSettings(kConfigName))
.WillOnce(Return(R"({
"device_id": "state_device_id",
"cloud_id": "state_cloud_id"
})"));
- EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
Reload();
EXPECT_EQ("state_cloud_id", GetSettings().cloud_id);
EXPECT_EQ("state_device_id", GetSettings().device_id);
}
+TEST_F(ConfigTest, LoadStateUnnamed) {
+ EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(""));
+
+ EXPECT_CALL(config_store_, LoadSettings()).Times(1);
+
+ Reload();
+}
+
+TEST_F(ConfigTest, LoadStateNamed) {
+ EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return("{}"));
+
+ EXPECT_CALL(config_store_, LoadSettings()).Times(0);
+
+ Reload();
+}
+
TEST_F(ConfigTest, LoadState) {
auto state = R"({
"version": 1,
@@ -133,9 +149,8 @@
"secret": "c3RhdGVfc2VjcmV0",
"service_url": "state_service_url"
})";
- EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(state));
+ EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
- EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
Reload();
EXPECT_EQ("state_client_id", GetSettings().client_id);
@@ -243,9 +258,10 @@
EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
- EXPECT_CALL(config_store_, SaveSettings(_))
- .WillOnce(Invoke([](const std::string& json) {
- auto expected = R"({
+ EXPECT_CALL(config_store_, SaveSettings(kConfigName, _, _))
+ .WillOnce(WithArgs<1, 2>(
+ Invoke([](const std::string& json, const DoneCallback& callback) {
+ auto expected = R"({
'version': 1,
'api_key': 'set_api_key',
'client_id': 'set_client_id',
@@ -266,8 +282,9 @@
'secret': 'AQIDBAU=',
'service_url': 'set_service_url'
})";
- EXPECT_JSON_EQ(expected, *test::CreateValue(json));
- }));
+ EXPECT_JSON_EQ(expected, *test::CreateValue(json));
+ callback.Run(nullptr);
+ })));
change.Commit();
}
diff --git a/src/device_manager.cc b/src/device_manager.cc
index 04d7a6b..8eed558 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 @@
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 @@
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};
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc
index 5bef931..ebc66cd 100644
--- a/src/weave_unittest.cc
+++ b/src/weave_unittest.cc
@@ -204,10 +204,6 @@
})));
}
- void InitConfigStore() {
- EXPECT_CALL(config_store_, SaveSettings("")).WillRepeatedly(Return());
- }
-
void InitNetwork() {
EXPECT_CALL(network_, AddConnectionChangedCallback(_))
.WillRepeatedly(Invoke(
@@ -267,7 +263,6 @@
}
void InitDefaultExpectations() {
- InitConfigStore();
InitNetwork();
EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv")))
.WillOnce(Return());
@@ -360,13 +355,11 @@
}
TEST_F(WeaveTest, StartMinimal) {
- InitConfigStore();
device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_,
&network_, nullptr, nullptr, &wifi_, nullptr);
}
TEST_F(WeaveTest, StartNoWifi) {
- InitConfigStore();
InitNetwork();
InitHttpServer();
InitDnsSd();
@@ -450,7 +443,6 @@
void SetUp() override {
WeaveTest::SetUp();
- InitConfigStore();
InitHttpServer();
InitNetwork();
InitDnsSd();