blob: 40ef9c01ec3061c57c2217d289682a2bb59319c6 [file] [log] [blame]
Christopher Wileya4915c42014-03-27 14:45:37 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "buffet/manager.h"
6
Alex Vakulenkob3aac252014-05-07 17:35:24 -07007#include <map>
Alex Vakulenko9ea5a322015-04-17 15:35:34 -07008#include <set>
Alex Vakulenkob3aac252014-05-07 17:35:24 -07009#include <string>
10
Christopher Wileya4915c42014-03-27 14:45:37 -070011#include <base/bind.h>
12#include <base/bind_helpers.h>
Alex Vakulenko665c8852014-09-11 16:57:24 -070013#include <base/json/json_reader.h>
Christopher Wileyb76eb292014-05-05 16:09:16 -070014#include <base/json/json_writer.h>
Christopher Wileycd419662015-02-06 17:51:43 -080015#include <base/time/time.h>
Alex Vakulenkoa8b95bc2014-08-27 11:00:57 -070016#include <chromeos/dbus/async_event_sequencer.h>
17#include <chromeos/dbus/exported_object_manager.h>
18#include <chromeos/errors/error.h>
Garret Kelly9a895382015-05-29 15:56:52 -040019#include <chromeos/http/http_transport.h>
Anton Muhin332df192014-11-22 05:59:14 +040020#include <chromeos/key_value_store.h>
Christopher Wileyb76eb292014-05-05 16:09:16 -070021#include <dbus/bus.h>
Christopher Wiley90016242014-04-01 17:33:29 -070022#include <dbus/object_path.h>
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070023#include <dbus/values_util.h>
Christopher Wileya4915c42014-03-27 14:45:37 -070024
Vitaly Buka72410b22015-05-13 13:48:59 -070025#include "buffet/base_api_handler.h"
Alex Vakulenko665c8852014-09-11 16:57:24 -070026#include "buffet/commands/command_instance.h"
Alex Vakulenkoe03af6d2015-04-20 11:00:54 -070027#include "buffet/commands/schema_constants.h"
Alex Vakulenko57123b22014-10-28 13:50:16 -070028#include "buffet/states/state_change_queue.h"
Alex Vakulenko07216fe2014-09-19 15:31:09 -070029#include "buffet/states/state_manager.h"
Christopher Wileye0fdeee2015-02-07 18:29:32 -080030#include "buffet/storage_impls.h"
Christopher Wileya4915c42014-03-27 14:45:37 -070031
Christopher Wiley2d2d92b2014-07-29 14:07:10 -070032using chromeos::dbus_utils::AsyncEventSequencer;
Christopher Wileyb5dd5ea2014-08-11 10:51:20 -070033using chromeos::dbus_utils::ExportedObjectManager;
Christopher Wileya4915c42014-03-27 14:45:37 -070034
35namespace buffet {
36
Alex Vakulenko57123b22014-10-28 13:50:16 -070037namespace {
38// Max of 100 state update events should be enough in the queue.
39const size_t kMaxStateChangeQueueSize = 100;
Garret Kelly9a895382015-05-29 15:56:52 -040040// The number of seconds each HTTP request will be allowed before timing out.
41const int kRequestTimeoutSeconds = 30;
Alex Vakulenko57123b22014-10-28 13:50:16 -070042} // anonymous namespace
43
Alex Vakulenkof2784de2014-08-15 11:49:35 -070044Manager::Manager(const base::WeakPtr<ExportedObjectManager>& object_manager)
45 : dbus_object_(object_manager.get(),
46 object_manager->GetBus(),
Vitaly Buka7ad8ffb2015-03-20 09:46:57 -070047 org::chromium::Buffet::ManagerAdaptor::GetObjectPath()) {
48}
Christopher Wileya4915c42014-03-27 14:45:37 -070049
Vitaly Buka7ad8ffb2015-03-20 09:46:57 -070050Manager::~Manager() {
51}
Alex Vakulenko57123b22014-10-28 13:50:16 -070052
Vitaly Buka76e70592015-04-16 11:39:02 -070053void Manager::Start(const base::FilePath& config_path,
54 const base::FilePath& state_path,
55 const base::FilePath& test_definitions_path,
56 bool xmpp_enabled,
57 const AsyncEventSequencer::CompletionAction& cb) {
Alex Vakulenko95110752014-09-03 16:27:21 -070058 command_manager_ =
59 std::make_shared<CommandManager>(dbus_object_.GetObjectManager());
Vitaly Buka5e6ff6c2015-05-11 15:41:33 -070060 command_manager_->AddOnCommandDefChanged(base::Bind(
61 &Manager::OnCommandDefsChanged, weak_ptr_factory_.GetWeakPtr()));
Christopher Wileybb5b8482015-02-12 13:42:16 -080062 command_manager_->Startup(base::FilePath{"/etc/buffet"},
63 test_definitions_path);
Vitaly Buka72410b22015-05-13 13:48:59 -070064 state_change_queue_.reset(new StateChangeQueue(kMaxStateChangeQueueSize));
Alex Vakulenko57123b22014-10-28 13:50:16 -070065 state_manager_ = std::make_shared<StateManager>(state_change_queue_.get());
Vitaly Bukadbb8c352015-05-27 09:27:08 -070066 state_manager_->AddOnChangedCallback(
67 base::Bind(&Manager::OnStateChanged, weak_ptr_factory_.GetWeakPtr()));
Alex Vakulenko07216fe2014-09-19 15:31:09 -070068 state_manager_->Startup();
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070069
70 std::unique_ptr<BuffetConfig> config{new BuffetConfig{state_path}};
71 config->AddOnChangedCallback(
72 base::Bind(&Manager::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
Christopher Wiley583d64b2015-03-24 14:30:17 -070073 config->Load(config_path);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070074
Garret Kelly9a895382015-05-29 15:56:52 -040075 auto transport = chromeos::http::Transport::CreateDefault();
76 transport->SetDefaultTimeout(base::TimeDelta::FromSeconds(
77 kRequestTimeoutSeconds));
78
Christopher Wileye0fdeee2015-02-07 18:29:32 -080079 // TODO(avakulenko): Figure out security implications of storing
80 // device info state data unencrypted.
Vitaly Buka72410b22015-05-13 13:48:59 -070081 device_info_.reset(new DeviceRegistrationInfo(
Garret Kelly9a895382015-05-29 15:56:52 -040082 command_manager_, state_manager_, std::move(config), transport,
83 xmpp_enabled));
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070084 device_info_->AddOnRegistrationChangedCallback(base::Bind(
85 &Manager::OnRegistrationChanged, weak_ptr_factory_.GetWeakPtr()));
Vitaly Buka72410b22015-05-13 13:48:59 -070086
Vitaly Buka2f7efdb2015-05-27 16:00:21 -070087 base_api_handler_.reset(new BaseApiHandler{
88 device_info_->AsWeakPtr(), state_manager_, command_manager_});
Vitaly Buka72410b22015-05-13 13:48:59 -070089
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070090 device_info_->Start();
Alex Vakulenko2348e422014-11-21 08:57:57 -080091 dbus_adaptor_.RegisterWithDBusObject(&dbus_object_);
92 dbus_object_.RegisterAsync(cb);
Christopher Wileya4915c42014-03-27 14:45:37 -070093}
94
Alex Vakulenko2348e422014-11-21 08:57:57 -080095void Manager::CheckDeviceRegistered(DBusMethodResponse<std::string> response) {
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070096 LOG(INFO) << "Received call to Manager.CheckDeviceRegistered()";
Alex Vakulenko5c7bf012014-10-30 16:28:38 -070097 chromeos::ErrorPtr error;
Nathan Bullock5e022a32015-04-08 15:13:07 -040098 bool registered = device_info_->HaveRegistrationCredentials(&error);
Alex Vakulenkob3aac252014-05-07 17:35:24 -070099 // If it fails due to any reason other than 'device not registered',
100 // treat it as a real error and report it to the caller.
101 if (!registered &&
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700102 !error->HasError(kErrorDomainGCD, "device_not_registered")) {
103 response->ReplyWithError(error.get());
104 return;
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700105 }
Christopher Wileya4915c42014-03-27 14:45:37 -0700106
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700107 response->Return(registered ? device_info_->GetConfig().device_id()
108 : std::string());
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700109}
110
Alex Vakulenko2348e422014-11-21 08:57:57 -0800111void Manager::GetDeviceInfo(DBusMethodResponse<std::string> response) {
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700112 LOG(INFO) << "Received call to Manager.GetDeviceInfo()";
113
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700114 chromeos::ErrorPtr error;
115 auto device_info = device_info_->GetDeviceInfo(&error);
116 if (!device_info) {
117 response->ReplyWithError(error.get());
118 return;
119 }
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700120
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700121 std::string device_info_str;
Nathan Bullock4b6c0fb2015-04-01 15:32:58 -0400122 base::JSONWriter::WriteWithOptions(device_info.get(),
123 base::JSONWriter::OPTIONS_PRETTY_PRINT, &device_info_str);
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700124 response->Return(device_info_str);
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700125}
126
Alex Vakulenko2348e422014-11-21 08:57:57 -0800127void Manager::RegisterDevice(DBusMethodResponse<std::string> response,
Vitaly Bukacad2e332015-05-14 23:33:32 -0700128 const std::string& ticket_id) {
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400129 LOG(INFO) << "Received call to Manager.RegisterDevice()";
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700130
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700131 chromeos::ErrorPtr error;
Vitaly Bukacad2e332015-05-14 23:33:32 -0700132 std::string device_id = device_info_->RegisterDevice(ticket_id, &error);
David Zeuthen1dbad472015-02-12 15:24:21 -0500133 if (!device_id.empty()) {
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700134 response->Return(device_id);
David Zeuthen1dbad472015-02-12 15:24:21 -0500135 return;
136 }
137 if (!error) {
138 // TODO(zeuthen): This can be changed to CHECK(error) once
139 // RegisterDevice() has been fixed to set |error| when failing.
Vitaly Buka7ad8ffb2015-03-20 09:46:57 -0700140 chromeos::Error::AddTo(&error, FROM_HERE, kErrorDomainGCD, "internal_error",
David Zeuthen1dbad472015-02-12 15:24:21 -0500141 "device_id empty but error not set");
142 }
143 response->ReplyWithError(error.get());
Christopher Wileya4915c42014-03-27 14:45:37 -0700144}
145
Alex Vakulenko2348e422014-11-21 08:57:57 -0800146void Manager::UpdateState(DBusMethodResponse<> response,
147 const chromeos::VariantDictionary& property_set) {
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700148 chromeos::ErrorPtr error;
Vitaly Buka247620b2015-05-26 15:42:20 -0700149 if (!state_manager_->SetProperties(property_set, &error))
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700150 response->ReplyWithError(error.get());
151 else
152 response->Return();
Christopher Wileya4915c42014-03-27 14:45:37 -0700153}
154
Alex Vakulenko61ad4db2015-01-20 10:50:04 -0800155bool Manager::GetState(chromeos::ErrorPtr* error, std::string* state) {
156 auto json = state_manager_->GetStateValuesAsJson(error);
157 if (!json)
158 return false;
Nathan Bullock4b6c0fb2015-04-01 15:32:58 -0400159 base::JSONWriter::WriteWithOptions(
160 json.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, state);
Alex Vakulenko61ad4db2015-01-20 10:50:04 -0800161 return true;
162}
163
Vitaly Buka64fc5fc2015-03-24 12:42:24 -0700164void Manager::AddCommand(DBusMethodResponse<std::string> response,
Vitaly Bukab2098422015-05-31 23:32:46 -0700165 const std::string& json_command,
166 const std::string& in_user_role) {
Anton Muhin5191e812014-10-30 17:49:48 +0400167 static int next_id = 0;
Alex Vakulenko665c8852014-09-11 16:57:24 -0700168 std::string error_message;
169 std::unique_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
170 json_command, base::JSON_PARSE_RFC, nullptr, &error_message));
171 if (!value) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800172 response->ReplyWithError(FROM_HERE, chromeos::errors::json::kDomain,
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700173 chromeos::errors::json::kParseError,
174 error_message);
Alex Vakulenko665c8852014-09-11 16:57:24 -0700175 return;
176 }
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700177 chromeos::ErrorPtr error;
Alex Vakulenko665c8852014-09-11 16:57:24 -0700178 auto command_instance = buffet::CommandInstance::FromJson(
Alex Vakulenkof784e212015-04-20 12:33:52 -0700179 value.get(), commands::attributes::kCommand_Visibility_Local,
Alex Vakulenkod1978d32015-04-29 17:33:26 -0700180 command_manager_->GetCommandDictionary(), nullptr, &error);
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700181 if (!command_instance) {
182 response->ReplyWithError(error.get());
183 return;
184 }
Vitaly Bukab2098422015-05-31 23:32:46 -0700185
186 UserRole role;
187 if (!FromString(in_user_role, &role, &error)) {
188 response->ReplyWithError(error.get());
189 return;
190 }
191
192 UserRole minimal_role =
193 command_instance->GetCommandDefinition()->GetMinimalRole();
194 if (role < minimal_role) {
195 chromeos::Error::AddToPrintf(
196 &error, FROM_HERE, kErrorDomainGCD, "access_denied",
197 "User role '%s' less than minimal: '%s'", ToString(role).c_str(),
198 ToString(minimal_role).c_str());
199 response->ReplyWithError(error.get());
200 return;
201 }
202
Vitaly Buka64fc5fc2015-03-24 12:42:24 -0700203 std::string id = std::to_string(++next_id);
204 command_instance->SetID(id);
Alex Vakulenko5c7bf012014-10-30 16:28:38 -0700205 command_manager_->AddCommand(std::move(command_instance));
Vitaly Buka64fc5fc2015-03-24 12:42:24 -0700206 response->Return(id);
Alex Vakulenko665c8852014-09-11 16:57:24 -0700207}
208
Vitaly Buka3886e8f2015-03-24 11:39:40 -0700209void Manager::GetCommand(DBusMethodResponse<std::string> response,
210 const std::string& id) {
211 const CommandInstance* command = command_manager_->FindCommand(id);
212 if (!command) {
213 response->ReplyWithError(FROM_HERE, kErrorDomainGCD, "unknown_command",
214 "Can't find command with id: " + id);
215 return;
216 }
217 std::string command_str;
Nathan Bullock4b6c0fb2015-04-01 15:32:58 -0400218 base::JSONWriter::WriteWithOptions(command->ToJson().get(),
219 base::JSONWriter::OPTIONS_PRETTY_PRINT, &command_str);
Vitaly Buka3886e8f2015-03-24 11:39:40 -0700220 response->Return(command_str);
221}
222
Alex Vakulenkoe03af6d2015-04-20 11:00:54 -0700223void Manager::SetCommandVisibility(
Alex Vakulenko41f73a92015-04-24 18:09:32 -0700224 std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<>> response,
Alex Vakulenkoe03af6d2015-04-20 11:00:54 -0700225 const std::vector<std::string>& in_names,
226 const std::string& in_visibility) {
227 CommandDefinition::Visibility visibility;
228 chromeos::ErrorPtr error;
229 if (!visibility.FromString(in_visibility, &error)) {
230 response->ReplyWithError(error.get());
231 return;
232 }
233 if (!command_manager_->SetCommandVisibility(in_names, visibility, &error)) {
234 response->ReplyWithError(error.get());
235 return;
236 }
237 response->Return();
238}
239
Alex Vakulenko2348e422014-11-21 08:57:57 -0800240std::string Manager::TestMethod(const std::string& message) {
Alex Vakulenko7a1dc0b2014-08-15 11:45:46 -0700241 LOG(INFO) << "Received call to test method: " << message;
242 return message;
Christopher Wileyb76eb292014-05-05 16:09:16 -0700243}
244
Vitaly Bukafa947062015-04-17 00:41:31 -0700245bool Manager::UpdateDeviceInfo(chromeos::ErrorPtr* error,
246 const std::string& in_name,
247 const std::string& in_description,
248 const std::string& in_location) {
249 return device_info_->UpdateDeviceInfo(in_name, in_description, in_location,
250 error);
Christopher Wileyc900e482015-02-15 15:42:04 -0800251}
252
Vitaly Bukaff81db62015-05-14 21:25:45 -0700253bool Manager::UpdateServiceConfig(chromeos::ErrorPtr* error,
254 const std::string& client_id,
255 const std::string& client_secret,
256 const std::string& api_key,
257 const std::string& oauth_url,
258 const std::string& service_url) {
259 return device_info_->UpdateServiceConfig(client_id, client_secret, api_key,
260 oauth_url, service_url, error);
261}
262
Vitaly Bukaaabadee2015-03-18 23:33:44 -0700263void Manager::OnCommandDefsChanged() {
Alex Vakulenko9ea5a322015-04-17 15:35:34 -0700264 // Limit only to commands that are visible to the local clients.
265 auto commands = command_manager_->GetCommandDictionary().GetCommandsAsJson(
266 [](const buffet::CommandDefinition* def) {
267 return def->GetVisibility().local;
Vitaly Bukadbb8c352015-05-27 09:27:08 -0700268 }, true, nullptr);
Vitaly Bukaaabadee2015-03-18 23:33:44 -0700269 CHECK(commands);
270 std::string json;
Nathan Bullock4b6c0fb2015-04-01 15:32:58 -0400271 base::JSONWriter::WriteWithOptions(commands.get(),
272 base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
Vitaly Bukaaabadee2015-03-18 23:33:44 -0700273 dbus_adaptor_.SetCommandDefs(json);
274}
275
Vitaly Bukadbb8c352015-05-27 09:27:08 -0700276void Manager::OnStateChanged() {
277 auto state = state_manager_->GetStateValuesAsJson(nullptr);
278 CHECK(state);
279 std::string json;
280 base::JSONWriter::WriteWithOptions(
281 state.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
282 dbus_adaptor_.SetState(json);
283}
284
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700285void Manager::OnRegistrationChanged(RegistrationStatus status) {
286 dbus_adaptor_.SetStatus(StatusToString(status));
287}
288
289void Manager::OnConfigChanged(const BuffetConfig& config) {
290 dbus_adaptor_.SetDeviceId(config.device_id());
291 dbus_adaptor_.SetOemName(config.oem_name());
292 dbus_adaptor_.SetModelName(config.model_name());
293 dbus_adaptor_.SetModelId(config.model_id());
294 dbus_adaptor_.SetName(config.name());
295 dbus_adaptor_.SetDescription(config.description());
296 dbus_adaptor_.SetLocation(config.location());
297 dbus_adaptor_.SetAnonymousAccessRole(config.local_anonymous_access_role());
298}
299
Christopher Wileya4915c42014-03-27 14:45:37 -0700300} // namespace buffet