From 53c29b547630280baea3b43f9bf2e640e7ac8dde Mon Sep 17 00:00:00 2001 From: Marc Lepage <67919234+mlepage-google@users.noreply.github.com> Date: Tue, 3 May 2022 19:02:43 -0400 Subject: [PATCH] AclStorage dependency injection (#17966) * AclStorage dependency injection Make it into an inteface and inject it into Server via initParams. Fixes #14443 * Clarify docs for Init function * Rename ExampleAclStorage to DefaultAclStorage --- src/app/server/AclStorage.cpp | 143 --------------------- src/app/server/AclStorage.h | 15 ++- src/app/server/BUILD.gn | 2 + src/app/server/DefaultAclStorage.cpp | 185 +++++++++++++++++++++++++++ src/app/server/DefaultAclStorage.h | 37 ++++++ src/app/server/Server.cpp | 5 +- src/app/server/Server.h | 12 +- 7 files changed, 249 insertions(+), 150 deletions(-) create mode 100644 src/app/server/DefaultAclStorage.cpp create mode 100644 src/app/server/DefaultAclStorage.h diff --git a/src/app/server/AclStorage.cpp b/src/app/server/AclStorage.cpp index 136eeca7cc9f19..9fdb412ed523ec 100644 --- a/src/app/server/AclStorage.cpp +++ b/src/app/server/AclStorage.cpp @@ -23,7 +23,6 @@ using namespace chip; using namespace chip::app; using namespace chip::Access; -using EncodableEntry = AclStorage::EncodableEntry; using Entry = AccessControl::Entry; using EntryListener = AccessControl::EntryListener; using StagingAuthMode = Clusters::AccessControl::AuthMode; @@ -33,40 +32,6 @@ using Target = AccessControl::Entry::Target; namespace { -/* -Size calculation for TLV encoded entry. - -Because EncodeForWrite is used without an accessing fabric, the fabric index is -not encoded. However, let's assume it is. This yields 17 bytes total overhead, -but it's wise to add a few more for safety. - -Each subject may require up to 9 bytes. Each target may require up to 14 bytes -(only one of endpoint or device type will be encoded). - -DATA C T L V NOTES -structure (anonymous) 1 0x15 - field 1 privilege 1 1 1 - field 2 authmode 1 1 1 - field 3 subjects 1 1 - uint64 1 8 per subject - end list 1 0x18 - field 4 targets 1 1 - structure (anonymous) 1 per target - field 0 cluster 1 1 4 - field 1 endpoint 1 1 2 only field 1 or 2 - field 2 devicetype 1 1 4 only field 1 or 2 - end structure 1 - end list 1 0x18 - field 254 fabric index 1 1 1 not written -end structure 1 0x18 -*/ - -// TODO(#14455): get actual values for max subjects/targets -constexpr int kEncodedEntryOverheadBytes = 17 + 8; -constexpr int kEncodedEntrySubjectBytes = 9 * CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY; -constexpr int kEncodedEntryTargetBytes = 14 * CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY; -constexpr int kEncodedEntryTotalBytes = kEncodedEntryOverheadBytes + kEncodedEntrySubjectBytes + kEncodedEntryTargetBytes; - struct StagingSubject { NodeId nodeId; @@ -253,63 +218,6 @@ CHIP_ERROR Convert(const StagingTarget & from, Target & to) return CHIP_NO_ERROR; } -class : public EntryListener -{ -public: - void OnEntryChanged(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index, const Entry * entry, - ChangeType changeType) override - { - CHIP_ERROR err; - - DefaultStorageKeyAllocator key; - - uint8_t buffer[kEncodedEntryTotalBytes] = { 0 }; - - VerifyOrExit(mPersistentStorage != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - - if (changeType == ChangeType::kRemoved) - { - // Shuffle down entries past index, then delete entry at last index. - while (true) - { - uint16_t size = static_cast(sizeof(buffer)); - err = mPersistentStorage->SyncGetKeyValue(key.AccessControlAclEntry(fabric, index + 1), buffer, size); - if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) - { - break; - } - SuccessOrExit(err); - SuccessOrExit(err = mPersistentStorage->SyncSetKeyValue(key.AccessControlAclEntry(fabric, index), buffer, size)); - index++; - } - SuccessOrExit(err = mPersistentStorage->SyncDeleteKeyValue(key.AccessControlAclEntry(fabric, index))); - } - else - { - // Write added/updated entry at index. - VerifyOrExit(entry != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - TLV::TLVWriter writer; - writer.Init(buffer); - EncodableEntry encodableEntry(*entry); - SuccessOrExit(err = encodableEntry.EncodeForWrite(writer, TLV::AnonymousTag())); - SuccessOrExit(err = mPersistentStorage->SyncSetKeyValue(key.AccessControlAclEntry(fabric, index), buffer, - static_cast(writer.GetLengthWritten()))); - } - - return; - - exit: - ChipLogError(DataManagement, "AclStorage: failed %" CHIP_ERROR_FORMAT, err.Format()); - } - - // Must initialize before use. - void Init(PersistentStorageDelegate & persistentStorage) { mPersistentStorage = &persistentStorage; } - -private: - PersistentStorageDelegate * mPersistentStorage = nullptr; - -} sEntryListener; - } // namespace namespace chip { @@ -441,56 +349,5 @@ CHIP_ERROR AclStorage::EncodableEntry::Stage() const return CHIP_NO_ERROR; } -CHIP_ERROR AclStorage::Init(PersistentStorageDelegate & persistentStorage, const FabricTable & fabricTable) -{ - ChipLogProgress(DataManagement, "AclStorage: initializing"); - - CHIP_ERROR err; - - DefaultStorageKeyAllocator key; - - size_t count = 0; - - for (auto & info : fabricTable) - { - auto fabric = info.GetFabricIndex(); - for (size_t index = 0; /**/; ++index) - { - uint8_t buffer[kEncodedEntryTotalBytes] = { 0 }; - uint16_t size = static_cast(sizeof(buffer)); - err = persistentStorage.SyncGetKeyValue(key.AccessControlAclEntry(fabric, index), buffer, size); - if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) - { - break; - } - SuccessOrExit(err); - - TLV::TLVReader reader; - reader.Init(buffer, size); - SuccessOrExit(err = reader.Next()); - - DecodableEntry decodableEntry; - SuccessOrExit(err = decodableEntry.Decode(reader)); - - Entry & entry = decodableEntry.GetEntry(); - SuccessOrExit(err = entry.SetFabricIndex(fabric)); - - SuccessOrExit(err = GetAccessControl().CreateEntry(nullptr, fabric, nullptr, entry)); - count++; - } - } - - ChipLogProgress(DataManagement, "AclStorage: %u entries loaded", (unsigned) count); - - sEntryListener.Init(persistentStorage); - GetAccessControl().AddEntryListener(sEntryListener); - - return CHIP_NO_ERROR; - -exit: - ChipLogError(DataManagement, "AclStorage: failed %" CHIP_ERROR_FORMAT, err.Format()); - return err; -} - } // namespace app } // namespace chip diff --git a/src/app/server/AclStorage.h b/src/app/server/AclStorage.h index f1f0d97c195657..72e9dd4706ac76 100644 --- a/src/app/server/AclStorage.h +++ b/src/app/server/AclStorage.h @@ -138,12 +138,19 @@ class AclStorage mutable StagingTarget mStagingTargets[CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY]; }; + virtual ~AclStorage() = default; + /** - * Initialize must be called. It loads ACL entries for all fabrics from persistent storage, - * then installs a listener for the access control system module to maintain ACL entries in - * persistent storage so they remain in sync with entries in the access control system module. + * Initialize should be called after chip::Access::AccessControl is initialized. + * + * Implementations should take this opportunity to populate AccessControl with ACL entries + * loaded from persistent storage. A half-open range of fabrics [first, last) is provided + * so this can be done on a per-fabric basis. + * + * Implementations should also install an entry change listener on AccessControl to maintain + * ACL entries in persistent storage as they are changed. */ - CHIP_ERROR Init(PersistentStorageDelegate & persistentStorage, const FabricTable & fabricTable); + virtual CHIP_ERROR Init(PersistentStorageDelegate & persistentStorage, ConstFabricIterator first, ConstFabricIterator last) = 0; }; } // namespace app diff --git a/src/app/server/BUILD.gn b/src/app/server/BUILD.gn index d0694b8d52f526..66dc5dc95b8e87 100644 --- a/src/app/server/BUILD.gn +++ b/src/app/server/BUILD.gn @@ -42,6 +42,8 @@ static_library("server") { "CommissioningModeProvider.h", "CommissioningWindowManager.cpp", "CommissioningWindowManager.h", + "DefaultAclStorage.cpp", + "DefaultAclStorage.h", "Dnssd.cpp", "Dnssd.h", "EchoHandler.cpp", diff --git a/src/app/server/DefaultAclStorage.cpp b/src/app/server/DefaultAclStorage.cpp new file mode 100644 index 00000000000000..f80a112ba93ea7 --- /dev/null +++ b/src/app/server/DefaultAclStorage.cpp @@ -0,0 +1,185 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::Access; + +using EncodableEntry = AclStorage::EncodableEntry; +using Entry = AccessControl::Entry; +using EntryListener = AccessControl::EntryListener; +using StagingAuthMode = Clusters::AccessControl::AuthMode; +using StagingPrivilege = Clusters::AccessControl::Privilege; +using StagingTarget = Clusters::AccessControl::Structs::Target::Type; +using Target = AccessControl::Entry::Target; + +namespace { + +/* +Size calculation for TLV encoded entry. + +Because EncodeForWrite is used without an accessing fabric, the fabric index is +not encoded. However, let's assume it is. This yields 17 bytes total overhead, +but it's wise to add a few more for safety. + +Each subject may require up to 9 bytes. Each target may require up to 14 bytes +(only one of endpoint or device type will be encoded). + +DATA C T L V NOTES +structure (anonymous) 1 0x15 + field 1 privilege 1 1 1 + field 2 authmode 1 1 1 + field 3 subjects 1 1 + uint64 1 8 per subject + end list 1 0x18 + field 4 targets 1 1 + structure (anonymous) 1 per target + field 0 cluster 1 1 4 + field 1 endpoint 1 1 2 only field 1 or 2 + field 2 devicetype 1 1 4 only field 1 or 2 + end structure 1 + end list 1 0x18 + field 254 fabric index 1 1 1 not written +end structure 1 0x18 +*/ + +// TODO(#14455): get actual values for max subjects/targets +constexpr int kEncodedEntryOverheadBytes = 17 + 8; +constexpr int kEncodedEntrySubjectBytes = 9 * CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY; +constexpr int kEncodedEntryTargetBytes = 14 * CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY; +constexpr int kEncodedEntryTotalBytes = kEncodedEntryOverheadBytes + kEncodedEntrySubjectBytes + kEncodedEntryTargetBytes; + +class : public EntryListener +{ +public: + void OnEntryChanged(const SubjectDescriptor * subjectDescriptor, FabricIndex fabric, size_t index, const Entry * entry, + ChangeType changeType) override + { + CHIP_ERROR err; + + DefaultStorageKeyAllocator key; + + uint8_t buffer[kEncodedEntryTotalBytes] = { 0 }; + + VerifyOrExit(mPersistentStorage != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + if (changeType == ChangeType::kRemoved) + { + // Shuffle down entries past index, then delete entry at last index. + while (true) + { + uint16_t size = static_cast(sizeof(buffer)); + err = mPersistentStorage->SyncGetKeyValue(key.AccessControlAclEntry(fabric, index + 1), buffer, size); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + break; + } + SuccessOrExit(err); + SuccessOrExit(err = mPersistentStorage->SyncSetKeyValue(key.AccessControlAclEntry(fabric, index), buffer, size)); + index++; + } + SuccessOrExit(err = mPersistentStorage->SyncDeleteKeyValue(key.AccessControlAclEntry(fabric, index))); + } + else + { + // Write added/updated entry at index. + VerifyOrExit(entry != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + TLV::TLVWriter writer; + writer.Init(buffer); + EncodableEntry encodableEntry(*entry); + SuccessOrExit(err = encodableEntry.EncodeForWrite(writer, TLV::AnonymousTag())); + SuccessOrExit(err = mPersistentStorage->SyncSetKeyValue(key.AccessControlAclEntry(fabric, index), buffer, + static_cast(writer.GetLengthWritten()))); + } + + return; + + exit: + ChipLogError(DataManagement, "DefaultAclStorage: failed %" CHIP_ERROR_FORMAT, err.Format()); + } + + // Must initialize before use. + void Init(PersistentStorageDelegate & persistentStorage) { mPersistentStorage = &persistentStorage; } + +private: + PersistentStorageDelegate * mPersistentStorage = nullptr; + +} sEntryListener; + +} // namespace + +namespace chip { +namespace app { + +CHIP_ERROR DefaultAclStorage::Init(PersistentStorageDelegate & persistentStorage, ConstFabricIterator first, + ConstFabricIterator last) +{ + ChipLogProgress(DataManagement, "DefaultAclStorage: initializing"); + + CHIP_ERROR err; + + DefaultStorageKeyAllocator key; + + size_t count = 0; + + for (auto it = first; it != last; ++it) + { + auto fabric = it->GetFabricIndex(); + for (size_t index = 0; /**/; ++index) + { + uint8_t buffer[kEncodedEntryTotalBytes] = { 0 }; + uint16_t size = static_cast(sizeof(buffer)); + err = persistentStorage.SyncGetKeyValue(key.AccessControlAclEntry(fabric, index), buffer, size); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + break; + } + SuccessOrExit(err); + + TLV::TLVReader reader; + reader.Init(buffer, size); + SuccessOrExit(err = reader.Next()); + + DecodableEntry decodableEntry; + SuccessOrExit(err = decodableEntry.Decode(reader)); + + Entry & entry = decodableEntry.GetEntry(); + SuccessOrExit(err = entry.SetFabricIndex(fabric)); + + SuccessOrExit(err = GetAccessControl().CreateEntry(nullptr, fabric, nullptr, entry)); + count++; + } + } + + ChipLogProgress(DataManagement, "DefaultAclStorage: %u entries loaded", (unsigned) count); + + sEntryListener.Init(persistentStorage); + GetAccessControl().AddEntryListener(sEntryListener); + + return CHIP_NO_ERROR; + +exit: + ChipLogError(DataManagement, "DefaultAclStorage: failed %" CHIP_ERROR_FORMAT, err.Format()); + return err; +} + +} // namespace app +} // namespace chip diff --git a/src/app/server/DefaultAclStorage.h b/src/app/server/DefaultAclStorage.h new file mode 100644 index 00000000000000..668da610e4af4c --- /dev/null +++ b/src/app/server/DefaultAclStorage.h @@ -0,0 +1,37 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace chip { +namespace app { + +class DefaultAclStorage : public AclStorage +{ +public: + /** + * Initialize must be called. It loads ACL entries for all fabrics from persistent storage, + * then installs a listener for the access control system module to maintain ACL entries in + * persistent storage so they remain in sync with entries in the access control system module. + */ + CHIP_ERROR Init(PersistentStorageDelegate & persistentStorage, ConstFabricIterator first, ConstFabricIterator last) override; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index ff328cdaf2bdf7..968e3f43ff8467 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -112,6 +112,7 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) VerifyOrExit(initParams.persistentStorageDelegate != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.accessDelegate != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(initParams.aclStorage != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.groupDataProvider != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); // TODO(16969): Remove chip::Platform::MemoryInit() call from Server class, it belongs to outer code @@ -134,7 +135,9 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) SuccessOrExit(err = mAccessControl.Init(initParams.accessDelegate, sDeviceTypeResolver)); Access::SetAccessControl(mAccessControl); - SuccessOrExit(err = mAclStorage.Init(*mDeviceStorage, mFabrics)); + + mAclStorage = initParams.aclStorage; + SuccessOrExit(err = mAclStorage->Init(*mDeviceStorage, mFabrics.begin(), mFabrics.end())); app::DnssdServer::Instance().SetFabricTable(&mFabrics); app::DnssdServer::Instance().SetCommissioningModeProvider(&mCommissioningWindowManager); diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 2e596ea6a56e73..6a2192414dc770 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -96,8 +97,11 @@ struct ServerInitParams // Protection Key (IPK) for CASE. Must be initialized before being provided. Credentials::GroupDataProvider * groupDataProvider = nullptr; // Access control delegate: MUST be injected. Used to look up access control rules. Must be - // initialized before being provided + // initialized before being provided. Access::AccessControl::Delegate * accessDelegate = nullptr; + // ACL storage: MUST be injected. Used to store ACL entries in persistent storage. Must NOT + // be initialized before being provided. + app::AclStorage * aclStorage = nullptr; // Network native params can be injected depending on the // selected Endpoint implementation void * endpointNativeParams = nullptr; @@ -154,6 +158,7 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams #if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION static chip::SimpleSessionResumptionStorage sSessionResumptionStorage; #endif + static chip::app::DefaultAclStorage sAclStorage; // KVS-based persistent storage delegate injection chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = DeviceLayer::PersistedStorage::KeyValueStoreMgr(); @@ -175,6 +180,9 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams // Inject access control delegate this->accessDelegate = Access::Examples::GetAccessControlDelegate(); + // Inject ACL storage. (Don't initialize it.) + this->aclStorage = &sAclStorage; + return CHIP_NO_ERROR; } }; @@ -357,7 +365,7 @@ class Server ServerFabricDelegate mFabricDelegate; Access::AccessControl mAccessControl; - app::AclStorage mAclStorage; + app::AclStorage * mAclStorage; uint16_t mOperationalServicePort; uint16_t mUserDirectedCommissioningPort;