1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use crate::errors;
use crate::{Host, Result, WasccEntity};
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
use wascap::jwt::Token;
use wascap::prelude::*;

pub(crate) type ClaimsMap = Arc<RwLock<HashMap<String, Claims<wascap::jwt::Actor>>>>;

/// An authorizer is responsible for determining whether an actor can be loaded as well as
/// whether an actor can invoke another entity. For invocation checks, the authorizer is only ever invoked _after_
/// an initial capability attestation check has been performed and _passed_. This has the net effect of making it
/// impossible to override the base behavior of checking that an actor's embedded JWT contains the right
/// capability attestations.
pub trait Authorizer: Sync + Send {
    /// This check is performed during the `add_actor` call, allowing the custom authorizer to do things
    /// like verify a provenance chain, make external calls, etc.
    fn can_load(&self, claims: &Claims<Actor>) -> bool;
    /// This check will be performed for _every_ invocation that has passed the base capability check,
    /// including the operation that occurs during `bind_actor`. Developers should be aware of this because
    /// if `set_authorizer` is done _after_ actor binding, it could potentially allow an unauthorized binding.
    fn can_invoke(&self, claims: &Claims<Actor>, target: &WasccEntity, operation: &str) -> bool;
}

pub(crate) struct DefaultAuthorizer {}

impl DefaultAuthorizer {
    pub fn new() -> impl Authorizer {
        DefaultAuthorizer {}
    }
}

impl Authorizer for DefaultAuthorizer {
    fn can_load(&self, _claims: &Claims<Actor>) -> bool {
        true
    }

    // This doesn't actually mean everyone can invoke everything. Remember that the host itself
    // will _always_ enforce the claims check on an actor having the required capability
    // attestation
    fn can_invoke(&self, _claims: &Claims<Actor>, target: &WasccEntity, _operation: &str) -> bool {
        match target {
            WasccEntity::Actor(_a) => true,
            WasccEntity::Capability { .. } => true,
        }
    }
}

pub(crate) fn get_all_claims(map: ClaimsMap) -> Vec<(String, Claims<wascap::jwt::Actor>)> {
    map.read()
        .unwrap()
        .iter()
        .map(|(pk, claims)| (pk.clone(), claims.clone()))
        .collect()
}

// We don't (yet) support per-operation security constraints, but when we do, this
// function will be ready to support that without breaking everyone else's calls
pub(crate) fn can_invoke(
    claims: &Claims<wascap::jwt::Actor>,
    capability_id: &str,
    _operation: &str,
) -> bool {
    // Edge case - deliver configuration to an actor directly,
    // so "self invocation" needs to be authorized
    if claims.subject == capability_id {
        return true;
    }
    claims
        .metadata
        .as_ref()
        .unwrap()
        .caps
        .as_ref()
        .map_or(false, |caps| caps.contains(&capability_id.to_string()))
}

// Extract claims from the JWT embedded in the wasm module's custom section
pub(crate) fn extract_claims(buf: &[u8]) -> Result<wascap::jwt::Token<wascap::jwt::Actor>> {
    let token = wascap::wasm::extract_claims(buf)?;
    match token {
        Some(token) => {
            let claims = token.claims.clone();
            let caps = claims.metadata.as_ref().unwrap().caps.clone();
            info!(
                "Actor claims loaded for {} - {}",
                &claims.subject,
                caps.unwrap_or(vec![]).join(",")
            );
            Ok(token)
        }
        None => Err(errors::new(errors::ErrorKind::Authorization(
            "No embedded JWT in actor module".to_string(),
        ))),
    }
}

pub(crate) fn enforce_validation(jwt: &str) -> Result<()> {
    let v = validate_token::<wascap::jwt::Actor>(jwt)?;
    if v.expired {
        Err(errors::new(errors::ErrorKind::Authorization(
            "Expired token".to_string(),
        )))
    } else if v.cannot_use_yet {
        Err(errors::new(errors::ErrorKind::Authorization(format!(
            "Module cannot be used before {}",
            v.not_before_human
        ))))
    } else {
        Ok(())
    }
}

pub(crate) fn register_claims(
    claims_map: ClaimsMap,
    subject: &str,
    claims: Claims<wascap::jwt::Actor>,
) {
    claims_map
        .write()
        .unwrap()
        .insert(subject.to_string(), claims);
}

pub(crate) fn unregister_claims(claims_map: ClaimsMap, subject: &str) {
    {
        let mut lock = claims_map.write().unwrap();
        let _ = lock.remove(subject);
    }
}

impl Host {
    pub(crate) fn check_auth(&self, token: &Token<wascap::jwt::Actor>) -> bool {
        self.authorizer.read().unwrap().can_load(&token.claims)
    }
}