github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/tlf/utils.go (about) 1 // Copyright 2016 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 // This is all stuff copied from libkbfs. 5 6 package tlf 7 8 import ( 9 "fmt" 10 "sort" 11 "strings" 12 13 "github.com/keybase/client/go/externals" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/keybase1" 16 ) 17 18 const ( 19 // TlfHandleExtensionSep is the string that separates the folder 20 // participants from an extension suffix in the TLF name. 21 TlfHandleExtensionSep = " " 22 23 // PublicUIDName is the name given to keybase1.PublicUID. This string 24 // should correspond to an illegal or reserved Keybase user name. 25 PublicUIDName = "_public" 26 ) 27 28 // SplitAndNormalizeTLFName returns separate lists of normalized 29 // writer and reader names, as well as the extension suffix, of the 30 // given `name`. 31 func SplitAndNormalizeTLFName(mctx libkb.MetaContext, name string, public bool) ( 32 writerNames, readerNames []string, 33 extensionSuffix string, err error) { 34 35 names := strings.SplitN(name, TlfHandleExtensionSep, 2) 36 if len(names) > 2 { 37 return nil, nil, "", BadTLFNameError{name} 38 } 39 if len(names) > 1 { 40 extensionSuffix = names[1] 41 } 42 43 splitNames := strings.SplitN(names[0], ReaderSep, 3) 44 if len(splitNames) > 2 { 45 return nil, nil, "", BadTLFNameError{name} 46 } 47 writerNames = strings.Split(splitNames[0], ",") 48 if len(splitNames) > 1 { 49 readerNames = strings.Split(splitNames[1], ",") 50 } 51 52 hasPublic := len(readerNames) == 0 53 54 if public && !hasPublic { 55 // No public folder exists for this folder. 56 return nil, nil, "", NoSuchNameError{Name: name} 57 } 58 59 normalizedName, err := NormalizeNamesInTLF(mctx, 60 writerNames, readerNames, extensionSuffix) 61 if err != nil { 62 return nil, nil, "", err 63 } 64 if normalizedName != name { 65 return nil, nil, "", NameNotCanonical{name, normalizedName} 66 } 67 68 return writerNames, readerNames, strings.ToLower(extensionSuffix), nil 69 } 70 71 // NormalizeNamesInTLF takes a split TLF name and, without doing any 72 // resolutions or identify calls, normalizes all elements of the 73 // name. It then returns the normalized name. 74 func NormalizeNamesInTLF(mctx libkb.MetaContext, writerNames, readerNames []string, 75 extensionSuffix string) (string, error) { 76 sortedWriterNames := make([]string, len(writerNames)) 77 var err error 78 for i, w := range writerNames { 79 sortedWriterNames[i], err = NormalizeAssertionOrName(mctx, w) 80 if err != nil { 81 return "", err 82 } 83 } 84 sort.Strings(sortedWriterNames) 85 normalizedName := strings.Join(sortedWriterNames, ",") 86 if len(readerNames) > 0 { 87 sortedReaderNames := make([]string, len(readerNames)) 88 for i, r := range readerNames { 89 sortedReaderNames[i], err = NormalizeAssertionOrName(mctx, r) 90 if err != nil { 91 return "", err 92 } 93 } 94 sort.Strings(sortedReaderNames) 95 normalizedName += ReaderSep + strings.Join(sortedReaderNames, ",") 96 } 97 if len(extensionSuffix) != 0 { 98 // This *should* be normalized already but make sure. I can see not 99 // doing so might surprise a caller. 100 normalizedName += TlfHandleExtensionSep + strings.ToLower(extensionSuffix) 101 } 102 103 return normalizedName, nil 104 } 105 106 // NormalizeAssertionOrName normalizes the given assertion or name `s`. 107 // 108 // TODO: this function can likely be replaced with a call to 109 // AssertionParseAndOnly when CORE-2967 and CORE-2968 are fixed. 110 func NormalizeAssertionOrName(mctx libkb.MetaContext, s string) (string, error) { 111 if libkb.CheckUsername.F(s) { 112 return libkb.NewNormalizedUsername(s).String(), nil 113 } 114 115 // TODO: this fails for http and https right now (see CORE-2968). 116 socialAssertion, isSocialAssertion := externals.NormalizeSocialAssertion(mctx, s) 117 if isSocialAssertion { 118 return socialAssertion.String(), nil 119 } 120 121 if expr, err := externals.AssertionParseAndOnly(mctx, s); err == nil { 122 // If the expression only contains a single url, make sure 123 // it's not a just considered a single keybase username. If 124 // it is, then some non-username slipped into the default 125 // "keybase" case and should be considered an error. 126 urls := expr.CollectUrls(nil) 127 if len(urls) == 1 && urls[0].IsKeybase() { 128 return "", NoSuchUserError{s} 129 } 130 131 // Normalize and return. Ideally `AssertionParseAndOnly` 132 // would normalize for us, but that doesn't work yet, so for 133 // now we'll just lower-case. This will incorrectly lower 134 // case http/https/web assertions, as well as case-sensitive 135 // social assertions in AND expressions. TODO: see CORE-2967. 136 return strings.ToLower(s), nil 137 } 138 139 return "", BadTLFNameError{s} 140 } 141 142 // NoSuchNameError indicates that the user tried to access a 143 // subdirectory entry that doesn't exist. 144 type NoSuchNameError struct { 145 Name string 146 } 147 148 // Error implements the error interface for NoSuchNameError 149 func (e NoSuchNameError) Error() string { 150 return fmt.Sprintf("%s doesn't exist", e.Name) 151 } 152 153 // BadTLFNameError indicates a top-level folder name that has an 154 // incorrect format. 155 type BadTLFNameError struct { 156 Name string 157 } 158 159 // Error implements the error interface for BadTLFNameError. 160 func (e BadTLFNameError) Error() string { 161 return fmt.Sprintf("TLF name %s is in an incorrect format", e.Name) 162 } 163 164 // NameNotCanonical indicates that a name isn't a canonical, and that 165 // another (not necessarily canonical) name should be tried. 166 type NameNotCanonical struct { 167 Name, NameToTry string 168 } 169 170 func (e NameNotCanonical) Error() string { 171 return fmt.Sprintf("TLF name %s isn't canonical: try %s instead", 172 e.Name, e.NameToTry) 173 } 174 175 // NoSuchUserError indicates that the given user couldn't be resolved. 176 type NoSuchUserError struct { 177 Input string 178 } 179 180 // Error implements the error interface for NoSuchUserError 181 func (e NoSuchUserError) Error() string { 182 return fmt.Sprintf("%s is not a Keybase user", e.Input) 183 } 184 185 // ToStatus implements the keybase1.ToStatusAble interface for NoSuchUserError 186 func (e NoSuchUserError) ToStatus() keybase1.Status { 187 return keybase1.Status{ 188 Name: "NotFound", 189 Code: int(keybase1.StatusCode_SCNotFound), 190 Desc: e.Error(), 191 } 192 }