Event validation simplification - pull logic into InteractionModelEngine.cpp only (#36251)
* Pull back the event path validity mixin, start with a datamodel implementation
* Fix compile logic after I moved things away
* Add one more check
* Restyle
* Fix typo
* Fix includes
* Restyle
* Update src/app/InteractionModelEngine.cpp
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
* Rename method
* Restyle
* More renames
* Restyle
* Fix some renames
* Restyle
* A few more renames
* Restyle
---------
Co-authored-by: Andrei Litvin <andreilitvin@google.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn
index 13a15f2..0a000a2 100644
--- a/src/app/BUILD.gn
+++ b/src/app/BUILD.gn
@@ -255,7 +255,6 @@
"reporting/Read-Ember.cpp",
"reporting/Read-Ember.h",
]
- public_deps += [ "${chip_root}/src/app/ember_coupling" ]
} else if (chip_use_data_model_interface == "check") {
sources += [
"reporting/Read-Checked.cpp",
@@ -265,10 +264,7 @@
"reporting/Read-Ember.cpp",
"reporting/Read-Ember.h",
]
- public_deps += [
- "${chip_root}/src/app/data-model-provider",
- "${chip_root}/src/app/ember_coupling",
- ]
+ public_deps += [ "${chip_root}/src/app/data-model-provider" ]
} else { # enabled
sources += [
"reporting/Read-DataModel.cpp",
diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp
index 40ec6e7..49baa70 100644
--- a/src/app/InteractionModelEngine.cpp
+++ b/src/app/InteractionModelEngine.cpp
@@ -27,10 +27,13 @@
#include <cinttypes>
+#include <access/AccessRestrictionProvider.h>
+#include <access/Privilege.h>
#include <access/RequestPath.h>
#include <access/SubjectDescriptor.h>
#include <app/AppConfig.h>
#include <app/CommandHandlerInterfaceRegistry.h>
+#include <app/EventPathParams.h>
#include <app/RequiredPrivilege.h>
#include <app/data-model-provider/ActionReturnStatus.h>
#include <app/data-model-provider/MetadataTypes.h>
@@ -39,6 +42,7 @@
#include <app/util/af-types.h>
#include <app/util/ember-compatibility-functions.h>
#include <app/util/endpoint-config-api.h>
+#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/Global.h>
#include <lib/core/TLVUtilities.h>
@@ -53,12 +57,148 @@
#include <app/codegen-data-model-provider/Instance.h>
#endif
-#if !CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
-#include <app/ember_coupling/EventPathValidity.mixin.h> // nogncheck
-#endif
-
namespace chip {
namespace app {
+namespace {
+
+/**
+ * Helper to handle wildcard events in the event path.
+ *
+ * Validates that ACL access is permitted to:
+ * - Cluster::View in case the path is a wildcard for the event id
+ * - Event read if the path is a concrete event path
+ */
+bool MayHaveAccessibleEventPathForEndpointAndCluster(const ConcreteClusterPath & path, const EventPathParams & aEventPath,
+ const Access::SubjectDescriptor & aSubjectDescriptor)
+{
+ Access::RequestPath requestPath{ .cluster = path.mClusterId,
+ .endpoint = path.mEndpointId,
+ .requestType = Access::RequestType::kEventReadRequest };
+
+ Access::Privilege requiredPrivilege = Access::Privilege::kView;
+
+ if (!aEventPath.HasWildcardEventId())
+ {
+ requestPath.entityId = aEventPath.mEventId;
+ requiredPrivilege =
+ RequiredPrivilege::ForReadEvent(ConcreteEventPath(path.mEndpointId, path.mClusterId, aEventPath.mEventId));
+ }
+
+ return (Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, requiredPrivilege) == CHIP_NO_ERROR);
+}
+
+#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
+
+bool MayHaveAccessibleEventPathForEndpoint(DataModel::Provider * aProvider, EndpointId aEndpoint,
+ const EventPathParams & aEventPath, const Access::SubjectDescriptor & aSubjectDescriptor)
+{
+ if (!aEventPath.HasWildcardClusterId())
+ {
+ return MayHaveAccessibleEventPathForEndpointAndCluster(ConcreteClusterPath(aEndpoint, aEventPath.mClusterId), aEventPath,
+ aSubjectDescriptor);
+ }
+
+ DataModel::ClusterEntry clusterEntry = aProvider->FirstCluster(aEventPath.mEndpointId);
+ while (clusterEntry.IsValid())
+ {
+ if (MayHaveAccessibleEventPathForEndpointAndCluster(clusterEntry.path, aEventPath, aSubjectDescriptor))
+ {
+ return true;
+ }
+ clusterEntry = aProvider->NextCluster(clusterEntry.path);
+ }
+
+ return false;
+}
+
+bool MayHaveAccessibleEventPath(DataModel::Provider * aProvider, const EventPathParams & aEventPath,
+ const Access::SubjectDescriptor & subjectDescriptor)
+{
+ VerifyOrReturnValue(aProvider != nullptr, false);
+
+ if (!aEventPath.HasWildcardEndpointId())
+ {
+ return MayHaveAccessibleEventPathForEndpoint(aProvider, aEventPath.mEndpointId, aEventPath, subjectDescriptor);
+ }
+
+ for (EndpointId endpointId = aProvider->FirstEndpoint(); endpointId != kInvalidEndpointId;
+ endpointId = aProvider->NextEndpoint(endpointId))
+ {
+ if (MayHaveAccessibleEventPathForEndpoint(aProvider, endpointId, aEventPath, subjectDescriptor))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+#else
+
+/**
+ * Helper to handle wildcard clusters in the event path.
+ */
+bool MayHaveAccessibleEventPathForEndpoint(EndpointId aEndpoint, const EventPathParams & aEventPath,
+ const Access::SubjectDescriptor & aSubjectDescriptor)
+{
+ if (aEventPath.HasWildcardClusterId())
+ {
+ auto * endpointType = emberAfFindEndpointType(aEndpoint);
+ if (endpointType == nullptr)
+ {
+ // Not going to have any valid paths in here.
+ return false;
+ }
+
+ for (decltype(endpointType->clusterCount) idx = 0; idx < endpointType->clusterCount; ++idx)
+ {
+ bool mayHaveAccessiblePath = MayHaveAccessibleEventPathForEndpointAndCluster(
+ ConcreteClusterPath(aEndpoint, endpointType->cluster[idx].clusterId), aEventPath, aSubjectDescriptor);
+ if (mayHaveAccessiblePath)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ auto * cluster = emberAfFindServerCluster(aEndpoint, aEventPath.mClusterId);
+ if (cluster == nullptr)
+ {
+ // Nothing valid here.
+ return false;
+ }
+ return MayHaveAccessibleEventPathForEndpointAndCluster(ConcreteClusterPath(aEndpoint, cluster->clusterId), aEventPath,
+ aSubjectDescriptor);
+}
+
+bool MayHaveAccessibleEventPath(const EventPathParams & aEventPath, const Access::SubjectDescriptor & aSubjectDescriptor)
+{
+ if (!aEventPath.HasWildcardEndpointId())
+ {
+ // No need to check whether the endpoint is enabled, because
+ // emberAfFindEndpointType returns null for disabled endpoints.
+ return MayHaveAccessibleEventPathForEndpoint(aEventPath.mEndpointId, aEventPath, aSubjectDescriptor);
+ }
+
+ for (uint16_t endpointIndex = 0; endpointIndex < emberAfEndpointCount(); ++endpointIndex)
+ {
+ if (!emberAfEndpointIndexIsEnabled(endpointIndex))
+ {
+ continue;
+ }
+ if (MayHaveAccessibleEventPathForEndpoint(emberAfEndpointFromIndex(endpointIndex), aEventPath, aSubjectDescriptor))
+ {
+ return true;
+ }
+ }
+
+ // none of the paths matched
+ return false;
+}
+#endif
+
+} // namespace
class AutoReleaseSubscriptionInfoIterator
{
@@ -583,30 +723,13 @@
continue;
}
-#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
- aHasValidEventPath = mDataModelProvider->EventPathIncludesAccessibleConcretePath(eventPath, aSubjectDescriptor);
-#else
// The definition of "valid path" is "path exists and ACL allows
// access". We need to do some expansion of wildcards to handle that.
- if (eventPath.HasWildcardEndpointId())
- {
- for (uint16_t endpointIndex = 0; !aHasValidEventPath && endpointIndex < emberAfEndpointCount(); ++endpointIndex)
- {
- if (!emberAfEndpointIndexIsEnabled(endpointIndex))
- {
- continue;
- }
- aHasValidEventPath =
- HasValidEventPathForEndpoint(emberAfEndpointFromIndex(endpointIndex), eventPath, aSubjectDescriptor);
- }
- }
- else
- {
- // No need to check whether the endpoint is enabled, because
- // emberAfFindEndpointType returns null for disabled endpoints.
- aHasValidEventPath = HasValidEventPathForEndpoint(eventPath.mEndpointId, eventPath, aSubjectDescriptor);
- }
-#endif // CHIP_CONFIG_USE_EMBER_DATA_MODEL
+#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
+ aHasValidEventPath = MayHaveAccessibleEventPath(mDataModelProvider, eventPath, aSubjectDescriptor);
+#else
+ aHasValidEventPath = MayHaveAccessibleEventPath(eventPath, aSubjectDescriptor);
+#endif
}
if (err == CHIP_ERROR_END_OF_TLV)
@@ -676,7 +799,7 @@
size_t requestedEventPathCount = 0;
AttributePathIBs::Parser attributePathListParser;
bool hasValidAttributePath = false;
- bool hasValidEventPath = false;
+ bool mayHaveValidEventPath = false;
CHIP_ERROR err = subscribeRequestParser.GetAttributeRequests(&attributePathListParser);
if (err == CHIP_NO_ERROR)
@@ -699,7 +822,7 @@
if (err == CHIP_NO_ERROR)
{
auto subjectDescriptor = apExchangeContext->GetSessionHandle()->AsSecureSession()->GetSubjectDescriptor();
- err = ParseEventPaths(subjectDescriptor, eventPathListParser, hasValidEventPath, requestedEventPathCount);
+ err = ParseEventPaths(subjectDescriptor, eventPathListParser, mayHaveValidEventPath, requestedEventPathCount);
if (err != CHIP_NO_ERROR)
{
return Status::InvalidAction;
@@ -719,7 +842,7 @@
return Status::InvalidAction;
}
- if (!hasValidAttributePath && !hasValidEventPath)
+ if (!hasValidAttributePath && !mayHaveValidEventPath)
{
ChipLogError(InteractionModel,
"Subscription from [%u:" ChipLogFormatX64 "] has no access at all. Rejecting request.",
diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp b/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp
index dd320e5..eab221a 100644
--- a/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp
+++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider.cpp
@@ -33,9 +33,6 @@
#include <lib/core/DataModelTypes.h>
#include <lib/support/CodeUtils.h>
-// separated out for code-reuse
-#include <app/ember_coupling/EventPathValidity.mixin.h>
-
#include <optional>
#include <variant>
@@ -772,30 +769,5 @@
return DeviceTypeEntryFromEmber(deviceTypes[idx]);
}
-bool CodegenDataModelProvider::EventPathIncludesAccessibleConcretePath(const EventPathParams & path,
- const Access::SubjectDescriptor & descriptor)
-{
-
- if (!path.HasWildcardEndpointId())
- {
- // No need to check whether the endpoint is enabled, because
- // emberAfFindEndpointType returns null for disabled endpoints.
- return HasValidEventPathForEndpoint(path.mEndpointId, path, descriptor);
- }
-
- for (uint16_t endpointIndex = 0; endpointIndex < emberAfEndpointCount(); ++endpointIndex)
- {
- if (!emberAfEndpointIndexIsEnabled(endpointIndex))
- {
- continue;
- }
- if (HasValidEventPathForEndpoint(emberAfEndpointFromIndex(endpointIndex), path, descriptor))
- {
- return true;
- }
- }
- return false;
-}
-
} // namespace app
} // namespace chip
diff --git a/src/app/codegen-data-model-provider/CodegenDataModelProvider.h b/src/app/codegen-data-model-provider/CodegenDataModelProvider.h
index 5c87e26..7549ef1 100644
--- a/src/app/codegen-data-model-provider/CodegenDataModelProvider.h
+++ b/src/app/codegen-data-model-provider/CodegenDataModelProvider.h
@@ -141,8 +141,6 @@
return CHIP_NO_ERROR;
}
- bool EventPathIncludesAccessibleConcretePath(const EventPathParams & path,
- const Access::SubjectDescriptor & descriptor) override;
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder) override;
DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
diff --git a/src/app/codegen-data-model-provider/model.gni b/src/app/codegen-data-model-provider/model.gni
index 4205c6f..f134d51 100644
--- a/src/app/codegen-data-model-provider/model.gni
+++ b/src/app/codegen-data-model-provider/model.gni
@@ -39,6 +39,5 @@
codegen_data_model_PUBLIC_DEPS = [
"${chip_root}/src/app/common:attribute-type",
"${chip_root}/src/app/data-model-provider",
- "${chip_root}/src/app/ember_coupling",
"${chip_root}/src/app/codegen-data-model-provider:instance-header",
]
diff --git a/src/app/data-model-provider/Provider.h b/src/app/data-model-provider/Provider.h
index 2bafd1a..16dd732 100644
--- a/src/app/data-model-provider/Provider.h
+++ b/src/app/data-model-provider/Provider.h
@@ -59,16 +59,6 @@
// event emitting, path marking and other operations
virtual InteractionModelContext CurrentContext() const { return mContext; }
- /// Validates that the given event path is supported, where path may contain wildcards.
- ///
- /// If any wild cards exist on the given path, the implementation is expected to validate
- /// that an accessible event path exists on some wildcard expansion.
- ///
- /// At the very minimum this will validate that a valid endpoint/cluster can be expanded
- /// from the input path and that the given descriptor has access to it.
- virtual bool EventPathIncludesAccessibleConcretePath(const EventPathParams & path,
- const Access::SubjectDescriptor & descriptor) = 0;
-
/// TEMPORARY/TRANSITIONAL requirement for transitioning from ember-specific code
/// ReadAttribute is REQUIRED to perform:
/// - ACL validation (see notes on OperationFlags::kInternal)
diff --git a/src/app/ember_coupling/BUILD.gn b/src/app/ember_coupling/BUILD.gn
deleted file mode 100644
index 422fc8b..0000000
--- a/src/app/ember_coupling/BUILD.gn
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2024 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.
-
-import("//build_overrides/chip.gni")
-
-# The sources in this directory exist to limit the amount of code duplication
-# and assume strong ember and access coupling
-#
-# They are NOT stand-alone compilable and are rather for `#include` support.
-source_set("ember_coupling") {
- sources = [ "EventPathValidity.mixin.h" ]
-}
diff --git a/src/app/ember_coupling/EventPathValidity.mixin.h b/src/app/ember_coupling/EventPathValidity.mixin.h
deleted file mode 100644
index 54b8bcf..0000000
--- a/src/app/ember_coupling/EventPathValidity.mixin.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *
- * Copyright (c) 2020-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.
- */
-
-// This is intended as a TOP LEVEL INCLUDE inside code that uses it
-
-namespace chip {
-namespace app {
-namespace {
-
-static bool CanAccessEvent(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteClusterPath & aPath,
- Access::Privilege aNeededPrivilege)
-{
- Access::RequestPath requestPath{ .cluster = aPath.mClusterId,
- .endpoint = aPath.mEndpointId,
- .requestType = Access::RequestType::kEventReadRequest };
- // leave requestPath.entityId optional value unset to indicate wildcard
- CHIP_ERROR err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, aNeededPrivilege);
- return (err == CHIP_NO_ERROR);
-}
-
-static bool CanAccessEvent(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteEventPath & aPath)
-{
- Access::RequestPath requestPath{ .cluster = aPath.mClusterId,
- .endpoint = aPath.mEndpointId,
- .requestType = Access::RequestType::kEventReadRequest,
- .entityId = aPath.mEventId };
- CHIP_ERROR err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, RequiredPrivilege::ForReadEvent(aPath));
- return (err == CHIP_NO_ERROR);
-}
-
-/**
- * Helper to handle wildcard events in the event path.
- */
-static bool HasValidEventPathForEndpointAndCluster(EndpointId aEndpoint, const EmberAfCluster * aCluster,
- const EventPathParams & aEventPath,
- const Access::SubjectDescriptor & aSubjectDescriptor)
-{
- if (aEventPath.HasWildcardEventId())
- {
- // We have no way to expand wildcards. Just assume that we would need
- // View permissions for whatever events are involved.
- ConcreteClusterPath clusterPath(aEndpoint, aCluster->clusterId);
- return CanAccessEvent(aSubjectDescriptor, clusterPath, Access::Privilege::kView);
- }
-
- ConcreteEventPath path(aEndpoint, aCluster->clusterId, aEventPath.mEventId);
- return CanAccessEvent(aSubjectDescriptor, path);
-}
-
-/**
- * Helper to handle wildcard clusters in the event path.
- */
-static bool HasValidEventPathForEndpoint(EndpointId aEndpoint, const EventPathParams & aEventPath,
- const Access::SubjectDescriptor & aSubjectDescriptor)
-{
- if (aEventPath.HasWildcardClusterId())
- {
- auto * endpointType = emberAfFindEndpointType(aEndpoint);
- if (endpointType == nullptr)
- {
- // Not going to have any valid paths in here.
- return false;
- }
-
- for (decltype(endpointType->clusterCount) idx = 0; idx < endpointType->clusterCount; ++idx)
- {
- bool hasValidPath =
- HasValidEventPathForEndpointAndCluster(aEndpoint, &endpointType->cluster[idx], aEventPath, aSubjectDescriptor);
- if (hasValidPath)
- {
- return true;
- }
- }
-
- return false;
- }
-
- auto * cluster = emberAfFindServerCluster(aEndpoint, aEventPath.mClusterId);
- if (cluster == nullptr)
- {
- // Nothing valid here.
- return false;
- }
- return HasValidEventPathForEndpointAndCluster(aEndpoint, cluster, aEventPath, aSubjectDescriptor);
-}
-
-} // namespace
-} // namespace app
-} // namespace chip
diff --git a/src/app/tests/test-interaction-model-api.h b/src/app/tests/test-interaction-model-api.h
index ae5cc63..df410c6 100644
--- a/src/app/tests/test-interaction-model-api.h
+++ b/src/app/tests/test-interaction-model-api.h
@@ -117,11 +117,6 @@
CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; }
- bool EventPathIncludesAccessibleConcretePath(const EventPathParams & path,
- const Access::SubjectDescriptor & descriptor) override
- {
- return true;
- }
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder) override;
DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
diff --git a/src/controller/tests/data_model/DataModelFixtures.h b/src/controller/tests/data_model/DataModelFixtures.h
index bdc251e..e9b32ae 100644
--- a/src/controller/tests/data_model/DataModelFixtures.h
+++ b/src/controller/tests/data_model/DataModelFixtures.h
@@ -115,11 +115,6 @@
CHIP_ERROR Shutdown() override { return CHIP_NO_ERROR; }
- bool EventPathIncludesAccessibleConcretePath(const EventPathParams & path,
- const Access::SubjectDescriptor & descriptor) override
- {
- return true;
- }
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder) override;
DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,