Skip to content

Commit

Permalink
Merge pull request #3326 from rina23q/feature/3242/writable-device-id-3
Browse files Browse the repository at this point in the history
feat: support read-write device.id in tedge cert/connect
  • Loading branch information
rina23q authored Jan 28, 2025
2 parents e44a0ff + 2f61a0a commit 74947b0
Show file tree
Hide file tree
Showing 26 changed files with 1,486 additions and 234 deletions.
23 changes: 12 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

115 changes: 76 additions & 39 deletions crates/common/tedge_config/src/tedge_config_cli/tedge_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,8 @@ define_tedge_config! {
device: {
/// Identifier of the device within the fleet. It must be globally
/// unique and is derived from the device certificate.
#[tedge_config(readonly(
write_error = "\
The device id is read from the device certificate and cannot be set directly.\n\
To set 'device.id' to some <id>, you can use `tedge cert create --device-id <id>`.",
function = "device_id",
))]
#[tedge_config(reader(function = "device_id", private))]
#[tedge_config(example = "Raspberrypi-4d18303a-6d3a-11eb-b1a6-175f6bb72665")]
#[tedge_config(note = "This setting is derived from the device certificate and is therefore read only.")]
#[tedge_config(reader(private))]
#[doku(as = "String")]
id: Result<String, ReadError>,

Expand Down Expand Up @@ -215,14 +208,9 @@ define_tedge_config! {
device: {
/// Identifier of the device within the fleet. It must be globally
/// unique and is derived from the device certificate.
#[tedge_config(readonly(
write_error = "\
The device id is read from the device certificate and cannot be set directly.\n\
To set 'device.id' to some <id>, you can use `tedge cert create --device-id <id>`.",
function = "c8y_device_id",
))]
#[tedge_config(reader(function = "c8y_device_id"))]
#[tedge_config(default(from_optional_key = "device.id"))]
#[tedge_config(example = "Raspberrypi-4d18303a-6d3a-11eb-b1a6-175f6bb72665")]
#[tedge_config(note = "This setting is derived from the device certificate and is therefore read only.")]
#[doku(as = "String")]
id: Result<String, ReadError>,

Expand Down Expand Up @@ -411,14 +399,9 @@ define_tedge_config! {
device: {
/// Identifier of the device within the fleet. It must be globally
/// unique and is derived from the device certificate.
#[tedge_config(readonly(
write_error = "\
The device id is read from the device certificate and cannot be set directly.\n\
To set 'device.id' to some <id>, you can use `tedge cert create --device-id <id>`.",
function = "az_device_id",
))]
#[tedge_config(reader(function = "az_device_id"))]
#[tedge_config(default(from_optional_key = "device.id"))]
#[tedge_config(example = "Raspberrypi-4d18303a-6d3a-11eb-b1a6-175f6bb72665")]
#[tedge_config(note = "This setting is derived from the device certificate and is therefore read only.")]
#[doku(as = "String")]
id: Result<String, ReadError>,

Expand Down Expand Up @@ -481,14 +464,9 @@ define_tedge_config! {
device: {
/// Identifier of the device within the fleet. It must be globally
/// unique and is derived from the device certificate.
#[tedge_config(readonly(
write_error = "\
The device id is read from the device certificate and cannot be set directly.\n\
To set 'device.id' to some <id>, you can use `tedge cert create --device-id <id>`.",
function = "aws_device_id",
))]
#[tedge_config(reader(function = "aws_device_id"))]
#[tedge_config(default(from_optional_key = "device.id"))]
#[tedge_config(example = "Raspberrypi-4d18303a-6d3a-11eb-b1a6-175f6bb72665")]
#[tedge_config(note = "This setting is derived from the device certificate and is therefore read only.")]
#[doku(as = "String")]
id: Result<String, ReadError>,

Expand Down Expand Up @@ -929,6 +907,15 @@ impl TEdgeConfigReader {
Some(Cloud::Aws(profile)) => &self.aws.try_get(profile)?.device.cert_path,
})
}

pub fn device_id<'a>(&self, cloud: Option<impl Into<Cloud<'a>>>) -> Result<&str, ReadError> {
Ok(match cloud.map(<_>::into) {
None => self.device.id()?,
Some(Cloud::C8y(profile)) => self.c8y.try_get(profile)?.device.id()?,
Some(Cloud::Az(profile)) => self.az.try_get(profile)?.device.id()?,
Some(Cloud::Aws(profile)) => self.aws.try_get(profile)?.device.id()?,
})
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -1019,27 +1006,77 @@ fn default_http_bind_address(dto: &TEdgeConfigDto) -> IpAddr {

fn device_id_from_cert(cert_path: &Utf8Path) -> Result<String, ReadError> {
let pem = PemCertificate::from_pem_file(cert_path)
.map_err(|err| cert_error_into_config_error(ReadOnlyKey::DeviceId.to_cow_str(), err))?;
.map_err(|err| cert_error_into_config_error(ReadableKey::DeviceId.to_cow_str(), err))?;
let device_id = pem
.subject_common_name()
.map_err(|err| cert_error_into_config_error(ReadOnlyKey::DeviceId.to_cow_str(), err))?;
.map_err(|err| cert_error_into_config_error(ReadableKey::DeviceId.to_cow_str(), err))?;
Ok(device_id)
}

fn device_id(device: &TEdgeConfigReaderDevice) -> Result<String, ReadError> {
device_id_from_cert(&device.cert_path)
fn device_id(
device: &TEdgeConfigReaderDevice,
dto_value: &OptionalConfig<String>,
) -> Result<String, ReadError> {
match dto_value.or_none() {
Some(id) => Ok(id.clone()),
None => device_id_from_cert(&device.cert_path),
}
}

fn c8y_device_id(device: &TEdgeConfigReaderC8yDevice) -> Result<String, ReadError> {
device_id_from_cert(&device.cert_path)
fn c8y_device_id(
c8y_device: &TEdgeConfigReaderC8yDevice,
dto_value: &OptionalConfig<String>,
) -> Result<String, ReadError> {
match dto_value.or_none() {
Some(id) => Ok(id.clone()),
None => device_id_from_cert(&c8y_device.cert_path),
}
}

fn az_device_id(device: &TEdgeConfigReaderAzDevice) -> Result<String, ReadError> {
device_id_from_cert(&device.cert_path)
fn az_device_id(
az_device: &TEdgeConfigReaderAzDevice,
dto_value: &OptionalConfig<String>,
) -> Result<String, ReadError> {
match dto_value.or_none() {
Some(id) => Ok(id.clone()),
None => device_id_from_cert(&az_device.cert_path),
}
}

fn aws_device_id(device: &TEdgeConfigReaderAwsDevice) -> Result<String, ReadError> {
device_id_from_cert(&device.cert_path)
fn aws_device_id(
aws_device: &TEdgeConfigReaderAwsDevice,
dto_value: &OptionalConfig<String>,
) -> Result<String, ReadError> {
match dto_value.or_none() {
Some(id) => Ok(id.clone()),
None => device_id_from_cert(&aws_device.cert_path),
}
}

pub fn explicit_device_id(
config_location: &TEdgeConfigLocation,
cloud: &Option<Cloud>,
) -> Option<String> {
let dto = config_location.load_dto_from_toml_and_env().ok()?;

match cloud {
None => dto.device.id.clone(),
Some(Cloud::C8y(profile)) => {
let key = profile.map(|name| name.to_string());
let c8y_dto = dto.c8y.try_get(key.as_deref(), "c8y").ok()?;
c8y_dto.device.id.clone()
}
Some(Cloud::Az(profile)) => {
let key = profile.map(|name| name.to_string());
let az_dto = dto.az.try_get(key.as_deref(), "az").ok()?;
az_dto.device.id.clone()
}
Some(Cloud::Aws(profile)) => {
let key = profile.map(|name| name.to_string());
let aws_dto = dto.aws.try_get(key.as_deref(), "aws").ok()?;
aws_dto.device.id.clone()
}
}
}

fn cert_error_into_config_error(key: Cow<'static, str>, err: CertificateError) -> ReadError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ impl TEdgeConfigLocation {
Ok(TEdgeConfig::from_dto(&dto, self))
}

pub fn load_dto_from_toml_and_env(&self) -> Result<TEdgeConfigDto, TEdgeConfigError> {
self.load_dto::<FileAndEnvironment>(self.toml_path())
}

fn load_dto<Sources: ConfigSources>(
&self,
path: &Utf8Path,
Expand Down
1 change: 1 addition & 0 deletions crates/common/tedge_config_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tracing = { workspace = true }
url = { workspace = true }

[dev-dependencies]
certificate = { workspace = true, features = ["reqwest"] }
clap = { workspace = true }
serde = { workspace = true, features = ["rc"] }
serde_json = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/common/tedge_config_macros/examples/macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ define_tedge_config! {
}
}

fn device_id(_reader: &TEdgeConfigReaderDevice) -> Result<String, ReadError> {
fn device_id(_reader: &TEdgeConfigReaderDevice, _: &()) -> Result<String, ReadError> {
Ok("dummy-device-id".to_owned())
}

Expand Down
6 changes: 3 additions & 3 deletions crates/common/tedge_config_macros/examples/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ fn main() {
keys,
[
"c8y.http",
"c8y.smartrest.use_operation_id",
"c8y.url",
"c8y.profiles.cloud.http",
"c8y.profiles.cloud.smartrest.use_operation_id",
"c8y.profiles.cloud.url",
"c8y.profiles.edge.http",
"c8y.profiles.edge.smartrest.use_operation_id",
"c8y.profiles.edge.url"
"c8y.profiles.edge.url",
"c8y.smartrest.use_operation_id",
"c8y.url",
]
);
}
Loading

0 comments on commit 74947b0

Please sign in to comment.