Skip to content

Commit

Permalink
Add zap gen for Matter access privilege definitions (#16327)
Browse files Browse the repository at this point in the history
New zap template iterates over access definitions for app server
clusters, for attributes/commands/events, to generate parallel arrays of
custom privileges for read/write attribute, invoke command, and read
event.

New privilege storage source files provide an API to access the generated
data, and an implementation using the generated data.

The data is generated and the storage is built per-app.

The library (DM, IM, app common) RequiredPrivilege module now uses the
privilege-storage API to access populated data on a per-app basis. Weak
implementations of the privilege storage API provide a default implementation
lacking generated data, so test artifacts can be built.

Fixes #14419
  • Loading branch information
mlepage-google authored and pull[bot] committed May 18, 2022
1 parent 26d0532 commit 2082148
Show file tree
Hide file tree
Showing 29 changed files with 2,777 additions and 117 deletions.
116 changes: 13 additions & 103 deletions src/app/RequiredPrivilege.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,120 +18,30 @@

#include "RequiredPrivilege.h"

#include <app-common/zap-generated/cluster-objects.h>

namespace {

using namespace chip;
using namespace chip::app;
using namespace chip::Access;

// Privilege override entries are stored in a table per operation (read attribute,
// write attribute, invoke command, read entry). Cluster cannot be invalid, but
// endpoint and field can be invalid, which means wildcard. For each cluster,
// more specific entries should be before less specific entries, so they take effect.
struct PrivilegeOverride
{
ClusterId mCluster;
EndpointId mEndpoint;
Privilege mPrivilege; // NOTE: here so packing is tighter
FieldId mField;

constexpr PrivilegeOverride(ClusterId cluster, EndpointId endpoint, FieldId field, Privilege privilege) :
mCluster(cluster), mEndpoint(endpoint), mPrivilege(privilege), mField(field)
{}

static_assert(sizeof(FieldId) >= sizeof(AttributeId), "FieldId must be able to hold AttributeId");
static_assert(sizeof(FieldId) >= sizeof(CommandId), "FieldId must be able to hold CommandId");
static_assert(sizeof(FieldId) >= sizeof(EventId), "FieldId must be able to hold EventId");
};

// WARNING: for each cluster, put more specific entries before less specific entries
constexpr PrivilegeOverride kPrivilegeOverrideForReadAttribute[] = {
PrivilegeOverride(Clusters::AccessControl::Id, kInvalidEndpointId, kInvalidFieldId, Privilege::kAdminister),
};

// WARNING: for each cluster, put more specific entries before less specific entries
constexpr PrivilegeOverride kPrivilegeOverrideForWriteAttribute[] = {
PrivilegeOverride(Clusters::AccessControl::Id, kInvalidEndpointId, kInvalidFieldId, Privilege::kAdminister),
};

// WARNING: for each cluster, put more specific entries before less specific entries
constexpr PrivilegeOverride kPrivilegeOverrideForInvokeCommand[] = {
PrivilegeOverride(Clusters::AccessControl::Id, kInvalidEndpointId, kInvalidFieldId, Privilege::kAdminister),
};

// WARNING: for each cluster, put more specific entries before less specific entries
constexpr PrivilegeOverride kPrivilegeOverrideForReadEvent[] = {
PrivilegeOverride(Clusters::AccessControl::Id, kInvalidEndpointId, kInvalidFieldId, Privilege::kAdminister),
};

enum class Operation
{
kReadAttribute = 0,
kWriteAttribute = 1,
kInvokeCommand = 2,
kReadEvent = 3
};

constexpr Privilege kDefaultPrivilege[] = {
Privilege::kView, // for read attribute
Privilege::kOperate, // for write attribute
Privilege::kOperate, // for invoke command
Privilege::kView // for read event
};

const PrivilegeOverride * const kPrivilegeOverride[] = { kPrivilegeOverrideForReadAttribute, kPrivilegeOverrideForWriteAttribute,
kPrivilegeOverrideForInvokeCommand, kPrivilegeOverrideForReadEvent };

constexpr size_t kNumPrivilegeOverride[] = { ArraySize(kPrivilegeOverrideForReadAttribute),
ArraySize(kPrivilegeOverrideForWriteAttribute),
ArraySize(kPrivilegeOverrideForInvokeCommand),
ArraySize(kPrivilegeOverrideForReadEvent) };

Privilege GetRequiredPrivilege(Operation operation, ClusterId cluster, EndpointId endpoint, FieldId field)
{
VerifyOrDie(cluster != kInvalidClusterId && endpoint != kInvalidEndpointId && field != kInvalidFieldId);

const auto * const pStart = kPrivilegeOverride[static_cast<int>(operation)];
const auto * const pEnd = pStart + kNumPrivilegeOverride[static_cast<int>(operation)];

for (const auto * p = pStart; p < pEnd; ++p)
{
if (p->mCluster == cluster && (p->mEndpoint == endpoint || p->mEndpoint == kInvalidEndpointId) &&
(p->mField == field || p->mField == kInvalidFieldId))
{
return p->mPrivilege;
}
}

return kDefaultPrivilege[static_cast<int>(operation)];
}

} // namespace

namespace chip {
namespace app {

Privilege RequiredPrivilege::ForReadAttribute(const ConcreteAttributePath & path)
constexpr Access::Privilege RequiredPrivilege::kPrivilegeMapper[];

} // namespace app
} // namespace chip

int __attribute__((weak)) MatterGetAccessPrivilegeForReadAttribute(chip::ClusterId cluster, chip::AttributeId attribute)
{
return GetRequiredPrivilege(Operation::kReadAttribute, path.mClusterId, path.mEndpointId, path.mAttributeId);
return kMatterAccessPrivilegeAdminister;
}

Privilege RequiredPrivilege::ForWriteAttribute(const ConcreteAttributePath & path)
int __attribute__((weak)) MatterGetAccessPrivilegeForWriteAttribute(chip::ClusterId cluster, chip::AttributeId attribute)
{
return GetRequiredPrivilege(Operation::kWriteAttribute, path.mClusterId, path.mEndpointId, path.mAttributeId);
return kMatterAccessPrivilegeAdminister;
}

Privilege RequiredPrivilege::ForInvokeCommand(const ConcreteCommandPath & path)
int __attribute__((weak)) MatterGetAccessPrivilegeForInvokeCommand(chip::ClusterId cluster, chip::CommandId command)
{
return GetRequiredPrivilege(Operation::kInvokeCommand, path.mClusterId, path.mEndpointId, path.mCommandId);
return kMatterAccessPrivilegeAdminister;
}

Privilege RequiredPrivilege::ForReadEvent(const ConcreteEventPath & path)
int __attribute__((weak)) MatterGetAccessPrivilegeForReadEvent(chip::ClusterId cluster, chip::EventId event)
{
return GetRequiredPrivilege(Operation::kReadEvent, path.mClusterId, path.mEndpointId, path.mEventId);
return kMatterAccessPrivilegeAdminister;
}

} // namespace app
} // namespace chip
44 changes: 38 additions & 6 deletions src/app/RequiredPrivilege.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include "ConcreteCommandPath.h"
#include "ConcreteEventPath.h"

#include <app/util/privilege-storage.h>

#include <access/Privilege.h>

#include <lib/core/CHIPCore.h>
Expand All @@ -30,17 +32,47 @@
namespace chip {
namespace app {

// This functionality is intended to come from Ember, but until Ember supports it,
// this class will provide a workable alternative.
class RequiredPrivilege
{
using Privilege = Access::Privilege;

static constexpr Privilege kPrivilegeMapper[] = { Privilege::kView, Privilege::kOperate, Privilege::kManage,
Privilege::kAdminister };

static_assert(ArraySize(kPrivilegeMapper) > kMatterAccessPrivilegeView &&
kPrivilegeMapper[kMatterAccessPrivilegeView] == Privilege::kView,
"Must map privilege correctly");
static_assert(ArraySize(kPrivilegeMapper) > kMatterAccessPrivilegeOperate &&
kPrivilegeMapper[kMatterAccessPrivilegeOperate] == Privilege::kOperate,
"Must map privilege correctly");
static_assert(ArraySize(kPrivilegeMapper) > kMatterAccessPrivilegeManage &&
kPrivilegeMapper[kMatterAccessPrivilegeManage] == Privilege::kManage,
"Must map privilege correctly");
static_assert(ArraySize(kPrivilegeMapper) > kMatterAccessPrivilegeAdminister &&
kPrivilegeMapper[kMatterAccessPrivilegeAdminister] == Privilege::kAdminister,
"Must map privilege correctly");
static_assert(ArraySize(kPrivilegeMapper) > kMatterAccessPrivilegeMaxValue, "Must map all privileges");

public:
static Privilege ForReadAttribute(const ConcreteAttributePath & path);
static Privilege ForWriteAttribute(const ConcreteAttributePath & path);
static Privilege ForInvokeCommand(const ConcreteCommandPath & path);
static Privilege ForReadEvent(const ConcreteEventPath & path);
static Privilege ForReadAttribute(const ConcreteAttributePath & path)
{
return kPrivilegeMapper[MatterGetAccessPrivilegeForReadAttribute(path.mClusterId, path.mAttributeId)];
}

static Privilege ForWriteAttribute(const ConcreteAttributePath & path)
{
return kPrivilegeMapper[MatterGetAccessPrivilegeForWriteAttribute(path.mClusterId, path.mAttributeId)];
}

static Privilege ForInvokeCommand(const ConcreteCommandPath & path)
{
return kPrivilegeMapper[MatterGetAccessPrivilegeForInvokeCommand(path.mClusterId, path.mCommandId)];
}

static Privilege ForReadEvent(const ConcreteEventPath & path)
{
return kPrivilegeMapper[MatterGetAccessPrivilegeForReadEvent(path.mClusterId, path.mEventId)];
}
};

} // namespace app
Expand Down
1 change: 1 addition & 0 deletions src/app/chip_data_model.gni
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ template("chip_data_model") {
"${_app_root}/util/ember-print.cpp",
"${_app_root}/util/error-mapping.cpp",
"${_app_root}/util/message.cpp",
"${_app_root}/util/privilege-storage.cpp",
"${_app_root}/util/util.cpp",
"${chip_root}/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp",
]
Expand Down
133 changes: 133 additions & 0 deletions src/app/util/privilege-storage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
*
* Copyright (c) 2022 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 "privilege-storage.h"

#include <zap-generated/access.h>

#include <lib/support/CodeUtils.h>

#include <cstdint>

using chip::AttributeId;
using chip::ClusterId;
using chip::CommandId;
using chip::EventId;

namespace {

#ifdef GENERATED_ACCESS_READ_ATTRIBUTE__CLUSTER
namespace GeneratedAccessReadAttribute {
constexpr ClusterId kCluster[] = GENERATED_ACCESS_READ_ATTRIBUTE__CLUSTER;
constexpr AttributeId kAttribute[] = GENERATED_ACCESS_READ_ATTRIBUTE__ATTRIBUTE;
constexpr uint8_t kPrivilege[] = GENERATED_ACCESS_READ_ATTRIBUTE__PRIVILEGE;
static_assert(ArraySize(kCluster) == ArraySize(kAttribute) && ArraySize(kAttribute) == ArraySize(kPrivilege),
"Generated parallel arrays must be same size");
} // namespace GeneratedAccessReadAttribute
#endif

#ifdef GENERATED_ACCESS_WRITE_ATTRIBUTE__CLUSTER
namespace GeneratedAccessWriteAttribute {
constexpr ClusterId kCluster[] = GENERATED_ACCESS_WRITE_ATTRIBUTE__CLUSTER;
constexpr AttributeId kAttribute[] = GENERATED_ACCESS_WRITE_ATTRIBUTE__ATTRIBUTE;
constexpr uint8_t kPrivilege[] = GENERATED_ACCESS_WRITE_ATTRIBUTE__PRIVILEGE;
static_assert(ArraySize(kCluster) == ArraySize(kAttribute) && ArraySize(kAttribute) == ArraySize(kPrivilege),
"Generated parallel arrays must be same size");
} // namespace GeneratedAccessWriteAttribute
#endif

#ifdef GENERATED_ACCESS_INVOKE_COMMAND__CLUSTER
namespace GeneratedAccessInvokeCommand {
constexpr ClusterId kCluster[] = GENERATED_ACCESS_INVOKE_COMMAND__CLUSTER;
constexpr CommandId kCommand[] = GENERATED_ACCESS_INVOKE_COMMAND__COMMAND;
constexpr uint8_t kPrivilege[] = GENERATED_ACCESS_INVOKE_COMMAND__PRIVILEGE;
static_assert(ArraySize(kCluster) == ArraySize(kCommand) && ArraySize(kCommand) == ArraySize(kPrivilege),
"Generated parallel arrays must be same size");
} // namespace GeneratedAccessInvokeCommand
#endif

#ifdef GENERATED_ACCESS_READ_EVENT__CLUSTER
namespace GeneratedAccessReadEvent {
constexpr ClusterId kCluster[] = GENERATED_ACCESS_READ_EVENT__CLUSTER;
constexpr EventId kEvent[] = GENERATED_ACCESS_READ_EVENT__EVENT;
constexpr uint8_t kPrivilege[] = GENERATED_ACCESS_READ_EVENT__PRIVILEGE;
static_assert(ArraySize(kCluster) == ArraySize(kEvent) && ArraySize(kEvent) == ArraySize(kPrivilege),
"Generated parallel arrays must be same size");
} // namespace GeneratedAccessReadEvent
#endif

} // anonymous namespace

int MatterGetAccessPrivilegeForReadAttribute(ClusterId cluster, AttributeId attribute)
{
#ifdef GENERATED_ACCESS_READ_ATTRIBUTE__CLUSTER
using namespace GeneratedAccessReadAttribute;
for (size_t i = 0; i < ArraySize(kCluster); ++i)
{
if (kCluster[i] == cluster && kAttribute[i] == attribute)
{
return kPrivilege[i];
}
}
#endif
return kMatterAccessPrivilegeView;
}

int MatterGetAccessPrivilegeForWriteAttribute(ClusterId cluster, AttributeId attribute)
{
#ifdef GENERATED_ACCESS_WRITE_ATTRIBUTE__CLUSTER
using namespace GeneratedAccessWriteAttribute;
for (size_t i = 0; i < ArraySize(kCluster); ++i)
{
if (kCluster[i] == cluster && kAttribute[i] == attribute)
{
return kPrivilege[i];
}
}
#endif
return kMatterAccessPrivilegeOperate;
}

int MatterGetAccessPrivilegeForInvokeCommand(ClusterId cluster, CommandId command)
{
#ifdef GENERATED_ACCESS_INVOKE_COMMAND__CLUSTER
using namespace GeneratedAccessInvokeCommand;
for (size_t i = 0; i < ArraySize(kCluster); ++i)
{
if (kCluster[i] == cluster && kCommand[i] == command)
{
return kPrivilege[i];
}
}
#endif
return kMatterAccessPrivilegeOperate;
}

int MatterGetAccessPrivilegeForReadEvent(ClusterId cluster, EventId event)
{
#ifdef GENERATED_ACCESS_READ_EVENT__CLUSTER
using namespace GeneratedAccessReadEvent;
for (size_t i = 0; i < ArraySize(kCluster); ++i)
{
if (kCluster[i] == cluster && kEvent[i] == event)
{
return kPrivilege[i];
}
}
#endif
return kMatterAccessPrivilegeView;
}
30 changes: 30 additions & 0 deletions src/app/util/privilege-storage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
*
* Copyright (c) 2022 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 <lib/core/DataModelTypes.h>

constexpr int kMatterAccessPrivilegeView = 0;
constexpr int kMatterAccessPrivilegeOperate = 1;
constexpr int kMatterAccessPrivilegeManage = 2;
constexpr int kMatterAccessPrivilegeAdminister = 3;
constexpr int kMatterAccessPrivilegeMaxValue = kMatterAccessPrivilegeAdminister;

int MatterGetAccessPrivilegeForReadAttribute(chip::ClusterId cluster, chip::AttributeId attribute);
int MatterGetAccessPrivilegeForWriteAttribute(chip::ClusterId cluster, chip::AttributeId attribute);
int MatterGetAccessPrivilegeForInvokeCommand(chip::ClusterId cluster, chip::CommandId command);
int MatterGetAccessPrivilegeForReadEvent(chip::ClusterId cluster, chip::EventId event);
5 changes: 5 additions & 0 deletions src/app/zap-templates/app-templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
"path": "templates/app/MatterIDL.zapt",
"name": "Human-readable Matter IDL",
"output": "Clusters.matter"
},
{
"path": "templates/app/access.zapt",
"name": "Matter access definitions",
"output": "access.h"
}
]
}
Loading

0 comments on commit 2082148

Please sign in to comment.