#[derive(Debug, Clone)]
pub struct SspiAuthToken {
pub data: Vec<u8>,
pub is_complete: bool,
}
impl SspiAuthToken {
pub fn new(data: Vec<u8>, is_complete: bool) -> Self {
Self { data, is_complete }
}
pub fn complete() -> Self {
Self {
data: Vec::new(),
is_complete: true,
}
}
pub fn continue_needed(data: Vec<u8>) -> Self {
Self {
data,
is_complete: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SecurityPackage {
#[default]
Negotiate,
Kerberos,
Ntlm,
}
impl SecurityPackage {
#[cfg(windows)]
pub fn as_sspi_name(&self) -> &'static str {
match self {
SecurityPackage::Negotiate => "Negotiate",
SecurityPackage::Kerberos => "Kerberos",
SecurityPackage::Ntlm => "NTLM",
}
}
pub fn display_name(&self) -> &'static str {
match self {
SecurityPackage::Negotiate => "Negotiate",
SecurityPackage::Kerberos => "Kerberos",
SecurityPackage::Ntlm => "NTLM",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct IntegratedAuthConfig {
pub server_spn: Option<String>,
pub security_package: SecurityPackage,
pub channel_bindings: Option<Vec<u8>>,
pub is_loopback: bool,
}
impl IntegratedAuthConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_spn(spn: String) -> Self {
Self {
server_spn: Some(spn),
..Default::default()
}
}
pub fn with_package(mut self, package: SecurityPackage) -> Self {
self.security_package = package;
self
}
pub fn with_channel_bindings(mut self, bindings: Vec<u8>) -> Self {
self.channel_bindings = Some(bindings);
self
}
pub fn with_loopback(mut self, is_loopback: bool) -> Self {
self.is_loopback = is_loopback;
self
}
}
pub trait SecurityContext: Send + Sync {
fn package_name(&self) -> &str;
fn generate_token(
&mut self,
server_token: Option<&[u8]>,
) -> Result<SspiAuthToken, super::SecurityError>;
fn is_complete(&self) -> bool;
fn spn(&self) -> &str;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auth_token_new() {
let token = SspiAuthToken::new(vec![1, 2, 3], false);
assert_eq!(token.data, vec![1, 2, 3]);
assert!(!token.is_complete);
}
#[test]
fn test_auth_token_complete() {
let token = SspiAuthToken::complete();
assert!(token.data.is_empty());
assert!(token.is_complete);
}
#[test]
fn test_auth_token_continue_needed() {
let token = SspiAuthToken::continue_needed(vec![4, 5, 6]);
assert_eq!(token.data, vec![4, 5, 6]);
assert!(!token.is_complete);
}
#[test]
fn test_security_package_default() {
let pkg = SecurityPackage::default();
assert_eq!(pkg, SecurityPackage::Negotiate);
}
#[test]
fn test_security_package_display_name() {
assert_eq!(SecurityPackage::Negotiate.display_name(), "Negotiate");
assert_eq!(SecurityPackage::Kerberos.display_name(), "Kerberos");
assert_eq!(SecurityPackage::Ntlm.display_name(), "NTLM");
}
#[test]
fn test_integrated_auth_config_default() {
let config = IntegratedAuthConfig::new();
assert!(config.server_spn.is_none());
assert_eq!(config.security_package, SecurityPackage::Negotiate);
assert!(config.channel_bindings.is_none());
assert!(!config.is_loopback);
}
#[test]
fn test_integrated_auth_config_with_spn() {
let config = IntegratedAuthConfig::with_spn("MSSQLSvc/server:1433".to_string());
assert_eq!(config.server_spn, Some("MSSQLSvc/server:1433".to_string()));
}
#[test]
fn test_integrated_auth_config_builder() {
let config = IntegratedAuthConfig::new()
.with_package(SecurityPackage::Kerberos)
.with_channel_bindings(vec![1, 2, 3])
.with_loopback(true);
assert_eq!(config.security_package, SecurityPackage::Kerberos);
assert_eq!(config.channel_bindings, Some(vec![1, 2, 3]));
assert!(config.is_loopback);
}
}