Matter SDK Coverage Report
Current view: top level - access/examples - GroupAuxiliaryAccessControlDelegate.cpp (source / functions) Coverage Total Hit
Test: SHA:3f9cd168e84cd831b7699126f5296f5c5498690f Lines: 84.7 % 111 94
Test Date: 2026-04-27 19:52:19 Functions: 76.5 % 17 13

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2026 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : #include "GroupAuxiliaryAccessControlDelegate.h"
      20              : 
      21              : #include <credentials/FabricTable.h>
      22              : #include <credentials/GroupDataProvider.h>
      23              : #include <lib/core/CHIPCore.h>
      24              : #include <lib/core/NodeId.h>
      25              : #include <lib/core/Optional.h>
      26              : #include <lib/support/CHIPMem.h>
      27              : #include <lib/support/CodeUtils.h>
      28              : #include <lib/support/TypeTraits.h>
      29              : 
      30              : namespace {
      31              : 
      32              : using namespace chip;
      33              : 
      34              : using chip::Access::AccessControl;
      35              : using chip::Access::AuthMode;
      36              : using chip::Access::AuxiliaryType;
      37              : using chip::Access::Privilege;
      38              : using chip::Access::RequestPath;
      39              : using chip::Access::SubjectDescriptor;
      40              : 
      41              : using Entry         = chip::Access::AccessControl::Entry;
      42              : using EntryIterator = chip::Access::AccessControl::EntryIterator;
      43              : using Target        = Entry::Target;
      44              : 
      45              : class EntryDelegate : public AccessControl::Entry::Delegate
      46              : {
      47              : public:
      48            8 :     void Init(Entry & entry, FabricIndex fabricIndex, GroupId groupId, EndpointId endpointId)
      49              :     {
      50            8 :         mFabricIndex = fabricIndex;
      51            8 :         mGroupId     = groupId;
      52            8 :         mEndpointId  = endpointId;
      53            8 :         entry.SetDelegate(*this);
      54            8 :     }
      55              : 
      56            8 :     void Release() override { Platform::Delete(this); }
      57              : 
      58            0 :     CHIP_ERROR GetAuthMode(AuthMode & authMode) const override
      59              :     {
      60            0 :         authMode = AuthMode::kGroup;
      61            0 :         return CHIP_NO_ERROR;
      62              :     }
      63              : 
      64            8 :     CHIP_ERROR GetFabricIndex(FabricIndex & fabricIndex) const override
      65              :     {
      66            8 :         fabricIndex = mFabricIndex;
      67            8 :         return CHIP_NO_ERROR;
      68              :     }
      69              : 
      70            0 :     CHIP_ERROR GetPrivilege(Privilege & privilege) const override
      71              :     {
      72            0 :         privilege = Privilege::kOperate;
      73            0 :         return CHIP_NO_ERROR;
      74              :     }
      75              : 
      76            0 :     CHIP_ERROR GetAuxiliaryType(AuxiliaryType & auxiliaryType) const override
      77              :     {
      78            0 :         auxiliaryType = AuxiliaryType::kGroupcast;
      79            0 :         return CHIP_NO_ERROR;
      80              :     }
      81              : 
      82            8 :     CHIP_ERROR GetSubjectCount(size_t & count) const override
      83              :     {
      84            8 :         count = 1;
      85            8 :         return CHIP_NO_ERROR;
      86              :     }
      87              : 
      88            8 :     CHIP_ERROR GetSubject(size_t index, NodeId & subject) const override
      89              :     {
      90            8 :         if (index == 0)
      91              :         {
      92            8 :             subject = NodeIdFromGroupId(mGroupId);
      93            8 :             return CHIP_NO_ERROR;
      94              :         }
      95            0 :         return CHIP_ERROR_SENTINEL;
      96              :     }
      97              : 
      98            8 :     CHIP_ERROR GetTargetCount(size_t & count) const override
      99              :     {
     100            8 :         count = 1;
     101            8 :         return CHIP_NO_ERROR;
     102              :     }
     103              : 
     104            8 :     CHIP_ERROR GetTarget(size_t index, Target & target) const override
     105              :     {
     106            8 :         if (index == 0)
     107              :         {
     108            8 :             target.flags    = Target::kEndpoint;
     109            8 :             target.endpoint = mEndpointId;
     110            8 :             return CHIP_NO_ERROR;
     111              :         }
     112            0 :         return CHIP_ERROR_SENTINEL;
     113              :     }
     114              : 
     115              : private:
     116              :     FabricIndex mFabricIndex;
     117              :     GroupId mGroupId;
     118              :     EndpointId mEndpointId;
     119              : };
     120              : 
     121              : class AuxiliaryEntryIteratorDelegate : public EntryIterator::Delegate
     122              : {
     123              : public:
     124            4 :     void Init(EntryIterator & iterator, Credentials::GroupDataProvider * groupDataProvider, FabricTable * fabricTable,
     125              :               FabricIndex fabricIndex)
     126              :     {
     127            4 :         mGroupDataProvider = groupDataProvider;
     128            4 :         mFabricTable       = fabricTable;
     129            4 :         mFabricIndex       = fabricIndex;
     130              : 
     131            4 :         if (mFabricIndex == kUndefinedFabricIndex)
     132              :         {
     133            2 :             mIterateOverFabricIndices = true;
     134              :             // If the fabric table is defined, it can be used to find and iterate over all
     135              :             // valid existing fabric indices. Otherwise, iteration can be done starting from
     136              :             // the minimum fabric index and going up
     137            2 :             if (mFabricTable)
     138              :             {
     139            1 :                 mFabricTableIter.SetValue(mFabricTable->begin());
     140            1 :                 if (mFabricTableIter.Value() != mFabricTable->end())
     141              :                 {
     142            1 :                     mFabricIndex = mFabricTableIter.Value()->GetFabricIndex();
     143              :                 }
     144              :             }
     145              :             else
     146              :             {
     147            1 :                 mFabricIndex = kMinValidFabricIndex;
     148              :             }
     149              :         }
     150              : 
     151            4 :         if (mGroupDataProvider)
     152              :         {
     153            4 :             mGroupInfoIterator = mGroupDataProvider->IterateGroupInfo(mFabricIndex);
     154              :         }
     155            4 :         iterator.SetDelegate(*this);
     156            4 :     }
     157              : 
     158            4 :     ~AuxiliaryEntryIteratorDelegate() override
     159            4 :     {
     160            4 :         if (mGroupInfoIterator)
     161              :         {
     162            0 :             mGroupInfoIterator->Release();
     163              :         }
     164            4 :         if (mEndpointIterator)
     165              :         {
     166            0 :             mEndpointIterator->Release();
     167              :         }
     168            4 :     }
     169              : 
     170            4 :     void Release() override { Platform::Delete(this); }
     171              : 
     172           12 :     CHIP_ERROR Next(Entry & entry) override
     173              :     {
     174           12 :         if (mGroupDataProvider == nullptr)
     175              :         {
     176            0 :             return CHIP_ERROR_SENTINEL;
     177              :         }
     178              : 
     179          278 :         while (mGroupInfoIterator != nullptr || mEndpointIterator != nullptr || mIterateOverFabricIndices)
     180              :         {
     181          274 :             if (mEndpointIterator != nullptr)
     182              :             {
     183           15 :                 Credentials::GroupDataProvider::GroupEndpoint endpoint;
     184           15 :                 if (mEndpointIterator->Next(endpoint))
     185              :                 {
     186              :                     // Groups cannot be created with endpoint 0, so this state should never be reached
     187              :                     // because of restrictions with creating/joining groups. We skip here in the case
     188              :                     // this does occur, no entry would be needed to validate against for endpoint 0.
     189            8 :                     if (endpoint.endpoint_id == kRootEndpointId)
     190              :                     {
     191            0 :                         continue;
     192              :                     }
     193              : 
     194            8 :                     auto * delegate = Platform::New<EntryDelegate>();
     195            8 :                     if (delegate == nullptr)
     196              :                     {
     197            0 :                         return CHIP_ERROR_NO_MEMORY;
     198              :                     }
     199            8 :                     delegate->Init(entry, mFabricIndex, mGroupId, endpoint.endpoint_id);
     200            8 :                     return CHIP_NO_ERROR;
     201              :                 }
     202            7 :                 mEndpointIterator->Release();
     203            7 :                 mEndpointIterator = nullptr;
     204              :             }
     205              : 
     206          266 :             if (mGroupInfoIterator != nullptr)
     207              :             {
     208          266 :                 Credentials::GroupDataProvider::GroupInfo info;
     209          266 :                 if (mGroupInfoIterator->Next(info))
     210              :                 {
     211            8 :                     if (info.flags & to_underlying(Credentials::GroupDataProvider::GroupInfo::Flags::kHasAuxiliaryACL))
     212              :                     {
     213            7 :                         mGroupId          = info.group_id;
     214            7 :                         mEndpointIterator = mGroupDataProvider->IterateEndpoints(mFabricIndex, mGroupId);
     215              :                     }
     216            8 :                     continue;
     217              :                 }
     218          258 :                 mGroupInfoIterator->Release();
     219          258 :                 mGroupInfoIterator = nullptr;
     220              :             }
     221              : 
     222          258 :             if (mIterateOverFabricIndices)
     223              :             {
     224              :                 // This indicates that there are no more fabric indices to iterate over (unless it is
     225              :                 // determined in the conditions below that at least 1 more is remaining, in which
     226              :                 // case this will be set to true again).
     227          256 :                 mIterateOverFabricIndices = false;
     228              : 
     229          256 :                 if (mFabricTable && mFabricTableIter.HasValue())
     230              :                 {
     231            2 :                     if ((mFabricTableIter.Value() != mFabricTable->end()) && (++mFabricTableIter.Value() != mFabricTable->end()))
     232              :                     {
     233            1 :                         mFabricIndex              = mFabricTableIter.Value()->GetFabricIndex();
     234            1 :                         mGroupInfoIterator        = mGroupDataProvider->IterateGroupInfo(mFabricIndex);
     235            1 :                         mIterateOverFabricIndices = true;
     236              :                     }
     237              :                 }
     238          254 :                 else if (mFabricIndex < kMaxValidFabricIndex)
     239              :                 {
     240          253 :                     mFabricIndex++;
     241          253 :                     mGroupInfoIterator        = mGroupDataProvider->IterateGroupInfo(mFabricIndex);
     242          253 :                     mIterateOverFabricIndices = true;
     243              :                 }
     244              :             }
     245              :         }
     246              : 
     247            4 :         return CHIP_ERROR_SENTINEL;
     248              :     }
     249              : 
     250              : private:
     251              :     Credentials::GroupDataProvider * mGroupDataProvider;
     252              :     FabricTable * mFabricTable;
     253              :     FabricIndex mFabricIndex;
     254              :     chip::Optional<chip::ConstFabricIterator> mFabricTableIter;
     255              :     Credentials::GroupDataProvider::GroupInfoIterator * mGroupInfoIterator = nullptr;
     256              :     Credentials::GroupDataProvider::EndpointIterator * mEndpointIterator   = nullptr;
     257              :     GroupId mGroupId                                                       = kUndefinedGroupId;
     258              : 
     259              :     // When true, this indicates the entries are not fabric scoped. AuxiliaryEntries() through
     260              :     // the Next() function will iterate over all fabrics to fetch auxiliary ACL entries. This
     261              :     // will be set to true when a fabric index is not specified (kUndefinedFabricIndex).
     262              :     bool mIterateOverFabricIndices = false;
     263              : };
     264              : 
     265              : } // namespace
     266              : 
     267              : namespace chip {
     268              : namespace Access {
     269              : namespace Examples {
     270              : 
     271              : /*
     272              :  * This function (in conjunction with Next() from the AuxiliaryEntryIteratorDelegate) will create an auxiliary
     273              :  * ACL entry for every <fabric index, group ID, endpoint ID> that belongs based on the information from
     274              :  * the group data provider. This is the simplest base case of what a set of auxiliary ACL entries will look
     275              :  * like. The structure of auxiliary ACL entries can be formatted differently, as long as the equivalence class
     276              :  * maps to this simplest base case.
     277              :  */
     278            4 : CHIP_ERROR GroupAuxiliaryAccessControlDelegate::AuxiliaryEntries(AccessControl::EntryIterator & iterator,
     279              :                                                                  const FabricIndex * fabricIndex) const
     280              : {
     281            4 :     auto * delegate = Platform::New<AuxiliaryEntryIteratorDelegate>();
     282            4 :     if (delegate)
     283              :     {
     284            4 :         delegate->Init(iterator, mGroupDataProvider, mFabricTable, fabricIndex ? *fabricIndex : kUndefinedFabricIndex);
     285            4 :         return CHIP_NO_ERROR;
     286              :     }
     287            0 :     return CHIP_ERROR_NO_MEMORY;
     288              : }
     289              : 
     290            4 : CHIP_ERROR GroupAuxiliaryAccessControlDelegate::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
     291              :                                                       Privilege requestPrivilege)
     292              : {
     293            4 :     if (IsGroupId(subjectDescriptor.subject) && (mGroupDataProvider != nullptr))
     294              :     {
     295            4 :         GroupId groupId = GroupIdFromNodeId(subjectDescriptor.subject);
     296            3 :         if ((requestPath.endpoint != kRootEndpointId) &&
     297            3 :             (mGroupDataProvider->HasEndpoint(subjectDescriptor.fabricIndex, groupId, requestPath.endpoint)) &&
     298            9 :             (requestPath.requestType == Access::RequestType::kCommandInvokeRequest) && (requestPrivilege == Privilege::kOperate) &&
     299            2 :             (subjectDescriptor.authMode == Access::AuthMode::kGroup))
     300              :         {
     301            2 :             return CHIP_NO_ERROR;
     302              :         }
     303              :     }
     304              : 
     305            2 :     return CHIP_ERROR_ACCESS_DENIED;
     306              : }
     307              : 
     308              : } // namespace Examples
     309              : } // namespace Access
     310              : } // namespace chip
        

Generated by: LCOV version 2.0-1