use super::config;
use crate::{
app_auth::{app_state, AppState},
app_container,
client::AuthClient,
AuthError,
};
use bincode::deserialize;
use safe_core::{
client::{AuthActions, Client},
core_structs::{access_container_enc_key, AccessContainerEntry, AppAccess},
ipc::{req::ContainerPermissions, AppExchangeInfo, IpcError},
utils::symmetric_decrypt,
};
use safe_nd::{AppPermissions, MDataAddress};
use std::collections::HashMap;
use xor_name::XorName;
#[derive(Debug)]
pub struct RegisteredApp {
pub app_info: AppExchangeInfo,
pub containers: HashMap<String, ContainerPermissions>,
pub app_perms: AppPermissions,
}
pub async fn remove_revoked_app(client: &AuthClient, app_id: String) -> Result<(), AuthError> {
let client = client.clone();
let c2 = client.clone();
let c3 = client.clone();
let c4 = client.clone();
let app_id2 = app_id.clone();
let app_id3 = app_id.clone();
let (apps_version, apps) = config::list_apps(&client).await?;
let app_state = app_state(&c2, &apps, &app_id).await?;
let (apps, apps_version) = match app_state {
AppState::Revoked => (apps, apps_version),
AppState::Authenticated => return Err(AuthError::from("App is not revoked")),
AppState::NotAuthenticated => return Err(AuthError::IpcError(IpcError::UnknownApp)),
};
let _ = config::remove_app(&c3, apps, config::next_version(apps_version), &app_id2).await?;
let _ = app_container::remove(c4, &app_id3).await?;
Ok(())
}
pub async fn list_revoked(client: &AuthClient) -> Result<Vec<AppExchangeInfo>, AuthError> {
let (_, auth_cfg) = config::list_apps(client).await?;
let access_container = client.access_container().await;
let entries = client
.list_seq_mdata_entries(access_container.name(), access_container.type_tag())
.await?;
let mut apps = Vec::new();
let nonce = access_container
.nonce()
.ok_or_else(|| AuthError::from("No nonce on access container's MDataInfo"))?;
for app in auth_cfg.values() {
let key = access_container_enc_key(&app.info.id, &app.keys.enc_key, nonce)?;
let revoked = entries
.get(&key)
.map_or(true, |entry| entry.data.is_empty());
if revoked {
apps.push(app.info.clone());
}
}
Ok(apps)
}
pub async fn list_registered(client: &AuthClient) -> Result<Vec<RegisteredApp>, AuthError> {
let (_, auth_cfg) = config::list_apps(client).await?;
let access_container = client.access_container().await;
let entries = client
.list_seq_mdata_entries(access_container.name(), access_container.type_tag())
.await?;
let (mut authorised_keys, _version) = client.list_auth_keys_and_version().await?;
let mut apps = Vec::new();
let nonce = access_container
.nonce()
.ok_or_else(|| AuthError::from("No nonce on access container's MDataInfo"))?;
for app in auth_cfg.values() {
let key = access_container_enc_key(&app.info.id, &app.keys.enc_key, nonce)?;
let entry = match entries.get(&key) {
Some(entry) if !entry.data.is_empty() => Some(entry),
_ => None,
};
if let Some(entry) = entry {
let plaintext = symmetric_decrypt(&entry.data, &app.keys.enc_key)?;
let app_access = deserialize::<AccessContainerEntry>(&plaintext)?;
let mut containers = HashMap::new();
for (container_name, (_, permission_set)) in app_access {
let _ = containers.insert(container_name, permission_set);
}
let app_public_key = app.keys.public_key();
let app_perms = authorised_keys.remove(&app_public_key).unwrap_or_default();
let registered_app = RegisteredApp {
app_info: app.info.clone(),
containers,
app_perms,
};
apps.push(registered_app);
}
}
Ok(apps)
}
pub async fn apps_accessing_mutable_data(
client: &AuthClient,
name: XorName,
type_tag: u64,
) -> Result<Vec<AppAccess>, AuthError> {
let c2 = client.clone();
let permissions = client
.list_mdata_permissions(MDataAddress::Seq {
name,
tag: type_tag,
})
.await?;
let (_, apps) = config::list_apps(&c2).await?;
let apps = apps
.into_iter()
.map(|(_, app_info)| (app_info.keys.public_key(), app_info.info))
.collect::<HashMap<_, _>>();
let mut app_access_vec: Vec<AppAccess> = Vec::new();
for (user, perm_set) in permissions {
let app_access = match apps.get(&user) {
Some(app_info) => AppAccess {
sign_key: user,
permissions: perm_set,
name: Some(app_info.name.clone()),
app_id: Some(app_info.id.clone()),
},
None => {
AppAccess {
sign_key: user,
permissions: perm_set,
name: None,
app_id: None,
}
}
};
app_access_vec.push(app_access);
}
Ok(app_access_vec)
}