Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
remote-helper: Produce push certificates
Draft lorenz opened 2 months ago
3 files changed +92 -10 c06b00e3 b101e4a9
modified crates/radicle-remote-helper/src/push.rs
@@ -4,7 +4,7 @@ mod canonical;
mod error;

use std::collections::HashMap;
-
use std::io::IsTerminal;
+
use std::io::{IsTerminal, Write as _};
use std::process::ExitStatus;
use std::str::FromStr;
use std::{assert_eq, io};
@@ -974,29 +974,69 @@ fn push_ref(
    verbosity: Verbosity,
    expected_refs: &[String],
) -> Result<(), Error> {
+
    // FIXME: Not a good idea to randomly load the profile here, do properly.
+
    let profile = Profile::load()?;
+

    let path = dunce::canonicalize(stored.path())?.display().to_string();
    // Nb. The *force* indicator (`+`) is processed by Git tooling before we even reach this code.
    // This happens during the `list for-push` phase.
-
    let refspec = git::fmt::refspec::Refspec { src, dst, force };
+
    let refspec = git::fmt::refspec::Refspec {
+
        src,
+
        dst: dst.strip_namespace(),
+
        force,
+
    };
+

+
    let mut cmd = std::process::Command::new("git");

-
    let mut args = vec!["send-pack".to_string()];
+
    cmd.env("GIT_NAMESPACE", dst.namespace().as_str());
+

+
    let mut args = vec![
+
        "-c".to_string(),
+
        "gpg.format=ssh".to_string(),
+
        "-c".to_string(),
+
        format!(
+
            "user.signingKey=key::{}",
+
            radicle::crypto::ssh::fmt::key(profile.id())
+
        ),
+
        "send-pack".to_string(),
+
    ];

    let verbosity: git::Verbosity = verbosity.into();
    args.extend(verbosity.into_flag());

    args.extend([path.to_string(), refspec.to_string()]);

+
    // FIXME: Promote the hooks path to `Home`.
+
    let hooks = profile
+
        .home()
+
        .path()
+
        .join("hooks")
+
        .to_string_lossy()
+
        .to_string();
+

+
    // FIXME: Think about a good way to generate nonce. The environment variable just gives control.
+
    let nonce =
+
        std::env::var(radicle::profile::env::RAD_RNG_SEED).unwrap_or("thefutureisnow".to_string());
+

+
    // FIXME: Make sure this file exists, create it if it doesn't.
+
    let signers = profile
+
        .home()
+
        .keys()
+
        .join("radicle.pub")
+
        .to_string_lossy()
+
        .to_string();
+

+
    args.push(format!(r#"--receive-pack=git -c receive.certNonceSeed={nonce} -c core.hooksPath="{hooks}" -c gpg.ssh.allowedSignersFile="{signers}" receive-pack"#));
+

+
    args.push("--signed=true".to_string());
+

    for expected in expected_refs {
-
        args.push(format!(
-
            "--force-with-lease=refs/namespaces/{}/{expected}",
-
            dst.namespace()
-
        ));
+
        args.push(format!("--force-with-lease={expected}"));
    }

-
    // Rely on the environment variable `GIT_DIR`.
-
    let working = None;
+
    cmd.args(args);

-
    let output = radicle::git::run(working, args)?;
+
    let output = cmd.output()?;

    if !output.status.success() {
        return Err(Error::SendPackFailed {
@@ -1004,6 +1044,8 @@ fn push_ref(
            stdout: String::from_utf8_lossy(&output.stdout).to_string(),
            status: output.status,
        });
+
    } else {
+
        std::io::stderr().write_all(&output.stderr)?;
    }

    Ok(())
added post-receive
@@ -0,0 +1,14 @@
+
#! /usr/bin/env bash
+

+
# NOTE: Copy/link this file to `$RAD_HOME/hooks/post-receive`
+
# and make it executable.
+

+
#echo "post-receive: begin"
+

+
echo "Push certificate received:"
+
git cat-file blob "$GIT_PUSH_CERT" | sed -e 's/^/\t/'
+
echo ""
+

+
# TODO: Do something interesting with the certificate.
+

+
#echo "post-receive: end"
added pre-receive
@@ -0,0 +1,26 @@
+
#! /usr/bin/env bash
+

+
# NOTE: Copy/link this file to `$RAD_HOME/hooks/pre-receive`
+
# and make it executable.
+

+
#echo "pre-receive: begin"
+

+
#env | grep "GIT_PUSH_CERT"
+

+
# Check that `GIT_PUSH_CERT_NONCE_STATUS=OK`
+

+
if [ "$GIT_PUSH_CERT_NONCE_STATUS" != "OK" ]
+
then
+
	echo "pre-receive: GIT_PUSH_CERT_NONCE_STATUS ≠ \"OK\"."
+
	exit 1
+
fi
+

+
if [ "$GIT_PUSH_CERT_STATUS" != "G" ]
+
then
+
	echo "pre-receive: GIT_PUSH_CERT_STATUS ≠ \"G\"."
+
	exit 2
+
fi
+

+
# TODO Check that $GIT_PUSH_CERT_KEY matches ourselves.
+

+
#echo "pre-receive: end"