Функциональность getpasswd в Go? - PullRequest
43 голосов
/ 26 января 2010

Положение:

Я хочу получить ввод пароля с консоли stdin - без повторения того, что пользователь вводит . Есть ли что-то сопоставимое с getpasswd функциональностью в Go?

Что я пробовал:

Я пытался использовать syscall.Read, но он повторяет то, что напечатано.

Ответы [ 9 ]

70 голосов
/ 24 сентября 2015

Ниже приведен один из лучших способов сделать это.Сначала получите terminal пакет от go get golang.org/x/crypto/ssh

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "syscall"

    "golang.org/x/crypto/ssh/terminal"
)

func main() {
    username, password := credentials()
    fmt.Printf("Username: %s, Password: %s\n", username, password)
}

func credentials() (string, string) {
    reader := bufio.NewReader(os.Stdin)

    fmt.Print("Enter Username: ")
    username, _ := reader.ReadString('\n')

    fmt.Print("Enter Password: ")
    bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
    if err == nil {
        fmt.Println("\nPassword typed: " + string(bytePassword))
    }
    password := string(bytePassword)

    return strings.TrimSpace(username), strings.TrimSpace(password)
}

http://play.golang.org/p/l-9IP1mrhA

27 голосов
/ 15 ноября 2012

Только что видел письмо в # go-nuts maillist. Есть кто-то, кто написал довольно простой пакет go для использования. Вы можете найти его здесь: https://github.com/howeyc/gopass

Это что-то вроде этого:

package main

import "fmt"
import "github.com/howeyc/gopass"

func main() {
    fmt.Printf("Password: ")
    pass := gopass.GetPasswd()
    // Do something with pass
}
8 голосов
/ 26 января 2010

Вы можете сделать это, выполнив stty -echo, чтобы отключить эхо, а затем stty echo после прочтения пароля, чтобы снова включить его

.
3 голосов
/ 26 сентября 2018

У меня был похожий вариант использования, и следующий фрагмент кода хорошо работает для меня. Не стесняйтесь попробовать это, если вы все еще застряли здесь.

import (
    "fmt"
    "golang.org/x/crypto/ssh/terminal"

)

func main() {
    fmt.Printf("Now, please type in the password (mandatory): ")
    password, _ := terminal.ReadPassword(0)

    fmt.Printf("Password is : %s", password)
}

Конечно, вам нужно заранее установить пакет терминала, используя go get.

3 голосов
/ 07 мая 2016

Вот решение, которое я разработал с использованием Go1.6.2, которое может оказаться полезным.

Используются только следующие стандартные пакеты: bufio, fmt, os, strings и syscall. В частности, он использует syscall.ForkExec() и syscall.Wait4() для вызова stty, чтобы отключить / включить эхо терминала.

Я проверил это на Linux и BSD (Mac). Это не будет работать на окнах.

// getPassword - Prompt for password. Use stty to disable echoing.
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
    fmt.Print(prompt)

    // Common settings and variables for both stty calls.
    attrs := syscall.ProcAttr{
        Dir:   "",
        Env:   []string{},
        Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
        Sys:   nil}
    var ws syscall.WaitStatus

    // Disable echoing.
    pid, err := syscall.ForkExec(
        "/bin/stty",
        []string{"stty", "-echo"},
        &attrs)
    if err != nil {
        panic(err)
    }

    // Wait for the stty process to complete.
    _, err = syscall.Wait4(pid, &ws, 0, nil)
    if err != nil {
        panic(err)
    }

    // Echo is disabled, now grab the data.
    reader := bufio.NewReader(os.Stdin)
    text, err := reader.ReadString('\n')
    if err != nil {
        panic(err)
    }

    // Re-enable echo.
    pid, err = syscall.ForkExec(
        "/bin/stty",
        []string{"stty", "echo"},
        &attrs)
    if err != nil {
        panic(err)
    }

    // Wait for the stty process to complete.
    _, err = syscall.Wait4(pid, &ws, 0, nil)
    if err != nil {
        panic(err)
    }

    return strings.TrimSpace(text)
}
2 голосов
/ 22 июля 2015

Вот версия, специфичная для Linux:

func terminalEcho(show bool) {
    // Enable or disable echoing terminal input. This is useful specifically for
    // when users enter passwords.
    // calling terminalEcho(true) turns on echoing (normal mode)
    // calling terminalEcho(false) hides terminal input.
    var termios = &syscall.Termios{}
    var fd = os.Stdout.Fd()

    if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
        syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
        return
    }

    if show {
        termios.Lflag |= syscall.ECHO
    } else {
        termios.Lflag &^= syscall.ECHO
    }

    if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
        uintptr(syscall.TCSETS),
        uintptr(unsafe.Pointer(termios))); err != 0 {
        return
    }
}

Итак, чтобы использовать это:

fmt.Print("password: ")

terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")

Это системный вызов TCGETS, специфичный для Linux. Существуют разные значения системных вызовов для OSX и Windows.

1 голос
/ 07 мая 2016

Вы также можете использовать функцию PasswordPrompt пакета https://github.com/peterh/liner.

1 голос
/ 26 января 2010

Требуется запуск stty с помощью функции Go ForkExec ():

package main

import (
    os      "os"
    bufio   "bufio"
    fmt     "fmt"
    str     "strings"
)

func main() {
    fmt.Println();
    if passwd, err := Getpasswd("Enter password: "); err == nil {
        fmt.Printf("\n\nPassword: '%s'\n",passwd)
    }
}

func Getpasswd(prompt string) (passwd string, err os.Error) {
    fmt.Print(prompt);
    const stty_arg0  = "/bin/stty";
    stty_argv_e_off := []string{"stty","-echo"};
    stty_argv_e_on  := []string{"stty","echo"};
    const exec_cwdir = "";
    fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
    pid, err := os.ForkExec(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
    if err != nil {
        return passwd, os.NewError(fmt.Sprintf("Failed turning off console echo for password entry:\n\t%s",err))
    }
    rd := bufio.NewReader(os.Stdin);
    os.Wait(pid,0);
    line, err := rd.ReadString('\n');
    if err == nil {
        passwd = str.TrimSpace(line)
    } else {
        err = os.NewError(fmt.Sprintf("Failed during password entry: %s",err))
    }
    pid, e := os.ForkExec(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
    if e == nil {
        os.Wait(pid,0)
    } else if err == nil {
        err = os.NewError(fmt.Sprintf("Failed turning on console echo post password entry:\n\t%s",e))
    }
    return passwd, err
}
0 голосов
/ 26 января 2010

Вы можете получить желаемое поведение с помощью метода Read из объекта os.File (или переменной os.Stdin). Следующий пример программы будет читать строку текста (завершается нажатием клавиши возврата), но не будет отображать ее до вызова fmt.Printf.

package main

import "fmt"
import "os"

func main() {
  var input []byte = make( []byte, 100 );
  os.Stdin.Read( input );
  fmt.Printf( "%s", input );
}

Если вам нужно более продвинутое поведение, вам, вероятно, придется использовать утилиты Go C-wrapper и создать некоторые оболочки для низкоуровневых вызовов API.

...