blob: 32dac4e9d85e641b093e5ca2466e56202f2ddbb0 [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-cluster/ServerClusterInterface.h"
#include <pw_unit_test/framework.h>
#include <access/AccessControl.h>
#include <access/examples/ExampleAccessControlDelegate.h>
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
#include <access/AccessRestrictionProvider.h>
#endif
#include <app/clusters/access-control-server/access-control-cluster.h>
#include <app/data-model-provider/MetadataTypes.h>
#include <app/server-cluster/DefaultServerCluster.h>
#include <app/server-cluster/testing/AttributeTesting.h>
#include <app/server-cluster/testing/ClusterTester.h>
#include <clusters/AccessControl/Enums.h>
#include <clusters/AccessControl/Metadata.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/BitFlags.h>
#include <lib/support/ReadOnlyBuffer.h>
#include <platform/NetworkCommissioning.h>
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
#include <app-common/zap-generated/cluster-objects.h>
#endif
namespace {
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::DataModel;
// Simple DeviceTypeResolver for tests
class TestDeviceTypeResolver : public Access::AccessControl::DeviceTypeResolver
{
public:
bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) override { return false; }
};
static TestDeviceTypeResolver gTestDeviceTypeResolver;
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
// Mock AccessRestrictionProvider for testing ReviewFabricRestrictions command.
// This mock captures the parameters passed to RequestFabricRestrictionReview
// and allows tests to verify that the command handler correctly processes
// the request and calls the provider with the expected data.
class TestAccessRestrictionProvider : public Access::AccessRestrictionProvider
{
public:
// Override DoRequestFabricRestrictionReview to capture call parameters
// and allow tests to control the return value.
CHIP_ERROR DoRequestFabricRestrictionReview(const FabricIndex fabricIndex, uint64_t token,
const std::vector<Entry> & arl) override
{
// Store the parameters passed to this method so tests can verify them
mLastFabricIndex = fabricIndex;
mLastToken = token;
mLastArl = arl;
mRequestCount++;
// Return the configured error code (defaults to CHIP_NO_ERROR)
return mReturnError;
}
// Test verification fields - these are populated when DoRequestFabricRestrictionReview is called
FabricIndex mLastFabricIndex = kUndefinedFabricIndex;
uint64_t mLastToken = 0;
std::vector<Entry> mLastArl;
size_t mRequestCount = 0;
// Control field - set this to simulate different error conditions
CHIP_ERROR mReturnError = CHIP_NO_ERROR;
};
#endif // CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
struct TestAccessControlCluster : public ::testing::Test
{
static void SetUpTestSuite()
{
ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR);
// Initialize AccessControl for reading mandatory attributes
Access::AccessControl::Delegate * delegate = Access::Examples::GetAccessControlDelegate();
ASSERT_EQ(Access::GetAccessControl().Init(delegate, gTestDeviceTypeResolver), CHIP_NO_ERROR);
}
static void TearDownTestSuite()
{
Access::GetAccessControl().Finish();
Platform::MemoryShutdown();
}
};
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
// Test fixture for tests that require a mock AccessRestrictionProvider.
// This fixture sets up the mock provider, cluster, and tester in SetUp()
// and restores the previous provider in TearDown().
struct TestAccessControlClusterWithMockProvider : public TestAccessControlCluster
{
TestAccessControlClusterWithMockProvider() : mTester(mCluster) {}
void SetUp() override
{
// Save the previous provider to restore it after the test
mPreviousProvider = Access::GetAccessControl().GetAccessRestrictionProvider();
Access::GetAccessControl().SetAccessRestrictionProvider(&mMockProvider);
// Initialize the cluster and tester
ASSERT_EQ(mCluster.Startup(mTester.GetServerClusterContext()), CHIP_NO_ERROR);
}
void TearDown() override
{
mCluster.Shutdown(ClusterShutdownType::kClusterShutdown);
// Restore the previous provider to avoid use-after-free issues
Access::GetAccessControl().SetAccessRestrictionProvider(mPreviousProvider);
}
TestAccessRestrictionProvider mMockProvider;
AccessControlCluster mCluster;
Testing::ClusterTester mTester;
private:
Access::AccessRestrictionProvider * mPreviousProvider = nullptr;
};
#endif // CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
TEST_F(TestAccessControlCluster, CompileTest)
{
AccessControlCluster cluster;
ASSERT_EQ(cluster.GetClusterFlags({ kRootEndpointId, AccessControl::Id }), BitFlags<ClusterQualityFlags>());
}
TEST_F(TestAccessControlCluster, CommandsTest)
{
AccessControlCluster cluster;
ConcreteClusterPath accessControlPath = ConcreteClusterPath(kRootEndpointId, AccessControl::Id);
ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> acceptedCommandsBuilder;
ASSERT_EQ(cluster.AcceptedCommands(accessControlPath, acceptedCommandsBuilder), CHIP_NO_ERROR);
ReadOnlyBuffer<DataModel::AcceptedCommandEntry> acceptedCommands = acceptedCommandsBuilder.TakeBuffer();
ReadOnlyBufferBuilder<CommandId> generatedCommandsBuilder;
ASSERT_EQ(cluster.GeneratedCommands(accessControlPath, generatedCommandsBuilder), CHIP_NO_ERROR);
ReadOnlyBuffer<CommandId> generatedCommands = generatedCommandsBuilder.TakeBuffer();
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
// Check accepted commands
ASSERT_EQ(acceptedCommands.size(), AccessControl::Commands::kAcceptedCommandsCount);
ASSERT_EQ(acceptedCommands[0].commandId, AccessControl::Commands::ReviewFabricRestrictions::Id);
ASSERT_EQ(acceptedCommands[0].GetInvokePrivilege(),
AccessControl::Commands::ReviewFabricRestrictions::kMetadataEntry.GetInvokePrivilege());
// Check generated commands
ASSERT_EQ(generatedCommands.size(), AccessControl::Commands::kGeneratedCommandsCount);
ASSERT_EQ(generatedCommands[0], AccessControl::Commands::ReviewFabricRestrictionsResponse::Id);
#else
ASSERT_EQ(acceptedCommands.size(), (size_t) (0));
ASSERT_EQ(generatedCommands.size(), (size_t) (0));
#endif
}
TEST_F(TestAccessControlCluster, AttributesTest)
{
AccessControlCluster cluster;
ConcreteClusterPath accessControlPath = ConcreteClusterPath(kRootEndpointId, AccessControl::Id);
ReadOnlyBufferBuilder<DataModel::AttributeEntry> attributesBuilder;
ASSERT_EQ(cluster.Attributes(accessControlPath, attributesBuilder), CHIP_NO_ERROR);
ReadOnlyBufferBuilder<DataModel::AttributeEntry> expectedBuilder;
ASSERT_EQ(expectedBuilder.ReferenceExisting(DefaultServerCluster::GlobalAttributes()), CHIP_NO_ERROR);
ASSERT_EQ(expectedBuilder.AppendElements({
#if CHIP_CONFIG_ENABLE_ACL_EXTENSIONS
AccessControl::Attributes::Extension::kMetadataEntry,
#endif
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
AccessControl::Attributes::CommissioningARL::kMetadataEntry, AccessControl::Attributes::Arl::kMetadataEntry
#endif
}),
CHIP_NO_ERROR);
ASSERT_EQ(expectedBuilder.AppendElements(AccessControl::Attributes::kMandatoryMetadata), CHIP_NO_ERROR);
ASSERT_TRUE(Testing::EqualAttributeSets(attributesBuilder.TakeBuffer(), expectedBuilder.TakeBuffer()));
}
// Helper function to count elements in a decodable list
// Returns CHIP_ERROR to allow callers to use ASSERT_EQ/EXPECT_EQ for better error messages
template <typename DecodableListType>
CHIP_ERROR CountListElements(DecodableListType & list, size_t & count)
{
count = 0;
auto it = list.begin();
while (it.Next())
{
++count;
}
// Return the iterator status to the caller for assertion
return it.GetStatus();
}
// Test that all available attributes (mandatory and optional) can be read
TEST_F(TestAccessControlCluster, ReadAttributesTest)
{
AccessControlCluster cluster;
Testing::ClusterTester tester(cluster);
ASSERT_EQ(cluster.Startup(tester.GetServerClusterContext()), CHIP_NO_ERROR);
// Test mandatory attributes (always available)
// According to Matter spec, minimum values are:
// - SubjectsPerAccessControlEntry: >= 4
// - TargetsPerAccessControlEntry: >= 3
// - AccessControlEntriesPerFabric: >= 4
uint16_t subjectsPerEntry = 0;
auto status = tester.ReadAttribute(AccessControl::Attributes::SubjectsPerAccessControlEntry::Id, subjectsPerEntry);
ASSERT_TRUE(status.IsSuccess());
ASSERT_GE(subjectsPerEntry, 4u);
uint16_t targetsPerEntry = 0;
status = tester.ReadAttribute(AccessControl::Attributes::TargetsPerAccessControlEntry::Id, targetsPerEntry);
ASSERT_TRUE(status.IsSuccess());
ASSERT_GE(targetsPerEntry, 3u);
uint16_t entriesPerFabric = 0;
status = tester.ReadAttribute(AccessControl::Attributes::AccessControlEntriesPerFabric::Id, entriesPerFabric);
ASSERT_TRUE(status.IsSuccess());
ASSERT_GE(entriesPerFabric, 4u);
#if CHIP_CONFIG_ENABLE_ACL_EXTENSIONS
// Read Extension attribute (optional)
AccessControl::Attributes::Extension::TypeInfo::DecodableType extension;
status = tester.ReadAttribute(AccessControl::Attributes::Extension::Id, extension);
ASSERT_TRUE(status.IsSuccess());
size_t extensionCount = 0;
ASSERT_EQ(CountListElements(extension, extensionCount), CHIP_NO_ERROR);
ASSERT_EQ(extensionCount, 0u);
#endif // CHIP_CONFIG_ENABLE_ACL_EXTENSIONS
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
// Read CommissioningARL attribute (optional)
AccessControl::Attributes::CommissioningARL::TypeInfo::DecodableType commissioningArl;
status = tester.ReadAttribute(AccessControl::Attributes::CommissioningARL::Id, commissioningArl);
ASSERT_TRUE(status.IsSuccess());
size_t commissioningArlCount = 0;
ASSERT_EQ(CountListElements(commissioningArl, commissioningArlCount), CHIP_NO_ERROR);
ASSERT_EQ(commissioningArlCount, 0u);
// Read Arl attribute (optional)
AccessControl::Attributes::Arl::TypeInfo::DecodableType arl;
status = tester.ReadAttribute(AccessControl::Attributes::Arl::Id, arl);
ASSERT_TRUE(status.IsSuccess());
size_t arlCount = 0;
ASSERT_EQ(CountListElements(arl, arlCount), CHIP_NO_ERROR);
ASSERT_EQ(arlCount, 0u);
#endif // CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
cluster.Shutdown(ClusterShutdownType::kClusterShutdown);
}
#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_Success)
{
// Build a ReviewFabricRestrictions command request with ARL entries.
// The request contains a list of CommissioningAccessRestrictionEntryStruct entries,
// each specifying an endpoint, cluster, and list of restrictions.
AccessControl::Commands::ReviewFabricRestrictions::Type request;
// Create a single ARL entry for endpoint 1, OnOff cluster
AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type entry;
entry.endpoint = 1;
entry.cluster = OnOff::Id;
// Create a restriction that forbids access to attribute 0x0000
// This restriction type means attribute reads/writes are forbidden
AccessControl::Structs::AccessRestrictionStruct::Type restriction;
restriction.type = AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeAccessForbidden;
restriction.id.SetNonNull(0x0000); // Attribute ID
// Wrap the restriction in a DataModel::List as required by the struct
entry.restrictions = DataModel::List<const AccessControl::Structs::AccessRestrictionStruct::Type>(&restriction, 1);
// Wrap the entry in a DataModel::List for the request
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(&entry, 1);
// Invoke the ReviewFabricRestrictions command
// The command handler should process the request, call the AccessRestrictionProvider,
// and return a response with a token.
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.IsSuccess());
ASSERT_GT(result.response->token, 0u);
ASSERT_EQ(mMockProvider.mRequestCount, 1u);
ASSERT_EQ(mMockProvider.mLastArl.size(), 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].endpointNumber, 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].clusterId, OnOff::Id);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions.size(), 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions[0].restrictionType,
Access::AccessRestrictionProvider::Type::kAttributeAccessForbidden);
ASSERT_TRUE(mMockProvider.mLastArl[0].restrictions[0].id.HasValue());
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions[0].id.Value(), 0x0000u);
}
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_EmptyArl)
{
// Test the case where an empty ARL list is sent.
// According to the spec, an empty list means "review all restrictions" for the fabric.
// Create a request with an empty ARL list
// This signals a request to review all restrictions for the accessing fabric
AccessControl::Commands::ReviewFabricRestrictions::Type request;
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(nullptr, 0);
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.IsSuccess());
ASSERT_GT(result.response->token, 0u);
ASSERT_EQ(mMockProvider.mRequestCount, 1u);
ASSERT_EQ(mMockProvider.mLastArl.size(), 0u);
}
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_MultipleEntries)
{
// Test the command with multiple ARL entries in a single request.
// This verifies that the command handler correctly processes and forwards
// all entries to the AccessRestrictionProvider.
// Create an array of ARL entries for different endpoints and clusters
AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type entries[2];
// First entry: endpoint 1, OnOff cluster with attribute access restriction
entries[0].endpoint = 1;
entries[0].cluster = OnOff::Id;
AccessControl::Structs::AccessRestrictionStruct::Type restriction1;
restriction1.type = AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeAccessForbidden;
restriction1.id.SetNonNull(0x0000);
entries[0].restrictions = DataModel::List<const AccessControl::Structs::AccessRestrictionStruct::Type>(&restriction1, 1);
// Second entry: endpoint 2, Level Control cluster with command restriction
entries[1].endpoint = 2;
entries[1].cluster = LevelControl::Id;
AccessControl::Structs::AccessRestrictionStruct::Type restriction2;
restriction2.type = AccessControl::Enums::AccessRestrictionTypeEnum::kCommandForbidden;
restriction2.id.SetNonNull(0x0000);
entries[1].restrictions = DataModel::List<const AccessControl::Structs::AccessRestrictionStruct::Type>(&restriction2, 1);
// Build the request with both entries
AccessControl::Commands::ReviewFabricRestrictions::Type request;
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(entries, 2);
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.IsSuccess());
ASSERT_EQ(mMockProvider.mRequestCount, 1u);
ASSERT_EQ(mMockProvider.mLastArl.size(), 2u);
ASSERT_EQ(mMockProvider.mLastArl[0].endpointNumber, 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].clusterId, OnOff::Id);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions.size(), 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions[0].restrictionType,
Access::AccessRestrictionProvider::Type::kAttributeAccessForbidden);
ASSERT_EQ(mMockProvider.mLastArl[1].endpointNumber, 2u);
ASSERT_EQ(mMockProvider.mLastArl[1].clusterId, LevelControl::Id);
ASSERT_EQ(mMockProvider.mLastArl[1].restrictions.size(), 1u);
ASSERT_EQ(mMockProvider.mLastArl[1].restrictions[0].restrictionType,
Access::AccessRestrictionProvider::Type::kCommandForbidden);
}
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_WildcardRestriction)
{
// Test the command with a wildcard restriction (null ID).
// A null ID means the restriction applies to all attributes/commands/events
// of the specified type for the given cluster and endpoint.
// Create a request with a wildcard restriction
AccessControl::Commands::ReviewFabricRestrictions::Type request;
AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type entry;
entry.endpoint = 1;
entry.cluster = OnOff::Id;
// Create a restriction with null ID (wildcard)
// This means "forbid access to all attributes" on this cluster/endpoint
AccessControl::Structs::AccessRestrictionStruct::Type restriction;
restriction.type = AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeAccessForbidden;
restriction.id.SetNull(); // Wildcard - applies to all attributes
entry.restrictions = DataModel::List<const AccessControl::Structs::AccessRestrictionStruct::Type>(&restriction, 1);
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(&entry, 1);
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.IsSuccess());
ASSERT_EQ(mMockProvider.mLastArl.size(), 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions.size(), 1u);
ASSERT_FALSE(mMockProvider.mLastArl[0].restrictions[0].id.HasValue());
}
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_MultipleRestrictionsPerEntry)
{
// Test the command with an ARL entry containing multiple restrictions.
// A single entry can have multiple restrictions of different types.
// Create an entry with multiple restrictions
AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type entry;
entry.endpoint = 1;
entry.cluster = OnOff::Id;
// Create multiple restrictions for the same entry
AccessControl::Structs::AccessRestrictionStruct::Type restrictions[3];
// First restriction: forbid attribute access for attribute 0x0000
restrictions[0].type = AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeAccessForbidden;
restrictions[0].id.SetNonNull(0x0000);
// Second restriction: forbid attribute writes for attribute 0x0001
restrictions[1].type = AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeWriteForbidden;
restrictions[1].id.SetNonNull(0x0001);
// Third restriction: forbid command invocation for command 0x0000
restrictions[2].type = AccessControl::Enums::AccessRestrictionTypeEnum::kCommandForbidden;
restrictions[2].id.SetNonNull(0x0000);
// Wrap all restrictions in a list
entry.restrictions = DataModel::List<const AccessControl::Structs::AccessRestrictionStruct::Type>(restrictions, 3);
AccessControl::Commands::ReviewFabricRestrictions::Type request;
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(&entry, 1);
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.IsSuccess());
ASSERT_EQ(mMockProvider.mLastArl.size(), 1u);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions.size(), 3u);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions[0].restrictionType,
Access::AccessRestrictionProvider::Type::kAttributeAccessForbidden);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions[1].restrictionType,
Access::AccessRestrictionProvider::Type::kAttributeWriteForbidden);
ASSERT_EQ(mMockProvider.mLastArl[0].restrictions[2].restrictionType,
Access::AccessRestrictionProvider::Type::kCommandForbidden);
}
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_ProviderError)
{
// Test error handling when the AccessRestrictionProvider returns an error.
// The command handler should propagate the error from the provider.
// Configure the mock to return an error when RequestFabricRestrictionReview is called
// This simulates a failure scenario (e.g., provider unable to process the request)
mMockProvider.mReturnError = CHIP_ERROR_INTERNAL;
// Create a minimal valid request with empty ARL list
AccessControl::Commands::ReviewFabricRestrictions::Type request;
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(nullptr, 0);
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.status.has_value());
ASSERT_EQ(result.status->GetStatus(), CHIP_ERROR_INTERNAL);
ASSERT_EQ(mMockProvider.mRequestCount, 1u);
}
TEST_F(TestAccessControlClusterWithMockProvider, ReviewFabricRestrictionsCommand_AllRestrictionTypes)
{
// Test that all restriction types are correctly handled by the command.
// This verifies that the command handler correctly converts enum values
// from the Matter data model to the internal AccessRestrictionProvider types.
constexpr size_t kNumRestrictionTypes = 4;
AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type entries[kNumRestrictionTypes];
AccessControl::Structs::AccessRestrictionStruct::Type restrictions[kNumRestrictionTypes];
const struct
{
AccessControl::Enums::AccessRestrictionTypeEnum dmType;
Access::AccessRestrictionProvider::Type internalType;
} typeMappings[kNumRestrictionTypes] = {
{ AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeAccessForbidden,
Access::AccessRestrictionProvider::Type::kAttributeAccessForbidden },
{ AccessControl::Enums::AccessRestrictionTypeEnum::kAttributeWriteForbidden,
Access::AccessRestrictionProvider::Type::kAttributeWriteForbidden },
{ AccessControl::Enums::AccessRestrictionTypeEnum::kCommandForbidden,
Access::AccessRestrictionProvider::Type::kCommandForbidden },
{ AccessControl::Enums::AccessRestrictionTypeEnum::kEventForbidden,
Access::AccessRestrictionProvider::Type::kEventForbidden },
};
for (size_t i = 0; i < kNumRestrictionTypes; ++i)
{
entries[i].endpoint = 1;
entries[i].cluster = OnOff::Id;
restrictions[i].type = typeMappings[i].dmType;
restrictions[i].id.SetNonNull(static_cast<uint32_t>(i));
entries[i].restrictions = DataModel::List<const AccessControl::Structs::AccessRestrictionStruct::Type>(&restrictions[i], 1);
}
// Build request with all entries
AccessControl::Commands::ReviewFabricRestrictions::Type request;
request.arl = DataModel::List<const AccessControl::Structs::CommissioningAccessRestrictionEntryStruct::Type>(
entries, kNumRestrictionTypes);
auto result = mTester.Invoke(AccessControl::Commands::ReviewFabricRestrictions::Id, request);
ASSERT_TRUE(result.IsSuccess());
ASSERT_EQ(mMockProvider.mLastArl.size(), kNumRestrictionTypes);
for (size_t i = 0; i < kNumRestrictionTypes; ++i)
{
ASSERT_EQ(mMockProvider.mLastArl[i].restrictions[0].restrictionType, typeMappings[i].internalType);
}
}
#endif // CHIP_CONFIG_USE_ACCESS_RESTRICTIONS
} // namespace