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
// Copyright (c) Microsoft Corporation. All rights reserved.
//! Kerberos credentials supplied by the SDK host.
//!
//! By default the provider authenticates only requests made for sessions the
//! client creates or resumes; see
//! [`KerberosAuthScope`](crate::kerberos_auth::KerberosAuthScope). The
//! registration ends when the connection closes.
use std::future::Future;
use std::sync::Arc;
use async_trait::async_trait;
use crate::Result;
pub use crate::rpc::{KerberosAuthAcquireRequest, KerberosAuthMode, KerberosAuthScope};
pub(crate) const ACQUIRE_METHOD: &str = "kerberosAuth.acquire";
/// Supplies initial SPNEGO tokens for Kerberos service principals requested by the runtime.
///
/// Configure an implementation with
/// [`ClientOptions::with_kerberos_auth`](crate::ClientOptions::with_kerberos_auth).
/// Async functions and closures taking a [`KerberosAuthAcquireRequest`] implement this trait.
#[async_trait]
pub trait KerberosAuthProvider: Send + Sync + 'static {
/// Return a fresh, single-use base64 SPNEGO token without the `Negotiate ` prefix, or
/// `None` when credentials are unavailable.
///
/// The request's `spn` uses the platform-independent `service/host[@REALM]` form;
/// validate it before issuing credentials. An error is returned to the runtime as a
/// JSON-RPC error. Never log or persist the token.
///
/// When the runtime stops waiting for this request, or the connection closes, the
/// SDK drops the returned future, so any separately spawned work must stop on its own.
async fn acquire(&self, request: KerberosAuthAcquireRequest) -> Result<Option<String>>;
}
#[async_trait]
impl<F, Fut> KerberosAuthProvider for F
where
F: Fn(KerberosAuthAcquireRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Option<String>>> + Send,
{
async fn acquire(&self, request: KerberosAuthAcquireRequest) -> Result<Option<String>> {
(self)(request).await
}
}
/// Kerberos authentication callback configuration.
///
/// When set on [`ClientOptions::kerberos_auth`](crate::ClientOptions::kerberos_auth),
/// [`Client::start`](crate::Client::start) registers the provider with the runtime
/// before sessions can be created. The registration ends when the connection closes.
#[non_exhaustive]
#[derive(Clone)]
pub struct KerberosAuthConfig {
/// [`KerberosAuthMode::Fallback`] uses the provider only when native lookup fails;
/// [`KerberosAuthMode::Always`] never attempts native lookup.
pub mode: KerberosAuthMode,
/// Traffic the provider authenticates; `None` lets the runtime default to
/// [`KerberosAuthScope::Connection`].
///
/// [`KerberosAuthScope::Connection`] authenticates only requests made for
/// sessions this client creates or resumes. [`KerberosAuthScope::Runtime`]
/// also authenticates the runtime's shared background traffic; only one
/// connection at a time can hold it, so registration fails if another
/// client holds it.
pub scope: Option<KerberosAuthScope>,
/// Supplies tokens for `kerberosAuth.acquire` requests.
pub provider: Arc<dyn KerberosAuthProvider>,
}
impl KerberosAuthConfig {
/// Build a configuration for `provider` in the given `mode`, with the
/// runtime's default scope.
pub fn new<P>(mode: KerberosAuthMode, provider: P) -> Self
where
P: KerberosAuthProvider,
{
Self {
mode,
scope: None,
provider: Arc::new(provider),
}
}
/// Set the traffic the provider authenticates; see [`KerberosAuthConfig::scope`].
pub fn with_scope(mut self, scope: KerberosAuthScope) -> Self {
self.scope = Some(scope);
self
}
}
impl std::fmt::Debug for KerberosAuthConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KerberosAuthConfig")
.field("mode", &self.mode)
.field("scope", &self.scope)
.field("provider", &"<set>")
.finish()
}
}
/// Handle `kerberosAuth.acquire` with the configured provider, then register it with the runtime.
///
/// These requests honor `$/cancelRequest`, which drops the provider future.
#[cfg(feature = "runtime")]
pub(crate) async fn register(client: &crate::Client, config: KerberosAuthConfig) -> Result<()> {
let KerberosAuthConfig {
mode,
scope,
provider,
} = config;
client.register_request_handler(ACQUIRE_METHOD, move |params, _client| {
let provider = provider.clone();
async move {
let token = provider.acquire(serde_json::from_value(params)?).await?;
Ok(serde_json::to_value(
crate::rpc::KerberosAuthAcquireResult { token },
)?)
}
})?;
client
.rpc()
.kerberos_auth()
.set_provider(crate::rpc::KerberosAuthSetProviderRequest { mode, scope })
.await
}