Skip to content

Commit

Permalink
Add example implementation of access control (#11550)
Browse files Browse the repository at this point in the history
* Add full implementation of access control

New interface (issue #10249) and new implementation (issue #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.

* Fix configuration

Was added at last minute, missed this spot.

* Address code review comments

- rewrite portions of the check loop to use boolean instead of goto
- refactor privilege checking to make it clearer
- ensure storage classes are POD types (with static_assert)
- remove memsets on storage classes (for now, can re-add later)
- clarify some comments
- remove debug log statement

* Restyled by clang-format

* Address code review comments

Also fix some compiler warnings/errors on other builds

* Add tests for fabric filtered indexing

Refactor the index conversion (to/from fabric filtered) to be clearer.

* Restyled by clang-format

* Address code review comments

- change Target::Flags from int to unsigned
- use unsigned in static_asserts for flags
- tweak auto variables

* Add docs and comments

- Add API documentation (mainly to clarify in/out parameters).
- Add implementation comments (where warranted).
- Add more unit tests (mainly for removing subjects/targets).
- A few fixes to get aforementioned tests passing.
- A bit of refactoring/renaming to clarify the code.

* Restyled by whitespace

* Restyled by clang-format

* Some code review suggestions

* Restyled by clang-format

* Fix errors on other compilers

* Fix more build errors on other compilers

* More code review suggestions

* Restyled by clang-format

* Fix typo in config flag

Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
mlepage-google and restyled-commits authored Nov 16, 2021
1 parent 48db955 commit 09f9837
Show file tree
Hide file tree
Showing 13 changed files with 2,654 additions and 635 deletions.
177 changes: 118 additions & 59 deletions src/access/AccessControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,92 +23,151 @@ 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(((unsigned(Privilege::kAdminister) & unsigned(Privilege::kManage)) == 0) &&
((unsigned(Privilege::kAdminister) & unsigned(Privilege::kOperate)) == 0) &&
((unsigned(Privilege::kAdminister) & unsigned(Privilege::kView)) == 0) &&
((unsigned(Privilege::kAdminister) & unsigned(Privilege::kProxyView)) == 0) &&
((unsigned(Privilege::kManage) & unsigned(Privilege::kOperate)) == 0) &&
((unsigned(Privilege::kManage) & unsigned(Privilege::kView)) == 0) &&
((unsigned(Privilege::kManage) & unsigned(Privilege::kProxyView)) == 0) &&
((unsigned(Privilege::kOperate) & unsigned(Privilege::kView)) == 0) &&
((unsigned(Privilege::kOperate) & unsigned(Privilege::kProxyView)) == 0) &&
((unsigned(Privilege::kView) & unsigned(Privilege::kProxyView)) == 0),
"Privilege bits must be unique");

bool CheckRequestPrivilegeAgainstEntryPrivilege(Privilege requestPrivilege, Privilege entryPrivilege)
{
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 (entryPrivilege)
{
case Privilege::kView:
return requestPrivilege == Privilege::kView;
case Privilege::kProxyView:
return requestPrivilege == Privilege::kProxyView || requestPrivilege == Privilege::kView;
case Privilege::kOperate:
return requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView;
case Privilege::kManage:
return requestPrivilege == Privilege::kManage || requestPrivilege == Privilege::kOperate ||
requestPrivilege == Privilege::kView;
case Privilege::kAdminister:
return requestPrivilege == Privilege::kAdminister || requestPrivilege == Privilege::kManage ||
requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView ||
requestPrivilege == Privilege::kProxyView;
}
return false;
}

} // 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");

if (!entry->MatchesPrivilege(privilege))
continue;
ChipLogDetail(DataManagement, " --> matched privilege");
if (!entry->MatchesAuthMode(subjectDescriptor.authMode))
continue;
ChipLogDetail(DataManagement, " --> matched authmode");
if (!entry->MatchesSubject(subjectDescriptor.subjects[0]))
AuthMode authMode = AuthMode::kNone;
ReturnErrorOnFailure(entry.GetAuthMode(authMode));
if (authMode != subjectDescriptor.authMode)
{
continue;
ChipLogDetail(DataManagement, " --> matched subject");
if (!entry->MatchesTarget(requestPath.endpoint, requestPath.cluster))
continue;
ChipLogDetail(DataManagement, " --> matched target");
}

err = CHIP_NO_ERROR;
break;
Privilege privilege = Privilege::kView;
ReturnErrorOnFailure(entry.GetPrivilege(privilege));
if (!CheckRequestPrivilegeAgainstEntryPrivilege(requestPrivilege, privilege))
{
continue;
}

size_t subjectCount = 0;
ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
if (subjectCount > 0)
{
bool subjectMatched = false;
for (size_t i = 0; i < subjectCount; ++i)
{
NodeId subject = kUndefinedNodeId;
ReturnErrorOnFailure(entry.GetSubject(i, subject));
if (subject == subjectDescriptor.subjects[0])
{
subjectMatched = true;
break;
}
// TODO: check against CATs in subject descriptor
}
if (!subjectMatched)
{
continue;
}
}

size_t targetCount = 0;
ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
if (targetCount > 0)
{
bool targetMatched = false;
for (size_t i = 0; i < targetCount; ++i)
{
Entry::Target target;
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)
targetMatched = true;
break;
}
if (!targetMatched)
{
continue;
}
}

// Entry passed all checks: access is allowed.
return CHIP_NO_ERROR;
}

iterator->Release();
return err;
// No entry was found which passed all checks: access is denied.
return CHIP_ERROR_ACCESS_DENIED;
}

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

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

} // namespace Access
Expand Down
Loading

0 comments on commit 09f9837

Please sign in to comment.