1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum AccessControlEntryAuthMode {
19 Pase = 1,
21 Case = 2,
23 Group = 3,
25}
26
27impl AccessControlEntryAuthMode {
28 pub fn from_u8(value: u8) -> Option<Self> {
30 match value {
31 1 => Some(AccessControlEntryAuthMode::Pase),
32 2 => Some(AccessControlEntryAuthMode::Case),
33 3 => Some(AccessControlEntryAuthMode::Group),
34 _ => None,
35 }
36 }
37
38 pub fn to_u8(self) -> u8 {
40 self as u8
41 }
42}
43
44impl From<AccessControlEntryAuthMode> for u8 {
45 fn from(val: AccessControlEntryAuthMode) -> Self {
46 val as u8
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
51#[repr(u8)]
52pub enum AccessControlEntryPrivilege {
53 View = 1,
55 Proxyview = 2,
56 Operate = 3,
58 Manage = 4,
60 Administer = 5,
62}
63
64impl AccessControlEntryPrivilege {
65 pub fn from_u8(value: u8) -> Option<Self> {
67 match value {
68 1 => Some(AccessControlEntryPrivilege::View),
69 2 => Some(AccessControlEntryPrivilege::Proxyview),
70 3 => Some(AccessControlEntryPrivilege::Operate),
71 4 => Some(AccessControlEntryPrivilege::Manage),
72 5 => Some(AccessControlEntryPrivilege::Administer),
73 _ => None,
74 }
75 }
76
77 pub fn to_u8(self) -> u8 {
79 self as u8
80 }
81}
82
83impl From<AccessControlEntryPrivilege> for u8 {
84 fn from(val: AccessControlEntryPrivilege) -> Self {
85 val as u8
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
90#[repr(u8)]
91pub enum AccessRestrictionType {
92 Attributeaccessforbidden = 0,
94 Attributewriteforbidden = 1,
96 Commandforbidden = 2,
98 Eventforbidden = 3,
100}
101
102impl AccessRestrictionType {
103 pub fn from_u8(value: u8) -> Option<Self> {
105 match value {
106 0 => Some(AccessRestrictionType::Attributeaccessforbidden),
107 1 => Some(AccessRestrictionType::Attributewriteforbidden),
108 2 => Some(AccessRestrictionType::Commandforbidden),
109 3 => Some(AccessRestrictionType::Eventforbidden),
110 _ => None,
111 }
112 }
113
114 pub fn to_u8(self) -> u8 {
116 self as u8
117 }
118}
119
120impl From<AccessRestrictionType> for u8 {
121 fn from(val: AccessRestrictionType) -> Self {
122 val as u8
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
127#[repr(u8)]
128pub enum ChangeType {
129 Changed = 0,
131 Added = 1,
133 Removed = 2,
135}
136
137impl ChangeType {
138 pub fn from_u8(value: u8) -> Option<Self> {
140 match value {
141 0 => Some(ChangeType::Changed),
142 1 => Some(ChangeType::Added),
143 2 => Some(ChangeType::Removed),
144 _ => None,
145 }
146 }
147
148 pub fn to_u8(self) -> u8 {
150 self as u8
151 }
152}
153
154impl From<ChangeType> for u8 {
155 fn from(val: ChangeType) -> Self {
156 val as u8
157 }
158}
159
160#[derive(Debug, serde::Serialize)]
163pub struct AccessControlEntry {
164 pub privilege: Option<AccessControlEntryPrivilege>,
165 pub auth_mode: Option<AccessControlEntryAuthMode>,
166 pub subjects: Option<Vec<u64>>,
167 pub targets: Option<Vec<AccessControlTarget>>,
168}
169
170#[derive(Debug, serde::Serialize)]
171pub struct AccessControlExtension {
172 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
173 pub data: Option<Vec<u8>>,
174}
175
176#[derive(Debug, serde::Serialize)]
177pub struct AccessControlTarget {
178 pub cluster: Option<u32>,
179 pub endpoint: Option<u16>,
180 pub device_type: Option<u32>,
181}
182
183#[derive(Debug, serde::Serialize)]
184pub struct AccessRestrictionEntry {
185 pub endpoint: Option<u16>,
186 pub cluster: Option<u32>,
187 pub restrictions: Option<Vec<AccessRestriction>>,
188}
189
190#[derive(Debug, serde::Serialize)]
191pub struct AccessRestriction {
192 pub type_: Option<AccessRestrictionType>,
193 pub id: Option<u32>,
194}
195
196#[derive(Debug, serde::Serialize)]
197pub struct CommissioningAccessRestrictionEntry {
198 pub endpoint: Option<u16>,
199 pub cluster: Option<u32>,
200 pub restrictions: Option<Vec<AccessRestriction>>,
201}
202
203pub fn encode_review_fabric_restrictions(arl: Vec<CommissioningAccessRestrictionEntry>) -> anyhow::Result<Vec<u8>> {
207 let tlv = tlv::TlvItemEnc {
208 tag: 0,
209 value: tlv::TlvItemValueEnc::StructInvisible(vec![
210 (0, tlv::TlvItemValueEnc::Array(arl.into_iter().map(|v| {
211 let mut fields = Vec::new();
212 if let Some(x) = v.endpoint { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
213 if let Some(x) = v.cluster { fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
214 if let Some(listv) = v.restrictions {
215 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
216 let mut nested_fields = Vec::new();
217 if let Some(x) = inner.type_ { nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
218 if let Some(x) = inner.id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
219 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
220 }).collect();
221 fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
222 }
223 (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
224 }).collect())).into(),
225 ]),
226 };
227 Ok(tlv.encode()?)
228}
229
230pub fn decode_acl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlEntry>> {
234 let mut res = Vec::new();
235 if let tlv::TlvItemValue::List(v) = inp {
236 for item in v {
237 res.push(AccessControlEntry {
238 privilege: item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
239 auth_mode: item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
240 subjects: {
241 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
242 let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
243 Some(items)
244 } else {
245 None
246 }
247 },
248 targets: {
249 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
250 let mut items = Vec::new();
251 for list_item in l {
252 items.push(AccessControlTarget {
253 cluster: list_item.get_int(&[0]).map(|v| v as u32),
254 endpoint: list_item.get_int(&[1]).map(|v| v as u16),
255 device_type: list_item.get_int(&[2]).map(|v| v as u32),
256 });
257 }
258 Some(items)
259 } else {
260 None
261 }
262 },
263 });
264 }
265 }
266 Ok(res)
267}
268
269pub fn decode_extension(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlExtension>> {
271 let mut res = Vec::new();
272 if let tlv::TlvItemValue::List(v) = inp {
273 for item in v {
274 res.push(AccessControlExtension {
275 data: item.get_octet_string_owned(&[1]),
276 });
277 }
278 }
279 Ok(res)
280}
281
282pub fn decode_subjects_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
284 if let tlv::TlvItemValue::Int(v) = inp {
285 Ok(*v as u16)
286 } else {
287 Err(anyhow::anyhow!("Expected UInt16"))
288 }
289}
290
291pub fn decode_targets_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
293 if let tlv::TlvItemValue::Int(v) = inp {
294 Ok(*v as u16)
295 } else {
296 Err(anyhow::anyhow!("Expected UInt16"))
297 }
298}
299
300pub fn decode_access_control_entries_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
302 if let tlv::TlvItemValue::Int(v) = inp {
303 Ok(*v as u16)
304 } else {
305 Err(anyhow::anyhow!("Expected UInt16"))
306 }
307}
308
309pub fn decode_commissioning_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CommissioningAccessRestrictionEntry>> {
311 let mut res = Vec::new();
312 if let tlv::TlvItemValue::List(v) = inp {
313 for item in v {
314 res.push(CommissioningAccessRestrictionEntry {
315 endpoint: item.get_int(&[0]).map(|v| v as u16),
316 cluster: item.get_int(&[1]).map(|v| v as u32),
317 restrictions: {
318 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
319 let mut items = Vec::new();
320 for list_item in l {
321 items.push(AccessRestriction {
322 type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
323 id: list_item.get_int(&[1]).map(|v| v as u32),
324 });
325 }
326 Some(items)
327 } else {
328 None
329 }
330 },
331 });
332 }
333 }
334 Ok(res)
335}
336
337pub fn decode_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessRestrictionEntry>> {
339 let mut res = Vec::new();
340 if let tlv::TlvItemValue::List(v) = inp {
341 for item in v {
342 res.push(AccessRestrictionEntry {
343 endpoint: item.get_int(&[0]).map(|v| v as u16),
344 cluster: item.get_int(&[1]).map(|v| v as u32),
345 restrictions: {
346 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
347 let mut items = Vec::new();
348 for list_item in l {
349 items.push(AccessRestriction {
350 type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
351 id: list_item.get_int(&[1]).map(|v| v as u32),
352 });
353 }
354 Some(items)
355 } else {
356 None
357 }
358 },
359 });
360 }
361 }
362 Ok(res)
363}
364
365
366pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
378 if cluster_id != 0x001F {
380 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x001F, got {}\"}}", cluster_id);
381 }
382
383 match attribute_id {
384 0x0000 => {
385 match decode_acl(tlv_value) {
386 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
387 Err(e) => format!("{{\"error\": \"{}\"}}", e),
388 }
389 }
390 0x0001 => {
391 match decode_extension(tlv_value) {
392 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
393 Err(e) => format!("{{\"error\": \"{}\"}}", e),
394 }
395 }
396 0x0002 => {
397 match decode_subjects_per_access_control_entry(tlv_value) {
398 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
399 Err(e) => format!("{{\"error\": \"{}\"}}", e),
400 }
401 }
402 0x0003 => {
403 match decode_targets_per_access_control_entry(tlv_value) {
404 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
405 Err(e) => format!("{{\"error\": \"{}\"}}", e),
406 }
407 }
408 0x0004 => {
409 match decode_access_control_entries_per_fabric(tlv_value) {
410 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
411 Err(e) => format!("{{\"error\": \"{}\"}}", e),
412 }
413 }
414 0x0005 => {
415 match decode_commissioning_arl(tlv_value) {
416 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
417 Err(e) => format!("{{\"error\": \"{}\"}}", e),
418 }
419 }
420 0x0006 => {
421 match decode_arl(tlv_value) {
422 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
423 Err(e) => format!("{{\"error\": \"{}\"}}", e),
424 }
425 }
426 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
427 }
428}
429
430pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
435 vec![
436 (0x0000, "ACL"),
437 (0x0001, "Extension"),
438 (0x0002, "SubjectsPerAccessControlEntry"),
439 (0x0003, "TargetsPerAccessControlEntry"),
440 (0x0004, "AccessControlEntriesPerFabric"),
441 (0x0005, "CommissioningARL"),
442 (0x0006, "ARL"),
443 ]
444}
445
446#[derive(Debug, serde::Serialize)]
447pub struct ReviewFabricRestrictionsResponse {
448 pub token: Option<u64>,
449}
450
451pub fn decode_review_fabric_restrictions_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ReviewFabricRestrictionsResponse> {
455 if let tlv::TlvItemValue::List(_fields) = inp {
456 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
457 Ok(ReviewFabricRestrictionsResponse {
458 token: item.get_int(&[0]),
459 })
460 } else {
461 Err(anyhow::anyhow!("Expected struct fields"))
462 }
463}
464
465#[derive(Debug, serde::Serialize)]
466pub struct AccessControlEntryChangedEvent {
467 pub admin_node_id: Option<u64>,
468 pub admin_passcode_id: Option<u16>,
469 pub change_type: Option<ChangeType>,
470 pub latest_value: Option<AccessControlEntry>,
471}
472
473#[derive(Debug, serde::Serialize)]
474pub struct AccessControlExtensionChangedEvent {
475 pub admin_node_id: Option<u64>,
476 pub admin_passcode_id: Option<u16>,
477 pub change_type: Option<ChangeType>,
478 pub latest_value: Option<AccessControlExtension>,
479}
480
481#[derive(Debug, serde::Serialize)]
482pub struct FabricRestrictionReviewUpdateEvent {
483 pub token: Option<u64>,
484 pub instruction: Option<String>,
485 pub arl_request_flow_url: Option<String>,
486}
487
488pub fn decode_access_control_entry_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlEntryChangedEvent> {
492 if let tlv::TlvItemValue::List(_fields) = inp {
493 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
494 Ok(AccessControlEntryChangedEvent {
495 admin_node_id: item.get_int(&[1]),
496 admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
497 change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
498 latest_value: {
499 if let Some(nested_tlv) = item.get(&[4]) {
500 if let tlv::TlvItemValue::List(_) = nested_tlv {
501 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
502 Some(AccessControlEntry {
503 privilege: nested_item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
504 auth_mode: nested_item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
505 subjects: {
506 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[3]) {
507 let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
508 Some(items)
509 } else {
510 None
511 }
512 },
513 targets: {
514 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[4]) {
515 let mut items = Vec::new();
516 for list_item in l {
517 items.push(AccessControlTarget {
518 cluster: list_item.get_int(&[0]).map(|v| v as u32),
519 endpoint: list_item.get_int(&[1]).map(|v| v as u16),
520 device_type: list_item.get_int(&[2]).map(|v| v as u32),
521 });
522 }
523 Some(items)
524 } else {
525 None
526 }
527 },
528 })
529 } else {
530 None
531 }
532 } else {
533 None
534 }
535 },
536 })
537 } else {
538 Err(anyhow::anyhow!("Expected struct fields"))
539 }
540}
541
542pub fn decode_access_control_extension_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlExtensionChangedEvent> {
544 if let tlv::TlvItemValue::List(_fields) = inp {
545 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
546 Ok(AccessControlExtensionChangedEvent {
547 admin_node_id: item.get_int(&[1]),
548 admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
549 change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
550 latest_value: {
551 if let Some(nested_tlv) = item.get(&[4]) {
552 if let tlv::TlvItemValue::List(_) = nested_tlv {
553 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
554 Some(AccessControlExtension {
555 data: nested_item.get_octet_string_owned(&[1]),
556 })
557 } else {
558 None
559 }
560 } else {
561 None
562 }
563 },
564 })
565 } else {
566 Err(anyhow::anyhow!("Expected struct fields"))
567 }
568}
569
570pub fn decode_fabric_restriction_review_update_event(inp: &tlv::TlvItemValue) -> anyhow::Result<FabricRestrictionReviewUpdateEvent> {
572 if let tlv::TlvItemValue::List(_fields) = inp {
573 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
574 Ok(FabricRestrictionReviewUpdateEvent {
575 token: item.get_int(&[0]),
576 instruction: item.get_string_owned(&[1]),
577 arl_request_flow_url: item.get_string_owned(&[2]),
578 })
579 } else {
580 Err(anyhow::anyhow!("Expected struct fields"))
581 }
582}
583