blob: 035e1adefe0cddb188c4be717b92bf212dfdf969 [file] [log] [blame]
Vitaly Buka5a7c4f52016-01-21 15:09:34 -08001// 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
Vitaly Bukaebde3c12016-02-23 18:50:42 -08005#include "src/access_revocation_manager_impl.h"
Vitaly Buka5a7c4f52016-01-21 15:09:34 -08006
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"
Vitaly Buka51dcfad2016-02-23 17:52:46 -080013#include "src/utils.h"
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080014
15namespace weave {
16
17namespace {
18const char kConfigFileName[] = "black_list";
19
20const char kUser[] = "user";
21const char kApp[] = "app";
22const char kExpiration[] = "expiration";
Vitaly Buka51dcfad2016-02-23 17:52:46 -080023const char kRevocation[] = "revocation";
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080024}
25
Vitaly Bukaebde3c12016-02-23 18:50:42 -080026AccessRevocationManagerImpl::AccessRevocationManagerImpl(
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080027 provider::ConfigStore* store,
28 size_t capacity,
29 base::Clock* clock)
30 : capacity_{capacity}, clock_{clock}, store_{store} {
31 Load();
32}
33
Vitaly Bukaebde3c12016-02-23 18:50:42 -080034void AccessRevocationManagerImpl::Load() {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080035 if (!store_)
36 return;
37 if (auto list = base::ListValue::From(
38 base::JSONReader::Read(store_->LoadSettings(kConfigFileName)))) {
Vitaly Buka51dcfad2016-02-23 17:52:46 -080039 for (const auto& value : *list) {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080040 const base::DictionaryValue* entry{nullptr};
41 std::string user;
42 std::string app;
Vitaly Buka51dcfad2016-02-23 17:52:46 -080043 Entry e;
44 int revocation = 0;
45 int expiration = 0;
46 if (value->GetAsDictionary(&entry) && entry->GetString(kUser, &user) &&
47 Base64Decode(user, &e.user_id) && entry->GetString(kApp, &app) &&
48 Base64Decode(app, &e.app_id) &&
49 entry->GetInteger(kRevocation, &revocation) &&
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080050 entry->GetInteger(kExpiration, &expiration)) {
Vitaly Buka51dcfad2016-02-23 17:52:46 -080051 e.revocation = FromJ2000Time(revocation);
52 e.expiration = FromJ2000Time(expiration);
53 if (e.expiration > clock_->Now())
54 entries_.insert(e);
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080055 }
56 }
57 if (entries_.size() < list->GetSize()) {
58 // Save some storage space by saving without expired entries.
59 Save({});
60 }
61 }
62}
63
Vitaly Bukaebde3c12016-02-23 18:50:42 -080064void AccessRevocationManagerImpl::Save(const DoneCallback& callback) {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080065 if (!store_) {
66 if (!callback.is_null())
67 callback.Run(nullptr);
68 return;
69 }
70
71 base::ListValue list;
72 for (const auto& e : entries_) {
73 scoped_ptr<base::DictionaryValue> entry{new base::DictionaryValue};
Vitaly Buka51dcfad2016-02-23 17:52:46 -080074 entry->SetString(kUser, Base64Encode(e.user_id));
75 entry->SetString(kApp, Base64Encode(e.app_id));
76 entry->SetInteger(kRevocation, ToJ2000Time(e.revocation));
77 entry->SetInteger(kExpiration, ToJ2000Time(e.expiration));
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080078 list.Append(std::move(entry));
79 }
80
81 std::string json;
82 base::JSONWriter::Write(list, &json);
83 store_->SaveSettings(kConfigFileName, json, callback);
84}
85
Vitaly Buka00180aa2016-03-08 20:42:30 -080086void AccessRevocationManagerImpl::Shrink() {
87 base::Time oldest[2] = {base::Time::Max(), base::Time::Max()};
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080088 for (auto i = begin(entries_); i != end(entries_);) {
Vitaly Buka51dcfad2016-02-23 17:52:46 -080089 if (i->expiration <= clock_->Now())
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080090 i = entries_.erase(i);
Vitaly Buka00180aa2016-03-08 20:42:30 -080091 else {
92 // Non-strict comparison to ensure counting same timestamps as different.
93 if (i->revocation <= oldest[0]) {
94 oldest[1] = oldest[0];
95 oldest[0] = i->revocation;
96 } else {
97 oldest[1] = std::min(oldest[1], i->revocation);
98 }
Vitaly Buka5a7c4f52016-01-21 15:09:34 -080099 ++i;
Vitaly Buka00180aa2016-03-08 20:42:30 -0800100 }
101 }
102 CHECK_GT(capacity_, 1u);
103 if (entries_.size() >= capacity_) {
104 // List is full so we are going to remove oldest entries from the list.
105 for (auto i = begin(entries_); i != end(entries_);) {
106 if (i->revocation <= oldest[1])
107 i = entries_.erase(i);
108 else {
109 ++i;
110 }
111 }
112 // And replace with a single rule to block everything older.
113 Entry all_blocking_entry;
114 all_blocking_entry.expiration = base::Time::Max();
115 all_blocking_entry.revocation = oldest[1];
116 entries_.insert(all_blocking_entry);
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800117 }
118}
119
Vitaly Bukaebde3c12016-02-23 18:50:42 -0800120void AccessRevocationManagerImpl::AddEntryAddedCallback(
Vitaly Bukad1d52e72016-02-22 16:36:54 -0800121 const base::Closure& callback) {
122 on_entry_added_callbacks_.push_back(callback);
123}
124
Vitaly Bukaebde3c12016-02-23 18:50:42 -0800125void AccessRevocationManagerImpl::Block(const Entry& entry,
126 const DoneCallback& callback) {
Vitaly Buka51dcfad2016-02-23 17:52:46 -0800127 if (entry.expiration <= clock_->Now()) {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800128 if (!callback.is_null()) {
129 ErrorPtr error;
Vitaly Buka1c833772016-01-26 14:05:01 -0800130 Error::AddTo(&error, FROM_HERE, "aleady_expired",
131 "Entry already expired");
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800132 callback.Run(std::move(error));
133 }
134 return;
135 }
Vitaly Buka00180aa2016-03-08 20:42:30 -0800136
137 // Iterating is OK as Save below is more expensive.
138 Shrink();
139 CHECK_LT(entries_.size(), capacity_);
Vitaly Bukad1d52e72016-02-22 16:36:54 -0800140
Vitaly Buka51dcfad2016-02-23 17:52:46 -0800141 auto existing = entries_.find(entry);
142 if (existing != entries_.end()) {
143 Entry new_entry = entry;
144 new_entry.expiration = std::max(entry.expiration, existing->expiration);
145 new_entry.revocation = std::max(entry.revocation, existing->revocation);
146 entries_.erase(existing);
147 entries_.insert(new_entry);
148 } else {
149 entries_.insert(entry);
150 }
151
Vitaly Bukad1d52e72016-02-22 16:36:54 -0800152 for (const auto& cb : on_entry_added_callbacks_)
153 cb.Run();
154
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800155 Save(callback);
156}
157
Vitaly Bukaebde3c12016-02-23 18:50:42 -0800158bool AccessRevocationManagerImpl::IsBlocked(const std::vector<uint8_t>& user_id,
159 const std::vector<uint8_t>& app_id,
160 base::Time timestamp) const {
Vitaly Buka51dcfad2016-02-23 17:52:46 -0800161 Entry entry_to_find;
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800162 for (const auto& user : {{}, user_id}) {
163 for (const auto& app : {{}, app_id}) {
Vitaly Buka51dcfad2016-02-23 17:52:46 -0800164 entry_to_find.user_id = user;
165 entry_to_find.app_id = app;
166 auto match = entries_.find(entry_to_find);
167 if (match != end(entries_) && match->expiration > clock_->Now() &&
168 match->revocation >= timestamp) {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800169 return true;
Vitaly Buka51dcfad2016-02-23 17:52:46 -0800170 }
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800171 }
172 }
173 return false;
174}
175
Vitaly Bukaebde3c12016-02-23 18:50:42 -0800176std::vector<AccessRevocationManager::Entry>
177AccessRevocationManagerImpl::GetEntries() const {
Vitaly Buka51dcfad2016-02-23 17:52:46 -0800178 return {begin(entries_), end(entries_)};
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800179}
180
Vitaly Bukaebde3c12016-02-23 18:50:42 -0800181size_t AccessRevocationManagerImpl::GetSize() const {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800182 return entries_.size();
183}
184
Vitaly Bukaebde3c12016-02-23 18:50:42 -0800185size_t AccessRevocationManagerImpl::GetCapacity() const {
Vitaly Buka5a7c4f52016-01-21 15:09:34 -0800186 return capacity_;
187}
188
189} // namespace weave