A Go password manager for the terminal
Two Widgets
The terminal UI, like in the previous Snapshot columns, uses the termui package from GitHub. Listing 5 calls ui.Init()
in line 12 to initialize its functions and calls ui.Close()
to acknowledge a user abort in the defer
statement in line 15. This neatly folds up the UI to leave a usable terminal for the shell.
Listing 5
ui.go
01 package main 02 import ( 03 "fmt" 04 ui "github.com/gizak/termui/v3" 05 "github.com/gizak/termui/v3/widgets" 06 ) 07 func runUI(lines []string) { 08 rows := []string{} 09 for _, line := range lines { 10 rows = append(rows, mask(line)) 11 } 12 if err := ui.Init(); err != nil { 13 panic(err) 14 } 15 defer ui.Close() 16 lb := widgets.NewList() 17 lb.Rows = rows 18 lb.SelectedRow = 0 19 lb.SelectedRowStyle = ui.NewStyle(ui.ColorBlack) 20 lb.TextStyle.Fg = ui.ColorGreen 21 lb.Title = fmt.Sprintf("passview 1.0") 22 pa := widgets.NewParagraph() 23 pa.Text = "[Q]uit [Enter]reveal" 24 pa.TextStyle.Fg = ui.ColorBlack 25 w, h := ui.TerminalDimensions() 26 lb.SetRect(0, 0, w, h-3) 27 pa.SetRect(0, h-3, w, h) 28 ui.Render(lb, pa) 29 uiEvents := ui.PollEvents() 30 for { 31 select { 32 case e := <-uiEvents: 33 switch e.ID { 34 case "k": 35 hideCur(lb) 36 lb.ScrollUp() 37 ui.Render(lb) 38 case "j": 39 hideCur(lb) 40 lb.ScrollDown() 41 ui.Render(lb) 42 case "q", "<C-c>": 43 return 44 case "<Enter>": 45 showCur(lb, lines) 46 ui.Render(lb) 47 } 48 } 49 } 50 } 51 func hideCur(lb *widgets.List) { 52 idx := lb.SelectedRow 53 lb.Rows[idx] = mask(lb.Rows[idx]) 54 } 55 func showCur(lb *widgets.List, lines []string) { 56 idx := lb.SelectedRow 57 lb.Rows[idx] = lines[idx] 58 }
The terminal UI shown in Figure 1 consists of two stacked widgets: On top, there is a listbox with the password entries, which the user can scroll through. It also supports leafing through multiple pages if the list of entries is longer than the maximum number of lines displayed. Below the listbox, at the bottom of the terminal window, a paragraph widget indicates which keys the user can press next: Enter reveals the selected password, while Q exits the program.
To allow the UI to take advantage of the entire geometry of the terminal window, line 25 queries the window dimensions, using the TerminalsDimensions()
helper function from the termui package. From the width and height of the window, lines 26 and 27 then determine the position and dimensions of the two stacked widgets. In this case, the paragraph widget is assigned the bottom three lines, while the listbox on top gets everything else. Horizontally, both widgets extend to the edges of the terminal window.
The listbox entries reside in the Rows
attribute of the listbox as an array slice of strings. Line 17 populates this array with the rows
array slice. Before this happened, the for
loop starting in line 9 stuffed all masked entries into rows
but kept the original lines in lines
. The two array slices for masked and unmasked entries make it easy to later reveal masked entries: The code only needs to look at the same index number in the original slice to reveal the unmasked content.
After line 28 has drawn the widgets on screen, line 29 fires off the PollEvents()
Go routine, which will intercept all of the user's keystrokes concurrently from now on and send them to the uiEvents
channel. From there, the program fetches events via the select
statement in the infinite for
loop starting in line 30 and immediately responds to all incoming keystrokes. If the user presses K to scroll up, line 35 uses hideCur()
(starting in line 51) and the mask()
function to hide a password that may have been previously revealed in the current listbox entry. Then, ScrollUp()
(line 36) tells the listbox to scroll up one item, and the subsequent Render
command smoothly displays the change in the UI. The same applies to pressing J, which lets the user scroll down through the list of entries.
Line 44 intercepts presses of the Enter key and calls the showCur()
function defined in line 55. The function fetches the original unmasked password entry from the lines
list and replaces the currently selected line of the listbox with it. And, hey presto, account and password are displayed on the screen in clear text. hideCur()
starting in line 51 does the opposite and hides the current entry using the mask()
function when the user moves away.
Installation
As always, the binary can be generated from the Go code using the typical three-step process (Listing 6). This process fetches all the dependent libraries from GitHub, compiles them, and binds everything together to create the finished pv
binary. You can then copy this to any target computer with a similar architecture. It'll run there without complaints, and it also conveniently even conjures up the UI into the terminal on remote machines. You will want to copy the test.age
password file to a file in your home directory for production operation; the password reminder is then ready for use.
Listing 6
Compiling the Program
$ go mod init pv $ go mod tidy $ go build pv.go crypto.go util.go ui.go
Infos
- Age: https://github.com/FiloSottile/age
- "What did Ken Thompson mean when he said, 'I'd spell creat with an "e".'?": https://unix.stackexchange.com/questions/10893/what-did-ken-thompson-mean-when-he-vsaid-id-spell-creat-with-an-e
« Previous 1 2 3
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Fedora Asahi 40 Remix Available for Macs with Apple Silicon
If you've been anticipating KDE's Plasma 6 for your Apple Silicon-powered Mac, then you're in luck.
-
Red Hat Adds New Deployment Option for Enterprise Linux Platforms
Red Hat has re-imagined enterprise Linux for an AI future with Image Mode.
-
OSJH and LPI Release 2024 Open Source Pros Job Survey Results
See what open source professionals look for in a new role.
-
Proton 9.0-1 Released to Improve Gaming with Steam
The latest release of Proton 9 adds several improvements and fixes an issue that has been problematic for Linux users.
-
So Long Neofetch and Thanks for the Info
Today is a day that every Linux user who enjoys bragging about their system(s) will mourn, as Neofetch has come to an end.
-
Ubuntu 24.04 Comes with a “Flaw"
If you're thinking you might want to upgrade from your current Ubuntu release to the latest, there's something you might want to consider before doing so.
-
Canonical Releases Ubuntu 24.04
After a brief pause because of the XZ vulnerability, Ubuntu 24.04 is now available for install.
-
Linux Servers Targeted by Akira Ransomware
A group of bad actors who have already extorted $42 million have their sights set on the Linux platform.
-
TUXEDO Computers Unveils Linux Laptop Featuring AMD Ryzen CPU
This latest release is the first laptop to include the new CPU from Ryzen and Linux preinstalled.
-
XZ Gets the All-Clear
The back door xz vulnerability has been officially reverted for Fedora 40 and versions 38 and 39 were never affected.