Coverage Report

Created: 2026-03-27 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/app/server/AclStorage.cpp
Line
Count
Source
1
/*
2
 *
3
 *    Copyright (c) 2022 Project CHIP Authors
4
 *
5
 *    Licensed under the Apache License, Version 2.0 (the "License");
6
 *    you may not use this file except in compliance with the License.
7
 *    You may obtain a copy of the License at
8
 *
9
 *        http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *    Unless required by applicable law or agreed to in writing, software
12
 *    distributed under the License is distributed on an "AS IS" BASIS,
13
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *    See the License for the specific language governing permissions and
15
 *    limitations under the License.
16
 */
17
18
#include <app/server/AclStorage.h>
19
20
#include <lib/support/DefaultStorageKeyAllocator.h>
21
22
using namespace chip;
23
using namespace chip::app;
24
using namespace chip::Access;
25
26
using Entry                = AccessControl::Entry;
27
using EntryListener        = AccessControl::EntryListener;
28
using StagingAuthMode      = Clusters::AccessControl::AccessControlEntryAuthModeEnum;
29
using StagingPrivilege     = Clusters::AccessControl::AccessControlEntryPrivilegeEnum;
30
using StagingAuxiliaryType = Clusters::AccessControl::AccessControlAuxiliaryTypeEnum;
31
using StagingTarget        = Clusters::AccessControl::Structs::AccessControlTargetStruct::Type;
32
using Target               = AccessControl::Entry::Target;
33
34
namespace {
35
36
struct StagingSubject
37
{
38
    NodeId nodeId;
39
    StagingAuthMode authMode;
40
};
41
42
CHIP_ERROR Convert(AuthMode from, StagingAuthMode & to)
43
0
{
44
0
    switch (from)
45
0
    {
46
0
    case AuthMode::kPase:
47
0
        to = StagingAuthMode::kPase;
48
0
        break;
49
0
    case AuthMode::kCase:
50
0
        to = StagingAuthMode::kCase;
51
0
        break;
52
0
    case AuthMode::kGroup:
53
0
        to = StagingAuthMode::kGroup;
54
0
        break;
55
0
    default:
56
0
        return CHIP_ERROR_INVALID_ARGUMENT;
57
0
    }
58
0
    return CHIP_NO_ERROR;
59
0
}
60
61
CHIP_ERROR Convert(StagingAuthMode from, AuthMode & to)
62
0
{
63
0
    switch (from)
64
0
    {
65
0
    case StagingAuthMode::kPase:
66
0
        to = AuthMode::kPase;
67
0
        break;
68
0
    case StagingAuthMode::kCase:
69
0
        to = AuthMode::kCase;
70
0
        break;
71
0
    case StagingAuthMode::kGroup:
72
0
        to = AuthMode::kGroup;
73
0
        break;
74
0
    default:
75
0
        return CHIP_ERROR_INVALID_ARGUMENT;
76
0
    }
77
0
    return CHIP_NO_ERROR;
78
0
}
79
80
CHIP_ERROR Convert(Privilege from, StagingPrivilege & to)
81
0
{
82
0
    switch (from)
83
0
    {
84
0
    case Privilege::kView:
85
0
        to = StagingPrivilege::kView;
86
0
        break;
87
0
    case Privilege::kProxyView:
88
0
        to = StagingPrivilege::kProxyView;
89
0
        break;
90
0
    case Privilege::kOperate:
91
0
        to = StagingPrivilege::kOperate;
92
0
        break;
93
0
    case Privilege::kManage:
94
0
        to = StagingPrivilege::kManage;
95
0
        break;
96
0
    case Privilege::kAdminister:
97
0
        to = StagingPrivilege::kAdminister;
98
0
        break;
99
0
    default:
100
0
        return CHIP_ERROR_INVALID_ARGUMENT;
101
0
    }
102
0
    return CHIP_NO_ERROR;
103
0
}
104
105
CHIP_ERROR Convert(StagingPrivilege from, Privilege & to)
106
0
{
107
0
    switch (from)
108
0
    {
109
0
    case StagingPrivilege::kView:
110
0
        to = Privilege::kView;
111
0
        break;
112
0
    case StagingPrivilege::kProxyView:
113
0
        to = Privilege::kProxyView;
114
0
        break;
115
0
    case StagingPrivilege::kOperate:
116
0
        to = Privilege::kOperate;
117
0
        break;
118
0
    case StagingPrivilege::kManage:
119
0
        to = Privilege::kManage;
120
0
        break;
121
0
    case StagingPrivilege::kAdminister:
122
0
        to = Privilege::kAdminister;
123
0
        break;
124
0
    default:
125
0
        return CHIP_ERROR_INVALID_ARGUMENT;
126
0
    }
127
0
    return CHIP_NO_ERROR;
128
0
}
129
130
CHIP_ERROR Convert(AuxiliaryType from, StagingAuxiliaryType & to)
131
0
{
132
0
    switch (from)
133
0
    {
134
0
    case AuxiliaryType::kSystem:
135
0
        to = StagingAuxiliaryType::kSystem;
136
0
        break;
137
0
    case AuxiliaryType::kGroupcast:
138
0
        to = StagingAuxiliaryType::kGroupcast;
139
0
        break;
140
0
    default:
141
0
        return CHIP_ERROR_INVALID_ARGUMENT;
142
0
    }
143
0
    return CHIP_NO_ERROR;
144
0
}
145
146
CHIP_ERROR Convert(StagingAuxiliaryType from, AuxiliaryType & to)
147
0
{
148
0
    switch (from)
149
0
    {
150
0
    case StagingAuxiliaryType::kSystem:
151
0
        to = AuxiliaryType::kSystem;
152
0
        break;
153
0
    case StagingAuxiliaryType::kGroupcast:
154
0
        to = AuxiliaryType::kGroupcast;
155
0
        break;
156
0
    default:
157
0
        return CHIP_ERROR_INVALID_ARGUMENT;
158
0
    }
159
0
    return CHIP_NO_ERROR;
160
0
}
161
162
CHIP_ERROR Convert(NodeId from, StagingSubject & to)
163
0
{
164
0
    if (IsOperationalNodeId(from) || IsCASEAuthTag(from))
165
0
    {
166
0
        to = { .nodeId = from, .authMode = StagingAuthMode::kCase };
167
0
    }
168
0
    else if (IsGroupId(from))
169
0
    {
170
0
        to = { .nodeId = GroupIdFromNodeId(from), .authMode = StagingAuthMode::kGroup };
171
0
    }
172
0
    else if (IsPAKEKeyId(from))
173
0
    {
174
0
        to = { .nodeId = PAKEKeyIdFromNodeId(from), .authMode = StagingAuthMode::kPase };
175
0
    }
176
0
    else
177
0
    {
178
0
        return CHIP_ERROR_INVALID_ARGUMENT;
179
0
    }
180
0
    return CHIP_NO_ERROR;
181
0
}
182
183
CHIP_ERROR Convert(StagingSubject from, NodeId & to)
184
0
{
185
0
    switch (from.authMode)
186
0
    {
187
0
    case StagingAuthMode::kPase:
188
0
        VerifyOrReturnError((from.nodeId & ~kMaskPAKEKeyId) == 0, CHIP_ERROR_INVALID_ARGUMENT);
189
0
        to = NodeIdFromPAKEKeyId(static_cast<PasscodeId>(from.nodeId));
190
0
        break;
191
0
    case StagingAuthMode::kCase:
192
0
        to = from.nodeId;
193
0
        break;
194
0
    case StagingAuthMode::kGroup:
195
0
        VerifyOrReturnError((from.nodeId & ~kMaskGroupId) == 0, CHIP_ERROR_INVALID_ARGUMENT);
196
0
        to = NodeIdFromGroupId(static_cast<GroupId>(from.nodeId));
197
0
        break;
198
0
    default:
199
0
        return CHIP_ERROR_INVALID_ARGUMENT;
200
0
    }
201
0
    return CHIP_NO_ERROR;
202
0
}
203
204
CHIP_ERROR Convert(const Target & from, StagingTarget & to)
205
0
{
206
0
    if ((from.flags & Target::kCluster) != 0)
207
0
    {
208
0
        to.cluster.SetNonNull(from.cluster);
209
0
    }
210
0
    else
211
0
    {
212
0
        to.cluster.SetNull();
213
0
    }
214
0
    if ((from.flags & Target::kEndpoint) != 0)
215
0
    {
216
0
        to.endpoint.SetNonNull(from.endpoint);
217
0
    }
218
0
    else
219
0
    {
220
0
        to.endpoint.SetNull();
221
0
    }
222
0
    if ((from.flags & Target::kDeviceType) != 0)
223
0
    {
224
0
        to.deviceType.SetNonNull(from.deviceType);
225
0
    }
226
0
    else
227
0
    {
228
0
        to.deviceType.SetNull();
229
0
    }
230
0
    return CHIP_NO_ERROR;
231
0
}
232
233
CHIP_ERROR Convert(const StagingTarget & from, Target & to)
234
0
{
235
0
    to.flags = 0;
236
0
    if (!from.cluster.IsNull())
237
0
    {
238
0
        to.flags |= Target::kCluster;
239
0
        to.cluster = from.cluster.Value();
240
0
    }
241
0
    if (!from.endpoint.IsNull())
242
0
    {
243
0
        to.flags |= Target::kEndpoint;
244
0
        to.endpoint = from.endpoint.Value();
245
0
    }
246
0
    if (!from.deviceType.IsNull())
247
0
    {
248
0
        to.flags |= Target::kDeviceType;
249
0
        to.deviceType = from.deviceType.Value();
250
0
    }
251
0
    return CHIP_NO_ERROR;
252
0
}
253
254
} // namespace
255
256
namespace chip {
257
namespace app {
258
259
CHIP_ERROR AclStorage::DecodableEntry::Decode(TLV::TLVReader & reader)
260
0
{
261
0
    ReturnErrorOnFailure(mStagingEntry.Decode(reader));
262
0
    ReturnErrorOnFailure(Unstage());
263
0
    return CHIP_NO_ERROR;
264
0
}
265
266
CHIP_ERROR AclStorage::DecodableEntry::Unstage()
267
0
{
268
0
    ReturnErrorOnFailure(GetAccessControl().PrepareEntry(mEntry));
269
270
0
    ReturnErrorOnFailure(mEntry.SetFabricIndex(mStagingEntry.fabricIndex));
271
272
0
    {
273
0
        Privilege privilege;
274
0
        ReturnErrorOnFailure(Convert(mStagingEntry.privilege, privilege));
275
0
        ReturnErrorOnFailure(mEntry.SetPrivilege(privilege));
276
0
    }
277
278
0
    {
279
0
        AuthMode authMode;
280
0
        ReturnErrorOnFailure(Convert(mStagingEntry.authMode, authMode));
281
0
        ReturnErrorOnFailure(mEntry.SetAuthMode(authMode));
282
0
    }
283
284
0
    if (mStagingEntry.auxiliaryType.HasValue())
285
0
    {
286
0
        AuxiliaryType auxiliaryType;
287
0
        ReturnErrorOnFailure(Convert(mStagingEntry.auxiliaryType.Value(), auxiliaryType));
288
0
        ReturnErrorOnFailure(mEntry.SetAuxiliaryType(auxiliaryType));
289
0
    }
290
291
0
    if (!mStagingEntry.subjects.IsNull())
292
0
    {
293
0
        auto iterator = mStagingEntry.subjects.Value().begin();
294
0
        while (iterator.Next())
295
0
        {
296
0
            StagingSubject tmp = { .nodeId = iterator.GetValue(), .authMode = mStagingEntry.authMode };
297
0
            NodeId subject;
298
0
            ReturnErrorOnFailure(Convert(tmp, subject));
299
0
            ReturnErrorOnFailure(mEntry.AddSubject(nullptr, subject));
300
0
        }
301
0
        ReturnErrorOnFailure(iterator.GetStatus());
302
0
    }
303
304
0
    if (!mStagingEntry.targets.IsNull())
305
0
    {
306
0
        auto iterator = mStagingEntry.targets.Value().begin();
307
0
        while (iterator.Next())
308
0
        {
309
0
            Target target;
310
0
            ReturnErrorOnFailure(Convert(iterator.GetValue(), target));
311
0
            ReturnErrorOnFailure(mEntry.AddTarget(nullptr, target));
312
0
        }
313
0
        ReturnErrorOnFailure(iterator.GetStatus());
314
0
    }
315
316
0
    return CHIP_NO_ERROR;
317
0
}
318
319
CHIP_ERROR AclStorage::EncodableEntry::EncodeForRead(TLV::TLVWriter & writer, TLV::Tag tag, FabricIndex fabric) const
320
0
{
321
0
    ReturnErrorOnFailure(Stage());
322
0
    ReturnErrorOnFailure(mStagingEntry.EncodeForRead(writer, tag, fabric));
323
0
    return CHIP_NO_ERROR;
324
0
}
325
326
CHIP_ERROR AclStorage::EncodableEntry::EncodeForWrite(TLV::TLVWriter & writer, TLV::Tag tag) const
327
0
{
328
0
    ReturnErrorOnFailure(Stage());
329
0
    ReturnErrorOnFailure(mStagingEntry.EncodeForWrite(writer, tag));
330
0
    return CHIP_NO_ERROR;
331
0
}
332
333
CHIP_ERROR AclStorage::EncodableEntry::Stage() const
334
0
{
335
0
    ReturnErrorOnFailure(mEntry.GetFabricIndex(mStagingEntry.fabricIndex));
336
337
0
    {
338
0
        Privilege privilege;
339
0
        ReturnErrorOnFailure(mEntry.GetPrivilege(privilege));
340
0
        ReturnErrorOnFailure(Convert(privilege, mStagingEntry.privilege));
341
0
    }
342
343
0
    {
344
0
        AuthMode authMode;
345
0
        ReturnErrorOnFailure(mEntry.GetAuthMode(authMode));
346
0
        ReturnErrorOnFailure(Convert(authMode, mStagingEntry.authMode));
347
0
    }
348
349
0
    {
350
0
        AuxiliaryType auxiliaryType;
351
0
        CHIP_ERROR err = mEntry.GetAuxiliaryType(auxiliaryType);
352
0
        if (err == CHIP_NO_ERROR)
353
0
        {
354
0
            StagingAuxiliaryType stagingAuxiliaryType;
355
0
            ReturnErrorOnFailure(Convert(auxiliaryType, stagingAuxiliaryType));
356
0
            mStagingEntry.auxiliaryType.SetValue(stagingAuxiliaryType);
357
0
        }
358
0
        else if (err != CHIP_ERROR_NOT_IMPLEMENTED)
359
0
        {
360
            // CHIP_ERROR_NOT_IMPLEMENTED is okay since this field is optional, and doesn't
361
            // need to be set in all cases (the implementation will be up to the delegate).
362
            // Any other sort of error is unexepcted so we return it here.
363
0
            return err;
364
0
        }
365
0
    }
366
367
0
    {
368
0
        size_t count;
369
0
        ReturnErrorOnFailure(mEntry.GetSubjectCount(count));
370
0
        if (count > 0)
371
0
        {
372
0
            for (size_t i = 0; i < count; ++i)
373
0
            {
374
0
                NodeId subject;
375
0
                ReturnErrorOnFailure(mEntry.GetSubject(i, subject));
376
0
                StagingSubject tmp;
377
0
                ReturnErrorOnFailure(Convert(subject, tmp));
378
0
                mStagingSubjects[i] = tmp.nodeId;
379
0
            }
380
0
            mStagingEntry.subjects.SetNonNull(mStagingSubjects, count);
381
0
        }
382
0
        else
383
0
        {
384
0
            mStagingEntry.subjects.SetNull();
385
0
        }
386
0
    }
387
388
0
    {
389
0
        size_t count;
390
0
        ReturnErrorOnFailure(mEntry.GetTargetCount(count));
391
0
        if (count > 0)
392
0
        {
393
0
            for (size_t i = 0; i < count; ++i)
394
0
            {
395
0
                Target target;
396
0
                ReturnErrorOnFailure(mEntry.GetTarget(i, target));
397
0
                ReturnErrorOnFailure(Convert(target, mStagingTargets[i]));
398
0
            }
399
0
            mStagingEntry.targets.SetNonNull(mStagingTargets, count);
400
0
        }
401
0
        else
402
0
        {
403
0
            mStagingEntry.targets.SetNull();
404
0
        }
405
0
    }
406
407
0
    return CHIP_NO_ERROR;
408
0
}
409
410
} // namespace app
411
} // namespace chip