pray_core/
ssh_identity.rs1use crate::PrayResult;
2
3const USER_FINGERPRINT_ENV_VARS: &[&str] = &[
4 "PRAY_SSH_USER_FINGERPRINT",
5 "SSH_USER_FINGERPRINT",
6 "PRAY_SSH_PUBLISHER",
7];
8
9pub fn normalize_identity(value: &str) -> String {
10 value.trim().to_ascii_uppercase()
11}
12
13pub fn active_ssh_user_fingerprint() -> Option<String> {
14 for name in USER_FINGERPRINT_ENV_VARS {
15 if let Ok(value) = std::env::var(name) {
16 let normalized = normalize_identity(&value);
17 if !normalized.is_empty() {
18 return Some(normalized);
19 }
20 }
21 }
22 None
23}
24
25pub fn signing_identity(label: &str, fingerprint: Option<&str>) -> String {
26 if let Some(fingerprint) = fingerprint {
27 let normalized = normalize_identity(fingerprint);
28 if looks_like_ssh_fingerprint(&normalized) {
29 return normalized;
30 }
31 }
32 label.trim().to_string()
33}
34
35pub fn package_signing_identity(
36 signer: Option<&str>,
37 signer_fingerprint: Option<&str>,
38) -> Option<String> {
39 if let Some(fingerprint) = signer_fingerprint {
40 let normalized = normalize_identity(fingerprint);
41 if looks_like_ssh_fingerprint(&normalized) {
42 return Some(normalized);
43 }
44 }
45 signer
46 .map(str::trim)
47 .filter(|value| !value.is_empty())
48 .map(str::to_string)
49}
50
51pub fn looks_like_ssh_fingerprint(value: &str) -> bool {
52 let upper = normalize_identity(value);
53 upper.starts_with("SHA256:") || upper.starts_with("SHA512:") || upper.starts_with("MD5:")
54}
55
56pub fn fingerprint_matches_allowed(identity: &str, allowed: &[String]) -> bool {
57 if allowed.is_empty() {
58 return false;
59 }
60 let normalized = normalize_identity(identity);
61 allowed
62 .iter()
63 .any(|entry| normalize_identity(entry) == normalized)
64}
65
66pub fn read_env_identity(name: &str) -> Option<String> {
67 std::env::var(name)
68 .ok()
69 .map(|value| normalize_identity(&value))
70 .filter(|value| !value.is_empty())
71}
72
73pub fn ssh_public_key_fingerprint(public_key: &str) -> PrayResult<String> {
74 crate::auth::ssh_public_key_fingerprint_text(public_key)
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn signing_identity_prefers_fingerprint() {
83 assert_eq!(
84 signing_identity("alice@example.com", Some("sha256:abc")),
85 "SHA256:ABC"
86 );
87 assert_eq!(
88 signing_identity("alice@example.com", None),
89 "alice@example.com"
90 );
91 }
92
93 #[test]
94 fn package_signing_identity_prefers_fingerprint_field() {
95 assert_eq!(
96 package_signing_identity(Some("alice@example.com"), Some("sha256:deadbeef"),),
97 Some("SHA256:DEADBEEF".to_string())
98 );
99 }
100
101 #[test]
102 fn package_signing_identity_ignores_non_fingerprint_field() {
103 assert_eq!(
104 package_signing_identity(Some("alice@example.com"), Some("alice@example.com"),),
105 Some("alice@example.com".to_string())
106 );
107 }
108}