Merge remote-tracking branch 'weave/master' into merge
diff --git a/examples/daemon/light/light.cc b/examples/daemon/light/light.cc index 611a37d..eaf9392 100644 --- a/examples/daemon/light/light.cc +++ b/examples/daemon/light/light.cc
@@ -283,6 +283,7 @@ int main(int argc, char** argv) { Daemon::Options opts; + opts.model_id_ = "AIAAA"; if (!opts.Parse(argc, argv)) { Daemon::Options::ShowUsage(argv[0]); return 1;
diff --git a/examples/daemon/lock/lock.cc b/examples/daemon/lock/lock.cc index a4bd213..61961f0 100644 --- a/examples/daemon/lock/lock.cc +++ b/examples/daemon/lock/lock.cc
@@ -129,6 +129,7 @@ int main(int argc, char** argv) { Daemon::Options opts; + opts.model_id_ = "AOAAA"; if (!opts.Parse(argc, argv)) { Daemon::Options::ShowUsage(argv[0]); return 1;
diff --git a/examples/daemon/sample/sample.cc b/examples/daemon/sample/sample.cc index 97cef61..2ab4b27 100644 --- a/examples/daemon/sample/sample.cc +++ b/examples/daemon/sample/sample.cc
@@ -104,7 +104,7 @@ device_->SetStateProperty(kComponent, "_sample._ping_count", base::FundamentalValue{++ping_count_}, nullptr); - LOG(INFO) << "New state: " << device_->GetState(); + LOG(INFO) << "New component state: " << device_->GetComponents(); base::DictionaryValue result; cmd->Complete(result, nullptr);
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc index 7d4e31b..2214be8 100644 --- a/examples/provider/file_config_store.cc +++ b/examples/provider/file_config_store.cc
@@ -16,11 +16,10 @@ namespace examples { const char kSettingsDir[] = "/var/lib/weave/"; -const char kSettingsPath[] = "/var/lib/weave/weave_settings.json"; -const char kCategory[] = "example"; FileConfigStore::FileConfigStore(bool disable_security, const std::string& model_id) - : disable_security_{disable_security}, model_id_{model_id} {} + : disable_security_{disable_security}, model_id_{model_id}, + settings_path_{"/var/lib/weave/weave_settings_" + model_id + ".json"} {} bool FileConfigStore::LoadDefaults(Settings* settings) { char host_name[HOST_NAME_MAX] = {}; @@ -54,16 +53,16 @@ } std::string FileConfigStore::LoadSettings() { - LOG(INFO) << "Loading settings from " << kSettingsPath; - std::ifstream str(kSettingsPath); + LOG(INFO) << "Loading settings from " << settings_path_; + std::ifstream str(settings_path_); return std::string(std::istreambuf_iterator<char>(str), std::istreambuf_iterator<char>()); } void FileConfigStore::SaveSettings(const std::string& settings) { CHECK(mkdir(kSettingsDir, S_IRWXU) == 0 || errno == EEXIST); - LOG(INFO) << "Saving settings to " << kSettingsPath; - std::ofstream str(kSettingsPath); + LOG(INFO) << "Saving settings to " << settings_path_; + std::ofstream str(settings_path_); str << settings; }
diff --git a/examples/provider/file_config_store.h b/examples/provider/file_config_store.h index 8764cc5..578f940 100644 --- a/examples/provider/file_config_store.h +++ b/examples/provider/file_config_store.h
@@ -16,15 +16,16 @@ class FileConfigStore : public provider::ConfigStore { public: - explicit FileConfigStore(bool disable_security, const std::string& model_id); + FileConfigStore(bool disable_security, const std::string& model_id); bool LoadDefaults(Settings* settings) override; std::string LoadSettings() override; void SaveSettings(const std::string& settings) override; private: - bool disable_security_{false}; - std::string model_id_{"AAAAA"}; + const bool disable_security_; + const std::string model_id_; + const std::string settings_path_; }; } // namespace examples
diff --git a/include/weave/device.h b/include/weave/device.h index 2d7aaff..99035f4 100644 --- a/include/weave/device.h +++ b/include/weave/device.h
@@ -53,6 +53,10 @@ // Returns the full JSON dictionary containing trait definitions. virtual const base::DictionaryValue& GetTraits() const = 0; + // Sets callback which is called when new trait definitions are added. + virtual void AddTraitDefsChangedCallback( + const base::Closure& callback) = 0; + // Adds a new component instance to device. Traits used by this component // must be already defined. virtual bool AddComponent(const std::string& name, @@ -172,6 +176,7 @@ // Adds provided commands definitions. Can be called multiple times with // condition that definitions do not conflict. // Invalid value is fatal. + // DO NOT USE IN YOUR CODE: use AddTraitDefinitions() instead. LIBWEAVE_DEPRECATED virtual void AddCommandDefinitionsFromJson( const std::string& json) = 0; LIBWEAVE_DEPRECATED virtual void AddCommandDefinitions( @@ -182,6 +187,7 @@ // "base.reboot". Each command can have no more than one handler. // Empty |command_name| sets default handler for all unhanded commands. // No new command handlers can be set after default handler was set. + // DO NOT USE IN YOUR CODE: use AddCommandHandler() with component parameter. LIBWEAVE_DEPRECATED virtual void AddCommandHandler( const std::string& command_name, const CommandHandlerCallback& callback) = 0; @@ -189,6 +195,7 @@ // Adds provided state definitions. Can be called multiple times with // condition that definitions do not conflict. // Invalid value is fatal. + // DO NOT USE IN YOUR CODE: use AddTraitDefinitions() instead. LIBWEAVE_DEPRECATED virtual void AddStateDefinitionsFromJson( const std::string& json) = 0; LIBWEAVE_DEPRECATED virtual void AddStateDefinitions( @@ -201,6 +208,7 @@ // device->SetStatePropertiesFromJson("{'base':{'firmwareVersion':'123'}}") // Method completely replaces properties included |json| or |dict|. // Properties of the state not included |json| or |dict| will stay unchanged. + // DO NOT USE IN YOUR CODE: use SetStateProperties() with component parameter. LIBWEAVE_DEPRECATED virtual bool SetStatePropertiesFromJson( const std::string& json, ErrorPtr* error) = 0; @@ -210,19 +218,21 @@ // Returns value of the single property. // |name| is full property name, including package name. e.g. "base.network". + // DO NOT USE IN YOUR CODE: use GetStateProperty() with component parameter. LIBWEAVE_DEPRECATED virtual const base::Value* GetStateProperty( const std::string& name) const = 0; // Sets value of the single property. // |name| is full property name, including package name. e.g. "base.network". + // DO NOT USE IN YOUR CODE: use SetStateProperty() with component parameter. LIBWEAVE_DEPRECATED virtual bool SetStateProperty( const std::string& name, const base::Value& value, ErrorPtr* error) = 0; // Returns aggregated state properties across all registered packages. + // DO NOT USE IN YOUR CODE: use GetComponents() instead. LIBWEAVE_DEPRECATED virtual const base::DictionaryValue& GetState() const = 0; - }; } // namespace weave
diff --git a/include/weave/export.h b/include/weave/export.h index f698176..6a658f9 100644 --- a/include/weave/export.h +++ b/include/weave/export.h
@@ -8,8 +8,6 @@ #define LIBWEAVE_EXPORT __attribute__((__visibility__("default"))) #define LIBWEAVE_PRIVATE __attribute__((__visibility__("hidden"))) -// TODO(avakulenko): Once all the sample clients are migrated to new APIs, -// mark the old one officially deprecated by uncomment the following attribute. -#define LIBWEAVE_DEPRECATED // __attribute__((deprecated)) +#define LIBWEAVE_DEPRECATED __attribute__((deprecated)) #endif // LIBWEAVE_INCLUDE_WEAVE_EXPORT_H_
diff --git a/include/weave/test/mock_device.h b/include/weave/test/mock_device.h index 88cc5e0..612afb9 100644 --- a/include/weave/test/mock_device.h +++ b/include/weave/test/mock_device.h
@@ -24,6 +24,8 @@ MOCK_METHOD1(AddTraitDefinitionsFromJson, void(const std::string& json)); MOCK_METHOD1(AddTraitDefinitions, void(const base::DictionaryValue& dict)); MOCK_CONST_METHOD0(GetTraits, const base::DictionaryValue&()); + MOCK_METHOD1(AddTraitDefsChangedCallback, + void(const base::Closure& callback)); MOCK_METHOD3(AddComponent, bool(const std::string& name, const std::vector<std::string>& traits, ErrorPtr* error));
diff --git a/libweave.gypi b/libweave.gypi index b529add..6f64c89 100644 --- a/libweave.gypi +++ b/libweave.gypi
@@ -115,6 +115,7 @@ 'third_party/chromium/base/third_party/dmg_fp/dtoa.cc', 'third_party/chromium/base/third_party/icu/icu_utf.cc', 'third_party/chromium/base/time/clock.cc', + 'third_party/chromium/base/time/default_clock.cc', 'third_party/chromium/base/time/time.cc', 'third_party/chromium/base/time/time_posix.cc', 'third_party/chromium/base/values.cc',
diff --git a/src/base_api_handler_unittest.cc b/src/base_api_handler_unittest.cc index 23ef95e..14421b0 100644 --- a/src/base_api_handler_unittest.cc +++ b/src/base_api_handler_unittest.cc
@@ -5,6 +5,7 @@ #include "src/base_api_handler.h" #include <base/strings/string_number_conversions.h> +#include <base/time/default_clock.h> #include <base/values.h> #include <gtest/gtest.h> #include <weave/provider/test/mock_config_store.h> @@ -53,11 +54,10 @@ .WillRepeatedly(Invoke(&component_manager_, &ComponentManager::AddCommandHandler)); - std::unique_ptr<Config> config{new Config{&config_store_}}; - config->Load(); - dev_reg_.reset(new DeviceRegistrationInfo(&component_manager_, - std::move(config), nullptr, - &http_client_, nullptr)); + config_.Load(); + dev_reg_.reset(new DeviceRegistrationInfo(&config_, &component_manager_, + nullptr, &http_client_, nullptr, + nullptr)); EXPECT_CALL(device_, GetSettings()) .WillRepeatedly(ReturnRef(dev_reg_->GetSettings())); @@ -91,6 +91,7 @@ } provider::test::MockConfigStore config_store_; + Config config_{&config_store_}; StrictMock<provider::test::MockHttpClient> http_client_; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; ComponentManagerImpl component_manager_;
diff --git a/src/component_manager_impl.cc b/src/component_manager_impl.cc index a23f34d..89ca0cd 100644 --- a/src/component_manager_impl.cc +++ b/src/component_manager_impl.cc
@@ -31,10 +31,8 @@ LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap() : EnumToStringMap(kMap) {} -ComponentManagerImpl::ComponentManagerImpl() {} - -ComponentManagerImpl::ComponentManagerImpl(base::Clock* clock) : clock_{clock} { -} +ComponentManagerImpl::ComponentManagerImpl(base::Clock* clock) + : clock_{clock ? clock : &default_clock_} {} ComponentManagerImpl::~ComponentManagerImpl() {} @@ -343,7 +341,7 @@ auto& queue = state_change_queues_[component_path]; if (!queue) queue.reset(new StateChangeQueue{kMaxStateChangeQueueSize}); - base::Time timestamp = clock_ ? clock_->Now() : base::Time::Now(); + base::Time timestamp = clock_->Now(); queue->NotifyPropertiesUpdated(timestamp, dict); for (const auto& cb : on_state_changed_) cb.Run();
diff --git a/src/component_manager_impl.h b/src/component_manager_impl.h index c59c6d9..6b8ff13 100644 --- a/src/component_manager_impl.h +++ b/src/component_manager_impl.h
@@ -5,6 +5,8 @@ #ifndef LIBWEAVE_SRC_COMPONENT_MANAGER_IMPL_H_ #define LIBWEAVE_SRC_COMPONENT_MANAGER_IMPL_H_ +#include <base/time/default_clock.h> + #include "src/commands/command_queue.h" #include "src/component_manager.h" #include "src/states/state_change_queue.h" @@ -13,8 +15,7 @@ class ComponentManagerImpl final : public ComponentManager { public: - ComponentManagerImpl(); - explicit ComponentManagerImpl(base::Clock* clock); + explicit ComponentManagerImpl(base::Clock* clock = nullptr); ~ComponentManagerImpl() override; // Loads trait definition schema. @@ -188,7 +189,9 @@ const std::string& path, ErrorPtr* error); + base::DefaultClock default_clock_; base::Clock* clock_{nullptr}; + // An ID of last state change update. Each NotifyPropertiesUpdated() // invocation increments this value by 1. UpdateID last_state_change_id_{0};
diff --git a/src/component_manager_unittest.cc b/src/component_manager_unittest.cc index 902ee15..c63d2dd 100644 --- a/src/component_manager_unittest.cc +++ b/src/component_manager_unittest.cc
@@ -12,10 +12,12 @@ #include "src/bind_lambda.h" #include "src/commands/schema_constants.h" #include "src/mock_component_manager.h" +#include "src/test/mock_clock.h" namespace weave { using test::CreateDictionaryValue; +using testing::Return; namespace { @@ -65,47 +67,36 @@ // } // } // } -void CreateTestComponentTree(ComponentManager* manager) { - const char kTraits[] = R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})"; - auto json = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager->LoadTraits(*json, nullptr)); - EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr)); - EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t2"}, - nullptr)); - EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t3"}, - nullptr)); - EXPECT_TRUE(manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"}, - nullptr)); - EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4", - {"t5", "t6"}, nullptr)); -} +class ComponentManagerTest : public ::testing::Test { + protected: + void CreateTestComponentTree(ComponentManager* manager) { + const char kTraits[] = + R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})"; + auto json = CreateDictionaryValue(kTraits); + ASSERT_TRUE(manager->LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr)); + EXPECT_TRUE( + manager->AddComponentArrayItem("comp1", "comp2", {"t2"}, nullptr)); + EXPECT_TRUE( + manager->AddComponentArrayItem("comp1", "comp2", {"t3"}, nullptr)); + EXPECT_TRUE( + manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"}, nullptr)); + EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4", + {"t5", "t6"}, nullptr)); + } -// Test clock class to record predefined time intervals. -// Implementation from base/test/simple_test_clock.{h|cc} -class SimpleTestClock : public base::Clock { - public: - base::Time Now() override { return now_; } - - // Advances the clock by |delta|. - void Advance(base::TimeDelta delta) { now_ += delta; } - - // Sets the clock to the given time. - void SetNow(base::Time now) { now_ = now; } - - private: - base::Time now_; + test::MockClock clock_; + ComponentManagerImpl manager_{&clock_}; }; } // anonymous namespace -TEST(ComponentManager, Empty) { - ComponentManagerImpl manager; - EXPECT_TRUE(manager.GetTraits().empty()); - EXPECT_TRUE(manager.GetComponents().empty()); +TEST_F(ComponentManagerTest, Empty) { + EXPECT_TRUE(manager_.GetTraits().empty()); + EXPECT_TRUE(manager_.GetComponents().empty()); } -TEST(ComponentManager, LoadTraits) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, LoadTraits) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -125,13 +116,12 @@ } })"; auto json = CreateDictionaryValue(kTraits); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); - EXPECT_JSON_EQ(kTraits, manager.GetTraits()); - EXPECT_TRUE(manager.GetComponents().empty()); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); + EXPECT_JSON_EQ(kTraits, manager_.GetTraits()); + EXPECT_TRUE(manager_.GetComponents().empty()); } -TEST(ComponentManager, LoadTraitsDuplicateIdentical) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, LoadTraitsDuplicateIdentical) { const char kTraits1[] = R"({ "trait1": { "commands": { @@ -151,7 +141,7 @@ } })"; auto json = CreateDictionaryValue(kTraits1); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); const char kTraits2[] = R"({ "trait1": { "commands": { @@ -171,7 +161,7 @@ } })"; json = CreateDictionaryValue(kTraits2); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); const char kExpected[] = R"({ "trait1": { "commands": { @@ -195,11 +185,10 @@ } } })"; - EXPECT_JSON_EQ(kExpected, manager.GetTraits()); + EXPECT_JSON_EQ(kExpected, manager_.GetTraits()); } -TEST(ComponentManager, LoadTraitsDuplicateOverride) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, LoadTraitsDuplicateOverride) { const char kTraits1[] = R"({ "trait1": { "commands": { @@ -219,7 +208,7 @@ } })"; auto json = CreateDictionaryValue(kTraits1); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); const char kTraits2[] = R"({ "trait1": { "commands": { @@ -239,15 +228,14 @@ } })"; json = CreateDictionaryValue(kTraits2); - EXPECT_FALSE(manager.LoadTraits(*json, nullptr)); + EXPECT_FALSE(manager_.LoadTraits(*json, nullptr)); } -TEST(ComponentManager, AddTraitDefChangedCallback) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddTraitDefChangedCallback) { int count = 0; int count2 = 0; - manager.AddTraitDefChangedCallback(base::Bind([&count]() { count++; })); - manager.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; })); + manager_.AddTraitDefChangedCallback(base::Bind([&count]() { count++; })); + manager_.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; })); EXPECT_EQ(1, count); EXPECT_EQ(1, count2); // New definitions. @@ -264,7 +252,7 @@ } })"; auto json = CreateDictionaryValue(kTraits1); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); EXPECT_EQ(2, count); // Duplicate definition, shouldn't call the callback. const char kTraits2[] = R"({ @@ -275,7 +263,7 @@ } })"; json = CreateDictionaryValue(kTraits2); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); EXPECT_EQ(2, count); // New definition, should call the callback now. const char kTraits3[] = R"({ @@ -286,30 +274,28 @@ } })"; json = CreateDictionaryValue(kTraits3); - EXPECT_TRUE(manager.LoadTraits(*json, nullptr)); + EXPECT_TRUE(manager_.LoadTraits(*json, nullptr)); EXPECT_EQ(3, count); // Wrong definition, shouldn't call the callback. const char kTraits4[] = R"({ "trait4": "foo" })"; json = CreateDictionaryValue(kTraits4); - EXPECT_FALSE(manager.LoadTraits(*json, nullptr)); + EXPECT_FALSE(manager_.LoadTraits(*json, nullptr)); EXPECT_EQ(3, count); // Make sure both callbacks were called the same number of times. EXPECT_EQ(count2, count); } -TEST(ComponentManager, LoadTraitsNotAnObject) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, LoadTraitsNotAnObject) { const char kTraits1[] = R"({"trait1": 0})"; auto json = CreateDictionaryValue(kTraits1); ErrorPtr error; - EXPECT_FALSE(manager.LoadTraits(*json, &error)); + EXPECT_FALSE(manager_.LoadTraits(*json, &error)); EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); } -TEST(ComponentManager, FindTraitDefinition) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, FindTraitDefinition) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -329,9 +315,9 @@ } })"; auto json = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*json, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*json, nullptr)); - const base::DictionaryValue* trait = manager.FindTraitDefinition("trait1"); + const base::DictionaryValue* trait = manager_.FindTraitDefinition("trait1"); ASSERT_NE(nullptr, trait); const char kExpected1[] = R"({ "commands": { @@ -346,7 +332,7 @@ })"; EXPECT_JSON_EQ(kExpected1, *trait); - trait = manager.FindTraitDefinition("trait2"); + trait = manager_.FindTraitDefinition("trait2"); ASSERT_NE(nullptr, trait); const char kExpected2[] = R"({ "state": { @@ -355,11 +341,10 @@ })"; EXPECT_JSON_EQ(kExpected2, *trait); - EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3")); + EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait3")); } -TEST(ComponentManager, FindCommandDefinition) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, FindCommandDefinition) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -381,9 +366,9 @@ } })"; auto json = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*json, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*json, nullptr)); - const auto* cmd_def = manager.FindCommandDefinition("trait1.command1"); + const auto* cmd_def = manager_.FindCommandDefinition("trait1.command1"); ASSERT_NE(nullptr, cmd_def); const char kExpected1[] = R"({ "minimalRole": "user", @@ -391,28 +376,28 @@ })"; EXPECT_JSON_EQ(kExpected1, *cmd_def); - cmd_def = manager.FindCommandDefinition("trait2.command1"); + cmd_def = manager_.FindCommandDefinition("trait2.command1"); ASSERT_NE(nullptr, cmd_def); const char kExpected2[] = R"({ "minimalRole": "manager" })"; EXPECT_JSON_EQ(kExpected2, *cmd_def); - cmd_def = manager.FindCommandDefinition("trait2.command2"); + cmd_def = manager_.FindCommandDefinition("trait2.command2"); ASSERT_NE(nullptr, cmd_def); const char kExpected3[] = R"({ "minimalRole": "owner" })"; EXPECT_JSON_EQ(kExpected3, *cmd_def); - EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command2")); - EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3.command1")); - EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait")); - EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command1.parameters")); + EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait1.command2")); + EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait3.command1")); + EXPECT_EQ(nullptr, manager_.FindTraitDefinition("trait")); + EXPECT_EQ(nullptr, + manager_.FindTraitDefinition("trait1.command1.parameters")); } -TEST(ComponentManager, GetMinimalRole) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, GetMinimalRole) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -428,31 +413,31 @@ } })"; auto json = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*json, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*json, nullptr)); UserRole role; - ASSERT_TRUE(manager.GetMinimalRole("trait1.command1", &role, nullptr)); + ASSERT_TRUE(manager_.GetMinimalRole("trait1.command1", &role, nullptr)); EXPECT_EQ(UserRole::kUser, role); - ASSERT_TRUE(manager.GetMinimalRole("trait1.command2", &role, nullptr)); + ASSERT_TRUE(manager_.GetMinimalRole("trait1.command2", &role, nullptr)); EXPECT_EQ(UserRole::kViewer, role); - ASSERT_TRUE(manager.GetMinimalRole("trait2.command1", &role, nullptr)); + ASSERT_TRUE(manager_.GetMinimalRole("trait2.command1", &role, nullptr)); EXPECT_EQ(UserRole::kManager, role); - ASSERT_TRUE(manager.GetMinimalRole("trait2.command2", &role, nullptr)); + ASSERT_TRUE(manager_.GetMinimalRole("trait2.command2", &role, nullptr)); EXPECT_EQ(UserRole::kOwner, role); - EXPECT_FALSE(manager.GetMinimalRole("trait1.command3", &role, nullptr)); + EXPECT_FALSE(manager_.GetMinimalRole("trait1.command3", &role, nullptr)); } -TEST(ComponentManager, AddComponent) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddComponent) { const char kTraits[] = R"({"trait1": {}, "trait2": {}, "trait3": {}})"; auto json = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*json, nullptr)); - EXPECT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); - EXPECT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*json, nullptr)); + EXPECT_TRUE( + manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("", "comp2", {"trait3"}, nullptr)); const char kExpected[] = R"({ "comp1": { "traits": ["trait1", "trait2"] @@ -461,18 +446,17 @@ "traits": ["trait3"] } })"; - EXPECT_JSON_EQ(kExpected, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected, manager_.GetComponents()); // 'trait4' is undefined, so can't add a component referring to it. - EXPECT_FALSE(manager.AddComponent("", "comp3", {"trait4"}, nullptr)); + EXPECT_FALSE(manager_.AddComponent("", "comp3", {"trait4"}, nullptr)); } -TEST(ComponentManager, AddSubComponent) { - ComponentManagerImpl manager; - EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr)); - EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr)); - EXPECT_TRUE(manager.AddComponent("comp1", "comp3", {}, nullptr)); - EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr)); +TEST_F(ComponentManagerTest, AddSubComponent) { + EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1", "comp2", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1", "comp3", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1.comp2", "comp4", {}, nullptr)); const char kExpected[] = R"({ "comp1": { "traits": [], @@ -491,23 +475,22 @@ } } })"; - EXPECT_JSON_EQ(kExpected, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected, manager_.GetComponents()); } -TEST(ComponentManager, AddComponentArrayItem) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddComponentArrayItem) { const char kTraits[] = R"({"foo": {}, "bar": {}})"; auto json = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*json, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*json, nullptr)); - EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr)); - EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"foo"}, - nullptr)); - EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"bar"}, - nullptr)); - EXPECT_TRUE(manager.AddComponent("comp1.comp2[1]", "comp3", {}, nullptr)); - EXPECT_TRUE(manager.AddComponent("comp1.comp2[1].comp3", "comp4", {}, - nullptr)); + EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr)); + EXPECT_TRUE( + manager_.AddComponentArrayItem("comp1", "comp2", {"foo"}, nullptr)); + EXPECT_TRUE( + manager_.AddComponentArrayItem("comp1", "comp2", {"bar"}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1.comp2[1]", "comp3", {}, nullptr)); + EXPECT_TRUE( + manager_.AddComponent("comp1.comp2[1].comp3", "comp4", {}, nullptr)); const char kExpected[] = R"({ "comp1": { "traits": [], @@ -533,96 +516,91 @@ } } })"; - EXPECT_JSON_EQ(kExpected, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected, manager_.GetComponents()); } -TEST(ComponentManager, AddComponentExist) { - ComponentManagerImpl manager; - EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr)); - EXPECT_FALSE(manager.AddComponent("", "comp1", {}, nullptr)); - EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr)); - EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr)); +TEST_F(ComponentManagerTest, AddComponentExist) { + EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr)); + EXPECT_FALSE(manager_.AddComponent("", "comp1", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1", "comp2", {}, nullptr)); + EXPECT_FALSE(manager_.AddComponent("comp1", "comp2", {}, nullptr)); } -TEST(ComponentManager, AddComponentDoesNotExist) { - ComponentManagerImpl manager; - EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr)); +TEST_F(ComponentManagerTest, AddComponentDoesNotExist) { + EXPECT_FALSE(manager_.AddComponent("comp1", "comp2", {}, nullptr)); } -TEST(ComponentManager, AddComponentTreeChangedCallback) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddComponentTreeChangedCallback) { int count = 0; int count2 = 0; - manager.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; })); - manager.AddComponentTreeChangedCallback( + manager_.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; })); + manager_.AddComponentTreeChangedCallback( base::Bind([&count2]() { count2++; })); EXPECT_EQ(1, count); EXPECT_EQ(1, count2); - EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("", "comp1", {}, nullptr)); EXPECT_EQ(2, count); - EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1", "comp2", {}, nullptr)); EXPECT_EQ(3, count); - EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponent("comp1.comp2", "comp4", {}, nullptr)); EXPECT_EQ(4, count); - EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponentArrayItem("comp1", "comp3", {}, nullptr)); EXPECT_EQ(5, count); - EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr)); + EXPECT_TRUE(manager_.AddComponentArrayItem("comp1", "comp3", {}, nullptr)); EXPECT_EQ(6, count); // Make sure both callbacks were called the same number of times. EXPECT_EQ(count2, count); } -TEST(ComponentManager, FindComponent) { - ComponentManagerImpl manager; - CreateTestComponentTree(&manager); +TEST_F(ComponentManagerTest, FindComponent) { + CreateTestComponentTree(&manager_); - const base::DictionaryValue* comp = manager.FindComponent("comp1", nullptr); + const base::DictionaryValue* comp = manager_.FindComponent("comp1", nullptr); ASSERT_NE(nullptr, comp); EXPECT_TRUE(HasTrait(*comp, "t1")); - comp = manager.FindComponent("comp1.comp2[0]", nullptr); + comp = manager_.FindComponent("comp1.comp2[0]", nullptr); ASSERT_NE(nullptr, comp); EXPECT_TRUE(HasTrait(*comp, "t2")); - comp = manager.FindComponent("comp1.comp2[1]", nullptr); + comp = manager_.FindComponent("comp1.comp2[1]", nullptr); ASSERT_NE(nullptr, comp); EXPECT_TRUE(HasTrait(*comp, "t3")); - comp = manager.FindComponent("comp1.comp2[1].comp3", nullptr); + comp = manager_.FindComponent("comp1.comp2[1].comp3", nullptr); ASSERT_NE(nullptr, comp); EXPECT_TRUE(HasTrait(*comp, "t4")); - comp = manager.FindComponent("comp1.comp2[1].comp3.comp4", nullptr); + comp = manager_.FindComponent("comp1.comp2[1].comp3.comp4", nullptr); ASSERT_NE(nullptr, comp); EXPECT_TRUE(HasTrait(*comp, "t5")); // Some whitespaces don't hurt. - comp = manager.FindComponent(" comp1 . comp2 [ \t 1 ] . comp3.comp4 ", - nullptr); + comp = manager_.FindComponent(" comp1 . comp2 [ \t 1 ] . comp3.comp4 ", + nullptr); EXPECT_NE(nullptr, comp); // Now check some failure cases. ErrorPtr error; - EXPECT_EQ(nullptr, manager.FindComponent("", &error)); + EXPECT_EQ(nullptr, manager_.FindComponent("", &error)); EXPECT_NE(nullptr, error.get()); // 'comp2' doesn't exist: - EXPECT_EQ(nullptr, manager.FindComponent("comp2", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp2", nullptr)); // 'comp1.comp2' is an array, not a component: - EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2", nullptr)); // 'comp1.comp2[3]' doesn't exist: - EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[3]", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[3]", nullptr)); // Empty component names: - EXPECT_EQ(nullptr, manager.FindComponent(".comp2[1]", nullptr)); - EXPECT_EQ(nullptr, manager.FindComponent("comp1.[1]", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent(".comp2[1]", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.[1]", nullptr)); // Invalid array indices: - EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[s]", nullptr)); - EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[-2]", nullptr)); - EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1e1]", nullptr)); - EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[s]", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[-2]", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[1e1]", nullptr)); + EXPECT_EQ(nullptr, manager_.FindComponent("comp1.comp2[1", nullptr)); } -TEST(ComponentManager, ParseCommandInstance) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, ParseCommandInstance) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -644,9 +622,9 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp2", {"trait2"}, nullptr)); std::string id; const char kCommand1[] = R"({ @@ -657,13 +635,15 @@ })"; auto command1 = CreateDictionaryValue(kCommand1); EXPECT_NE(nullptr, - manager.ParseCommandInstance(*command1, Command::Origin::kLocal, - UserRole::kUser, &id, nullptr).get()); + manager_.ParseCommandInstance(*command1, Command::Origin::kLocal, + UserRole::kUser, &id, nullptr) + .get()); EXPECT_EQ("1234-12345", id); // Not enough access rights EXPECT_EQ(nullptr, - manager.ParseCommandInstance(*command1, Command::Origin::kLocal, - UserRole::kViewer, &id, nullptr).get()); + manager_.ParseCommandInstance(*command1, Command::Origin::kLocal, + UserRole::kViewer, &id, nullptr) + .get()); const char kCommand2[] = R"({ "name": "trait1.command3", @@ -673,8 +653,9 @@ auto command2 = CreateDictionaryValue(kCommand2); // trait1.command3 doesn't exist EXPECT_EQ(nullptr, - manager.ParseCommandInstance(*command2, Command::Origin::kLocal, - UserRole::kOwner, &id, nullptr).get()); + manager_.ParseCommandInstance(*command2, Command::Origin::kLocal, + UserRole::kOwner, &id, nullptr) + .get()); EXPECT_TRUE(id.empty()); const char kCommand3[] = R"({ @@ -685,8 +666,9 @@ auto command3 = CreateDictionaryValue(kCommand3); // Component comp1 doesn't have trait2. EXPECT_EQ(nullptr, - manager.ParseCommandInstance(*command3, Command::Origin::kLocal, - UserRole::kOwner, &id, nullptr).get()); + manager_.ParseCommandInstance(*command3, Command::Origin::kLocal, + UserRole::kOwner, &id, nullptr) + .get()); // No component specified, find the suitable component const char kCommand4[] = R"({ @@ -694,7 +676,7 @@ "parameters": {} })"; auto command4 = CreateDictionaryValue(kCommand4); - auto command_instance = manager.ParseCommandInstance( + auto command_instance = manager_.ParseCommandInstance( *command4, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr); EXPECT_NE(nullptr, command_instance.get()); EXPECT_EQ("comp1", command_instance->GetComponent()); @@ -704,7 +686,7 @@ "parameters": {} })"; auto command5 = CreateDictionaryValue(kCommand5); - command_instance = manager.ParseCommandInstance( + command_instance = manager_.ParseCommandInstance( *command5, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr); EXPECT_NE(nullptr, command_instance.get()); EXPECT_EQ("comp2", command_instance->GetComponent()); @@ -716,12 +698,12 @@ })"; auto command6 = CreateDictionaryValue(kCommand6); EXPECT_EQ(nullptr, - manager.ParseCommandInstance(*command6, Command::Origin::kLocal, - UserRole::kOwner, &id, nullptr).get()); + manager_.ParseCommandInstance(*command6, Command::Origin::kLocal, + UserRole::kOwner, &id, nullptr) + .get()); } -TEST(ComponentManager, AddCommand) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddCommand) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -730,8 +712,8 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr)); std::string id; const char kCommand[] = R"({ @@ -741,17 +723,16 @@ "parameters": {} })"; auto command = CreateDictionaryValue(kCommand); - auto command_instance = manager.ParseCommandInstance( + auto command_instance = manager_.ParseCommandInstance( *command, Command::Origin::kLocal, UserRole::kUser, &id, nullptr); ASSERT_NE(nullptr, command_instance.get()); - manager.AddCommand(std::move(command_instance)); - const auto* queued_command = manager.FindCommand(id); + manager_.AddCommand(std::move(command_instance)); + const auto* queued_command = manager_.FindCommand(id); ASSERT_NE(nullptr, queued_command); EXPECT_EQ("trait1.command1", queued_command->GetName()); } -TEST(ComponentManager, AddCommandHandler) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddCommandHandler) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -765,9 +746,10 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr)); + ASSERT_TRUE( + manager_.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr)); std::string last_tags; auto handler = [&last_tags](int tag, const std::weak_ptr<Command>& command) { @@ -776,9 +758,12 @@ last_tags += std::to_string(tag); }; - manager.AddCommandHandler("comp1", "trait1.command1", base::Bind(handler, 1)); - manager.AddCommandHandler("comp2", "trait1.command1", base::Bind(handler, 2)); - manager.AddCommandHandler("comp2", "trait2.command2", base::Bind(handler, 3)); + manager_.AddCommandHandler("comp1", "trait1.command1", + base::Bind(handler, 1)); + manager_.AddCommandHandler("comp2", "trait1.command1", + base::Bind(handler, 2)); + manager_.AddCommandHandler("comp2", "trait2.command2", + base::Bind(handler, 3)); EXPECT_TRUE(last_tags.empty()); const char kCommand1[] = R"({ @@ -786,10 +771,10 @@ "component": "comp1" })"; auto command1 = CreateDictionaryValue(kCommand1); - auto command_instance = manager.ParseCommandInstance( + auto command_instance = manager_.ParseCommandInstance( *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr); ASSERT_NE(nullptr, command_instance.get()); - manager.AddCommand(std::move(command_instance)); + manager_.AddCommand(std::move(command_instance)); EXPECT_EQ("1", last_tags); last_tags.clear(); @@ -798,10 +783,10 @@ "component": "comp2" })"; auto command2 = CreateDictionaryValue(kCommand2); - command_instance = manager.ParseCommandInstance( + command_instance = manager_.ParseCommandInstance( *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr); ASSERT_NE(nullptr, command_instance.get()); - manager.AddCommand(std::move(command_instance)); + manager_.AddCommand(std::move(command_instance)); EXPECT_EQ("2", last_tags); last_tags.clear(); @@ -811,16 +796,15 @@ "parameters": {} })"; auto command3 = CreateDictionaryValue(kCommand3); - command_instance = manager.ParseCommandInstance( + command_instance = manager_.ParseCommandInstance( *command3, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr); ASSERT_NE(nullptr, command_instance.get()); - manager.AddCommand(std::move(command_instance)); + manager_.AddCommand(std::move(command_instance)); EXPECT_EQ("3", last_tags); last_tags.clear(); } -TEST(ComponentManager, AddDefaultCommandHandler) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddDefaultCommandHandler) { const char kTraits[] = R"({ "trait1": { "commands": { @@ -834,15 +818,15 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp", {"trait1", "trait2"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp", {"trait1", "trait2"}, nullptr)); int count = 0; auto handler = [&count](int tag, const std::weak_ptr<Command>& command) { count++; }; - manager.AddCommandHandler("", "", base::Bind(handler, 1)); + manager_.AddCommandHandler("", "", base::Bind(handler, 1)); EXPECT_EQ(0, count); const char kCommand1[] = R"({ @@ -850,10 +834,10 @@ "component": "comp" })"; auto command1 = CreateDictionaryValue(kCommand1); - auto command_instance = manager.ParseCommandInstance( + auto command_instance = manager_.ParseCommandInstance( *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr); ASSERT_NE(nullptr, command_instance.get()); - manager.AddCommand(std::move(command_instance)); + manager_.AddCommand(std::move(command_instance)); EXPECT_EQ(1, count); const char kCommand2[] = R"({ @@ -861,20 +845,19 @@ "component": "comp" })"; auto command2 = CreateDictionaryValue(kCommand2); - command_instance = manager.ParseCommandInstance( + command_instance = manager_.ParseCommandInstance( *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr); ASSERT_NE(nullptr, command_instance.get()); - manager.AddCommand(std::move(command_instance)); + manager_.AddCommand(std::move(command_instance)); EXPECT_EQ(2, count); } -TEST(ComponentManager, SetStateProperties) { - ComponentManagerImpl manager; - CreateTestComponentTree(&manager); +TEST_F(ComponentManagerTest, SetStateProperties) { + CreateTestComponentTree(&manager_); const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})"; auto state1 = CreateDictionaryValue(kState1); - ASSERT_TRUE(manager.SetStateProperties("comp1", *state1, nullptr)); + ASSERT_TRUE(manager_.SetStateProperties("comp1", *state1, nullptr)); const char kExpected1[] = R"({ "comp1": { "traits": [ "t1" ], @@ -901,11 +884,11 @@ } } })"; - EXPECT_JSON_EQ(kExpected1, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected1, manager_.GetComponents()); const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})"; auto state2 = CreateDictionaryValue(kState2); - ASSERT_TRUE(manager.SetStateProperties("comp1", *state2, nullptr)); + ASSERT_TRUE(manager_.SetStateProperties("comp1", *state2, nullptr)); const char kExpected2[] = R"({ "comp1": { @@ -933,12 +916,12 @@ } } })"; - EXPECT_JSON_EQ(kExpected2, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected2, manager_.GetComponents()); const char kState3[] = R"({"t5": {"p1": 1}})"; auto state3 = CreateDictionaryValue(kState3); - ASSERT_TRUE(manager.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3, - nullptr)); + ASSERT_TRUE(manager_.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3, + nullptr)); const char kExpected3[] = R"({ "comp1": { @@ -967,14 +950,13 @@ } } })"; - EXPECT_JSON_EQ(kExpected3, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected3, manager_.GetComponents()); } -TEST(ComponentManager, SetStatePropertiesFromJson) { - ComponentManagerImpl manager; - CreateTestComponentTree(&manager); +TEST_F(ComponentManagerTest, SetStatePropertiesFromJson) { + CreateTestComponentTree(&manager_); - ASSERT_TRUE(manager.SetStatePropertiesFromJson( + ASSERT_TRUE(manager_.SetStatePropertiesFromJson( "comp1.comp2[1].comp3.comp4", R"({"t5": {"p1": 3}, "t6": {"p2": 5}})", nullptr)); @@ -1007,11 +989,10 @@ } } })"; - EXPECT_JSON_EQ(kExpected, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected, manager_.GetComponents()); } -TEST(ComponentManager, SetGetStateProperty) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, SetGetStateProperty) { const char kTraits[] = R"({ "trait1": { "state": { @@ -1027,11 +1008,12 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE( + manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); base::StringValue p1("foo"); - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", p1, nullptr)); const char kExpected1[] = R"({ "comp1": { @@ -1041,10 +1023,10 @@ } } })"; - EXPECT_JSON_EQ(kExpected1, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected1, manager_.GetComponents()); base::FundamentalValue p2(2); - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait2.prop3", p2, nullptr)); const char kExpected2[] = R"({ "comp1": { @@ -1055,29 +1037,27 @@ } } })"; - EXPECT_JSON_EQ(kExpected2, manager.GetComponents()); + EXPECT_JSON_EQ(kExpected2, manager_.GetComponents()); // Just the package name without property: - EXPECT_FALSE(manager.SetStateProperty("comp1", "trait2", p2, nullptr)); + EXPECT_FALSE(manager_.SetStateProperty("comp1", "trait2", p2, nullptr)); - const base::Value* value = manager.GetStateProperty("comp1", "trait1.prop1", - nullptr); + const base::Value* value = + manager_.GetStateProperty("comp1", "trait1.prop1", nullptr); ASSERT_NE(nullptr, value); EXPECT_TRUE(p1.Equals(value)); - value = manager.GetStateProperty("comp1", "trait2.prop3", nullptr); + value = manager_.GetStateProperty("comp1", "trait2.prop3", nullptr); ASSERT_NE(nullptr, value); EXPECT_TRUE(p2.Equals(value)); // Non-existing property: - EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2.p", nullptr)); + EXPECT_EQ(nullptr, manager_.GetStateProperty("comp1", "trait2.p", nullptr)); // Non-existing component - EXPECT_EQ(nullptr, manager.GetStateProperty("comp2", "trait.prop", nullptr)); + EXPECT_EQ(nullptr, manager_.GetStateProperty("comp2", "trait.prop", nullptr)); // Just the package name without property: - EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2", nullptr)); + EXPECT_EQ(nullptr, manager_.GetStateProperty("comp1", "trait2", nullptr)); } -TEST(ComponentManager, AddStateChangedCallback) { - SimpleTestClock clock; - ComponentManagerImpl manager{&clock}; +TEST_F(ComponentManagerTest, AddStateChangedCallback) { const char kTraits[] = R"({ "trait1": { "state": { @@ -1087,38 +1067,36 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr)); int count = 0; int count2 = 0; - manager.AddStateChangedCallback(base::Bind([&count]() { count++; })); - manager.AddStateChangedCallback(base::Bind([&count2]() { count2++; })); + manager_.AddStateChangedCallback(base::Bind([&count]() { count++; })); + manager_.AddStateChangedCallback(base::Bind([&count2]() { count2++; })); EXPECT_EQ(1, count); EXPECT_EQ(1, count2); - EXPECT_EQ(0, manager.GetLastStateChangeId()); + EXPECT_EQ(0, manager_.GetLastStateChangeId()); base::StringValue p1("foo"); - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", p1, nullptr)); EXPECT_EQ(2, count); EXPECT_EQ(2, count2); - EXPECT_EQ(1, manager.GetLastStateChangeId()); + EXPECT_EQ(1, manager_.GetLastStateChangeId()); - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", p1, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop2", p1, nullptr)); EXPECT_EQ(3, count); EXPECT_EQ(3, count2); - EXPECT_EQ(2, manager.GetLastStateChangeId()); + EXPECT_EQ(2, manager_.GetLastStateChangeId()); // Fail - no component. - ASSERT_FALSE(manager.SetStateProperty("comp2", "trait1.prop2", p1, nullptr)); + ASSERT_FALSE(manager_.SetStateProperty("comp2", "trait1.prop2", p1, nullptr)); EXPECT_EQ(3, count); EXPECT_EQ(3, count2); - EXPECT_EQ(2, manager.GetLastStateChangeId()); + EXPECT_EQ(2, manager_.GetLastStateChangeId()); } -TEST(ComponentManager, ComponentStateUpdates) { - SimpleTestClock clock; - ComponentManagerImpl manager{&clock}; +TEST_F(ComponentManagerTest, ComponentStateUpdates) { const char kTraits[] = R"({ "trait1": { "state": { @@ -1134,48 +1112,50 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE( + manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); + ASSERT_TRUE( + manager_.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr)); std::vector<ComponentManager::UpdateID> updates1; auto callback1 = [&updates1](ComponentManager::UpdateID id) { updates1.push_back(id); }; // State change queue is empty, callback should be called immediately. - auto token1 = manager.AddServerStateUpdatedCallback(base::Bind(callback1)); + auto token1 = manager_.AddServerStateUpdatedCallback(base::Bind(callback1)); ASSERT_EQ(1u, updates1.size()); - EXPECT_EQ(manager.GetLastStateChangeId(), updates1.front()); + EXPECT_EQ(manager_.GetLastStateChangeId(), updates1.front()); updates1.clear(); base::StringValue foo("foo"); base::Time time1 = base::Time::Now(); - clock.SetNow(time1); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(time1)); // These three updates should be grouped into two separate state change queue // items, since they all happen at the same time, but for two different // components. - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", foo, nullptr)); - ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", foo, nullptr)); - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", foo, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", foo, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp2", "trait2.prop3", foo, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop2", foo, nullptr)); std::vector<ComponentManager::UpdateID> updates2; auto callback2 = [&updates2](ComponentManager::UpdateID id) { updates2.push_back(id); }; // State change queue is not empty, so callback will be called later. - auto token2 = manager.AddServerStateUpdatedCallback(base::Bind(callback2)); + auto token2 = manager_.AddServerStateUpdatedCallback(base::Bind(callback2)); EXPECT_TRUE(updates2.empty()); base::StringValue bar("bar"); base::Time time2 = time1 + base::TimeDelta::FromSeconds(1); - clock.SetNow(time2); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(time2)); // Two more update events (as above) but at |time2|. - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", bar, nullptr)); - ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", bar, nullptr)); - ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", bar, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop1", bar, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp2", "trait2.prop3", bar, nullptr)); + ASSERT_TRUE(manager_.SetStateProperty("comp1", "trait1.prop2", bar, nullptr)); - auto snapshot = manager.GetAndClearRecordedStateChanges(); - EXPECT_EQ(manager.GetLastStateChangeId(), snapshot.update_id); + auto snapshot = manager_.GetAndClearRecordedStateChanges(); + EXPECT_EQ(manager_.GetLastStateChangeId(), snapshot.update_id); ASSERT_EQ(4u, snapshot.state_changes.size()); EXPECT_EQ("comp1", snapshot.state_changes[0].component); @@ -1199,38 +1179,37 @@ *snapshot.state_changes[3].changed_properties); // Make sure previous GetAndClearRecordedStateChanges() clears the queue. - auto snapshot2 = manager.GetAndClearRecordedStateChanges(); - EXPECT_EQ(manager.GetLastStateChangeId(), snapshot2.update_id); + auto snapshot2 = manager_.GetAndClearRecordedStateChanges(); + EXPECT_EQ(manager_.GetLastStateChangeId(), snapshot2.update_id); EXPECT_TRUE(snapshot2.state_changes.empty()); // Now indicate that we have update the changes on the server. - manager.NotifyStateUpdatedOnServer(snapshot.update_id); + manager_.NotifyStateUpdatedOnServer(snapshot.update_id); ASSERT_EQ(1u, updates1.size()); EXPECT_EQ(snapshot.update_id, updates1.front()); ASSERT_EQ(1u, updates2.size()); EXPECT_EQ(snapshot.update_id, updates2.front()); } -TEST(ComponentManager, FindComponentWithTrait) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, FindComponentWithTrait) { const char kTraits[] = R"({ "trait1": {}, "trait2": {}, "trait3": {} })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE( + manager_.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp2", {"trait3"}, nullptr)); - EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait1")); - EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait2")); - EXPECT_EQ("comp2", manager.FindComponentWithTrait("trait3")); - EXPECT_EQ("", manager.FindComponentWithTrait("trait4")); + EXPECT_EQ("comp1", manager_.FindComponentWithTrait("trait1")); + EXPECT_EQ("comp1", manager_.FindComponentWithTrait("trait2")); + EXPECT_EQ("comp2", manager_.FindComponentWithTrait("trait3")); + EXPECT_EQ("", manager_.FindComponentWithTrait("trait4")); } -TEST(ComponentManager, AddLegacyCommandAndStateDefinitions) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, AddLegacyCommandAndStateDefinitions) { const char kCommandDefs1[] = R"({ "package1": { "command1": { @@ -1248,7 +1227,7 @@ } })"; auto json = CreateDictionaryValue(kCommandDefs1); - EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr)); + EXPECT_TRUE(manager_.AddLegacyCommandDefinitions(*json, nullptr)); const char kExpected1[] = R"({ "package1": { "commands": { @@ -1269,11 +1248,11 @@ } } })"; - EXPECT_JSON_EQ(kExpected1, manager.GetTraits()); + EXPECT_JSON_EQ(kExpected1, manager_.GetTraits()); const char kExpectedComponents1[] = R"({ "__weave__": { "traits": ["package1", "package2"] } })"; - EXPECT_JSON_EQ(kExpectedComponents1, manager.GetComponents()); + EXPECT_JSON_EQ(kExpectedComponents1, manager_.GetComponents()); const char kCommandDefs2[] = R"({ "package2": { @@ -1285,7 +1264,7 @@ } })"; json = CreateDictionaryValue(kCommandDefs2); - EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr)); + EXPECT_TRUE(manager_.AddLegacyCommandDefinitions(*json, nullptr)); const char kExpected2[] = R"({ "package1": { "commands": { @@ -1313,14 +1292,14 @@ } } })"; - EXPECT_JSON_EQ(kExpected2, manager.GetTraits()); + EXPECT_JSON_EQ(kExpected2, manager_.GetTraits()); const char kExpectedComponents2[] = R"({ "__weave__": { "traits": ["package1", "package2", "package3"] } })"; - EXPECT_JSON_EQ(kExpectedComponents2, manager.GetComponents()); + EXPECT_JSON_EQ(kExpectedComponents2, manager_.GetComponents()); // Redefining existing commands. - EXPECT_FALSE(manager.AddLegacyCommandDefinitions(*json, nullptr)); + EXPECT_FALSE(manager_.AddLegacyCommandDefinitions(*json, nullptr)); const char kStateDefs1[] = R"({ "package1": { @@ -1333,11 +1312,11 @@ } })"; json = CreateDictionaryValue(kStateDefs1); - EXPECT_TRUE(manager.AddLegacyStateDefinitions(*json, nullptr)); + EXPECT_TRUE(manager_.AddLegacyStateDefinitions(*json, nullptr)); const char kExpectedComponents3[] = R"({ "__weave__": { "traits": ["package1", "package2", "package3", "package4"] } })"; - EXPECT_JSON_EQ(kExpectedComponents3, manager.GetComponents()); + EXPECT_JSON_EQ(kExpectedComponents3, manager_.GetComponents()); const char kExpected3[] = R"({ "package1": { @@ -1376,14 +1355,14 @@ } } })"; - EXPECT_JSON_EQ(kExpected3, manager.GetTraits()); + EXPECT_JSON_EQ(kExpected3, manager_.GetTraits()); const char kExpectedComponents4[] = R"({ "__weave__": { "traits": ["package1", "package2", "package3", "package4"] } })"; - EXPECT_JSON_EQ(kExpectedComponents4, manager.GetComponents()); + EXPECT_JSON_EQ(kExpectedComponents4, manager_.GetComponents()); // Redefining existing commands. - EXPECT_FALSE(manager.AddLegacyStateDefinitions(*json, nullptr)); + EXPECT_FALSE(manager_.AddLegacyStateDefinitions(*json, nullptr)); const char kExpected4[] = R"({ "package1": { @@ -1406,11 +1385,10 @@ "command2": { "minimalRole": "owner" } } })"; - EXPECT_JSON_EQ(kExpected4, manager.GetLegacyCommandDefinitions()); + EXPECT_JSON_EQ(kExpected4, manager_.GetLegacyCommandDefinitions()); } -TEST(ComponentManager, GetLegacyState) { - ComponentManagerImpl manager; +TEST_F(ComponentManagerTest, GetLegacyState) { const char kTraits[] = R"({ "trait1": { "state": { @@ -1426,13 +1404,13 @@ } })"; auto traits = CreateDictionaryValue(kTraits); - ASSERT_TRUE(manager.LoadTraits(*traits, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr)); - ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr)); + ASSERT_TRUE(manager_.LoadTraits(*traits, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp1", {"trait1"}, nullptr)); + ASSERT_TRUE(manager_.AddComponent("", "comp2", {"trait2"}, nullptr)); - ASSERT_TRUE(manager.SetStatePropertiesFromJson( + ASSERT_TRUE(manager_.SetStatePropertiesFromJson( "comp1", R"({"trait1": {"prop1": "foo", "prop2": "bar"}})", nullptr)); - ASSERT_TRUE(manager.SetStatePropertiesFromJson( + ASSERT_TRUE(manager_.SetStatePropertiesFromJson( "comp2", R"({"trait2": {"prop3": "baz", "prop4": "quux"}})", nullptr)); const char kExpected[] = R"({ @@ -1445,10 +1423,10 @@ "prop4": "quux" } })"; - EXPECT_JSON_EQ(kExpected, manager.GetLegacyState()); + EXPECT_JSON_EQ(kExpected, manager_.GetLegacyState()); } -TEST(ComponentManager, TestMockComponentManager) { +TEST_F(ComponentManagerTest, TestMockComponentManager) { // Check that all the virtual methods are mocked out. MockComponentManager mock; }
diff --git a/src/device_manager.cc b/src/device_manager.cc index 7a9021f..cb575b8 100644 --- a/src/device_manager.cc +++ b/src/device_manager.cc
@@ -13,6 +13,7 @@ #include "src/component_manager_impl.h" #include "src/config.h" #include "src/device_registration_info.h" +#include "src/privet/auth_manager.h" #include "src/privet/privet_manager.h" #include "src/string_utils.h" #include "src/utils.h" @@ -26,15 +27,26 @@ provider::DnsServiceDiscovery* dns_sd, provider::HttpServer* http_server, provider::Wifi* wifi, - provider::Bluetooth* bluetooth) { - component_manager_.reset(new ComponentManagerImpl); + provider::Bluetooth* bluetooth) + : config_{new Config{config_store}}, + component_manager_{new ComponentManagerImpl} { + config_->Load(); - std::unique_ptr<Config> config{new Config{config_store}}; - config->Load(); + if (http_server) { + auth_manager_.reset( + new privet::AuthManager(config_->GetSettings().secret, + http_server->GetHttpsCertificateFingerprint())); + + if (auth_manager_->GetSecret() != config_->GetSettings().secret) { + // There is no Config::OnChangedCallback registered. + Config::Transaction transaction(config_.get()); + transaction.set_secret(auth_manager_->GetSecret()); + } + } device_info_.reset(new DeviceRegistrationInfo( - component_manager_.get(), std::move(config), task_runner, http_client, - network)); + config_.get(), component_manager_.get(), task_runner, http_client, + network, auth_manager_.get())); base_api_handler_.reset(new BaseApiHandler{device_info_.get(), this}); device_info_->Start(); @@ -68,8 +80,8 @@ provider::Wifi* wifi, provider::Bluetooth* bluetooth) { privet_.reset(new privet::Manager{task_runner}); - privet_->Start(network, dns_sd, http_server, wifi, device_info_.get(), - component_manager_.get()); + privet_->Start(network, dns_sd, http_server, wifi, auth_manager_.get(), + device_info_.get(), component_manager_.get()); } GcdState DeviceManager::GetGcdState() const { @@ -93,6 +105,10 @@ return component_manager_->GetTraits(); } +void DeviceManager::AddTraitDefsChangedCallback(const base::Closure& callback) { + component_manager_->AddTraitDefChangedCallback(callback); +} + bool DeviceManager::AddComponent(const std::string& name, const std::vector<std::string>& traits, ErrorPtr* error) {
diff --git a/src/device_manager.h b/src/device_manager.h index 3b042eb..d21f398 100644 --- a/src/device_manager.h +++ b/src/device_manager.h
@@ -16,6 +16,7 @@ class DeviceRegistrationInfo; namespace privet { +class AuthManager; class Manager; } // namespace privet @@ -38,6 +39,7 @@ void AddTraitDefinitionsFromJson(const std::string& json) override; void AddTraitDefinitions(const base::DictionaryValue& dict) override; const base::DictionaryValue& GetTraits() const override; + void AddTraitDefsChangedCallback(const base::Closure& callback) override; bool AddComponent(const std::string& name, const std::vector<std::string>& traits, ErrorPtr* error) override; @@ -99,6 +101,8 @@ provider::Wifi* wifi, provider::Bluetooth* bluetooth); + std::unique_ptr<Config> config_; + std::unique_ptr<privet::AuthManager> auth_manager_; std::unique_ptr<ComponentManager> component_manager_; std::unique_ptr<DeviceRegistrationInfo> device_info_; std::unique_ptr<BaseApiHandler> base_api_handler_;
diff --git a/src/device_registration_info.cc b/src/device_registration_info.cc index 110ba81..a6d863c 100644 --- a/src/device_registration_info.cc +++ b/src/device_registration_info.cc
@@ -27,6 +27,7 @@ #include "src/http_constants.h" #include "src/json_error_codes.h" #include "src/notification/xmpp_channel.h" +#include "src/privet/auth_manager.h" #include "src/string_utils.h" #include "src/utils.h" @@ -234,19 +235,32 @@ return code >= http::kContinue && code < http::kBadRequest; } +std::unique_ptr<base::DictionaryValue> BuildDeviceLocalAuth( + const std::string& id, + const std::string& client_token, + const std::string& cert_fingerprint) { + std::unique_ptr<base::DictionaryValue> auth{new base::DictionaryValue}; + auth->SetString("localId", id); + auth->SetString("clientToken", client_token); + auth->SetString("certFingerprint", cert_fingerprint); + return auth; +} + } // anonymous namespace DeviceRegistrationInfo::DeviceRegistrationInfo( + Config* config, ComponentManager* component_manager, - std::unique_ptr<Config> config, provider::TaskRunner* task_runner, provider::HttpClient* http_client, - provider::Network* network) + provider::Network* network, + privet::AuthManager* auth_manager) : http_client_{http_client}, task_runner_{task_runner}, + config_{config}, component_manager_{component_manager}, - config_{std::move(config)}, - network_{network} { + network_{network}, + auth_manager_{auth_manager} { cloud_backoff_policy_.reset(new BackoffEntry::Policy{}); cloud_backoff_policy_->num_errors_to_ignore = 0; cloud_backoff_policy_->initial_delay_ms = 1000; @@ -428,6 +442,9 @@ // Now that we have a new access token, retry the connection. StartNotificationChannel(); } + + SendAuthInfo(); + callback.Run(nullptr); } @@ -628,7 +645,7 @@ access_token_expiration_ = base::Time::Now() + base::TimeDelta::FromSeconds(expires_in); - Config::Transaction change{config_.get()}; + Config::Transaction change{config_}; change.set_cloud_id(cloud_id); change.set_robot_account(robot_account); change.set_refresh_token(refresh_token); @@ -637,6 +654,7 @@ task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {}); StartNotificationChannel(); + SendAuthInfo(); // We're going to respond with our success immediately and we'll connect to // cloud shortly after. @@ -712,8 +730,8 @@ return; } - if (data->allow_response_without_content && - response->GetContentType().empty()) { + if (response->GetContentType().empty()) { + // Assume no body if no content type. cloud_backoff_entry_->InformOfRequest(true); return data->callback.Run({}, nullptr); } @@ -804,7 +822,7 @@ void DeviceRegistrationInfo::UpdateDeviceInfo(const std::string& name, const std::string& description, const std::string& location) { - Config::Transaction change{config_.get()}; + Config::Transaction change{config_}; change.set_name(name); change.set_description(description); change.set_location(location); @@ -818,7 +836,7 @@ void DeviceRegistrationInfo::UpdateBaseConfig(AuthScope anonymous_access_role, bool local_discovery_enabled, bool local_pairing_enabled) { - Config::Transaction change(config_.get()); + Config::Transaction change(config_); change.set_local_anonymous_access_role(anonymous_access_role); change.set_local_discovery_enabled(local_discovery_enabled); change.set_local_pairing_enabled(local_pairing_enabled); @@ -836,7 +854,7 @@ "Unable to change config for registered device"); return false; } - Config::Transaction change{config_.get()}; + Config::Transaction change{config_}; change.set_client_id(client_id); change.set_client_secret(client_secret); change.set_api_key(api_key); @@ -912,6 +930,48 @@ AsWeakPtr())); } +void DeviceRegistrationInfo::SendAuthInfo() { + if (!auth_manager_ || auth_info_update_inprogress_) + return; + auth_info_update_inprogress_ = true; + + std::string id = GetSettings().device_id; + std::string token = Base64Encode(auth_manager_->GetRootDeviceToken()); + std::string fingerprint = + Base64Encode(auth_manager_->GetCertificateFingerprint()); + + std::unique_ptr<base::DictionaryValue> auth = + BuildDeviceLocalAuth(id, token, fingerprint); + + // TODO(vitalybuka): Remove args from URL when server is ready. + std::string url = + GetDeviceURL("upsertLocalAuthInfo", {{"localid", id}, + {"clienttoken", token}, + {"certfingerprint", fingerprint}}); + DoCloudRequest( + HttpClient::Method::kPost, url, auth.get(), + base::Bind(&DeviceRegistrationInfo::OnSendAuthInfoDone, AsWeakPtr())); +} + +void DeviceRegistrationInfo::OnSendAuthInfoDone( + const base::DictionaryValue& body, + ErrorPtr error) { + CHECK(auth_info_update_inprogress_); + auth_info_update_inprogress_ = false; + + if (!error) { + // TODO(vitalybuka): Enable this when we start uploading real data. + // Config::Transaction change{config_.get()}; + // change.set_local_auth_info_changed(false); + // change.Commit(); + return; + } + + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&DeviceRegistrationInfo::SendAuthInfo, AsWeakPtr()), + {}); +} + void DeviceRegistrationInfo::OnDeviceInfoRetrieved( const base::DictionaryValue& device_info, ErrorPtr error) { @@ -1270,7 +1330,7 @@ connected_to_cloud_ = false; LOG(INFO) << "Device is unregistered from the cloud. Deleting credentials"; - Config::Transaction change{config_.get()}; + Config::Transaction change{config_}; // Keep cloud_id to switch to detect kInvalidCredentials after restart. change.set_robot_account(""); change.set_refresh_token("");
diff --git a/src/device_registration_info.h b/src/device_registration_info.h index bacab48..04fb7b6 100644 --- a/src/device_registration_info.h +++ b/src/device_registration_info.h
@@ -41,6 +41,10 @@ class TaskRunner; } +namespace privet { +class AuthManager; +} + extern const char kErrorDomainOAuth2[]; extern const char kErrorDomainGCD[]; extern const char kErrorDomainGCDServer[]; @@ -53,12 +57,12 @@ base::Callback<void(const base::DictionaryValue& response, ErrorPtr error)>; - DeviceRegistrationInfo( - ComponentManager* component_manager, - std::unique_ptr<Config> config, - provider::TaskRunner* task_runner, - provider::HttpClient* http_client, - provider::Network* network); + DeviceRegistrationInfo(Config* config, + ComponentManager* component_manager, + provider::TaskRunner* task_runner, + provider::HttpClient* http_client, + provider::Network* network, + privet::AuthManager* auth_manager); ~DeviceRegistrationInfo() override; @@ -116,7 +120,7 @@ // TODO(vitalybuka): remove getters and pass config to dependent code. const Config::Settings& GetSettings() const { return config_->GetSettings(); } - Config* GetMutableConfig() { return config_.get(); } + Config* GetMutableConfig() { return config_; } GcdState GetGcdState() const { return gcd_state_; } @@ -178,8 +182,6 @@ provider::HttpClient::Method method; std::string url; std::string body; - // Workaround for inconsistent APIs which returns no body. - bool allow_response_without_content = false; CloudRequestDoneCallback callback; }; void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data); @@ -199,6 +201,9 @@ ErrorPtr error); void OnUpdateDeviceResourceError(ErrorPtr error); + void SendAuthInfo(); + void OnSendAuthInfoDone(const base::DictionaryValue& body, ErrorPtr error); + // Callback from GetDeviceInfo() to retrieve the device resource timestamp // and retry UpdateDeviceResource() call. void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info, @@ -301,11 +306,12 @@ provider::HttpClient* http_client_{nullptr}; provider::TaskRunner* task_runner_{nullptr}; + + Config* config_{nullptr}; + // Global component manager. ComponentManager* component_manager_{nullptr}; - std::unique_ptr<Config> config_; - // Backoff manager for DoCloudRequest() method. std::unique_ptr<BackoffEntry::Policy> cloud_backoff_policy_; std::unique_ptr<BackoffEntry> cloud_backoff_entry_; @@ -331,12 +337,15 @@ // is in flight to the cloud server. ResourceUpdateCallbackList queued_resource_update_callbacks_; + bool auth_info_update_inprogress_{false}; + std::unique_ptr<NotificationChannel> primary_notification_channel_; std::unique_ptr<PullChannel> pull_channel_; NotificationChannel* current_notification_channel_{nullptr}; bool notification_channel_starting_{false}; provider::Network* network_{nullptr}; + privet::AuthManager* auth_manager_{nullptr}; // Tracks our GCD state. GcdState gcd_state_{GcdState::kUnconfigured};
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc index 72405dc..cc519d2 100644 --- a/src/device_registration_info_unittest.cc +++ b/src/device_registration_info_unittest.cc
@@ -16,9 +16,12 @@ #include "src/bind_lambda.h" #include "src/component_manager_impl.h" #include "src/http_constants.h" +#include "src/privet/auth_manager.h" +#include "src/test/mock_clock.h" using testing::_; using testing::AtLeast; +using testing::HasSubstr; using testing::Invoke; using testing::InvokeWithoutArgs; using testing::Mock; @@ -49,6 +52,7 @@ "fkjh7f.apps.googleusercontent.com"; const char kClientSecret[] = "5sdGdGlfolGlrFKfdFlgP6FG"; const char kCloudId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196"; +const char kDeviceId[] = "f6885e46-b432-42d7-86a5-d759bfb61f62"; const char kClaimTicketId[] = "RTcUE"; const char kAccessToken[] = "ya29.1.AADtN_V-dLUM-sVZ0qVjG9Dxm5NgdS9J" @@ -63,6 +67,12 @@ const char kRobotAccountEmail[] = "6ed0b3f54f9bd619b942f4ad2441c252@" "clouddevices.gserviceaccount.com"; +const char kAuthInfo[] = R"({ + "certFingerprint": + "FQY6BEINDjw3FgsmYChRWgMzMhc4TC8uG0UUUFhdDz0=", + "clientToken": "UBPkqttkiWt5VWgICLP0eHuCQgECRgMaVm0+gA==", + "localId": "f6885e46-b432-42d7-86a5-d759bfb61f62" +})"; } // namespace test_data @@ -113,11 +123,11 @@ class DeviceRegistrationInfoTest : public ::testing::Test { protected: void SetUp() override { - std::unique_ptr<Config> config{new Config{&config_store_}}; - config_ = config.get(); - dev_reg_.reset(new DeviceRegistrationInfo{&component_manager_, - std::move(config), &task_runner_, - &http_client_, nullptr}); + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(base::Time::FromTimeT(1450000000))); + dev_reg_.reset(new DeviceRegistrationInfo{&config_, &component_manager_, + &task_runner_, &http_client_, + nullptr, &auth_}); ReloadDefaults(); } @@ -139,7 +149,7 @@ settings->service_url = test_data::kServiceURL; return true; })); - config_->Load(); + config_.Load(); dev_reg_->Start(); } @@ -148,6 +158,7 @@ dict.SetString("refresh_token", test_data::kRefreshToken); dict.SetString("cloud_id", test_data::kCloudId); dict.SetString("robot_account", test_data::kRobotAccountEmail); + dict.SetString("device_id", test_data::kDeviceId); std::string json_string; base::JSONWriter::WriteWithOptions( dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string); @@ -184,7 +195,14 @@ provider::test::MockConfigStore config_store_; StrictMock<MockHttpClient> http_client_; base::DictionaryValue data_; - Config* config_{nullptr}; + Config config_{&config_store_}; + test::MockClock clock_; + privet::AuthManager auth_{ + {68, 52, 36, 95, 74, 89, 25, 2, 31, 5, 65, 87, 64, 32, 17, 26, 8, 73, 57, + 16, 33, 82, 71, 10, 72, 62, 45, 1, 77, 97, 70, 24}, + {21, 6, 58, 4, 66, 13, 14, 60, 55, 22, 11, 38, 96, 40, 81, 90, 3, 51, 50, + 23, 56, 76, 47, 46, 27, 69, 20, 80, 88, 93, 15, 61}, + &clock_}; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; ComponentManagerImpl component_manager_; }; @@ -244,6 +262,18 @@ callback.Run(ReplyWithJson(200, json), nullptr); }))); + EXPECT_CALL( + http_client_, + SendRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"), + HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _)) + .WillOnce(WithArgs<3, 4>( + Invoke([](const std::string& data, + const HttpClient::SendRequestCallback& callback) { + EXPECT_JSON_EQ(test_data::kAuthInfo, *CreateDictionaryValue(data)); + base::DictionaryValue json; + callback.Run(ReplyWithJson(200, json), nullptr); + }))); + EXPECT_TRUE(RefreshAccessToken(nullptr)); EXPECT_TRUE(HaveRegistrationCredentials()); } @@ -340,6 +370,8 @@ } TEST_F(DeviceRegistrationInfoTest, RegisterDevice) { + ReloadSettings(); + auto json_traits = CreateDictionaryValue(R"({ 'base': { 'commands': { @@ -522,6 +554,18 @@ callback.Run(ReplyWithJson(200, json), nullptr); }))); + EXPECT_CALL( + http_client_, + SendRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"), + HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _)) + .WillOnce(WithArgs<3, 4>( + Invoke([](const std::string& data, + const HttpClient::SendRequestCallback& callback) { + EXPECT_JSON_EQ(test_data::kAuthInfo, *CreateDictionaryValue(data)); + base::DictionaryValue json; + callback.Run(ReplyWithJson(200, json), nullptr); + }))); + bool done = false; dev_reg_->RegisterDevice( test_data::kClaimTicketId, base::Bind([this, &done](ErrorPtr error) {
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc index 0864242..49ef787 100644 --- a/src/privet/auth_manager.cc +++ b/src/privet/auth_manager.cc
@@ -11,12 +11,18 @@ #include "src/privet/openssl_utils.h" #include "src/string_utils.h" +extern "C" { +#include "third_party/libuweave/src/macaroon.h" +} + namespace weave { namespace privet { namespace { const char kTokenDelimeter[] = ":"; +const size_t kCaveatBuffetSize = 32; +const size_t kMaxMacaroonSize = 1024; // Returns "scope:id:time". std::string CreateTokenData(const UserInfo& user_info, const base::Time& time) { @@ -49,11 +55,30 @@ return UserInfo{static_cast<AuthScope>(scope), id}; } +class Caveat { + public: + Caveat(UwMacaroonCaveatType type, uint32_t value) { + CHECK(uw_macaroon_caveat_create_with_uint_(type, value, buffer, + sizeof(buffer), &caveat)); + } + + const UwMacaroonCaveat& GetCaveat() const { return caveat; } + + private: + UwMacaroonCaveat caveat; + uint8_t buffer[kCaveatBuffetSize]; + + DISALLOW_COPY_AND_ASSIGN(Caveat); +}; + } // namespace AuthManager::AuthManager(const std::vector<uint8_t>& secret, - const std::vector<uint8_t>& certificate_fingerprint) - : secret_{secret}, certificate_fingerprint_{certificate_fingerprint} { + const std::vector<uint8_t>& certificate_fingerprint, + base::Clock* clock) + : clock_{clock ? clock : &default_clock_}, + secret_{secret}, + certificate_fingerprint_{certificate_fingerprint} { if (secret_.size() != kSha256OutputSize) { secret_.resize(kSha256OutputSize); base::RandBytes(secret_.data(), secret_.size()); @@ -63,9 +88,8 @@ AuthManager::~AuthManager() {} // Returns "[hmac]scope:id:time". -std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info, - const base::Time& time) { - std::string data_str{CreateTokenData(user_info, time)}; +std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info) { + std::string data_str{CreateTokenData(user_info, Now())}; std::vector<uint8_t> data{data_str.begin(), data_str.end()}; std::vector<uint8_t> hash{HmacSha256(secret_, data)}; hash.insert(hash.end(), data.begin(), data.end()); @@ -84,5 +108,29 @@ return SplitTokenData(std::string(data.begin(), data.end()), time); } +std::vector<uint8_t> AuthManager::GetRootDeviceToken() const { + Caveat scope{kUwMacaroonCaveatTypeScope, kUwMacaroonCaveatScopeTypeOwner}; + Caveat issued{kUwMacaroonCaveatTypeIssued, + static_cast<uint32_t>(Now().ToTimeT())}; + + UwMacaroonCaveat caveats[] = { + scope.GetCaveat(), issued.GetCaveat(), + }; + + UwMacaroon macaroon{}; + CHECK(uw_macaroon_new_from_root_key_( + &macaroon, secret_.data(), secret_.size(), caveats, arraysize(caveats))); + + std::vector<uint8_t> token(kMaxMacaroonSize); + size_t len = 0; + CHECK(uw_macaroon_dump_(&macaroon, token.data(), token.size(), &len)); + token.resize(len); + return token; +} + +base::Time AuthManager::Now() const { + return clock_->Now(); +} + } // namespace privet } // namespace weave
diff --git a/src/privet/auth_manager.h b/src/privet/auth_manager.h index 607b820..b3a9599 100644 --- a/src/privet/auth_manager.h +++ b/src/privet/auth_manager.h
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include <base/time/default_clock.h> #include <base/time/time.h> #include <weave/error.h> @@ -19,11 +20,11 @@ class AuthManager { public: AuthManager(const std::vector<uint8_t>& secret, - const std::vector<uint8_t>& certificate_fingerprint); + const std::vector<uint8_t>& certificate_fingerprint, + base::Clock* clock = nullptr); ~AuthManager(); - std::vector<uint8_t> CreateAccessToken(const UserInfo& user_info, - const base::Time& time); + std::vector<uint8_t> CreateAccessToken(const UserInfo& user_info); UserInfo ParseAccessToken(const std::vector<uint8_t>& token, base::Time* time) const; @@ -31,8 +32,14 @@ const std::vector<uint8_t>& GetCertificateFingerprint() const { return certificate_fingerprint_; } + std::vector<uint8_t> GetRootDeviceToken() const; + + base::Time Now() const; private: + base::DefaultClock default_clock_; + base::Clock* clock_{nullptr}; + std::vector<uint8_t> secret_; std::vector<uint8_t> certificate_fingerprint_;
diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc index 6b9ae29..4847e01 100644 --- a/src/privet/auth_manager_unittest.cc +++ b/src/privet/auth_manager_unittest.cc
@@ -4,19 +4,41 @@ #include "src/privet/auth_manager.h" +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <weave/settings.h> +#include "src/data_encoding.h" +#include "src/test/mock_clock.h" + +using testing::Return; + namespace weave { namespace privet { class AuthManagerTest : public testing::Test { public: - void SetUp() override {} + void SetUp() override { + EXPECT_GE(auth_.GetSecret().size(), 32u); + EXPECT_GE(auth_.GetCertificateFingerprint().size(), 32u); + + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(base::Time::FromTimeT(1410000000))); + } protected: - const base::Time time_ = base::Time::FromTimeT(1410000000); - AuthManager auth_{{}, {}}; + const std::vector<uint8_t> kSecret{69, 53, 17, 37, 80, 73, 2, 5, 79, 64, 41, + 57, 12, 54, 65, 63, 72, 74, 93, 81, 20, 95, + 89, 3, 94, 92, 27, 21, 49, 90, 36, 6}; + const std::vector<uint8_t> kSecret2{ + 78, 40, 39, 68, 29, 19, 70, 86, 38, 61, 13, 55, 33, 32, 51, 52, + 34, 43, 97, 48, 8, 56, 11, 99, 50, 59, 24, 26, 31, 71, 76, 28}; + const std::vector<uint8_t> kFingerprint{ + 22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, + 60, 62, 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85}; + + test::MockClock clock_; + AuthManager auth_{kSecret, kFingerprint, &clock_}; }; TEST_F(AuthManagerTest, RandomSecret) { @@ -24,63 +46,92 @@ } TEST_F(AuthManagerTest, DifferentSecret) { - AuthManager auth{{}, {}}; + AuthManager auth{kSecret2, {}}; + EXPECT_GE(auth.GetSecret().size(), 32u); EXPECT_NE(auth_.GetSecret(), auth.GetSecret()); } TEST_F(AuthManagerTest, Constructor) { - std::vector<uint8_t> secret; - std::vector<uint8_t> fingerpint; - for (uint8_t i = 0; i < 32; ++i) { - secret.push_back(i); - fingerpint.push_back(i + 100); - } + EXPECT_EQ(kSecret, auth_.GetSecret()); + EXPECT_EQ(kFingerprint, auth_.GetCertificateFingerprint()); +} - AuthManager auth{secret, fingerpint}; - EXPECT_EQ(secret, auth.GetSecret()); - EXPECT_EQ(fingerpint, auth.GetCertificateFingerprint()); +TEST_F(AuthManagerTest, CreateAccessToken) { + EXPECT_EQ( + "OUH2L2npY+Gzwjf9AnqigGSK3hxIVR+xX8/Cnu4DGf8wOjA6MTQxMDAwMDAwMA==", + Base64Encode(auth_.CreateAccessToken(UserInfo{AuthScope::kNone, 123}))); + EXPECT_EQ( + "iZx0qgEHFF5lq+Q503GtgU0d6gLQ9TlLsU+DcFbZb2QxOjIzNDoxNDEwMDAwMDAw", + Base64Encode(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 234}))); + EXPECT_EQ( + "fTjecsbwtYj6i8/qPJz900B8EMAjRqU8jLT9kfMoz0czOjQ1NjoxNDEwMDAwMDAw", + Base64Encode(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}))); + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(clock_.Now() + base::TimeDelta::FromDays(11))); + EXPECT_EQ( + "qAmlJykiPTnFljfOKSf3BUII9YZG8/ttzD76q+fII1YyOjM0NToxNDEwOTUwNDAw", + Base64Encode(auth_.CreateAccessToken(UserInfo{AuthScope::kUser, 345}))); } TEST_F(AuthManagerTest, CreateSameToken) { - EXPECT_EQ(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_), - auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_)); + EXPECT_EQ(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}), + auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555})); } TEST_F(AuthManagerTest, CreateTokenDifferentScope) { - EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 456}, time_), - auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_)); + EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 456}), + auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456})); } TEST_F(AuthManagerTest, CreateTokenDifferentUser) { - EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_), - auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 789}, time_)); + EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}), + auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 789})); } TEST_F(AuthManagerTest, CreateTokenDifferentTime) { - EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}, time_), - auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}, - base::Time::FromTimeT(1400000000))); + auto token = auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}); + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(base::Time::FromTimeT(1400000000))); + EXPECT_NE(token, auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567})); } TEST_F(AuthManagerTest, CreateTokenDifferentInstance) { - EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_), - AuthManager({}, {}) - .CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_)); + EXPECT_NE( + auth_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}), + AuthManager({}, {}).CreateAccessToken(UserInfo{AuthScope::kUser, 123})); } TEST_F(AuthManagerTest, ParseAccessToken) { // Multiple attempts with random secrets. for (size_t i = 0; i < 1000; ++i) { - AuthManager auth{{}, {}}; + AuthManager auth{{}, {}, &clock_}; - auto token = auth.CreateAccessToken(UserInfo{AuthScope::kUser, 5}, time_); + auto token = auth.CreateAccessToken(UserInfo{AuthScope::kUser, 5}); base::Time time2; EXPECT_EQ(AuthScope::kUser, auth.ParseAccessToken(token, &time2).scope()); EXPECT_EQ(5u, auth.ParseAccessToken(token, &time2).user_id()); // Token timestamp resolution is one second. - EXPECT_GE(1, std::abs((time_ - time2).InSeconds())); + EXPECT_GE(1, std::abs((clock_.Now() - time2).InSeconds())); } } +TEST_F(AuthManagerTest, GetRootDeviceToken) { + EXPECT_EQ("UFTBUcgd9d0HnPRnLeroN2mCQgECRgMaVArkgA==", + Base64Encode(auth_.GetRootDeviceToken())); +} + +TEST_F(AuthManagerTest, GetRootDeviceTokenDifferentTime) { + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(clock_.Now() + base::TimeDelta::FromDays(15))); + EXPECT_EQ("UGKqwMYGQNOd8jeYFDOsM02CQgECRgMaVB6rAA==", + Base64Encode(auth_.GetRootDeviceToken())); +} + +TEST_F(AuthManagerTest, GetRootDeviceTokenDifferentSecret) { + AuthManager auth{kSecret2, {}, &clock_}; + EXPECT_EQ("UK1ACOc3cWGjGBoTIX2bd3qCQgECRgMaVArkgA==", + Base64Encode(auth.GetRootDeviceToken())); +} + } // namespace privet } // namespace weave
diff --git a/src/privet/device_ui_kind.cc b/src/privet/device_ui_kind.cc index ed7c5ff..8909bed 100644 --- a/src/privet/device_ui_kind.cc +++ b/src/privet/device_ui_kind.cc
@@ -4,42 +4,40 @@ #include "src/privet/device_ui_kind.h" +#include <unordered_map> + #include <base/logging.h> namespace weave { namespace privet { std::string GetDeviceUiKind(const std::string& manifest_id) { + + // Map of device short id to ui device kind + static const std::unordered_map<std::string, std::string> device_kind_map = { + {"AC", "accessPoint"}, + {"AK", "aggregator"}, + {"AM", "camera"}, + {"AB", "developmentBoard"}, + {"AH", "acHeating"}, + {"AI", "light"}, + {"AO", "lock"}, + {"AE", "printer"}, + {"AF", "scanner"}, + {"AD", "speaker"}, + {"AL", "storage"}, + {"AJ", "toy"}, + {"AA", "vendor"}, + {"AN", "video"} + }; + CHECK_EQ(5u, manifest_id.size()); - std::string kind = manifest_id.substr(0, 2); - if (kind == "AC") - return "accessPoint"; - if (kind == "AK") - return "aggregator"; - if (kind == "AM") - return "camera"; - if (kind == "AB") - return "developmentBoard"; - if (kind == "AH") - return "acHeating"; - if (kind == "AI") - return "light"; - if (kind == "AO") - return "lock"; - if (kind == "AE") - return "printer"; - if (kind == "AF") - return "scanner"; - if (kind == "AD") - return "speaker"; - if (kind == "AL") - return "storage"; - if (kind == "AJ") - return "toy"; - if (kind == "AA") - return "vendor"; - if (kind == "AN") - return "video"; + std::string short_id = manifest_id.substr(0, 2); + + auto iter = device_kind_map.find(short_id); + if (iter != device_kind_map.end()) + return iter->second; + LOG(FATAL) << "Invalid model id: " << manifest_id; return std::string(); }
diff --git a/src/privet/mock_delegates.h b/src/privet/mock_delegates.h index 0cfa4ed..5c003a7 100644 --- a/src/privet/mock_delegates.h +++ b/src/privet/mock_delegates.h
@@ -62,8 +62,7 @@ class MockSecurityDelegate : public SecurityDelegate { public: - MOCK_METHOD2(CreateAccessToken, - std::string(const UserInfo&, const base::Time&)); + MOCK_METHOD1(CreateAccessToken, std::string(const UserInfo&)); MOCK_CONST_METHOD2(ParseAccessToken, UserInfo(const std::string&, base::Time*)); MOCK_CONST_METHOD0(GetPairingTypes, std::set<PairingType>()); @@ -81,7 +80,7 @@ MOCK_METHOD2(CancelPairing, bool(const std::string&, ErrorPtr*)); MockSecurityDelegate() { - EXPECT_CALL(*this, CreateAccessToken(_, _)) + EXPECT_CALL(*this, CreateAccessToken(_)) .WillRepeatedly(Return("GuestAccessToken")); EXPECT_CALL(*this, ParseAccessToken(_, _))
diff --git a/src/privet/openssl_utils.cc b/src/privet/openssl_utils.cc index f7bee9b..f38fd1a 100644 --- a/src/privet/openssl_utils.cc +++ b/src/privet/openssl_utils.cc
@@ -19,8 +19,8 @@ const std::vector<uint8_t>& data) { std::vector<uint8_t> mac(kSha256OutputSize); uint8_t hmac_state[uw_crypto_hmac_required_buffer_size_()]; - CHECK_EQ(0u, uw_crypto_hmac_init_(hmac_state, sizeof(hmac_state), key.data(), - key.size())); + CHECK(uw_crypto_hmac_init_(hmac_state, sizeof(hmac_state), key.data(), + key.size())); CHECK(uw_crypto_hmac_update_(hmac_state, sizeof(hmac_state), data.data(), data.size())); CHECK(uw_crypto_hmac_final_(hmac_state, sizeof(hmac_state), mac.data(),
diff --git a/src/privet/privet_handler.cc b/src/privet/privet_handler.cc index 0c9887f..b71ea15 100644 --- a/src/privet/privet_handler.cc +++ b/src/privet/privet_handler.cc
@@ -676,10 +676,9 @@ } base::DictionaryValue output; - output.SetString( - kAuthAccessTokenKey, - security_->CreateAccessToken( - UserInfo{requested_auth_scope, ++last_user_id_}, base::Time::Now())); + output.SetString(kAuthAccessTokenKey, + security_->CreateAccessToken( + UserInfo{requested_auth_scope, ++last_user_id_})); output.SetString(kAuthTokenTypeKey, kAuthorizationHeaderPrefix); output.SetInteger(kAuthExpiresInKey, kAccessTokenExpirationSeconds); output.SetString(kAuthScopeKey, EnumToString(requested_auth_scope));
diff --git a/src/privet/privet_handler_unittest.cc b/src/privet/privet_handler_unittest.cc index 13c1999..09146e8 100644 --- a/src/privet/privet_handler_unittest.cc +++ b/src/privet/privet_handler_unittest.cc
@@ -392,7 +392,7 @@ TEST_F(PrivetHandlerTest, AuthPairing) { EXPECT_CALL(security_, IsValidPairingCode("testToken")) .WillRepeatedly(Return(true)); - EXPECT_CALL(security_, CreateAccessToken(_, _)) + EXPECT_CALL(security_, CreateAccessToken(_)) .WillRepeatedly(Return("OwnerAccessToken")); const char kInput[] = R"({ 'mode': 'pairing',
diff --git a/src/privet/privet_manager.cc b/src/privet/privet_manager.cc index a308eec..16c552e 100644 --- a/src/privet/privet_manager.cc +++ b/src/privet/privet_manager.cc
@@ -47,8 +47,12 @@ DnsServiceDiscovery* dns_sd, HttpServer* http_server, Wifi* wifi, + AuthManager* auth_manager, DeviceRegistrationInfo* device, ComponentManager* component_manager) { + CHECK(auth_manager); + CHECK(device); + disable_security_ = device->GetSettings().disable_security; device_ = DeviceDelegate::CreateDefault( @@ -58,19 +62,9 @@ component_manager); cloud_observer_.Add(cloud_.get()); - auth_.reset(new AuthManager(device->GetSettings().secret, - http_server->GetHttpsCertificateFingerprint())); security_.reset(new SecurityManager( - auth_.get(), device->GetSettings().pairing_modes, + auth_manager, device->GetSettings().pairing_modes, device->GetSettings().embedded_code, disable_security_, task_runner_)); - if (device->GetSettings().secret.empty()) { - // TODO(vitalybuka): Post all Config::Transaction to avoid following. - task_runner_->PostDelayedTask( - FROM_HERE, - base::Bind(&Manager::SaveDeviceSecret, weak_ptr_factory_.GetWeakPtr(), - base::Unretained(device->GetMutableConfig())), - {}); - } network->AddConnectionChangedCallback( base::Bind(&Manager::OnConnectivityChanged, base::Unretained(this))); @@ -174,10 +168,5 @@ OnChanged(); } -void Manager::SaveDeviceSecret(Config* config) { - Config::Transaction transaction(config); - transaction.set_secret(auth_->GetSecret()); -} - } // namespace privet } // namespace weave
diff --git a/src/privet/privet_manager.h b/src/privet/privet_manager.h index 1342584..371d843 100644 --- a/src/privet/privet_manager.h +++ b/src/privet/privet_manager.h
@@ -50,6 +50,7 @@ provider::DnsServiceDiscovery* dns_sd, provider::HttpServer* http_server, provider::Wifi* wifi, + AuthManager* auth_manager, DeviceRegistrationInfo* device, ComponentManager* component_manager); @@ -78,13 +79,10 @@ void OnChanged(); void OnConnectivityChanged(); - void SaveDeviceSecret(Config* config); - bool disable_security_{false}; provider::TaskRunner* task_runner_{nullptr}; std::unique_ptr<CloudDelegate> cloud_; std::unique_ptr<DeviceDelegate> device_; - std::unique_ptr<AuthManager> auth_; std::unique_ptr<SecurityManager> security_; std::unique_ptr<WifiBootstrapManager> wifi_bootstrap_manager_; std::unique_ptr<Publisher> publisher_;
diff --git a/src/privet/security_delegate.h b/src/privet/security_delegate.h index 40f297f..051bf20 100644 --- a/src/privet/security_delegate.h +++ b/src/privet/security_delegate.h
@@ -22,8 +22,7 @@ virtual ~SecurityDelegate() {} // Creates access token for the given scope, user id and |time|. - virtual std::string CreateAccessToken(const UserInfo& user_info, - const base::Time& time) = 0; + virtual std::string CreateAccessToken(const UserInfo& user_info) = 0; // Validates |token| and returns scope and user id parsed from that. virtual UserInfo ParseAccessToken(const std::string& token,
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc index a838dae..00550d1 100644 --- a/src/privet/security_manager.cc +++ b/src/privet/security_manager.cc
@@ -141,9 +141,8 @@ } // Returns "base64([hmac]scope:id:time)". -std::string SecurityManager::CreateAccessToken(const UserInfo& user_info, - const base::Time& time) { - return Base64Encode(auth_manager_->CreateAccessToken(user_info, time)); +std::string SecurityManager::CreateAccessToken(const UserInfo& user_info) { + return Base64Encode(auth_manager_->CreateAccessToken(user_info)); } // Parses "base64([hmac]scope:id:time)". @@ -344,7 +343,7 @@ if (is_security_disabled_) return true; - if (block_pairing_until_ > base::Time::Now()) { + if (block_pairing_until_ > auth_manager_->Now()) { Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kDeviceBusy, "Too many pairing attempts"); return false; @@ -353,7 +352,7 @@ if (++pairing_attemts_ >= kMaxAllowedPairingAttemts) { LOG(INFO) << "Pairing blocked for" << kPairingBlockingTimeMinutes << "minutes."; - block_pairing_until_ = base::Time::Now(); + block_pairing_until_ = auth_manager_->Now(); block_pairing_until_ += base::TimeDelta::FromMinutes(kPairingBlockingTimeMinutes); }
diff --git a/src/privet/security_manager.h b/src/privet/security_manager.h index 26a42d4..7a3c56a 100644 --- a/src/privet/security_manager.h +++ b/src/privet/security_manager.h
@@ -60,8 +60,7 @@ ~SecurityManager() override; // SecurityDelegate methods - std::string CreateAccessToken(const UserInfo& user_info, - const base::Time& time) override; + std::string CreateAccessToken(const UserInfo& user_info) override; UserInfo ParseAccessToken(const std::string& token, base::Time* time) const override; std::set<PairingType> GetPairingTypes() const override; @@ -84,6 +83,8 @@ void RegisterPairingListeners(const PairingStartListener& on_start, const PairingEndListener& on_end); + const AuthManager* GetAuthManager() const { return auth_manager_; } + private: FRIEND_TEST_ALL_PREFIXES(SecurityManagerTest, ThrottlePairing); // Allows limited number of new sessions without successful authorization.
diff --git a/src/privet/security_manager_unittest.cc b/src/privet/security_manager_unittest.cc index 6236d78..d9f5c56 100644 --- a/src/privet/security_manager_unittest.cc +++ b/src/privet/security_manager_unittest.cc
@@ -24,10 +24,12 @@ #include "src/data_encoding.h" #include "src/privet/auth_manager.h" #include "src/privet/openssl_utils.h" +#include "src/test/mock_clock.h" #include "third_party/chromium/crypto/p224_spake.h" -using testing::Eq; using testing::_; +using testing::Eq; +using testing::Return; namespace weave { namespace privet { @@ -57,6 +59,11 @@ class SecurityManagerTest : public testing::Test { protected: + void SetUp() override { + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(base::Time::FromTimeT(1410000000))); + } + void PairAndAuthenticate(std::string* fingerprint, std::string* signature) { std::string session_id; std::string device_commitment_base64; @@ -94,13 +101,15 @@ const base::Time time_ = base::Time::FromTimeT(1410000000); provider::test::FakeTaskRunner task_runner_; + test::MockClock clock_; AuthManager auth_manager_{ {}, {{ 59, 47, 77, 247, 129, 187, 188, 158, 172, 105, 246, 93, 102, 83, 8, 138, 176, 141, 37, 63, 223, 40, 153, 121, 134, 23, 120, 106, 24, 205, 7, 135, - }}}; + }}, + &clock_}; SecurityManager security_{&auth_manager_, {PairingType::kEmbeddedCode}, "1234", @@ -109,56 +118,54 @@ }; TEST_F(SecurityManagerTest, IsBase64) { - EXPECT_TRUE(IsBase64( - security_.CreateAccessToken(UserInfo{AuthScope::kUser, 7}, time_))); + EXPECT_TRUE( + IsBase64(security_.CreateAccessToken(UserInfo{AuthScope::kUser, 7}))); } TEST_F(SecurityManagerTest, CreateSameToken) { - EXPECT_EQ( - security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_), - security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_)); + EXPECT_EQ(security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}), + security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555})); } TEST_F(SecurityManagerTest, CreateTokenDifferentScope) { - EXPECT_NE( - security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 456}, time_), - security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_)); + EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 456}), + security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456})); } TEST_F(SecurityManagerTest, CreateTokenDifferentUser) { - EXPECT_NE( - security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_), - security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 789}, time_)); + EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}), + security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 789})); } TEST_F(SecurityManagerTest, CreateTokenDifferentTime) { - EXPECT_NE( - security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}, time_), - security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}, - base::Time::FromTimeT(1400000000))); + auto token = security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}); + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(base::Time::FromTimeT(1400000000))); + EXPECT_NE(token, + security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567})); } TEST_F(SecurityManagerTest, CreateTokenDifferentInstance) { - AuthManager auth{{}, {}}; - EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_), + AuthManager auth{{}, {}, &clock_}; + EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}), SecurityManager(&auth, {}, "", false, &task_runner_) - .CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_)); + .CreateAccessToken(UserInfo{AuthScope::kUser, 123})); } TEST_F(SecurityManagerTest, ParseAccessToken) { // Multiple attempts with random secrets. for (size_t i = 0; i < 1000; ++i) { - AuthManager auth{{}, {}}; + AuthManager auth{{}, {}, &clock_}; SecurityManager security{&auth, {}, "", false, &task_runner_}; std::string token = - security.CreateAccessToken(UserInfo{AuthScope::kUser, 5}, time_); + security.CreateAccessToken(UserInfo{AuthScope::kUser, 5}); base::Time time2; EXPECT_EQ(AuthScope::kUser, security.ParseAccessToken(token, &time2).scope()); EXPECT_EQ(5u, security.ParseAccessToken(token, &time2).user_id()); // Token timestamp resolution is one second. - EXPECT_GE(1, std::abs((time_ - time2).InSeconds())); + EXPECT_GE(1, std::abs((clock_.Now() - time2).InSeconds())); } } @@ -251,13 +258,13 @@ EXPECT_TRUE(pair()); EXPECT_TRUE(pair()); EXPECT_FALSE(pair()); - EXPECT_GT(security_.block_pairing_until_, base::Time::Now()); + EXPECT_GT(security_.block_pairing_until_, clock_.Now()); EXPECT_LE(security_.block_pairing_until_, - base::Time::Now() + base::TimeDelta::FromMinutes(15)); + clock_.Now() + base::TimeDelta::FromMinutes(15)); // Wait timeout. security_.block_pairing_until_ = - base::Time::Now() - base::TimeDelta::FromMinutes(1); + clock_.Now() - base::TimeDelta::FromMinutes(1); // Allow exactly one attempt. EXPECT_TRUE(pair()); @@ -265,7 +272,7 @@ // Wait timeout. security_.block_pairing_until_ = - base::Time::Now() - base::TimeDelta::FromMinutes(1); + clock_.Now() - base::TimeDelta::FromMinutes(1); // Completely unblock by successfully pairing. std::string fingerprint;
diff --git a/src/test/mock_clock.h b/src/test/mock_clock.h new file mode 100644 index 0000000..f1a2260 --- /dev/null +++ b/src/test/mock_clock.h
@@ -0,0 +1,22 @@ +// Copyright 2015 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_TEST_MOCK_CLOCK_H_ +#define LIBWEAVE_SRC_TEST_MOCK_CLOCK_H_ + +#include <base/time/clock.h> +#include <gmock/gmock.h> + +namespace weave { +namespace test { + +class MockClock : public base::Clock { + public: + MOCK_METHOD0(Now, base::Time()); +}; + +} // namespace test +} // namespace weave + +#endif // LIBWEAVE_SRC_TEST_MOCK_CLOCK_H_
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc index 66850cf..24c6973 100644 --- a/src/weave_unittest.cc +++ b/src/weave_unittest.cc
@@ -183,10 +183,11 @@ protected: void SetUp() override {} + template <class UrlMatcher> void ExpectRequest(HttpClient::Method method, - const std::string& url, + const UrlMatcher& url_matcher, const std::string& json_response) { - EXPECT_CALL(http_client_, SendRequest(method, url, _, _, _)) + EXPECT_CALL(http_client_, SendRequest(method, url_matcher, _, _, _)) .WillOnce(WithArgs<4>(Invoke([json_response]( const HttpClient::SendRequestCallback& callback) { std::unique_ptr<provider::test::MockHttpClientResponse> response{ @@ -400,6 +401,9 @@ "https://accounts.google.com/o/oauth2/token", kAuthTokenResponse); + ExpectRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"), + {}); + InitDnsSdPublishing(true, "DB"); bool done = false;
diff --git a/third_party/libuweave/src/crypto_hmac.c b/third_party/libuweave/src/crypto_hmac.c index 56bb754..8b75133 100644 --- a/third_party/libuweave/src/crypto_hmac.c +++ b/third_party/libuweave/src/crypto_hmac.c
@@ -24,7 +24,7 @@ } HMAC_CTX* context = (HMAC_CTX*)state_buffer; HMAC_CTX_init(context); - return HMAC_Init(context, key, key_len, EVP_sha256()) ? 0 : sizeof(HMAC_CTX); + return HMAC_Init(context, key, key_len, EVP_sha256()); } bool uw_crypto_hmac_update_(uint8_t* state_buffer,
diff --git a/third_party/libuweave/src/macaroon.c b/third_party/libuweave/src/macaroon.c index d7e6491..70afda1 100644 --- a/third_party/libuweave/src/macaroon.c +++ b/third_party/libuweave/src/macaroon.c
@@ -7,6 +7,8 @@ #include <string.h> #include "src/crypto_utils.h" +#include "src/macaroon_caveat.h" +#include "src/macaroon_encoding.h" static bool create_mac_tag_(const uint8_t* key, size_t key_len, const UwMacaroonCaveat* caveats, size_t num_caveats, @@ -124,3 +126,94 @@ return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN, additional_caveat, 1, new_macaroon->mac_tag); } + +// Encode a Macaroon to a byte string +bool uw_macaroon_dump_(const UwMacaroon* macaroon, + uint8_t* out, + size_t out_len, + size_t* resulting_str_len) { + if (macaroon == NULL || out == NULL || out_len == 0 || + resulting_str_len == NULL) { + return false; + } + + size_t offset = 0, item_len; + + if (!uw_macaroon_encoding_encode_byte_str_( + macaroon->mac_tag, UW_MACAROON_MAC_LEN, out, out_len, &item_len)) { + return false; + } + offset += item_len; + + if (!uw_macaroon_encoding_encode_array_len_( + (uint32_t)(macaroon->num_caveats), out + offset, out_len - offset, &item_len)) { + return false; + } + offset += item_len; + + for (size_t i = 0; i < macaroon->num_caveats; i++) { + if (!uw_macaroon_encoding_encode_byte_str_( + macaroon->caveats[i].bytes, macaroon->caveats[i].num_bytes, + out + offset, out_len - offset, &item_len)) { + return false; + } + offset += item_len; + } + + *resulting_str_len = offset; + return true; +} + +// Decode a byte string to a Macaroon +bool uw_macaroon_load_(const uint8_t* in, + size_t in_len, + uint8_t* caveats_buffer, + size_t caveats_buffer_size, + UwMacaroon* macaroon) { + if (in == NULL || in_len == 0 || caveats_buffer == NULL || + caveats_buffer_size == 0 || macaroon == NULL) { + return false; + } + + const uint8_t* tag; + size_t tag_len; + if (!uw_macaroon_encoding_decode_byte_str_(in, in_len, &tag, &tag_len) || + tag_len != UW_MACAROON_MAC_LEN) { + return false; + } + memcpy(macaroon->mac_tag, tag, UW_MACAROON_MAC_LEN); + + size_t offset = 0, cbor_item_len; + if (!uw_macaroon_encoding_get_item_len_(in, in_len, &cbor_item_len)) { + return false; + } + offset += cbor_item_len; + + uint32_t array_len; + if (!uw_macaroon_encoding_decode_array_len_(in + offset, in_len - offset, + &array_len)) { + return false; + } + macaroon->num_caveats = (size_t)array_len; + if (caveats_buffer_size < array_len * sizeof(UwMacaroonCaveat)) { + return false; + } + + UwMacaroonCaveat* caveats = (UwMacaroonCaveat*)caveats_buffer; + for (size_t i = 0; i < array_len; i++) { + if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset, + &cbor_item_len)) { + return false; + } + offset += cbor_item_len; + + if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset, + &(caveats[i].bytes), + &(caveats[i].num_bytes))) { + return false; + } + } + macaroon->caveats = caveats; + + return true; +}
diff --git a/third_party/libuweave/src/macaroon.h b/third_party/libuweave/src/macaroon.h index 98ada11..61242f7 100644 --- a/third_party/libuweave/src/macaroon.h +++ b/third_party/libuweave/src/macaroon.h
@@ -44,4 +44,21 @@ const UwMacaroonCaveat* additional_caveat, uint8_t* buffer, size_t buffer_size); +// Encode a Macaroon to a byte string +bool uw_macaroon_dump_(const UwMacaroon* macaroon, + uint8_t* out, + size_t out_len, + size_t* resulting_str_len); + +// Decode a byte string to a Macaroon (the caveats_buffer here is used only for +// the caveat pointer list *caveats in the UwMacaroon *macaroon). One note is +// that the function doesn't copy string values to new buffers, so the caller +// may maintain the input string around to make caveats with string values to +// be usuable. +bool uw_macaroon_load_(const uint8_t* in, + size_t in_len, + uint8_t* caveats_buffer, + size_t caveats_buffer_size, + UwMacaroon* macaroon); + #endif // LIBUWEAVE_SRC_MACAROON_H_
diff --git a/third_party/libuweave/src/macaroon_caveat.c b/third_party/libuweave/src/macaroon_caveat.c index a04c30d..594f9de 100644 --- a/third_party/libuweave/src/macaroon_caveat.c +++ b/third_party/libuweave/src/macaroon_caveat.c
@@ -12,7 +12,7 @@ // TODO(bozhu): Find a better way to pre-allocate memory for HMACc computations? // Are C99 variable-length arrays allowed on embedded devices? -#define HMAC_STATE_BUFFER_SIZE 300 +#define HMAC_STATE_BUFFER_SIZE 1024 static bool create_caveat_(UwMacaroonCaveatType type, const void* value, size_t value_len, uint8_t* buffer,
diff --git a/third_party/libuweave/src/macaroon_caveat.h b/third_party/libuweave/src/macaroon_caveat.h index 5f2c384..2e01742 100644 --- a/third_party/libuweave/src/macaroon_caveat.h +++ b/third_party/libuweave/src/macaroon_caveat.h
@@ -21,9 +21,16 @@ kUwMacaroonCaveatTypeIssued = 3, kUwMacaroonCaveatTypeTTL = 4, kUwMacaroonCaveatTypeExpiration = 5, - kUwMacaroonCaveatTypeSessionIdentifier = 16 + kUwMacaroonCaveatTypeSessionIdentifier = 16, } UwMacaroonCaveatType; +typedef enum { + kUwMacaroonCaveatScopeTypeOwner = 2, + kUwMacaroonCaveatScopeTypeManager = 8, + kUwMacaroonCaveatScopeTypeUser = 14, + kUwMacaroonCaveatScopeTypeViewer = 20, +} UwMacaroonCaveatScopeType; + bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type, uint8_t* buffer, size_t buffer_size,
diff --git a/third_party/libuweave/src/macaroon_encoding.c b/third_party/libuweave/src/macaroon_encoding.c index 214314d..3fb5323 100644 --- a/third_party/libuweave/src/macaroon_encoding.c +++ b/third_party/libuweave/src/macaroon_encoding.c
@@ -15,9 +15,10 @@ // #define FLAG_8BYTE_UINT 27 // Do not support 8-byte typedef enum { - kCborMajorTypeUint = 0, // type 0 - kCborMajorTypeByteStr = 2 << 5, // type 2 - kCborMajorTypeTextStr = 3 << 5, // type 3 + kCborMajorTypeUint = 0, // type 0 -- unsigned integers + kCborMajorTypeByteStr = 2 << 5, // type 2 -- byte strings + kCborMajorTypeTextStr = 3 << 5, // type 3 -- text strings + kCborMajorTypeArray = 4 << 5, // type 4 -- arrays } CborMajorType; // -- Prototypes begin -- @@ -49,7 +50,7 @@ CborMajorType type = get_type_(cbor); if (type != kCborMajorTypeUint && type != kCborMajorTypeByteStr && - type != kCborMajorTypeTextStr) { + type != kCborMajorTypeTextStr && type != kCborMajorTypeArray) { // Other types are not supported return false; } @@ -61,6 +62,8 @@ *first_item_len = uint_min_len_(unsigned_int) + 1; + // For arrays, it returns only the length of the array length portion, not the + // length of the whole array if (type == kCborMajorTypeByteStr || type == kCborMajorTypeTextStr) { *first_item_len += (size_t)unsigned_int; } @@ -84,6 +87,18 @@ resulting_cbor_len); } +bool uw_macaroon_encoding_encode_array_len_(const uint32_t array_len, + uint8_t* buffer, size_t buffer_size, + size_t* resulting_cbor_len) { + if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) { + return false; + } + + set_type_(kCborMajorTypeArray, buffer); + return blindly_encode_uint_(array_len, buffer, buffer_size, + resulting_cbor_len); +} + bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len, uint8_t* buffer, size_t buffer_size, size_t* resulting_cbor_len) { @@ -110,27 +125,30 @@ bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len, uint32_t* unsigned_int) { - if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL) { - return false; - } - - CborMajorType type = get_type_(cbor); - if (type != kCborMajorTypeUint) { + if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL || + get_type_(cbor) != kCborMajorTypeUint) { return false; } return blindly_decode_uint_(cbor, cbor_len, unsigned_int); } -bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len, - const uint8_t** out_str, - size_t* out_str_len) { - if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL) { +bool uw_macaroon_encoding_decode_array_len_(const uint8_t* cbor, + size_t cbor_len, + uint32_t* array_len) { + if (cbor == NULL || cbor_len == 0 || array_len == NULL || + get_type_(cbor) != kCborMajorTypeArray) { return false; } - CborMajorType type = get_type_(cbor); - if (type != kCborMajorTypeByteStr) { + return blindly_decode_uint_(cbor, cbor_len, array_len); +} + +bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len, + const uint8_t** out_str, + size_t* out_str_len) { + if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL || + get_type_(cbor) != kCborMajorTypeByteStr) { return false; } @@ -140,12 +158,8 @@ bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len, const uint8_t** out_str, size_t* out_str_len) { - if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL) { - return false; - } - - CborMajorType type = get_type_(cbor); - if (type != kCborMajorTypeTextStr) { + if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL || + get_type_(cbor) != kCborMajorTypeTextStr) { return false; }
diff --git a/third_party/libuweave/src/macaroon_encoding.h b/third_party/libuweave/src/macaroon_encoding.h index 2c11fd1..edddfc1 100644 --- a/third_party/libuweave/src/macaroon_encoding.h +++ b/third_party/libuweave/src/macaroon_encoding.h
@@ -10,7 +10,7 @@ * cryptographic use, such as signatures. We only need to support a very small * subset of the CBOR standard, since only these are used in our cryptographic * designs. The supported data types are: unsigned integers (maximum 32 bits), - * byte strings, and text strings. + * byte strings, text strings, and arrays. */ #include <stdbool.h> @@ -25,6 +25,9 @@ bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int, uint8_t* buffer, size_t buffer_size, size_t* resulting_cbor_len); +bool uw_macaroon_encoding_encode_array_len_(const uint32_t array_len, + uint8_t* buffer, size_t buffer_size, + size_t* resulting_cbor_len); bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len, uint8_t* buffer, size_t buffer_size, size_t* resulting_cbor_len); @@ -34,6 +37,8 @@ bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len, uint32_t* unsigned_int); +bool uw_macaroon_encoding_decode_array_len_(const uint8_t* cbor, + size_t cbor_len, uint32_t* array_len); bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len, const uint8_t** str, size_t* str_len);