Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add storage to the example ACL implementation #14253

Merged
merged 10 commits into from
Jan 27, 2022
27 changes: 27 additions & 0 deletions examples/platform/linux/AppMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include <credentials/examples/DefaultDeviceAttestationVerifier.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>

#include <access/examples/ExampleAccessControlDelegate.h>

#include <lib/support/CHIPMem.h>
#include <lib/support/ScopedBuffer.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
Expand Down Expand Up @@ -64,6 +66,27 @@ using namespace chip::DeviceLayer;
using namespace chip::Inet;
using namespace chip::Transport;

class GeneralStorageDelegate : public PersistentStorageDelegate
{
CHIP_ERROR SyncGetKeyValue(const char * key, void * buffer, uint16_t & size) override
{
ChipLogProgress(NotSpecified, "Retrieved value from general storage.");
return PersistedStorage::KeyValueStoreMgr().Get(key, buffer, size);
}

CHIP_ERROR SyncSetKeyValue(const char * key, const void * value, uint16_t size) override
{
ChipLogProgress(NotSpecified, "Stored value in general storage");
return PersistedStorage::KeyValueStoreMgr().Put(key, value, size);
}

CHIP_ERROR SyncDeleteKeyValue(const char * key) override
{
ChipLogProgress(NotSpecified, "Delete value in general storage");
return PersistedStorage::KeyValueStoreMgr().Delete(key);
}
};

#if defined(ENABLE_CHIP_SHELL)
using chip::Shell::Engine;
#endif
Expand All @@ -88,6 +111,8 @@ void EventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg
ChipLogProgress(DeviceLayer, "Receive kCHIPoBLEConnectionEstablished");
}
}

GeneralStorageDelegate gAclStorageDelegate;
} // namespace

#if CHIP_DEVICE_CONFIG_ENABLE_WPA
Expand Down Expand Up @@ -136,6 +161,8 @@ int ChipLinuxAppInit(int argc, char ** argv)

PrintOnboardingCodes(LinuxDeviceOptions::GetInstance().payload);

Access::Examples::SetAccessControlDelegateStorage(&gAclStorageDelegate);

#if defined(PW_RPC_ENABLED)
chip::rpc::Init();
ChipLogProgress(NotSpecified, "PW_RPC initialized.");
Expand Down
206 changes: 203 additions & 3 deletions src/access/examples/ExampleAccessControlDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
#include "ExampleAccessControlDelegate.h"

#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/DefaultStorageKeyAllocator.h>

#include <algorithm>
#include <cstdint>
#include <string>
#include <type_traits>

namespace {
Expand Down Expand Up @@ -125,6 +128,15 @@ class SubjectStorage
return CHIP_ERROR_INVALID_ARGUMENT;
}

public:
CHIP_ERROR Serialize(chip::TLV::TLVWriter & writer) { return writer.Put(chip::TLV::AnonymousTag(), mNode); }

CHIP_ERROR Deserialize(chip::TLV::TLVReader & reader)
{
ReturnErrorOnFailure(reader.Next(chip::TLV::AnonymousTag()));
return reader.Get(mNode);
}

private:
static bool IsValid(NodeId node) { return node != kUndefinedNodeId; }

Expand Down Expand Up @@ -180,6 +192,21 @@ class TargetStorage
return CHIP_ERROR_INVALID_ARGUMENT;
}

public:
CHIP_ERROR Serialize(chip::TLV::TLVWriter & writer)
{
ReturnErrorOnFailure(writer.Put(chip::TLV::AnonymousTag(), mCluster));
return writer.Put(chip::TLV::AnonymousTag(), mDeviceType);
}

CHIP_ERROR Deserialize(chip::TLV::TLVReader & reader)
{
ReturnErrorOnFailure(reader.Next(chip::TLV::AnonymousTag()));
ReturnErrorOnFailure(reader.Get(mCluster));
ReturnErrorOnFailure(reader.Next(chip::TLV::AnonymousTag()));
return reader.Get(mDeviceType);
}

private:
// TODO: eventually this functionality should live where the type itself is defined
static bool IsValidCluster(ClusterId cluster)
Expand Down Expand Up @@ -483,6 +510,94 @@ class EntryStorage
index = found ? toIndex : ArraySize(acl);
}

public:
static constexpr uint8_t kTagInUse = 1;
static constexpr uint8_t kTagFabricIndex = 2;
static constexpr uint8_t kTagAuthMode = 3;
static constexpr uint8_t kTagPrivilege = 4;
static constexpr uint8_t kTagSubjects = 5;
static constexpr uint8_t kTagTargets = 6;
// This value was chosen to be large enough to contain the data, but has not been fine-tuned.
static const size_t kStorageBufferSize = 192;

CHIP_ERROR Serialize(chip::PersistentStorageDelegate * storage, const char * key)
{
uint8_t buffer[kStorageBufferSize] = { 0 };
chip::TLV::TLVWriter writer;
writer.Init(buffer);
chip::TLV::TLVType container;
ReturnErrorOnFailure(writer.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::TLVType::kTLVType_Structure, container));

ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTagInUse), mInUse));
ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTagFabricIndex), mFabricIndex));
ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTagAuthMode), mAuthMode));
ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTagPrivilege), mPrivilege));

chip::TLV::TLVType internalContainer;
ReturnErrorOnFailure(
writer.StartContainer(chip::TLV::ContextTag(kTagSubjects), chip::TLV::TLVType::kTLVType_Array, internalContainer));
for (size_t i = 0; i < kMaxSubjects; ++i)
{
ReturnErrorOnFailure(mSubjects[i].Serialize(writer));
}
ReturnErrorOnFailure(writer.EndContainer(internalContainer));

ReturnErrorOnFailure(
writer.StartContainer(chip::TLV::ContextTag(kTagTargets), chip::TLV::TLVType::kTLVType_Array, internalContainer));
for (size_t i = 0; i < kMaxTargets; ++i)
{
ReturnErrorOnFailure(mTargets[i].Serialize(writer));
}
ReturnErrorOnFailure(writer.EndContainer(internalContainer));

ReturnErrorOnFailure(writer.EndContainer(container));
ReturnErrorOnFailure(writer.Finalize());

return storage->SyncSetKeyValue(key, buffer, static_cast<uint16_t>(writer.GetLengthWritten()));
}

CHIP_ERROR Deserialize(chip::PersistentStorageDelegate * storage, const char * key)
{
uint8_t buffer[kStorageBufferSize] = { 0 };
uint16_t bufferSize = static_cast<uint16_t>(sizeof(buffer));
ReturnErrorOnFailure(storage->SyncGetKeyValue(key, buffer, bufferSize));
chip::TLV::TLVReader reader;
reader.Init(buffer, bufferSize);

ReturnErrorOnFailure(reader.Next(chip::TLV::TLVType::kTLVType_Structure, chip::TLV::AnonymousTag()));

chip::TLV::TLVType container;
ReturnErrorOnFailure(reader.EnterContainer(container));

ReturnErrorOnFailure(reader.Next(chip::TLV::ContextTag(kTagInUse)));
ReturnErrorOnFailure(reader.Get(mInUse));
ReturnErrorOnFailure(reader.Next(chip::TLV::ContextTag(kTagFabricIndex)));
ReturnErrorOnFailure(reader.Get(mFabricIndex));
ReturnErrorOnFailure(reader.Next(chip::TLV::ContextTag(kTagAuthMode)));
ReturnErrorOnFailure(reader.Get(mAuthMode));
ReturnErrorOnFailure(reader.Next(chip::TLV::ContextTag(kTagPrivilege)));
ReturnErrorOnFailure(reader.Get(mPrivilege));

chip::TLV::TLVType innerContainer;
ReturnErrorOnFailure(reader.Next(chip::TLV::TLVType::kTLVType_Array, chip::TLV::ContextTag(kTagSubjects)));
ReturnErrorOnFailure(reader.EnterContainer(innerContainer));
for (size_t i = 0; i < kMaxSubjects; ++i)
{
ReturnErrorOnFailure(mSubjects[i].Deserialize(reader));
}
ReturnErrorOnFailure(reader.ExitContainer(innerContainer));

ReturnErrorOnFailure(reader.Next(chip::TLV::TLVType::kTLVType_Array, chip::TLV::ContextTag(kTagTargets)));
ReturnErrorOnFailure(reader.EnterContainer(innerContainer));
for (size_t i = 0; i < kMaxTargets; ++i)
{
ReturnErrorOnFailure(mTargets[i].Deserialize(reader));
}
ReturnErrorOnFailure(reader.ExitContainer(innerContainer));

return reader.ExitContainer(container);
}

public:
static constexpr size_t kMaxSubjects = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY;
static constexpr size_t kMaxTargets = CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_TARGETS_PER_ENTRY;
Expand Down Expand Up @@ -1020,6 +1135,12 @@ class AccessControlDelegate : public AccessControl::Delegate
EntryStorage::ConvertIndex(*index, *fabricIndex, EntryStorage::ConvertDirection::kAbsoluteToRelative);
}
}

CHIP_ERROR saveError = SaveToFlash();
if (saveError != CHIP_NO_ERROR && saveError != CHIP_ERROR_INCORRECT_STATE)
{
ChipLogDetail(DataManagement, "CreateEntry failed to save to flash");
}
}
return err;
}
Expand All @@ -1044,7 +1165,16 @@ class AccessControlDelegate : public AccessControl::Delegate
{
if (auto * storage = EntryStorage::FindUsedInAcl(index, fabricIndex))
{
return Copy(entry, *storage);
CHIP_ERROR err = Copy(entry, *storage);
if (err == CHIP_NO_ERROR)
{
CHIP_ERROR saveError = SaveToFlash();
if (saveError != CHIP_NO_ERROR && saveError != CHIP_ERROR_INCORRECT_STATE)
{
ChipLogDetail(DataManagement, "UpdateEntry failed to save to flash");
}
}
return err;
}
return CHIP_ERROR_SENTINEL;
}
Expand Down Expand Up @@ -1080,6 +1210,12 @@ class AccessControlDelegate : public AccessControl::Delegate
{
delegate.FixAfterDelete(*storage);
}

CHIP_ERROR saveError = SaveToFlash();
if (saveError != CHIP_NO_ERROR && saveError != CHIP_ERROR_INCORRECT_STATE)
{
ChipLogDetail(DataManagement, "DeleteEntry failed to save to flash");
}
return CHIP_NO_ERROR;
}
return CHIP_ERROR_SENTINEL;
Expand All @@ -1095,10 +1231,67 @@ class AccessControlDelegate : public AccessControl::Delegate
return CHIP_ERROR_BUFFER_TOO_SMALL;
}

public:
void SetStorageDelegate(chip::PersistentStorageDelegate * storageDelegate) { mStorageDelegate = storageDelegate; }

private:
CHIP_ERROR LoadFromFlash() { return CHIP_NO_ERROR; }
chip::PersistentStorageDelegate * mStorageDelegate = nullptr;

// The version of the storage data format. Increment this key when the format of the data model changes.
static const uint32_t kExampleAclStorageVersion = 1;
// This value was chosen to be large enough to contain the data, but has not been fine-tuned.
static const size_t kStorageBufferSize = 32;
static constexpr uint8_t kTagVersion = 1;

CHIP_ERROR LoadFromFlash()
{
VerifyOrReturnError(mStorageDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);

uint8_t buffer[kStorageBufferSize] = { 0 };
uint16_t size = static_cast<uint16_t>(sizeof(buffer));
chip::DefaultStorageKeyAllocator key;
ReturnErrorOnFailure(mStorageDelegate->SyncGetKeyValue(key.AccessControlList(), buffer, size));

CHIP_ERROR SaveToFlash() { return CHIP_NO_ERROR; }
chip::TLV::TLVReader reader;
reader.Init(buffer, size);

ReturnErrorOnFailure(reader.Next(chip::TLV::kTLVType_Structure, chip::TLV::AnonymousTag()));

chip::TLV::TLVType container;
ReturnErrorOnFailure(reader.EnterContainer(container));

ReturnErrorOnFailure(reader.Next(chip::TLV::ContextTag(kTagVersion)));
uint32_t version;
ReturnErrorOnFailure(reader.Get(version));
VerifyOrReturnError(version == kExampleAclStorageVersion, CHIP_ERROR_VERSION_MISMATCH);

for (size_t i = 0; i < EntryStorage::kNumberOfFabrics * EntryStorage::kEntriesPerFabric; ++i)
{
ReturnErrorOnFailure(EntryStorage::acl[i].Deserialize(mStorageDelegate, key.AccessControlEntry(i)));
}
return reader.ExitContainer(container);
}

CHIP_ERROR SaveToFlash()
{
VerifyOrReturnError(mStorageDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);

uint8_t buffer[kStorageBufferSize] = { 0 };
chip::TLV::TLVWriter writer;
writer.Init(buffer);
chip::DefaultStorageKeyAllocator key;
chip::TLV::TLVType container;
ReturnErrorOnFailure(writer.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::TLVType::kTLVType_Structure, container));
ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTagVersion), kExampleAclStorageVersion));
for (size_t i = 0; i < EntryStorage::kNumberOfFabrics * EntryStorage::kEntriesPerFabric; ++i)
{
ReturnErrorOnFailure(EntryStorage::acl[i].Serialize(mStorageDelegate, key.AccessControlEntry(i)));
}
ReturnErrorOnFailure(writer.EndContainer(container));
ReturnErrorOnFailure(writer.Finalize());

return mStorageDelegate->SyncSetKeyValue(key.AccessControlList(), buffer, static_cast<uint16_t>(writer.GetLengthWritten()));
}
};

static_assert(std::is_pod<SubjectStorage>(), "Storage type must be POD");
Expand All @@ -1122,6 +1315,13 @@ AccessControl::Delegate & GetAccessControlDelegate()
return accessControlDelegate;
}

void SetAccessControlDelegateStorage(chip::PersistentStorageDelegate * storageDelegate)
{
ChipLogDetail(DataManagement, "Examples::SetAccessControlDelegateStorage");
AccessControlDelegate & accessControlDelegate = static_cast<AccessControlDelegate &>(GetAccessControlDelegate());
accessControlDelegate.SetStorageDelegate(storageDelegate);
}

} // namespace Examples
} // namespace Access
} // namespace chip
3 changes: 3 additions & 0 deletions src/access/examples/ExampleAccessControlDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
#pragma once

#include "access/AccessControl.h"
#include <lib/core/CHIPPersistentStorageDelegate.h>

namespace chip {
namespace Access {
namespace Examples {

AccessControl::Delegate & GetAccessControlDelegate();

void SetAccessControlDelegateStorage(chip::PersistentStorageDelegate * storageDelegate);

} // namespace Examples
} // namespace Access
} // namespace chip
5 changes: 5 additions & 0 deletions src/lib/support/DefaultStorageKeyAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class DefaultStorageKeyAllocator

const char * FabricTable(chip::FabricIndex fabric) { return Format("f/%x/t", fabric); }

// Access Control List

const char * AccessControlList() { return Format("acl"); }
const char * AccessControlEntry(size_t index) { return Format("acl/%zx", index); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work on some platforms due to missing "%z" specifier. Suggest:

const char * AccessControlEntry(size_t index) { return Format("acl/%x", static_cast(index)); }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created #14547 to fix this.


// Group Data Provider

const char * FabricGroups(chip::FabricIndex fabric) { return Format("f/%x/g", fabric); }
Expand Down