use std::{fmt, rc::Rc};
use futures::future::LocalBoxFuture;
use lenso_kernel::{InvocationContext, NativeRequestEndpoint, NativeRequestFuture, NativeRequestHandle, PluginDependencies, RequestCapability, RuntimeFailure};
use lenso_plugin_authoring::{BoundCapabilityClient, CapabilityClient, CapabilityClientMany};
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;
#[doc(hidden)]
#[macro_export]
macro_rules! __lenso_provided_auth { () => { "{\"capability_id\":\"lenso.auth@1\",\"descriptor_version\":\"1.0.0\",\"operations\":[\"authenticate\"],\"operation_kinds\":{},\"default_admission\":{\"queue_capacity\":0,\"max_concurrency\":1},\"operation_admissions\":{},\"event_admission\":null,\"cross_lane_transfer\":false}" }; }
#[doc(hidden)]
#[macro_export]
macro_rules! __lenso_required_auth_client { () => { "{\"capability_id\":\"lenso.auth@1\",\"descriptor_version\":\"1.0.0\",\"cardinality\":\"one\"}" }; }
#[doc(hidden)]
#[macro_export]
macro_rules! __lenso_required_many_auth_client { () => { "{\"capability_id\":\"lenso.auth@1\",\"descriptor_version\":\"1.0.0\",\"cardinality\":\"many\"}" }; }
pub const AUTHENTICATE_OPERATION: &str = "authenticate";
pub use lenso_contract_runtime::{Timestamp, UnknownDomainError};
use lenso_contract_runtime::{decode_portable_json, encode_portable_json};
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateRequest {
#[serde(rename = "credential")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
pub credential: Option<AuthenticateRequestCredential>,
}
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateRequestCredential {
#[serde(rename = "scheme")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
pub scheme: String,
#[serde(rename = "value")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::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 = "lenso_contract_runtime::serde::deserialize_required")]
pub assertion: Option<AuthenticateResponseAssertion>,
#[serde(rename = "kind")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
pub kind: AuthenticateResponseKind,
}
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AuthenticateResponseAssertion {
#[serde(rename = "actor_kind")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
pub actor_kind: String,
#[serde(rename = "assurance")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
pub assurance: String,
#[serde(rename = "audience")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::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 = "lenso_contract_runtime::serde::deserialize_required")]
pub expires_at: Timestamp,
#[serde(rename = "issued_at")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::deserialize_required")]
pub issued_at: Timestamp,
#[serde(rename = "issuer")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::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 = "lenso_contract_runtime::serde::deserialize_required")]
pub proof: String,
#[serde(rename = "subject")]
#[serde(deserialize_with = "lenso_contract_runtime::serde::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;
fn invoke_native(endpoint: &dyn NativeRequestEndpoint, operation: &str, request: Self::Request, context: InvocationContext) -> NativeRequestFuture<Self> {
if operation != AUTHENTICATE_OPERATION {
return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
}
let Some(typed_endpoint) = endpoint
.typed_endpoint()
.and_then(|endpoint| endpoint.downcast_ref::<AuthRequestEndpoint>())
else {
return lenso_kernel::invoke_typed_or_erased_native_request::<Self>(endpoint, operation, request, context);
};
Rc::clone(&typed_endpoint.provider).authenticate(context, request)
}
}
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> { encode_portable_json(value) }
pub fn decode_authenticate_request(wire: &str) -> Result<AuthenticateRequest, serde_json::Error> { decode_portable_json(wire) }
pub fn encode_authenticate_response(value: &AuthenticateResponse) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_authenticate_response(wire: &str) -> Result<AuthenticateResponse, serde_json::Error> { decode_portable_json(wire) }
pub fn encode_authenticate_error(value: &AuthenticateError) -> Result<String, serde_json::Error> { encode_portable_json(value) }
pub fn decode_authenticate_error(wire: &str) -> Result<AuthenticateError, serde_json::Error> { decode_portable_json(wire) }
#[doc(hidden)]
pub trait __LensoIntoAuthAuthenticateResult {
fn __lenso_into_result(self) -> Result<Result<AuthenticateResponse, AuthenticateError>, RuntimeFailure>;
}
impl __LensoIntoAuthAuthenticateResult for Result<AuthenticateResponse, AuthenticateError> {
fn __lenso_into_result(self) -> Result<Result<AuthenticateResponse, AuthenticateError>, RuntimeFailure> { Ok(self) }
}
impl __LensoIntoAuthAuthenticateResult for Result<Result<AuthenticateResponse, AuthenticateError>, RuntimeFailure> {
fn __lenso_into_result(self) -> Result<Result<AuthenticateResponse, AuthenticateError>, RuntimeFailure> { self }
}
impl __LensoIntoAuthAuthenticateResult for Result<AuthenticateResponse, lenso_plugin_authoring::PluginError<AuthenticateError, RuntimeFailure>> {
fn __lenso_into_result(self) -> Result<Result<AuthenticateResponse, AuthenticateError>, RuntimeFailure> {
match self {
Ok(value) => Ok(Ok(value)),
Err(lenso_plugin_authoring::PluginError::Domain(error)) => Ok(Err(error)),
Err(lenso_plugin_authoring::PluginError::Runtime(error)) => Err(error),
}
}
}
impl __LensoIntoAuthAuthenticateResult for Result<AuthenticateResponse, AuthInvocationError> {
fn __lenso_into_result(self) -> Result<Result<AuthenticateResponse, AuthenticateError>, RuntimeFailure> {
match self {
Ok(value) => Ok(Ok(value)),
Err(AuthInvocationError::Domain(error)) => Ok(Err(error)),
Err(AuthInvocationError::Runtime(error)) => Err(error),
}
}
}
pub trait AuthProvider: fmt::Debug + 'static {
fn authenticate(&self, context: InvocationContext, request: AuthenticateRequest) -> NativeRequestFuture<Auth>;
}
#[doc(hidden)]
#[macro_export]
macro_rules! __lenso_native_lower_auth {
($plugin:ty, $support:path) => {
use $support as __LensoNativeSupportAuth;
impl $crate::AuthProvider for $plugin {
fn authenticate(&self, context: __LensoNativeSupportAuth::InvocationContext, request: $crate::AuthenticateRequest) -> __LensoNativeSupportAuth::NativeRequestFuture<$crate::Auth> {
let plugin = self.clone();
::std::boxed::Box::pin(async move {
let result = <$plugin>::authenticate(&plugin, context, request).await;
$crate::__LensoIntoAuthAuthenticateResult::__lenso_into_result(result)
})
}
}
};
}
#[derive(Debug)]
struct AuthRequestEndpoint { provider: Rc<dyn AuthProvider> }
#[derive(Debug)]
pub struct AuthEndpoint<P: AuthProvider> { provider: Rc<P>, request_endpoint: AuthRequestEndpoint }
impl<P: AuthProvider> AuthEndpoint<P> {
pub fn new(provider: P) -> Self {
let provider = Rc::new(provider);
let request_provider: Rc<dyn AuthProvider> = provider.clone();
Self { provider, request_endpoint: AuthRequestEndpoint { provider: request_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 typed_endpoint(&self) -> Option<&dyn std::any::Any> { Some(&self.request_endpoint) }
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 invocation = Rc::clone(&self.provider).authenticate(context, *request);
Box::pin(async move {
invocation.await.map(|result| {
result
.map(|value| Box::new(value) as Box<dyn std::any::Any>)
.map_err(|error| Box::new(error) as Box<dyn std::any::Any>)
})
})
}
_ => Box::pin(futures::future::ready(Err(RuntimeFailure::UnknownOperation { capability: CAPABILITY_ID, operation: operation.to_owned() }))),
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __lenso_native_endpoints_auth {
($provider:expr, $support:path) => {{
use $support as __LensoNativeSupport;
let endpoint = ::std::rc::Rc::new($crate::AuthEndpoint::new($provider));
(
vec![endpoint.clone() as ::std::rc::Rc<dyn __LensoNativeSupport::NativeRequestEndpoint>],
vec![],
vec![],
)
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __lenso_native_provide_auth {
($provider:expr, $lifecycle:expr, $support:path) => {{
use $support as __LensoNativeSupport;
let (request_endpoints, stream_endpoints, event_endpoints) =
$crate::__lenso_native_endpoints_auth!($provider, $support);
__LensoNativeSupport::NativePluginInstance::with_all_endpoints(
request_endpoints,
stream_endpoints,
event_endpoints,
$lifecycle,
)
}};
}
#[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: &PluginDependencies) -> Result<Self, RuntimeFailure> {
<Self as CapabilityClient>::from_dependencies(dependencies)
}
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)
}
}
impl CapabilityClient for AuthClient {
type Dependencies = PluginDependencies;
type Error = RuntimeFailure;
const CAPABILITY_ID: &'static str = CAPABILITY_ID;
const DESCRIPTOR_VERSION: &'static str = DESCRIPTOR_VERSION;
fn from_dependencies(dependencies: &PluginDependencies) -> Result<Self, RuntimeFailure> {
Ok(Self {
authenticate: dependencies.one::<Auth>()?,
})
}
fn already_connected() -> RuntimeFailure {
RuntimeFailure::PluginFailure {
detail: format!("Capability Port {CAPABILITY_ID} was connected more than once"),
}
}
}
impl CapabilityClientMany for AuthClient {
fn many_from_dependencies(
dependencies: &PluginDependencies,
) -> Result<Vec<BoundCapabilityClient<Self>>, RuntimeFailure> {
dependencies
.bindings()
.iter()
.filter(|binding| binding.capability_id() == CAPABILITY_ID)
.map(|binding| {
Ok(BoundCapabilityClient::new(
binding.provider_instance(),
Self {
authenticate: binding.handle().ok_or(RuntimeFailure::Unavailable { capability: CAPABILITY_ID })?.typed::<Auth>()?,
},
))
})
.collect()
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum AuthInvocationError {
Domain(AuthenticateError),
Runtime(RuntimeFailure),
}