diff options
author | Heiko <heiko@schaefer.name> | 2021-05-06 17:22:39 +0200 |
---|---|---|
committer | Heiko <heiko@schaefer.name> | 2021-05-06 18:17:31 +0200 |
commit | 53adfe14c15f5bee30c74b318f3671a447086cfd (patch) | |
tree | 940e5bc456de81eee3d1054a6eaf7406f5f57a5d | |
parent | 1848cc60aa0c5b9ed79de5369e0d219b1fa86000 (diff) | |
download | openpgp-ca-53adfe14c15f5bee30c74b318f3671a447086cfd.tar.gz |
Break the project into separate crates:
- openpgp-ca-lib
- openpgp-ca-bin
- openpgp-ca-restd
-rw-r--r-- | .reuse/dep5 | 2 | ||||
-rw-r--r-- | Cargo.lock | 35 | ||||
-rw-r--r-- | Cargo.toml | 76 | ||||
-rw-r--r-- | openpgp-ca-bin/Cargo.toml | 26 | ||||
-rw-r--r-- | openpgp-ca-bin/src/bin.rs (renamed from src/bin.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-bin/src/cli.rs (renamed from src/cli.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/Cargo.toml | 44 | ||||
-rw-r--r-- | openpgp-ca-lib/diesel.toml (renamed from diesel.toml) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/migrations/.gitkeep (renamed from migrations/.gitkeep) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/migrations/2020-08-17-130000_database/down.sql (renamed from migrations/2020-08-17-130000_database/down.sql) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/migrations/2020-08-17-130000_database/up.sql (renamed from migrations/2020-08-17-130000_database/up.sql) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/migrations/2020-10-13-130000_extend_certs/down.sql (renamed from migrations/2020-10-13-130000_extend_certs/down.sql) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/migrations/2020-10-13-130000_extend_certs/up.sql (renamed from migrations/2020-10-13-130000_extend_certs/up.sql) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/bridge.rs (renamed from src/bridge.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/ca.rs (renamed from src/ca.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/ca_secret.rs (renamed from src/ca_secret.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/cert.rs (renamed from src/cert.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/db/mod.rs (renamed from src/db/mod.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/db/models.rs (renamed from src/db/models.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/db/schema.rs (renamed from src/db/schema.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/export.rs (renamed from src/export.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/lib.rs (renamed from src/lib.rs) | 7 | ||||
-rw-r--r-- | openpgp-ca-lib/src/pgp.rs (renamed from src/pgp.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/revocation.rs (renamed from src/revocation.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/src/update.rs (renamed from src/update.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/tests/gnupg/mod.rs (renamed from tests/gnupg/mod.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/tests/test_gpg.rs (renamed from tests/test_gpg.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-lib/tests/test_oca.rs (renamed from tests/test_oca.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-restd/Cargo.toml | 51 | ||||
-rw-r--r-- | openpgp-ca-restd/src/README.md (renamed from src/restd/README.md) | 0 | ||||
-rw-r--r-- | openpgp-ca-restd/src/bin.rs (renamed from src/restd/bin.rs) | 5 | ||||
-rw-r--r-- | openpgp-ca-restd/src/cert_info.rs (renamed from src/restd/cert_info.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-restd/src/cli.rs (renamed from src/restd/cli.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-restd/src/client.rs (renamed from src/restd/client.rs) | 4 | ||||
-rw-r--r-- | openpgp-ca-restd/src/json.rs (renamed from src/restd/json.rs) | 4 | ||||
-rw-r--r-- | openpgp-ca-restd/src/lib.rs | 17 | ||||
-rw-r--r-- | openpgp-ca-restd/src/process_certs.rs (renamed from src/restd/process_certs.rs) | 15 | ||||
-rw-r--r-- | openpgp-ca-restd/src/restd.rs (renamed from src/restd/mod.rs) | 27 | ||||
-rw-r--r-- | openpgp-ca-restd/src/util.rs (renamed from src/restd/util.rs) | 0 | ||||
-rw-r--r-- | openpgp-ca-restd/tests/gnupg/mod.rs | 544 | ||||
-rw-r--r-- | openpgp-ca-restd/tests/test_restd.rs (renamed from tests/test_restd.rs) | 9 |
41 files changed, 747 insertions, 119 deletions
diff --git a/.reuse/dep5 b/.reuse/dep5 index 1b09bfa..7209a51 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -8,7 +8,7 @@ Copyright: 2019-2020 Heiko Schaefer <heiko@schaefer.name> License: GPL-3.0-or-later -Files: src/db/schema.rs Cargo.lock +Files: openpgp-ca-lib/src/db/schema.rs Cargo.lock Copyright: 2019-2020 Heiko Schaefer <heiko@schaefer.name> License: CC0-1.0 @@ -1737,27 +1737,48 @@ name = "openpgp-ca" version = "0.10.1" dependencies = [ "anyhow", + "clap", + "openpgp-ca-lib", + "structopt", +] + +[[package]] +name = "openpgp-ca-lib" +version = "0.10.1" +dependencies = [ + "anyhow", "chbs", "chrono", - "clap", "csv", "diesel", "diesel_migrations", - "futures", - "futures-util", - "lazy_static", - "once_cell", "openpgp-keylist", "publicsuffix", + "rexpect", + "sequoia-net", + "sequoia-openpgp", + "sha2", + "tempfile", + "tokio 0.2.25", +] + +[[package]] +name = "openpgp-ca-restd" +version = "0.10.1" +dependencies = [ + "anyhow", + "chrono", + "clap", + "csv", + "once_cell", + "openpgp-ca-lib", "reqwest 0.10.10", "rexpect", "rocket", "rocket_contrib", - "sequoia-net", "sequoia-openpgp", "serde", "serde_json", - "sha2", "structopt", "tempfile", "tokio 0.2.25", @@ -1,73 +1,9 @@ # SPDX-FileCopyrightText: 2019-2021 Heiko Schaefer <heiko@schaefer.name> # SPDX-License-Identifier: GPL-3.0-or-later -[package] -name = "openpgp-ca" -version = "0.10.1" -description = "OpenPGP CA is a tool for managing and certifying OpenPGP keys" -authors = ["Heiko Schaefer <heiko@schaefer.name>"] -license = "GPL-3.0-or-later" -categories = ["cryptography", "email"] -keywords = ["OpenPGP", "Sequoia", "PGP"] -homepage = "https://openpgp-ca.org" -repository = "https://gitlab.com/openpgp-ca/openpgp-ca" -documentation = "https://openpgp-ca.org/doc/" -edition = "2018" - -[lib] -name = "openpgp_ca_lib" -path = "src/lib.rs" - -[[bin]] -name = "openpgp-ca" -path = "src/bin.rs" - -[[bin]] -name = "openpgp-ca-restd" -path = "src/restd/bin.rs" - -[dependencies] -structopt = "0.3" -clap = "2.33" - -diesel = { version = "1.4", features = ["sqlite"] } -diesel_migrations = "1.4" - -lazy_static = "1.4" -once_cell = "1.4" -anyhow = "1.0" -chrono = { version = "0.4", features = ["serde"] } - -reqwest = { version = "0.10", features = ["blocking", "json"] } - -chbs = "0.0.8" -publicsuffix = "1.5" - -sha2 = "0.9" - -futures = "0.3" -futures-util = "0.3" - -openpgp-keylist = "0.2" - -sequoia-openpgp = "1.1" -sequoia-net = "0.23" - -# restd -# -> use unreleased rocket 0.5 for now (which works with rust stable) -# (use fitting version of serde that go with that rocket 0.5 revision) -rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "1f1f44f336e5a172361fc1860461bb03667b1ed2"} -rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "1f1f44f336e5a172361fc1860461bb03667b1ed2", features = ["handlebars_templates"] } -serde = { version = "=1.0.118", features = ["derive"] } -serde_json = "=1.0.60" - -# for rocket 0.5 -[dependencies.tokio] -version = "0.2.22" -features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] - -# for tests -[dev-dependencies] -tempfile = "3.1" -csv = "1.1" -rexpect = "0.4" +[workspace] +members = [ + "openpgp-ca-lib", + "openpgp-ca-bin", + "openpgp-ca-restd", +] diff --git a/openpgp-ca-bin/Cargo.toml b/openpgp-ca-bin/Cargo.toml new file mode 100644 index 0000000..167dc3d --- /dev/null +++ b/openpgp-ca-bin/Cargo.toml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2019-2021 Heiko Schaefer <heiko@schaefer.name> +# SPDX-License-Identifier: GPL-3.0-or-later + +[package] +name = "openpgp-ca" +version = "0.10.1" +description = "OpenPGP CA is a tool for managing and certifying OpenPGP keys" +authors = ["Heiko Schaefer <heiko@schaefer.name>"] +license = "GPL-3.0-or-later" +categories = ["cryptography", "email"] +keywords = ["OpenPGP", "Sequoia", "PGP"] +homepage = "https://openpgp-ca.org" +repository = "https://gitlab.com/openpgp-ca/openpgp-ca" +documentation = "https://openpgp-ca.org/doc/" +edition = "2018" + +[[bin]] +name = "openpgp-ca" +path = "src/bin.rs" + +[dependencies] +structopt = "0.3" +clap = "2.33" +anyhow = "1.0" + +openpgp-ca-lib = { path = "../openpgp-ca-lib" } diff --git a/src/bin.rs b/openpgp-ca-bin/src/bin.rs index 7205f43..7205f43 100644 --- a/src/bin.rs +++ b/openpgp-ca-bin/src/bin.rs diff --git a/src/cli.rs b/openpgp-ca-bin/src/cli.rs index 41f02ec..41f02ec 100644 --- a/src/cli.rs +++ b/openpgp-ca-bin/src/cli.rs diff --git a/openpgp-ca-lib/Cargo.toml b/openpgp-ca-lib/Cargo.toml new file mode 100644 index 0000000..8fabe52 --- /dev/null +++ b/openpgp-ca-lib/Cargo.toml @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2019-2021 Heiko Schaefer <heiko@schaefer.name> +# SPDX-License-Identifier: GPL-3.0-or-later + +[package] +name = "openpgp-ca-lib" +version = "0.10.1" +description = "OpenPGP CA is a tool for managing and certifying OpenPGP keys" +authors = ["Heiko Schaefer <heiko@schaefer.name>"] +license = "GPL-3.0-or-later" +categories = ["cryptography", "email"] +keywords = ["OpenPGP", "Sequoia", "PGP"] +homepage = "https://openpgp-ca.org" +repository = "https://gitlab.com/openpgp-ca/openpgp-ca" +documentation = "https://openpgp-ca.org/doc/" +edition = "2018" + +[lib] +name = "openpgp_ca_lib" +path = "src/lib.rs" + +[dependencies] +diesel = { version = "1.4", features = ["sqlite"] } +diesel_migrations = "1.4" + +anyhow = "1.0" +chrono = { version = "0.4", features = ["serde"] } + +tokio = { version = "0.2.22", features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] } + +chbs = "0.0.8" +publicsuffix = "1.5" + +sha2 = "0.9" + +openpgp-keylist = "0.2" + +sequoia-openpgp = "1.1" +sequoia-net = "0.23" + +# for tests +[dev-dependencies] +tempfile = "3.1" +csv = "1.1" +rexpect = "0.4" diff --git a/diesel.toml b/openpgp-ca-lib/diesel.toml index aa52397..aa52397 100644 --- a/diesel.toml +++ b/openpgp-ca-lib/diesel.toml diff --git a/migrations/.gitkeep b/openpgp-ca-lib/migrations/.gitkeep index e69de29..e69de29 100644 --- a/migrations/.gitkeep +++ b/openpgp-ca-lib/migrations/.gitkeep diff --git a/migrations/2020-08-17-130000_database/down.sql b/openpgp-ca-lib/migrations/2020-08-17-130000_database/down.sql index d65762f..d65762f 100644 --- a/migrations/2020-08-17-130000_database/down.sql +++ b/openpgp-ca-lib/migrations/2020-08-17-130000_database/down.sql diff --git a/migrations/2020-08-17-130000_database/up.sql b/openpgp-ca-lib/migrations/2020-08-17-130000_database/up.sql index 5b089e9..5b089e9 100644 --- a/migrations/2020-08-17-130000_database/up.sql +++ b/openpgp-ca-lib/migrations/2020-08-17-130000_database/up.sql diff --git a/migrations/2020-10-13-130000_extend_certs/down.sql b/openpgp-ca-lib/migrations/2020-10-13-130000_extend_certs/down.sql index 84ae708..84ae708 100644 --- a/migrations/2020-10-13-130000_extend_certs/down.sql +++ b/openpgp-ca-lib/migrations/2020-10-13-130000_extend_certs/down.sql diff --git a/migrations/2020-10-13-130000_extend_certs/up.sql b/openpgp-ca-lib/migrations/2020-10-13-130000_extend_certs/up.sql index a3d5c92..a3d5c92 100644 --- a/migrations/2020-10-13-130000_extend_certs/up.sql +++ b/openpgp-ca-lib/migrations/2020-10-13-130000_extend_certs/up.sql diff --git a/src/bridge.rs b/openpgp-ca-lib/src/bridge.rs index ddf014a..ddf014a 100644 --- a/src/bridge.rs +++ b/openpgp-ca-lib/src/bridge.rs diff --git a/src/ca.rs b/openpgp-ca-lib/src/ca.rs index 9ce8d7c..9ce8d7c 100644 --- a/src/ca.rs +++ b/openpgp-ca-lib/src/ca.rs diff --git a/src/ca_secret.rs b/openpgp-ca-lib/src/ca_secret.rs index 03180be..03180be 100644 --- a/src/ca_secret.rs +++ b/openpgp-ca-lib/src/ca_secret.rs diff --git a/src/cert.rs b/openpgp-ca-lib/src/cert.rs index 63c7891..63c7891 100644 --- a/src/cert.rs +++ b/openpgp-ca-lib/src/cert.rs diff --git a/src/db/mod.rs b/openpgp-ca-lib/src/db/mod.rs index dee370f..dee370f 100644 --- a/src/db/mod.rs +++ b/openpgp-ca-lib/src/db/mod.rs diff --git a/src/db/models.rs b/openpgp-ca-lib/src/db/models.rs index 9503bc5..9503bc5 100644 --- a/src/db/models.rs +++ b/openpgp-ca-lib/src/db/models.rs diff --git a/src/db/schema.rs b/openpgp-ca-lib/src/db/schema.rs index 9c2a729..9c2a729 100644 --- a/src/db/schema.rs +++ b/openpgp-ca-lib/src/db/schema.rs diff --git a/src/export.rs b/openpgp-ca-lib/src/export.rs index dc356b7..dc356b7 100644 --- a/src/export.rs +++ b/openpgp-ca-lib/src/export.rs diff --git a/src/lib.rs b/openpgp-ca-lib/src/lib.rs index 76d58de..0977ecc 100644 --- a/src/lib.rs +++ b/openpgp-ca-lib/src/lib.rs @@ -15,17 +15,12 @@ extern crate diesel; #[macro_use] extern crate diesel_migrations; -#[macro_use] -extern crate rocket; - mod bridge; pub mod ca; mod ca_secret; mod cert; -mod db; +pub mod db; mod export; pub mod pgp; mod revocation; mod update; - -pub mod restd; diff --git a/src/pgp.rs b/openpgp-ca-lib/src/pgp.rs index a5b535c..a5b535c 100644 --- a/src/pgp.rs +++ b/openpgp-ca-lib/src/pgp.rs diff --git a/src/revocation.rs b/openpgp-ca-lib/src/revocation.rs index 0fa2d97..0fa2d97 100644 --- a/src/revocation.rs +++ b/openpgp-ca-lib/src/revocation.rs diff --git a/src/update.rs b/openpgp-ca-lib/src/update.rs index 0987004..0987004 100644 --- a/src/update.rs +++ b/openpgp-ca-lib/src/update.rs diff --git a/tests/gnupg/mod.rs b/openpgp-ca-lib/tests/gnupg/mod.rs index 6eb9c5b..6eb9c5b 100644 --- a/tests/gnupg/mod.rs +++ b/openpgp-ca-lib/tests/gnupg/mod.rs diff --git a/tests/test_gpg.rs b/openpgp-ca-lib/tests/test_gpg.rs index cb63488..cb63488 100644 --- a/tests/test_gpg.rs +++ b/openpgp-ca-lib/tests/test_gpg.rs diff --git a/tests/test_oca.rs b/openpgp-ca-lib/tests/test_oca.rs index 8132e68..8132e68 100644 --- a/tests/test_oca.rs +++ b/openpgp-ca-lib/tests/test_oca.rs diff --git a/openpgp-ca-restd/Cargo.toml b/openpgp-ca-restd/Cargo.toml new file mode 100644 index 0000000..b0c4d17 --- /dev/null +++ b/openpgp-ca-restd/Cargo.toml @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2019-2021 Heiko Schaefer <heiko@schaefer.name> +# SPDX-License-Identifier: GPL-3.0-or-later + +[package] +name = "openpgp-ca-restd" +version = "0.10.1" +description = "OpenPGP CA is a tool for managing and certifying OpenPGP keys" +authors = ["Heiko Schaefer <heiko@schaefer.name>"] +license = "GPL-3.0-or-later" +categories = ["cryptography", "email"] +keywords = ["OpenPGP", "Sequoia", "PGP"] +homepage = "https://openpgp-ca.org" +repository = "https://gitlab.com/openpgp-ca/openpgp-ca" +documentation = "https://openpgp-ca.org/doc/" +edition = "2018" + +[[bin]] +name = "openpgp-ca-restd" +path = "src/bin.rs" + +[dependencies] +structopt = "0.3" +clap = "2.33" + +once_cell = "1.4" +anyhow = "1.0" +chrono = { version = "0.4", features = ["serde"] } + +reqwest = { version = "0.10", features = ["blocking", "json"] } + +sequoia-openpgp = "1.1" + +openpgp-ca-lib = { path = "../openpgp-ca-lib" } + +# restd +# -> use unreleased rocket 0.5 for now (which works with rust stable) +# (use fitting version of serde that go with that rocket 0.5 revision) +rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "1f1f44f336e5a172361fc1860461bb03667b1ed2" } +rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "1f1f44f336e5a172361fc1860461bb03667b1ed2", features = ["handlebars_templates"] } +serde = { version = "=1.0.118", features = ["derive"] } +serde_json = "=1.0.60" + +[dependencies.tokio] +version = "0.2.22" +features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] + +# for tests +[dev-dependencies] +tempfile = "3.1" +csv = "1.1" +rexpect = "0.4" diff --git a/src/restd/README.md b/openpgp-ca-restd/src/README.md index 4711a15..4711a15 100644 --- a/src/restd/README.md +++ b/openpgp-ca-restd/src/README.md diff --git a/src/restd/bin.rs b/openpgp-ca-restd/src/bin.rs index b75f5fc..045091c 100644 --- a/src/restd/bin.rs +++ b/openpgp-ca-restd/src/bin.rs @@ -12,11 +12,14 @@ #[macro_use] extern crate rocket; +mod cert_info; mod cli; +pub mod json; +mod process_certs; +mod restd; pub mod util; use cli::RestdCli; -use openpgp_ca_lib::restd; use structopt::StructOpt; #[launch] diff --git a/src/restd/cert_info.rs b/openpgp-ca-restd/src/cert_info.rs index 449d6ea..449d6ea 100644 --- a/src/restd/cert_info.rs +++ b/openpgp-ca-restd/src/cert_info.rs diff --git a/src/restd/cli.rs b/openpgp-ca-restd/src/cli.rs index eb577d5..eb577d5 100644 --- a/src/restd/cli.rs +++ b/openpgp-ca-restd/src/cli.rs diff --git a/src/restd/client.rs b/openpgp-ca-restd/src/client.rs index e660b41..a7cf230 100644 --- a/src/restd/client.rs +++ b/openpgp-ca-restd/src/client.rs @@ -11,9 +11,7 @@ use reqwest::{Response, StatusCode}; -use crate::restd::json::{ - CertResultJson, Certificate, ReturnError, ReturnGoodJson, -}; +use crate::json::{CertResultJson, Certificate, ReturnError, ReturnGoodJson}; use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; pub struct Client { diff --git a/src/restd/json.rs b/openpgp-ca-restd/src/json.rs index 7d677c1..0bcd970 100644 --- a/src/restd/json.rs +++ b/openpgp-ca-restd/src/json.rs @@ -10,8 +10,8 @@ use rocket::response::status::BadRequest; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; -use crate::db::models; -use crate::restd::cert_info::CertInfo; +use crate::cert_info::CertInfo; +use openpgp_ca_lib::db::models; /// A container for return-data about one Cert. /// diff --git a/openpgp-ca-restd/src/lib.rs b/openpgp-ca-restd/src/lib.rs new file mode 100644 index 0000000..7e41456 --- /dev/null +++ b/openpgp-ca-restd/src/lib.rs @@ -0,0 +1,17 @@ +// Copyright 2019-2021 Heiko Schaefer <heiko@schaefer.name> +// +// This file is part of OpenPGP CA +// https://gitlab.com/openpgp-ca/openpgp-ca +// +// SPDX-FileCopyrightText: 2019-2021 Heiko Schaefer <heiko@schaefer.name> +// SPDX-License-Identifier: GPL-3.0-or-later + +#[macro_use] +extern crate rocket; + +pub mod cert_info; +pub mod client; +pub mod json; +pub mod process_certs; +pub mod restd; +pub mod util; diff --git a/src/restd/process_certs.rs b/openpgp-ca-restd/src/process_certs.rs index a04b4ec..d411111 100644 --- a/src/restd/process_certs.rs +++ b/openpgp-ca-restd/src/process_certs.rs @@ -8,6 +8,7 @@ use core::time::Duration; use std::collections::HashSet; +use std::error::Error; use std::ops::Deref; use std::str::FromStr; use std::time::SystemTime; @@ -19,13 +20,13 @@ use sequoia_openpgp::types::{ }; use sequoia_openpgp::{Cert, Message, Packet}; -use crate::ca::OpenpgpCa; -use crate::pgp::Pgp; +use openpgp_ca_lib::ca::OpenpgpCa; +use openpgp_ca_lib::pgp::Pgp; + +use crate::cert_info::CertInfo; +use crate::json::*; use crate::restd; -use crate::restd::cert_info::CertInfo; -use crate::restd::json::*; -use crate::restd::util::{is_email_in_domain, split_emails, user_id_filter}; -use std::error::Error; +use crate::util::{is_email_in_domain, split_emails, user_id_filter}; const STANDARD_POLICY: &StandardPolicy = &StandardPolicy::new(); @@ -604,7 +605,7 @@ pub fn process_certs( #[cfg(test)] mod tests { - use crate::restd::process_certs::unpack_certring; + use crate::process_certs::unpack_certring; #[test] fn test_certring() { diff --git a/src/restd/mod.rs b/openpgp-ca-restd/src/restd.rs index abe99f2..7c16101 100644 --- a/src/restd/mod.rs +++ b/openpgp-ca-restd/src/restd.rs @@ -14,22 +14,13 @@ use rocket::http::Status; use rocket::response::status::BadRequest; use rocket_contrib::json::Json; -use crate::ca::OpenpgpCa; -use crate::db::models; -use crate::pgp::Pgp; -use crate::restd::cert_info::CertInfo; -use crate::restd::json::*; -use crate::restd::process_certs::{ - cert_to_cert_info, cert_to_warn, process_certs, -}; - -mod cli; -mod process_certs; -mod util; - -pub mod cert_info; -pub mod client; -pub mod json; +use openpgp_ca_lib::ca::OpenpgpCa; +use openpgp_ca_lib::db::models; +use openpgp_ca_lib::pgp::Pgp; + +use crate::cert_info::CertInfo; +use crate::json::*; +use crate::process_certs::{cert_to_cert_info, cert_to_warn, process_certs}; static DB: OnceCell<Option<String>> = OnceCell::new(); @@ -39,10 +30,10 @@ thread_local! { } // CA certifications are good for 365 days -const CERTIFICATION_DAYS: u64 = 365; +pub const CERTIFICATION_DAYS: u64 = 365; // armored cert size limit (1 MiB) -const CERT_SIZE_LIMIT: usize = 1024 * 1024; +pub const CERT_SIZE_LIMIT: usize = 1024 * 1024; // FIXME: link for information about bad certificates // - and what to do about them diff --git a/src/restd/util.rs b/openpgp-ca-restd/src/util.rs index d636e20..d636e20 100644 --- a/src/restd/util.rs +++ b/openpgp-ca-restd/src/util.rs diff --git a/openpgp-ca-restd/tests/gnupg/mod.rs b/openpgp-ca-restd/tests/gnupg/mod.rs new file mode 100644 index 0000000..6eb9c5b --- /dev/null +++ b/openpgp-ca-restd/tests/gnupg/mod.rs @@ -0,0 +1,544 @@ +// Copyright 2019-2020 Heiko Schaefer <heiko@schaefer.name> +// +// This file is part of OpenPGP CA +// https://gitlab.com/openpgp-ca/openpgp-ca +// +// SPDX-FileCopyrightText: 2019-2020 Heiko Schaefer <heiko@schaefer.name> +// SPDX-License-Identifier: GPL-3.0-or-later + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use std::fmt; +use std::io::Read; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::process::Stdio; + +use anyhow::{Context, Result}; +use csv::StringRecord; + +// FIXME: `LC_ALL=C` to make calls locale-independent + +pub fn make_context() -> Result<Ctx> { + let ctx = Ctx::ephemeral().context( + "SKIP: Failed to create GnuPG context. Is GnuPG installed?", + )?; + + ctx.start("gpg-agent").context( + "SKIP: Failed to to start gpg-agent. Is the GnuPG agent installed?", + )?; + + Ok(ctx) +} + +/// A GnuPG context. +#[derive(Debug)] +pub struct Ctx { + homedir: Option<PathBuf>, + components: BTreeMap<String, PathBuf>, + directories: BTreeMap<String, PathBuf>, + sockets: BTreeMap<String, PathBuf>, + #[allow(dead_code)] // We keep it around for the cleanup. + ephemeral: Option<tempfile::TempDir>, +} + +impl Ctx { + /// Creates a new context for the default GnuPG home directory. + pub fn new() -> Result<Self> { + Self::make(None, None) + } + + /// get the homedir Path + pub fn get_homedir(&self) -> &Path { + self.homedir.as_ref().unwrap().as_path() + } + + /// Creates a new context for the given GnuPG home directory. + pub fn with_homedir<P>(homedir: P) -> Result<Self> + where + P: AsRef<Path>, + { + Self::make(Some(homedir.as_ref()), None) + } + + /// Creates a new ephemeral context. + /// + /// The created home directory will be deleted once this object is + /// dropped. + pub fn ephemeral() -> Result<Self> { + Self::make(None, Some(tempfile::tempdir()?)) + } + + /// don't delete home directory. + /// this is intended for manually debugging data that was created in a + /// test-run. + pub fn leak_tempdir(&mut self) -> Option<PathBuf> { + if self.ephemeral.is_some() { + let _ = self.stop_all(); + let _ = self.remove_socket_dir(); + } + self.ephemeral.take().map(tempfile::TempDir::into_path) + } + + fn make( + homedir: Option<&Path>, + ephemeral: Option<tempfile::TempDir>, + ) -> Result<Self> { + let mut components: BTreeMap<String, PathBuf> = Default::default(); + let mut directories: BTreeMap<String, PathBuf> = Default::default(); + let mut sockets: BTreeMap<String, PathBuf> = Default::default(); + + let homedir: Option<PathBuf> = ephemeral + .as_ref() + .map(|tmp| tmp.path()) + .or(homedir) + .map(|p| p.into()); + + for fields in + Self::gpgconf(&homedir, &["--list-components"], 3)?.into_iter() + { + components.insert( + String::from_utf8(fields[0].clone())?, + String::from_utf8(fields[2].clone())?.into(), + ); + } + + for fields in Self::gpgconf(&homedir, &["--list-dirs"], 2)?.into_iter() + { + let (mut key, value) = (fields[0].clone(), fields[1].clone()); + if key.ends_with(b"-socket") { + let l = key.len(); + key.truncate(l - b"-socket".len()); + sockets.insert( + String::from_utf8(key)?, + String::from_utf8(value)?.into(), + ); + } else { + directories.insert( + String::from_utf8(key)?, + String::from_utf8(value)?.into(), + ); + } + } + + Ok(Ctx { + homedir, + components, + directories, + sockets, + ephemeral, + }) + } + + fn gpgconf( + homedir: &Option<PathBuf>, + arguments: &[&str], + nfields: usize, + ) -> Result<Vec<Vec<Vec<u8>>>> { + let nl = |&c: &u8| c as char == '\n'; + let colon = |&c: &u8| c as char == ':'; + + let mut gpgconf = Command::new("gpgconf"); + if let Some(homedir) = homedir { + gpgconf.arg("--homedir").arg(homedir); + + // https://dev.gnupg.org/T4496 + gpgconf.env("GNUPGHOME", homedir); + } + + for argument in arguments { + gpgconf.arg(argument); + } + let output = gpgconf.output().map_err(|e| -> anyhow::Error { + GnupgError::GgpConf(e.to_string()).into() + })?; + + if output.status.success() { + let mut result = Vec::new(); + for line in output.stdout.split(nl) { + if line.is_empty() { + // EOF. + break; + } + + let fields = line + .splitn(nfields, colon) + .map(|f| f.to_vec()) + .collect::<Vec<_>>(); + + if fields.len() != nfields { + return Err(GnupgError::GgpConf(format!( + "Malformed response, expected {} fields, \ + on line: {:?}", + nfields, line + )) + .into()); + } + + result.push(fields); + } + Ok(result) + } else { + Err(GnupgError::GgpConf( + String::from_utf8_lossy(&output.stderr).into_owned(), + ) + .into()) + } + } + + /// Returns the path to a GnuPG component. + pub fn component<C>(&self, component: C) -> Result<&Path> + where + C: AsRef<str>, + { + self.components + .get(component.as_ref()) + .map(|p| p.as_path()) + .ok_or_else(|| { + GnupgError::GgpConf(format!( + "No such component {:?}", + component.as_ref() + )) + .into() + }) + } + + /// Returns the path to a GnuPG directory. + pub fn directory<C>(&self, directory: C) -> Result<&Path> + where + C: AsRef<str>, + { + self.directories + .get(directory.as_ref()) + .map(|p| p.as_path()) + .ok_or_else(|| { + GnupgError::GgpConf(format!( + "No such directory {:?}", + directory.as_ref() + )) + .into() + }) + } + + /// Returns the path to a GnuPG socket. + pub fn socket<C>(&self, socket: C) -> Result<&Path> + where + C: AsRef<str>, + { + self.sockets + .get(socket.as_ref()) + .map(|p| p.as_path()) + .ok_or_else(|| { + GnupgError::GgpConf(format!( + "No such socket {:?}", + socket.as_ref() + )) + .into() + }) + } + + /// Creates directories for RPC communication. + pub fn create_socket_dir(&self) -> Result<()> { + Self::gpgconf(&self.homedir, &["--create-socketdir"], 1)?; + Ok(()) + } + + /// Removes directories for RPC communication. + /// + /// Note: This will stop all servers once they note that their + /// socket is gone. + pub fn remove_socket_dir(&self) -> Result<()> { + Self::gpgconf(&self.homedir, &["--remove-socketdir"], 1)?; + Ok(()) + } + + /// Starts a GnuPG component. + pub fn start(&self, component: &str) -> Result<()> { + self.create_socket_dir()?; + Self::gpgconf(&self.homedir, &["--launch", component], 1)?; + Ok(()) + } + + /// Stops a GnuPG component. + pub fn stop(&self, component: &str) -> Result<()> { + Self::gpgconf(&self.homedir, &["--kill", component], 1)?; + Ok(()) + } + + /// Stops all GnuPG components. + pub fn stop_all(&self) -> Result<()> { + self.stop("all") + } +} + +impl Drop for Ctx { + fn drop(&mut self) { + if self.ephemeral.is_some() { + let _ = self.stop_all(); + let _ = self.remove_socket_dir(); + } + } +} + +impl std::error::Error for GnupgError {} + +impl fmt::Display for GnupgError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GnupgError::GgpConf(s) => write!(f, "gpgconf: {}", s), + GnupgError::OperationFailed(s) => { + write!(f, "Operation failed: {}", s) + } + GnupgError::ProtocolError(s) => { + write!(f, "Protocol violation: {}", s) + } + } + } +} + +#[derive(Debug)] +/// Errors used in this module. +pub enum GnupgError { + /// Errors related to `gpgconf`. + GgpConf(String), + + /// The remote operation failed. + OperationFailed(String), + + /// The remote party violated the protocol. + ProtocolError(String), +} + +pub fn import(ctx: &Ctx, what: &[u8]) { + let mut gpg = Command::new("gpg") + .stdin(Stdio::piped()) + .arg("--homedir") + .arg(ctx.directory("homedir").unwrap()) + .arg("--import") + .spawn() + .expect("failed to start gpg"); + gpg.stdin.as_mut().unwrap().write_all(what).unwrap(); + let status = gpg.wait().unwrap(); + assert!(status.success()); +} + +pub fn export(ctx: &Ctx, search: &str) -> String { + let mut out = String::new(); + + let mut gpg = Command::new("gpg") + .stdout(Stdio::piped()) + .arg("--homedir") + .arg(ctx.directory("homedir").unwrap()) + .arg("--armor") + .arg("--export") + .arg(search) + .spawn() + .expect("failed to start gpg"); + let status = gpg.wait().unwrap(); + gpg.stdout + .as_mut() + .unwrap() + .read_to_string(&mut out) + .unwrap(); + assert!(status.success()); + + out +} + +pub fn export_secret(ctx: &Ctx, search: &str) -> String { + let mut out = String::new(); + + let mut gpg = Command::new("gpg") + .stdout(Stdio::piped()) + .arg("--homedir") + .arg(ctx.directory("homedir").unwrap()) + .arg("--armor") + .arg("--export-secret-keys") + .arg(search) + .spawn() + .expect("failed to start gpg"); + let status = gpg.wait().unwrap(); + gpg.stdout + .as_mut() + .unwrap() + .read_to_string(&mut out) + .unwrap(); + assert!(status.success()); + + out +} + +pub fn list_keys(ctx: &Ctx) -> Result<HashMap<String, String>> { + let res = list_keys_raw(&ctx); + + // filter: keep only the "uid" lines + let uids = res + .iter() + .filter(|&line| line.get(0) == Some("uid")) + .cloned() + .collect::<Vec<_>>(); + + // map: uid -> trust + Ok(uids + .iter() + .map(|u| (u.get(9).unwrap().to_owned(), u.get(1).unwrap().to_owned())) + .collect()) +} + +fn list_keys_raw(ctx: &Ctx) -> Vec<StringRecord> { + let gpg = Command::new("gpg") + .stdin(Stdio::piped()) + .arg("--homedir") + .arg(ctx.directory("homedir").unwrap()) + .arg("--list-keys") + .arg("--with-colons") + .output() + .expect("failed to start gpg"); + + let mut rdr = csv::ReaderBuilder::new() + .has_headers(false) + .delimiter(b':') + .flexible(true) + .from_reader(gpg.stdout.as_slice()); + + let status = gpg.status; + assert!(status.success()); + + rdr.records().map(|rec| rec.unwrap()).collect() +} + +pub fn edit_trust(ctx: &Ctx, user_id: &str, trust: u8) -> Result<()> { + let homedir = + String::from(ctx.directory("homedir").unwrap().to_str().unwrap()); + + let cmd = format!("gpg --homedir {} --edit-key {}", homedir, user_id); + + let mut p = rexpect::spawn(&cmd, Some(10_000)).unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("trust").unwrap(); + p.exp_string("Your decision?").unwrap(); + p.send_line(&format!("{}", trust)).unwrap(); + p.exp_string( + "Do you really want to set this key to ultimate trust? (y/N)", + ) + .unwrap(); + p.send_line("y").unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("quit").unwrap(); + p.exp_eof().unwrap(); + + Ok(()) +} + +pub fn make_revocation( + ctx: &Ctx, + user_id: &str, + filename: &str, + reason: u8, +) -> Result<()> { + let homedir = + String::from(ctx.directory("homedir").unwrap().to_str().unwrap()); + + let cmd = format!( + "gpg --homedir {} --output {} --gen-revoke {}", + homedir, filename, user_id + ); + + let mut p = rexpect::spawn(&cmd, Some(10_000)).unwrap(); + p.exp_string("Create a revocation certificate for this key? (y/N)") + .unwrap(); + p.send_line("y").unwrap(); + p.exp_string("Your decision?").unwrap(); + p.send_line(&format!("{}", reason)).unwrap(); + p.exp_string(">").unwrap(); + p.send_line("").unwrap(); + p.exp_string("Is this okay? (y/N)").unwrap(); + p.send_line("y").unwrap(); + p.exp_eof().unwrap(); + + Ok(()) +} + +pub fn edit_expire(ctx: &Ctx, user_id: &str, expires: &str) -> Result<()> { + let homedir = + String::from(ctx.directory("homedir").unwrap().to_str().unwrap()); + + let cmd = format!("gpg --homedir {} --edit-key {}", homedir, user_id); + + let mut p = rexpect::spawn(&cmd, Some(10_000)).unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("expire").unwrap(); + p.exp_string("Key is valid for? (0)").unwrap(); + p.send_line(expires).unwrap(); + p.exp_string("Is this correct? (y/N)").unwrap(); + p.send_line("y").unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("quit").unwrap(); + p.exp_string("Save changes? (y/N)").unwrap(); + p.send_line("y").unwrap(); + p.exp_eof().unwrap(); + + Ok(()) +} + +pub fn create_user(ctx: &Ctx, user_id: &str) { + let mut gpg = Command::new("gpg") + .stdin(Stdio::piped()) + .arg("--homedir") + .arg(ctx.directory("homedir").unwrap()) + .arg("--quick-generate-key") + .arg("--batch") + .arg("--passphrase") + .arg("") + .arg(user_id.to_string()) + .spawn() + .expect("failed to start gpg"); + let status = gpg.wait().unwrap(); + assert!(status.success()); +} + +pub fn sign(ctx: &Ctx, user_id: &str) -> Result<()> { + let homedir = + String::from(ctx.directory("homedir").unwrap().to_str().unwrap()); + + let cmd = format!("gpg --homedir {} --edit-key {}", homedir, user_id); + + let mut p = rexpect::spawn(&cmd, Some(10_000)).unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("sign").unwrap(); + p.exp_string("Really sign? (y/N)").unwrap(); + p.send_line("y").unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("save").unwrap(); + p.exp_eof().unwrap(); + + Ok(()) +} + +pub fn tsign(ctx: &Ctx, user_id: &str, level: u8, trust: u8) -> Result<()> { + let homedir = + String::from(ctx.directory("homedir").unwrap().to_str().unwrap()); + + let cmd = format!("gpg --homedir {} --edit-key {}", homedir, user_id); + + let mut p = rexpect::spawn(&cmd, Some(10_000)).unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("tsign").unwrap(); + p.exp_string("Your selection?").unwrap(); + p.send_line(&format!("{}", trust)).unwrap(); + p.exp_string("Your selection?").unwrap(); + p.send_line(&format!("{}", level)).unwrap(); + p.exp_string("Your selection?").unwrap(); + p.send_line("").unwrap(); // domain + p.exp_string("Really sign? (y/N)").unwrap(); + p.send_line("y").unwrap(); + p.exp_string("gpg>").unwrap(); + p.send_line("quit").unwrap(); + p.exp_string("Save changes? (y/N)").unwrap(); + p.send_line("y").unwrap(); + p.exp_eof().unwrap(); + + Ok(()) +} diff --git a/tests/test_restd.rs b/openpgp-ca-restd/tests/test_restd.rs index d978df6..b2d456b 100644 --- a/tests/test_restd.rs +++ b/openpgp-ca-restd/tests/test_restd.rs @@ -6,12 +6,13 @@ // SPDX-FileCopyrightText: 2019-2020 Heiko Schaefer <heiko@schaefer.name> // SPDX-License-Identifier: GPL-3.0-or-later -use openpgp_ca_lib::ca::OpenpgpCa; -use openpgp_ca_lib::restd; -use openpgp_ca_lib::restd::client::Client; -use openpgp_ca_lib::restd::json::{ +use openpgp_ca_restd::client::Client; +use openpgp_ca_restd::json::{ Action, CertResultJson, CertStatus, Certificate, }; +use openpgp_ca_restd::restd; + +use openpgp_ca_lib::ca::OpenpgpCa; use rocket::futures::prelude::future::{AbortHandle, Abortable}; |