diff --git a/identity/aziot-identity-client-async/src/lib.rs b/identity/aziot-identity-client-async/src/lib.rs index ee952f5b1..20e349ae3 100644 --- a/identity/aziot-identity-client-async/src/lib.rs +++ b/identity/aziot-identity-client-async/src/lib.rs @@ -131,12 +131,14 @@ impl Client { pub async fn create_module_identity( &self, module_name: &str, + managed_by: Option, ) -> Result { let uri = make_uri!("/identities/modules", self.api_version); let body = create_module_identity::Request { id_type: ID_TYPE_AZIOT.to_string(), module_id: module_name.to_string(), + managed_by, opts: None, }; @@ -154,6 +156,7 @@ impl Client { pub async fn create_local_identity( &self, module_name: &str, + managed_by: Option, opts: Option, ) -> Result { let uri = make_uri!("/identities/modules", self.api_version); @@ -162,6 +165,7 @@ impl Client { let body = create_module_identity::Request { id_type: ID_TYPE_LOCAL.to_string(), module_id: module_name.to_string(), + managed_by, opts: opts.map(|opts| create_module_identity::CreateModuleOpts::LocalIdOpts(opts)), }; @@ -179,6 +183,7 @@ impl Client { pub async fn update_module_identity( &self, module_name: &str, + managed_by: Option, ) -> Result { let uri = make_uri!( "/identities/modules", @@ -190,6 +195,7 @@ impl Client { let body = update_module_identity::Request { id_type: ID_TYPE_AZIOT.to_string(), module_id: module_name.to_string(), + managed_by, }; let request = HttpRequest::put(self.connector.clone(), uri, body) diff --git a/identity/aziot-identity-common-http/src/lib.rs b/identity/aziot-identity-common-http/src/lib.rs index fcef8d78c..99e0ace37 100644 --- a/identity/aziot-identity-common-http/src/lib.rs +++ b/identity/aziot-identity-common-http/src/lib.rs @@ -68,6 +68,8 @@ pub mod create_module_identity { pub id_type: String, #[serde(rename = "moduleId")] pub module_id: String, + #[serde(rename = "managedBy", skip_serializing_if = "Option::is_none")] + pub managed_by: Option, #[serde(flatten)] pub opts: Option, } @@ -86,6 +88,8 @@ pub mod update_module_identity { pub id_type: String, #[serde(rename = "moduleId")] pub module_id: String, + #[serde(rename = "managedBy", skip_serializing_if = "Option::is_none")] + pub managed_by: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/identity/aziot-identity-common/src/lib.rs b/identity/aziot-identity-common/src/lib.rs index 44c0483f8..ab0fc0829 100644 --- a/identity/aziot-identity-common/src/lib.rs +++ b/identity/aziot-identity-common/src/lib.rs @@ -38,6 +38,8 @@ pub struct AzureIoTSpec { pub gen_id: Option, #[serde(rename = "auth", skip_serializing_if = "Option::is_none")] pub auth: Option, + #[serde(rename = "managedBy", skip_serializing_if = "Option::is_none")] + pub managed_by: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/identity/aziot-identityd/src/http/create_or_list_module_identity.rs b/identity/aziot-identityd/src/http/create_or_list_module_identity.rs index 577371c32..49a34d3d3 100644 --- a/identity/aziot-identityd/src/http/create_or_list_module_identity.rs +++ b/identity/aziot-identityd/src/http/create_or_list_module_identity.rs @@ -77,7 +77,13 @@ impl http_common::server::Route for Route { }; let identity = match api - .create_identity(auth_id, Some(&body.id_type), &body.module_id, body.opts) + .create_identity( + auth_id, + Some(&body.id_type), + &body.module_id, + body.managed_by, + body.opts, + ) .await { Ok(id) => id, diff --git a/identity/aziot-identityd/src/http/get_update_or_delete_module_identity.rs b/identity/aziot-identityd/src/http/get_update_or_delete_module_identity.rs index 9720a2641..56abe01db 100644 --- a/identity/aziot-identityd/src/http/get_update_or_delete_module_identity.rs +++ b/identity/aziot-identityd/src/http/get_update_or_delete_module_identity.rs @@ -13,6 +13,12 @@ pub(super) struct Route { user: aziot_identityd_config::Credentials, } +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct UpdateIdentityRequest { + #[serde(rename = "managedBy", skip_serializing_if = "Option::is_none")] + pub managed_by: Option, +} + #[async_trait::async_trait] impl http_common::server::Route for Route { type ApiVersion = aziot_identity_common_http::ApiVersion; @@ -96,14 +102,14 @@ impl http_common::server::Route for Route { type PostBody = serde::de::IgnoredAny; - type PutBody = serde::de::IgnoredAny; + type PutBody = UpdateIdentityRequest; // clippy fires this lint for the `_body` parameter of the inner fn in the `async-trait` expansion. // It's not clear why clippy does this, especially since it doesn't raise it for other functions // that also ignore their `_body` parameter like `fn delete` above. // // So suppress it manually. #[allow(clippy::needless_pass_by_value)] - async fn put(self, _body: Self::PutBody) -> http_common::server::RouteResponse { + async fn put(self, body: Self::PutBody) -> http_common::server::RouteResponse { let mut api = self.api.lock().await; let api = &mut *api; @@ -113,7 +119,12 @@ impl http_common::server::Route for Route { }; let identity = match api - .update_identity(auth_id, self.id_type.as_deref(), &self.module_id) + .update_identity( + auth_id, + self.id_type.as_deref(), + &self.module_id, + body.managed_by, + ) .await { Ok(v) => v, diff --git a/identity/aziot-identityd/src/identity.rs b/identity/aziot-identityd/src/identity.rs index 3a62f4e54..2d5b3518b 100644 --- a/identity/aziot-identityd/src/identity.rs +++ b/identity/aziot-identityd/src/identity.rs @@ -106,6 +106,7 @@ impl IdentityManager { pub async fn create_module_identity( &self, module_id: &str, + managed_by: Option, ) -> Result { if module_id.trim().is_empty() { return Err(Error::invalid_parameter( @@ -126,7 +127,7 @@ impl IdentityManager { .with_proxy(self.proxy_uri.clone()); let new_module = client - .create_module(module_id, None, None) + .create_module(module_id, None, managed_by.clone()) .await .map_err(Error::HubClient)?; @@ -148,7 +149,7 @@ impl IdentityManager { x509_thumbprint: None, type_: Some(aziot_identity_common::hub::AuthType::Sas), }), - None, + managed_by, ) .await .map_err(Error::HubClient)?; @@ -177,6 +178,7 @@ impl IdentityManager { auth: Some(aziot_identity_common::AuthenticationInfo::from( module_credentials, )), + managed_by: response.managed_by, }); Ok(identity) } @@ -187,6 +189,7 @@ impl IdentityManager { pub async fn update_module_identity( &self, module_id: &str, + managed_by: Option, ) -> Result { if module_id.trim().is_empty() { return Err(Error::invalid_parameter( @@ -229,7 +232,7 @@ impl IdentityManager { x509_thumbprint: None, type_: Some(aziot_identity_common::hub::AuthType::Sas), }), - None, + managed_by, ) .await .map_err(Error::HubClient)?; @@ -258,6 +261,7 @@ impl IdentityManager { auth: Some(aziot_identity_common::AuthenticationInfo::from( module_credentials, )), + managed_by: response.managed_by, }); Ok(identity) } @@ -275,6 +279,7 @@ impl IdentityManager { module_id: None, gen_id: None, auth: Some(self.get_device_identity_key().await?), + managed_by: None, }, )), None => Err(Error::DeviceNotFound), @@ -366,6 +371,7 @@ impl IdentityManager { auth: Some(aziot_identity_common::AuthenticationInfo::from( module_credentials, )), + managed_by: module.managed_by, }); Ok(identity) @@ -415,6 +421,7 @@ impl IdentityManager { 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 + managed_by: module.managed_by, }, ) }) @@ -939,15 +946,15 @@ impl IdentityManager { let hub_module_ids = self.get_module_identities().await?; - for m in hub_module_ids { + for m in &hub_module_ids { if let aziot_identity_common::Identity::Aziot(m) = m { - if let Some(m) = m.module_id { - if !current_module_set.contains(&m) && prev_module_set.contains(&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?; log::info!("Hub identity {:?} removed", &m.0); - } else if current_module_set.contains(&m) { - if prev_module_set.contains(&m) { - current_module_set.remove(&m); + } else if current_module_set.contains(m) { + if prev_module_set.contains(m) { + current_module_set.remove(m); log::info!("Hub identity {:?} already exists", &m.0); } else { self.delete_module_identity(&m.0).await?; @@ -961,7 +968,8 @@ impl IdentityManager { } for m in current_module_set { - self.create_module_identity(&m.0).await?; + // TODO: do we need to take previous identity and pass along? + self.create_module_identity(&m.0, None).await?; log::info!("Hub identity {:?} added", &m.0); } diff --git a/identity/aziot-identityd/src/lib.rs b/identity/aziot-identityd/src/lib.rs index d96a3aa65..0ff7ad708 100644 --- a/identity/aziot-identityd/src/lib.rs +++ b/identity/aziot-identityd/src/lib.rs @@ -445,6 +445,7 @@ impl Api { auth_id: auth::AuthId, id_type: Option<&str>, module_id: &str, + managed_by: Option, opts: Option, ) -> Result { if !self.authorizer.authorize(auth::Operation { @@ -455,7 +456,7 @@ impl Api { } match_id_type!( id_type { - ID_TYPE_AZIOT => { self.id_manager.create_module_identity(module_id).await }, + ID_TYPE_AZIOT => { self.id_manager.create_module_identity(module_id, managed_by).await }, ID_TYPE_LOCAL => { if self.local_identities .get(&aziot_identity_common::ModuleId(module_id.to_owned())) @@ -485,6 +486,7 @@ impl Api { auth_id: auth::AuthId, id_type: Option<&str>, module_id: &str, + managed_by: Option, ) -> Result { if !self.authorizer.authorize(auth::Operation { auth_id, @@ -494,7 +496,7 @@ impl Api { } match_id_type!(id_type { - ID_TYPE_AZIOT => { self.id_manager.update_module_identity(module_id).await }, + ID_TYPE_AZIOT => { self.id_manager.update_module_identity(module_id, managed_by).await }, }) } diff --git a/test-common/src/client/identity.rs b/test-common/src/client/identity.rs index a82de68ba..27c68ba75 100644 --- a/test-common/src/client/identity.rs +++ b/test-common/src/client/identity.rs @@ -26,7 +26,7 @@ pub struct IdentityClient { impl Default for IdentityClient { fn default() -> Self { let mut identities = std::collections::BTreeMap::new(); - identities.insert("testModule".to_string(), test_identity("testModule")); + identities.insert("testModule".to_string(), test_identity("testModule", None)); let identities = tokio::sync::Mutex::new(std::cell::RefCell::new(identities)); @@ -68,9 +68,10 @@ impl IdentityClient { pub async fn create_module_identity( &self, module_name: &str, + managed_by: Option, ) -> Result { if self.get_identity_ok { - let identity = test_identity(module_name); + let identity = test_identity(module_name, managed_by); let identities = self.identities.lock().await; @@ -126,6 +127,7 @@ impl IdentityClient { pub async fn update_module_identity( &self, module_name: &str, + _managed_by: Option, ) -> Result { if self.update_identity_ok { // A real identity client would update the Idenitity in Hub. But this test @@ -154,7 +156,7 @@ impl IdentityClient { } /// Generates an Identity struct for a given module name. -fn test_identity(module_name: &str) -> Identity { +fn test_identity(module_name: &str, managed_by: Option) -> Identity { Identity::Aziot(aziot_identity_common::AzureIoTSpec { hub_name: "test-hub.test.net".to_string(), gateway_host: "gateway-host.test.net".to_string(), @@ -166,6 +168,7 @@ fn test_identity(module_name: &str) -> Identity { key_handle: Some(aziot_key_common::KeyHandle(format!("{}-key", module_name))), cert_id: Some(format!("{}-cert", module_name)), }), + managed_by, }) }