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  }