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