Skip to content

Commit

Permalink
Add access control implementation for reading ACL
Browse files Browse the repository at this point in the history
Add an actual implementation, incomplete of course. This supports
reading the entire ACL attribute (all indexes, all fabrics). It
doesn't support reading the extension attribute (which doesn't have any
dependent code yet), or parts of ACL entries, etc. And it doesn't
support writing (that's next priority).

Access Control isn't yet set up in common code, hooked up in the IM, or
persisted to flash. Therefore, it's set up here for now, and a few ACL
entries are created which will be removed before more set up and hook up
and persistence is done. (Just need some entries during development.)

Tested by running chip-tool against chip-all-clusters-app to read those
temporary ACL entries and inspect they are correctly read.

This work is toward issue #10254.
  • Loading branch information
mlepage-google committed Nov 30, 2021
1 parent c68c025 commit a51525c
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ static_library("app") {

public_deps = [
":app_buildconfig",
"${chip_root}/src/access",
"${chip_root}/src/app/util:device_callbacks_manager",
"${chip_root}/src/lib/support",
"${chip_root}/src/messaging",
Expand Down
240 changes: 239 additions & 1 deletion src/app/clusters/access-control-server/access-control-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,242 @@
* limitations under the License.
*/

void MatterAccessControlPluginServerInitCallback() {}
#include <access/AccessControl.h>
#include <access/examples/ExampleAccessControlDelegate.h>

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

#include <app/AttributeAccessInterface.h>
#include <app/CommandHandler.h>
#include <app/ConcreteCommandPath.h>
#include <app/data-model/Encode.h>
#include <app/util/af.h>
#include <app/util/attribute-storage.h>

namespace chip {
namespace app {
namespace Clusters {
namespace AccessControl {
namespace Structs {
namespace AccessControlEntry {

struct AccessControlEntryCodec
{
Access::AccessControl::Entry entry;

// TODO: use enum classes once they settle down
static int Encode(Access::Privilege privilege)
{
switch (privilege)
{
case Access::Privilege::kView:
return 1;
case Access::Privilege::kProxyView:
return 2;
case Access::Privilege::kOperate:
return 3;
case Access::Privilege::kManage:
return 4;
case Access::Privilege::kAdminister:
return 5;
default:
return 0;
}
}

// TODO: use enum classes once they settle down
static int Encode(Access::AuthMode authMode)
{
switch (authMode)
{
case Access::AuthMode::kPase:
return 1;
case Access::AuthMode::kCase:
return 2;
case Access::AuthMode::kGroup:
return 3;
default:
return 0;
}
}

CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag) const
{
TLV::TLVType outer;
ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outer));

FabricIndex fabricIndex;
ReturnErrorOnFailure(entry.GetFabricIndex(fabricIndex));
ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kFabricIndex)), fabricIndex));

Access::Privilege privilege;
ReturnErrorOnFailure(entry.GetPrivilege(privilege));
ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kPrivilege)), Encode(privilege)));

Access::AuthMode authMode;
ReturnErrorOnFailure(entry.GetAuthMode(authMode));
ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kAuthMode)), Encode(authMode)));

size_t subjectCount = 0;
ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
if (subjectCount > 0)
{
TLV::TLVType inner;
ReturnErrorOnFailure(
writer.StartContainer(TLV::ContextTag(to_underlying(Fields::kSubjects)), TLV::kTLVType_Array, inner));
for (size_t i = 0; i < subjectCount; ++i)
{
NodeId subject;
ReturnErrorOnFailure(entry.GetSubject(i, subject));
ReturnErrorOnFailure(DataModel::Encode(writer, TLV::AnonymousTag, subject));
}
ReturnErrorOnFailure(writer.EndContainer(inner));
}

size_t targetCount = 0;
ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
if (targetCount > 0)
{
TLV::TLVType inner;
ReturnErrorOnFailure(
writer.StartContainer(TLV::ContextTag(to_underlying(Fields::kTargets)), TLV::kTLVType_Array, inner));
for (size_t i = 0; i < targetCount; ++i)
{
TLV::TLVType innermost;
ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, innermost));
Access::AccessControl::Entry::Target target;
ReturnErrorOnFailure(entry.GetTarget(i, target));
if (target.flags & Access::AccessControl::Entry::Target::kCluster)
{
ReturnErrorOnFailure(
DataModel::Encode(writer, TLV::ContextTag(to_underlying(Target::Fields::kCluster)), target.cluster));
}
if (target.flags & Access::AccessControl::Entry::Target::kEndpoint)
{
ReturnErrorOnFailure(
DataModel::Encode(writer, TLV::ContextTag(to_underlying(Target::Fields::kEndpoint)), target.endpoint));
}
if (target.flags & Access::AccessControl::Entry::Target::kDeviceType)
{
ReturnErrorOnFailure(
DataModel::Encode(writer, TLV::ContextTag(to_underlying(Target::Fields::kDeviceType)), target.deviceType));
}
ReturnErrorOnFailure(writer.EndContainer(innermost));
}
ReturnErrorOnFailure(writer.EndContainer(inner));
}

ReturnErrorOnFailure(writer.EndContainer(outer));
return CHIP_NO_ERROR;
}
};

} // namespace AccessControlEntry
} // namespace Structs
} // namespace AccessControl
} // namespace Clusters
} // namespace app
} // namespace chip

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

class AccessControlAttribute : public AttributeAccessInterface
{
using AccessControlEntryCodec = chip::app::Clusters::AccessControl::Structs::AccessControlEntry::AccessControlEntryCodec;

public:
AccessControlAttribute() : AttributeAccessInterface(Optional<EndpointId>(0), Clusters::AccessControl::Id) {}

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;

private:
CHIP_ERROR ReadAcl(AttributeValueEncoder & aEncoder);
CHIP_ERROR ReadExtension(AttributeValueEncoder & aEncoder);
};

CHIP_ERROR AccessControlAttribute::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
// TODO: probably need to handle list indexes, fabric filtering, etc.
// but don't worry about all that right now

switch (aPath.mAttributeId)
{
case Clusters::AccessControl::Attributes::Acl::Id:
return ReadAcl(aEncoder);
case Clusters::AccessControl::Attributes::Extension::Id:
return ReadExtension(aEncoder);
}

return CHIP_NO_ERROR;
}

CHIP_ERROR AccessControlAttribute::ReadAcl(AttributeValueEncoder & aEncoder)
{
AccessControlEntryCodec codec;
AccessControl::EntryIterator iterator;

GetAccessControl().Entries(iterator);

CHIP_ERROR err = aEncoder.EncodeList([&](const TagBoundEncoder & encoder) -> CHIP_ERROR {
while (iterator.Next(codec.entry) == CHIP_NO_ERROR)
{
encoder.Encode(codec);
}
return CHIP_NO_ERROR;
});

return err;
}

CHIP_ERROR AccessControlAttribute::ReadExtension(AttributeValueEncoder & aEncoder)
{
// TODO: implement extension support
return CHIP_ERROR_NOT_IMPLEMENTED;
}

AccessControlAttribute gAttribute;

AccessControl gAccessControl(Examples::GetAccessControlDelegate());

void MatterAccessControlPluginServerInitCallback()
{
registerAttributeAccessOverride(&gAttribute);

// TODO: move access control setup to a better place but it's fine here for now
gAccessControl.Init();
SetAccessControl(gAccessControl);

// TODO: create a few entries during development, these will be removed,
// it's OK because access control isn't persisted and isn't hooked up in IM
AccessControl::Entry entry;

GetAccessControl().PrepareEntry(entry);
entry.SetFabricIndex(1);
entry.SetPrivilege(Privilege::kAdminister);
entry.SetAuthMode(AuthMode::kCase);
GetAccessControl().CreateEntry(nullptr, entry);

GetAccessControl().PrepareEntry(entry);
entry.SetFabricIndex(1);
entry.SetPrivilege(Privilege::kManage);
entry.SetAuthMode(AuthMode::kPase);
entry.AddSubject(nullptr, 0x0000000011112222);
entry.AddSubject(nullptr, 0x0000000033334444);
entry.AddTarget(nullptr, { AccessControl::Entry::Target::kCluster, 10, 0, 0 });
entry.AddTarget(nullptr, { AccessControl::Entry::Target::kEndpoint, 0, 1, 0 });
GetAccessControl().CreateEntry(nullptr, entry);

GetAccessControl().PrepareEntry(entry);
entry.SetFabricIndex(1);
entry.SetPrivilege(Privilege::kOperate);
entry.SetAuthMode(AuthMode::kGroup);
entry.AddSubject(nullptr, 0x0000000055556666);
entry.AddSubject(nullptr, 0x0000000077778888);
entry.AddTarget(nullptr, { AccessControl::Entry::Target::kCluster | AccessControl::Entry::Target::kEndpoint, 11, 2, 0 });
entry.AddTarget(nullptr, { AccessControl::Entry::Target::kDeviceType, 0, 0, 100 });
GetAccessControl().CreateEntry(nullptr, entry);
}

0 comments on commit a51525c

Please sign in to comment.