| // 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_black_list_manager_impl.h" |
| |
| #include <base/json/json_reader.h> |
| #include <base/json/json_writer.h> |
| #include <base/values.h> |
| |
| #include "src/commands/schema_constants.h" |
| #include "src/data_encoding.h" |
| |
| namespace weave { |
| |
| namespace { |
| const char kConfigFileName[] = "black_list"; |
| |
| const char kUser[] = "user"; |
| const char kApp[] = "app"; |
| const char kExpiration[] = "expiration"; |
| } |
| |
| AccessBlackListManagerImpl::AccessBlackListManagerImpl( |
| provider::ConfigStore* store, |
| size_t capacity, |
| base::Clock* clock) |
| : capacity_{capacity}, clock_{clock}, store_{store} { |
| Load(); |
| } |
| |
| void AccessBlackListManagerImpl::Load() { |
| if (!store_) |
| return; |
| if (auto list = base::ListValue::From( |
| base::JSONReader::Read(store_->LoadSettings(kConfigFileName)))) { |
| for (const auto& e : *list) { |
| const base::DictionaryValue* entry{nullptr}; |
| std::string user; |
| std::string app; |
| decltype(entries_)::key_type key; |
| int expiration; |
| if (e->GetAsDictionary(&entry) && entry->GetString(kUser, &user) && |
| Base64Decode(user, &key.first) && entry->GetString(kApp, &app) && |
| Base64Decode(app, &key.second) && |
| entry->GetInteger(kExpiration, &expiration)) { |
| base::Time expiration_time = base::Time::FromTimeT(expiration); |
| if (expiration_time > clock_->Now()) |
| entries_[key] = expiration_time; |
| } |
| } |
| if (entries_.size() < list->GetSize()) { |
| // Save some storage space by saving without expired entries. |
| Save({}); |
| } |
| } |
| } |
| |
| void AccessBlackListManagerImpl::Save(const DoneCallback& callback) { |
| if (!store_) { |
| if (!callback.is_null()) |
| callback.Run(nullptr); |
| return; |
| } |
| |
| base::ListValue list; |
| for (const auto& e : entries_) { |
| scoped_ptr<base::DictionaryValue> entry{new base::DictionaryValue}; |
| entry->SetString(kUser, Base64Encode(e.first.first)); |
| entry->SetString(kApp, Base64Encode(e.first.second)); |
| entry->SetInteger(kExpiration, e.second.ToTimeT()); |
| list.Append(std::move(entry)); |
| } |
| |
| std::string json; |
| base::JSONWriter::Write(list, &json); |
| store_->SaveSettings(kConfigFileName, json, callback); |
| } |
| |
| void AccessBlackListManagerImpl::RemoveExpired() { |
| for (auto i = begin(entries_); i != end(entries_);) { |
| if (i->second <= clock_->Now()) |
| i = entries_.erase(i); |
| else |
| ++i; |
| } |
| } |
| |
| void AccessBlackListManagerImpl::AddEntryAddedCallback( |
| const base::Closure& callback) { |
| on_entry_added_callbacks_.push_back(callback); |
| } |
| |
| void AccessBlackListManagerImpl::Block(const std::vector<uint8_t>& user_id, |
| const std::vector<uint8_t>& app_id, |
| const base::Time& expiration, |
| const DoneCallback& callback) { |
| // Iterating is OK as Save below is more expensive. |
| RemoveExpired(); |
| if (expiration <= clock_->Now()) { |
| if (!callback.is_null()) { |
| ErrorPtr error; |
| Error::AddTo(&error, FROM_HERE, "aleady_expired", |
| "Entry already expired"); |
| callback.Run(std::move(error)); |
| } |
| return; |
| } |
| if (entries_.size() >= capacity_) { |
| if (!callback.is_null()) { |
| ErrorPtr error; |
| Error::AddTo(&error, FROM_HERE, "blacklist_is_full", |
| "Unable to store more entries"); |
| callback.Run(std::move(error)); |
| } |
| return; |
| } |
| |
| auto& value = entries_[std::make_pair(user_id, app_id)]; |
| value = std::max(value, expiration); |
| for (const auto& cb : on_entry_added_callbacks_) |
| cb.Run(); |
| |
| Save(callback); |
| } |
| |
| void AccessBlackListManagerImpl::Unblock(const std::vector<uint8_t>& user_id, |
| const std::vector<uint8_t>& app_id, |
| const DoneCallback& callback) { |
| if (!entries_.erase(std::make_pair(user_id, app_id))) { |
| if (!callback.is_null()) { |
| ErrorPtr error; |
| Error::AddTo(&error, FROM_HERE, "entry_not_found", "Unknown entry"); |
| callback.Run(std::move(error)); |
| } |
| return; |
| } |
| // Iterating is OK as Save below is more expensive. |
| RemoveExpired(); |
| Save(callback); |
| } |
| |
| bool AccessBlackListManagerImpl::IsBlocked( |
| const std::vector<uint8_t>& user_id, |
| const std::vector<uint8_t>& app_id) const { |
| for (const auto& user : {{}, user_id}) { |
| for (const auto& app : {{}, app_id}) { |
| auto both = entries_.find(std::make_pair(user, app)); |
| if (both != end(entries_) && both->second > clock_->Now()) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| std::vector<AccessBlackListManager::Entry> |
| AccessBlackListManagerImpl::GetEntries() const { |
| std::vector<Entry> result; |
| for (const auto& e : entries_) |
| result.push_back({e.first.first, e.first.second, e.second}); |
| return result; |
| } |
| |
| size_t AccessBlackListManagerImpl::GetSize() const { |
| return entries_.size(); |
| } |
| |
| size_t AccessBlackListManagerImpl::GetCapacity() const { |
| return capacity_; |
| } |
| |
| } // namespace weave |