mirror of
https://github.com/passbolt/go-passbolt-cli.git
synced 2025-05-11 02:28:22 +00:00

Some checks failed
Go / build (push) Has been cancelled
Prompts should be on STDERR https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/utilities/V3_chap02.html#tag_18_05_03 (under PS1)
152 lines
3.9 KiB
Go
152 lines
3.9 KiB
Go
package util
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/passbolt/go-passbolt/api"
|
|
"github.com/passbolt/go-passbolt/helper"
|
|
"github.com/spf13/viper"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
// ReadPassword reads a Password interactively or via Pipe
|
|
func ReadPassword(prompt string) (string, error) {
|
|
fd := int(os.Stdin.Fd())
|
|
var pass string
|
|
if term.IsTerminal(fd) {
|
|
fmt.Fprint(os.Stderr, prompt);
|
|
|
|
inputPass, err := term.ReadPassword(fd)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
pass = string(inputPass)
|
|
} else {
|
|
reader := bufio.NewReader(os.Stdin)
|
|
s, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
pass = s
|
|
}
|
|
|
|
return strings.Replace(pass, "\n", "", 1), nil
|
|
}
|
|
|
|
// GetClient gets a Logged in Passbolt Client
|
|
func GetClient(ctx context.Context) (*api.Client, error) {
|
|
serverAddress := viper.GetString("serverAddress")
|
|
if serverAddress == "" {
|
|
return nil, fmt.Errorf("serverAddress is not defined")
|
|
}
|
|
|
|
userPrivateKey := viper.GetString("userPrivateKey")
|
|
if userPrivateKey == "" {
|
|
return nil, fmt.Errorf("userPrivateKey is not defined")
|
|
}
|
|
|
|
userPassword := viper.GetString("userPassword")
|
|
if userPassword == "" {
|
|
cliPassword, err := ReadPassword("Enter Password:")
|
|
if err != nil {
|
|
fmt.Println()
|
|
return nil, fmt.Errorf("Reading Password: %w", err)
|
|
}
|
|
|
|
userPassword = cliPassword
|
|
fmt.Println()
|
|
}
|
|
|
|
httpClient, err := GetHttpClient()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client, err := api.NewClient(httpClient, "", serverAddress, userPrivateKey, userPassword)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Creating Client: %w", err)
|
|
}
|
|
|
|
client.Debug = viper.GetBool("debug")
|
|
|
|
token := viper.GetString("serverVerifyToken")
|
|
encToken := viper.GetString("serverVerifyEncToken")
|
|
|
|
if token != "" {
|
|
err = client.VerifyServer(ctx, token, encToken)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Verifing Server: %w", err)
|
|
}
|
|
}
|
|
|
|
switch viper.GetString("mfaMode") {
|
|
case "interactive-totp":
|
|
client.MFACallback = func(ctx context.Context, c *api.Client, res *api.APIResponse) (http.Cookie, error) {
|
|
challenge := api.MFAChallenge{}
|
|
err := json.Unmarshal(res.Body, &challenge)
|
|
if err != nil {
|
|
return http.Cookie{}, fmt.Errorf("Parsing MFA Challenge")
|
|
}
|
|
if challenge.Provider.TOTP == "" {
|
|
return http.Cookie{}, fmt.Errorf("Server Provided no TOTP Provider")
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
var code string
|
|
code, err := ReadPassword("Enter TOTP:")
|
|
if err != nil {
|
|
fmt.Printf("\n")
|
|
return http.Cookie{}, fmt.Errorf("Reading TOTP: %w", err)
|
|
}
|
|
fmt.Printf("\n")
|
|
req := api.MFAChallengeResponse{
|
|
TOTP: code,
|
|
}
|
|
var raw *http.Response
|
|
raw, _, err = c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "mfa/verify/totp.json", "v2", req, nil)
|
|
if err != nil {
|
|
if errors.Unwrap(err) != api.ErrAPIResponseErrorStatusCode {
|
|
return http.Cookie{}, fmt.Errorf("Doing MFA Challenge Response: %w", err)
|
|
}
|
|
fmt.Println("TOTP Verification Failed")
|
|
} else {
|
|
// MFA worked so lets find the cookie and return it
|
|
for _, cookie := range raw.Cookies() {
|
|
if cookie.Name == "passbolt_mfa" {
|
|
return *cookie, nil
|
|
}
|
|
}
|
|
return http.Cookie{}, fmt.Errorf("Unable to find Passbolt MFA Cookie")
|
|
}
|
|
}
|
|
return http.Cookie{}, fmt.Errorf("Failed MFA Challenge 3 times: %w", err)
|
|
}
|
|
case "noninteractive-totp":
|
|
// if new flag is unset, use old flag instead
|
|
totpToken := viper.GetString("mfaTotpToken")
|
|
if totpToken == "" {
|
|
totpToken = viper.GetString("totpToken")
|
|
}
|
|
|
|
totpOffset := viper.GetDuration("mfaTotpOffset")
|
|
if totpOffset == time.Duration(0) {
|
|
totpOffset = viper.GetDuration("totpOffset")
|
|
}
|
|
|
|
helper.AddMFACallbackTOTP(client, viper.GetUint("mfaRetrys"), viper.GetDuration("mfaDelay"), totpOffset, totpToken)
|
|
case "none":
|
|
default:
|
|
}
|
|
|
|
err = client.Login(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Logging in: %w", err)
|
|
}
|
|
return client, nil
|
|
}
|