1use crate::database;
4use crate::graphql::{self, DateTime};
5use crate::keys::*;
6use crate::util;
7
8use graphql_client::*;
9use prettytable::{format, Table};
10use rusqlite::Connection;
11use std::path::PathBuf;
12use structopt::StructOpt;
13
14#[derive(StructOpt, Debug)]
15pub enum KeyOpt {
16 #[structopt(name = "list")]
17 List(List),
19
20 #[structopt(name = "register")]
21 Register(Register),
23
24 #[structopt(name = "import")]
25 Import(Import),
27
28 #[structopt(name = "delete")]
29 Delete(Delete),
31
32 #[structopt(name = "generate")]
33 Generate(Generate),
35}
36
37#[derive(StructOpt, Debug)]
39pub struct List {
40 #[structopt(long = "all", short = "a")]
41 all: bool,
43}
44
45#[derive(StructOpt, Debug)]
47pub struct Register {
48 #[structopt(long = "public")]
50 pub public_key_location: String,
51
52 #[structopt(long = "private")]
54 pub private_key_location: String,
55}
56
57#[derive(GraphQLQuery)]
58#[graphql(
59 schema_path = "graphql/schema.graphql",
60 query_path = "graphql/queries/publish_public_key.graphql",
61 response_derives = "Debug"
62)]
63struct PublishPublicKeyMutation;
64
65#[derive(StructOpt, Debug)]
67pub struct Delete {
68 public_key_id: String,
70}
71
72#[derive(StructOpt, Debug)]
74pub struct Generate {
75 key_path: PathBuf,
77
78 #[structopt(long = "force", short = "f")]
79 force: bool,
81}
82
83#[derive(StructOpt, Debug)]
85pub struct Import {
86 #[structopt(long = "user-name")]
87 user_name: String,
88 public_key_value: String,
89}
90
91fn add_key_pair_from_fs_to_database(
92 key_db: &mut Connection,
93 public_key_location: String,
94 private_key_location: String,
95) -> anyhow::Result<()> {
96 let (pk_id, pk_v, tx) =
97 add_personal_key_pair_to_database(key_db, public_key_location, private_key_location)?;
98 let q = PublishPublicKeyMutation::build_query(publish_public_key_mutation::Variables {
99 key_id: pk_id,
100 key: pk_v,
101 verifying_signature_id: None,
102 });
103 let response_or_err: Result<publish_public_key_mutation::ResponseData, _> =
104 graphql::execute_query(&q);
105 match response_or_err {
106 Ok(_) => {
107 tx.commit().map_err(|e| {
108 anyhow!(
109 "Failed to store key pair in local database: {}",
110 e.to_string()
111 )
112 })?;
113 println!("Key pair successfully added!")
114 }
115 Err(e) => {
116 error!("Failed to upload public key to server: {}", e);
117 #[cfg(feature = "telemetry")]
118 sentry::integrations::anyhow::capture_anyhow(&e);
119 }
120 };
121 Ok(())
122}
123
124pub fn keys(options: KeyOpt) -> anyhow::Result<()> {
125 let mut key_db = database::open_db()?;
126 match options {
127 KeyOpt::List(List { all }) => {
128 let keys = get_personal_keys_from_database(&key_db)?;
130 if all {
131 let wapm_public_keys = get_wapm_public_keys_from_database(&key_db)?;
132 match (keys.is_empty(), wapm_public_keys.is_empty()) {
133 (true, true) => println!("No keys found"),
134 (true, false) => {
135 println!("{}", create_wapm_public_key_table(wapm_public_keys)?);
136 }
137 (false, true) => {
138 println!("{}", create_personal_key_table(keys)?);
139 }
140 (false, false) => {
141 println!("PERSONAL KEYS:\n{}", create_personal_key_table(keys)?);
142 println!(
143 "\nWAPM PUBLIC KEYS:\n{}",
144 create_wapm_public_key_table(wapm_public_keys)?
145 );
146 }
147 }
148 } else if keys.is_empty() {
149 println!("No personal keys found");
150 } else {
151 println!("{}", create_personal_key_table(keys)?);
152 }
153 }
154 KeyOpt::Register(Register {
155 public_key_location,
156 private_key_location,
157 }) => {
158 add_key_pair_from_fs_to_database(
159 &mut key_db,
160 public_key_location,
161 private_key_location,
162 )?;
163 }
164 KeyOpt::Delete(Delete { public_key_id }) => {
165 let full_public_key = get_full_personal_public_key_by_id(&key_db, public_key_id)?;
166 warn!(
167 "You are about to delete the key pair associated with {:?} from wapm.\nThis cannot be undone.",
168 &full_public_key
169 );
170 let user_confirmed_key_deletion = util::prompt_user_for_yes(
171 "Please confirm that you want to permanently delete this key pair from wapm:",
172 )?;
173 if user_confirmed_key_deletion {
174 delete_key_pair(&mut key_db, full_public_key)?;
175 } else {
176 println!("Aborting");
177 }
178 }
179 KeyOpt::Import(Import {
180 user_name,
181 public_key_value,
182 }) => {
183 let user_name = user_name.trim().to_string();
184 let (pk_id, pkv) = normalize_public_key(public_key_value)?;
185 import_public_key(&mut key_db, &pk_id, &pkv, user_name)?;
186 }
187 KeyOpt::Generate(Generate { key_path, force }) => {
188 let private_key_path = key_path.join("minisign.key");
189 let public_key_path = key_path.join("minisign.pub");
190
191 if !key_path.exists() {
192 return Err(anyhow!(
193 "Path {} does not exist!",
194 &key_path.as_os_str().to_string_lossy()
195 ));
196 }
197 if !force {
198 if private_key_path.exists() {
199 return Err(anyhow!(
200 "Private key file, {}, exists",
201 &private_key_path.as_os_str().to_string_lossy()
202 ));
203 }
204
205 if public_key_path.exists() {
206 return Err(anyhow!(
207 "Public key file, {}, exists",
208 &public_key_path.as_os_str().to_string_lossy()
209 ));
210 }
211 }
212
213 let private_key_file = std::fs::File::create(&private_key_path)?;
214 let public_key_file = std::fs::File::create(&public_key_path)?;
215
216 info!("Generating key pair!");
217
218 let keypair = minisign::KeyPair::generate_and_write_encrypted_keypair(
219 public_key_file,
220 private_key_file,
221 None,
222 None,
224 )?;
225
226 info!(
227 "Key pair successfully generated! Public key is: {}",
228 keypair.pk.to_base64()
229 );
230
231 debug!("Adding key pair to database");
232
233 add_key_pair_from_fs_to_database(
234 &mut key_db,
235 public_key_path.to_string_lossy().to_string(),
236 private_key_path.to_string_lossy().to_string(),
237 )?;
238 }
239 }
240
241 Ok(())
242}
243
244pub fn create_personal_key_table(keys: Vec<PersonalKey>) -> anyhow::Result<String> {
245 let mut table = Table::new();
246 table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
247 table.add_row(row![
248 "TAG",
249 "ACTIVE",
250 "KEY",
251 "PRIVATE KEY LOCATION",
252 "DATE ADDED"
253 ]);
254 for key in keys {
255 table.add_row(row![
256 key.public_key_id,
257 key.active,
258 key.public_key_value,
259 key.private_key_location
260 .unwrap_or_else(|| "None".to_string()),
261 time::strftime("%Y-%m-%d", &time::at(key.date_created))?
262 ]);
263 }
264 Ok(format!("{}", table))
265}
266
267pub fn create_wapm_public_key_table(keys: Vec<WapmPublicKey>) -> anyhow::Result<String> {
268 let mut table = Table::new();
269 table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
270 table.add_row(row!["USER", "TAG", "KEY", "DATE ADDED"]);
271 for key in keys {
272 table.add_row(row![
273 key.user_name,
274 key.public_key_id,
275 key.public_key_value,
276 time::strftime("%Y-%m-%d", &time::at(key.date_created))?
277 ]);
278 }
279 Ok(format!("{}", table))
280}