buffet: Added minimal role into command definition Possible values "viewer", "user", "owner", "manager". Default minimal role is set to "user". Role check is not yet implemented. BUG=brillo:808 TEST=`FEATURES=test emerge-gizmo buffet` Change-Id: I74d16c24618b50de1e7a2ffa37d7aef36978a38a Reviewed-on: https://chromium-review.googlesource.com/274117 Reviewed-by: Vitaly Buka <vitalybuka@chromium.org> Commit-Queue: Vitaly Buka <vitalybuka@chromium.org> Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp index 8bcc7d8..b91bdef 100644 --- a/buffet/buffet.gyp +++ b/buffet/buffet.gyp
@@ -35,6 +35,7 @@ 'commands/prop_values.cc', 'commands/schema_constants.cc', 'commands/schema_utils.cc', + 'commands/user_role.cc', 'device_registration_info.cc', 'dbus_bindings/org.chromium.Buffet.Command.xml', 'dbus_bindings/org.chromium.Buffet.Manager.xml',
diff --git a/buffet/commands/command_definition.h b/buffet/commands/command_definition.h index a5e2916..ea8e5e6 100644 --- a/buffet/commands/command_definition.h +++ b/buffet/commands/command_definition.h
@@ -11,6 +11,7 @@ #include <base/macros.h> #include "buffet/commands/object_schema.h" +#include "buffet/commands/user_role.h" namespace buffet { @@ -61,6 +62,10 @@ const Visibility& GetVisibility() const { return visibility_; } // Changes the command visibility. void SetVisibility(const Visibility& visibility); + // Returns the role required to execute command. + UserRole GetMinimalRole() const { return minimal_role_; } + // Changes the role required to execute command. + void SetMinimalRole(UserRole minimal_role) { minimal_role_ = minimal_role; } private: std::string category_; // Cmd category. Could be "powerd" for "base.reboot". @@ -68,6 +73,8 @@ std::unique_ptr<const ObjectSchema> progress_; // Command progress def. std::unique_ptr<const ObjectSchema> results_; // Command results def. Visibility visibility_; // Available to all by default. + // Minimal role required to execute command. + UserRole minimal_role_{UserRole::kUser}; DISALLOW_COPY_AND_ASSIGN(CommandDefinition); };
diff --git a/buffet/commands/command_dictionary.cc b/buffet/commands/command_dictionary.cc index 0fdae65..4f66c7d 100644 --- a/buffet/commands/command_dictionary.cc +++ b/buffet/commands/command_dictionary.cc
@@ -72,6 +72,7 @@ const ObjectSchema* base_results_def = nullptr; // By default make it available to all clients. auto visibility = CommandDefinition::Visibility::GetAll(); + UserRole minimal_role{UserRole::kUser}; if (base_commands) { auto cmd = base_commands->FindCommand(full_command_name); if (cmd) { @@ -79,6 +80,7 @@ base_progress_def = cmd->GetProgress(); base_results_def = cmd->GetResults(); visibility = cmd->GetVisibility(); + minimal_role = cmd->GetMinimalRole(); } // If the base command dictionary was provided but the command was not @@ -123,8 +125,8 @@ return false; std::string value; - using commands::attributes::kCommand_Visibility; - if (command_def_json->GetString(kCommand_Visibility, &value)) { + if (command_def_json->GetString(commands::attributes::kCommand_Visibility, + &value)) { if (!visibility.FromString(value, error)) { chromeos::Error::AddToPrintf( error, FROM_HERE, errors::commands::kDomain, @@ -134,12 +136,24 @@ } } + if (command_def_json->GetString(commands::attributes::kCommand_Role, + &value)) { + if (!FromString(value, &minimal_role, error)) { + chromeos::Error::AddToPrintf( + error, FROM_HERE, errors::commands::kDomain, + errors::commands::kInvalidMinimalRole, + "Error parsing command '%s'", full_command_name.c_str()); + return false; + } + } + std::unique_ptr<CommandDefinition> command_def{ new CommandDefinition{category, std::move(parameters_schema), std::move(progress_schema), std::move(results_schema)}}; command_def->SetVisibility(visibility); + command_def->SetMinimalRole(minimal_role); new_defs.emplace(full_command_name, std::move(command_def)); command_iter.Advance(); @@ -227,6 +241,8 @@ base::DictionaryValue* command_def = new base::DictionaryValue; command_def->Set(commands::attributes::kCommand_Parameters, parameters.release()); + command_def->SetString(commands::attributes::kCommand_Role, + ToString(pair.second->GetMinimalRole())); package->SetWithoutPathExpansion(command_name, command_def); } return dict;
diff --git a/buffet/commands/command_dictionary_unittest.cc b/buffet/commands/command_dictionary_unittest.cc index e31f8cc..aeae236 100644 --- a/buffet/commands/command_dictionary_unittest.cc +++ b/buffet/commands/command_dictionary_unittest.cc
@@ -230,12 +230,14 @@ auto expected = R"({ 'base': { 'reboot': { - 'parameters': {'delay': {'minimum': 10}} + 'parameters': {'delay': {'minimum': 10}}, + 'minimalRole': 'user' } }, 'robot': { '_jump': { - 'parameters': {'_height': 'integer'} + 'parameters': {'_height': 'integer'}, + 'minimalRole': 'user' } } })"; @@ -253,7 +255,8 @@ 'minimum': 10, 'type': 'integer' } - } + }, + 'minimalRole': 'user' } }, 'robot': { @@ -262,7 +265,8 @@ '_height': { 'type': 'integer' } - } + }, + 'minimalRole': 'user' } } })"; @@ -322,14 +326,14 @@ ASSERT_NE(nullptr, json.get()); auto expected = R"({ 'test': { - 'command1': {'parameters': {}}, - 'command2': {'parameters': {}}, - 'command3': {'parameters': {}}, - 'command4': {'parameters': {}}, - 'command5': {'parameters': {}}, - 'command6': {'parameters': {}}, - 'command7': {'parameters': {}}, - 'command8': {'parameters': {}} + 'command1': {'parameters': {}, 'minimalRole': 'user'}, + 'command2': {'parameters': {}, 'minimalRole': 'user'}, + 'command3': {'parameters': {}, 'minimalRole': 'user'}, + 'command4': {'parameters': {}, 'minimalRole': 'user'}, + 'command5': {'parameters': {}, 'minimalRole': 'user'}, + 'command6': {'parameters': {}, 'minimalRole': 'user'}, + 'command7': {'parameters': {}, 'minimalRole': 'user'}, + 'command8': {'parameters': {}, 'minimalRole': 'user'} } })"; EXPECT_JSON_EQ(expected, *json); @@ -340,10 +344,10 @@ ASSERT_NE(nullptr, json.get()); expected = R"({ 'test': { - 'command2': {'parameters': {}}, - 'command4': {'parameters': {}}, - 'command6': {'parameters': {}}, - 'command8': {'parameters': {}} + 'command2': {'parameters': {}, 'minimalRole': 'user'}, + 'command4': {'parameters': {}, 'minimalRole': 'user'}, + 'command6': {'parameters': {}, 'minimalRole': 'user'}, + 'command8': {'parameters': {}, 'minimalRole': 'user'} } })"; EXPECT_JSON_EQ(expected, *json); @@ -354,10 +358,10 @@ ASSERT_NE(nullptr, json.get()); expected = R"({ 'test': { - 'command3': {'parameters': {}}, - 'command4': {'parameters': {}}, - 'command7': {'parameters': {}}, - 'command8': {'parameters': {}} + 'command3': {'parameters': {}, 'minimalRole': 'user'}, + 'command4': {'parameters': {}, 'minimalRole': 'user'}, + 'command7': {'parameters': {}, 'minimalRole': 'user'}, + 'command8': {'parameters': {}, 'minimalRole': 'user'} } })"; EXPECT_JSON_EQ(expected, *json); @@ -369,67 +373,14 @@ ASSERT_NE(nullptr, json.get()); expected = R"({ 'test': { - 'command4': {'parameters': {}}, - 'command8': {'parameters': {}} + 'command4': {'parameters': {}, 'minimalRole': 'user'}, + 'command8': {'parameters': {}, 'minimalRole': 'user'} } })"; EXPECT_JSON_EQ(expected, *json); } -TEST(CommandDictionary, LoadCommandsWithVisibility) { - CommandDictionary dict; - auto json = CreateDictionaryValue(R"({ - 'base': { - 'command1': { - 'parameters': {}, - 'results': {}, - 'visibility':'none' - }, - 'command2': { - 'parameters': {}, - 'results': {}, - 'visibility':'local' - }, - 'command3': { - 'parameters': {}, - 'results': {}, - 'visibility':'cloud' - }, - 'command4': { - 'parameters': {}, - 'results': {}, - 'visibility':'all' - }, - 'command5': { - 'parameters': {}, - 'results': {}, - 'visibility':'cloud,local' - } - } - })"); - EXPECT_TRUE(dict.LoadCommands(*json, "testd", nullptr, nullptr)); - auto cmd = dict.FindCommand("base.command1"); - ASSERT_NE(nullptr, cmd); - EXPECT_EQ("none", cmd->GetVisibility().ToString()); - - cmd = dict.FindCommand("base.command2"); - ASSERT_NE(nullptr, cmd); - EXPECT_EQ("local", cmd->GetVisibility().ToString()); - - cmd = dict.FindCommand("base.command3"); - ASSERT_NE(nullptr, cmd); - EXPECT_EQ("cloud", cmd->GetVisibility().ToString()); - - cmd = dict.FindCommand("base.command4"); - ASSERT_NE(nullptr, cmd); - EXPECT_EQ("all", cmd->GetVisibility().ToString()); - - cmd = dict.FindCommand("base.command5"); - ASSERT_NE(nullptr, cmd); - EXPECT_EQ("all", cmd->GetVisibility().ToString()); -} - -TEST(CommandDictionary, LoadCommandsWithVisibility_Inheritance) { +TEST(CommandDictionary, LoadWithPermissions) { CommandDictionary base_dict; auto json = CreateDictionaryValue(R"({ 'base': { @@ -439,21 +390,25 @@ 'visibility':'none' }, 'command2': { + 'minimalRole': 'viewer', 'parameters': {}, 'results': {}, 'visibility':'local' }, 'command3': { + 'minimalRole': 'user', 'parameters': {}, 'results': {}, 'visibility':'cloud' }, 'command4': { + 'minimalRole': 'manager', 'parameters': {}, 'results': {}, 'visibility':'all' }, 'command5': { + 'minimalRole': 'owner', 'parameters': {}, 'results': {}, 'visibility':'local,cloud' @@ -462,6 +417,31 @@ })"); EXPECT_TRUE(base_dict.LoadCommands(*json, "testd", nullptr, nullptr)); + auto cmd = base_dict.FindCommand("base.command1"); + ASSERT_NE(nullptr, cmd); + EXPECT_EQ("none", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole()); + + cmd = base_dict.FindCommand("base.command2"); + ASSERT_NE(nullptr, cmd); + EXPECT_EQ("local", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kViewer, cmd->GetMinimalRole()); + + cmd = base_dict.FindCommand("base.command3"); + ASSERT_NE(nullptr, cmd); + EXPECT_EQ("cloud", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole()); + + cmd = base_dict.FindCommand("base.command4"); + ASSERT_NE(nullptr, cmd); + EXPECT_EQ("all", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kManager, cmd->GetMinimalRole()); + + cmd = base_dict.FindCommand("base.command5"); + ASSERT_NE(nullptr, cmd); + EXPECT_EQ("all", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kOwner, cmd->GetMinimalRole()); + CommandDictionary dict; json = CreateDictionaryValue(R"({ 'base': { @@ -493,32 +473,38 @@ })"); EXPECT_TRUE(dict.LoadCommands(*json, "testd", &base_dict, nullptr)); - auto cmd = dict.FindCommand("base.command1"); + cmd = dict.FindCommand("base.command1"); ASSERT_NE(nullptr, cmd); EXPECT_EQ("none", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole()); cmd = dict.FindCommand("base.command2"); ASSERT_NE(nullptr, cmd); EXPECT_EQ("local", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kViewer, cmd->GetMinimalRole()); cmd = dict.FindCommand("base.command3"); ASSERT_NE(nullptr, cmd); EXPECT_EQ("cloud", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole()); cmd = dict.FindCommand("base.command4"); ASSERT_NE(nullptr, cmd); EXPECT_EQ("all", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kManager, cmd->GetMinimalRole()); cmd = dict.FindCommand("base.command5"); ASSERT_NE(nullptr, cmd); EXPECT_EQ("all", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kOwner, cmd->GetMinimalRole()); cmd = dict.FindCommand("base._command6"); ASSERT_NE(nullptr, cmd); EXPECT_EQ("all", cmd->GetVisibility().ToString()); + EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole()); } -TEST(CommandDictionary, LoadCommandsWithVisibility_Failures) { +TEST(CommandDictionary, LoadWithPermissions_InvalidVisibility) { CommandDictionary dict; chromeos::ErrorPtr error; @@ -533,10 +519,27 @@ })"); EXPECT_FALSE(dict.LoadCommands(*json, "testd", nullptr, &error)); EXPECT_EQ("invalid_command_visibility", error->GetCode()); - EXPECT_EQ("Error parsing command 'base.jump'", error->GetMessage()); EXPECT_EQ("invalid_parameter_value", error->GetInnerError()->GetCode()); - EXPECT_EQ("Invalid command visibility value 'foo'", - error->GetInnerError()->GetMessage()); + error.reset(); +} + +TEST(CommandDictionary, LoadWithPermissions_InvalidRole) { + CommandDictionary dict; + chromeos::ErrorPtr error; + + auto json = CreateDictionaryValue(R"({ + 'base': { + 'jump': { + 'parameters': {}, + 'results': {}, + 'visibility':'local,cloud', + 'minimalRole':'foo' + } + } + })"); + EXPECT_FALSE(dict.LoadCommands(*json, "testd", nullptr, &error)); + EXPECT_EQ("invalid_minimal_role", error->GetCode()); + EXPECT_EQ("invalid_parameter_value", error->GetInnerError()->GetCode()); error.reset(); }
diff --git a/buffet/commands/enum_to_string.h b/buffet/commands/enum_to_string.h new file mode 100644 index 0000000..031c164 --- /dev/null +++ b/buffet/commands/enum_to_string.h
@@ -0,0 +1,69 @@ +// Copyright 2015 The Chromium OS 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 BUFFET_COMMANDS_ENUM_TO_STRING_H_ +#define BUFFET_COMMANDS_ENUM_TO_STRING_H_ + +#include <string> + +namespace buffet { + +// Helps to map enumeration to stings and back. +// +// Usage example: +// .h file: +// enum class MyEnum { kV1, kV2 }; +// std::string ToString(MyEnum id); +// bool FromString(const std::string& str, MyEnum* id); +// +// .cc file: +// template <> +// const EnumToString<MyEnum>::Map EnumToString<MyEnum>::kMap[] = { +// {MyEnum::kV1, "v1"}, +// {MyEnum::kV2, "v2"}, +// }; +// +// std::string ToString(MyEnum id) { +// return EnumToString<MyEnum>::FindNameById(id); +// } +// +// bool FromString(const std::string& str, MyEnum* id) { +// return EnumToString<MyEnum>::FindIdByName(str, id)); +// } +template <typename T> +class EnumToString final { + public: + static std::string FindNameById(T id) { + for (const Map& m : kMap) { + if (m.id == id) { + CHECK(m.name); + return m.name; + } + } + NOTREACHED(); + return std::string(); + } + + static bool FindIdByName(const std::string& name, T* id) { + for (const Map& m : kMap) { + if (m.name && m.name == name) { + *id = m.id; + return true; + } + } + return false; + } + + private: + struct Map { + const T id; + const char* const name; + }; + + static const Map kMap[]; +}; + +} // namespace buffet + +#endif // BUFFET_COMMANDS_ENUM_TO_STRING_H_
diff --git a/buffet/commands/schema_constants.cc b/buffet/commands/schema_constants.cc index eec6177..7af3a89 100644 --- a/buffet/commands/schema_constants.cc +++ b/buffet/commands/schema_constants.cc
@@ -24,11 +24,13 @@ const char kInvalidCommandName[] = "invalid_command_name"; const char kCommandFailed[] = "command_failed"; const char kInvalidCommandVisibility[] = "invalid_command_visibility"; +const char kInvalidMinimalRole[] = "invalid_minimal_role"; } // namespace commands } // namespace errors namespace commands { namespace attributes { + const char kType[] = "type"; const char kDisplayName[] = "displayName"; const char kDefault[] = "default"; @@ -52,6 +54,13 @@ const char kCommand_Progress[] = "progress"; const char kCommand_Results[] = "results"; const char kCommand_State[] = "state"; + +const char kCommand_Role[] = "minimalRole"; +const char kCommand_Role_Manager[] = "manager"; +const char kCommand_Role_Owner[] = "owner"; +const char kCommand_Role_User[] = "user"; +const char kCommand_Role_Viewer[] = "viewer"; + const char kCommand_ErrorCode[] = "error.code"; const char kCommand_ErrorMessage[] = "error.message"; @@ -60,6 +69,7 @@ const char kCommand_Visibility_Local[] = "local"; const char kCommand_Visibility_Cloud[] = "cloud"; const char kCommand_Visibility_All[] = "all"; + } // namespace attributes } // namespace commands
diff --git a/buffet/commands/schema_constants.h b/buffet/commands/schema_constants.h index 2bc371d..f9690fc 100644 --- a/buffet/commands/schema_constants.h +++ b/buffet/commands/schema_constants.h
@@ -27,6 +27,7 @@ extern const char kInvalidCommandName[]; extern const char kCommandFailed[]; extern const char kInvalidCommandVisibility[]; +extern const char kInvalidMinimalRole[]; } // namespace commands } // namespace errors @@ -56,6 +57,13 @@ extern const char kCommand_Progress[]; extern const char kCommand_Results[]; extern const char kCommand_State[]; + +extern const char kCommand_Role[]; +extern const char kCommand_Role_Manager[]; +extern const char kCommand_Role_Owner[]; +extern const char kCommand_Role_User[]; +extern const char kCommand_Role_Viewer[]; + extern const char kCommand_ErrorCode[]; extern const char kCommand_ErrorMessage[]; @@ -64,6 +72,7 @@ extern const char kCommand_Visibility_Local[]; extern const char kCommand_Visibility_Cloud[]; extern const char kCommand_Visibility_All[]; + } // namespace attributes } // namespace commands
diff --git a/buffet/commands/user_role.cc b/buffet/commands/user_role.cc new file mode 100644 index 0000000..a9237ee --- /dev/null +++ b/buffet/commands/user_role.cc
@@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "buffet/commands/user_role.h" + +#include <chromeos/errors/error.h> + +#include "buffet/commands/enum_to_string.h" +#include "buffet/commands/schema_constants.h" + +namespace buffet { + +template <> +const EnumToString<UserRole>::Map EnumToString<UserRole>::kMap[] = { + {UserRole::kViewer, commands::attributes::kCommand_Role_Viewer}, + {UserRole::kUser, commands::attributes::kCommand_Role_User}, + {UserRole::kOwner, commands::attributes::kCommand_Role_Owner}, + {UserRole::kManager, commands::attributes::kCommand_Role_Manager}, +}; + +std::string ToString(UserRole role) { + return EnumToString<UserRole>::FindNameById(role); +} + +bool FromString(const std::string& str, + UserRole* role, + chromeos::ErrorPtr* error) { + if (EnumToString<UserRole>::FindIdByName(str, role)) + return true; + chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, + errors::commands::kInvalidPropValue, + "Invalid role: '%s'", str.c_str()); + return false; +} + +} // namespace buffet
diff --git a/buffet/commands/user_role.h b/buffet/commands/user_role.h new file mode 100644 index 0000000..711abdf --- /dev/null +++ b/buffet/commands/user_role.h
@@ -0,0 +1,28 @@ +// Copyright 2015 The Chromium OS 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 BUFFET_COMMANDS_USER_ROLE_H_ +#define BUFFET_COMMANDS_USER_ROLE_H_ + +#include <string> +#include <chromeos/errors/error.h> + +namespace buffet { + +enum class UserRole { + kViewer, + kUser, + kManager, + kOwner, +}; + +std::string ToString(UserRole role); + +bool FromString(const std::string& str, + UserRole* role, + chromeos::ErrorPtr* error); + +} // namespace buffet + +#endif // BUFFET_COMMANDS_USER_ROLE_H_
diff --git a/buffet/device_registration_info_unittest.cc b/buffet/device_registration_info_unittest.cc index c3088c4..c791355 100644 --- a/buffet/device_registration_info_unittest.cc +++ b/buffet/device_registration_info_unittest.cc
@@ -373,7 +373,8 @@ 'minimum': 10, 'type': 'integer' } - } + }, + 'minimalRole': 'user' } }, 'robot': { @@ -382,7 +383,8 @@ '_height': { 'type': 'integer' } - } + }, + 'minimalRole': 'user' } } })"; @@ -406,10 +408,12 @@ 'base': { 'reboot': { 'parameters': {'delay': 'integer'}, + 'minimalRole': 'user', 'results': {} }, 'shutdown': { 'parameters': {}, + 'minimalRole': 'user', 'results': {} } } @@ -419,12 +423,14 @@ 'base': { 'reboot': { 'parameters': {'delay': {'minimum': 10}}, + 'minimalRole': 'user', 'results': {} } }, 'robot': { '_jump': { 'parameters': {'_height': 'integer'}, + 'minimalRole': 'user', 'results': {} } } @@ -481,7 +487,8 @@ 'robot': { '_jump': { 'parameters': {'_height': 'integer'}, - 'results': {'status': 'string'} + 'results': {'status': 'string'}, + 'minimalRole': 'user' } } })"); @@ -492,7 +499,8 @@ auto commands_json = unittests::CreateValue(R"([{ 'name':'robot._jump', 'id':'1234', - 'parameters': {'_height': 100} + 'parameters': {'_height': 100}, + 'minimalRole': 'user' }])"); ASSERT_NE(nullptr, commands_json.get()); const base::ListValue* command_list = nullptr;