1 // Copyright 2016 The Weave 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 "src/access_api_handler.h"
6 
7 #include <base/bind.h>
8 #include <weave/device.h>
9 
10 #include "src/access_black_list_manager.h"
11 #include "src/commands/schema_constants.h"
12 #include "src/data_encoding.h"
13 #include "src/json_error_codes.h"
14 
15 namespace weave {
16 
17 namespace {
18 
19 const char kComponent[] = "accessControl";
20 const char kTrait[] = "_accessControlBlackList";
21 const char kStateSize[] = "_accessControlBlackList.size";
22 const char kStateCapacity[] = "_accessControlBlackList.capacity";
23 const char kUserId[] = "userId";
24 const char kApplicationId[] = "applicationId";
25 const char kExpirationTimeout[] = "expirationTimeoutSec";
26 const char kBlackList[] = "blackList";
27 
GetIds(const base::DictionaryValue & parameters,std::vector<uint8_t> * user_id_decoded,std::vector<uint8_t> * app_id_decoded,ErrorPtr * error)28 bool GetIds(const base::DictionaryValue& parameters,
29             std::vector<uint8_t>* user_id_decoded,
30             std::vector<uint8_t>* app_id_decoded,
31             ErrorPtr* error) {
32   std::string user_id;
33   parameters.GetString(kUserId, &user_id);
34   if (!Base64Decode(user_id, user_id_decoded)) {
35     Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue,
36                        "Invalid user id '%s'", user_id.c_str());
37     return false;
38   }
39 
40   std::string app_id;
41   parameters.GetString(kApplicationId, &app_id);
42   if (!Base64Decode(app_id, app_id_decoded)) {
43     Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue,
44                        "Invalid app id '%s'", user_id.c_str());
45     return false;
46   }
47 
48   return true;
49 }
50 
51 }  // namespace
52 
AccessApiHandler(Device * device,AccessBlackListManager * manager)53 AccessApiHandler::AccessApiHandler(Device* device,
54                                    AccessBlackListManager* manager)
55     : device_{device}, manager_{manager} {
56   device_->AddTraitDefinitionsFromJson(R"({
57     "_accessControlBlackList": {
58       "commands": {
59         "block": {
60           "minimalRole": "owner",
61           "parameters": {
62             "userId": {
63               "type": "string"
64             },
65             "applicationId": {
66               "type": "string"
67             },
68             "expirationTimeoutSec": {
69               "type": "integer"
70             }
71           }
72         },
73         "unblock": {
74           "minimalRole": "owner",
75           "parameters": {
76             "userId": {
77               "type": "string"
78             },
79             "applicationId": {
80               "type": "string"
81             }
82           }
83         },
84         "list": {
85           "minimalRole": "owner",
86           "parameters": {},
87           "results": {
88             "blackList": {
89               "type": "array",
90               "items": {
91                 "type": "object",
92                 "properties": {
93                   "userId": {
94                     "type": "string"
95                   },
96                   "applicationId": {
97                     "type": "string"
98                   }
99                 },
100                 "additionalProperties": false
101               }
102             }
103           }
104         }
105       },
106       "state": {
107         "size": {
108           "type": "integer",
109           "isRequired": true
110         },
111         "capacity": {
112           "type": "integer",
113           "isRequired": true
114         }
115       }
116     }
117   })");
118   CHECK(device_->AddComponent(kComponent, {kTrait}, nullptr));
119   UpdateState();
120 
121   device_->AddCommandHandler(
122       kComponent, "_accessControlBlackList.block",
123       base::Bind(&AccessApiHandler::Block, weak_ptr_factory_.GetWeakPtr()));
124   device_->AddCommandHandler(
125       kComponent, "_accessControlBlackList.unblock",
126       base::Bind(&AccessApiHandler::Unblock, weak_ptr_factory_.GetWeakPtr()));
127   device_->AddCommandHandler(
128       kComponent, "_accessControlBlackList.list",
129       base::Bind(&AccessApiHandler::List, weak_ptr_factory_.GetWeakPtr()));
130 }
131 
Block(const std::weak_ptr<Command> & cmd)132 void AccessApiHandler::Block(const std::weak_ptr<Command>& cmd) {
133   auto command = cmd.lock();
134   if (!command)
135     return;
136 
137   CHECK(command->GetState() == Command::State::kQueued)
138       << EnumToString(command->GetState());
139   command->SetProgress(base::DictionaryValue{}, nullptr);
140 
141   const auto& parameters = command->GetParameters();
142   std::vector<uint8_t> user_id;
143   std::vector<uint8_t> app_id;
144   ErrorPtr error;
145   if (!GetIds(parameters, &user_id, &app_id, &error)) {
146     command->Abort(error.get(), nullptr);
147     return;
148   }
149 
150   int timeout_sec = 0;
151   parameters.GetInteger(kExpirationTimeout, &timeout_sec);
152 
153   base::Time expiration =
154       base::Time::Now() + base::TimeDelta::FromSeconds(timeout_sec);
155 
156   manager_->Block(user_id, app_id, expiration,
157                   base::Bind(&AccessApiHandler::OnCommandDone,
158                              weak_ptr_factory_.GetWeakPtr(), cmd));
159 }
160 
Unblock(const std::weak_ptr<Command> & cmd)161 void AccessApiHandler::Unblock(const std::weak_ptr<Command>& cmd) {
162   auto command = cmd.lock();
163   if (!command)
164     return;
165 
166   CHECK(command->GetState() == Command::State::kQueued)
167       << EnumToString(command->GetState());
168   command->SetProgress(base::DictionaryValue{}, nullptr);
169 
170   const auto& parameters = command->GetParameters();
171   std::vector<uint8_t> user_id;
172   std::vector<uint8_t> app_id;
173   ErrorPtr error;
174   if (!GetIds(parameters, &user_id, &app_id, &error)) {
175     command->Abort(error.get(), nullptr);
176     return;
177   }
178 
179   manager_->Unblock(user_id, app_id,
180                     base::Bind(&AccessApiHandler::OnCommandDone,
181                                weak_ptr_factory_.GetWeakPtr(), cmd));
182 }
183 
List(const std::weak_ptr<Command> & cmd)184 void AccessApiHandler::List(const std::weak_ptr<Command>& cmd) {
185   auto command = cmd.lock();
186   if (!command)
187     return;
188 
189   CHECK(command->GetState() == Command::State::kQueued)
190       << EnumToString(command->GetState());
191   command->SetProgress(base::DictionaryValue{}, nullptr);
192 
193   std::unique_ptr<base::ListValue> entries{new base::ListValue};
194   for (const auto& e : manager_->GetEntries()) {
195     std::unique_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
196     entry->SetString(kUserId, Base64Encode(e.user_id));
197     entry->SetString(kApplicationId, Base64Encode(e.app_id));
198     entries->Append(entry.release());
199   }
200 
201   base::DictionaryValue result;
202   result.Set(kBlackList, entries.release());
203 
204   command->Complete(result, nullptr);
205 }
206 
OnCommandDone(const std::weak_ptr<Command> & cmd,ErrorPtr error)207 void AccessApiHandler::OnCommandDone(const std::weak_ptr<Command>& cmd,
208                                      ErrorPtr error) {
209   auto command = cmd.lock();
210   if (!command)
211     return;
212   UpdateState();
213   if (error) {
214     command->Abort(error.get(), nullptr);
215     return;
216   }
217   command->Complete({}, nullptr);
218 }
219 
UpdateState()220 void AccessApiHandler::UpdateState() {
221   base::DictionaryValue state;
222   state.SetInteger(kStateSize, manager_->GetSize());
223   state.SetInteger(kStateCapacity, manager_->GetCapacity());
224   device_->SetStateProperties(kComponent, state, nullptr);
225 }
226 
227 }  // namespace weave
228