use async_trait::async_trait;
use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet};
use thiserror::Error;
use tracing::info;
use uuid::Uuid;
use uvb_core::TenantId;
#[derive(Debug, Error)]
pub enum SecretAccessError {
#[error("Access denied: {0}")]
AccessDenied(String),
#[error("Service not authenticated")]
ServiceNotAuthenticated,
#[error("Service not authorized for secret: {secret_id}")]
ServiceNotAuthorized { secret_id: String },
#[error("Secret not found: {0}")]
SecretNotFound(String),
#[error("Invalid service identity")]
InvalidServiceIdentity,
#[error("Access grant expired at {0}")]
AccessGrantExpired(DateTime<Utc>),
#[error("Access request pending approval")]
AccessRequestPending,
#[error("Access request denied")]
AccessRequestDenied,
#[error("Invalid access policy")]
InvalidAccessPolicy,
#[error("Storage error: {0}")]
Storage(String),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct ServiceIdentity {
pub service_id: String,
pub name: String,
pub service_type: ServiceType,
pub tenant_id: TenantId,
pub cert_fingerprint: Option<String>,
pub environment: Environment,
pub created_at: DateTime<Utc>,
}
impl ServiceIdentity {
pub fn new(
name: String,
service_type: ServiceType,
tenant_id: TenantId,
environment: Environment,
) -> Self {
Self {
service_id: Uuid::new_v4().to_string(),
name,
service_type,
tenant_id,
cert_fingerprint: None,
environment,
created_at: Utc::now(),
}
}
pub fn with_cert_fingerprint(mut self, fingerprint: String) -> Self {
self.cert_fingerprint = Some(fingerprint);
self
}
pub fn identity_hash(&self) -> String {
let mut hasher = Sha256::new();
hasher.update(self.service_id.as_bytes());
hasher.update(self.name.as_bytes());
hasher.update(self.tenant_id.to_string().as_bytes());
hex::encode(hasher.finalize())
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ServiceType {
AuthService,
MfaService,
UserService,
ApiGateway,
BackgroundWorker,
AdminService,
AnalyticsService,
Unknown,
}
impl ServiceType {
pub fn name(&self) -> &'static str {
match self {
Self::AuthService => "Authentication Service",
Self::MfaService => "MFA Service",
Self::UserService => "User Service",
Self::ApiGateway => "API Gateway",
Self::BackgroundWorker => "Background Worker",
Self::AdminService => "Admin Service",
Self::AnalyticsService => "Analytics Service",
Self::Unknown => "Unknown",
}
}
pub fn default_trust_level(&self) -> TrustLevel {
match self {
Self::AuthService | Self::MfaService => TrustLevel::High,
Self::ApiGateway | Self::UserService => TrustLevel::Medium,
Self::BackgroundWorker | Self::AdminService => TrustLevel::Medium,
Self::AnalyticsService => TrustLevel::Low,
Self::Unknown => TrustLevel::Untrusted,
}
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Environment {
Production,
Staging,
Development,
Testing,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum TrustLevel {
Untrusted = 0,
Low = 1,
Medium = 2,
High = 3,
Critical = 4,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecretScope {
pub secret_id: String,
pub name: String,
pub secret_type: SecretType,
pub tenant_id: TenantId,
pub required_trust_level: TrustLevel,
pub allowed_services: HashSet<String>,
pub denied_services: HashSet<String>,
pub allowed_environments: HashSet<Environment>,
pub require_approval: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl SecretScope {
pub fn new(
name: String,
secret_type: SecretType,
tenant_id: TenantId,
required_trust_level: TrustLevel,
) -> Self {
let now = Utc::now();
Self {
secret_id: Uuid::new_v4().to_string(),
name,
secret_type,
tenant_id,
required_trust_level,
allowed_services: HashSet::new(),
denied_services: HashSet::new(),
allowed_environments: HashSet::from_iter(vec![
Environment::Production,
Environment::Staging,
Environment::Development,
]),
require_approval: false,
created_at: now,
updated_at: now,
}
}
pub fn allow_service(mut self, service_id: String) -> Self {
self.allowed_services.insert(service_id);
self.updated_at = Utc::now();
self
}
pub fn deny_service(mut self, service_id: String) -> Self {
self.denied_services.insert(service_id);
self.updated_at = Utc::now();
self
}
pub fn with_environments(mut self, environments: HashSet<Environment>) -> Self {
self.allowed_environments = environments;
self.updated_at = Utc::now();
self
}
pub fn with_approval_required(mut self, required: bool) -> Self {
self.require_approval = required;
self.updated_at = Utc::now();
self
}
pub fn is_service_allowed(&self, service_id: &str) -> bool {
if self.denied_services.contains(service_id) {
return false;
}
if self.allowed_services.is_empty() {
return true;
}
self.allowed_services.contains(service_id)
}
pub fn is_environment_allowed(&self, environment: Environment) -> bool {
self.allowed_environments.contains(&environment)
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum SecretType {
EncryptionKey,
TotpSecret,
ApiKey,
DatabasePassword,
JwtSigningKey,
OAuthClientSecret,
WebhookSecret,
Other,
}
impl SecretType {
pub fn name(&self) -> &'static str {
match self {
Self::EncryptionKey => "Encryption Key",
Self::TotpSecret => "TOTP Secret",
Self::ApiKey => "API Key",
Self::DatabasePassword => "Database Password",
Self::JwtSigningKey => "JWT Signing Key",
Self::OAuthClientSecret => "OAuth Client Secret",
Self::WebhookSecret => "Webhook Secret",
Self::Other => "Other",
}
}
pub fn default_trust_level(&self) -> TrustLevel {
match self {
Self::EncryptionKey | Self::TotpSecret | Self::JwtSigningKey => TrustLevel::High,
Self::DatabasePassword | Self::OAuthClientSecret => TrustLevel::Medium,
Self::ApiKey | Self::WebhookSecret => TrustLevel::Medium,
Self::Other => TrustLevel::Low,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AccessGrant {
pub grant_id: String,
pub service_identity: ServiceIdentity,
pub secret_id: String,
pub granted_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
pub granted_by: String,
pub reason: String,
pub access_count: u64,
pub last_accessed_at: Option<DateTime<Utc>>,
}
impl AccessGrant {
pub fn new(
service_identity: ServiceIdentity,
secret_id: String,
granted_by: String,
reason: String,
expires_at: Option<DateTime<Utc>>,
) -> Self {
Self {
grant_id: Uuid::new_v4().to_string(),
service_identity,
secret_id,
granted_at: Utc::now(),
expires_at,
granted_by,
reason,
access_count: 0,
last_accessed_at: None,
}
}
pub fn is_expired(&self) -> bool {
if let Some(expires_at) = self.expires_at {
Utc::now() > expires_at
} else {
false
}
}
pub fn record_access(&mut self) {
self.access_count += 1;
self.last_accessed_at = Some(Utc::now());
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AccessRequest {
pub request_id: String,
pub service_identity: ServiceIdentity,
pub secret_id: String,
pub requested_at: DateTime<Utc>,
pub requested_by: String,
pub reason: String,
pub status: AccessRequestStatus,
pub reviewed_by: Option<String>,
pub reviewed_at: Option<DateTime<Utc>>,
pub review_notes: Option<String>,
}
impl AccessRequest {
pub fn new(
service_identity: ServiceIdentity,
secret_id: String,
requested_by: String,
reason: String,
) -> Self {
Self {
request_id: Uuid::new_v4().to_string(),
service_identity,
secret_id,
requested_at: Utc::now(),
requested_by,
reason,
status: AccessRequestStatus::Pending,
reviewed_by: None,
reviewed_at: None,
review_notes: None,
}
}
pub fn approve(&mut self, reviewer: String, notes: Option<String>) {
self.status = AccessRequestStatus::Approved;
self.reviewed_by = Some(reviewer);
self.reviewed_at = Some(Utc::now());
self.review_notes = notes;
}
pub fn deny(&mut self, reviewer: String, notes: Option<String>) {
self.status = AccessRequestStatus::Denied;
self.reviewed_by = Some(reviewer);
self.reviewed_at = Some(Utc::now());
self.review_notes = notes;
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AccessRequestStatus {
Pending,
Approved,
Denied,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AccessAuditLog {
pub log_id: String,
pub service_identity: ServiceIdentity,
pub secret_id: String,
pub access_type: AccessType,
pub result: AccessResult,
pub timestamp: DateTime<Utc>,
pub ip_address: Option<String>,
pub context: HashMap<String, String>,
}
impl AccessAuditLog {
pub fn new(
service_identity: ServiceIdentity,
secret_id: String,
access_type: AccessType,
result: AccessResult,
) -> Self {
Self {
log_id: Uuid::new_v4().to_string(),
service_identity,
secret_id,
access_type,
result,
timestamp: Utc::now(),
ip_address: None,
context: HashMap::new(),
}
}
pub fn with_context(mut self, key: String, value: String) -> Self {
self.context.insert(key, value);
self
}
pub fn with_ip(mut self, ip: String) -> Self {
self.ip_address = Some(ip);
self
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AccessType {
Read,
Write,
Delete,
List,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AccessResult {
Allowed,
Denied,
Error,
}
#[async_trait]
pub trait SecretAccessStorage: Send + Sync {
async fn store_service_identity(
&self,
identity: &ServiceIdentity,
) -> Result<(), SecretAccessError>;
async fn get_service_identity(
&self,
service_id: &str,
) -> Result<Option<ServiceIdentity>, SecretAccessError>;
async fn store_secret_scope(&self, scope: &SecretScope) -> Result<(), SecretAccessError>;
async fn get_secret_scope(
&self,
secret_id: &str,
) -> Result<Option<SecretScope>, SecretAccessError>;
async fn store_access_grant(&self, grant: &AccessGrant) -> Result<(), SecretAccessError>;
async fn get_access_grant(
&self,
service_id: &str,
secret_id: &str,
) -> Result<Option<AccessGrant>, SecretAccessError>;
async fn update_access_grant(&self, grant: &AccessGrant) -> Result<(), SecretAccessError>;
async fn store_access_request(&self, request: &AccessRequest) -> Result<(), SecretAccessError>;
async fn get_access_request(
&self,
request_id: &str,
) -> Result<Option<AccessRequest>, SecretAccessError>;
async fn update_access_request(&self, request: &AccessRequest)
-> Result<(), SecretAccessError>;
async fn store_audit_log(&self, log: &AccessAuditLog) -> Result<(), SecretAccessError>;
async fn get_audit_logs(
&self,
secret_id: &str,
limit: usize,
) -> Result<Vec<AccessAuditLog>, SecretAccessError>;
}
pub struct InMemorySecretAccessStorage {
service_identities: tokio::sync::RwLock<HashMap<String, ServiceIdentity>>,
secret_scopes: tokio::sync::RwLock<HashMap<String, SecretScope>>,
access_grants: tokio::sync::RwLock<HashMap<(String, String), AccessGrant>>,
access_requests: tokio::sync::RwLock<HashMap<String, AccessRequest>>,
audit_logs: tokio::sync::RwLock<Vec<AccessAuditLog>>,
}
impl InMemorySecretAccessStorage {
pub fn new() -> Self {
Self {
service_identities: tokio::sync::RwLock::new(HashMap::new()),
secret_scopes: tokio::sync::RwLock::new(HashMap::new()),
access_grants: tokio::sync::RwLock::new(HashMap::new()),
access_requests: tokio::sync::RwLock::new(HashMap::new()),
audit_logs: tokio::sync::RwLock::new(Vec::new()),
}
}
}
impl Default for InMemorySecretAccessStorage {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl SecretAccessStorage for InMemorySecretAccessStorage {
async fn store_service_identity(
&self,
identity: &ServiceIdentity,
) -> Result<(), SecretAccessError> {
let mut identities = self.service_identities.write().await;
identities.insert(identity.service_id.clone(), identity.clone());
Ok(())
}
async fn get_service_identity(
&self,
service_id: &str,
) -> Result<Option<ServiceIdentity>, SecretAccessError> {
let identities = self.service_identities.read().await;
Ok(identities.get(service_id).cloned())
}
async fn store_secret_scope(&self, scope: &SecretScope) -> Result<(), SecretAccessError> {
let mut scopes = self.secret_scopes.write().await;
scopes.insert(scope.secret_id.clone(), scope.clone());
Ok(())
}
async fn get_secret_scope(
&self,
secret_id: &str,
) -> Result<Option<SecretScope>, SecretAccessError> {
let scopes = self.secret_scopes.read().await;
Ok(scopes.get(secret_id).cloned())
}
async fn store_access_grant(&self, grant: &AccessGrant) -> Result<(), SecretAccessError> {
let mut grants = self.access_grants.write().await;
let key = (
grant.service_identity.service_id.clone(),
grant.secret_id.clone(),
);
grants.insert(key, grant.clone());
Ok(())
}
async fn get_access_grant(
&self,
service_id: &str,
secret_id: &str,
) -> Result<Option<AccessGrant>, SecretAccessError> {
let grants = self.access_grants.read().await;
let key = (service_id.to_string(), secret_id.to_string());
Ok(grants.get(&key).cloned())
}
async fn update_access_grant(&self, grant: &AccessGrant) -> Result<(), SecretAccessError> {
self.store_access_grant(grant).await
}
async fn store_access_request(&self, request: &AccessRequest) -> Result<(), SecretAccessError> {
let mut requests = self.access_requests.write().await;
requests.insert(request.request_id.clone(), request.clone());
Ok(())
}
async fn get_access_request(
&self,
request_id: &str,
) -> Result<Option<AccessRequest>, SecretAccessError> {
let requests = self.access_requests.read().await;
Ok(requests.get(request_id).cloned())
}
async fn update_access_request(
&self,
request: &AccessRequest,
) -> Result<(), SecretAccessError> {
self.store_access_request(request).await
}
async fn store_audit_log(&self, log: &AccessAuditLog) -> Result<(), SecretAccessError> {
let mut logs = self.audit_logs.write().await;
logs.push(log.clone());
Ok(())
}
async fn get_audit_logs(
&self,
secret_id: &str,
limit: usize,
) -> Result<Vec<AccessAuditLog>, SecretAccessError> {
let logs = self.audit_logs.read().await;
let mut filtered: Vec<_> = logs
.iter()
.filter(|log| log.secret_id == secret_id)
.cloned()
.collect();
filtered.sort_by_key(|a| std::cmp::Reverse(a.timestamp));
Ok(filtered.into_iter().take(limit).collect())
}
}
pub struct SecretAccessControlManager<S: SecretAccessStorage> {
storage: S,
}
impl<S: SecretAccessStorage> SecretAccessControlManager<S> {
pub fn new(storage: S) -> Self {
Self { storage }
}
pub async fn register_service(
&self,
identity: ServiceIdentity,
) -> Result<ServiceIdentity, SecretAccessError> {
self.storage.store_service_identity(&identity).await?;
info!(
"Registered service {} ({})",
identity.name, identity.service_id
);
Ok(identity)
}
pub async fn create_secret_scope(
&self,
scope: SecretScope,
) -> Result<SecretScope, SecretAccessError> {
self.storage.store_secret_scope(&scope).await?;
info!("Created secret scope {} ({})", scope.name, scope.secret_id);
Ok(scope)
}
pub async fn check_access(
&self,
service_id: &str,
secret_id: &str,
access_type: AccessType,
) -> Result<(), SecretAccessError> {
let service = self
.storage
.get_service_identity(service_id)
.await?
.ok_or(SecretAccessError::ServiceNotAuthenticated)?;
let scope = self
.storage
.get_secret_scope(secret_id)
.await?
.ok_or_else(|| SecretAccessError::SecretNotFound(secret_id.to_string()))?;
if !scope.is_service_allowed(service_id) {
self.log_access(
service.clone(),
secret_id.to_string(),
access_type,
AccessResult::Denied,
)
.await?;
return Err(SecretAccessError::ServiceNotAuthorized {
secret_id: secret_id.to_string(),
});
}
if !scope.is_environment_allowed(service.environment) {
self.log_access(
service.clone(),
secret_id.to_string(),
access_type,
AccessResult::Denied,
)
.await?;
return Err(SecretAccessError::AccessDenied(format!(
"Environment {:?} not allowed",
service.environment
)));
}
let service_trust = service.service_type.default_trust_level();
if service_trust < scope.required_trust_level {
self.log_access(
service.clone(),
secret_id.to_string(),
access_type,
AccessResult::Denied,
)
.await?;
return Err(SecretAccessError::AccessDenied(format!(
"Insufficient trust level: {:?} < {:?}",
service_trust, scope.required_trust_level
)));
}
if let Some(mut grant) = self.storage.get_access_grant(service_id, secret_id).await? {
if grant.is_expired() {
self.log_access(
service.clone(),
secret_id.to_string(),
access_type,
AccessResult::Denied,
)
.await?;
return Err(SecretAccessError::AccessGrantExpired(
grant.expires_at.unwrap(),
));
}
grant.record_access();
self.storage.update_access_grant(&grant).await?;
} else if scope.require_approval {
self.log_access(
service.clone(),
secret_id.to_string(),
access_type,
AccessResult::Denied,
)
.await?;
return Err(SecretAccessError::AccessDenied(
"Approval required for this secret".to_string(),
));
}
self.log_access(
service,
secret_id.to_string(),
access_type,
AccessResult::Allowed,
)
.await?;
info!(
"Access granted: service {} -> secret {}",
service_id, secret_id
);
Ok(())
}
pub async fn grant_access(
&self,
service_id: &str,
secret_id: &str,
granted_by: String,
reason: String,
expires_in_seconds: Option<i64>,
) -> Result<AccessGrant, SecretAccessError> {
let service = self
.storage
.get_service_identity(service_id)
.await?
.ok_or(SecretAccessError::ServiceNotAuthenticated)?;
let expires_at = expires_in_seconds.map(|secs| Utc::now() + Duration::seconds(secs));
let grant = AccessGrant::new(
service,
secret_id.to_string(),
granted_by,
reason,
expires_at,
);
self.storage.store_access_grant(&grant).await?;
info!(
"Access granted: service {} -> secret {}",
service_id, secret_id
);
Ok(grant)
}
pub async fn request_access(
&self,
service_id: &str,
secret_id: &str,
requested_by: String,
reason: String,
) -> Result<AccessRequest, SecretAccessError> {
let service = self
.storage
.get_service_identity(service_id)
.await?
.ok_or(SecretAccessError::ServiceNotAuthenticated)?;
let request = AccessRequest::new(service, secret_id.to_string(), requested_by, reason);
self.storage.store_access_request(&request).await?;
info!(
"Access requested: service {} -> secret {}",
service_id, secret_id
);
Ok(request)
}
pub async fn approve_request(
&self,
request_id: &str,
reviewer: String,
notes: Option<String>,
expires_in_seconds: Option<i64>,
) -> Result<AccessGrant, SecretAccessError> {
let mut request = self
.storage
.get_access_request(request_id)
.await?
.ok_or_else(|| SecretAccessError::Storage("Access request not found".to_string()))?;
request.approve(reviewer.clone(), notes);
self.storage.update_access_request(&request).await?;
let grant = self
.grant_access(
&request.service_identity.service_id,
&request.secret_id,
reviewer,
request.reason.clone(),
expires_in_seconds,
)
.await?;
info!("Access request approved: {}", request_id);
Ok(grant)
}
pub async fn deny_request(
&self,
request_id: &str,
reviewer: String,
notes: Option<String>,
) -> Result<(), SecretAccessError> {
let mut request = self
.storage
.get_access_request(request_id)
.await?
.ok_or_else(|| SecretAccessError::Storage("Access request not found".to_string()))?;
request.deny(reviewer, notes);
self.storage.update_access_request(&request).await?;
info!("Access request denied: {}", request_id);
Ok(())
}
async fn log_access(
&self,
service: ServiceIdentity,
secret_id: String,
access_type: AccessType,
result: AccessResult,
) -> Result<(), SecretAccessError> {
let log = AccessAuditLog::new(service, secret_id, access_type, result);
self.storage.store_audit_log(&log).await?;
Ok(())
}
pub async fn get_audit_logs(
&self,
secret_id: &str,
limit: usize,
) -> Result<Vec<AccessAuditLog>, SecretAccessError> {
self.storage.get_audit_logs(secret_id, limit).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_service_registration() {
let storage = InMemorySecretAccessStorage::new();
let manager = SecretAccessControlManager::new(storage);
let identity = ServiceIdentity::new(
"mfa-service".to_string(),
ServiceType::MfaService,
TenantId::new("test_tenant"),
Environment::Production,
);
let registered = manager.register_service(identity.clone()).await.unwrap();
assert_eq!(registered.service_id, identity.service_id);
}
#[tokio::test]
async fn test_access_control() {
let storage = InMemorySecretAccessStorage::new();
let manager = SecretAccessControlManager::new(storage);
let service = ServiceIdentity::new(
"mfa-service".to_string(),
ServiceType::MfaService,
TenantId::new("test_tenant"),
Environment::Production,
);
manager.register_service(service.clone()).await.unwrap();
let scope = SecretScope::new(
"totp-master-key".to_string(),
SecretType::TotpSecret,
TenantId::new("test_tenant"),
TrustLevel::High,
)
.allow_service(service.service_id.clone());
manager.create_secret_scope(scope.clone()).await.unwrap();
assert!(manager
.check_access(&service.service_id, &scope.secret_id, AccessType::Read)
.await
.is_ok());
}
#[tokio::test]
async fn test_access_denied() {
let storage = InMemorySecretAccessStorage::new();
let manager = SecretAccessControlManager::new(storage);
let service = ServiceIdentity::new(
"analytics-service".to_string(),
ServiceType::AnalyticsService,
TenantId::new("test_tenant"),
Environment::Production,
);
manager.register_service(service.clone()).await.unwrap();
let scope = SecretScope::new(
"totp-master-key".to_string(),
SecretType::TotpSecret,
TenantId::new("test_tenant"),
TrustLevel::High, );
manager.create_secret_scope(scope.clone()).await.unwrap();
assert!(manager
.check_access(&service.service_id, &scope.secret_id, AccessType::Read)
.await
.is_err());
}
#[tokio::test]
async fn test_access_request_workflow() {
let storage = InMemorySecretAccessStorage::new();
let manager = SecretAccessControlManager::new(storage);
let service = ServiceIdentity::new(
"api-gateway".to_string(),
ServiceType::ApiGateway,
TenantId::new("test_tenant"),
Environment::Production,
);
manager.register_service(service.clone()).await.unwrap();
let scope = SecretScope::new(
"jwt-signing-key".to_string(),
SecretType::JwtSigningKey,
TenantId::new("test_tenant"),
TrustLevel::Medium,
)
.with_approval_required(true);
manager.create_secret_scope(scope.clone()).await.unwrap();
let request = manager
.request_access(
&service.service_id,
&scope.secret_id,
"dev@example.com".to_string(),
"Need to sign JWTs for API".to_string(),
)
.await
.unwrap();
let grant = manager
.approve_request(
&request.request_id,
"admin@example.com".to_string(),
Some("Approved for production use".to_string()),
Some(86400), )
.await
.unwrap();
assert!(!grant.is_expired());
assert!(manager
.check_access(&service.service_id, &scope.secret_id, AccessType::Read)
.await
.is_ok());
}
}