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