lenso-capability-auth 0.1.1

Generated Auth Capability contracts for Lenso vNext.
Documentation
// @generated by lenso-contract-codegen; do not edit.
use std::{fmt, rc::Rc};
use futures::future::LocalBoxFuture;
use lenso_kernel::{InvocationContext, ModuleDependencies, RuntimeFailure, NativeRequestEndpoint, NativeRequestHandle, RequestCapability};

pub const CAPABILITY_ID: &str = "lenso.auth@1";
pub const DESCRIPTOR_VERSION: &str = "1.0.0";
pub const PORTABLE: bool = true;
pub const CROSS_LANE_TRANSFER: bool = false;
pub const AUTH_CAPABILITY_ID: &str = CAPABILITY_ID;
pub const AUTH_DESCRIPTOR_VERSION: &str = DESCRIPTOR_VERSION;

pub const AUTHENTICATE_OPERATION: &str = "authenticate";

pub type Int64 = String;
pub type Uint64 = String;
pub type Bytes = String;
pub type Timestamp = String;
pub type Duration = String;
pub type OptionalValue<T> = Option<Option<T>>;

#[allow(dead_code)]
fn deserialize_required<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
    D: serde::Deserializer<'de>,
    T: serde::Deserialize<'de>,
{
    <T as serde::Deserialize>::deserialize(deserializer)
}

#[allow(dead_code, clippy::option_option)]
fn deserialize_optional_value<'de, D, T>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: serde::Deserialize<'de>,
{
    Ok(Some(<Option<T> as serde::Deserialize>::deserialize(deserializer)?))
}

#[allow(dead_code)]
fn validate_portable_json_value(value: &serde_json::Value) -> Result<(), String> {
    match value {
        serde_json::Value::Number(number) => {
            let safe = number.as_i64().is_some_and(|value| (-9_007_199_254_740_991..=9_007_199_254_740_991).contains(&value))
                || number.as_u64().is_some_and(|value| value <= 9_007_199_254_740_991)
                || (number.is_f64() && number.as_f64().is_some_and(|value| value.is_finite() && (value.abs() <= 9_007_199_254_740_991.0 || value.fract() != 0.0)));
            if !safe {
                return Err("wire JSON contains an unsafe number".to_owned());
            }
        }
        serde_json::Value::Array(values) => {
            for value in values {
                validate_portable_json_value(value)?;
            }
        }
        serde_json::Value::Object(values) => {
            for value in values.values() {
                validate_portable_json_value(value)?;
            }
        }
        serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::String(_) => {}
    }
    Ok(())
}

#[allow(dead_code)]
fn portable_json_error(detail: String) -> serde_json::Error {
    serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::InvalidData, detail))
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct UnknownDomainError {
    pub code: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payload: Option<serde_json::Value>,
    #[serde(default, flatten)]
    pub extra: std::collections::BTreeMap<String, serde_json::Value>,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateRequest {
    #[serde(rename = "credential")]
    #[serde(deserialize_with = "deserialize_required")]
    pub credential: Option<AuthenticateRequestCredential>,
}

#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateRequestCredential {
    #[serde(rename = "scheme")]
    #[serde(deserialize_with = "deserialize_required")]
    pub scheme: String,
    #[serde(rename = "value")]
    #[serde(deserialize_with = "deserialize_required")]
    pub value: String,
}

impl fmt::Debug for AuthenticateRequestCredential {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("AuthenticateRequestCredential")
            .field("scheme", &self.scheme)
            .field("value", &"<redacted>")
            .finish()
    }
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateResponse {
    #[serde(rename = "assertion")]
    #[serde(deserialize_with = "deserialize_required")]
    pub assertion: Option<AuthenticateResponseAssertion>,
    #[serde(rename = "kind")]
    #[serde(deserialize_with = "deserialize_required")]
    pub kind: AuthenticateResponseKind,
}

#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateResponseAssertion {
    #[serde(rename = "actor_kind")]
    #[serde(deserialize_with = "deserialize_required")]
    pub actor_kind: String,
    #[serde(rename = "assurance")]
    #[serde(deserialize_with = "deserialize_required")]
    pub assurance: String,
    #[serde(rename = "audience")]
    #[serde(deserialize_with = "deserialize_required")]
    pub audience: Vec<String>,
    #[serde(rename = "claims")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub claims: Option<std::collections::BTreeMap<String, serde_json::Value>>,
    #[serde(rename = "expires_at")]
    #[serde(deserialize_with = "deserialize_required")]
    pub expires_at: Timestamp,
    #[serde(rename = "issued_at")]
    #[serde(deserialize_with = "deserialize_required")]
    pub issued_at: Timestamp,
    #[serde(rename = "issuer")]
    #[serde(deserialize_with = "deserialize_required")]
    pub issuer: String,
    #[serde(rename = "parent_provenance")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parent_provenance: Option<String>,
    #[serde(rename = "proof")]
    #[serde(deserialize_with = "deserialize_required")]
    pub proof: String,
    #[serde(rename = "subject")]
    #[serde(deserialize_with = "deserialize_required")]
    pub subject: String,
}

impl fmt::Debug for AuthenticateResponseAssertion {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("AuthenticateResponseAssertion")
            .field("actor_kind", &self.actor_kind)
            .field("assurance", &self.assurance)
            .field("audience", &self.audience)
            .field("claims", &self.claims)
            .field("expires_at", &self.expires_at)
            .field("issued_at", &self.issued_at)
            .field("issuer", &self.issuer)
            .field("parent_provenance", &self.parent_provenance)
            .field("proof", &"<redacted>")
            .field("subject", &self.subject)
            .finish()
    }
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum AuthenticateResponseKind {
    #[serde(rename = "absent")]
    Absent,
    #[serde(rename = "authenticated")]
    Authenticated,
}

#[derive(Clone, Debug, PartialEq)]
pub enum AuthenticateError {
    Expired,
    Invalid,
    Revoked,
    Unsupported,
    Unknown(UnknownDomainError),
}

#[derive(Debug)]
pub struct Auth;
impl RequestCapability for Auth {
    type Request = AuthenticateRequest;
    type Response = AuthenticateResponse;
    type DomainError = AuthenticateError;
    const ID: &'static str = CAPABILITY_ID;
    const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;
}

impl serde::Serialize for AuthenticateError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        use serde::ser::SerializeMap;
        match self {
            Self::Expired => serializer.serialize_str("expired"),
            Self::Invalid => serializer.serialize_str("invalid"),
            Self::Revoked => serializer.serialize_str("revoked"),
            Self::Unsupported => serializer.serialize_str("unsupported"),
            Self::Unknown(value) => {
                let mut map = serializer.serialize_map(Some(1 + usize::from(value.payload.is_some()) + value.extra.len()))?;
                map.serialize_entry("code", &value.code)?;
                if let Some(payload) = &value.payload {
                    map.serialize_entry("payload", payload)?;
                }
                for (key, extra) in &value.extra {
                    map.serialize_entry(key, extra)?;
                }
                map.end()
            },
        }
    }
}

impl<'de> serde::Deserialize<'de> for AuthenticateError {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = <serde_json::Value as serde::Deserialize>::deserialize(deserializer)?;
        match value {
            serde_json::Value::String(code) => match code.as_str() {
                "expired" => Ok(Self::Expired),
                "invalid" => Ok(Self::Invalid),
                "revoked" => Ok(Self::Revoked),
                "unsupported" => Ok(Self::Unsupported),
                _ => Ok(Self::Unknown(UnknownDomainError { code, payload: None, extra: std::collections::BTreeMap::new() })),
            },
            serde_json::Value::Object(mut object) => {
                let Some(code) = object.remove("code").and_then(|value| value.as_str().map(ToOwned::to_owned)) else {
                    return Err(serde::de::Error::custom("Domain Error object is missing a string code"));
                };
                let payload = object.remove("payload");
                let extra = object.into_iter().collect::<std::collections::BTreeMap<_, _>>();
                Ok(Self::Unknown(UnknownDomainError { code, payload, extra }))
            }
            other => Err(serde::de::Error::custom(format!("Domain Error must be a string or object, got {other}"))),
        }
    }
}

pub fn encode_authenticate_request(value: &AuthenticateRequest) -> Result<String, serde_json::Error> { let value = serde_json::to_value(value)?; validate_portable_json_value(&value).map_err(portable_json_error)?; serde_json::to_string(&value) }
pub fn decode_authenticate_request(wire: &str) -> Result<AuthenticateRequest, serde_json::Error> { let value: serde_json::Value = serde_json::from_str(wire)?; validate_portable_json_value(&value).map_err(portable_json_error)?; serde_json::from_value(value) }
pub fn encode_authenticate_response(value: &AuthenticateResponse) -> Result<String, serde_json::Error> { let value = serde_json::to_value(value)?; validate_portable_json_value(&value).map_err(portable_json_error)?; serde_json::to_string(&value) }
pub fn decode_authenticate_response(wire: &str) -> Result<AuthenticateResponse, serde_json::Error> { let value: serde_json::Value = serde_json::from_str(wire)?; validate_portable_json_value(&value).map_err(portable_json_error)?; serde_json::from_value(value) }
pub fn encode_authenticate_error(value: &AuthenticateError) -> Result<String, serde_json::Error> { let value = serde_json::to_value(value)?; validate_portable_json_value(&value).map_err(portable_json_error)?; serde_json::to_string(&value) }
pub fn decode_authenticate_error(wire: &str) -> Result<AuthenticateError, serde_json::Error> { let value: serde_json::Value = serde_json::from_str(wire)?; validate_portable_json_value(&value).map_err(portable_json_error)?; serde_json::from_value(value) }

pub trait AuthProvider: fmt::Debug + 'static {
    fn authenticate(&self, context: InvocationContext, request: AuthenticateRequest) -> LocalBoxFuture<'static, Result<AuthenticateResponse, AuthInvocationError>>;
}

#[derive(Debug)]
pub struct AuthEndpoint<P> { provider: Rc<P> }
impl<P: AuthProvider> AuthEndpoint<P> {
    pub fn new(provider: P) -> Self { Self { provider: Rc::new(provider) } }
}

impl<P: AuthProvider> NativeRequestEndpoint for AuthEndpoint<P> {
    fn capability_id(&self) -> &'static str { CAPABILITY_ID }
    fn descriptor_version(&self) -> &'static str { DESCRIPTOR_VERSION }
    fn operations(&self) -> &'static [&'static str] { &[
        AUTHENTICATE_OPERATION,
    ] }
    fn invoke(&self, operation: &str, request: Box<dyn std::any::Any>, context: InvocationContext) -> LocalBoxFuture<'static, Result<Result<Box<dyn std::any::Any>, Box<dyn std::any::Any>>, RuntimeFailure>> {
        match operation {
            AUTHENTICATE_OPERATION => {
                let Ok(request) = request.downcast::<AuthenticateRequest>() else {
                    return Box::pin(futures::future::ready(Err(RuntimeFailure::ProtocolViolation { capability: CAPABILITY_ID })));
                };
                let provider = Rc::clone(&self.provider);
                Box::pin(async move {
                    match provider.authenticate(context, *request).await {
                        Ok(value) => Ok(Ok(Box::new(value) as Box<dyn std::any::Any>)),
                        Err(AuthInvocationError::Domain(error)) => Ok(Err(Box::new(error) as Box<dyn std::any::Any>)),
                        Err(AuthInvocationError::Runtime(error)) => Err(error),
                    }
                })
            }
            _ => Box::pin(futures::future::ready(Err(RuntimeFailure::UnknownOperation { capability: CAPABILITY_ID, operation: operation.to_owned() }))),
        }
    }
}

#[derive(Debug)]
pub struct AuthClient {
    authenticate: NativeRequestHandle<Auth>,
}
impl AuthClient {
    pub fn new(handle: NativeRequestHandle<Auth>) -> Self {
        Self { authenticate: handle }
    }

    pub fn from_dependencies(dependencies: &ModuleDependencies) -> Result<Self, RuntimeFailure> {
        Ok(Self {
            authenticate: dependencies.one::<Auth>()?,
        })
    }

    pub async fn authenticate(&self, request: AuthenticateRequest) -> Result<AuthenticateResponse, AuthInvocationError> {
        self.authenticate.invoke(AUTHENTICATE_OPERATION, request).await
            .map_err(AuthInvocationError::Runtime)?
            .map_err(AuthInvocationError::Domain)
    }

    pub async fn authenticate_with_context(&self, context: InvocationContext, request: AuthenticateRequest) -> Result<AuthenticateResponse, AuthInvocationError> {
        self.authenticate.invoke_with_context(AUTHENTICATE_OPERATION, context, request).await
            .map_err(AuthInvocationError::Runtime)?
            .map_err(AuthInvocationError::Domain)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AuthInvocationError {
    Domain(AuthenticateError),
    Runtime(RuntimeFailure),
}