use crate::org_membership::MembershipOutcome;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InboundMode {
Auto,
Notify,
}
pub trait OrgPolicy {
fn inbound_mode(&self, org_did: &str) -> Option<InboundMode>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PairAction {
AutoOrgVerified { org_did: String },
NotifyOrgEligible { org_did: String },
Manual,
}
pub fn decide(outcome: &MembershipOutcome, policy: &dyn OrgPolicy) -> PairAction {
let org_dids = match outcome {
MembershipOutcome::Verified { org_dids, .. } => org_dids,
MembershipOutcome::NoClaim | MembershipOutcome::Rejected { .. } => {
return PairAction::Manual;
}
};
if let Some(org_did) = org_dids
.iter()
.find(|&od| policy.inbound_mode(od) == Some(InboundMode::Auto))
{
return PairAction::AutoOrgVerified {
org_did: org_did.clone(),
};
}
if let Some(org_did) = org_dids
.iter()
.find(|&od| policy.inbound_mode(od) == Some(InboundMode::Notify))
{
return PairAction::NotifyOrgEligible {
org_did: org_did.clone(),
};
}
PairAction::Manual
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
struct MapPolicy(HashMap<String, InboundMode>);
impl OrgPolicy for MapPolicy {
fn inbound_mode(&self, org_did: &str) -> Option<InboundMode> {
self.0.get(org_did).copied()
}
}
fn verified(orgs: &[&str]) -> MembershipOutcome {
MembershipOutcome::Verified {
op_did:
"did:wire:op:darby-0000000000000000000000000000000000000000000000000000000000000000"
.into(),
org_dids: orgs.iter().map(|s| s.to_string()).collect(),
}
}
fn policy(entries: &[(&str, InboundMode)]) -> MapPolicy {
MapPolicy(entries.iter().map(|(k, v)| (k.to_string(), *v)).collect())
}
#[test]
fn auto_when_org_opted_into_auto() {
let out = verified(&["did:wire:org:slanchaai-1"]);
let pol = policy(&[("did:wire:org:slanchaai-1", InboundMode::Auto)]);
assert_eq!(
decide(&out, &pol),
PairAction::AutoOrgVerified {
org_did: "did:wire:org:slanchaai-1".into()
}
);
}
#[test]
fn notify_when_org_is_notify() {
let out = verified(&["did:wire:org:slanchaai-1"]);
let pol = policy(&[("did:wire:org:slanchaai-1", InboundMode::Notify)]);
assert_eq!(
decide(&out, &pol),
PairAction::NotifyOrgEligible {
org_did: "did:wire:org:slanchaai-1".into()
}
);
}
#[test]
fn manual_when_org_untrusted() {
let out = verified(&["did:wire:org:stranger-1"]);
let pol = policy(&[("did:wire:org:slanchaai-1", InboundMode::Auto)]);
assert_eq!(decide(&out, &pol), PairAction::Manual);
}
#[test]
fn auto_beats_notify_across_orgs() {
let out = verified(&["did:wire:org:notifyco-1", "did:wire:org:autoco-1"]);
let pol = policy(&[
("did:wire:org:notifyco-1", InboundMode::Notify),
("did:wire:org:autoco-1", InboundMode::Auto),
]);
assert_eq!(
decide(&out, &pol),
PairAction::AutoOrgVerified {
org_did: "did:wire:org:autoco-1".into()
}
);
}
#[test]
fn manual_on_no_claim() {
let pol = policy(&[("did:wire:org:slanchaai-1", InboundMode::Auto)]);
assert_eq!(
decide(&MembershipOutcome::NoClaim, &pol),
PairAction::Manual
);
}
#[test]
fn manual_on_rejected() {
let pol = policy(&[("did:wire:org:slanchaai-1", InboundMode::Auto)]);
let rejected = MembershipOutcome::Rejected {
reason: "forged".into(),
};
assert_eq!(decide(&rejected, &pol), PairAction::Manual);
}
}