summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-10-14 07:43:24 +0000
committerLars Wirzenius <liw@liw.fi>2023-10-14 07:43:24 +0000
commit5da21579b71533a51a0c5a0494712a658361958b (patch)
tree45fe3df14de53a55b7ff9e09fe5d6f435103c36a
parentdc54c84f8aab5b45722f1e75a5d6607cba8aabae (diff)
parent7a8569d83c336d173a04a137716289e0bcde9323 (diff)
downloadsshca-5da21579b71533a51a0c5a0494712a658361958b.tar.gz
Merge branch 'liw/certify-all' into 'main'
feat: certify all users at once Closes #24 See merge request larswirzenius/sshca!69
-rw-r--r--src/cmd/user.rs57
-rw-r--r--src/error.rs3
-rw-r--r--sshca.md28
3 files changed, 77 insertions, 11 deletions
diff --git a/src/cmd/user.rs b/src/cmd/user.rs
index 3bd0050..e1eb4bf 100644
--- a/src/cmd/user.rs
+++ b/src/cmd/user.rs
@@ -131,7 +131,7 @@ impl Runnable for New {
/// Certify a user.
#[derive(Debug, Parser)]
pub struct Certify {
- /// Write output to this file.
+ /// Write output to this file. If --all is used, to a file in this directory.
#[clap(long)]
output: Option<PathBuf>,
@@ -142,24 +142,35 @@ pub struct Certify {
#[clap(long, default_value = DEFAULT_VALIDITY)]
expires_in: String,
+ /// CA to use to certify.
#[clap(long)]
ca: String,
- username: String,
+ /// User to certify, unless --all is used.
+ username: Option<String>,
+
+ /// Certify all users?
+ #[clap(long)]
+ all: bool,
}
-impl Runnable for Certify {
- fn run(&mut self, _config: &Config, store: &mut KeyStore) -> Result<(), CAError> {
+impl Certify {
+ fn certify(
+ &self,
+ username: &str,
+ store: &KeyStore,
+ output: &Option<PathBuf>,
+ ) -> Result<(), CAError> {
let ca = if let Some(pair) = store.get_user_ca(&self.ca) {
pair
} else {
return Err(CAError::NoSuchUserCA(self.ca.clone()));
};
- let user = if let Some(user) = store.get_user(&self.username) {
+ let user = if let Some(user) = store.get_user(username) {
user
} else {
- return Err(CAError::NoSuchUser(self.username.clone()));
+ return Err(CAError::NoSuchUser(username.into()));
};
let key = user.public();
@@ -167,12 +178,36 @@ impl Runnable for Certify {
let valid_for = parse_validity(&self.expires_in)?;
let cert = ca.certify(key, &valid_for, user.principals())?;
- if let Some(output) = &self.output {
- let mut file = File::create(output).map_err(|e| CAError::Create(output.into(), e))?;
- write!(file, "{}", cert).map_err(|e| CAError::Write(output.into(), e))?;
+ if let Some(filename) = output {
+ let mut file =
+ File::create(filename).map_err(|e| CAError::Create(filename.into(), e))?;
+ write!(file, "{}", cert).map_err(|e| CAError::Write(filename.into(), e))?;
} else {
- print!("{}", cert);
- }
+ print!("{}", cert)
+ };
+
+ Ok(())
+ }
+}
+
+impl Runnable for Certify {
+ fn run(&mut self, _config: &Config, store: &mut KeyStore) -> Result<(), CAError> {
+ if self.all {
+ let dirname = if let Some(output) = &self.output {
+ output.clone()
+ } else {
+ PathBuf::from(".")
+ };
+ for user in store.users() {
+ println!("Certifying user {}", user.name());
+ let filename = dirname.join(format!("{}-cert.pub", user.name()));
+ self.certify(user.name(), store, &Some(filename))?;
+ }
+ } else if let Some(user) = self.username.as_ref() {
+ self.certify(user, store, &self.output)?;
+ } else {
+ return Err(CAError::UserOrAll);
+ };
Ok(())
}
diff --git a/src/error.rs b/src/error.rs
index 5ef6d9d..65b422e 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -94,4 +94,7 @@ pub enum CAError {
#[error("host key for {0} has expired, only valid until {1}")]
ExpiredHostKey(String, String),
+
+ #[error("must give name of user to certify, or --all")]
+ UserOrAll,
}
diff --git a/sshca.md b/sshca.md
index 57dc317..7e91162 100644
--- a/sshca.md
+++ b/sshca.md
@@ -1006,6 +1006,34 @@ when I run ssh-keygen -L -f my.cert
then stdout matches regex Valid: from \d+-\d+-\d+T\d+:\d+:\d+ to \d+-\d+-\d+T\d+:\d+:\d+
~~~
+### Certify all users
+
+_Requirement: we must be able to create all users certificates at once._
+
+Justification: This is convenient if there are many users.
+
+~~~scenario
+given an installed sshca
+given file .config/sshca/config.yaml from config.yaml
+
+when I run sshca ca new user CAv1
+
+when I run ssh-keygen -t ed25519 -N '' -f granny
+when I run sshca user new granny granny.pub
+
+when I run ssh-keygen -t ed25519 -N '' -f gytha
+when I run sshca user new gytha gytha.pub
+
+when I run sshca user certify --ca CAv1 --all
+then file granny-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/
+then file gytha-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/
+
+given a directory certs
+when I run sshca user certify --ca CAv1 --all --output=certs
+then file certs/granny-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/
+then file certs/gytha-cert.pub matches regex /^ssh-ed25519-cert-v01@openssh.com/
+~~~
+
### User CA can't certify a host
_Requirement: we can't certify a host with a user CA._