From 3b42f18dee8845bf2d930e7046b6e7c233e8f7bf Mon Sep 17 00:00:00 2001 From: Subhomoy Haldar Date: Tue, 23 Aug 2022 20:47:38 +0530 Subject: [PATCH] os: add input_password(prompt) and unit tests (#15507) --- .github/workflows/ci.yml | 10 +++++++ examples/password/correct.expect | 8 ++++++ examples/password/incorrect.expect | 8 ++++++ examples/password/password.v | 14 ++++++++++ examples/password/password_ci.vsh | 3 +++ vlib/os/password_nix.c.v | 42 ++++++++++++++++++++++++++++++ vlib/os/password_windows.c.v | 26 ++++++++++++++++++ 7 files changed, 111 insertions(+) create mode 100755 examples/password/correct.expect create mode 100755 examples/password/incorrect.expect create mode 100644 examples/password/password.v create mode 100644 examples/password/password_ci.vsh create mode 100644 vlib/os/password_nix.c.v create mode 100644 vlib/os/password_windows.c.v diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2efc705c..a241f876a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: ## The following is needed for examples/wkhtmltopdf.v wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb sudo apt-get install --quiet -y xfonts-75dpi xfonts-base + sudo apt-get install --quiet -y expect sudo dpkg -i wkhtmltox_0.12.6-1.focal_amd64.deb - name: Build v run: | @@ -92,6 +93,11 @@ jobs: ./v2 -o v3 -usecache cmd/v ./v3 version ./v3 -o tetris -usecache examples/tetris/tetris.v + - name: Test password input + run: | + ./v examples/password + ./v examples/password/password_ci.vsh + ubuntu-tcc-boehm-gc: runs-on: ubuntu-20.04 @@ -239,6 +245,10 @@ jobs: ./v2 -o v3 -usecache cmd/v ./v3 version ./v3 -o tetris -usecache examples/tetris/tetris.v + - name: Test password input + run: | + ./v examples/password + ./v examples/password/password_ci.vsh ubuntu: runs-on: ubuntu-20.04 diff --git a/examples/password/correct.expect b/examples/password/correct.expect new file mode 100755 index 000000000..1ab9c4706 --- /dev/null +++ b/examples/password/correct.expect @@ -0,0 +1,8 @@ +#!/usr/bin/expect +spawn ./password +expect "Enter your password : " +send "Sample\r" +expect "Confirm password : " +send "Sample\r" +expect "Password confirmed! You entered: Sample ." +expect eof diff --git a/examples/password/incorrect.expect b/examples/password/incorrect.expect new file mode 100755 index 000000000..61bf9e924 --- /dev/null +++ b/examples/password/incorrect.expect @@ -0,0 +1,8 @@ +#!/usr/bin/expect +spawn ./password +expect "Enter your password : " +send "Sample123\r" +expect "Confirm password : " +send "Sample234\r" +expect "Passwords do not match ." +expect eof diff --git a/examples/password/password.v b/examples/password/password.v new file mode 100644 index 000000000..32fd0d068 --- /dev/null +++ b/examples/password/password.v @@ -0,0 +1,14 @@ +module main + +import os + +fn main() { + original_password := os.input_password('Enter your password : ')! + repeated_password := os.input_password('Confirm password : ')! + + if original_password == repeated_password { + println('Password confirmed! You entered: $original_password .') + } else { + println('Passwords do not match .') + } +} diff --git a/examples/password/password_ci.vsh b/examples/password/password_ci.vsh new file mode 100644 index 000000000..a0df1e347 --- /dev/null +++ b/examples/password/password_ci.vsh @@ -0,0 +1,3 @@ +chdir('examples/password')? +assert execute('./correct.expect').exit_code == 0 +assert execute('./incorrect.expect').exit_code == 0 diff --git a/vlib/os/password_nix.c.v b/vlib/os/password_nix.c.v new file mode 100644 index 000000000..dc9bbfaf6 --- /dev/null +++ b/vlib/os/password_nix.c.v @@ -0,0 +1,42 @@ +module os + +#include + +pub struct C.termios { +mut: + c_iflag int + c_oflag int + c_cflag int + c_lflag int + c_cc [20]u8 +} + +fn C.tcgetattr(fd int, ptr &C.termios) int + +fn C.tcsetattr(fd int, action int, const_ptr &C.termios) + +// input_password prompts the user for a password-like secret. It disables +// the terminal echo during user input and resets it back to normal when done. +pub fn input_password(prompt string) !string { + if is_atty(1) <= 0 || getenv('TERM') == 'dumb' { + return error('Could not obtain password discretely.') + } + + old_state := C.termios{} + if unsafe { C.tcgetattr(0, &old_state) } != 0 { + return last_error() + } + defer { + unsafe { C.tcsetattr(0, C.TCSANOW, &old_state) } + println('') + } + + mut new_state := old_state + + new_state.c_lflag &= int(~u32(C.ECHO)) // Disable echoing of characters + unsafe { C.tcsetattr(0, C.TCSANOW, &new_state) } + + password := input_opt(prompt) or { return error('Failed to read password') } + + return password +} diff --git a/vlib/os/password_windows.c.v b/vlib/os/password_windows.c.v new file mode 100644 index 000000000..273c444bc --- /dev/null +++ b/vlib/os/password_windows.c.v @@ -0,0 +1,26 @@ +module os + +#include + +// input_password prompts the user for a password-like secret. It disables +// the terminal echo during user input and resets it back to normal when done. +pub fn input_password(prompt string) !string { + if is_atty(1) <= 0 || getenv('TERM') == 'dumb' { + return error('Could not obtain password discretely.') + } + + std_handle := C.GetStdHandle(C.STD_INPUT_HANDLE) + mut mode := u32(0) + + unsafe { C.GetConsoleMode(std_handle, &mode) } + unsafe { C.SetConsoleMode(std_handle, mode & (~u32(C.ENABLE_ECHO_INPUT))) } + + defer { + unsafe { C.SetConsoleMode(std_handle, &mode) } + println('') + } + + password := input_opt(prompt) or { return error('Failed to read password') } + + return password +} -- 2.30.2