use crate::tlv;
use anyhow;
use serde_json;
use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum AccessControlEntryAuthMode {
Pase = 1,
Case = 2,
Group = 3,
}
impl AccessControlEntryAuthMode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(AccessControlEntryAuthMode::Pase),
2 => Some(AccessControlEntryAuthMode::Case),
3 => Some(AccessControlEntryAuthMode::Group),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<AccessControlEntryAuthMode> for u8 {
fn from(val: AccessControlEntryAuthMode) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum AccessControlEntryPrivilege {
View = 1,
Proxyview = 2,
Operate = 3,
Manage = 4,
Administer = 5,
}
impl AccessControlEntryPrivilege {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(AccessControlEntryPrivilege::View),
2 => Some(AccessControlEntryPrivilege::Proxyview),
3 => Some(AccessControlEntryPrivilege::Operate),
4 => Some(AccessControlEntryPrivilege::Manage),
5 => Some(AccessControlEntryPrivilege::Administer),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<AccessControlEntryPrivilege> for u8 {
fn from(val: AccessControlEntryPrivilege) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum AccessRestrictionType {
Attributeaccessforbidden = 0,
Attributewriteforbidden = 1,
Commandforbidden = 2,
Eventforbidden = 3,
}
impl AccessRestrictionType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(AccessRestrictionType::Attributeaccessforbidden),
1 => Some(AccessRestrictionType::Attributewriteforbidden),
2 => Some(AccessRestrictionType::Commandforbidden),
3 => Some(AccessRestrictionType::Eventforbidden),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<AccessRestrictionType> for u8 {
fn from(val: AccessRestrictionType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ChangeType {
Changed = 0,
Added = 1,
Removed = 2,
}
impl ChangeType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ChangeType::Changed),
1 => Some(ChangeType::Added),
2 => Some(ChangeType::Removed),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ChangeType> for u8 {
fn from(val: ChangeType) -> Self {
val as u8
}
}
#[derive(Debug, serde::Serialize)]
pub struct AccessControlEntry {
pub privilege: Option<AccessControlEntryPrivilege>,
pub auth_mode: Option<AccessControlEntryAuthMode>,
pub subjects: Option<Vec<u64>>,
pub targets: Option<Vec<AccessControlTarget>>,
}
#[derive(Debug, serde::Serialize)]
pub struct AccessControlExtension {
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub data: Option<Vec<u8>>,
}
#[derive(Debug, serde::Serialize)]
pub struct AccessControlTarget {
pub cluster: Option<u32>,
pub endpoint: Option<u16>,
pub device_type: Option<u32>,
}
#[derive(Debug, serde::Serialize)]
pub struct AccessRestrictionEntry {
pub endpoint: Option<u16>,
pub cluster: Option<u32>,
pub restrictions: Option<Vec<AccessRestriction>>,
}
#[derive(Debug, serde::Serialize)]
pub struct AccessRestriction {
pub type_: Option<AccessRestrictionType>,
pub id: Option<u32>,
}
#[derive(Debug, serde::Serialize)]
pub struct CommissioningAccessRestrictionEntry {
pub endpoint: Option<u16>,
pub cluster: Option<u32>,
pub restrictions: Option<Vec<AccessRestriction>>,
}
pub fn encode_review_fabric_restrictions(arl: Vec<CommissioningAccessRestrictionEntry>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::Array(arl.into_iter().map(|v| {
let mut fields = Vec::new();
if let Some(x) = v.endpoint { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = v.cluster { fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
if let Some(listv) = v.restrictions {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.type_ { nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
if let Some(x) = inner.id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
(0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
}).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_acl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(AccessControlEntry {
privilege: item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
auth_mode: item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
subjects: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
Some(items)
} else {
None
}
},
targets: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
let mut items = Vec::new();
for list_item in l {
items.push(AccessControlTarget {
cluster: list_item.get_int(&[0]).map(|v| v as u32),
endpoint: list_item.get_int(&[1]).map(|v| v as u16),
device_type: list_item.get_int(&[2]).map(|v| v as u32),
});
}
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_extension(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlExtension>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(AccessControlExtension {
data: item.get_octet_string_owned(&[1]),
});
}
}
Ok(res)
}
pub fn decode_subjects_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_targets_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_access_control_entries_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_commissioning_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CommissioningAccessRestrictionEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(CommissioningAccessRestrictionEntry {
endpoint: item.get_int(&[0]).map(|v| v as u16),
cluster: item.get_int(&[1]).map(|v| v as u32),
restrictions: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let mut items = Vec::new();
for list_item in l {
items.push(AccessRestriction {
type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
id: list_item.get_int(&[1]).map(|v| v as u32),
});
}
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessRestrictionEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(AccessRestrictionEntry {
endpoint: item.get_int(&[0]).map(|v| v as u16),
cluster: item.get_int(&[1]).map(|v| v as u32),
restrictions: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let mut items = Vec::new();
for list_item in l {
items.push(AccessRestriction {
type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
id: list_item.get_int(&[1]).map(|v| v as u32),
});
}
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x001F {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x001F, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_acl(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_extension(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_subjects_per_access_control_entry(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_targets_per_access_control_entry(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_access_control_entries_per_fabric(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_commissioning_arl(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_arl(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
_ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
}
}
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "ACL"),
(0x0001, "Extension"),
(0x0002, "SubjectsPerAccessControlEntry"),
(0x0003, "TargetsPerAccessControlEntry"),
(0x0004, "AccessControlEntriesPerFabric"),
(0x0005, "CommissioningARL"),
(0x0006, "ARL"),
]
}
#[derive(Debug, serde::Serialize)]
pub struct ReviewFabricRestrictionsResponse {
pub token: Option<u64>,
}
pub fn decode_review_fabric_restrictions_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ReviewFabricRestrictionsResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(ReviewFabricRestrictionsResponse {
token: item.get_int(&[0]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
#[derive(Debug, serde::Serialize)]
pub struct AccessControlEntryChangedEvent {
pub admin_node_id: Option<u64>,
pub admin_passcode_id: Option<u16>,
pub change_type: Option<ChangeType>,
pub latest_value: Option<AccessControlEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct AccessControlExtensionChangedEvent {
pub admin_node_id: Option<u64>,
pub admin_passcode_id: Option<u16>,
pub change_type: Option<ChangeType>,
pub latest_value: Option<AccessControlExtension>,
}
#[derive(Debug, serde::Serialize)]
pub struct FabricRestrictionReviewUpdateEvent {
pub token: Option<u64>,
pub instruction: Option<String>,
pub arl_request_flow_url: Option<String>,
}
pub fn decode_access_control_entry_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlEntryChangedEvent> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(AccessControlEntryChangedEvent {
admin_node_id: item.get_int(&[1]),
admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
latest_value: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(AccessControlEntry {
privilege: nested_item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
auth_mode: nested_item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
subjects: {
if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[3]) {
let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
Some(items)
} else {
None
}
},
targets: {
if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[4]) {
let mut items = Vec::new();
for list_item in l {
items.push(AccessControlTarget {
cluster: list_item.get_int(&[0]).map(|v| v as u32),
endpoint: list_item.get_int(&[1]).map(|v| v as u16),
device_type: list_item.get_int(&[2]).map(|v| v as u32),
});
}
Some(items)
} else {
None
}
},
})
} else {
None
}
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_access_control_extension_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlExtensionChangedEvent> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(AccessControlExtensionChangedEvent {
admin_node_id: item.get_int(&[1]),
admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
latest_value: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(AccessControlExtension {
data: nested_item.get_octet_string_owned(&[1]),
})
} else {
None
}
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_fabric_restriction_review_update_event(inp: &tlv::TlvItemValue) -> anyhow::Result<FabricRestrictionReviewUpdateEvent> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(FabricRestrictionReviewUpdateEvent {
token: item.get_int(&[0]),
instruction: item.get_string_owned(&[1]),
arl_request_flow_url: item.get_string_owned(&[2]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}