mirror of
				https://github.com/pcvolkmer/cert-tools.git
				synced 2025-10-30 20:46:13 +00:00 
			
		
		
		
	feat: graphical cert list
This commit is contained in:
		
							
								
								
									
										118
									
								
								ui/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								ui/src/main.rs
									
									
									
									
									
								
							| @@ -4,9 +4,11 @@ use cert_tools::{Chain, PrivateKey}; | |||||||
| use iced::widget::text_editor::{default, Content, Status}; | use iced::widget::text_editor::{default, Content, Status}; | ||||||
| use iced::widget::{ | use iced::widget::{ | ||||||
|     button, column, container, horizontal_rule, horizontal_space, row, text, text_editor, |     button, column, container, horizontal_rule, horizontal_space, row, text, text_editor, | ||||||
|     Scrollable, |     Container, Scrollable, | ||||||
|  | }; | ||||||
|  | use iced::{ | ||||||
|  |     application, clipboard, Background, Border, Color, Element, Font, Length, Shadow, Size, Task, | ||||||
| }; | }; | ||||||
| use iced::{application, clipboard, Background, Color, Element, Font, Length, Size, Task}; |  | ||||||
| use itertools::Itertools; | use itertools::Itertools; | ||||||
| use std::cmp::Ordering; | use std::cmp::Ordering; | ||||||
| use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||||
| @@ -19,11 +21,18 @@ fn main() -> iced::Result { | |||||||
|         .run_with(Ui::new) |         .run_with(Ui::new) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | enum UiMode { | ||||||
|  |     CertList, | ||||||
|  |     Output | ||||||
|  | } | ||||||
|  |  | ||||||
| struct Ui { | struct Ui { | ||||||
|     cert_file: Option<PathBuf>, |     cert_file: Option<PathBuf>, | ||||||
|     ca_file: Option<PathBuf>, |     ca_file: Option<PathBuf>, | ||||||
|     key_file: Option<PathBuf>, |     key_file: Option<PathBuf>, | ||||||
|  |  | ||||||
|  |     mode: UiMode, | ||||||
|  |     chain: Option<Chain>, | ||||||
|     output: Content, |     output: Content, | ||||||
|     status: String, |     status: String, | ||||||
| } | } | ||||||
| @@ -35,6 +44,8 @@ impl Ui { | |||||||
|                 cert_file: None, |                 cert_file: None, | ||||||
|                 ca_file: None, |                 ca_file: None, | ||||||
|                 key_file: None, |                 key_file: None, | ||||||
|  |                 mode: UiMode::Output, | ||||||
|  |                 chain: None, | ||||||
|                 output: Content::default(), |                 output: Content::default(), | ||||||
|                 status: String::new(), |                 status: String::new(), | ||||||
|             }, |             }, | ||||||
| @@ -47,17 +58,20 @@ impl Ui { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn update(&mut self, message: Message) -> Task<Message> { |     fn update(&mut self, message: Message) -> Task<Message> { | ||||||
|  |         self.mode = UiMode::Output; | ||||||
|         match message { |         match message { | ||||||
|             Message::PickCertFile => Task::perform(pick_file(), Message::SetCertFile), |             Message::PickCertFile => Task::perform(pick_file(), Message::SetCertFile), | ||||||
|             Message::PickCaFile => Task::perform(pick_file(), Message::SetCaFile), |             Message::PickCaFile => Task::perform(pick_file(), Message::SetCaFile), | ||||||
|             Message::PickKeyFile => Task::perform(pick_file(), Message::SetKeyFile), |             Message::PickKeyFile => Task::perform(pick_file(), Message::SetKeyFile), | ||||||
|             Message::ClearCertFile => { |             Message::ClearCertFile => { | ||||||
|                 self.cert_file = None; |                 self.cert_file = None; | ||||||
|  |                 self.chain = None; | ||||||
|                 self.output = Content::default(); |                 self.output = Content::default(); | ||||||
|                 Task::none() |                 Task::none() | ||||||
|             } |             } | ||||||
|             Message::ClearCaFile => { |             Message::ClearCaFile => { | ||||||
|                 self.ca_file = None; |                 self.ca_file = None; | ||||||
|  |                 self.chain = None; | ||||||
|                 self.output = Content::default(); |                 self.output = Content::default(); | ||||||
|                 Task::none() |                 Task::none() | ||||||
|             } |             } | ||||||
| @@ -70,8 +84,13 @@ impl Ui { | |||||||
|                 match file { |                 match file { | ||||||
|                     Ok(file) => { |                     Ok(file) => { | ||||||
|                         self.cert_file = Some(file); |                         self.cert_file = Some(file); | ||||||
|  |                         self.chain = match self.load_chain() { | ||||||
|  |                             Ok(chain) => Some(chain), | ||||||
|  |                             _ => None | ||||||
|  |                         }; | ||||||
|                         self.output = Content::default(); |                         self.output = Content::default(); | ||||||
|                     }, |                         self.mode = UiMode::CertList; | ||||||
|  |                     } | ||||||
|                     _ => self.cert_file = None, |                     _ => self.cert_file = None, | ||||||
|                 }; |                 }; | ||||||
|                 Task::none() |                 Task::none() | ||||||
| @@ -80,8 +99,13 @@ impl Ui { | |||||||
|                 match file { |                 match file { | ||||||
|                     Ok(file) => { |                     Ok(file) => { | ||||||
|                         self.ca_file = Some(file); |                         self.ca_file = Some(file); | ||||||
|  |                         self.chain = match self.load_chain() { | ||||||
|  |                             Ok(chain) => Some(chain), | ||||||
|  |                             _ => None | ||||||
|  |                         }; | ||||||
|                         self.output = Content::default(); |                         self.output = Content::default(); | ||||||
|                     }, |                         self.mode = UiMode::CertList; | ||||||
|  |                     } | ||||||
|                     _ => self.ca_file = None, |                     _ => self.ca_file = None, | ||||||
|                 }; |                 }; | ||||||
|                 Task::none() |                 Task::none() | ||||||
| @@ -91,7 +115,8 @@ impl Ui { | |||||||
|                     Ok(file) => { |                     Ok(file) => { | ||||||
|                         self.key_file = Some(file); |                         self.key_file = Some(file); | ||||||
|                         self.output = Content::default(); |                         self.output = Content::default(); | ||||||
|                     }, |                         self.mode = UiMode::CertList; | ||||||
|  |                     } | ||||||
|                     _ => self.key_file = None, |                     _ => self.key_file = None, | ||||||
|                 }; |                 }; | ||||||
|                 Task::none() |                 Task::none() | ||||||
| @@ -101,6 +126,7 @@ impl Ui { | |||||||
|                     Ok(output) => { |                     Ok(output) => { | ||||||
|                         self.output = Content::with_text(output.as_str()); |                         self.output = Content::with_text(output.as_str()); | ||||||
|                         self.status = String::new(); |                         self.status = String::new(); | ||||||
|  |                         self.mode = UiMode::CertList; | ||||||
|                     } |                     } | ||||||
|                     Err(err) => { |                     Err(err) => { | ||||||
|                         self.output = Content::default(); |                         self.output = Content::default(); | ||||||
| @@ -122,7 +148,7 @@ impl Ui { | |||||||
|                 }; |                 }; | ||||||
|                 Task::none() |                 Task::none() | ||||||
|             } |             } | ||||||
|             Message::Copy => clipboard::write::<Message>(self.output.text()), |             Message::Copy => clipboard::write::<Message>(self.output.text()) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -252,11 +278,60 @@ impl Ui { | |||||||
|                         value: Color::WHITE, |                         value: Color::WHITE, | ||||||
|                         ..default(theme, Status::Disabled) |                         ..default(theme, Status::Disabled) | ||||||
|                     }) |                     }) | ||||||
|                     .font(Font::MONOSPACE) |                     .font(Font::MONOSPACE), | ||||||
|             ) |             ) | ||||||
|             .height(Length::Fill) |             .height(Length::Fill) | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |         let certs = { | ||||||
|  |             let mut result = column![]; | ||||||
|  |              | ||||||
|  |             if let Some(chain) = &self.chain { | ||||||
|  |                 for cert in chain.certs() { | ||||||
|  |                     result = | ||||||
|  |                         result.push( | ||||||
|  |                             Container::new(column![ | ||||||
|  |                         text(cert.name().to_string()).size(18), | ||||||
|  |                         row![ | ||||||
|  |                             text("Name: ").width(200), | ||||||
|  |                             text(cert.name().to_string()) | ||||||
|  |                         ], | ||||||
|  |                         row![ | ||||||
|  |                             text("Issuer: ").width(200), | ||||||
|  |                             text(cert.issuer().to_string()) | ||||||
|  |                         ], | ||||||
|  |                         row![ | ||||||
|  |                             text("SHA-1-Fingerprint: ").width(200), | ||||||
|  |                             text(cert.fingerprint().sha1.to_string()) | ||||||
|  |                         ], | ||||||
|  |                         row![ | ||||||
|  |                             text("SHA-256-Fingerprint: ").width(200), | ||||||
|  |                             text(cert.fingerprint().sha256.to_string()) | ||||||
|  |                         ], | ||||||
|  |                         row![ | ||||||
|  |                             text("Subject-Key-Id: ").width(200), | ||||||
|  |                             text(cert.subject_key_id().to_string()) | ||||||
|  |                         ], | ||||||
|  |                         row![ | ||||||
|  |                             text("Authority_Key-Id: ").width(200), | ||||||
|  |                             text(cert.authority_key_id().to_string()) | ||||||
|  |                         ], | ||||||
|  |                     ]) | ||||||
|  |                                 .padding(4) | ||||||
|  |                                 .style(|t| container::Style { | ||||||
|  |                                     border: Border::default().width(1), | ||||||
|  |                                     background: Some(Background::Color(Color::parse("#eee").unwrap())), | ||||||
|  |                                     ..container::Style::default() | ||||||
|  |                                 }) | ||||||
|  |                                 .width(Length::Fill), | ||||||
|  |                         ) | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |              | ||||||
|  |             let content = result.spacing(2); | ||||||
|  |             Scrollable::new(content).height(Length::Fill) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         let indicator = { |         let indicator = { | ||||||
|             let content = match self.indicator_state() { |             let content = match self.indicator_state() { | ||||||
|                 IndicatorState::Unknown => ("?", "#aaaaaa", "#ffffff"), |                 IndicatorState::Unknown => ("?", "#aaaaaa", "#ffffff"), | ||||||
| @@ -286,7 +361,10 @@ impl Ui { | |||||||
|             .spacing(96), |             .spacing(96), | ||||||
|             horizontal_rule(1), |             horizontal_rule(1), | ||||||
|             buttons, |             buttons, | ||||||
|             output, |             match self.mode { | ||||||
|  |                 UiMode::CertList => certs, | ||||||
|  |                 UiMode::Output => output | ||||||
|  |             }, | ||||||
|             horizontal_rule(1), |             horizontal_rule(1), | ||||||
|             text(&self.status) |             text(&self.status) | ||||||
|         ] |         ] | ||||||
| @@ -420,6 +498,28 @@ Authority-Key-Id:    {} | |||||||
|         Ok(result) |         Ok(result) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     fn load_chain(&self) -> Result<Chain, String> { | ||||||
|  |         if let Some(cert_file) = &self.cert_file { | ||||||
|  |             let chain = Chain::read(cert_file); | ||||||
|  |  | ||||||
|  |             if let Ok(mut chain) = chain { | ||||||
|  |                 if let Some(ca_file) = &self.ca_file { | ||||||
|  |                     if let Ok(ca_chain) = Chain::read(ca_file) { | ||||||
|  |                         for ca_cert in ca_chain.into_vec() { | ||||||
|  |                             chain.push(ca_cert); | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         return Err("Cannot read CA file".to_string()); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return Ok(chain); | ||||||
|  |             } else { | ||||||
|  |                 return Err("Cannot read Certificate file".to_string()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Ok(Chain::from(vec![])) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     fn indicator_state(&self) -> IndicatorState { |     fn indicator_state(&self) -> IndicatorState { | ||||||
|         let mut result = IndicatorState::Unknown; |         let mut result = IndicatorState::Unknown; | ||||||
|  |  | ||||||
| @@ -482,7 +582,7 @@ enum Message { | |||||||
|     SetKeyFile(Result<PathBuf, Error>), |     SetKeyFile(Result<PathBuf, Error>), | ||||||
|     Print, |     Print, | ||||||
|     Merge, |     Merge, | ||||||
|     Copy, |     Copy | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user