|  | // 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_revocation_manager.h" | 
|  | #include "src/commands/schema_constants.h" | 
|  | #include "src/data_encoding.h" | 
|  | #include "src/json_error_codes.h" | 
|  | #include "src/utils.h" | 
|  |  | 
|  | namespace weave { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kComponent[] = "accessControl"; | 
|  | const char kTrait[] = "_accessRevocationList"; | 
|  | const char kStateCapacity[] = "_accessRevocationList.capacity"; | 
|  | const char kUserId[] = "userId"; | 
|  | const char kApplicationId[] = "applicationId"; | 
|  | const char kExpirationTime[] = "expirationTime"; | 
|  | const char kRevocationTimestamp[] = "revocationTimestamp"; | 
|  | const char kRevocationList[] = "revocationList"; | 
|  |  | 
|  | 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, | 
|  | AccessRevocationManager* manager) | 
|  | : device_{device}, manager_{manager} { | 
|  | device_->AddTraitDefinitionsFromJson(R"({ | 
|  | "_accessRevocationList": { | 
|  | "commands": { | 
|  | "add": { | 
|  | "minimalRole": "owner", | 
|  | "parameters": { | 
|  | "userId": { | 
|  | "type": "string" | 
|  | }, | 
|  | "applicationId": { | 
|  | "type": "string" | 
|  | }, | 
|  | "revocationTimestamp": { | 
|  | "type": "integer" | 
|  | }, | 
|  | "expirationTime": { | 
|  | "type": "integer" | 
|  | } | 
|  | } | 
|  | }, | 
|  | "list": { | 
|  | "minimalRole": "owner", | 
|  | "parameters": {}, | 
|  | "results": { | 
|  | "revocationList": { | 
|  | "type": "array", | 
|  | "items": { | 
|  | "type": "object", | 
|  | "properties": { | 
|  | "userId": { | 
|  | "type": "string" | 
|  | }, | 
|  | "applicationId": { | 
|  | "type": "string" | 
|  | }, | 
|  | "revocationTimestamp": { | 
|  | "type": "integer" | 
|  | }, | 
|  | "expirationTime": { | 
|  | "type": "integer" | 
|  | } | 
|  | }, | 
|  | "additionalProperties": false | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | }, | 
|  | "state": { | 
|  | "capacity": { | 
|  | "type": "integer", | 
|  | "isRequired": true | 
|  | } | 
|  | } | 
|  | } | 
|  | })"); | 
|  | CHECK(device_->AddComponent(kComponent, {kTrait}, nullptr)); | 
|  | UpdateState(); | 
|  |  | 
|  | device_->AddCommandHandler( | 
|  | kComponent, "_accessRevocationList.add", | 
|  | base::Bind(&AccessApiHandler::Block, weak_ptr_factory_.GetWeakPtr())); | 
|  | device_->AddCommandHandler( | 
|  | kComponent, "_accessRevocationList.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 expiration_j2k = 0; | 
|  | if (!parameters.GetInteger(kExpirationTime, &expiration_j2k)) { | 
|  | Error::AddToPrintf(&error, FROM_HERE, errors::commands::kInvalidPropValue, | 
|  | "Expiration time is missing"); | 
|  | command->Abort(error.get(), nullptr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int revocation_j2k = 0; | 
|  | if (!parameters.GetInteger(kRevocationTimestamp, &revocation_j2k)) { | 
|  | Error::AddToPrintf(&error, FROM_HERE, errors::commands::kInvalidPropValue, | 
|  | "Revocation timestamp is missing"); | 
|  | command->Abort(error.get(), nullptr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | manager_->Block(AccessRevocationManager::Entry{user_id, app_id, | 
|  | FromJ2000Time(revocation_j2k), | 
|  | FromJ2000Time(expiration_j2k)}, | 
|  | 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(kRevocationList, 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(kStateCapacity, manager_->GetCapacity()); | 
|  | device_->SetStateProperties(kComponent, state, nullptr); | 
|  | } | 
|  |  | 
|  | }  // namespace weave |