зеркало из https://github.com/getsops/sops.git
165 строки
4.3 KiB
Go
165 строки
4.3 KiB
Go
package sops
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/goware/prefixer"
|
|
"github.com/mitchellh/go-wordwrap"
|
|
)
|
|
|
|
// UserError is a well-formatted error for the purpose of being displayed to
|
|
// the end user.
|
|
type UserError interface {
|
|
error
|
|
UserError() string
|
|
}
|
|
|
|
var statusSuccess = color.New(color.FgGreen).Sprint("SUCCESS")
|
|
var statusFailed = color.New(color.FgRed).Sprint("FAILED")
|
|
|
|
type getDataKeyError struct {
|
|
RequiredSuccessfulKeyGroups int
|
|
GroupResults []error
|
|
}
|
|
|
|
func (err *getDataKeyError) successfulKeyGroups() int {
|
|
n := 0
|
|
for _, r := range err.GroupResults {
|
|
if r == nil {
|
|
n++
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (err *getDataKeyError) Error() string {
|
|
return fmt.Sprintf("Error getting data key: %d successful groups "+
|
|
"required, got %d", err.RequiredSuccessfulKeyGroups,
|
|
err.successfulKeyGroups())
|
|
}
|
|
|
|
func (err *getDataKeyError) UserError() string {
|
|
var groupErrs []string
|
|
for i, res := range err.GroupResults {
|
|
groupErr := decryptGroupError{
|
|
err: res,
|
|
groupName: fmt.Sprintf("%d", i),
|
|
}
|
|
groupErrs = append(groupErrs, groupErr.UserError())
|
|
}
|
|
var trailer string
|
|
if err.RequiredSuccessfulKeyGroups == 0 {
|
|
trailer = "Recovery failed because no master key was able to decrypt " +
|
|
"the file. In order for SOPS to recover the file, at least one key " +
|
|
"has to be successful, but none were."
|
|
} else {
|
|
trailer = fmt.Sprintf("Recovery failed because the file was "+
|
|
"encrypted with a Shamir threshold of %d, but only %d part(s) "+
|
|
"were successfully recovered, one for each successful key group. "+
|
|
"In order for SOPS to recover the file, at least %d groups have "+
|
|
"to be successful. In order for a group to be successful, "+
|
|
"decryption has to succeed with any of the keys in that key group.",
|
|
err.RequiredSuccessfulKeyGroups, err.successfulKeyGroups(),
|
|
err.RequiredSuccessfulKeyGroups)
|
|
}
|
|
trailer = wordwrap.WrapString(trailer, 75)
|
|
return fmt.Sprintf("Failed to get the data key required to "+
|
|
"decrypt the SOPS file.\n\n%s\n\n%s",
|
|
strings.Join(groupErrs, "\n\n"), trailer)
|
|
}
|
|
|
|
type decryptGroupError struct {
|
|
groupName string
|
|
err error
|
|
}
|
|
|
|
func (r *decryptGroupError) Error() string {
|
|
return fmt.Sprintf("could not decrypt group %s: %s", r.groupName, r.err)
|
|
}
|
|
|
|
func (r *decryptGroupError) UserError() string {
|
|
var status string
|
|
if r.err == nil {
|
|
status = statusSuccess
|
|
} else {
|
|
status = statusFailed
|
|
}
|
|
header := fmt.Sprintf(`Group %s: %s`, r.groupName, status)
|
|
if r.err == nil {
|
|
return header
|
|
}
|
|
message := r.err.Error()
|
|
if userError, ok := r.err.(UserError); ok {
|
|
message = userError.UserError()
|
|
}
|
|
reader := prefixer.New(strings.NewReader(message), " ")
|
|
// Safe to ignore this error, as reading from a strings.Reader can't fail
|
|
errMsg, _ := io.ReadAll(reader)
|
|
return fmt.Sprintf("%s\n%s", header, string(errMsg))
|
|
}
|
|
|
|
type decryptKeyErrors []error
|
|
|
|
func (e decryptKeyErrors) Error() string {
|
|
return fmt.Sprintf("error decrypting key: %s", []error(e))
|
|
}
|
|
|
|
func (e decryptKeyErrors) UserError() string {
|
|
var errStrs []string
|
|
for _, err := range []error(e) {
|
|
if userErr, ok := err.(UserError); ok {
|
|
errStrs = append(errStrs, userErr.UserError())
|
|
} else {
|
|
errStrs = append(errStrs, err.Error())
|
|
}
|
|
}
|
|
return strings.Join(errStrs, "\n\n")
|
|
}
|
|
|
|
type decryptKeyError struct {
|
|
keyName string
|
|
errs []error
|
|
}
|
|
|
|
func (e *decryptKeyError) isSuccessful() bool {
|
|
for _, err := range e.errs {
|
|
if err == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (e *decryptKeyError) Error() string {
|
|
return fmt.Sprintf("error decrypting key %s: %s", e.keyName, e.errs)
|
|
}
|
|
|
|
func (e *decryptKeyError) UserError() string {
|
|
var status string
|
|
if e.isSuccessful() {
|
|
status = statusSuccess
|
|
} else {
|
|
status = statusFailed
|
|
}
|
|
header := fmt.Sprintf("%s: %s", e.keyName, status)
|
|
if e.isSuccessful() {
|
|
return header
|
|
}
|
|
var errMessages []string
|
|
for _, err := range e.errs {
|
|
wrappedErr := wordwrap.WrapString(err.Error(), 60)
|
|
reader := prefixer.New(strings.NewReader(wrappedErr), " | ")
|
|
// Safe to ignore this error, as reading from a strings.Reader can't fail
|
|
errMsg, _ := io.ReadAll(reader)
|
|
errMsg[0] = '-'
|
|
errMessages = append(errMessages, string(errMsg))
|
|
}
|
|
joinedMsgs := strings.Join(errMessages, "\n\n")
|
|
reader := prefixer.New(strings.NewReader(joinedMsgs), " ")
|
|
errMsg, _ := io.ReadAll(reader)
|
|
return fmt.Sprintf("%s\n%s", header, string(errMsg))
|
|
}
|