mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-09-05 16:47:20 -04:00
Implement initial TOTP support (secret in third notes line, will eventually have its own dedicated field)
This commit is contained in:
@@ -3,11 +3,13 @@ module github.com/rwinkhart/MUTN
|
|||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/pquerna/otp v1.4.1-0.20231130234153-3357de7c0481
|
||||||
golang.org/x/crypto v0.20.0
|
golang.org/x/crypto v0.20.0
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/stretchr/testify v1.8.4 // indirect
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
golang.org/x/sys v0.17.0 // indirect
|
||||||
golang.org/x/term v0.17.0 // indirect
|
golang.org/x/term v0.17.0 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pquerna/otp v1.4.1-0.20231130234153-3357de7c0481 h1:FkxbO331O7mS5EJkP+MCi0o2gswh/Aezs+//NmefrR8=
|
||||||
|
github.com/pquerna/otp v1.4.1-0.20231130234153-3357de7c0481/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ func main() {
|
|||||||
field = 2
|
field = 2
|
||||||
case "note", "-n":
|
case "note", "-n":
|
||||||
field = 3
|
field = 3
|
||||||
|
case "totp", "-t":
|
||||||
|
fmt.Println("TOTP code copied to clipboard - until this process is closed, your clipboard will be kept up to date with the current TOTP code")
|
||||||
|
field = 5 // TODO Update field after removed from notes (breaking sshyp entry compatibility)
|
||||||
default:
|
default:
|
||||||
cli.HelpCopy()
|
cli.HelpCopy()
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-7
@@ -3,9 +3,12 @@ package offline
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CopyArgument copies a field from an entry to the clipboard
|
||||||
func CopyArgument(targetLocation string, field int, executableName string) {
|
func CopyArgument(targetLocation string, field int, executableName string) {
|
||||||
if isFile, _ := TargetIsFile(targetLocation, true, 2); isFile {
|
if isFile, _ := TargetIsFile(targetLocation, true, 2); isFile {
|
||||||
|
|
||||||
@@ -14,23 +17,43 @@ func CopyArgument(targetLocation string, field int, executableName string) {
|
|||||||
|
|
||||||
// ensure field exists in entry
|
// ensure field exists in entry
|
||||||
if len(decryptedEntry) > field {
|
if len(decryptedEntry) > field {
|
||||||
copySubject = decryptedEntry[field]
|
|
||||||
|
// ensure field is not empty
|
||||||
|
if decryptedEntry[field] == "" {
|
||||||
|
fmt.Println(AnsiError + "Field is empty" + AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field != 5 { // TODO Update field after removed from notes (breaking sshyp entry compatibility)
|
||||||
|
copySubject = decryptedEntry[field]
|
||||||
|
} else { // TOTP mode
|
||||||
|
var err error
|
||||||
|
var currentSecond int // track current second for TOTP code refresh
|
||||||
|
var copied bool // track whether the TOTP code has been copied to the clipboard for the first time
|
||||||
|
for { // keep field copied to clipboard, refresh on 30-second intervals
|
||||||
|
currentSecond = time.Now().Second()
|
||||||
|
if currentSecond == 0 || currentSecond == 30 || !copied {
|
||||||
|
copySubject, err = totp.GenerateCode(decryptedEntry[5], time.Now())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(AnsiError + "Error generating TOTP code" + AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
copyField(copySubject, "")
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(AnsiError + "Field does not exist in entry" + AnsiReset)
|
fmt.Println(AnsiError + "Field does not exist in entry" + AnsiReset)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure field is not blank
|
|
||||||
if copySubject == "" {
|
|
||||||
fmt.Println(AnsiError + "Field is empty" + AnsiReset)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy field to clipboard, launch clipboard clearing process
|
// copy field to clipboard, launch clipboard clearing process
|
||||||
copyField(copySubject, executableName)
|
copyField(copySubject, executableName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClipClearArgument is called to clear the clipboard after 30 seconds if the contents have not been modified
|
||||||
func ClipClearArgument() {
|
func ClipClearArgument() {
|
||||||
// read previous clipboard contents from stdin
|
// read previous clipboard contents from stdin
|
||||||
clipScanner := bufio.NewScanner(os.Stdin)
|
clipScanner := bufio.NewScanner(os.Stdin)
|
||||||
|
|||||||
+12
-10
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// TODO MacOS support is entirely untested - I would appreciate feedback on this implementation
|
// TODO MacOS support is entirely untested - I would appreciate feedback on this implementation
|
||||||
|
|
||||||
// CopyField copies a field from an entry to the clipboard
|
// copyField copies a field from an entry to the clipboard
|
||||||
func copyField(copySubject string, executableName string) {
|
func copyField(copySubject string, executableName string) {
|
||||||
cmd := exec.Command("pbcopy")
|
cmd := exec.Command("pbcopy")
|
||||||
writeToStdin(cmd, copySubject)
|
writeToStdin(cmd, copySubject)
|
||||||
@@ -22,18 +22,20 @@ func copyField(copySubject string, executableName string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = exec.Command(executableName, "clipclear")
|
// launch clipboard clearing process if executableName is provided
|
||||||
writeToStdin(cmd, copySubject)
|
if executableName != "" {
|
||||||
err = cmd.Start()
|
cmd = exec.Command(executableName, "clipclear")
|
||||||
if err != nil {
|
writeToStdin(cmd, copySubject)
|
||||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
err = cmd.Start()
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClipClear is called in a separate process to clear the clipboard after 30 seconds
|
// clipClear is called in a separate process to clear the clipboard after 30 seconds
|
||||||
func clipClear(oldContents string) {
|
func clipClear(oldContents string) {
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
|
|||||||
+12
-10
@@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyField copies a field from an entry to the clipboard
|
// copyField copies a field from an entry to the clipboard
|
||||||
func copyField(copySubject string, executableName string) {
|
func copyField(copySubject string, executableName string) {
|
||||||
cmd := exec.Command("termux-clipboard-set")
|
cmd := exec.Command("termux-clipboard-set")
|
||||||
writeToStdin(cmd, copySubject)
|
writeToStdin(cmd, copySubject)
|
||||||
@@ -20,18 +20,20 @@ func copyField(copySubject string, executableName string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = exec.Command(executableName, "clipclear")
|
// launch clipboard clearing process if executableName is provided
|
||||||
writeToStdin(cmd, copySubject)
|
if executableName != "" {
|
||||||
err = cmd.Start()
|
cmd = exec.Command(executableName, "clipclear")
|
||||||
if err != nil {
|
writeToStdin(cmd, copySubject)
|
||||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
err = cmd.Start()
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClipClear is called in a separate process to clear the clipboard after 30 seconds
|
// clipClear is called in a separate process to clear the clipboard after 30 seconds
|
||||||
func clipClear(oldContents string) {
|
func clipClear(oldContents string) {
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyField copies a field from an entry to the clipboard
|
// copyField copies a field from an entry to the clipboard
|
||||||
func copyField(copySubject string, executableName string) {
|
func copyField(copySubject string, executableName string) {
|
||||||
var envSet bool // track whether environment variables are set
|
var envSet bool // track whether environment variables are set
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
@@ -31,18 +31,20 @@ func copyField(copySubject string, executableName string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = exec.Command(executableName, "clipclear")
|
// launch clipboard clearing process if executableName is provided
|
||||||
writeToStdin(cmd, copySubject)
|
if executableName != "" {
|
||||||
err = cmd.Start()
|
cmd = exec.Command(executableName, "clipclear")
|
||||||
if err != nil {
|
writeToStdin(cmd, copySubject)
|
||||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
err = cmd.Start()
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClipClear is called in a separate process to clear the clipboard after 30 seconds
|
// clipClear is called in a separate process to clear the clipboard after 30 seconds
|
||||||
func clipClear(oldContents string) {
|
func clipClear(oldContents string) {
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
|
|||||||
+12
-10
@@ -10,7 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyField copies a field from an entry to the clipboard
|
// copyField copies a field from an entry to the clipboard
|
||||||
func copyField(copySubject string, executableName string) {
|
func copyField(copySubject string, executableName string) {
|
||||||
cmd := exec.Command("powershell.exe", "-c", fmt.Sprintf("echo '%s' | Set-Clipboard", strings.ReplaceAll(copySubject, "'", "''")))
|
cmd := exec.Command("powershell.exe", "-c", fmt.Sprintf("echo '%s' | Set-Clipboard", strings.ReplaceAll(copySubject, "'", "''")))
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
@@ -19,18 +19,20 @@ func copyField(copySubject string, executableName string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = exec.Command(executableName, "clipclear")
|
// launch clipboard clearing process if executableName is provided
|
||||||
writeToStdin(cmd, copySubject)
|
if executableName != "" {
|
||||||
err = cmd.Start()
|
cmd = exec.Command(executableName, "clipclear")
|
||||||
if err != nil {
|
writeToStdin(cmd, copySubject)
|
||||||
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
err = cmd.Start()
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0) // only exit if clipboard clearing process is launched, otherwise assume continuous clipboard refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClipClear is called in a separate process to clear the clipboard after 30 seconds
|
// clipClear is called in a separate process to clear the clipboard after 30 seconds
|
||||||
func clipClear(oldContents string) {
|
func clipClear(oldContents string) {
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user