| /* |
| * |
| * 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. |
| */ |
| |
| #pragma once |
| |
| #include <app-common/zap-generated/cluster-objects.h> |
| #include <credentials/CHIPCert.h> |
| #include <lib/core/CHIPPersistentStorageDelegate.h> |
| #include <lib/core/CHIPVendorIdentifiers.hpp> |
| #include <lib/core/NodeId.h> |
| #include <vector> |
| |
| namespace chip { |
| namespace app { |
| |
| namespace datastore { |
| |
| struct AccessControlEntryStruct |
| { |
| Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum privilege = |
| static_cast<Clusters::JointFabricDatastore::DatastoreAccessControlEntryPrivilegeEnum>(0); |
| Clusters::JointFabricDatastore::DatastoreAccessControlEntryAuthModeEnum authMode = |
| static_cast<Clusters::JointFabricDatastore::DatastoreAccessControlEntryAuthModeEnum>(0); |
| std::vector<uint64_t> subjects; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type> targets; |
| }; |
| |
| struct ACLEntryStruct |
| { |
| chip::NodeId nodeID = static_cast<chip::NodeId>(0); |
| uint16_t listID = static_cast<uint16_t>(0); |
| AccessControlEntryStruct ACLEntry; |
| Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type statusEntry; |
| }; |
| |
| } // namespace datastore |
| |
| /** |
| * A struct which extends the DatastoreNodeInformationEntry type with FriendlyName buffer reservation. |
| */ |
| struct GenericDatastoreNodeInformationEntry |
| : public Clusters::JointFabricDatastore::Structs::DatastoreNodeInformationEntryStruct::Type |
| { |
| GenericDatastoreNodeInformationEntry(NodeId nodeId = 0, |
| Clusters::JointFabricDatastore::DatastoreStateEnum state = |
| Clusters::JointFabricDatastore::DatastoreStateEnum::kUnknownEnumValue, |
| Optional<CharSpan> label = NullOptional) |
| { |
| Set(nodeId, state, label); |
| } |
| |
| GenericDatastoreNodeInformationEntry(const GenericDatastoreNodeInformationEntry & op) { *this = op; } |
| |
| GenericDatastoreNodeInformationEntry & operator=(const GenericDatastoreNodeInformationEntry & op) |
| { |
| Set(op.nodeID, op.commissioningStatusEntry.state, MakeOptional(op.friendlyName)); |
| return *this; |
| } |
| |
| void Set(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state, Optional<CharSpan> label = NullOptional) |
| { |
| this->nodeID = nodeId; |
| this->commissioningStatusEntry.state = state; |
| Set(label); |
| } |
| |
| void Set(Optional<CharSpan> label = NullOptional) |
| { |
| if (label.HasValue()) |
| { |
| memset(mFriendlyNameBuffer, 0, sizeof(mFriendlyNameBuffer)); |
| if (label.Value().size() > sizeof(mFriendlyNameBuffer)) |
| { |
| memcpy(mFriendlyNameBuffer, label.Value().data(), sizeof(mFriendlyNameBuffer)); |
| this->friendlyName = CharSpan(mFriendlyNameBuffer, sizeof(mFriendlyNameBuffer)); |
| } |
| else |
| { |
| memcpy(mFriendlyNameBuffer, label.Value().data(), label.Value().size()); |
| this->friendlyName = CharSpan(mFriendlyNameBuffer, label.Value().size()); |
| } |
| } |
| else |
| { |
| this->friendlyName = CharSpan(); |
| } |
| } |
| |
| private: |
| static constexpr size_t kFriendlyNameMaxSize = 32u; |
| |
| char mFriendlyNameBuffer[kFriendlyNameMaxSize]; |
| }; |
| |
| class JointFabricDatastore |
| { |
| public: |
| static JointFabricDatastore & GetInstance() |
| { |
| static JointFabricDatastore sInstance; |
| return sInstance; |
| } |
| |
| ByteSpan GetAnchorRootCA() const { return ByteSpan(mAnchorRootCA, mAnchorRootCALength); } |
| |
| CHIP_ERROR SetAnchorNodeId(NodeId anchorNodeId) |
| { |
| mAnchorNodeId = anchorNodeId; |
| return CHIP_NO_ERROR; |
| } |
| NodeId GetAnchorNodeId() { return mAnchorNodeId; } |
| |
| CHIP_ERROR SetAnchorVendorId(VendorId anchorVendorId) |
| { |
| mAnchorVendorId = anchorVendorId; |
| return CHIP_NO_ERROR; |
| } |
| VendorId GetAnchorVendorId() { return mAnchorVendorId; } |
| |
| CHIP_ERROR SetFriendlyName(const CharSpan & friendlyName) |
| { |
| if (friendlyName.size() >= sizeof(mFriendlyNameBuffer)) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| mFriendlyNameBufferLength = friendlyName.size(); |
| memcpy(mFriendlyNameBuffer, friendlyName.data(), mFriendlyNameBufferLength); |
| mFriendlyNameBuffer[mFriendlyNameBufferLength] = '\0'; // Ensure null-termination |
| return CHIP_NO_ERROR; |
| } |
| CharSpan GetFriendlyName() const { return CharSpan(mFriendlyNameBuffer, mFriendlyNameBufferLength); } |
| |
| const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> & GetGroupEntries() |
| { |
| return mGroupInformationEntries; |
| } |
| |
| Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type & GetStatus() { return mDatastoreStatusEntry; } |
| |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type> & GetEndpointGroupIDList() |
| { |
| return mEndpointGroupIDEntries; |
| } |
| |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> & GetEndpointBindingList() |
| { |
| return mEndpointBindingEntries; |
| } |
| |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type> & GetNodeKeySetList() |
| { |
| return mNodeKeySetEntries; |
| } |
| |
| std::vector<datastore::ACLEntryStruct> & GetNodeACLList() { return mACLEntries; } |
| |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> & GetNodeEndpointList() |
| { |
| return mEndpointEntries; |
| } |
| |
| CHIP_ERROR AddPendingNode(NodeId nodeId, const CharSpan & friendlyName); |
| CHIP_ERROR UpdateNode(NodeId nodeId, const CharSpan & friendlyName); |
| CHIP_ERROR RemoveNode(NodeId nodeId); |
| CHIP_ERROR RefreshNode(NodeId nodeId); |
| |
| CHIP_ERROR SetNode(NodeId nodeId, Clusters::JointFabricDatastore::DatastoreStateEnum state); |
| |
| CHIP_ERROR AddGroupKeySetEntry(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet); |
| bool IsGroupKeySetEntryPresent(uint16_t groupKeySetId); |
| CHIP_ERROR RemoveGroupKeySetEntry(uint16_t groupKeySetId); |
| CHIP_ERROR UpdateGroupKeySetEntry(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet); |
| |
| CHIP_ERROR AddAdmin(Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type & adminId); |
| bool IsAdminEntryPresent(NodeId nodeId); |
| CHIP_ERROR UpdateAdmin(NodeId nodeId, CharSpan friendlyName, ByteSpan icac); |
| CHIP_ERROR RemoveAdmin(NodeId nodeId); |
| |
| CHIP_ERROR AddGroup(const Clusters::JointFabricDatastore::Commands::AddGroup::DecodableType & commandData); |
| CHIP_ERROR UpdateGroup(const Clusters::JointFabricDatastore::Commands::UpdateGroup::DecodableType & commandData); |
| CHIP_ERROR RemoveGroup(const Clusters::JointFabricDatastore::Commands::RemoveGroup::DecodableType & commandData); |
| |
| CHIP_ERROR UpdateEndpointForNode(NodeId nodeId, EndpointId endpointId, CharSpan friendlyName); |
| |
| CHIP_ERROR AddGroupIDToEndpointForNode(NodeId nodeId, EndpointId endpointId, GroupId groupId); |
| CHIP_ERROR RemoveGroupIDFromEndpointForNode(NodeId nodeId, EndpointId endpointId, GroupId groupId); |
| |
| CHIP_ERROR |
| AddBindingToEndpointForNode(NodeId nodeId, EndpointId endpointId, |
| const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding); |
| CHIP_ERROR |
| RemoveBindingFromEndpointForNode(uint16_t listId, NodeId nodeId, EndpointId endpointId); |
| |
| CHIP_ERROR |
| AddACLToNode(NodeId nodeId, |
| const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & aclEntry); |
| CHIP_ERROR RemoveACLFromNode(uint16_t listId, NodeId nodeId); |
| |
| const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> & GetGroupKeySetList() |
| { |
| return mGroupKeySetList; |
| } |
| const std::vector<GenericDatastoreNodeInformationEntry> & GetNodeInformationEntries() { return mNodeInformationEntries; } |
| const std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type> & |
| GetAdminEntries() |
| { |
| return mAdminEntries; |
| } |
| |
| /** |
| * Used to notify of changes in the node list and more TODO. |
| */ |
| class Listener |
| { |
| public: |
| virtual ~Listener() = default; |
| |
| /** |
| * Notifies of a change in the node list. |
| */ |
| virtual void MarkNodeListChanged() = 0; |
| |
| private: |
| Listener * mNext = nullptr; |
| |
| friend class JointFabricDatastore; |
| }; |
| |
| /** |
| * Add a listener to be notified of changes in the Joint Fabric Datastore. |
| * |
| * @param [in] listener The listener to add. |
| */ |
| void AddListener(Listener & listener); |
| |
| /** |
| * Remove a listener from being notified of changes in the Joint Fabric Datastore. |
| * |
| * @param [in] listener The listener to remove. |
| */ |
| void RemoveListener(Listener & listener); |
| |
| private: |
| static constexpr size_t kMaxNodes = 256; |
| static constexpr size_t kMaxAdminNodes = 32; |
| static constexpr size_t kMaxGroups = kMaxNodes / 16; |
| static constexpr size_t kMaxGroupKeySet = kMaxGroups * 16; |
| static constexpr size_t kMaxFriendlyNameSize = 32; |
| static constexpr size_t kMaxACLs = 64; |
| |
| uint8_t mAnchorRootCA[Credentials::kMaxDERCertLength] = { 0 }; |
| size_t mAnchorRootCALength = 0; |
| char mFriendlyNameBuffer[kMaxFriendlyNameSize] = { 0 }; |
| size_t mFriendlyNameBufferLength = 0; |
| NodeId mAnchorNodeId = kUndefinedNodeId; |
| VendorId mAnchorVendorId = VendorId::NotSpecified; |
| Clusters::JointFabricDatastore::Structs::DatastoreStatusEntryStruct::Type mDatastoreStatusEntry; |
| |
| // TODO: Persist these members to local storage |
| std::vector<GenericDatastoreNodeInformationEntry> mNodeInformationEntries; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> mGroupKeySetList; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreAdministratorInformationEntryStruct::Type> mAdminEntries; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreGroupInformationEntryStruct::Type> mGroupInformationEntries; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type> mEndpointGroupIDEntries; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> mEndpointBindingEntries; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type> mNodeKeySetEntries; |
| std::vector<datastore::ACLEntryStruct> mACLEntries; |
| std::vector<Clusters::JointFabricDatastore::Structs::DatastoreEndpointEntryStruct::Type> mEndpointEntries; |
| |
| Listener * mListeners = nullptr; |
| |
| CHIP_ERROR IsNodeIDInDatastore(NodeId nodeId, size_t & index); |
| |
| CHIP_ERROR UpdateNodeKeySetList(Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type & groupKeySet); |
| CHIP_ERROR RemoveKeySet(uint16_t groupKeySetId); |
| |
| CHIP_ERROR IsGroupIDInDatastore(GroupId groupId, size_t & index); |
| CHIP_ERROR IsNodeIdInNodeInformationEntries(NodeId nodeId, size_t & index); |
| CHIP_ERROR IsNodeIdAndEndpointInEndpointInformationEntries(NodeId nodeId, EndpointId endpointId, size_t & index); |
| |
| CHIP_ERROR GenerateAndAssignAUniqueListID(uint16_t & listId); |
| bool BindingMatches(const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding1, |
| const Clusters::JointFabricDatastore::Structs::DatastoreBindingTargetStruct::Type & binding2); |
| bool ACLMatches(const datastore::AccessControlEntryStruct & acl1, |
| const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlEntryStruct::DecodableType & acl2); |
| bool ACLTargetMatches(const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target1, |
| const Clusters::JointFabricDatastore::Structs::DatastoreAccessControlTargetStruct::Type & target2); |
| }; |
| |
| } // namespace app |
| } // namespace chip |