feat: password obfuscation

This commit is contained in:
Arnaud Locquet 2020-10-26 18:26:21 +01:00 committed by Stephan Dilly
parent 35f3a25cba
commit a75f43de77
3 changed files with 47 additions and 9 deletions

View file

@ -4,7 +4,7 @@ use tui::{backend::Backend, layout::Rect, Frame};
use asyncgit::sync::cred::BasicAuthCredential;
use crate::components::TextInputComponent;
use crate::components::{InputType, TextInputComponent};
use crate::{
components::{
visibility_blocking, CommandBlocking, CommandInfo, Component,
@ -37,13 +37,15 @@ impl CredComponent {
key_config.clone(),
&strings::username_popup_title(&key_config),
&strings::username_popup_msg(&key_config),
),
)
.with_input_type(InputType::Singleline),
input_password: TextInputComponent::new(
theme,
key_config.clone(),
&strings::password_popup_title(&key_config),
&strings::password_popup_msg(&key_config),
),
)
.with_input_type(InputType::Password),
key_config,
cred: BasicAuthCredential::new(None, None),
}

View file

@ -41,7 +41,7 @@ pub use reset::ResetComponent;
pub use select_branch::SelectBranchComponent;
pub use stashmsg::StashMsgComponent;
pub use tag_commit::TagCommitComponent;
pub use textinput::TextInputComponent;
pub use textinput::{InputType, TextInputComponent};
pub use utils::filetree::FileTreeItemKind;
use crate::ui::style::Theme;

View file

@ -9,11 +9,20 @@ use crate::{
};
use anyhow::Result;
use crossterm::event::{Event, KeyCode, KeyModifiers};
use itertools::Itertools;
use std::ops::Range;
use tui::{
backend::Backend, layout::Rect, style::Modifier, text::Span,
widgets::Clear, Frame,
};
#[derive(PartialEq)]
pub enum InputType {
Singleline,
Multiline,
Password,
}
/// primarily a subcomponet for user input of text (used in `CommitComponent`)
pub struct TextInputComponent {
title: String,
@ -23,6 +32,7 @@ pub struct TextInputComponent {
theme: SharedTheme,
key_config: SharedKeyConfig,
cursor_position: usize,
input_type: InputType,
}
impl TextInputComponent {
@ -41,9 +51,18 @@ impl TextInputComponent {
title: title.to_string(),
default_msg: default_msg.to_string(),
cursor_position: 0,
input_type: InputType::Multiline,
}
}
pub const fn with_input_type(
mut self,
input_type: InputType,
) -> Self {
self.input_type = input_type;
self
}
/// Clear the `msg`.
pub fn clear(&mut self) {
self.msg.clear();
@ -113,7 +132,7 @@ impl TextInputComponent {
// if the cursor is not at the first character.
if self.cursor_position > 0 {
txt.push(Span::styled(
&self.msg[..self.cursor_position],
self.get_msg(0..self.cursor_position),
style,
));
}
@ -122,7 +141,9 @@ impl TextInputComponent {
.next_char_position()
// if the cursor is at the end of the msg
// a whitespace is used to underline
.map_or(" ", |pos| &self.msg[self.cursor_position..pos]);
.map_or(" ".to_owned(), |pos| {
self.get_msg(self.cursor_position..pos)
});
if cursor_str == "\n" {
txt.push(Span::styled(
@ -142,12 +163,22 @@ impl TextInputComponent {
// still remaining characters.
if let Some(pos) = self.next_char_position() {
if pos < self.msg.len() {
txt.push(Span::styled(&self.msg[pos..], style));
txt.push(Span::styled(
self.get_msg(pos..self.msg.len()),
style,
));
}
}
txt
}
fn get_msg(&self, range: Range<usize>) -> String {
match self.input_type {
InputType::Password => range.map(|_| "*").join(""),
_ => self.msg[range].to_owned(),
}
}
}
impl DrawableComponent for TextInputComponent {
@ -166,8 +197,13 @@ impl DrawableComponent for TextInputComponent {
self.get_draw_text()
};
let area = ui::centered_rect(60, 20, f.size());
let area = ui::rect_min(10, 3, area);
let area = match self.input_type {
InputType::Multiline => {
let area = ui::centered_rect(60, 20, f.size());
ui::rect_min(10, 3, area)
}
_ => ui::centered_rect_absolute(32, 3, f.size()),
};
f.render_widget(Clear, area);
f.render_widget(