| // 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 |