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_black_list_manager_impl.h"
6 
7 #include <base/json/json_reader.h>
8 #include <base/json/json_writer.h>
9 #include <base/values.h>
10 
11 #include "src/commands/schema_constants.h"
12 #include "src/data_encoding.h"
13 
14 namespace weave {
15 
16 namespace {
17 const char kConfigFileName[] = "black_list";
18 
19 const char kUser[] = "user";
20 const char kApp[] = "app";
21 const char kExpiration[] = "expiration";
22 }
23 
AccessBlackListManagerImpl(provider::ConfigStore * store,size_t capacity,base::Clock * clock)24 AccessBlackListManagerImpl::AccessBlackListManagerImpl(
25     provider::ConfigStore* store,
26     size_t capacity,
27     base::Clock* clock)
28     : capacity_{capacity}, clock_{clock}, store_{store} {
29   Load();
30 }
31 
Load()32 void AccessBlackListManagerImpl::Load() {
33   if (!store_)
34     return;
35   if (auto list = base::ListValue::From(
36           base::JSONReader::Read(store_->LoadSettings(kConfigFileName)))) {
37     for (const auto& e : *list) {
38       const base::DictionaryValue* entry{nullptr};
39       std::string user;
40       std::string app;
41       decltype(entries_)::key_type key;
42       int expiration;
43       if (e->GetAsDictionary(&entry) && entry->GetString(kUser, &user) &&
44           Base64Decode(user, &key.first) && entry->GetString(kApp, &app) &&
45           Base64Decode(app, &key.second) &&
46           entry->GetInteger(kExpiration, &expiration)) {
47         base::Time expiration_time = base::Time::FromTimeT(expiration);
48         if (expiration_time > clock_->Now())
49           entries_[key] = expiration_time;
50       }
51     }
52     if (entries_.size() < list->GetSize()) {
53       // Save some storage space by saving without expired entries.
54       Save({});
55     }
56   }
57 }
58 
Save(const DoneCallback & callback)59 void AccessBlackListManagerImpl::Save(const DoneCallback& callback) {
60   if (!store_) {
61     if (!callback.is_null())
62       callback.Run(nullptr);
63     return;
64   }
65 
66   base::ListValue list;
67   for (const auto& e : entries_) {
68     scoped_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
69     entry->SetString(kUser, Base64Encode(e.first.first));
70     entry->SetString(kApp, Base64Encode(e.first.second));
71     entry->SetInteger(kExpiration, e.second.ToTimeT());
72     list.Append(std::move(entry));
73   }
74 
75   std::string json;
76   base::JSONWriter::Write(list, &json);
77   store_->SaveSettings(kConfigFileName, json, callback);
78 }
79 
RemoveExpired()80 void AccessBlackListManagerImpl::RemoveExpired() {
81   for (auto i = begin(entries_); i != end(entries_);) {
82     if (i->second <= clock_->Now())
83       i = entries_.erase(i);
84     else
85       ++i;
86   }
87 }
88 
Block(const std::vector<uint8_t> & user_id,const std::vector<uint8_t> & app_id,const base::Time & expiration,const DoneCallback & callback)89 void AccessBlackListManagerImpl::Block(const std::vector<uint8_t>& user_id,
90                                        const std::vector<uint8_t>& app_id,
91                                        const base::Time& expiration,
92                                        const DoneCallback& callback) {
93   // Iterating is OK as Save below is more expensive.
94   RemoveExpired();
95   if (expiration <= clock_->Now()) {
96     if (!callback.is_null()) {
97       ErrorPtr error;
98       Error::AddTo(&error, FROM_HERE, "aleady_expired",
99                    "Entry already expired");
100       callback.Run(std::move(error));
101     }
102     return;
103   }
104   if (entries_.size() >= capacity_) {
105     if (!callback.is_null()) {
106       ErrorPtr error;
107       Error::AddTo(&error, FROM_HERE, "blacklist_is_full",
108                    "Unable to store more entries");
109       callback.Run(std::move(error));
110     }
111     return;
112   }
113   auto& value = entries_[std::make_pair(user_id, app_id)];
114   value = std::max(value, expiration);
115   Save(callback);
116 }
117 
Unblock(const std::vector<uint8_t> & user_id,const std::vector<uint8_t> & app_id,const DoneCallback & callback)118 void AccessBlackListManagerImpl::Unblock(const std::vector<uint8_t>& user_id,
119                                          const std::vector<uint8_t>& app_id,
120                                          const DoneCallback& callback) {
121   if (!entries_.erase(std::make_pair(user_id, app_id))) {
122     if (!callback.is_null()) {
123       ErrorPtr error;
124       Error::AddTo(&error, FROM_HERE, "entry_not_found", "Unknown entry");
125       callback.Run(std::move(error));
126     }
127     return;
128   }
129   // Iterating is OK as Save below is more expensive.
130   RemoveExpired();
131   Save(callback);
132 }
133 
IsBlocked(const std::vector<uint8_t> & user_id,const std::vector<uint8_t> & app_id) const134 bool AccessBlackListManagerImpl::IsBlocked(
135     const std::vector<uint8_t>& user_id,
136     const std::vector<uint8_t>& app_id) const {
137   for (const auto& user : {{}, user_id}) {
138     for (const auto& app : {{}, app_id}) {
139       auto both = entries_.find(std::make_pair(user, app));
140       if (both != end(entries_) && both->second > clock_->Now())
141         return true;
142     }
143   }
144   return false;
145 }
146 
147 std::vector<AccessBlackListManager::Entry>
GetEntries() const148 AccessBlackListManagerImpl::GetEntries() const {
149   std::vector<Entry> result;
150   for (const auto& e : entries_)
151     result.push_back({e.first.first, e.first.second, e.second});
152   return result;
153 }
154 
GetSize() const155 size_t AccessBlackListManagerImpl::GetSize() const {
156   return entries_.size();
157 }
158 
GetCapacity() const159 size_t AccessBlackListManagerImpl::GetCapacity() const {
160   return capacity_;
161 }
162 
163 }  // namespace weave
164