blob: 5b570954e4759aba7063cb901daab4f596e9e297 [file] [log] [blame]
/*
*
* Copyright (c) 2025 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 <app/server/JointFabricDatastore.h>
namespace chip {
namespace app {
#define CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR CHIP_CORE_ERROR(0x87)
void JointFabricDatastore::AddListener(Listener & listener)
{
if (mListeners == nullptr)
{
mListeners = &listener;
listener.mNext = nullptr;
return;
}
for (Listener * l = mListeners; /**/; l = l->mNext)
{
if (l == &listener)
{
return;
}
if (l->mNext == nullptr)
{
l->mNext = &listener;
listener.mNext = nullptr;
return;
}
}
}
void JointFabricDatastore::RemoveListener(Listener & listener)
{
if (mListeners == &listener)
{
mListeners = listener.mNext;
listener.mNext = nullptr;
return;
}
for (Listener * l = mListeners; l != nullptr; l = l->mNext)
{
if (l->mNext == &listener)
{
l->mNext = listener.mNext;
listener.mNext = nullptr;
return;
}
}
}
CHIP_ERROR JointFabricDatastore::AddPendingNode(NodeId nodeId, const CharSpan & friendlyName)
{
VerifyOrReturnError(mNodeInformationEntries.size() < kMaxNodes, CHIP_ERROR_NO_MEMORY);
mNodeInformationEntries.push_back(GenericDatastoreNodeInformationEntry(
nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum::kPending, MakeOptional(friendlyName)));
for (Listener * listener = mListeners; listener != nullptr; listener = listener->mNext)
{
listener->MarkNodeListChanged();
}
return CHIP_NO_ERROR;
}
CHIP_ERROR JointFabricDatastore::UpdateNode(NodeId nodeId, const CharSpan & friendlyName)
{
for (auto & entry : mNodeInformationEntries)
{
if (entry.nodeID == nodeId)
{
entry.Set(MakeOptional(friendlyName));
for (Listener * listener = mListeners; listener != nullptr; listener = listener->mNext)
{
listener->MarkNodeListChanged();
}
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::RemoveNode(NodeId nodeId)
{
for (auto it = mNodeInformationEntries.begin(); it != mNodeInformationEntries.end(); ++it)
{
if (it->nodeID == nodeId)
{
mNodeInformationEntries.erase(it);
for (Listener * listener = mListeners; listener != nullptr; listener = listener->mNext)
{
listener->MarkNodeListChanged();
}
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::RefreshNode(NodeId nodeId)
{
// 1. && 2.
ReturnErrorOnFailure(SetNode(nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum::kPending));
// 3. TODO: Read the PartsList of the Descriptor cluster from the Node.
// 4. TODO
// 5. TODO
// 6.
ReturnErrorOnFailure(SetNode(nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted));
for (Listener * listener = mListeners; listener != nullptr; listener = listener->mNext)
{
listener->MarkNodeListChanged();
}
return CHIP_NO_ERROR;
}
CHIP_ERROR JointFabricDatastore::SetNode(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIDInDatastore(nodeId, index));
mNodeInformationEntries[index].commissioningStatusEntry.state = state;
return CHIP_NO_ERROR;
}
CHIP_ERROR JointFabricDatastore::IsNodeIDInDatastore(NodeId nodeId, size_t & index)
{
for (auto & entry : mNodeInformationEntries)
{
if (entry.nodeID == nodeId)
{
index = static_cast<size_t>(&entry - &mNodeInformationEntries[0]);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR
JointFabricDatastore::AddGroupKeySetEntry(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet)
{
VerifyOrReturnError(IsGroupKeySetEntryPresent(groupKeySet.groupKeySetID) == false, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mGroupKeySetList.size() < kMaxGroupKeySet, CHIP_ERROR_NO_MEMORY);
mGroupKeySetList.push_back(groupKeySet);
return CHIP_NO_ERROR;
}
bool JointFabricDatastore::IsGroupKeySetEntryPresent(uint16_t groupKeySetId)
{
for (auto & entry : mGroupKeySetList)
{
if (entry.groupKeySetID == groupKeySetId)
{
return true;
}
}
return false;
}
CHIP_ERROR JointFabricDatastore::RemoveGroupKeySetEntry(uint16_t groupKeySetId)
{
for (auto it = mGroupKeySetList.begin(); it != mGroupKeySetList.end(); ++it)
{
if (it->groupKeySetID == groupKeySetId)
{
mGroupKeySetList.erase(it);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR
JointFabricDatastore::UpdateGroupKeySetEntry(
Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet)
{
for (auto & entry : mGroupKeySetList)
{
if (entry.groupKeySetID == groupKeySet.groupKeySetID)
{
entry = groupKeySet;
ReturnErrorOnFailure(UpdateNodeKeySetList(entry));
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR
JointFabricDatastore::AddAdmin(
Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type & adminId)
{
VerifyOrReturnError(IsAdminEntryPresent(adminId.nodeID) == false, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mAdminEntries.size() < kMaxAdminNodes, CHIP_ERROR_NO_MEMORY);
mAdminEntries.push_back(adminId);
return CHIP_NO_ERROR;
}
bool JointFabricDatastore::IsAdminEntryPresent(NodeId nodeId)
{
for (auto & entry : mAdminEntries)
{
if (entry.nodeID == nodeId)
{
return true;
}
}
return false;
}
CHIP_ERROR JointFabricDatastore::UpdateAdmin(NodeId nodeId, CharSpan friendlyName, ByteSpan icac)
{
for (auto & entry : mAdminEntries)
{
if (entry.nodeID == nodeId)
{
entry.friendlyName = friendlyName;
entry.icac = icac;
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::RemoveAdmin(NodeId nodeId)
{
for (auto it = mAdminEntries.begin(); it != mAdminEntries.end(); ++it)
{
if (it->nodeID == nodeId)
{
mAdminEntries.erase(it);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR
JointFabricDatastore::UpdateNodeKeySetList(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet)
{
for (auto & entry : mNodeKeySetEntries)
{
if (entry.groupKeySetID == groupKeySet.groupKeySetID)
{
// TODO: update device
entry.statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted;
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::RemoveKeySet(uint16_t groupKeySetId)
{
for (auto it = mNodeKeySetEntries.begin(); it != mNodeKeySetEntries.end(); ++it)
{
if (it->groupKeySetID == groupKeySetId)
{
if (it->statusEntry.state != Clusters::JointFabricDatastore::DatastoreStateEnum::kDeletePending)
{
return CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR; // Cannot remove a key set that is not pending
}
ReturnErrorOnFailure(RemoveGroupKeySetEntry(groupKeySetId));
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::AddGroup(const Clusters::JointFabricDatastore::Commands::AddGroup::DecodableType & commandData)
{
size_t index = 0;
// Check if the group ID already exists in the datastore
VerifyOrReturnError(IsGroupIDInDatastore(commandData.groupID, index) == CHIP_ERROR_NOT_FOUND,
CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR);
if (commandData.groupCAT.ValueOr(0) == kAdminCATIdentifier || commandData.groupCAT.ValueOr(0) == kAnchorCATIdentifier)
{
// If the group is an AdminCAT or AnchorCAT, we cannot add it
return CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR;
}
Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type groupEntry;
groupEntry.groupID = commandData.groupID;
groupEntry.friendlyName = commandData.friendlyName;
groupEntry.groupKeySetID = commandData.groupKeySetID;
groupEntry.groupCAT = commandData.groupCAT;
groupEntry.groupCATVersion = commandData.groupCATVersion;
groupEntry.groupPermission = commandData.groupPermission;
// Add the group entry to the datastore
mGroupInformationEntries.push_back(groupEntry);
return CHIP_NO_ERROR;
}
CHIP_ERROR
JointFabricDatastore::UpdateGroup(const Clusters::JointFabricDatastore::Commands::UpdateGroup::DecodableType & commandData)
{
size_t index = 0;
// Check if the group ID exists in the datastore
VerifyOrReturnError(IsGroupIDInDatastore(commandData.groupID, index) == CHIP_NO_ERROR,
CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR);
if (commandData.groupCAT.ValueOr(0) == kAdminCATIdentifier || commandData.groupCAT.ValueOr(0) == kAnchorCATIdentifier)
{
// If the group is an AdminCAT or AnchorCAT, we cannot update it
return CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR;
}
// Update the group entry with the new data
if (commandData.friendlyName.IsNull() == false)
{
mGroupInformationEntries[index].friendlyName = commandData.friendlyName.Value();
}
if (commandData.groupKeySetID.IsNull() == false)
{
mGroupInformationEntries[index].groupKeySetID = commandData.groupKeySetID.Value();
}
if (commandData.groupCAT.IsNull() == false)
{
mGroupInformationEntries[index].groupCAT = commandData.groupCAT.Value();
}
if (commandData.groupCATVersion.IsNull() == false)
{
mGroupInformationEntries[index].groupCATVersion = commandData.groupCATVersion.Value();
}
if (commandData.groupPermission != Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum::kUnknownEnumValue)
{
// If the groupPermission is not set to kUnknownEnumValue, update it
mGroupInformationEntries[index].groupPermission = commandData.groupPermission;
}
// TODO: iterate through each Node Information Entry to check for membership, and when found, set to pending, update device, set
// to committed, etc.
return CHIP_NO_ERROR;
}
CHIP_ERROR
JointFabricDatastore::RemoveGroup(const Clusters::JointFabricDatastore::Commands::RemoveGroup::DecodableType & commandData)
{
size_t index = 0;
// Check if the group ID exists in the datastore
VerifyOrReturnError(IsGroupIDInDatastore(commandData.groupID, index) == CHIP_NO_ERROR,
CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR);
// Remove the group entry from the datastore
auto it = mGroupInformationEntries.begin();
std::advance(it, index);
if (it->groupCAT == kAdminCATIdentifier || it->groupCAT == kAnchorCATIdentifier)
{
// If the group is an AdminCAT or AnchorCAT, we cannot remove it
return CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR;
}
mGroupInformationEntries.erase(it);
return CHIP_NO_ERROR;
}
CHIP_ERROR JointFabricDatastore::IsGroupIDInDatastore(chip::GroupId groupId, size_t & index)
{
for (auto & entry : mGroupInformationEntries)
{
if (entry.groupID == groupId)
{
index = static_cast<size_t>(&entry - &mGroupInformationEntries[0]);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::IsNodeIdInNodeInformationEntries(NodeId nodeId, size_t & index)
{
for (auto & entry : mNodeInformationEntries)
{
if (entry.nodeID == nodeId)
{
index = static_cast<size_t>(&entry - &mNodeInformationEntries[0]);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::UpdateEndpointForNode(NodeId nodeId, chip::EndpointId endpointId, CharSpan friendlyName)
{
for (auto & entry : mEndpointEntries)
{
if (entry.nodeID == nodeId && entry.endpointID == endpointId)
{
entry.friendlyName = friendlyName;
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::IsNodeIdAndEndpointInEndpointInformationEntries(NodeId nodeId, EndpointId endpointId,
size_t & index)
{
for (auto & entry : mEndpointEntries)
{
if (entry.nodeID == nodeId && entry.endpointID == endpointId)
{
index = static_cast<size_t>(&entry - &mEndpointEntries[0]);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR JointFabricDatastore::AddGroupIDToEndpointForNode(NodeId nodeId, chip::EndpointId endpointId, chip::GroupId groupId)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIdAndEndpointInEndpointInformationEntries(nodeId, endpointId, index));
VerifyOrReturnError(IsGroupIDInDatastore(groupId, index) == CHIP_ERROR_NOT_FOUND, CHIP_ERROR_IM_STATUS_CODE_CONSTRAINT_ERROR);
if (mGroupInformationEntries[index].groupKeySetID.IsNull() == false)
{
uint16_t groupKeySetID = mGroupInformationEntries[index].groupKeySetID.Value();
// make sure mNodeKeySetEntries contains an entry for this keyset and node, else add one and update device
bool nodeKeySetExists = false;
for (auto & entry : mNodeKeySetEntries)
{
if (entry.nodeID == nodeId && entry.groupKeySetID == groupKeySetID)
{
nodeKeySetExists = true;
break; // Found the group key set, no need to add it again
}
}
if (!nodeKeySetExists)
{
// Create a new group key set entry if it doesn't exist
Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type newNodeKeySet;
newNodeKeySet.nodeID = nodeId;
newNodeKeySet.groupKeySetID = groupKeySetID;
newNodeKeySet.statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kPending;
// TODO: Update device
mNodeKeySetEntries.push_back(newNodeKeySet);
mNodeKeySetEntries.back().statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted;
}
}
// Check if the group ID already exists for the endpoint
for (auto & entry : mEndpointGroupIDEntries)
{
if (entry.nodeID == nodeId && entry.endpointID == endpointId && entry.groupID == groupId)
{
return CHIP_NO_ERROR;
}
}
VerifyOrReturnError(mEndpointGroupIDEntries.size() < kMaxGroups, CHIP_ERROR_NO_MEMORY);
// Create a new endpoint group ID entry
Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type newGroupEntry;
newGroupEntry.nodeID = nodeId;
newGroupEntry.endpointID = endpointId;
newGroupEntry.groupID = groupId;
newGroupEntry.statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kPending;
// TODO: Update device
// Add the new ACL entry to the datastore
mEndpointGroupIDEntries.push_back(newGroupEntry);
mEndpointGroupIDEntries.back().statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted;
return CHIP_NO_ERROR;
}
CHIP_ERROR JointFabricDatastore::RemoveGroupIDFromEndpointForNode(NodeId nodeId, chip::EndpointId endpointId, chip::GroupId groupId)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIdAndEndpointInEndpointInformationEntries(nodeId, endpointId, index));
for (auto it = mEndpointGroupIDEntries.begin(); it != mEndpointGroupIDEntries.end(); ++it)
{
if (it->nodeID == nodeId && it->endpointID == endpointId && it->groupID == groupId)
{
it->statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kDeletePending;
// TODO: Update device
mEndpointGroupIDEntries.erase(it);
if (IsGroupIDInDatastore(groupId, index) == CHIP_NO_ERROR)
{
for (auto it2 = mNodeKeySetEntries.begin(); it2 != mNodeKeySetEntries.end();)
{
bool incrementIndex = true;
if (it2->nodeID == nodeId && mGroupInformationEntries[index].groupKeySetID.IsNull() == false &&
it2->groupKeySetID == mGroupInformationEntries[index].groupKeySetID.Value())
{
it2->statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kDeletePending;
// TODO: Update device
it2 = mNodeKeySetEntries.erase(it2);
incrementIndex = false;
}
if (incrementIndex)
{
++it2;
}
else
{
incrementIndex = true;
}
}
}
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
// look-up the highest listId used so far, from Endpoint Binding Entries and ACL Entries
CHIP_ERROR JointFabricDatastore::GenerateAndAssignAUniqueListID(uint16_t & listId)
{
uint16_t highestListID = 0;
for (auto & entry : mEndpointBindingEntries)
{
if (entry.listID >= highestListID)
{
highestListID = entry.listID + 1;
}
}
for (auto & entry : mACLEntries)
{
if (entry.listID >= highestListID)
{
highestListID = entry.listID + 1;
}
}
listId = highestListID;
return CHIP_NO_ERROR;
}
bool JointFabricDatastore::BindingMatches(
const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding1,
const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding2)
{
if (binding1.node.HasValue() && binding2.node.HasValue())
{
if (binding1.node.Value() != binding2.node.Value())
{
return false;
}
}
else if (binding1.node.HasValue() || binding2.node.HasValue())
{
return false;
}
if (binding1.group.HasValue() && binding2.group.HasValue())
{
if (binding1.group.Value() != binding2.group.Value())
{
return false;
}
}
else if (binding1.group.HasValue() || binding2.group.HasValue())
{
return false;
}
if (binding1.endpoint.HasValue() && binding2.endpoint.HasValue())
{
if (binding1.endpoint.Value() != binding2.endpoint.Value())
{
return false;
}
}
else if (binding1.endpoint.HasValue() || binding2.endpoint.HasValue())
{
return false;
}
if (binding1.cluster.HasValue() && binding2.cluster.HasValue())
{
if (binding1.cluster.Value() != binding2.cluster.Value())
{
return false;
}
}
else if (binding1.cluster.HasValue() || binding2.cluster.HasValue())
{
return false;
}
return true;
}
CHIP_ERROR
JointFabricDatastore::AddBindingToEndpointForNode(
NodeId nodeId, chip::EndpointId endpointId,
const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIdAndEndpointInEndpointInformationEntries(nodeId, endpointId, index));
// Check if the group ID already exists for the endpoint
for (auto & entry : mEndpointBindingEntries)
{
if (entry.nodeID == nodeId && entry.endpointID == endpointId)
{
if (BindingMatches(entry.binding, binding))
{
return CHIP_NO_ERROR;
}
}
}
VerifyOrReturnError(mEndpointBindingEntries.size() < kMaxGroups, CHIP_ERROR_NO_MEMORY);
// Create a new binding entry
Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type newBindingEntry;
newBindingEntry.nodeID = nodeId;
newBindingEntry.endpointID = endpointId;
newBindingEntry.binding = binding;
ReturnErrorOnFailure(GenerateAndAssignAUniqueListID(newBindingEntry.listID));
newBindingEntry.statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kPending;
// Add the new binding entry to the datastore
mEndpointBindingEntries.push_back(newBindingEntry);
// TODO: Update device
mEndpointBindingEntries.back().statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted;
return CHIP_NO_ERROR;
}
CHIP_ERROR
JointFabricDatastore::RemoveBindingFromEndpointForNode(uint16_t listId, NodeId nodeId, chip::EndpointId endpointId)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIdAndEndpointInEndpointInformationEntries(nodeId, endpointId, index));
for (auto it = mEndpointBindingEntries.begin(); it != mEndpointBindingEntries.end(); ++it)
{
if (it->nodeID == nodeId && it->listID == listId && it->endpointID == endpointId)
{
it->statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kDeletePending;
// TODO: Update device
mEndpointBindingEntries.erase(it);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
bool JointFabricDatastore::ACLTargetMatches(
const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target1,
const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target2)
{
if (!target1.cluster.IsNull() && !target2.cluster.IsNull())
{
if (target1.cluster.Value() != target2.cluster.Value())
{
return false;
}
}
else if (!target1.cluster.IsNull() || !target2.cluster.IsNull())
{
return false;
}
if (!target1.endpoint.IsNull() && !target2.endpoint.IsNull())
{
if (target1.endpoint.Value() != target2.endpoint.Value())
{
return false;
}
}
else if (!target1.endpoint.IsNull() || !target2.endpoint.IsNull())
{
return false;
}
if (!target1.deviceType.IsNull() && !target2.deviceType.IsNull())
{
if (target1.deviceType.Value() != target2.deviceType.Value())
{
return false;
}
}
else if (!target1.deviceType.IsNull() || !target2.deviceType.IsNull())
{
return false;
}
return true;
}
bool JointFabricDatastore::ACLMatches(
const datastore::AccessControlEntryStruct & acl1,
const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & acl2)
{
if (acl1.privilege != acl2.privilege)
{
return false;
}
if (acl1.authMode != acl2.authMode)
{
return false;
}
{
auto it1 = acl1.subjects.begin();
auto it2 = acl2.subjects.Value().begin();
while (it1 != acl1.subjects.end() && it2.Next())
{
if (*it1 != it2.GetValue())
{
return false;
}
++it1;
}
if (it2.Next())
{
return false; // acl2 has more subjects
}
}
{
auto it1 = acl1.targets.begin();
auto it2 = acl2.targets.Value().begin();
while (it1 != acl1.targets.end() && it2.Next())
{
if (ACLTargetMatches(*it1, it2.GetValue()) == false)
{
return false;
}
++it1;
}
if (it2.Next())
{
return false; // acl2 has more targets
}
}
return true;
}
CHIP_ERROR
JointFabricDatastore::AddACLToNode(
NodeId nodeId, const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & aclEntry)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIdInNodeInformationEntries(nodeId, index));
// Check if the ACL entry already exists for the node
for (auto & entry : mACLEntries)
{
if (entry.nodeID == nodeId)
{
if (ACLMatches(entry.ACLEntry, aclEntry))
{
return CHIP_NO_ERROR;
}
}
}
VerifyOrReturnError(mACLEntries.size() < kMaxACLs, CHIP_ERROR_NO_MEMORY);
// Create a new ACL entry
datastore::ACLEntryStruct newACLEntry;
newACLEntry.nodeID = nodeId;
newACLEntry.ACLEntry.privilege = aclEntry.privilege;
newACLEntry.ACLEntry.authMode = aclEntry.authMode;
newACLEntry.statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kPending;
{
auto iter = aclEntry.subjects.Value().begin();
while (iter.Next())
{
newACLEntry.ACLEntry.subjects.push_back(iter.GetValue());
}
ReturnErrorOnFailure(iter.GetStatus());
}
{
auto iter = aclEntry.targets.Value().begin();
while (iter.Next())
{
newACLEntry.ACLEntry.targets.push_back(iter.GetValue());
}
ReturnErrorOnFailure(iter.GetStatus());
}
ReturnErrorOnFailure(GenerateAndAssignAUniqueListID(newACLEntry.listID));
// Add the new ACL entry to the datastore
mACLEntries.push_back(newACLEntry);
// TODO: Update device
mACLEntries.back().statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kCommitted;
return CHIP_NO_ERROR;
}
CHIP_ERROR JointFabricDatastore::RemoveACLFromNode(uint16_t listId, NodeId nodeId)
{
size_t index = 0;
ReturnErrorOnFailure(IsNodeIdInNodeInformationEntries(nodeId, index));
for (auto it = mACLEntries.begin(); it != mACLEntries.end(); ++it)
{
if (it->nodeID == nodeId && it->listID == listId)
{
it->statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kDeletePending;
// TODO: Update device
mACLEntries.erase(it);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NOT_FOUND;
}
} // namespace app
} // namespace chip