Skip to content

Commit

Permalink
Add full implementation of access control
Browse files Browse the repository at this point in the history
New interface (issue project-chip#10249) and new implementation (issue project-chip#10250).

Implementation is all in-memory and uses only static storage and stack
(no heap).

Some details missing (e.g. CAT support) but most is here, though not yet
hooked up to other code.

Comes with unit tests.
  • Loading branch information
mlepage-google committed Nov 8, 2021
1 parent 3948f4f commit 4008c6b
Show file tree
Hide file tree
Showing 13 changed files with 2,417 additions and 641 deletions.
162 changes: 107 additions & 55 deletions src/access/AccessControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,92 +23,144 @@ namespace {
using chip::FabricIndex;
using namespace chip::Access;

// Avoid GetAccessControl returning nullptr before SetAccessControl is called.
class UnimplementedDataProvider : public AccessControlDataProvider
AccessControl defaultAccessControl;
AccessControl * globalAccessControl = &defaultAccessControl;

static_assert((int(Privilege::kAdminister) & int(Privilege::kManage)) == 0);
static_assert((int(Privilege::kAdminister) & int(Privilege::kOperate)) == 0);
static_assert((int(Privilege::kAdminister) & int(Privilege::kView)) == 0);
static_assert((int(Privilege::kAdminister) & int(Privilege::kProxyView)) == 0);
static_assert((int(Privilege::kManage) & int(Privilege::kOperate)) == 0);
static_assert((int(Privilege::kManage) & int(Privilege::kView)) == 0);
static_assert((int(Privilege::kManage) & int(Privilege::kProxyView)) == 0);
static_assert((int(Privilege::kOperate) & int(Privilege::kView)) == 0);
static_assert((int(Privilege::kOperate) & int(Privilege::kProxyView)) == 0);
static_assert((int(Privilege::kView) & int(Privilege::kProxyView)) == 0);

int GetGrantedPrivileges(Privilege privilege)
{
CHIP_ERROR Init() override { return CHIP_NO_ERROR; }

void Finish() override {}

EntryIterator * Entries() const override { return nullptr; }

EntryIterator * Entries(FabricIndex fabricIndex) const override { return nullptr; }
};

// Avoid GetAccessControl returning nullptr before SetAccessControl is called.
UnimplementedDataProvider gUnimplementedDataProvider;
AccessControl gUnimplementedAccessControl(gUnimplementedDataProvider);

AccessControl * gAccessControl = &gUnimplementedAccessControl;
switch (privilege)
{
case Privilege::kView:
return int(Privilege::kView);
case Privilege::kProxyView:
return int(Privilege::kProxyView) | int(Privilege::kView);
case Privilege::kOperate:
return int(Privilege::kOperate) | int(Privilege::kView);
case Privilege::kManage:
return int(Privilege::kManage) | int(Privilege::kOperate) | int(Privilege::kView);
case Privilege::kAdminister:
return int(Privilege::kAdminister) | int(Privilege::kManage) | int(Privilege::kOperate) | int(Privilege::kView) | int(Privilege::kProxyView);
}
return 0;
}

} // namespace

namespace chip {
namespace Access {

AccessControl::Entry::Delegate AccessControl::Entry::mDefaultDelegate;
AccessControl::EntryIterator::Delegate AccessControl::EntryIterator::mDefaultDelegate;
AccessControl::Delegate AccessControl::mDefaultDelegate;

CHIP_ERROR AccessControl::Init()
{
ChipLogDetail(DataManagement, "access control: initializing");
// ...
return CHIP_NO_ERROR;
ChipLogDetail(DataManagement, "AccessControl::Init");
return mDelegate.Init();
}

void AccessControl::Finish()
CHIP_ERROR AccessControl::Finish()
{
ChipLogDetail(DataManagement, "access control: finishing");
// ...
ChipLogDetail(DataManagement, "AccessControl::Finish");
return mDelegate.Finish();
}

CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath, Privilege privilege)
CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath, Privilege requestPrivilege)
{
CHIP_ERROR err = CHIP_ERROR_ACCESS_DENIED;
EntryIterator iterator;
ReturnErrorOnFailure(Entries(iterator, &subjectDescriptor.fabricIndex));

EntryIterator * iterator = mDataProvider.Entries(subjectDescriptor.fabricIndex);
// TODO: check error (but can't until we have an implementation)
#if 0
ReturnErrorCodeIf(iterator == nullptr, CHIP_ERROR_INTERNAL);
#else
ReturnErrorCodeIf(iterator == nullptr, CHIP_NO_ERROR);
#endif

// TODO: a few more cases (PASE commissioning, CASE Authenticated Tags, etc.)

while (auto entry = iterator->Next())
Entry entry;
while (iterator.Next(entry) == CHIP_NO_ERROR)
{
ChipLogDetail(DataManagement, "Checking entry");
ChipLogDetail(DataManagement, "got an entry");

if (!entry->MatchesPrivilege(privilege))
AuthMode authMode;
ReturnErrorOnFailure(entry.GetAuthMode(authMode));
if (authMode != subjectDescriptor.authMode)
{
continue;
ChipLogDetail(DataManagement, " --> matched privilege");
if (!entry->MatchesAuthMode(subjectDescriptor.authMode))
}

Privilege privilege;
ReturnErrorOnFailure(entry.GetPrivilege(privilege));
int grantedPrivileges = GetGrantedPrivileges(privilege);
if ((grantedPrivileges & int(requestPrivilege)) == 0)
{
continue;
ChipLogDetail(DataManagement, " --> matched authmode");
if (!entry->MatchesSubject(subjectDescriptor.subjects[0]))
}

size_t subjectCount;
NodeId subject;
ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
if (subjectCount > 0)
{
for (size_t i = 0; i < subjectCount; ++i)
{
ReturnErrorOnFailure(entry.GetSubject(i, subject));
if (subject == subjectDescriptor.subjects[0])
{
goto subjectMatched;
}
// TODO: check against CATs in subject descriptor
}
continue;
ChipLogDetail(DataManagement, " --> matched subject");
if (!entry->MatchesTarget(requestPath.endpoint, requestPath.cluster))
}
subjectMatched:

size_t targetCount;
Entry::Target target;
ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
if (targetCount > 0)
{
for (size_t i = 0; i < targetCount; ++i)
{
ReturnErrorOnFailure(entry.GetTarget(i, target));
if ((target.flags & Entry::Target::kCluster) && target.cluster != requestPath.cluster)
{
continue;
}
if ((target.flags & Entry::Target::kEndpoint) && target.endpoint != requestPath.endpoint)
{
continue;
}
// TODO: check against target.deviceType (requires lookup)
goto targetMatched;
}
continue;
ChipLogDetail(DataManagement, " --> matched target");
}
targetMatched:

err = CHIP_NO_ERROR;
break;
return CHIP_NO_ERROR;
}

iterator->Release();
return err;
return CHIP_ERROR_ACCESS_DENIED;
}

AccessControl * GetAccessControl()
AccessControl & GetAccessControl()
{
return gAccessControl;
return *globalAccessControl;
}

void SetAccessControl(AccessControl * accessControl)
void ResetAccessControl()
{
if (accessControl != nullptr)
{
gAccessControl = accessControl;
}
globalAccessControl = &defaultAccessControl;
}

void SetAccessControl(AccessControl & accessControl)
{
globalAccessControl = &accessControl;
}

} // namespace Access
Expand Down
Loading

0 comments on commit 4008c6b

Please sign in to comment.