use sshcerts::error::Error; use sshcerts::ssh::{AllowedSigner, AllowedSignerParsingError}; #[test] fn parse_good_allowed_signer() { let allowed_signer = "mitchell@confurious.io ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!( allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M", ); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string()], ); assert!(!allowed_signer.cert_authority); assert!(allowed_signer.namespaces.is_none()); assert!(allowed_signer.valid_after.is_none()); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_quoted_principals() { let allowed_signer = "\"mitchell@confurious.io,mitchell\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!( allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M", ); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchell".to_string()], ); assert!(!allowed_signer.cert_authority); assert!(allowed_signer.namespaces.is_none()); assert!(allowed_signer.valid_after.is_none()); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_options() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,mitchell\" valid-before=\"20240505Z\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell".to_string()]) ); assert!(allowed_signer.valid_after.is_none()); assert_eq!(allowed_signer.valid_before, Some(1714867200i64)); } #[test] fn parse_good_allowed_signer_with_utc_timestamp() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,mitchell\" valid-after=20240505Z ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell".to_string()]) ); assert_eq!(allowed_signer.valid_after, Some(1714867200)); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_hm_timestamp() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,mitchell\" valid-after=202405050102Z ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell".to_string()]) ); assert_eq!(allowed_signer.valid_after, Some(1714870920i64)); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_hms_timestamp() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,mitchell\" valid-after=20240505010230Z ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell".to_string()]) ); assert_eq!(allowed_signer.valid_after, Some(1714870950i64)); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_consecutive_spaces() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,#mitchell\" valid-before=\"20240505Z\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5 "; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "#mitchell".to_string()]) ); assert!(allowed_signer.valid_after.is_none()); assert_eq!(allowed_signer.valid_before, Some(1714867200i64)); } #[test] fn parse_good_allowed_signer_with_empty_namespaces() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,,mitchell\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell".to_string()]) ); assert!(allowed_signer.valid_after.is_none()); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_space_in_namespaces() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=\"thanh,mitchell tech\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell tech".to_string()]) ); assert!(allowed_signer.valid_after.is_none()); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_good_allowed_signer_with_unquoted_namespaces() { let allowed_signer = "mitchell@confurious.io,mitchel2@confurious.io cert-authority namespaces=thanh,mitchell ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_ok()); let allowed_signer = allowed_signer.unwrap(); assert_eq!(allowed_signer.key.fingerprint().to_string(), "SHA256:QAtqtvvCePelMMUNPP7madH2zNa1ATxX1nt9L/0C5+M"); assert_eq!( allowed_signer.principals, vec!["mitchell@confurious.io".to_string(), "mitchel2@confurious.io".to_string()], ); assert!(allowed_signer.cert_authority); assert_eq!( allowed_signer.namespaces, Some(vec!["thanh".to_string(), "mitchell".to_string()]) ); assert!(allowed_signer.valid_after.is_none()); assert!(allowed_signer.valid_before.is_none()); } #[test] fn parse_bad_allowed_signer_with_wrong_key_type() { let allowed_signer = "mitchell@confurious.io ecdsa-sha2-nistp384 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); } #[test] fn parse_bad_allowed_signer_with_invalid_option() { let allowed_signer = "mitchell@confurious.io option=test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); } #[test] fn parse_bad_allowed_signer_with_invalid_namespaces() { let allowed_signer = "mitchell@confurious.io namespaces=a\"test\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); let allowed_signer = "mitchell@confurious.io namespaces=\"tester,thanh\"\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); } #[test] fn parse_bad_allowed_signer_with_invalid_principals() { let allowed_signer = "mitchell@confurious.io ,thanh@timweri.me option=test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); } #[test] fn parse_bad_allowed_signer_with_empty_principal() { let allowed_signer = "mitchell@confurious.io,,thanh@timweri.me option=test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); } #[test] fn parse_bad_allowed_signer_with_timestamp_option() { let allowed_signer = "mitchell@confurious.io valid-before=-143 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); } #[test] fn parse_bad_allowed_signer_with_conflicting_timestamps() { let allowed_signer = "mitchell@confurious.io valid-before=20240505 valid-after=20240505 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); assert!(matches!(allowed_signer, Err(Error::InvalidAllowedSigner(AllowedSignerParsingError::InvalidTimestamps)))); } #[test] fn parse_bad_allowed_signer_with_duplicate_option() { let allowed_signer = "mitchell@confurious.io namespaces=thanh namespaces=mitchell ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); assert!( matches!( allowed_signer, Err(Error::InvalidAllowedSigner(AllowedSignerParsingError::DuplicateOptions(_))), ) ); } #[test] fn parse_bad_allowed_signer_with_quoted_key() { let allowed_signer = "mitchell@confurious.io \"ssh-ed25519\" AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); assert!( matches!( allowed_signer, Err(Error::InvalidAllowedSigner(AllowedSignerParsingError::InvalidKey)), ) ); let allowed_signer = "mitchell@confurious.io \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5\""; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); assert!( matches!( allowed_signer, Err(Error::InvalidAllowedSigner(AllowedSignerParsingError::InvalidQuotes)), ) ); let allowed_signer = "mitchell@confurious.io ssh-ed25519 \"AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5\""; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); assert!( matches!( allowed_signer, Err(Error::InvalidAllowedSigner(AllowedSignerParsingError::InvalidQuotes)), ) ); } #[test] fn parse_bad_allowed_signer_with_invalid_timestamp() { let allowed_signer = "mitchell@confurious.io valid-before=1941 \"ssh-ed25519\" AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); let allowed_signer = "mitchell@confurious.io valid-before=\"1941\" \"ssh-ed25519\" AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); let allowed_signer = "mitchell@confurious.io valid-before=19411293 \"ssh-ed25519\" AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); let allowed_signer = "mitchell@confurious.io valid-before=1941293 \"ssh-ed25519\" AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); let allowed_signer = "mitchell@confurious.io valid-before=19411293Z \"ssh-ed25519\" AAAAC3NzaC1lZDI1NTE5AAAAIDO0VQD9TIdICZLWFWwtf7s8/aENve8twGTEmNV0myh5"; let allowed_signer = AllowedSigner::from_string(allowed_signer); assert!(allowed_signer.is_err()); }