Skip to content

Commit

Permalink
Access control device type support (project-chip#16794)
Browse files Browse the repository at this point in the history
Create a DeviceTypeResolver that can be installed in
AccessControl so device type can work in targets.

Fixes project-chip#14431
  • Loading branch information
mlepage-google authored Mar 30, 2022
1 parent 0d98cc2 commit df68705
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 20 deletions.
16 changes: 9 additions & 7 deletions src/access/AccessControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,18 +162,18 @@ namespace Access {
AccessControl::Entry::Delegate AccessControl::Entry::mDefaultDelegate;
AccessControl::EntryIterator::Delegate AccessControl::EntryIterator::mDefaultDelegate;

CHIP_ERROR AccessControl::Init(AccessControl::Delegate * delegate)
CHIP_ERROR AccessControl::Init(AccessControl::Delegate * delegate, DeviceTypeResolver & deviceTypeResolver)
{
VerifyOrReturnError(!IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

ChipLogProgress(DataManagement, "AccessControl: initializing");

// delegate can never be null. This was already checked
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
CHIP_ERROR retval = delegate->Init();
if (retval == CHIP_NO_ERROR)
{
mDelegate = delegate;
mDelegate = delegate;
mDeviceTypeResolver = &deviceTypeResolver;
}

return retval;
Expand Down Expand Up @@ -326,7 +326,11 @@ CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, con
{
continue;
}
// TODO(#14431): device type target not yet supported (add lookup/match when supported)
if (target.flags & Entry::Target::kDeviceType &&
!mDeviceTypeResolver->IsDeviceTypeOnEndpoint(target.deviceType, requestPath.endpoint))
{
continue;
}
targetMatched = true;
break;
}
Expand Down Expand Up @@ -401,8 +405,6 @@ bool AccessControl::IsValid(const Entry & entry)
(!kHasEndpoint || IsValidEndpointId(target.endpoint)) &&
(!kHasDeviceType || IsValidDeviceTypeId(target.deviceType)),
log = "invalid target");
// TODO(#14431): device type target not yet supported (remove check when supported)
VerifyOrExit(!kHasDeviceType, log = "device type target not yet supported");
}

return true;
Expand Down
17 changes: 14 additions & 3 deletions src/access/AccessControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ namespace Access {
class AccessControl
{
public:
/**
* Used by access control to determine if a device type resolves to an endpoint.
*/
struct DeviceTypeResolver
{
public:
virtual ~DeviceTypeResolver() = default;

virtual bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) = 0;
};

/**
* Handle to an entry in the access control list.
*
Expand Down Expand Up @@ -376,12 +387,10 @@ class AccessControl
/**
* Initialize the access control module. Must be called before first use.
*
* @param delegate - The delegate to use for acces control
*
* @return CHIP_NO_ERROR on success, CHIP_ERROR_INCORRECT_STATE if called more than once,
* CHIP_ERROR_INVALID_ARGUMENT if delegate is null, or other fatal error.
*/
CHIP_ERROR Init(AccessControl::Delegate * delegate);
CHIP_ERROR Init(AccessControl::Delegate * delegate, DeviceTypeResolver & deviceTypeResolver);

/**
* Deinitialize the access control module. Must be called when finished.
Expand Down Expand Up @@ -498,6 +507,8 @@ class AccessControl
bool IsValid(const Entry & entry);

Delegate * mDelegate = nullptr;

DeviceTypeResolver * mDeviceTypeResolver = nullptr;
};

/**
Expand Down
18 changes: 11 additions & 7 deletions src/access/tests/TestAccessControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,12 @@ constexpr DeviceTypeId invalidDeviceTypes[] = {
};
// clang-format on

class DeviceTypeResolver : public AccessControl::DeviceTypeResolver
{
public:
bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) override { return false; }
} testDeviceTypeResolver;

// For testing, supports one subject and target, allows any value (valid or invalid)
class TestEntryDelegate : public Entry::Delegate
{
Expand Down Expand Up @@ -1335,12 +1341,11 @@ void TestAclValidateTarget(nlTestSuite * inSuite, void * inContext)
accessControl.DeleteEntry(1);
}

// TODO(#14431): device type target not yet supported (flip != to == when supported)
for (auto deviceType : validDeviceTypes)
{
NL_TEST_ASSERT(inSuite, entry.SetTarget(0, { .flags = Target::kDeviceType, .deviceType = deviceType }) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.UpdateEntry(0, entry) != CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.CreateEntry(nullptr, entry) != CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.UpdateEntry(0, entry) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.CreateEntry(nullptr, entry) == CHIP_NO_ERROR);
accessControl.DeleteEntry(1);
}

Expand All @@ -1358,7 +1363,6 @@ void TestAclValidateTarget(nlTestSuite * inSuite, void * inContext)
}
}

// TODO(#14431): device type target not yet supported (flip != to == when supported)
for (auto cluster : validClusters)
{
for (auto deviceType : validDeviceTypes)
Expand All @@ -1368,8 +1372,8 @@ void TestAclValidateTarget(nlTestSuite * inSuite, void * inContext)
entry.SetTarget(
0, { .flags = Target::kCluster | Target::kDeviceType, .cluster = cluster, .deviceType = deviceType }) ==
CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.UpdateEntry(0, entry) != CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.CreateEntry(nullptr, entry) != CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.UpdateEntry(0, entry) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, accessControl.CreateEntry(nullptr, entry) == CHIP_NO_ERROR);
accessControl.DeleteEntry(1);
}
}
Expand Down Expand Up @@ -2135,7 +2139,7 @@ int Setup(void * inContext)
{
AccessControl::Delegate * delegate = Examples::GetAccessControlDelegate(nullptr);
SetAccessControl(accessControl);
VerifyOrDie(GetAccessControl().Init(delegate) == CHIP_NO_ERROR);
VerifyOrDie(GetAccessControl().Init(delegate, testDeviceTypeResolver) == CHIP_NO_ERROR);
return SUCCESS;
}

Expand Down
6 changes: 6 additions & 0 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,11 @@ CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDesc
* Check if the given cluster has the given DataVersion.
*/
bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion);

/**
* Returns true if device type is on endpoint, false otherwise.
*/
bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint);

} // namespace app
} // namespace chip
11 changes: 10 additions & 1 deletion src/app/server/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ void StopEventLoop(intptr_t arg)
}
}

class DeviceTypeResolver : public chip::Access::AccessControl::DeviceTypeResolver
{
public:
bool IsDeviceTypeOnEndpoint(chip::DeviceTypeId deviceType, chip::EndpointId endpoint) override
{
return chip::app::IsDeviceTypeOnEndpoint(deviceType, endpoint);
}
} sDeviceTypeResolver;

} // namespace

namespace chip {
Expand Down Expand Up @@ -142,7 +151,7 @@ CHIP_ERROR Server::Init(AppDelegate * delegate, uint16_t secureServicePort, uint
accessDelegate = Access::Examples::GetAccessControlDelegate(&mDeviceStorage);
VerifyOrExit(accessDelegate != nullptr, ChipLogError(AppServer, "Invalid access delegate found."));

err = mAccessControl.Init(accessDelegate);
err = mAccessControl.Init(accessDelegate, sDeviceTypeResolver);
SuccessOrExit(err);
Access::SetAccessControl(mAccessControl);

Expand Down
9 changes: 8 additions & 1 deletion src/app/tests/AppTestContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@

namespace {

class TestDeviceTypeResolver : public chip::Access::AccessControl::DeviceTypeResolver
{
public:
bool IsDeviceTypeOnEndpoint(chip::DeviceTypeId deviceType, chip::EndpointId endpoint) override { return false; }
} gDeviceTypeResolver;

chip::Access::AccessControl gPermissiveAccessControl;

} // namespace
Expand All @@ -37,7 +43,8 @@ CHIP_ERROR AppContext::Init()
ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(&GetExchangeManager()));

Access::SetAccessControl(gPermissiveAccessControl);
ReturnErrorOnFailure(Access::GetAccessControl().Init(chip::Access::Examples::GetPermissiveAccessControlDelegate()));
ReturnErrorOnFailure(
Access::GetAccessControl().Init(chip::Access::Examples::GetPermissiveAccessControlDelegate(), gDeviceTypeResolver));

return CHIP_NO_ERROR;
}
Expand Down
5 changes: 5 additions & 0 deletions src/app/tests/TestReadInteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
}
}

bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
{
return false;
}

class TestReadInteraction
{
public:
Expand Down
6 changes: 6 additions & 0 deletions src/app/tests/integration/chip_im_initiator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,12 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
{
return true;
}

bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
{
return false;
}

} // namespace app
} // namespace chip

Expand Down
6 changes: 6 additions & 0 deletions src/app/tests/integration/chip_im_responder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
{
return true;
}

bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
{
return false;
}

} // namespace app
} // namespace chip

Expand Down
6 changes: 6 additions & 0 deletions src/app/util/ember-compatibility-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,12 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
return (*(version)) == aRequiredVersion;
}

bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
{
uint16_t index = emberAfIndexFromEndpoint(endpoint);
return index != 0xFFFF && emberAfDeviceIdFromIndex(index) == deviceType;
}

} // namespace app
} // namespace chip

Expand Down
5 changes: 5 additions & 0 deletions src/controller/tests/data_model/TestRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
return false;
}
}

bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
{
return false;
}
} // namespace app
} // namespace chip

Expand Down
11 changes: 10 additions & 1 deletion src/darwin/Framework/CHIP/CHIPControllerAccessControl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <access/RequestPath.h>
#include <access/SubjectDescriptor.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/InteractionModelEngine.h>
#include <lib/core/CHIPError.h>

using namespace chip;
Expand All @@ -32,6 +33,14 @@
// CHIPIMDispatch.mm.
constexpr EndpointId kSupportedEndpoint = 0;

class DeviceTypeResolver : public Access::AccessControl::DeviceTypeResolver {
public:
bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) override
{
return app::IsDeviceTypeOnEndpoint(deviceType, endpoint);
}
} gDeviceTypeResolver;

// TODO: Make the policy more configurable by consumers.
class AccessControlDelegate : public Access::AccessControl::Delegate {
CHIP_ERROR Check(
Expand Down Expand Up @@ -68,7 +77,7 @@ + (void)init
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
GetAccessControl().Init(&gDelegate);
GetAccessControl().Init(&gDelegate, gDeviceTypeResolver);
});
}

Expand Down
2 changes: 2 additions & 0 deletions src/darwin/Framework/CHIP/CHIPIMDispatch.mm
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath,
return false;
}

bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) { return false; }

CHIP_ERROR WriteSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath,
TLV::TLVReader & aReader, WriteHandler * aWriteHandler)
{
Expand Down

0 comments on commit df68705

Please sign in to comment.