Skip to content

Commit 058f199

Browse files
Add some APIs for defining local endpoints to Matter.framework. (#31673)
* Add some APIs for defining local endpoints to Matter.framework. These are not hooked up to anything yet, in the sense that you can create an MTREndpointDescription but then can't do anything with it. APIs on MTRDeviceController that take an MTREndpointDescription and hook up the endpoint to actually be exposed will come later. * Address review comments. * Fix some build/lint issues. * Address more review comments. * Addressing more review comments.
1 parent 4422581 commit 058f199

16 files changed

+1908
-1
lines changed

src/darwin/Framework/CHIP/Matter.h

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#define MTR_INCLUDED_FROM_UMBRELLA_HEADER
2121

22+
#import <Matter/MTRAccessGrant.h>
2223
#import <Matter/MTRAsyncCallbackWorkQueue.h>
2324
#import <Matter/MTRBackwardsCompatShims.h>
2425
#import <Matter/MTRBaseClusters.h>
@@ -45,6 +46,7 @@
4546
#import <Matter/MTRDeviceControllerParameters.h>
4647
#import <Matter/MTRDeviceControllerStartupParams.h>
4748
#import <Matter/MTRDeviceControllerStorageDelegate.h>
49+
#import <Matter/MTRDeviceTypeRevision.h>
4850
#import <Matter/MTRDiagnosticLogsType.h>
4951
#import <Matter/MTRError.h>
5052
#import <Matter/MTRFabricInfo.h>
@@ -56,6 +58,9 @@
5658
#import <Matter/MTROnboardingPayloadParser.h>
5759
#import <Matter/MTROperationalCertificateIssuer.h>
5860
#import <Matter/MTRQRCodeSetupPayloadParser.h>
61+
#import <Matter/MTRServerAttribute.h>
62+
#import <Matter/MTRServerCluster.h>
63+
#import <Matter/MTRServerEndpoint.h>
5964
#import <Matter/MTRSetupPayload.h>
6065
#import <Matter/MTRStorage.h>
6166
#import <Matter/MTRStructsObjc.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Copyright (c) 2024 Project CHIP Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
#import <Matter/MTRBaseClusters.h>
19+
#import <Matter/MTRDefines.h>
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
/**
24+
* An access grant, which can be represented as an entry in the Matter Access
25+
* Control cluster.
26+
*/
27+
MTR_NEWLY_AVAILABLE
28+
@interface MTRAccessGrant : NSObject <NSCopying>
29+
30+
- (instancetype)init NS_UNAVAILABLE;
31+
+ (instancetype)new NS_UNAVAILABLE;
32+
33+
/**
34+
* Grant access at the provided level to a specific node on the fabric. The
35+
* provided nodeID must be an operational node identifier.
36+
*/
37+
+ (nullable MTRAccessGrant *)accessGrantForNodeID:(NSNumber *)nodeID privilege:(MTRAccessControlEntryPrivilege)privilege;
38+
39+
/**
40+
* Grant access to any node on the fabric that has a matching CASE Authenticated
41+
* Tag in its operational certificate. The provided caseAuthenticatedTag must
42+
* be a 32-bit unsigned integer with lower 16 bits not 0, per the Matter
43+
* specification.
44+
*/
45+
+ (nullable MTRAccessGrant *)accessGrantForCASEAuthenticatedTag:(NSNumber *)caseAuthenticatedTag privilege:(MTRAccessControlEntryPrivilege)privilege;
46+
47+
/**
48+
* Grant access to any node on the fabric that is communicating with us via
49+
* group messages sent to the given group. The provided groupID must be a valid
50+
* group identifier in the range 1-65535.
51+
*/
52+
+ (nullable MTRAccessGrant *)accessGrantForGroupID:(NSNumber *)groupID privilege:(MTRAccessControlEntryPrivilege)privilege;
53+
54+
/**
55+
* Grant access to any node on the fabric, as long as it's communicating with us
56+
* over a unicast authenticated channel.
57+
*/
58+
+ (MTRAccessGrant *)accessGrantForAllNodesWithPrivilege:(MTRAccessControlEntryPrivilege)privilege;
59+
60+
/**
61+
* The matter access control subject ID that access has been granted for. Nil
62+
* when access has been granted for all subjects (e.g. via initForAllNodesWithPrivilege).
63+
*/
64+
@property (nonatomic, copy, readonly, nullable) NSNumber * subjectID;
65+
66+
/**
67+
* The privilege that has been granted
68+
*/
69+
@property (nonatomic, assign, readonly) MTRAccessControlEntryPrivilege grantedPrivilege;
70+
71+
/**
72+
* The type of authentication mode the access grant is
73+
* for. MTRAccessControlEntryAuthModeCASE for unicast messages and
74+
* MTRAccessControlEntryAuthModeGroup for groupcast ones.
75+
*/
76+
@property (nonatomic, assign, readonly) MTRAccessControlEntryAuthMode authenticationMode;
77+
78+
@end
79+
80+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Copyright (c) 2024 Project CHIP Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "MTRDefines_Internal.h"
18+
#import "MTRLogging_Internal.h"
19+
#import <Matter/MTRAccessGrant.h>
20+
21+
#include <lib/core/CASEAuthTag.h>
22+
#include <lib/core/GroupId.h>
23+
#include <lib/core/NodeId.h>
24+
#include <lib/support/SafeInt.h>
25+
26+
using namespace chip;
27+
28+
MTR_DIRECT_MEMBERS
29+
@implementation MTRAccessGrant
30+
31+
+ (nullable MTRAccessGrant *)accessGrantForNodeID:(NSNumber *)nodeID privilege:(MTRAccessControlEntryPrivilege)privilege
32+
{
33+
NodeId id = nodeID.unsignedLongLongValue;
34+
if (!IsOperationalNodeId(id)) {
35+
MTR_LOG_ERROR("MTRAccessGrant provided non-operational node ID: 0x%llx", id);
36+
return nil;
37+
}
38+
39+
return [[MTRAccessGrant alloc] initWithSubject:[nodeID copy] privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeCASE];
40+
}
41+
42+
+ (nullable MTRAccessGrant *)accessGrantForCASEAuthenticatedTag:(NSNumber *)caseAuthenticatedTag privilege:(MTRAccessControlEntryPrivilege)privilege
43+
{
44+
auto value = caseAuthenticatedTag.unsignedLongLongValue;
45+
if (!CanCastTo<CASEAuthTag>(value)) {
46+
MTR_LOG_ERROR("MTRAccessGrant provided too-large CAT value: 0x%llx", value);
47+
return nil;
48+
}
49+
50+
CASEAuthTag tag = static_cast<CASEAuthTag>(value);
51+
if (!IsValidCASEAuthTag(tag)) {
52+
MTR_LOG_ERROR("MTRAccessGrant provided invalid CAT value: 0x%" PRIx32, tag);
53+
return nil;
54+
}
55+
56+
return [[MTRAccessGrant alloc] initWithSubject:@(NodeIdFromCASEAuthTag(tag)) privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeCASE];
57+
}
58+
59+
+ (nullable MTRAccessGrant *)accessGrantForGroupID:(NSNumber *)groupID privilege:(MTRAccessControlEntryPrivilege)privilege
60+
{
61+
auto value = groupID.unsignedLongLongValue;
62+
if (!CanCastTo<GroupId>(value)) {
63+
MTR_LOG_ERROR("MTRAccessGrant provided too-large group id: 0x%llx", value);
64+
return nil;
65+
}
66+
67+
GroupId id = static_cast<GroupId>(value);
68+
if (!IsValidGroupId(id)) {
69+
MTR_LOG_ERROR("MTRAccessGrant provided invalid group id: 0x%" PRIx32, id);
70+
return nil;
71+
}
72+
73+
return [[MTRAccessGrant alloc] initWithSubject:@(NodeIdFromGroupId(id)) privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeGroup];
74+
}
75+
76+
+ (MTRAccessGrant *)accessGrantForAllNodesWithPrivilege:(MTRAccessControlEntryPrivilege)privilege
77+
{
78+
return [[MTRAccessGrant alloc] initWithSubject:nil privilege:privilege authenticationMode:MTRAccessControlEntryAuthModeCASE];
79+
}
80+
81+
// initWithSubject assumes that the subject has already been validated and, if
82+
// needed, copied from the input.
83+
- (nullable instancetype)initWithSubject:(nullable NSNumber *)subject privilege:(MTRAccessControlEntryPrivilege)privilege authenticationMode:(MTRAccessControlEntryAuthMode)authenticationMode
84+
{
85+
if (!(self = [super init])) {
86+
return nil;
87+
}
88+
89+
_subjectID = subject;
90+
_grantedPrivilege = privilege;
91+
_authenticationMode = authenticationMode;
92+
return self;
93+
}
94+
95+
- (id)copyWithZone:(NSZone *)zone
96+
{
97+
// We have no mutable state.
98+
return self;
99+
}
100+
101+
- (BOOL)isEqual:(id)object
102+
{
103+
if ([object class] != [self class]) {
104+
return NO;
105+
}
106+
107+
MTRAccessGrant * other = object;
108+
109+
BOOL sameSubjectID = (_subjectID == nil && other.subjectID == nil) || [_subjectID isEqual:other.subjectID];
110+
return sameSubjectID && _grantedPrivilege == other.grantedPrivilege && _authenticationMode == other.authenticationMode;
111+
}
112+
113+
- (NSUInteger)hash
114+
{
115+
return _subjectID.unsignedLongLongValue ^ _grantedPrivilege ^ _authenticationMode;
116+
}
117+
118+
- (NSString *)description
119+
{
120+
NSString * privilege = @"Unknown";
121+
switch (_grantedPrivilege) {
122+
case MTRAccessControlEntryPrivilegeView:
123+
privilege = @"View";
124+
break;
125+
case MTRAccessControlEntryPrivilegeProxyView:
126+
privilege = @"ProxyView";
127+
break;
128+
case MTRAccessControlEntryPrivilegeOperate:
129+
privilege = @"Operate";
130+
break;
131+
case MTRAccessControlEntryPrivilegeManage:
132+
privilege = @"Manage";
133+
break;
134+
case MTRAccessControlEntryPrivilegeAdminister:
135+
privilege = @"Administer";
136+
break;
137+
}
138+
139+
if (_subjectID == nil) {
140+
return [NSString stringWithFormat:@"<%@ all nodes can %@>", self.class, privilege];
141+
}
142+
143+
NodeId nodeId = static_cast<NodeId>(_subjectID.unsignedLongLongValue);
144+
if (IsGroupId(nodeId)) {
145+
return [NSString stringWithFormat:@"<%@ group 0x%x can %@>", self.class, GroupIdFromNodeId(nodeId), privilege];
146+
}
147+
148+
if (IsCASEAuthTag(nodeId)) {
149+
return [NSString stringWithFormat:@"<%@ nodes with CASE Authenticated Tag 0x%08x can %@>", self.class, CASEAuthTagFromNodeId(nodeId), privilege];
150+
}
151+
152+
return [NSString stringWithFormat:@"<%@ node 0x%016llx can %@>", self.class, nodeId, privilege];
153+
}
154+
155+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Copyright (c) 2024 Project CHIP Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
#import <Matter/MTRDefines.h>
19+
20+
NS_ASSUME_NONNULL_BEGIN
21+
22+
/**
23+
* A representation of a "device type revision" in the sense used in the Matter
24+
* specification. This has an identifier and a version number.
25+
*/
26+
MTR_NEWLY_AVAILABLE
27+
@interface MTRDeviceTypeRevision : NSObject <NSCopying>
28+
29+
- (instancetype)init NS_UNAVAILABLE;
30+
+ (instancetype)new NS_UNAVAILABLE;
31+
32+
/**
33+
* The provided deviceTypeID must be in the range 0xVVVV0000-0xVVVVBFFF, where
34+
* VVVV is the vendor identifier (0 for standard device types).
35+
*
36+
* The provided deviceTypeRevision must be in the range 1-65535.
37+
*/
38+
- (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision;
39+
40+
@property (nonatomic, copy, readonly) NSNumber * deviceTypeID;
41+
@property (nonatomic, copy, readonly) NSNumber * deviceTypeRevision;
42+
43+
@end
44+
45+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Copyright (c) 2024 Project CHIP Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "MTRDefines_Internal.h"
18+
#import "MTRLogging_Internal.h"
19+
#import <Matter/MTRDeviceTypeRevision.h>
20+
21+
#include <lib/core/CHIPError.h>
22+
#include <lib/core/DataModelTypes.h>
23+
#include <lib/support/SafeInt.h>
24+
25+
using namespace chip;
26+
27+
MTR_DIRECT_MEMBERS
28+
@implementation MTRDeviceTypeRevision
29+
30+
- (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision
31+
{
32+
auto deviceTypeIDValue = deviceTypeID.unsignedLongLongValue;
33+
if (!CanCastTo<DeviceTypeId>(deviceTypeIDValue)) {
34+
MTR_LOG_ERROR("MTRDeviceTypeRevision provided too-large device type ID: 0x%llx", deviceTypeIDValue);
35+
return nil;
36+
}
37+
38+
auto id = static_cast<DeviceTypeId>(deviceTypeIDValue);
39+
if (!IsValidDeviceTypeId(id)) {
40+
MTR_LOG_ERROR("MTRDeviceTypeRevision provided invalid device type ID: 0x%" PRIx32, id);
41+
return nil;
42+
}
43+
44+
auto revisionValue = revision.unsignedLongLongValue;
45+
if (!CanCastTo<uint16_t>(revisionValue) || revisionValue < 1) {
46+
MTR_LOG_ERROR("MTRDeviceTypeRevision provided invalid device type revision: 0x%llx", revisionValue);
47+
return nil;
48+
}
49+
50+
return [self initInternalWithDeviceTypeID:[deviceTypeID copy] revision:[revision copy]];
51+
}
52+
53+
// initInternalWithDeviceTypeID:revision assumes that the device type ID and device
54+
// revision have already been validated and, if needed, copied from the input.
55+
- (instancetype)initInternalWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision
56+
{
57+
if (!(self = [super init])) {
58+
return nil;
59+
}
60+
61+
_deviceTypeID = deviceTypeID;
62+
_deviceTypeRevision = revision;
63+
return self;
64+
}
65+
66+
- (id)copyWithZone:(NSZone *)zone
67+
{
68+
// We have no mutable state.
69+
return self;
70+
}
71+
72+
- (BOOL)isEqual:(id)object
73+
{
74+
if ([object class] != [self class]) {
75+
return NO;
76+
}
77+
78+
MTRDeviceTypeRevision * other = object;
79+
80+
return [_deviceTypeID isEqual:other.deviceTypeID] && [_deviceTypeRevision isEqual:other.deviceTypeRevision];
81+
}
82+
83+
- (NSUInteger)hash
84+
{
85+
return _deviceTypeID.unsignedLongValue ^ _deviceTypeRevision.unsignedShortValue;
86+
}
87+
88+
@end

0 commit comments

Comments
 (0)