diff --git a/aziotctl/aziotctl-common/src/config/apply.rs b/aziotctl/aziotctl-common/src/config/apply.rs index d25051d98..e7dbdc5cd 100644 --- a/aziotctl/aziotctl-common/src/config/apply.rs +++ b/aziotctl/aziotctl-common/src/config/apply.rs @@ -27,6 +27,7 @@ pub fn run( cloud_timeout_sec, cloud_retries, aziot_max_requests, + prefer_module_identity_cache, mut aziot_keys, mut preloaded_keys, cert_issuance, @@ -343,6 +344,8 @@ pub fn run( homedir: super::AZIOT_IDENTITYD_HOMEDIR_PATH.into(), + prefer_module_identity_cache, + max_requests: aziot_max_requests.identityd, cloud_timeout_sec, diff --git a/aziotctl/aziotctl-common/src/config/super_config.rs b/aziotctl/aziotctl-common/src/config/super_config.rs index 1dc777ffb..9d8f09779 100644 --- a/aziotctl/aziotctl-common/src/config/super_config.rs +++ b/aziotctl/aziotctl-common/src/config/super_config.rs @@ -54,6 +54,9 @@ pub struct Config { #[serde(default, skip_serializing_if = "AziotMaxRequests::is_default")] pub aziot_max_requests: AziotMaxRequests, + #[serde(default)] + pub prefer_module_identity_cache: bool, + pub provisioning: Provisioning, pub localid: Option, diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key-no-pad/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key-no-pad/identityd.toml index 066308e34..fe1042b1c 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key-no-pad/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key-no-pad/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key/identityd.toml index 066308e34..fe1042b1c 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-symmetric-key/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-tpm/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-tpm/identityd.toml index 2758e8616..b2c8fc1a8 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-tpm/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-tpm/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-est-bootstrap-auto-renew/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-est-bootstrap-auto-renew/identityd.toml index c7551a169..04eda99c3 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-est-bootstrap-auto-renew/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-est-bootstrap-auto-renew/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-bootstrap/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-bootstrap/identityd.toml index 5a5b67fb6..d474889f6 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-bootstrap/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-bootstrap/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-custom-bootstrap/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-custom-bootstrap/identityd.toml index 3cb1cc354..a73820fd1 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-custom-bootstrap/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-custom-bootstrap/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-subject-dn-bootstrap/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-subject-dn-bootstrap/identityd.toml index 94962d3cb..5e1c05ff1 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-subject-dn-bootstrap/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est-subject-dn-bootstrap/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est/identityd.toml index 3cb1cc354..a73820fd1 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-est/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-localca/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-localca/identityd.toml index 3cb1cc354..a73820fd1 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-localca/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11-localca/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11/identityd.toml index 3cb1cc354..a73820fd1 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509-pkcs11/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/dps-x509/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/dps-x509/identityd.toml index 3cb1cc354..a73820fd1 100644 --- a/aziotctl/aziotctl-common/test-files/apply/dps-x509/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/dps-x509/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "dps" diff --git a/aziotctl/aziotctl-common/test-files/apply/local-gateway/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/local-gateway/identityd.toml index 5f47a9981..e1df71532 100644 --- a/aziotctl/aziotctl-common/test-files/apply/local-gateway/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/local-gateway/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] local_gateway_hostname = "my-gateway-device" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/config.toml b/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/config.toml index 451e3cd63..2d3dd5d10 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/config.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/config.toml @@ -1,3 +1,5 @@ +prefer_module_identity_cache = true + [provisioning] source = "manual" connection_string = "HostName=example.azure-devices.net;DeviceId=my-device;SharedAccessKey=YXppb3QtaWRlbnRpdHktc2VydmljZXxhemlvdC1pZGU=" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/identityd.toml index c69dc823b..7da5b05ec 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-connection-string/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = true [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key-no-pad/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key-no-pad/identityd.toml index c69dc823b..df33f4b60 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key-no-pad/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key-no-pad/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key/identityd.toml index c69dc823b..df33f4b60 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-symmetric-key/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom-http/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom-http/identityd.toml index 855d1742b..c417bf667 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom-http/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom-http/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom/identityd.toml index 855d1742b..c417bf667 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-custom/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-subject-dn/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-subject-dn/identityd.toml index a6f112fc5..5be17bd72 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-subject-dn/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-x509-est-subject-dn/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-x509-pkcs11/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-x509-pkcs11/identityd.toml index 855d1742b..c417bf667 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-x509-pkcs11/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-x509-pkcs11/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/manual-x509/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/manual-x509/identityd.toml index 855d1742b..c417bf667 100644 --- a/aziotctl/aziotctl-common/test-files/apply/manual-x509/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/manual-x509/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false [provisioning] source = "manual" diff --git a/aziotctl/aziotctl-common/test-files/apply/throttle-limits/identityd.toml b/aziotctl/aziotctl-common/test-files/apply/throttle-limits/identityd.toml index 7d916f651..ee84d3868 100644 --- a/aziotctl/aziotctl-common/test-files/apply/throttle-limits/identityd.toml +++ b/aziotctl/aziotctl-common/test-files/apply/throttle-limits/identityd.toml @@ -1,5 +1,6 @@ hostname = "my-device" homedir = "/var/lib/aziot/identityd" +prefer_module_identity_cache = false max_requests = 50 [provisioning] diff --git a/aziotctl/config/unix/template.toml b/aziotctl/config/unix/template.toml index 5b28edb51..d42494b53 100644 --- a/aziotctl/config/unix/template.toml +++ b/aziotctl/config/unix/template.toml @@ -29,15 +29,29 @@ # cloud_retries controls how many times a request may be retried should it fail. # The client will always send at least one attempt, so its value will be the number # of retries after the first attempt should that fail (i.e. cloud_retries = 2 -# means that the client will make a total of 3 attempts). +# means that the client will make a total of 3 attempts). # -# cloud_timeout_sec has a minimum of 70s to allow hub to throttle requests. +# cloud_timeout_sec has a minimum of 70s to allow hub to throttle requests. # If a request is throttled, it will enter an exponential backoff with 4 retries instead # of using the configured value. The configured value is used for all other errors. # # cloud_timeout_sec = 70 # cloud_retries = 1 +# ============================================================================== +# Module identity cache preference +# ============================================================================== +# +# The default behavior is to request module identities from IoT Hub and fall back to a +# cached backup if the Hub request fails. This keeps identities in sync with IoT Hub, +# but results in extra requests to Hub that may not be necessary depending on use case. +# +# Setting prefer_module_identity_cache to true reverses the behavior so that the cached +# identities are preferred to IoT Hub requests. Requests to Hub are still made if identities +# are not found in the cache. +# +# prefer_module_identity_cache = false + # ============================================================================== # Provisioning # ============================================================================== diff --git a/aziotctl/src/config/mp.rs b/aziotctl/src/config/mp.rs index e68adc728..a6d08abbd 100644 --- a/aziotctl/src/config/mp.rs +++ b/aziotctl/src/config/mp.rs @@ -75,6 +75,8 @@ To reconfigure IoT Identity Service, run: aziot_max_requests: Default::default(), + prefer_module_identity_cache: Default::default(), + aziot_keys: Default::default(), preloaded_keys: Default::default(), diff --git a/aziotctl/src/internal/check/checks/host_connect_iothub.rs b/aziotctl/src/internal/check/checks/host_connect_iothub.rs index fac57f9d3..3642a17e5 100644 --- a/aziotctl/src/internal/check/checks/host_connect_iothub.rs +++ b/aziotctl/src/internal/check/checks/host_connect_iothub.rs @@ -219,6 +219,7 @@ mod tests { max_requests: 10, cloud_retries: 1, cloud_timeout_sec: 1, + prefer_module_identity_cache: false, provisioning: device_provisioning, principal: Vec::new(), endpoints: Endpoints::default(), diff --git a/identity/aziot-identityd-config/src/lib.rs b/identity/aziot-identityd-config/src/lib.rs index 239ac1bc9..418541137 100644 --- a/identity/aziot-identityd-config/src/lib.rs +++ b/identity/aziot-identityd-config/src/lib.rs @@ -21,6 +21,9 @@ pub struct Settings { pub homedir: std::path::PathBuf, + #[serde(default)] + pub prefer_module_identity_cache: bool, + /// Maximum number of simultaneous requests per user that identityd will service. #[serde( default = "http_common::Incoming::default_max_requests", diff --git a/identity/aziot-identityd/src/identity.rs b/identity/aziot-identityd/src/identity.rs index 5fa339b2e..71ec2c39c 100644 --- a/identity/aziot-identityd/src/identity.rs +++ b/identity/aziot-identityd/src/identity.rs @@ -4,6 +4,7 @@ use std::fmt::Write; use std::path::{Path, PathBuf}; use std::sync::Arc; +use aziot_identity_common::{hub::Module, Identity, IoTHubDevice}; use aziot_identityd_config as config; use config::Payload; @@ -19,6 +20,7 @@ pub(crate) const DEVICE_BACKUP_LOCATION: &str = "device_info"; pub struct IdentityManager { homedir_path: std::path::PathBuf, + prefer_module_identity_cache: bool, req_timeout: std::time::Duration, req_retries: u32, key_client: Arc, @@ -27,7 +29,7 @@ pub struct IdentityManager { tpm_client: Arc, proxy_uri: Option, - pub(crate) iot_hub_device: Option, + pub(crate) iot_hub_device: Option, pub(crate) identity_cert_renewal: Option< Arc>>, >, @@ -40,11 +42,12 @@ impl IdentityManager { key_engine: Arc>, cert_client: Arc, tpm_client: Arc, - iot_hub_device: Option, + iot_hub_device: Option, proxy_uri: Option, ) -> Self { IdentityManager { homedir_path: settings.homedir.clone(), + prefer_module_identity_cache: settings.prefer_module_identity_cache, req_timeout: std::time::Duration::from_secs(settings.cloud_timeout_sec), req_retries: settings.cloud_retries, key_client, @@ -57,7 +60,7 @@ impl IdentityManager { } } - pub fn set_device(&mut self, device: &aziot_identity_common::IoTHubDevice) { + pub fn set_device(&mut self, device: &IoTHubDevice) { ModuleBackup::set_device( &self.homedir_path, &device.iothub_hostname, @@ -103,10 +106,7 @@ impl IdentityManager { self.iot_hub_device = None; } - pub async fn create_module_identity( - &self, - module_id: &str, - ) -> Result { + pub async fn create_module_identity(&self, module_id: &str) -> Result { if module_id.trim().is_empty() { return Err(Error::invalid_parameter( "module_id", @@ -158,7 +158,7 @@ impl IdentityManager { &device.iothub_hostname, &device.device_id, &response.module_id, - Some(aziot_identity_common::hub::Module { + Some(Module { module_id: response.module_id.clone(), device_id: response.device_id.clone(), generation_id: response.generation_id.clone(), @@ -167,27 +167,23 @@ impl IdentityManager { }), ); - let identity = - aziot_identity_common::Identity::Aziot(aziot_identity_common::AzureIoTSpec { - hub_name: device.iothub_hostname.clone(), - gateway_host: device.local_gateway_hostname.clone(), - device_id: aziot_identity_common::DeviceId(response.device_id), - module_id: Some(aziot_identity_common::ModuleId(response.module_id)), - gen_id: response.generation_id.map(aziot_identity_common::GenId), - auth: Some(aziot_identity_common::AuthenticationInfo::from( - module_credentials, - )), - }); + let identity = Identity::Aziot(aziot_identity_common::AzureIoTSpec { + hub_name: device.iothub_hostname.clone(), + gateway_host: device.local_gateway_hostname.clone(), + device_id: aziot_identity_common::DeviceId(response.device_id), + module_id: Some(aziot_identity_common::ModuleId(response.module_id)), + gen_id: response.generation_id.map(aziot_identity_common::GenId), + auth: Some(aziot_identity_common::AuthenticationInfo::from( + module_credentials, + )), + }); Ok(identity) } None => Err(Error::DeviceNotFound), } } - pub async fn update_module_identity( - &self, - module_id: &str, - ) -> Result { + pub async fn update_module_identity(&self, module_id: &str) -> Result { if module_id.trim().is_empty() { return Err(Error::invalid_parameter( "module_id", @@ -239,7 +235,7 @@ impl IdentityManager { &device.iothub_hostname, &device.device_id, &response.module_id, - Some(aziot_identity_common::hub::Module { + Some(Module { module_id: response.module_id.clone(), device_id: response.device_id.clone(), generation_id: response.generation_id.clone(), @@ -248,43 +244,37 @@ impl IdentityManager { }), ); - let identity = - aziot_identity_common::Identity::Aziot(aziot_identity_common::AzureIoTSpec { - hub_name: device.iothub_hostname.clone(), - gateway_host: device.local_gateway_hostname.clone(), - device_id: aziot_identity_common::DeviceId(response.device_id), - module_id: Some(aziot_identity_common::ModuleId(response.module_id)), - gen_id: response.generation_id.map(aziot_identity_common::GenId), - auth: Some(aziot_identity_common::AuthenticationInfo::from( - module_credentials, - )), - }); + let identity = Identity::Aziot(aziot_identity_common::AzureIoTSpec { + hub_name: device.iothub_hostname.clone(), + gateway_host: device.local_gateway_hostname.clone(), + device_id: aziot_identity_common::DeviceId(response.device_id), + module_id: Some(aziot_identity_common::ModuleId(response.module_id)), + gen_id: response.generation_id.map(aziot_identity_common::GenId), + auth: Some(aziot_identity_common::AuthenticationInfo::from( + module_credentials, + )), + }); Ok(identity) } None => Err(Error::DeviceNotFound), } } - pub async fn get_device_identity(&self) -> Result { + pub async fn get_device_identity(&self) -> Result { match &self.iot_hub_device { - Some(device) => Ok(aziot_identity_common::Identity::Aziot( - aziot_identity_common::AzureIoTSpec { - hub_name: device.iothub_hostname.clone(), - gateway_host: device.local_gateway_hostname.clone(), - device_id: aziot_identity_common::DeviceId(device.device_id.clone()), - module_id: None, - gen_id: None, - auth: Some(self.get_device_identity_key().await?), - }, - )), + Some(device) => Ok(Identity::Aziot(aziot_identity_common::AzureIoTSpec { + hub_name: device.iothub_hostname.clone(), + gateway_host: device.local_gateway_hostname.clone(), + device_id: aziot_identity_common::DeviceId(device.device_id.clone()), + module_id: None, + gen_id: None, + auth: Some(self.get_device_identity_key().await?), + })), None => Err(Error::DeviceNotFound), } } - pub async fn get_module_identity( - &self, - module_id: &str, - ) -> Result { + pub async fn get_module_identity(&self, module_id: &str) -> Result { if module_id.trim().is_empty() { return Err(Error::invalid_parameter( "module_id", @@ -294,59 +284,36 @@ impl IdentityManager { match &self.iot_hub_device { Some(device) => { - let module = { - let client = aziot_cloud_client_async::HubClient::new( - device, - self.key_client.clone(), - self.tpm_client.clone(), - ) - .with_retry(self.req_retries) - .with_timeout(self.req_timeout) - .with_proxy(self.proxy_uri.clone()); - - match client.get_module(module_id).await { - Ok(module) => { - ModuleBackup::set_module_backup( - &self.homedir_path, - &device.iothub_hostname, - &device.device_id, - &module.module_id, - Some(aziot_identity_common::hub::Module { - module_id: module.module_id.clone(), - device_id: module.device_id.clone(), - generation_id: module.generation_id.clone(), - managed_by: module.managed_by.clone(), - authentication: None, - }), - ); - - module - } - Err(err) => { - if err.kind() == std::io::ErrorKind::NotFound { - ModuleBackup::set_module_backup( - &self.homedir_path, - &device.iothub_hostname, - &device.device_id, - module_id, - None, - ); - return Err(Error::HubClient(err)); - } - - let module = ModuleBackup::get_module_backup( - &self.homedir_path, - &device.iothub_hostname, - &device.device_id, - module_id, - ); + let module = if self.prefer_module_identity_cache { + let module = ModuleBackup::get_module_backup( + &self.homedir_path, + &device.iothub_hostname, + &device.device_id, + module_id, + ); - match module { - Some(module) => module, - None => return Err(Error::HubClient(err)), - } - } + if let Some(module) = module { + module + } else { + self.get_module_identity_from_hub(device, module_id).await? } + } else if let Ok(module) = + self.get_module_identity_from_hub(device, module_id).await + { + module + } else { + ModuleBackup::get_module_backup( + &self.homedir_path, + &device.iothub_hostname, + &device.device_id, + module_id, + ) + .ok_or_else(|| { + Error::HubClient(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("module {module_id} not found"), + )) + })? }; let master_id_key_handle = self.get_master_identity_key().await?; @@ -356,17 +323,16 @@ impl IdentityManager { let module_credentials = aziot_identity_common::Credentials::SharedPrivateKey(primary_key_handle.0); - let identity = - aziot_identity_common::Identity::Aziot(aziot_identity_common::AzureIoTSpec { - hub_name: device.iothub_hostname.clone(), - gateway_host: device.local_gateway_hostname.clone(), - device_id: aziot_identity_common::DeviceId(module.device_id), - module_id: Some(aziot_identity_common::ModuleId(module.module_id)), - gen_id: module.generation_id.map(aziot_identity_common::GenId), - auth: Some(aziot_identity_common::AuthenticationInfo::from( - module_credentials, - )), - }); + let identity = Identity::Aziot(aziot_identity_common::AzureIoTSpec { + hub_name: device.iothub_hostname.clone(), + gateway_host: device.local_gateway_hostname.clone(), + device_id: aziot_identity_common::DeviceId(module.device_id), + module_id: Some(aziot_identity_common::ModuleId(module.module_id)), + gen_id: module.generation_id.map(aziot_identity_common::GenId), + auth: Some(aziot_identity_common::AuthenticationInfo::from( + module_credentials, + )), + }); Ok(identity) } @@ -374,53 +340,30 @@ impl IdentityManager { } } - pub async fn get_module_identities( - &self, - ) -> Result, Error> { + pub async fn get_module_identities(&self, use_cache: bool) -> Result, Error> { match &self.iot_hub_device { - Some(device) => { - let client = aziot_cloud_client_async::HubClient::new( - device, - self.key_client.clone(), - self.tpm_client.clone(), - ) - .with_retry(self.req_retries) - .with_timeout(self.req_timeout) - .with_proxy(self.proxy_uri.clone()); - - let response = client.list_modules().await.map_err(Error::HubClient)?; - - let identities = response - .into_iter() - .map(|module| { - ModuleBackup::set_module_backup( - &self.homedir_path, - &device.iothub_hostname, - &device.device_id, - &module.module_id, - Some(aziot_identity_common::hub::Module { - module_id: module.module_id.clone(), - device_id: module.device_id.clone(), - generation_id: module.generation_id.clone(), - managed_by: module.managed_by.clone(), - authentication: None, - }), - ); - - aziot_identity_common::Identity::Aziot( - aziot_identity_common::AzureIoTSpec { - hub_name: device.iothub_hostname.clone(), - gateway_host: device.local_gateway_hostname.clone(), - device_id: aziot_identity_common::DeviceId(module.device_id), - module_id: Some(aziot_identity_common::ModuleId(module.module_id)), - gen_id: module.generation_id.map(aziot_identity_common::GenId), - auth: None, //Auth information can be requested via get_module_identity - }, - ) - }) - .collect(); - Ok(identities) - } + Some(device) => match (use_cache, self.prefer_module_identity_cache) { + (true, true) => { + let identities = ModuleBackup::list_module_backups(device, &self.homedir_path); + + if let Ok(identities) = identities { + Ok(identities) + } else { + self.list_module_identities_from_hub(device).await + } + } + (true, false) => { + let identities = self.list_module_identities_from_hub(device).await; + + if let Ok(identities) = identities { + Ok(identities) + } else { + ModuleBackup::list_module_backups(device, &self.homedir_path) + .map_err(Error::HubClient) + } + } + (false, _) => self.list_module_identities_from_hub(device).await, + }, None => Err(Error::DeviceNotFound), } } @@ -531,7 +474,7 @@ impl IdentityManager { async fn get_module_derived_keys( &self, master_id: aziot_key_common::KeyHandle, - module: aziot_identity_common::hub::Module, + module: Module, ) -> Result< ( aziot_key_common::KeyHandle, @@ -624,7 +567,7 @@ impl IdentityManager { .await? } }; - let device = aziot_identity_common::IoTHubDevice { + let device = IoTHubDevice { local_gateway_hostname: provisioning .local_gateway_hostname .clone() @@ -740,7 +683,7 @@ impl IdentityManager { credentials: aziot_identity_common::Credentials, local_gateway_hostname: Option, payload: Option, - ) -> Result { + ) -> Result { let backup_device = self.get_backup_provisioning_info(credentials.clone()); if skip_if_backup_is_valid && backup_device.is_some() { @@ -767,7 +710,7 @@ impl IdentityManager { .await .map_err(Error::DpsClient)?; - Ok(aziot_identity_common::IoTHubDevice { + Ok(IoTHubDevice { local_gateway_hostname: local_gateway_hostname .unwrap_or_else(|| response.assigned_hub.clone()), iothub_hostname: response.assigned_hub, @@ -779,7 +722,7 @@ impl IdentityManager { fn get_backup_provisioning_info( &self, credentials: aziot_identity_common::Credentials, - ) -> Option { + ) -> Option { let mut prev_device_info_path = self.homedir_path.clone(); prev_device_info_path.push(DEVICE_BACKUP_LOCATION); @@ -790,7 +733,7 @@ impl IdentityManager { match HubDeviceInfo::new(&prev_device_info_path) { Ok(device_info) => match device_info { Some(device_info) => { - let device = aziot_identity_common::IoTHubDevice { + let device = IoTHubDevice { local_gateway_hostname: device_info.local_gateway_hostname, iothub_hostname: device_info.hub_name, device_id: device_info.device_id, @@ -937,10 +880,10 @@ impl IdentityManager { curr_hub_device_info, ); - let hub_module_ids = self.get_module_identities().await?; + let hub_module_ids = self.get_module_identities(false).await?; for m in hub_module_ids { - if let aziot_identity_common::Identity::Aziot(m) = m { + if let Identity::Aziot(m) = m { if let Some(m) = m.module_id { if !current_module_set.contains(&m) && prev_module_set.contains(&m) { self.delete_module_identity(&m.0).await?; @@ -978,6 +921,100 @@ impl IdentityManager { Ok(()) } + + async fn get_module_identity_from_hub( + &self, + device: &IoTHubDevice, + module_id: &str, + ) -> Result { + let client = aziot_cloud_client_async::HubClient::new( + device, + self.key_client.clone(), + self.tpm_client.clone(), + ) + .with_retry(self.req_retries) + .with_timeout(self.req_timeout) + .with_proxy(self.proxy_uri.clone()); + + match client.get_module(module_id).await { + Ok(module) => { + ModuleBackup::set_module_backup( + &self.homedir_path, + &device.iothub_hostname, + &device.device_id, + &module.module_id, + Some(Module { + module_id: module.module_id.clone(), + device_id: module.device_id.clone(), + generation_id: module.generation_id.clone(), + managed_by: module.managed_by.clone(), + authentication: None, + }), + ); + + Ok(module) + } + Err(err) => { + if err.kind() == std::io::ErrorKind::NotFound { + ModuleBackup::set_module_backup( + &self.homedir_path, + &device.iothub_hostname, + &device.device_id, + module_id, + None, + ); + } + + Err(Error::HubClient(err)) + } + } + } + + async fn list_module_identities_from_hub( + &self, + device: &IoTHubDevice, + ) -> Result, Error> { + let client = aziot_cloud_client_async::HubClient::new( + device, + self.key_client.clone(), + self.tpm_client.clone(), + ) + .with_retry(self.req_retries) + .with_timeout(self.req_timeout) + .with_proxy(self.proxy_uri.clone()); + + let response = client.list_modules().await.map_err(Error::HubClient)?; + + let identities = response + .into_iter() + .map(|module| { + ModuleBackup::set_module_backup( + &self.homedir_path, + &device.iothub_hostname, + &device.device_id, + &module.module_id, + Some(Module { + module_id: module.module_id.clone(), + device_id: module.device_id.clone(), + generation_id: module.generation_id.clone(), + managed_by: module.managed_by.clone(), + authentication: None, + }), + ); + + Identity::Aziot(aziot_identity_common::AzureIoTSpec { + hub_name: device.iothub_hostname.clone(), + gateway_host: device.local_gateway_hostname.clone(), + device_id: aziot_identity_common::DeviceId(module.device_id), + module_id: Some(aziot_identity_common::ModuleId(module.module_id)), + gen_id: module.generation_id.map(aziot_identity_common::GenId), + auth: None, //Auth information can be requested via get_module_identity + }) + }) + .collect(); + + Ok(identities) + } } fn get_cert_subject(cert: &openssl::x509::X509) -> Result { @@ -1106,7 +1143,7 @@ impl ModuleBackup { iothub_hostname: &str, device_id: &str, module_id: &str, - data: Option, + data: Option, ) { let result = match data { Some(module) => { @@ -1138,7 +1175,7 @@ impl ModuleBackup { iothub_hostname: &str, device_id: &str, module_id: &str, - ) -> Option { + ) -> Option { match Self::get_module_path(homedir_path, iothub_hostname, device_id, module_id) { Ok(path) => match std::fs::read(path) { Ok(module) => match serde_json::from_slice(&module) { @@ -1164,6 +1201,46 @@ impl ModuleBackup { } } + pub fn list_module_backups( + device: &IoTHubDevice, + homedir_path: &Path, + ) -> Result, std::io::Error> { + let module_dir = + Self::get_device_path(homedir_path, &device.iothub_hostname, &device.device_id) + .map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "failed to calculate module path", + ) + })?; + let mut identities = Vec::new(); + + // Read all paths in module directory, but ignore directories. + for file in std::fs::read_dir(module_dir)? { + let path = file?.path(); + + if path.is_dir() { + continue; + } + + let module = std::fs::read(path)?; + let module: Module = serde_json::from_slice(&module)?; + + let identity = Identity::Aziot(aziot_identity_common::AzureIoTSpec { + hub_name: device.iothub_hostname.clone(), + gateway_host: device.local_gateway_hostname.clone(), + device_id: aziot_identity_common::DeviceId(module.device_id), + module_id: Some(aziot_identity_common::ModuleId(module.module_id)), + gen_id: module.generation_id.map(aziot_identity_common::GenId), + auth: None, //Auth information can be requested via get_module_identity + }); + + identities.push(identity); + } + + Ok(identities) + } + fn get_device_path( homedir_path: &Path, iothub_hostname: &str, diff --git a/identity/aziot-identityd/src/lib.rs b/identity/aziot-identityd/src/lib.rs index f6ed80fca..5a08b4956 100644 --- a/identity/aziot-identityd/src/lib.rs +++ b/identity/aziot-identityd/src/lib.rs @@ -421,7 +421,7 @@ impl Api { } match_id_type!(id_type { - ID_TYPE_AZIOT => { self.id_manager.get_module_identities().await }, + ID_TYPE_AZIOT => { self.id_manager.get_module_identities(true).await }, }) }