mod receive_share;
mod send_share;
pub use receive_share::*;
pub use send_share::*;
pub const SHARE_ENDPOINT: &str = "/shares";
#[cfg(test)]
mod tests {
use std::{collections::{HashMap, HashSet}, str::FromStr};
use http::Uri;
use ocm_types::{
discovery::{Capability, Criterium, Discovery},
share::NewShare,
};
use serde::Serialize;
use serde_json::Value;
use crate::{
common::HttpClient,
drivers::{
protocols::{MultiProtocol, Permission, Protocol},
resources::Resource,
shares::{InMemoryShareRepo, ReceivedShareRepo},
users::{InMemoryUserRepo, User, UserRepo},
},
};
use super::*;
struct TestClient<U: UserRepo, R: ReceivedShareRepo> {
pub users: U,
pub received_shares: R,
}
impl TestClient<InMemoryUserRepo, InMemoryShareRepo> {
fn new() -> Self {
Self {
users: InMemoryUserRepo::default(),
received_shares: InMemoryShareRepo::default(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, PartialOrd, Serialize)]
struct TestResource();
impl Resource for TestResource {
const RESOURCE_TYPE: &str = "test-resource";
fn uri(&self) -> &str {
"example.org/test-resource"
}
fn name(&self) -> &str {
"TestResource"
}
}
#[derive(Debug, Clone)]
struct TestProtocol(Uri);
impl Protocol for TestProtocol {
fn new(endpoint: Uri) -> Self
where
Self: Sized,
{
Self(endpoint)
}
fn share_resource(
&self,
_provider_id: &crate::drivers::shares::ProviderId,
_permissions: &HashSet<Permission>
) -> Result<MultiProtocol, &'static str> {
Ok(MultiProtocol {
additional_protocols: HashMap::from_iter([(
TestProtocol::new(self.endpoint().to_owned())
.identifier()
.to_string(),
Value::Null,
)]),
..Default::default()
})
}
fn endpoint(&self) -> &Uri {
&self.0
}
fn identifier(&self) -> &'static str {
"test-protocol"
}
fn supported_resource_types(&self) -> &[&str] {
&[TestResource::RESOURCE_TYPE]
}
fn resolve_client_properties(
&self,
_sending_server: &Discovery,
new_share: &NewShare,
) -> Result<
crate::drivers::protocols::MultiProtocol,
crate::drivers::protocols::ProtocolError,
> {
Ok(crate::drivers::protocols::MultiProtocol {
additional_protocols: new_share.protocol.additional_protocols.clone(),
..Default::default()
})
}
}
impl HttpClient for TestClient<InMemoryUserRepo, InMemoryShareRepo> {
async fn get(&self, _url: &Uri) -> Result<String, String> {
println!("Sharing tests should not require discovery");
Err("NOT_FOUND".to_string())
}
async fn post(&self, url: &Uri, body: serde_json::Value) -> Result<String, String> {
match url.to_string().as_str() {
"https://test-receiver.example.org/shares" => receive_share(
&self.received_shares,
&self.users,
"test-receiver.example.org",
serde_json::from_value(body).unwrap(),
vec![Criterium::HttpRequestSignatures],
&vec![Box::new(TestProtocol::new(
Uri::from_str("/something").unwrap(),
))],
&Discovery::default(),
Some("test-provider.example.org".into()),
)
.await
.map(|ok| serde_json::to_string(&ok).unwrap())
.map_err(|err| serde_json::to_string(&err).unwrap()),
_ => Err("NOT_FOUND".to_string()),
}
}
fn allow_http(&self) -> bool {
false
}
}
#[tokio::test]
async fn send_receive_share_with_endpoint_scheme() {
let discovery = Discovery {
enabled: true,
api_version: "1.2.0".to_string(),
end_point: "https://test-receiver.example.org".to_string(),
provider: Some("Test Receiver".to_string()),
capabilities: Some(vec![Capability::ProtocolObject]),
..Default::default()
};
let http_client = TestClient::new();
send_receive_share(http_client, &discovery).await;
}
#[tokio::test]
async fn send_receive_share_without_endpoint_scheme() {
let discovery = Discovery {
enabled: true,
api_version: "1.2.0".to_string(),
end_point: "test-receiver.example.org".to_string(),
provider: Some("Test Receiver".to_string()),
capabilities: Some(vec![Capability::ProtocolObject]),
..Default::default()
};
let http_client = TestClient::new();
send_receive_share(http_client, &discovery).await;
}
async fn send_receive_share(
http_client: TestClient<InMemoryUserRepo, InMemoryShareRepo>,
receiving_server: &Discovery,
) {
let test_protocol = TestProtocol::new("/test".parse().unwrap());
let protocol = test_protocol
.share_resource(&"test".to_string().into(), &HashSet::new())
.unwrap();
let new_share = NewShare {
share_with: "recipient@test-receiver.example.org".try_into().unwrap(),
name: "TestResource".to_string(),
description: None,
provider_id: "test".to_string(),
owner: "owner@test-provider.example.org".try_into().unwrap(),
sender: "owner@test-provider.example.org".try_into().unwrap(),
owner_display_name: None,
sender_display_name: None,
share_type: ocm_types::common::ShareType::User,
resource_type: "test-resource".to_string(),
expiration: None,
protocol: protocol.clone().into(),
};
send_share(
&http_client,
"test".to_string(),
"owner@test-provider.example.org".try_into().unwrap(),
receiving_server,
"recipient@test-receiver.example.org".try_into().unwrap(),
&TestResource(),
protocol.clone(),
)
.await
.expect_err("Share to non-existing recipient should be rejected");
http_client
.users
.insert(User {
id: "recipient".to_string(),
name: "Receiving Party".to_string(),
})
.await
.unwrap();
send_share(
&http_client,
"test".to_string(),
"owner@test-provider.example.org".try_into().unwrap(),
receiving_server,
"recipient@test-receiver.example.org".try_into().unwrap(),
&TestResource(),
protocol,
)
.await
.expect("Share should have been created");
let received_share = http_client
.received_shares
.get("test-provider.example.org".into(), "test")
.await
.expect("Received share was not stored");
assert_eq!(new_share, received_share.0);
}
}