| // Copyright 2016 The Weave Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/access_api_handler.h" | |
| #include <base/bind.h> | |
| #include <weave/device.h> | |
| #include "src/access_black_list_manager.h" | |
| #include "src/commands/schema_constants.h" | |
| #include "src/data_encoding.h" | |
| #include "src/json_error_codes.h" | |
| namespace weave { | |
| namespace { | |
| const char kComponent[] = "accessControl"; | |
| const char kTrait[] = "_accessControlBlackList"; | |
| const char kStateSize[] = "_accessControlBlackList.size"; | |
| const char kStateCapacity[] = "_accessControlBlackList.capacity"; | |
| const char kUserId[] = "userId"; | |
| const char kApplicationId[] = "applicationId"; | |
| const char kExpirationTimeout[] = "expirationTimeoutSec"; | |
| const char kBlackList[] = "blackList"; | |
| bool GetIds(const base::DictionaryValue& parameters, | |
| std::vector<uint8_t>* user_id_decoded, | |
| std::vector<uint8_t>* app_id_decoded, | |
| ErrorPtr* error) { | |
| std::string user_id; | |
| parameters.GetString(kUserId, &user_id); | |
| if (!Base64Decode(user_id, user_id_decoded)) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue, | |
| "Invalid user id '%s'", user_id.c_str()); | |
| return false; | |
| } | |
| std::string app_id; | |
| parameters.GetString(kApplicationId, &app_id); | |
| if (!Base64Decode(app_id, app_id_decoded)) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue, | |
| "Invalid app id '%s'", user_id.c_str()); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| AccessApiHandler::AccessApiHandler(Device* device, | |
| AccessBlackListManager* manager) | |
| : device_{device}, manager_{manager} { | |
| device_->AddTraitDefinitionsFromJson(R"({ | |
| "_accessControlBlackList": { | |
| "commands": { | |
| "block": { | |
| "minimalRole": "owner", | |
| "parameters": { | |
| "userId": { | |
| "type": "string" | |
| }, | |
| "applicationId": { | |
| "type": "string" | |
| }, | |
| "expirationTimeoutSec": { | |
| "type": "integer" | |
| } | |
| } | |
| }, | |
| "unblock": { | |
| "minimalRole": "owner", | |
| "parameters": { | |
| "userId": { | |
| "type": "string" | |
| }, | |
| "applicationId": { | |
| "type": "string" | |
| } | |
| } | |
| }, | |
| "list": { | |
| "minimalRole": "owner", | |
| "parameters": {}, | |
| "results": { | |
| "blackList": { | |
| "type": "array", | |
| "items": { | |
| "type": "object", | |
| "properties": { | |
| "userId": { | |
| "type": "string" | |
| }, | |
| "applicationId": { | |
| "type": "string" | |
| } | |
| }, | |
| "additionalProperties": false | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "state": { | |
| "size": { | |
| "type": "integer", | |
| "isRequired": true | |
| }, | |
| "capacity": { | |
| "type": "integer", | |
| "isRequired": true | |
| } | |
| } | |
| } | |
| })"); | |
| CHECK(device_->AddComponent(kComponent, {kTrait}, nullptr)); | |
| UpdateState(); | |
| device_->AddCommandHandler( | |
| kComponent, "_accessControlBlackList.block", | |
| base::Bind(&AccessApiHandler::Block, weak_ptr_factory_.GetWeakPtr())); | |
| device_->AddCommandHandler( | |
| kComponent, "_accessControlBlackList.unblock", | |
| base::Bind(&AccessApiHandler::Unblock, weak_ptr_factory_.GetWeakPtr())); | |
| device_->AddCommandHandler( | |
| kComponent, "_accessControlBlackList.list", | |
| base::Bind(&AccessApiHandler::List, weak_ptr_factory_.GetWeakPtr())); | |
| } | |
| void AccessApiHandler::Block(const std::weak_ptr<Command>& cmd) { | |
| auto command = cmd.lock(); | |
| if (!command) | |
| return; | |
| CHECK(command->GetState() == Command::State::kQueued) | |
| << EnumToString(command->GetState()); | |
| command->SetProgress(base::DictionaryValue{}, nullptr); | |
| const auto& parameters = command->GetParameters(); | |
| std::vector<uint8_t> user_id; | |
| std::vector<uint8_t> app_id; | |
| ErrorPtr error; | |
| if (!GetIds(parameters, &user_id, &app_id, &error)) { | |
| command->Abort(error.get(), nullptr); | |
| return; | |
| } | |
| int timeout_sec = 0; | |
| parameters.GetInteger(kExpirationTimeout, &timeout_sec); | |
| base::Time expiration = | |
| base::Time::Now() + base::TimeDelta::FromSeconds(timeout_sec); | |
| manager_->Block(user_id, app_id, expiration, | |
| base::Bind(&AccessApiHandler::OnCommandDone, | |
| weak_ptr_factory_.GetWeakPtr(), cmd)); | |
| } | |
| void AccessApiHandler::Unblock(const std::weak_ptr<Command>& cmd) { | |
| auto command = cmd.lock(); | |
| if (!command) | |
| return; | |
| CHECK(command->GetState() == Command::State::kQueued) | |
| << EnumToString(command->GetState()); | |
| command->SetProgress(base::DictionaryValue{}, nullptr); | |
| const auto& parameters = command->GetParameters(); | |
| std::vector<uint8_t> user_id; | |
| std::vector<uint8_t> app_id; | |
| ErrorPtr error; | |
| if (!GetIds(parameters, &user_id, &app_id, &error)) { | |
| command->Abort(error.get(), nullptr); | |
| return; | |
| } | |
| manager_->Unblock(user_id, app_id, | |
| base::Bind(&AccessApiHandler::OnCommandDone, | |
| weak_ptr_factory_.GetWeakPtr(), cmd)); | |
| } | |
| void AccessApiHandler::List(const std::weak_ptr<Command>& cmd) { | |
| auto command = cmd.lock(); | |
| if (!command) | |
| return; | |
| CHECK(command->GetState() == Command::State::kQueued) | |
| << EnumToString(command->GetState()); | |
| command->SetProgress(base::DictionaryValue{}, nullptr); | |
| std::unique_ptr<base::ListValue> entries{new base::ListValue}; | |
| for (const auto& e : manager_->GetEntries()) { | |
| std::unique_ptr<base::DictionaryValue> entry{new base::DictionaryValue}; | |
| entry->SetString(kUserId, Base64Encode(e.user_id)); | |
| entry->SetString(kApplicationId, Base64Encode(e.app_id)); | |
| entries->Append(entry.release()); | |
| } | |
| base::DictionaryValue result; | |
| result.Set(kBlackList, entries.release()); | |
| command->Complete(result, nullptr); | |
| } | |
| void AccessApiHandler::OnCommandDone(const std::weak_ptr<Command>& cmd, | |
| ErrorPtr error) { | |
| auto command = cmd.lock(); | |
| if (!command) | |
| return; | |
| UpdateState(); | |
| if (error) { | |
| command->Abort(error.get(), nullptr); | |
| return; | |
| } | |
| command->Complete({}, nullptr); | |
| } | |
| void AccessApiHandler::UpdateState() { | |
| base::DictionaryValue state; | |
| state.SetInteger(kStateSize, manager_->GetSize()); | |
| state.SetInteger(kStateCapacity, manager_->GetCapacity()); | |
| device_->SetStateProperties(kComponent, state, nullptr); | |
| } | |
| } // namespace weave |