github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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 }