github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfstool/md_dump_common.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/davecgh/go-spew/spew"
     8  	"github.com/keybase/client/go/kbfs/idutil"
     9  	"github.com/keybase/client/go/kbfs/kbfscodec"
    10  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    11  	"github.com/keybase/client/go/kbfs/kbfsmd"
    12  	"github.com/keybase/client/go/kbfs/libkbfs"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"golang.org/x/net/context"
    15  )
    16  
    17  // replacementMap is a map from a string to its replacement, intended
    18  // to provide human-readable strings to UIDs and KIDs. It is usually
    19  // created once per invocation and plumbed through everything that
    20  // uses it, filling it on-demand so as to avoid needless calls to the
    21  // service.
    22  type replacementMap map[string]string
    23  
    24  func mdDumpGetDeviceStringForCryptPublicKey(k kbfscrypto.CryptPublicKey, ui idutil.UserInfo) (
    25  	string, bool) {
    26  	deviceName, ok := ui.KIDNames[k.KID()]
    27  	if !ok {
    28  		return "", false
    29  	}
    30  
    31  	if revokedInfo, ok := ui.RevokedCryptPublicKeys[k]; ok {
    32  		return fmt.Sprintf("%s (revoked %s) (kid:%s)",
    33  			deviceName, revokedInfo.Time.Time(), k), true
    34  	}
    35  
    36  	return fmt.Sprintf("%s (kid:%s)", deviceName, k), true
    37  }
    38  
    39  func mdDumpGetDeviceStringForVerifyingKey(k kbfscrypto.VerifyingKey, ui idutil.UserInfo) (
    40  	string, bool) {
    41  	deviceName, ok := ui.KIDNames[k.KID()]
    42  	if !ok {
    43  		return "", false
    44  	}
    45  
    46  	if revokedInfo, ok := ui.RevokedVerifyingKeys[k]; ok {
    47  		return fmt.Sprintf("%s (revoked %s) (kid:%s)",
    48  			deviceName, revokedInfo.Time.Time(), k), true
    49  	}
    50  
    51  	return fmt.Sprintf("%s (kid:%s)", deviceName, k), true
    52  }
    53  
    54  func mdDumpFillReplacements(ctx context.Context, codec kbfscodec.Codec,
    55  	service libkbfs.KeybaseService, osg idutil.OfflineStatusGetter,
    56  	prefix string, rmd kbfsmd.RootMetadata, extra kbfsmd.ExtraMetadata,
    57  	replacements replacementMap) error {
    58  	writers, readers, err := rmd.GetUserDevicePublicKeys(extra)
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	offline := osg.OfflineAvailabilityForID(rmd.TlfID())
    64  
    65  	for _, userKeys := range []kbfsmd.UserDevicePublicKeys{writers, readers} {
    66  		for u := range userKeys {
    67  			// Make sure to only make one Resolve and one
    68  			// LoadUserPlusKeys call per user for a single
    69  			// replacements map.
    70  			if _, ok := replacements[u.String()]; ok {
    71  				continue
    72  			}
    73  
    74  			username, _, err := service.Resolve(
    75  				ctx, fmt.Sprintf("uid:%s", u),
    76  				keybase1.OfflineAvailability_NONE)
    77  			if err == nil {
    78  				replacements[u.String()] = fmt.Sprintf(
    79  					"%s (uid:%s)", username, u)
    80  			} else {
    81  				replacements[u.String()] = fmt.Sprintf(
    82  					"<unknown username> (uid:%s)", u)
    83  				printError(prefix, err)
    84  			}
    85  
    86  			ui, err := service.LoadUserPlusKeys(ctx, u, "", offline)
    87  			if err != nil {
    88  				printError(prefix, err)
    89  				continue
    90  			}
    91  
    92  			for _, k := range ui.CryptPublicKeys {
    93  				if _, ok := replacements[k.String()]; ok {
    94  					continue
    95  				}
    96  
    97  				if deviceStr, ok := mdDumpGetDeviceStringForCryptPublicKey(k, ui); ok {
    98  					replacements[k.String()] = deviceStr
    99  				}
   100  			}
   101  
   102  			for k := range ui.RevokedCryptPublicKeys {
   103  				if _, ok := replacements[k.String()]; ok {
   104  					continue
   105  				}
   106  
   107  				if deviceStr, ok := mdDumpGetDeviceStringForCryptPublicKey(k, ui); ok {
   108  					replacements[k.String()] = deviceStr
   109  				}
   110  			}
   111  
   112  			for _, k := range ui.VerifyingKeys {
   113  				if _, ok := replacements[k.String()]; ok {
   114  					continue
   115  				}
   116  
   117  				if deviceStr, ok := mdDumpGetDeviceStringForVerifyingKey(k, ui); ok {
   118  					replacements[k.String()] = deviceStr
   119  				}
   120  			}
   121  
   122  			for k := range ui.RevokedVerifyingKeys {
   123  				if _, ok := replacements[k.String()]; ok {
   124  					continue
   125  				}
   126  
   127  				if deviceStr, ok := mdDumpGetDeviceStringForVerifyingKey(k, ui); ok {
   128  					replacements[k.String()] = deviceStr
   129  				}
   130  			}
   131  		}
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func mdDumpReplaceAll(s string, replacements replacementMap) string {
   138  	for old, new := range replacements {
   139  		s = strings.ReplaceAll(s, old, new)
   140  	}
   141  	return s
   142  }
   143  
   144  func mdDumpReadOnlyRMDWithReplacements(
   145  	ctx context.Context, codec kbfscodec.Codec,
   146  	replacements replacementMap, rmd libkbfs.ReadOnlyRootMetadata) error {
   147  	c := spew.NewDefaultConfig()
   148  	c.Indent = "  "
   149  	c.DisablePointerAddresses = true
   150  	c.DisableCapacities = true
   151  	c.SortKeys = true
   152  
   153  	fmt.Print("Root metadata\n")
   154  	fmt.Print("-------------\n")
   155  
   156  	brmdDump, err := kbfsmd.DumpRootMetadata(codec, rmd.GetBareRootMetadata())
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	fmt.Printf("%s\n", mdDumpReplaceAll(brmdDump, replacements))
   162  
   163  	fmt.Print("Extra metadata\n")
   164  	fmt.Print("--------------\n")
   165  	extraDump, err := kbfsmd.DumpExtraMetadata(codec, rmd.Extra())
   166  	if err != nil {
   167  		return err
   168  	}
   169  	fmt.Printf("%s\n", mdDumpReplaceAll(extraDump, replacements))
   170  
   171  	fmt.Print("Private metadata\n")
   172  	fmt.Print("----------------\n")
   173  	pmdDump, err := libkbfs.DumpPrivateMetadata(
   174  		codec, len(rmd.GetSerializedPrivateMetadata()), *rmd.Data())
   175  	if err != nil {
   176  		return err
   177  	}
   178  	// Let the caller provide a trailing newline (if desired).
   179  	fmt.Printf("%s", mdDumpReplaceAll(pmdDump, replacements))
   180  
   181  	return nil
   182  }
   183  
   184  func mdDumpReadOnlyRMD(ctx context.Context, config libkbfs.Config,
   185  	prefix string, replacements replacementMap,
   186  	rmd libkbfs.ReadOnlyRootMetadata) error {
   187  	err := mdDumpFillReplacements(
   188  		ctx, config.Codec(), config.KeybaseService(), config,
   189  		prefix, rmd.GetBareRootMetadata(), rmd.Extra(), replacements)
   190  	if err != nil {
   191  		printError(prefix, err)
   192  	}
   193  
   194  	return mdDumpReadOnlyRMDWithReplacements(
   195  		ctx, config.Codec(), replacements, rmd)
   196  }