github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/idutil/tlf_names.go (about) 1 // Copyright 2019 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package idutil 6 7 import ( 8 "context" 9 "fmt" 10 "sort" 11 "strings" 12 13 "github.com/keybase/client/go/externals" 14 "github.com/keybase/client/go/kbfs/tlf" 15 kbname "github.com/keybase/client/go/kbun" 16 "github.com/pkg/errors" 17 ) 18 19 // TODO: this function can likely be replaced with a call to 20 // AssertionParseAndOnly when CORE-2967 and CORE-2968 are fixed. 21 func normalizeAssertionOrName(s string, t tlf.Type) (string, error) { 22 if kbname.CheckUsername(s) { 23 return kbname.NewNormalizedUsername(s).String(), nil 24 } 25 26 // TODO: this fails for http and https right now (see CORE-2968). 27 socialAssertion, isSocialAssertion := externals.NormalizeSocialAssertionStatic(context.TODO(), s) 28 if isSocialAssertion { 29 if t == tlf.SingleTeam { 30 return "", fmt.Errorf( 31 "No social assertions allowed for team TLF: %s", s) 32 } 33 return socialAssertion.String(), nil 34 } 35 36 sAssertion := s 37 if t == tlf.SingleTeam { 38 sAssertion = "team:" + s 39 } 40 if expr, err := externals.AssertionParseAndOnlyStatic(context.TODO(), sAssertion); err == nil { 41 // If the expression only contains a single url, make sure 42 // it's not a just considered a single keybase username. If 43 // it is, then some non-username slipped into the default 44 // "keybase" case and should be considered an error. 45 urls := expr.CollectUrls(nil) 46 if len(urls) == 1 && urls[0].IsKeybase() { 47 return "", NoSuchUserError{s} 48 } 49 50 // Normalize and return. Ideally `AssertionParseAndOnly` 51 // would normalize for us, but that doesn't work yet, so for 52 // now we'll just lower-case. This will incorrectly lower 53 // case http/https/web assertions, as well as case-sensitive 54 // social assertions in AND expressions. TODO: see CORE-2967. 55 return strings.ToLower(s), nil 56 } 57 58 return "", BadTLFNameError{Name: s} 59 } 60 61 // normalizeNames normalizes a slice of names and returns 62 // whether any of them changed. 63 func normalizeNames(names []string, t tlf.Type) (changesMade bool, err error) { 64 for i, name := range names { 65 x, err := normalizeAssertionOrName(name, t) 66 if err != nil { 67 return false, err 68 } 69 if x != name { 70 names[i] = x 71 changesMade = true 72 } 73 } 74 return changesMade, nil 75 } 76 77 // NormalizeNamesInTLF takes a split TLF name and, without doing any 78 // resolutions or identify calls, normalizes all elements of the 79 // name. It then returns the normalized name and a boolean flag 80 // whether any names were modified. 81 // This modifies the slices passed as arguments. 82 func NormalizeNamesInTLF(writerNames, readerNames []string, 83 t tlf.Type, extensionSuffix string) (normalizedName string, 84 changesMade bool, err error) { 85 changesMade, err = normalizeNames(writerNames, t) 86 if err != nil { 87 return "", false, err 88 } 89 sort.Strings(writerNames) 90 normalizedName = strings.Join(writerNames, ",") 91 if len(readerNames) > 0 { 92 rchanges, err := normalizeNames(readerNames, t) 93 if err != nil { 94 return "", false, err 95 } 96 changesMade = changesMade || rchanges 97 sort.Strings(readerNames) 98 normalizedName += tlf.ReaderSep + strings.Join(readerNames, ",") 99 } 100 if len(extensionSuffix) != 0 { 101 // This *should* be normalized already but make sure. I can see not 102 // doing so might surprise a caller. 103 nExt := strings.ToLower(extensionSuffix) 104 normalizedName += tlf.HandleExtensionSep + nExt 105 changesMade = changesMade || nExt != extensionSuffix 106 } 107 108 return normalizedName, changesMade, nil 109 } 110 111 // SplitAndNormalizeTLFName takes a tlf name as a string 112 // and tries to normalize it offline. In addition to other 113 // checks it returns TlfNameNotCanonical if it does not 114 // look canonical. 115 // Note that ordering differences do not result in TlfNameNotCanonical 116 // being returned. 117 func SplitAndNormalizeTLFName(name string, t tlf.Type) ( 118 writerNames, readerNames []string, 119 extensionSuffix string, err error) { 120 writerNames, readerNames, extensionSuffix, err = tlf.SplitName(name) 121 if err != nil { 122 return nil, nil, "", err 123 } 124 if t == tlf.SingleTeam && len(writerNames) != 1 { 125 // No team folder can have more than one writer. 126 return nil, nil, "", NoSuchNameError{Name: name} 127 } 128 129 hasReaders := len(readerNames) != 0 130 if t != tlf.Private && hasReaders { 131 // No public/team folder can have readers. 132 return nil, nil, "", NoSuchNameError{Name: name} 133 } 134 135 normalizedName, changes, err := NormalizeNamesInTLF( 136 writerNames, readerNames, t, extensionSuffix) 137 if err != nil { 138 return nil, nil, "", err 139 } 140 // Check for changes - not just ordering differences here. 141 if changes { 142 return nil, nil, "", errors.WithStack(TlfNameNotCanonical{name, normalizedName}) 143 } 144 145 return writerNames, readerNames, strings.ToLower(extensionSuffix), nil 146 }