Reuse native macOS Git credentials
Some checks failed
Dependency security audit / rustsec (push) Has been cancelled
Some checks failed
Dependency security audit / rustsec (push) Has been cancelled
This commit is contained in:
@@ -133,6 +133,49 @@ key_material = "keys"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "ios", target_os = "watchos")))]
|
||||
#[test]
|
||||
fn repository_https_remote_reuses_credentials_only_for_the_same_url() -> TestResult {
|
||||
let fixture = ConfigurationFixture::new()?;
|
||||
let vault = fixture.temporary.path().join("cwd/vault");
|
||||
fs::create_dir_all(vault.join(".git"))?;
|
||||
fs::write(
|
||||
vault.join(".git/config"),
|
||||
r#"[remote "origin"]
|
||||
url = https://git.example.test/team/store.git
|
||||
[remote "mirror"]
|
||||
url = https://mirror.example.test/team/store.git
|
||||
"#,
|
||||
)?;
|
||||
fixture.write_explicit(
|
||||
r#"
|
||||
vault = "../vault"
|
||||
default_key = "0123456789ABCDEF0123456789ABCDEF01234567"
|
||||
key_material = "keys"
|
||||
|
||||
[[git.remotes]]
|
||||
name = "legacy-name"
|
||||
url = "https://git.example.test/team/store.git"
|
||||
server_id = "existing-server"
|
||||
application_id = "existing-application"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = fixture
|
||||
.loader()
|
||||
.load(Some(Path::new("config/config.toml")))?;
|
||||
let origin = config.git_remote(Some("origin")).expect("origin remote");
|
||||
let (server, application) = origin.https_credentials().expect("HTTPS credentials");
|
||||
assert_eq!(server.as_str(), "existing-server");
|
||||
assert_eq!(application.as_str(), "existing-application");
|
||||
|
||||
let mirror = config.git_remote(Some("mirror")).expect("mirror remote");
|
||||
let (server, application) = mirror.https_credentials().expect("HTTPS credentials");
|
||||
assert_ne!(server.as_str(), "existing-server");
|
||||
assert_ne!(application.as_str(), "existing-application");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_commit_identity_defaults_validates_and_persists() -> TestResult {
|
||||
let fixture = ConfigurationFixture::new()?;
|
||||
|
||||
@@ -32,6 +32,8 @@ struct MemoryState {
|
||||
create_fault: Option<SecretStoreError>,
|
||||
retrieves: usize,
|
||||
protections: Vec<SecretProtection>,
|
||||
platform_https: Option<(String, SecretBytes)>,
|
||||
platform_https_requests: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -60,6 +62,13 @@ impl MemoryBackend {
|
||||
self.0.lock().expect("test mutex").protections.clone()
|
||||
}
|
||||
|
||||
fn with_platform_https(account: &str, password: &[u8]) -> Self {
|
||||
let backend = Self::default();
|
||||
backend.0.lock().expect("test mutex").platform_https =
|
||||
Some((account.to_owned(), SecretBytes::new(password.to_vec())));
|
||||
backend
|
||||
}
|
||||
|
||||
fn take_fault(state: &mut MemoryState) -> Result<(), SecretStoreError> {
|
||||
match state.fault.take() {
|
||||
Some(error) => Err(error),
|
||||
@@ -138,6 +147,22 @@ impl SecretStoreBackend for MemoryBackend {
|
||||
.ok_or(SecretStoreError::Missing)
|
||||
}
|
||||
|
||||
fn platform_https_git_credential(
|
||||
&self,
|
||||
endpoint: &url::Url,
|
||||
) -> Result<Option<(String, SecretBytes)>, SecretStoreError> {
|
||||
let mut state = self.0.lock().map_err(|_| SecretStoreError::Unavailable)?;
|
||||
state
|
||||
.platform_https_requests
|
||||
.push(endpoint.as_str().to_owned());
|
||||
Ok(state.platform_https.as_ref().map(|(account, password)| {
|
||||
(
|
||||
account.clone(),
|
||||
SecretBytes::new(password.expose().to_vec()),
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
fn lock(&self) -> Result<(), SecretStoreError> {
|
||||
let mut state = self.0.lock().map_err(|_| SecretStoreError::Unavailable)?;
|
||||
Self::take_fault(&mut state)
|
||||
@@ -149,6 +174,42 @@ impl SecretStoreBackend for MemoryBackend {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_lookup_uses_the_native_https_credential_before_private_storage() -> TestResult {
|
||||
let backend = MemoryBackend::with_platform_https("alice", b"native-token");
|
||||
let store = store(backend.clone());
|
||||
store.unlock()?;
|
||||
let remote = GitRemote::https(
|
||||
"origin",
|
||||
"https://example.test/team/store.git",
|
||||
"server",
|
||||
"application",
|
||||
)?;
|
||||
let (server, application) = remote.https_credentials().expect("HTTPS credentials");
|
||||
store.store_https_git_credential(
|
||||
server,
|
||||
application,
|
||||
"private-account",
|
||||
SecretBytes::new(b"private-token".to_vec()),
|
||||
)?;
|
||||
let retrieves_before = backend.retrieves();
|
||||
|
||||
let credential = store.credential_for(&remote)?;
|
||||
|
||||
assert_eq!(credential.username(), "alice");
|
||||
assert_eq!(credential.password(), b"native-token");
|
||||
assert_eq!(backend.retrieves(), retrieves_before);
|
||||
assert_eq!(
|
||||
backend
|
||||
.0
|
||||
.lock()
|
||||
.expect("test mutex")
|
||||
.platform_https_requests,
|
||||
["https://example.test/team/store.git".to_owned()]
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct MemoryPrompt(Arc<Mutex<MemoryPromptState>>);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user