use serde_json::{Value as JsonValue};
use thiserror::Error;
use crate::{
deserialization::{object_to_id, parse_into_id_array},
utils::CoreType,
};
const ATTRIBUTED_TO: &str = "attributedTo";
const ACTOR: &str = "actor";
const OWNER: &str = "owner";
const CONTROLLER: &str = "controller";
#[derive(Debug, Error)]
#[error("{0}")]
pub struct ObjectError(&'static str);
impl ObjectError {
pub fn message(&self) -> &'static str { self.0 }
}
pub fn parse_attributed_to(
value: &JsonValue,
) -> Result<Option<String>, ObjectError> {
let maybe_attributed_to = parse_into_id_array(value)
.map_err(|_| ObjectError("invalid 'attributedTo' property"))?
.first()
.cloned();
Ok(maybe_attributed_to)
}
fn deny_properties(
object: &JsonValue,
properties: &[&str],
) -> Result<(), ObjectError> {
for property in properties {
if !object[property].is_null() {
return Err(ObjectError("ambiguous ownership"));
};
};
Ok(())
}
pub fn get_owner(
object: &JsonValue,
core_type: CoreType,
) -> Result<String, ObjectError> {
let maybe_owner = match core_type {
CoreType::Object | CoreType::Collection => {
parse_attributed_to(&object[ATTRIBUTED_TO])?
.ok_or(ObjectError("'attributedTo' property is missing"))
},
CoreType::Link => Err(ObjectError("link doesn't have an owner")),
CoreType::Actor => {
object["id"].as_str()
.map(|id| id.to_owned())
.ok_or(ObjectError("'id' property is missing"))
},
CoreType::Activity => {
object_to_id(&object[ACTOR])
.map_err(|_| ObjectError("invalid 'actor' property"))
},
CoreType::PublicKey => {
object[OWNER].as_str()
.map(|id| id.to_owned())
.ok_or(ObjectError("'owner' property is missing"))
},
CoreType::VerificationMethod => {
object[CONTROLLER].as_str()
.map(|id| id.to_owned())
.ok_or(ObjectError("'controller' property is missing"))
},
};
let owner = maybe_owner?;
Ok(owner)
}
pub fn is_ownership_ambiguous(
object: &JsonValue,
core_type: CoreType,
) -> bool {
match core_type {
CoreType::Actor
| CoreType::Activity
| CoreType::PublicKey
| CoreType::VerificationMethod
=>
{
deny_properties(object, &[ATTRIBUTED_TO]).is_err()
},
_ => false,
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn test_parse_attributed_to_array() {
let object = json!({
"id": "https://social.example/objects/123",
"type": "Note",
"attributedTo": [
"https://social.example/actors/1",
"https://social.example/actors/2",
],
});
let attributed_to = parse_attributed_to(&object["attributedTo"])
.unwrap().unwrap();
assert_eq!(attributed_to, "https://social.example/actors/1");
}
#[test]
fn test_get_owner_object() {
let object = json!({
"id": "https://social.example/objects/123",
"type": "Note",
"attributedTo": "https://social.example/actors/1",
});
let owner = get_owner(&object, CoreType::Object).unwrap();
assert_eq!(owner, "https://social.example/actors/1");
let error = get_owner(&object, CoreType::Activity).err().unwrap();
assert_eq!(error.message(), "invalid 'actor' property");
}
#[test]
fn test_get_owner_actor() {
let object = json!({
"id": "https://social.example/actors/1",
"type": "Note",
"inbox": "https://social.example/actors/1/inbox",
"outbox": "https://social.example/actors/1/outbox",
});
let owner = get_owner(&object, CoreType::Actor).unwrap();
assert_eq!(owner, "https://social.example/actors/1");
let is_ambiguous = is_ownership_ambiguous(&object, CoreType::Actor);
assert_eq!(is_ambiguous, false);
}
#[test]
fn test_get_owner_actor_with_attributed_to() {
let object = json!({
"id": "https://social.example/actors/1",
"type": "Note",
"inbox": "https://social.example/actors/1/inbox",
"outbox": "https://social.example/actors/1/outbox",
"attributedTo": "https://social.example/actors/2",
});
let owner = get_owner(&object, CoreType::Actor).unwrap();
assert_eq!(owner, "https://social.example/actors/1");
let is_ambiguous = is_ownership_ambiguous(&object, CoreType::Actor);
assert_eq!(is_ambiguous, true);
}
}