/* * * Copyright (c) 2024 Project CHIP Authors * All rights reserved. * * 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 "access/AccessControl.h" #include "access/AccessRestrictionProvider.h" #include "access/examples/ExampleAccessControlDelegate.h" #include #include #include #include namespace chip { namespace Access { class TestAccessRestrictionProvider : public AccessRestrictionProvider { CHIP_ERROR DoRequestFabricRestrictionReview(const FabricIndex fabricIndex, uint64_t token, const std::vector & arl) { return CHIP_NO_ERROR; } }; AccessControl accessControl; TestAccessRestrictionProvider accessRestrictionProvider; constexpr ClusterId kNetworkCommissioningCluster = app::Clusters::NetworkCommissioning::Id; constexpr ClusterId kDescriptorCluster = app::Clusters::Descriptor::Id; constexpr ClusterId kOnOffCluster = app::Clusters::OnOff::Id; // Clusters allowed to have restrictions constexpr ClusterId kWiFiNetworkManagementCluster = app::Clusters::WiFiNetworkManagement::Id; constexpr ClusterId kThreadBorderRouterMgmtCluster = app::Clusters::ThreadBorderRouterManagement::Id; constexpr ClusterId kThreadNetworkDirectoryCluster = app::Clusters::ThreadNetworkDirectory::Id; constexpr NodeId kOperationalNodeId1 = 0x1111111111111111; constexpr NodeId kOperationalNodeId2 = 0x2222222222222222; constexpr NodeId kOperationalNodeId3 = 0x3333333333333333; bool operator==(const AccessRestrictionProvider::Restriction & lhs, const AccessRestrictionProvider::Restriction & rhs) { return lhs.restrictionType == rhs.restrictionType && lhs.id == rhs.id; } bool operator==(const AccessRestrictionProvider::Entry & lhs, const AccessRestrictionProvider::Entry & rhs) { return lhs.fabricIndex == rhs.fabricIndex && lhs.endpointNumber == rhs.endpointNumber && lhs.clusterId == rhs.clusterId && lhs.restrictions == rhs.restrictions; } struct AclEntryData { FabricIndex fabricIndex = kUndefinedFabricIndex; Privilege privilege = Privilege::kView; AuthMode authMode = AuthMode::kNone; NodeId subject; }; constexpr AclEntryData aclEntryData[] = { { .fabricIndex = 1, .privilege = Privilege::kAdminister, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1, }, { .fabricIndex = 2, .privilege = Privilege::kAdminister, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2, }, }; constexpr size_t aclEntryDataCount = ArraySize(aclEntryData); struct CheckData { SubjectDescriptor subjectDescriptor; RequestPath requestPath; Privilege privilege; bool allow; }; constexpr CheckData checkDataNoRestrictions[] = { // Checks for implicit PASE { .subjectDescriptor = { .fabricIndex = 0, .authMode = AuthMode::kPase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 0, .authMode = AuthMode::kPase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kPase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kPase, .subject = kOperationalNodeId3 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, // Checks for entry 0 { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, // Checks for entry 1 { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = 1, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, }; CHIP_ERROR LoadEntry(AccessControl::Entry & entry, const AclEntryData & entryData) { ReturnErrorOnFailure(entry.SetAuthMode(entryData.authMode)); ReturnErrorOnFailure(entry.SetFabricIndex(entryData.fabricIndex)); ReturnErrorOnFailure(entry.SetPrivilege(entryData.privilege)); ReturnErrorOnFailure(entry.AddSubject(nullptr, entryData.subject)); return CHIP_NO_ERROR; } CHIP_ERROR LoadAccessControl(AccessControl & ac, const AclEntryData * entryData, size_t count) { AccessControl::Entry entry; for (size_t i = 0; i < count; ++i, ++entryData) { ReturnErrorOnFailure(ac.PrepareEntry(entry)); ReturnErrorOnFailure(LoadEntry(entry, *entryData)); ReturnErrorOnFailure(ac.CreateEntry(nullptr, entry)); } return CHIP_NO_ERROR; } void RunChecks(const CheckData * checkData, size_t count) { for (size_t i = 0; i < count; i++) { CHIP_ERROR expectedResult = checkData[i].allow ? CHIP_NO_ERROR : CHIP_ERROR_ACCESS_RESTRICTED_BY_ARL; EXPECT_EQ(accessControl.Check(checkData[i].subjectDescriptor, checkData[i].requestPath, checkData[i].privilege), expectedResult); } } class DeviceTypeResolver : public AccessControl::DeviceTypeResolver { public: bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) override { return false; } } testDeviceTypeResolver; class TestAccessRestriction : public ::testing::Test { public: // protected void SetUp() override { accessRestrictionProvider.SetCommissioningEntries(std::vector()); accessRestrictionProvider.SetEntries(0, std::vector()); accessRestrictionProvider.SetEntries(1, std::vector()); accessRestrictionProvider.SetEntries(2, std::vector()); } static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); AccessControl::Delegate * delegate = Examples::GetAccessControlDelegate(); SetAccessControl(accessControl); GetAccessControl().SetAccessRestrictionProvider(&accessRestrictionProvider); VerifyOrDie(GetAccessControl().Init(delegate, testDeviceTypeResolver) == CHIP_NO_ERROR); EXPECT_EQ(LoadAccessControl(accessControl, aclEntryData, aclEntryDataCount), CHIP_NO_ERROR); } static void TearDownTestSuite() { GetAccessControl().Finish(); ResetAccessControlToDefault(); } }; // basic data check without restrictions TEST_F(TestAccessRestriction, MetaTest) { for (const auto & checkData : checkDataNoRestrictions) { CHIP_ERROR expectedResult = checkData.allow ? CHIP_NO_ERROR : CHIP_ERROR_ACCESS_DENIED; EXPECT_EQ(accessControl.Check(checkData.subjectDescriptor, checkData.requestPath, checkData.privilege), expectedResult); } } // ensure failure when adding restrictons on endpoint 0 (any cluster, including those allowed on other endpoints) TEST_F(TestAccessRestriction, InvalidRestrictionsOnEndpointZeroTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.endpointNumber = 0; entry.fabricIndex = 1; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); entry.clusterId = kDescriptorCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); entries.clear(); entry.clusterId = kNetworkCommissioningCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); entries.clear(); entry.clusterId = kWiFiNetworkManagementCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); entries.clear(); entry.clusterId = kThreadBorderRouterMgmtCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); entries.clear(); entry.clusterId = kThreadNetworkDirectoryCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); // also test a cluster on endpoint 0 that isnt in the special allowed list entries.clear(); entry.clusterId = kOnOffCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); } // ensure no failure adding restrictions on endpoint 1 for allowed clusters only: // wifi network management, thread border router, thread network directory TEST_F(TestAccessRestriction, ValidRestrictionsOnEndpointOneTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.endpointNumber = 1; entry.fabricIndex = 1; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); entry.clusterId = kWiFiNetworkManagementCluster; EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); entries.clear(); entry.clusterId = kThreadBorderRouterMgmtCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); entries.clear(); entry.clusterId = kThreadNetworkDirectoryCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); // also test a cluster on endpoint 1 that isnt in the special allowed list entries.clear(); entry.clusterId = kOnOffCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); } TEST_F(TestAccessRestriction, InvalidRestrictionsOnEndpointOneTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.endpointNumber = 1; entry.fabricIndex = 1; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); entry.clusterId = kOnOffCluster; entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_ERROR_INVALID_ARGUMENT); } constexpr CheckData accessAttributeRestrictionTestData[] = { { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, }; TEST_F(TestAccessRestriction, AccessAttributeRestrictionTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.fabricIndex = 1; entry.endpointNumber = 1; entry.clusterId = kWiFiNetworkManagementCluster; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); // test wildcarded entity id entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(accessAttributeRestrictionTestData, ArraySize(accessAttributeRestrictionTestData)); // test specific entity id entries.clear(); entry.restrictions[0].id.SetValue(1); entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(accessAttributeRestrictionTestData, ArraySize(accessAttributeRestrictionTestData)); } constexpr CheckData writeAttributeRestrictionTestData[] = { { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, }; TEST_F(TestAccessRestriction, WriteAttributeRestrictionTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.fabricIndex = 1; entry.endpointNumber = 1; entry.clusterId = kWiFiNetworkManagementCluster; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeWriteForbidden }); // test wildcarded entity id entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(writeAttributeRestrictionTestData, ArraySize(writeAttributeRestrictionTestData)); // test specific entity id entries.clear(); entry.restrictions[0].id.SetValue(1); entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(writeAttributeRestrictionTestData, ArraySize(writeAttributeRestrictionTestData)); } constexpr CheckData commandAttributeRestrictionTestData[] = { { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, }; TEST_F(TestAccessRestriction, CommandRestrictionTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.fabricIndex = 1; entry.endpointNumber = 1; entry.clusterId = kWiFiNetworkManagementCluster; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kCommandForbidden }); // test wildcarded entity id entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(commandAttributeRestrictionTestData, ArraySize(commandAttributeRestrictionTestData)); // test specific entity id entries.clear(); entry.restrictions[0].id.SetValue(1); entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(commandAttributeRestrictionTestData, ArraySize(commandAttributeRestrictionTestData)); } constexpr CheckData eventAttributeRestrictionTestData[] = { { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, }; TEST_F(TestAccessRestriction, EventRestrictionTest) { std::vector entries; AccessRestrictionProvider::Entry entry; entry.fabricIndex = 1; entry.endpointNumber = 1; entry.clusterId = kWiFiNetworkManagementCluster; entry.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kEventForbidden }); // test wildcarded entity id entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(eventAttributeRestrictionTestData, ArraySize(eventAttributeRestrictionTestData)); // test specific entity id entries.clear(); entry.restrictions[0].id.SetValue(1); entries.push_back(entry); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(eventAttributeRestrictionTestData, ArraySize(eventAttributeRestrictionTestData)); } constexpr CheckData combinedRestrictionTestData[] = { { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 2 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 3 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 4 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 3 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 4 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kEventReadRequest, .entityId = 5 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kCommandInvokeRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 2, .authMode = AuthMode::kCase, .subject = kOperationalNodeId2 }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeWriteRequest, .entityId = 2 }, .privilege = Privilege::kAdminister, .allow = true }, }; TEST_F(TestAccessRestriction, CombinedRestrictionTest) { // a restriction for all access to attribute 1 and 2, attributes 3 and 4 are allowed std::vector entries1; AccessRestrictionProvider::Entry entry1; entry1.fabricIndex = 1; entry1.endpointNumber = 1; entry1.clusterId = kWiFiNetworkManagementCluster; entry1.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeWriteForbidden }); entry1.restrictions[0].id.SetValue(1); entry1.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); entry1.restrictions[1].id.SetValue(2); entries1.push_back(entry1); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries1), CHIP_NO_ERROR); // a restriction for fabric 2 that forbids command 1 and 2. Check that command 1 is blocked on invoke, but attribute 2 write is // allowed std::vector entries2; AccessRestrictionProvider::Entry entry2; entry2.fabricIndex = 2; entry2.endpointNumber = 1; entry2.clusterId = kWiFiNetworkManagementCluster; entry2.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kCommandForbidden }); entry2.restrictions[0].id.SetValue(1); entry2.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kCommandForbidden }); entry2.restrictions[1].id.SetValue(2); entries2.push_back(entry2); EXPECT_EQ(accessRestrictionProvider.SetEntries(2, entries2), CHIP_NO_ERROR); RunChecks(combinedRestrictionTestData, ArraySize(combinedRestrictionTestData)); } TEST_F(TestAccessRestriction, AttributeStorageSeperationTest) { std::vector commissioningEntries; AccessRestrictionProvider::Entry entry1; entry1.fabricIndex = 1; entry1.endpointNumber = 1; entry1.clusterId = kWiFiNetworkManagementCluster; entry1.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeWriteForbidden }); entry1.restrictions[0].id.SetValue(1); commissioningEntries.push_back(entry1); EXPECT_EQ(accessRestrictionProvider.SetCommissioningEntries(commissioningEntries), CHIP_NO_ERROR); std::vector entries; AccessRestrictionProvider::Entry entry2; entry2.fabricIndex = 2; entry2.endpointNumber = 2; entry2.clusterId = kThreadBorderRouterMgmtCluster; entry2.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kCommandForbidden }); entry2.restrictions[0].id.SetValue(2); entries.push_back(entry2); EXPECT_EQ(accessRestrictionProvider.SetEntries(2, entries), CHIP_NO_ERROR); auto commissioningEntriesFetched = accessRestrictionProvider.GetCommissioningEntries(); std::vector arlEntriesFetched; EXPECT_EQ(accessRestrictionProvider.GetEntries(2, arlEntriesFetched), CHIP_NO_ERROR); EXPECT_EQ(commissioningEntriesFetched[0], entry1); EXPECT_EQ(commissioningEntriesFetched.size(), static_cast(1)); EXPECT_EQ(arlEntriesFetched[0], entry2); EXPECT_EQ(arlEntriesFetched.size(), static_cast(1)); EXPECT_FALSE(commissioningEntriesFetched[0] == arlEntriesFetched[0]); } constexpr CheckData listSelectionDuringCommissioningData[] = { { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1, .isCommissioning = true }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1, .isCommissioning = true }, .requestPath = { .cluster = kThreadBorderRouterMgmtCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1, .isCommissioning = false }, .requestPath = { .cluster = kWiFiNetworkManagementCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = false }, { .subjectDescriptor = { .fabricIndex = 1, .authMode = AuthMode::kCase, .subject = kOperationalNodeId1, .isCommissioning = false }, .requestPath = { .cluster = kThreadBorderRouterMgmtCluster, .endpoint = 1, .requestType = RequestType::kAttributeReadRequest, .entityId = 1 }, .privilege = Privilege::kAdminister, .allow = true }, }; TEST_F(TestAccessRestriction, ListSelectiondDuringCommissioningTest) { // during commissioning, read is allowed on WifiNetworkManagement and disallowed on ThreadBorderRouterMgmt // after commissioning, read is disallowed on WifiNetworkManagement and allowed on ThreadBorderRouterMgmt std::vector entries; AccessRestrictionProvider::Entry entry1; entry1.fabricIndex = 1; entry1.endpointNumber = 1; entry1.clusterId = kThreadBorderRouterMgmtCluster; entry1.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); entry1.restrictions[0].id.SetValue(1); entries.push_back(entry1); EXPECT_EQ(accessRestrictionProvider.SetCommissioningEntries(entries), CHIP_NO_ERROR); entries.clear(); AccessRestrictionProvider::Entry entry2; entry2.fabricIndex = 1; entry2.endpointNumber = 1; entry2.clusterId = kWiFiNetworkManagementCluster; entry2.restrictions.push_back({ .restrictionType = AccessRestrictionProvider::Type::kAttributeAccessForbidden }); entry2.restrictions[0].id.SetValue(1); entries.push_back(entry2); EXPECT_EQ(accessRestrictionProvider.SetEntries(1, entries), CHIP_NO_ERROR); RunChecks(listSelectionDuringCommissioningData, ArraySize(listSelectionDuringCommissioningData)); } } // namespace Access } // namespace chip