github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/client/cmd_dump_keyfamily.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package client
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"golang.org/x/net/context"
    11  
    12  	"github.com/keybase/cli"
    13  	"github.com/keybase/client/go/libcmdline"
    14  	"github.com/keybase/client/go/libkb"
    15  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    16  )
    17  
    18  const spacesPerIndent = 4
    19  
    20  func indentSpace(level int) string {
    21  	return strings.Repeat(" ", level*spacesPerIndent)
    22  }
    23  
    24  type CmdDumpKeyfamily struct {
    25  	libkb.Contextified
    26  	user string
    27  }
    28  
    29  func (v *CmdDumpKeyfamily) ParseArgv(ctx *cli.Context) error {
    30  	nargs := len(ctx.Args())
    31  	if nargs > 1 {
    32  		return fmt.Errorf("dump-keyfamily only takes one argument, the user to lookup")
    33  	}
    34  	if nargs == 1 {
    35  		v.user = ctx.Args()[0]
    36  	}
    37  	return nil
    38  }
    39  
    40  func NewCmdDumpKeyfamily(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
    41  	return cli.Command{
    42  		Name:         "dump-keyfamily",
    43  		Unlisted:     true,
    44  		ArgumentHelp: "[username]",
    45  		Usage:        "Print out a user's current key family",
    46  		Description:  "Print out a user's current key family. Don't specify a username to dump out your own keys.",
    47  		Flags:        []cli.Flag{},
    48  		Action: func(c *cli.Context) {
    49  			cl.ChooseCommand(&CmdDumpKeyfamily{Contextified: libkb.NewContextified(g)}, "dump-keyfamily", c)
    50  		},
    51  	}
    52  }
    53  
    54  func (v *CmdDumpKeyfamily) Run() (err error) {
    55  	configCli, err := GetConfigClient(v.G())
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	currentStatus, err := configCli.GetCurrentStatus(context.TODO(), 0)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	if !currentStatus.LoggedIn {
    65  		return fmt.Errorf("Not logged in.")
    66  	}
    67  
    68  	var username string
    69  	if v.user != "" {
    70  		username = v.user
    71  	} else {
    72  		username = currentStatus.User.Username
    73  	}
    74  
    75  	userCli, err := GetUserClient(v.G())
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	user, err := userCli.LoadUserByName(context.TODO(), keybase1.LoadUserByNameArg{Username: username})
    81  	if err != nil {
    82  		return fmt.Errorf("error loading user: %s", err)
    83  	}
    84  
    85  	publicKeys, err := userCli.LoadPublicKeys(context.TODO(), keybase1.LoadPublicKeysArg{Uid: user.Uid})
    86  	if err != nil {
    87  		return fmt.Errorf("error loading keys: %s", err)
    88  	}
    89  
    90  	devCli, err := GetDeviceClient(v.G())
    91  	if err != nil {
    92  		return err
    93  	}
    94  	devs, err := devCli.DeviceList(context.TODO(), 0)
    95  	if err != nil {
    96  		return fmt.Errorf("error loading device list: %s", err)
    97  	}
    98  
    99  	return v.printExportedUser(user, publicKeys, devs)
   100  }
   101  
   102  func findSubkeys(parentID keybase1.KID, allKeys []keybase1.PublicKey) []keybase1.PublicKey {
   103  	ret := []keybase1.PublicKey{}
   104  	for _, key := range allKeys {
   105  		if keybase1.KIDFromString(key.ParentID).Equal(parentID) {
   106  			ret = append(ret, key)
   107  		}
   108  	}
   109  	return ret
   110  }
   111  
   112  func (v *CmdDumpKeyfamily) printExportedUser(user keybase1.User, publicKeys []keybase1.PublicKey,
   113  	devices []keybase1.Device) error {
   114  
   115  	dui := v.G().UI.GetDumbOutputUI()
   116  	if len(publicKeys) == 0 {
   117  		dui.Printf("No public keys.\n")
   118  		return nil
   119  	}
   120  	dui.Printf("Public keys:\n")
   121  	// Keep track of subkeys we print, so that if e.g. a subkey's parent is
   122  	// nonexistent, we can notice that we skipped it.
   123  	subkeysShown := make(map[keybase1.KID]bool)
   124  	for _, key := range publicKeys {
   125  		if !key.IsSibkey {
   126  			// Subkeys will be printed under their respective sibkeys.
   127  			continue
   128  		}
   129  		subkeys := findSubkeys(key.KID, publicKeys)
   130  		err := v.printKey(key, subkeys, 1)
   131  		if err != nil {
   132  			return err
   133  		}
   134  		for _, subkey := range subkeys {
   135  			subkeysShown[subkey.KID] = true
   136  		}
   137  	}
   138  	// Print errors for any subkeys we failed to show.
   139  	for _, key := range publicKeys {
   140  		if !key.IsSibkey && !subkeysShown[key.KID] {
   141  			v.G().Log.Errorf("Dangling subkey: %s", key.KID)
   142  		}
   143  	}
   144  	return nil
   145  }
   146  
   147  func (v *CmdDumpKeyfamily) printKey(key keybase1.PublicKey, subkeys []keybase1.PublicKey, indent int) error {
   148  	if key.KID == "" {
   149  		return fmt.Errorf("Found a key with an empty KID.")
   150  	}
   151  	eldestStr := ""
   152  	if key.IsEldest {
   153  		eldestStr = " (eldest)"
   154  	}
   155  	dui := v.G().UI.GetDumbOutputUI()
   156  	dui.Printf("%s%s%s\n", indentSpace(indent), key.KID, eldestStr)
   157  	if key.PGPFingerprint != "" {
   158  		dui.Printf("%sPGP Fingerprint: %s\n", indentSpace(indent+1), libkb.PGPFingerprintFromHexNoError(key.PGPFingerprint).ToQuads())
   159  		dui.Printf("%sPGP Identities:\n", indentSpace(indent+1))
   160  		for _, identity := range key.PGPIdentities {
   161  			commentStr := ""
   162  			if identity.Comment != "" {
   163  				commentStr = fmt.Sprintf(" (%s)", identity.Comment)
   164  			}
   165  			emailStr := ""
   166  			if identity.Email != "" {
   167  				emailStr = fmt.Sprintf(" <%s>", identity.Email)
   168  			}
   169  			dui.Printf("%s%s%s%s\n", indentSpace(indent+2), identity.Username, commentStr, emailStr)
   170  		}
   171  	}
   172  	if key.DeviceID != "" || key.DeviceType != keybase1.DeviceTypeV2_NONE || key.DeviceDescription != "" {
   173  		dui.Printf("%sDevice:\n", indentSpace(indent+1))
   174  		if key.DeviceID != "" {
   175  			dui.Printf("%sID: %s\n", indentSpace(indent+2), key.DeviceID)
   176  		}
   177  		if key.DeviceType != keybase1.DeviceTypeV2_NONE {
   178  			dui.Printf("%sType: %s\n", indentSpace(indent+2), key.DeviceType)
   179  		}
   180  		if key.DeviceDescription != "" {
   181  			dui.Printf("%sDescription: %s\n", indentSpace(indent+2), key.DeviceDescription)
   182  		}
   183  	}
   184  	dui.Printf("%sCreated: %s\n", indentSpace(indent+1), keybase1.FromTime(key.CTime))
   185  	dui.Printf("%sExpires: %s\n", indentSpace(indent+1), keybase1.FromTime(key.ETime))
   186  
   187  	if len(subkeys) > 0 {
   188  		dui.Printf("%sSubkeys:\n", indentSpace(indent+1))
   189  		for _, subkey := range subkeys {
   190  			err := v.printKey(subkey, nil, indent+2)
   191  			if err != nil {
   192  				return err
   193  			}
   194  		}
   195  	}
   196  	return nil
   197  }
   198  
   199  func (v *CmdDumpKeyfamily) GetUsage() libkb.Usage {
   200  	return libkb.Usage{
   201  		Config: true,
   202  		API:    true,
   203  	}
   204  }