k2db_api_contract/
auth.rs1use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct AuthHeader {
8 pub scheme: String,
9 pub key_id: String,
10 pub secret: String,
11}
12
13impl AuthHeader {
14 pub fn parse(raw: &str) -> Option<Self> {
15 let trimmed = raw.trim();
16 let value = trimmed.strip_prefix("ApiKey ")?;
17 let (key_id, secret) = value.split_once('.')?;
18 let key_id = key_id.trim().strip_prefix("rbk_sys_").unwrap_or(key_id.trim());
19 let secret = secret.trim();
20 if key_id.is_empty() || secret.is_empty() {
21 return None;
22 }
23
24 Some(Self {
25 scheme: "ApiKey".to_owned(),
26 key_id: key_id.to_owned(),
27 secret: secret.to_owned(),
28 })
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub struct ApiKeyIdentity {
34 pub kind: String,
35 pub key_id: String,
36 pub name: String,
37 pub database: String,
38 pub permissions: Vec<String>,
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn parse_accepts_expected_apikey_shape() {
47 let parsed = AuthHeader::parse("ApiKey rbk_sys_demo.secret-value").expect("auth header");
48 assert_eq!(parsed.scheme, "ApiKey");
49 assert_eq!(parsed.key_id, "demo");
50 assert_eq!(parsed.secret, "secret-value");
51 }
52
53 #[test]
54 fn parse_rejects_malformed_values() {
55 assert!(AuthHeader::parse("Bearer token").is_none());
56 assert!(AuthHeader::parse("ApiKey missingdot").is_none());
57 assert!(AuthHeader::parse("ApiKey .secret").is_none());
58 assert!(AuthHeader::parse("ApiKey key.").is_none());
59 }
60}