Skip to main content

secretspec_ipc/
resolver.rs

1use crate::error::{ErrorKind, RpcError};
2use crate::protocol::RESOLVER_PROTOCOL;
3use crate::protocol::callback::{self, PromptParams, PromptResult};
4use crate::protocol::resolver::{
5    CAPABILITIES, DeleteParams, DeleteResult, GetParams, GetResult, InitializeApplication,
6    InitializedApplication, ReleaseParams, ReleaseResult, SetParams, SetResult, method,
7    validate_capabilities,
8};
9use crate::server::{ApplicationHandler, RequestContext, RpcResult, ServerConfig};
10use async_trait::async_trait;
11use serde::de::DeserializeOwned;
12use serde_json::Value;
13use std::sync::Arc;
14
15/// Typed northbound handler. Implementations never parse JSON-RPC envelopes or
16/// arbitrate cancellation/terminal races.
17///
18/// For connected streams (0.21+), the embedding host authenticates and
19/// authorizes the transport before dispatch. Its per-session handler must
20/// check initialization against server-owned policy before opening manifests
21/// or providers. Initialization context is not authenticated identity. Each
22/// connection needs isolated configuration and resource ownership.
23#[async_trait]
24pub trait ResolverHandler: Send + Sync + 'static {
25    async fn initialize(
26        &self,
27        context: &RequestContext,
28        application: InitializeApplication,
29    ) -> RpcResult<InitializedApplication>;
30
31    async fn get(&self, context: RequestContext, params: GetParams) -> RpcResult<GetResult>;
32
33    async fn release(
34        &self,
35        context: RequestContext,
36        params: ReleaseParams,
37    ) -> RpcResult<ReleaseResult>;
38
39    /// Methods this endpoint advertises. The default is resolution only, so an
40    /// endpoint gains a mutation method by naming it here and implementing it,
41    /// never by inheriting one it never considered.
42    fn capabilities(&self) -> Vec<String> {
43        CAPABILITIES
44            .iter()
45            .map(|item| (*item).to_string())
46            .collect()
47    }
48
49    /// Store one declared name (0.21+). Unreachable unless
50    /// [`Self::capabilities`] advertises `resolver.set`: the server answers an
51    /// unadvertised method itself and never reaches the handler.
52    async fn set(&self, _context: RequestContext, _params: SetParams) -> RpcResult<SetResult> {
53        Err(RpcError::new(ErrorKind::MethodNotFound))
54    }
55
56    /// Remove one declared name's stored value (0.21+), advertised as
57    /// `resolver.delete` under the same rule as [`Self::set`].
58    async fn delete(
59        &self,
60        _context: RequestContext,
61        _params: DeleteParams,
62    ) -> RpcResult<DeleteResult> {
63        Err(RpcError::new(ErrorKind::MethodNotFound))
64    }
65
66    async fn request_finished(&self, _request_id: crate::RequestId, _committed: bool) {}
67
68    async fn shutdown(&self) {}
69}
70
71struct ResolverApplication<H> {
72    handler: Arc<H>,
73}
74
75impl<H> ResolverApplication<H> {
76    fn new(handler: Arc<H>) -> Self {
77        Self { handler }
78    }
79}
80
81#[async_trait]
82impl<H: ResolverHandler> ApplicationHandler for ResolverApplication<H> {
83    fn protocol(&self) -> &'static str {
84        RESOLVER_PROTOCOL
85    }
86
87    fn capabilities(&self) -> Vec<String> {
88        self.handler.capabilities()
89    }
90
91    // The advertised list stopped being a constant once mutation methods became
92    // opt-in, so an implementation can now omit a method version 1 requires.
93    // That is a defect in the endpoint rather than anything a peer did, which is
94    // why the server reports it as `internal`.
95    fn validate_capabilities(&self, capabilities: &[String]) -> RpcResult<()> {
96        validate_capabilities(capabilities).map_err(|_| RpcError::new(ErrorKind::Internal))
97    }
98
99    async fn initialize(&self, context: &RequestContext, application: Value) -> RpcResult<Value> {
100        let application: InitializeApplication = parse(application)?;
101        application.validate().map_err(invalid_params)?;
102        let initialized = self.handler.initialize(context, application).await?;
103        initialized
104            .validate()
105            .map_err(|_| RpcError::new(ErrorKind::OperationFailed))?;
106        serde_json::to_value(initialized).map_err(|_| RpcError::new(ErrorKind::Internal))
107    }
108
109    async fn call(&self, context: RequestContext, method: &str, params: Value) -> RpcResult<Value> {
110        match method {
111            method::GET => {
112                let params: GetParams = parse(params)?;
113                params.validate().map_err(invalid_params)?;
114                let result = self.handler.get(context, params).await?;
115                serde_json::to_value(result).map_err(|_| RpcError::new(ErrorKind::Internal))
116            }
117            method::RELEASE => {
118                let params: ReleaseParams = parse(params)?;
119                params.validate().map_err(invalid_params)?;
120                let result = self.handler.release(context, params).await?;
121                serde_json::to_value(result).map_err(|_| RpcError::new(ErrorKind::Internal))
122            }
123            method::SET => {
124                let params: SetParams = parse(params)?;
125                params.validate().map_err(invalid_params)?;
126                let result = self.handler.set(context, params).await?;
127                serde_json::to_value(result).map_err(|_| RpcError::new(ErrorKind::Internal))
128            }
129            method::DELETE => {
130                let params: DeleteParams = parse(params)?;
131                params.validate().map_err(invalid_params)?;
132                let result = self.handler.delete(context, params).await?;
133                serde_json::to_value(result).map_err(|_| RpcError::new(ErrorKind::Internal))
134            }
135            _ => Err(RpcError::new(ErrorKind::MethodNotFound)),
136        }
137    }
138
139    async fn request_finished(&self, request_id: crate::RequestId, committed: bool) {
140        self.handler.request_finished(request_id, committed).await;
141    }
142
143    async fn shutdown(&self) {
144        self.handler.shutdown().await;
145    }
146}
147
148/// Ask the client for one secret value, on behalf of the request in `context`
149/// (0.21+).
150///
151/// Returns `interaction_required` when the client advertised no way to ask,
152/// which is the answer a headless consumer needs immediately rather than after
153/// its deadline elapses. The prompt inherits the originating request's deadline
154/// and cancellation, so it cannot outlive the resolve that raised it.
155pub async fn prompt(context: &RequestContext, params: &PromptParams) -> RpcResult<PromptResult> {
156    if !context.peer.supports(callback::method::PROMPT) {
157        return Err(RpcError::new(ErrorKind::InteractionRequired));
158    }
159    params
160        .validate()
161        .map_err(|_| RpcError::new(ErrorKind::Internal))?;
162    let result: PromptResult = context
163        .peer
164        .call(callback::method::PROMPT, params, context)
165        .await?;
166    // A client is not trusted to have sent something this endpoint would store:
167    // an empty answer means different things to different stores, so it is
168    // refused here rather than becoming a value.
169    result
170        .validate()
171        .map_err(|_| RpcError::new(ErrorKind::OperationFailed))?;
172    Ok(result)
173}
174
175fn parse<T: DeserializeOwned>(value: Value) -> RpcResult<T> {
176    serde_json::from_value(value).map_err(|_| RpcError::new(ErrorKind::InvalidParams))
177}
178
179fn invalid_params(_: crate::Error) -> RpcError {
180    RpcError::new(ErrorKind::InvalidParams)
181}
182
183/// Serve one typed resolution endpoint without assembling the generic adapter.
184pub async fn serve_resolver<R, W, H>(
185    reader: R,
186    writer: W,
187    handler: H,
188    config: ServerConfig,
189) -> crate::Result<()>
190where
191    R: tokio::io::AsyncRead + Unpin + Send + 'static,
192    W: tokio::io::AsyncWrite + Unpin + Send + 'static,
193    H: ResolverHandler,
194{
195    crate::server::serve(
196        reader,
197        writer,
198        Arc::new(ResolverApplication::new(Arc::new(handler))),
199        config,
200    )
201    .await
202}