Skip to content

Commit acdfc06

Browse files
mlepage-googlerestyled-commits
authored andcommitted
Add example implementation of access control (#11550)
* 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 <[email protected]>
1 parent c0a2c81 commit acdfc06

13 files changed

+2654
-635
lines changed

src/access/AccessControl.cpp

+118-59
Original file line numberDiff line numberDiff line change
@@ -23,92 +23,151 @@ namespace {
2323
using chip::FabricIndex;
2424
using namespace chip::Access;
2525

26-
// Avoid GetAccessControl returning nullptr before SetAccessControl is called.
27-
class UnimplementedDataProvider : public AccessControlDataProvider
26+
AccessControl defaultAccessControl;
27+
AccessControl * globalAccessControl = &defaultAccessControl;
28+
29+
static_assert(((unsigned(Privilege::kAdminister) & unsigned(Privilege::kManage)) == 0) &&
30+
((unsigned(Privilege::kAdminister) & unsigned(Privilege::kOperate)) == 0) &&
31+
((unsigned(Privilege::kAdminister) & unsigned(Privilege::kView)) == 0) &&
32+
((unsigned(Privilege::kAdminister) & unsigned(Privilege::kProxyView)) == 0) &&
33+
((unsigned(Privilege::kManage) & unsigned(Privilege::kOperate)) == 0) &&
34+
((unsigned(Privilege::kManage) & unsigned(Privilege::kView)) == 0) &&
35+
((unsigned(Privilege::kManage) & unsigned(Privilege::kProxyView)) == 0) &&
36+
((unsigned(Privilege::kOperate) & unsigned(Privilege::kView)) == 0) &&
37+
((unsigned(Privilege::kOperate) & unsigned(Privilege::kProxyView)) == 0) &&
38+
((unsigned(Privilege::kView) & unsigned(Privilege::kProxyView)) == 0),
39+
"Privilege bits must be unique");
40+
41+
bool CheckRequestPrivilegeAgainstEntryPrivilege(Privilege requestPrivilege, Privilege entryPrivilege)
2842
{
29-
CHIP_ERROR Init() override { return CHIP_NO_ERROR; }
30-
31-
void Finish() override {}
32-
33-
EntryIterator * Entries() const override { return nullptr; }
34-
35-
EntryIterator * Entries(FabricIndex fabricIndex) const override { return nullptr; }
36-
};
37-
38-
// Avoid GetAccessControl returning nullptr before SetAccessControl is called.
39-
UnimplementedDataProvider gUnimplementedDataProvider;
40-
AccessControl gUnimplementedAccessControl(gUnimplementedDataProvider);
41-
42-
AccessControl * gAccessControl = &gUnimplementedAccessControl;
43+
switch (entryPrivilege)
44+
{
45+
case Privilege::kView:
46+
return requestPrivilege == Privilege::kView;
47+
case Privilege::kProxyView:
48+
return requestPrivilege == Privilege::kProxyView || requestPrivilege == Privilege::kView;
49+
case Privilege::kOperate:
50+
return requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView;
51+
case Privilege::kManage:
52+
return requestPrivilege == Privilege::kManage || requestPrivilege == Privilege::kOperate ||
53+
requestPrivilege == Privilege::kView;
54+
case Privilege::kAdminister:
55+
return requestPrivilege == Privilege::kAdminister || requestPrivilege == Privilege::kManage ||
56+
requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kView ||
57+
requestPrivilege == Privilege::kProxyView;
58+
}
59+
return false;
60+
}
4361

4462
} // namespace
4563

4664
namespace chip {
4765
namespace Access {
4866

67+
AccessControl::Entry::Delegate AccessControl::Entry::mDefaultDelegate;
68+
AccessControl::EntryIterator::Delegate AccessControl::EntryIterator::mDefaultDelegate;
69+
AccessControl::Delegate AccessControl::mDefaultDelegate;
70+
4971
CHIP_ERROR AccessControl::Init()
5072
{
51-
ChipLogDetail(DataManagement, "access control: initializing");
52-
// ...
53-
return CHIP_NO_ERROR;
73+
ChipLogDetail(DataManagement, "AccessControl::Init");
74+
return mDelegate.Init();
5475
}
5576

56-
void AccessControl::Finish()
77+
CHIP_ERROR AccessControl::Finish()
5778
{
58-
ChipLogDetail(DataManagement, "access control: finishing");
59-
// ...
79+
ChipLogDetail(DataManagement, "AccessControl::Finish");
80+
return mDelegate.Finish();
6081
}
6182

62-
CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath, Privilege privilege)
83+
CHIP_ERROR AccessControl::Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath,
84+
Privilege requestPrivilege)
6385
{
64-
CHIP_ERROR err = CHIP_ERROR_ACCESS_DENIED;
86+
EntryIterator iterator;
87+
ReturnErrorOnFailure(Entries(iterator, &subjectDescriptor.fabricIndex));
6588

66-
EntryIterator * iterator = mDataProvider.Entries(subjectDescriptor.fabricIndex);
67-
// TODO: check error (but can't until we have an implementation)
68-
#if 0
69-
ReturnErrorCodeIf(iterator == nullptr, CHIP_ERROR_INTERNAL);
70-
#else
71-
ReturnErrorCodeIf(iterator == nullptr, CHIP_NO_ERROR);
72-
#endif
73-
74-
// TODO: a few more cases (PASE commissioning, CASE Authenticated Tags, etc.)
75-
76-
while (auto entry = iterator->Next())
89+
Entry entry;
90+
while (iterator.Next(entry) == CHIP_NO_ERROR)
7791
{
78-
ChipLogDetail(DataManagement, "Checking entry");
79-
80-
if (!entry->MatchesPrivilege(privilege))
81-
continue;
82-
ChipLogDetail(DataManagement, " --> matched privilege");
83-
if (!entry->MatchesAuthMode(subjectDescriptor.authMode))
84-
continue;
85-
ChipLogDetail(DataManagement, " --> matched authmode");
86-
if (!entry->MatchesSubject(subjectDescriptor.subjects[0]))
92+
AuthMode authMode = AuthMode::kNone;
93+
ReturnErrorOnFailure(entry.GetAuthMode(authMode));
94+
if (authMode != subjectDescriptor.authMode)
95+
{
8796
continue;
88-
ChipLogDetail(DataManagement, " --> matched subject");
89-
if (!entry->MatchesTarget(requestPath.endpoint, requestPath.cluster))
90-
continue;
91-
ChipLogDetail(DataManagement, " --> matched target");
97+
}
9298

93-
err = CHIP_NO_ERROR;
94-
break;
99+
Privilege privilege = Privilege::kView;
100+
ReturnErrorOnFailure(entry.GetPrivilege(privilege));
101+
if (!CheckRequestPrivilegeAgainstEntryPrivilege(requestPrivilege, privilege))
102+
{
103+
continue;
104+
}
105+
106+
size_t subjectCount = 0;
107+
ReturnErrorOnFailure(entry.GetSubjectCount(subjectCount));
108+
if (subjectCount > 0)
109+
{
110+
bool subjectMatched = false;
111+
for (size_t i = 0; i < subjectCount; ++i)
112+
{
113+
NodeId subject = kUndefinedNodeId;
114+
ReturnErrorOnFailure(entry.GetSubject(i, subject));
115+
if (subject == subjectDescriptor.subjects[0])
116+
{
117+
subjectMatched = true;
118+
break;
119+
}
120+
// TODO: check against CATs in subject descriptor
121+
}
122+
if (!subjectMatched)
123+
{
124+
continue;
125+
}
126+
}
127+
128+
size_t targetCount = 0;
129+
ReturnErrorOnFailure(entry.GetTargetCount(targetCount));
130+
if (targetCount > 0)
131+
{
132+
bool targetMatched = false;
133+
for (size_t i = 0; i < targetCount; ++i)
134+
{
135+
Entry::Target target;
136+
ReturnErrorOnFailure(entry.GetTarget(i, target));
137+
if ((target.flags & Entry::Target::kCluster) && target.cluster != requestPath.cluster)
138+
{
139+
continue;
140+
}
141+
if ((target.flags & Entry::Target::kEndpoint) && target.endpoint != requestPath.endpoint)
142+
{
143+
continue;
144+
}
145+
// TODO: check against target.deviceType (requires lookup)
146+
targetMatched = true;
147+
break;
148+
}
149+
if (!targetMatched)
150+
{
151+
continue;
152+
}
153+
}
154+
155+
// Entry passed all checks: access is allowed.
156+
return CHIP_NO_ERROR;
95157
}
96158

97-
iterator->Release();
98-
return err;
159+
// No entry was found which passed all checks: access is denied.
160+
return CHIP_ERROR_ACCESS_DENIED;
99161
}
100162

101-
AccessControl * GetAccessControl()
163+
AccessControl & GetAccessControl()
102164
{
103-
return gAccessControl;
165+
return *globalAccessControl;
104166
}
105167

106-
void SetAccessControl(AccessControl * accessControl)
168+
void SetAccessControl(AccessControl & accessControl)
107169
{
108-
if (accessControl != nullptr)
109-
{
110-
gAccessControl = accessControl;
111-
}
170+
globalAccessControl = &accessControl;
112171
}
113172

114173
} // namespace Access

0 commit comments

Comments
 (0)