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;