use http::StatusCode;
use ocm_types::{
common::ShareType,
discovery::{Criterium, Discovery},
error::{Error, ValidationError},
share::{
NewShare, SendingServer, ShareCreationResponse,
},
};
use serde::Serialize;
use crate::drivers::{
protocols::MultiProtocol,
shares::{ReceivedShareRepo, ShareRepoError},
users::{UserRepo, UserRepoError},
} ;
pub async fn receive_share<S: ReceivedShareRepo, U: UserRepo>(
received_share_repo: &S,
user_repo: &U,
ocm_server_fqdn: &str,
new_share: NewShare,
criteria: Vec<Criterium>,
supported_protocols: &Vec<Box<dyn crate::drivers::protocols::Protocol>>,
sending_server_discovery: &Discovery,
authenticated_sending_server: Option<SendingServer>,
) -> Result<ShareCreationResponse, ReceiveShareError> {
let sending_server = new_share.sending_server();
if criteria.contains(&Criterium::HttpRequestSignatures) {
match authenticated_sending_server {
None => {
Err(ReceiveShareError::MissingHttpSignature)?;
}
Some(authenticated_sending_server)
if sending_server != authenticated_sending_server =>
{
dbg!(&sending_server, &authenticated_sending_server);
Err(ReceiveShareError::InvalidHttpSignature)?;
}
Some(_) => (),
}
}
let protocols = normalize_protocols(&new_share, supported_protocols, sending_server_discovery)?;
if new_share.share_with.get_server_url() != ocm_server_fqdn {
Err(ReceiveShareError::InvalidShareWith(format!(
"Recipient FQDN '{}' must match FQDN of this OCM Server '{}'",
new_share.share_with.get_server_url(),
ocm_server_fqdn
)))?
};
let recipient_user_id = new_share.share_with.get_identifier();
let recipient = if new_share.share_type == ShareType::User {
user_repo.get(recipient_user_id).await?
} else {
Err(ReceiveShareError::UnsupportedShare(
"Currently only share type 'user' is supported!".to_string(),
))?
};
received_share_repo.insert(sending_server, (new_share, protocols)).await?;
Ok(ShareCreationResponse {
recipient_display_name: Some(recipient.name),
recipient_public_keys: vec![] })
}
fn normalize_protocols(
new_share: &NewShare,
supported_protocols: &Vec<Box<dyn crate::drivers::protocols::Protocol + 'static>>,
sending_server_discovery: &Discovery,
) -> Result<MultiProtocol, ReceiveShareError> {
let normalized_protocol_properties = supported_protocols
.iter()
.filter_map(|protocol| {
protocol
.resolve_client_properties(sending_server_discovery, new_share)
.ok()
})
.fold(MultiProtocol::default(), |acc, p| {
acc.merge(p)
});
if normalized_protocol_properties.is_empty() {
Err(ReceiveShareError::UnsupportedShare(
"Share is not accessible via any supported protocol.".to_string(),
))?
};
Ok(normalized_protocol_properties)
}
#[derive(Debug, Clone, Serialize)]
#[serde(into = "Error")]
pub enum ReceiveShareError {
InvalidShareWith(String),
UserNotFound { user: String },
UnsupportedShare(String),
InvalidSender(String),
MissingHttpSignature,
InvalidHttpSignature,
}
impl From<ReceiveShareError> for Error {
fn from(value: ReceiveShareError) -> Self {
match value {
ReceiveShareError::UserNotFound { user } => Error {
message: format!("User {user} does not exist"),
validation_errors: vec![],
},
ReceiveShareError::UnsupportedShare(message) => Error {
message,
validation_errors: vec![],
},
ReceiveShareError::InvalidShareWith(share_with) => Error {
message: format!("INVALID_SHARE_WITH {share_with}"),
validation_errors: vec![],
},
ReceiveShareError::InvalidSender(sender) => Error {
message: format!("INVALID_SENDER {sender}"),
validation_errors: vec![],
},
ReceiveShareError::MissingHttpSignature => Error {
message: "MISSING_HTTP_SIGNATURE".to_string(),
validation_errors: vec![],
},
ReceiveShareError::InvalidHttpSignature => Error {
message: "INVALID_HTTP_SIGNATURE".to_string(),
validation_errors: vec![ValidationError {
name: None,
message: Some(
"Sending Server from the http signature must match the FQDN of the sender"
.to_string(),
),
}],
},
}
}
}
impl ReceiveShareError {
pub fn status_code(&self) -> StatusCode {
match self {
ReceiveShareError::InvalidShareWith(_) => StatusCode::NOT_ACCEPTABLE,
ReceiveShareError::UserNotFound { .. } => StatusCode::NOT_FOUND,
ReceiveShareError::UnsupportedShare(_) => StatusCode::NOT_ACCEPTABLE,
ReceiveShareError::InvalidSender(_) => StatusCode::NOT_ACCEPTABLE,
ReceiveShareError::MissingHttpSignature => StatusCode::UNAUTHORIZED,
ReceiveShareError::InvalidHttpSignature => StatusCode::UNAUTHORIZED,
}
}
}
impl From<UserRepoError> for ReceiveShareError {
fn from(value: UserRepoError) -> Self {
match value {
UserRepoError::RepoAccessFailed => todo!(),
UserRepoError::NotFound(user) => ReceiveShareError::UserNotFound { user },
}
}
}
impl From<ShareRepoError> for ReceiveShareError {
fn from(value: ShareRepoError) -> Self {
match value {
ShareRepoError::NotFound => panic!("This should not happen"),
}
}
}