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

     1  // Copyright 2016 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 tlf
     6  
     7  import (
     8  	"reflect"
     9  	"sort"
    10  
    11  	"github.com/keybase/client/go/protocol/keybase1"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // Handle uniquely identified top-level folders by readers and writers.
    16  //
    17  // NOTE: if you change this type, ensure you update the `NumField` check in
    18  // `init`, and that the new fields are correctly checked for equality in
    19  // `Handle.DeepEqual()`.
    20  // TODO: Have separate types for writers vs. readers.
    21  type Handle struct {
    22  	Writers           []keybase1.UserOrTeamID    `codec:"w,omitempty"`
    23  	Readers           []keybase1.UserOrTeamID    `codec:"r,omitempty"`
    24  	UnresolvedWriters []keybase1.SocialAssertion `codec:"uw,omitempty"`
    25  	UnresolvedReaders []keybase1.SocialAssertion `codec:"ur,omitempty"`
    26  	ConflictInfo      *HandleExtension           `codec:"ci,omitempty"`
    27  	FinalizedInfo     *HandleExtension           `codec:"fi,omitempty"`
    28  
    29  	// caching field to track whether we've sorted the slices.
    30  	sorted bool `codec:"-"`
    31  }
    32  
    33  // init verifies that we haven't changed the number of fields, so that if we
    34  // do, the engineer can take a good, long look at what they've done.
    35  func init() {
    36  	if reflect.ValueOf(Handle{}).NumField() != 7 {
    37  		panic(errors.New(
    38  			"Unexpected number of fields in Handle; please update the check " +
    39  				"above and ensure that Handle.DeepEqual() accounts for the " +
    40  				"new field"))
    41  	}
    42  }
    43  
    44  // errNoWriters is the error returned by MakeHandle if it is
    45  // passed an empty list of writers.
    46  var errNoWriters = errors.New("Cannot make TLF handle with no writers; need rekey?")
    47  
    48  // errInvalidWriter is the error returned by MakeHandle if it
    49  // is passed an invalid writer.
    50  var errInvalidWriter = errors.New("Cannot make TLF handle with invalid writer")
    51  
    52  // errInvalidReader is the error returned by MakeHandle if it
    53  // is passed an invalid reader.
    54  var errInvalidReader = errors.New("Cannot make TLF handle with invalid reader")
    55  
    56  // UIDList can be used to lexicographically sort UserOrTeamIDs.
    57  type UIDList []keybase1.UserOrTeamID
    58  
    59  func (u UIDList) Len() int {
    60  	return len(u)
    61  }
    62  
    63  func (u UIDList) Less(i, j int) bool {
    64  	return u[i].Less(u[j])
    65  }
    66  
    67  func (u UIDList) Swap(i, j int) {
    68  	u[i], u[j] = u[j], u[i]
    69  }
    70  
    71  // SocialAssertionList can be used to lexicographically sort SocialAssertions.
    72  type SocialAssertionList []keybase1.SocialAssertion
    73  
    74  func (u SocialAssertionList) Len() int {
    75  	return len(u)
    76  }
    77  
    78  func (u SocialAssertionList) Less(i, j int) bool {
    79  	si := u[i].String()
    80  	sj := u[j].String()
    81  	return si < sj
    82  }
    83  
    84  func (u SocialAssertionList) Swap(i, j int) {
    85  	u[i], u[j] = u[j], u[i]
    86  }
    87  
    88  // MakeHandle creates a Handle from the given list of
    89  // readers and writers. If the given reader list contains just
    90  // keybase1.PUBLIC_UID, then the returned handle will be for a public
    91  // folder. Otherwise, it will be private. PUBLIC_UID shouldn't be in
    92  // any list in any other case.
    93  func MakeHandle(
    94  	writers, readers []keybase1.UserOrTeamID,
    95  	unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion,
    96  	extensions []HandleExtension) (Handle, error) {
    97  	if len(writers) == 0 {
    98  		if len(unresolvedWriters) == 1 {
    99  			return Handle{}, errors.Errorf(
   100  				"No resolution found for %s", unresolvedWriters[0])
   101  		} else if len(unresolvedWriters) > 1 {
   102  			return Handle{}, errors.Errorf(
   103  				"No resolutions found for %v", unresolvedWriters)
   104  		}
   105  		return Handle{}, errNoWriters
   106  	}
   107  
   108  	if writers[0].IsTeamOrSubteam() {
   109  		// Right now we only support single-team private TLFs.
   110  		if len(writers) > 1 || len(unresolvedWriters) != 0 {
   111  			return Handle{}, errInvalidWriter
   112  		} else if len(readers) != 0 || len(unresolvedReaders) != 0 {
   113  			return Handle{}, errInvalidReader
   114  		}
   115  	}
   116  
   117  	for i, w := range writers {
   118  		if w == keybase1.PUBLIC_UID {
   119  			return Handle{}, errInvalidWriter
   120  		} else if i > 0 && w.IsTeamOrSubteam() {
   121  			return Handle{}, errInvalidWriter
   122  		}
   123  	}
   124  
   125  	// If we have more than one reader, none of them should be the
   126  	// public UID.  And no readers should be a team.
   127  	checkPublic := (len(readers) + len(unresolvedReaders)) > 1
   128  	for _, r := range readers {
   129  		if checkPublic && r == keybase1.PUBLIC_UID {
   130  			return Handle{}, errInvalidReader
   131  		} else if r.IsTeamOrSubteam() {
   132  			return Handle{}, errInvalidReader
   133  		}
   134  	}
   135  
   136  	// TODO: Check for overlap between readers and writers, and
   137  	// for duplicates.
   138  
   139  	writersCopy := make([]keybase1.UserOrTeamID, len(writers))
   140  	copy(writersCopy, writers)
   141  	sort.Sort(UIDList(writersCopy))
   142  
   143  	var readersCopy []keybase1.UserOrTeamID
   144  	if len(readers) > 0 {
   145  		readersCopy = make([]keybase1.UserOrTeamID, len(readers))
   146  		copy(readersCopy, readers)
   147  		sort.Sort(UIDList(readersCopy))
   148  	}
   149  
   150  	var unresolvedWritersCopy []keybase1.SocialAssertion
   151  	if len(unresolvedWriters) > 0 {
   152  		unresolvedWritersCopy = make([]keybase1.SocialAssertion, len(unresolvedWriters))
   153  		copy(unresolvedWritersCopy, unresolvedWriters)
   154  		sort.Sort(SocialAssertionList(unresolvedWritersCopy))
   155  	}
   156  
   157  	var unresolvedReadersCopy []keybase1.SocialAssertion
   158  	if len(unresolvedReaders) > 0 {
   159  		unresolvedReadersCopy = make([]keybase1.SocialAssertion, len(unresolvedReaders))
   160  		copy(unresolvedReadersCopy, unresolvedReaders)
   161  		sort.Sort(SocialAssertionList(unresolvedReadersCopy))
   162  	}
   163  
   164  	conflictInfo, finalizedInfo := HandleExtensionList(extensions).Splat()
   165  
   166  	return Handle{
   167  		Writers:           writersCopy,
   168  		Readers:           readersCopy,
   169  		UnresolvedWriters: unresolvedWritersCopy,
   170  		UnresolvedReaders: unresolvedReadersCopy,
   171  		ConflictInfo:      conflictInfo,
   172  		FinalizedInfo:     finalizedInfo,
   173  		sorted:            true,
   174  	}, nil
   175  }
   176  
   177  // IsBackedByTeam returns true if h represents a TLF backed by a team. It could
   178  // be either a SingleTeam TLF or a private/public TLF backed by an implicit
   179  // team.
   180  func (h Handle) IsBackedByTeam() bool {
   181  	if len(h.Writers) != 1 ||
   182  		len(h.Readers) != 0 ||
   183  		len(h.UnresolvedReaders) != 0 ||
   184  		len(h.UnresolvedWriters) != 0 {
   185  		return false
   186  	}
   187  	return h.Writers[0].IsTeamOrSubteam()
   188  }
   189  
   190  // Type returns the type of TLF this Handle represents.
   191  func (h Handle) Type() Type {
   192  	if len(h.Readers) == 1 &&
   193  		h.Readers[0].Equal(keybase1.PublicUID.AsUserOrTeam()) {
   194  		return Public
   195  	} else if len(h.Writers) == 1 && h.Writers[0].IsTeamOrSubteam() {
   196  		return SingleTeam
   197  	}
   198  	return Private
   199  }
   200  
   201  // TypeForKeying returns the keying type for the handle h.
   202  func (h Handle) TypeForKeying() KeyingType {
   203  	if h.IsBackedByTeam() {
   204  		return TeamKeying
   205  	}
   206  	return h.Type().ToKeyingType()
   207  }
   208  
   209  func (h Handle) findUserInList(user keybase1.UserOrTeamID,
   210  	users []keybase1.UserOrTeamID) bool {
   211  	for _, u := range users {
   212  		if u == user {
   213  			return true
   214  		}
   215  	}
   216  	return false
   217  }
   218  
   219  // IsWriter returns whether or not the given user is a writer for the
   220  // top-level folder represented by this Handle.
   221  func (h Handle) IsWriter(user keybase1.UserOrTeamID) bool {
   222  	if h.TypeForKeying() == TeamKeying {
   223  		panic("Can't call Handle.IsWriter() for a single team TLF")
   224  	}
   225  	return h.findUserInList(user, h.Writers)
   226  }
   227  
   228  // IsReader returns whether or not the given user is a reader for the
   229  // top-level folder represented by this Handle.
   230  func (h Handle) IsReader(user keybase1.UserOrTeamID) bool {
   231  	if h.TypeForKeying() == TeamKeying {
   232  		panic("Can't call Handle.IsReader() for a single team TLF")
   233  	}
   234  	return h.TypeForKeying() == PublicKeying ||
   235  		h.findUserInList(user, h.Readers) ||
   236  		h.IsWriter(user)
   237  }
   238  
   239  // ResolvedUsers returns the concatenation of h.Writers and h.Readers,
   240  // except if the handle is public, the returned list won't contain
   241  // PUBLIC_UID.
   242  func (h Handle) ResolvedUsers() []keybase1.UserOrTeamID {
   243  	var resolvedUsers []keybase1.UserOrTeamID
   244  	resolvedUsers = append(resolvedUsers, h.Writers...)
   245  	if h.TypeForKeying() == PrivateKeying {
   246  		resolvedUsers = append(resolvedUsers, h.Readers...)
   247  	}
   248  	return resolvedUsers
   249  }
   250  
   251  // HasUnresolvedUsers returns true if this handle has any unresolved
   252  // writers or readers.
   253  func (h Handle) HasUnresolvedUsers() bool {
   254  	return len(h.UnresolvedWriters) > 0 || len(h.UnresolvedReaders) > 0
   255  }
   256  
   257  // UnresolvedUsers returns the concatenation of h.UnresolvedWriters
   258  // and h.UnresolvedReaders.
   259  func (h Handle) UnresolvedUsers() []keybase1.SocialAssertion {
   260  	var unresolvedUsers []keybase1.SocialAssertion
   261  	unresolvedUsers = append(unresolvedUsers, h.UnresolvedWriters...)
   262  	unresolvedUsers = append(unresolvedUsers, h.UnresolvedReaders...)
   263  	return unresolvedUsers
   264  }
   265  
   266  func uidSliceToSet(s []keybase1.UserOrTeamID) map[keybase1.UserOrTeamID]bool {
   267  	m := make(map[keybase1.UserOrTeamID]bool, len(s))
   268  	for _, u := range s {
   269  		m[u] = true
   270  	}
   271  	return m
   272  }
   273  
   274  func assertionSliceToSet(s []keybase1.SocialAssertion) map[keybase1.SocialAssertion]bool {
   275  	m := make(map[keybase1.SocialAssertion]bool, len(s))
   276  	for _, u := range s {
   277  		m[u] = true
   278  	}
   279  	return m
   280  }
   281  
   282  func resolveAssertions(
   283  	assertions map[keybase1.SocialAssertion]keybase1.UID,
   284  	unresolved []keybase1.SocialAssertion, resolved []keybase1.UserOrTeamID) (
   285  	map[keybase1.UserOrTeamID]bool, []keybase1.SocialAssertion) {
   286  	resolvedMap := uidSliceToSet(resolved)
   287  	unresolvedMap := assertionSliceToSet(unresolved)
   288  	for a, u := range assertions {
   289  		if unresolvedMap[a] {
   290  			resolvedMap[u.AsUserOrTeam()] = true
   291  			delete(unresolvedMap, a)
   292  		}
   293  	}
   294  	return resolvedMap, assertionSetToSlice(unresolvedMap)
   295  }
   296  
   297  func uidSetToSlice(m map[keybase1.UserOrTeamID]bool) (
   298  	s []keybase1.UserOrTeamID) {
   299  	for u := range m {
   300  		s = append(s, u)
   301  	}
   302  	return s
   303  }
   304  
   305  func assertionSetToSlice(m map[keybase1.SocialAssertion]bool) (s []keybase1.SocialAssertion) {
   306  	for u := range m {
   307  		s = append(s, u)
   308  	}
   309  	return s
   310  }
   311  
   312  // ResolveAssertions creates a new Handle given an existing one with
   313  // while resolving the passed assertions.
   314  func (h Handle) ResolveAssertions(
   315  	assertions map[keybase1.SocialAssertion]keybase1.UID) Handle {
   316  	if len(assertions) == 0 || (len(h.UnresolvedWriters) == 0 && len(h.UnresolvedReaders) == 0) || h.IsFinal() {
   317  		return h
   318  	}
   319  	var resolvedWriters, resolvedReaders map[keybase1.UserOrTeamID]bool
   320  	resolvedWriters, h.UnresolvedWriters = resolveAssertions(assertions, h.UnresolvedWriters, h.Writers)
   321  	resolvedReaders, h.UnresolvedReaders = resolveAssertions(assertions, h.UnresolvedReaders, h.Readers)
   322  	h.Writers = uidSetToSlice(resolvedWriters)
   323  	for _, u := range h.Writers {
   324  		delete(resolvedReaders, u)
   325  	}
   326  	h.Readers = uidSetToSlice(resolvedReaders)
   327  	sort.Sort(UIDList(h.Writers))
   328  	sort.Sort(UIDList(h.Readers))
   329  	sort.Sort(SocialAssertionList(h.UnresolvedWriters))
   330  	sort.Sort(SocialAssertionList(h.UnresolvedReaders))
   331  	h.sorted = true
   332  	return h
   333  }
   334  
   335  // Extensions returns a list of extensions for the given handle.
   336  func (h Handle) Extensions() (extensions []HandleExtension) {
   337  	if h.ConflictInfo != nil {
   338  		extensions = append(extensions, *h.ConflictInfo)
   339  	}
   340  	if h.FinalizedInfo != nil {
   341  		extensions = append(extensions, *h.FinalizedInfo)
   342  	}
   343  	return extensions
   344  }
   345  
   346  // IsFinal returns true if the handle has been finalized.
   347  func (h Handle) IsFinal() bool {
   348  	return h.FinalizedInfo != nil
   349  }
   350  
   351  // IsConflict returns true if the handle is a conflict handle.
   352  func (h Handle) IsConflict() bool {
   353  	return h.ConflictInfo != nil
   354  }
   355  
   356  // DeepEqual returns true if the handle is equal to another handle.
   357  // This can mutate the Handle in that it might sort its Writers,
   358  // Readers, UnresolvedWriters, and UnresolvedReaders.
   359  func (h *Handle) DeepEqual(other Handle) bool {
   360  	if len(h.Writers) != len(other.Writers) {
   361  		return false
   362  	}
   363  	if len(h.UnresolvedWriters) != len(other.UnresolvedWriters) {
   364  		return false
   365  	}
   366  	if len(h.Readers) != len(other.Readers) {
   367  		return false
   368  	}
   369  	if len(h.UnresolvedReaders) != len(other.UnresolvedReaders) {
   370  		return false
   371  	}
   372  
   373  	if !h.sorted {
   374  		sort.Sort(UIDList(h.Writers))
   375  		sort.Sort(UIDList(h.Readers))
   376  		sort.Sort(SocialAssertionList(h.UnresolvedWriters))
   377  		sort.Sort(SocialAssertionList(h.UnresolvedReaders))
   378  		h.sorted = true
   379  	}
   380  	if !other.sorted {
   381  		sort.Sort(UIDList(other.Writers))
   382  		sort.Sort(UIDList(other.Readers))
   383  		sort.Sort(SocialAssertionList(other.UnresolvedWriters))
   384  		sort.Sort(SocialAssertionList(other.UnresolvedReaders))
   385  	}
   386  
   387  	for i, v := range h.Writers {
   388  		if other.Writers[i] != v {
   389  			return false
   390  		}
   391  	}
   392  	for i, v := range h.UnresolvedWriters {
   393  		if other.UnresolvedWriters[i] != v {
   394  			return false
   395  		}
   396  	}
   397  	for i, v := range h.Readers {
   398  		if other.Readers[i] != v {
   399  			return false
   400  		}
   401  	}
   402  	for i, v := range h.UnresolvedReaders {
   403  		if other.UnresolvedReaders[i] != v {
   404  			return false
   405  		}
   406  	}
   407  	if h.IsConflict() != other.IsConflict() {
   408  		return false
   409  	}
   410  	if h.IsFinal() != other.IsFinal() {
   411  		return false
   412  	}
   413  	if h.ConflictInfo != nil &&
   414  		h.ConflictInfo.String() != other.ConflictInfo.String() {
   415  		return false
   416  	}
   417  	if h.FinalizedInfo != nil &&
   418  		h.FinalizedInfo.String() != other.FinalizedInfo.String() {
   419  		return false
   420  	}
   421  
   422  	return true
   423  }
   424  
   425  // checkUIDEquality returns true if `a` and `b` contain the same IDs,
   426  // regardless of order.  However, if `a` contains duplicates, this
   427  // function may return an incorrect value.
   428  func checkUIDEquality(a, b []keybase1.UserOrTeamID) bool {
   429  	aMap := make(map[keybase1.UserOrTeamID]bool)
   430  	for _, u := range a {
   431  		aMap[u] = true
   432  	}
   433  	for _, u := range b {
   434  		if !aMap[u] {
   435  			return false
   436  		}
   437  		delete(aMap, u)
   438  	}
   439  	return len(aMap) == 0
   440  }
   441  
   442  // ResolvedUsersEqual checks whether the resolved users of this TLF
   443  // matches the provided lists of writers and readers.
   444  func (h *Handle) ResolvedUsersEqual(
   445  	writers []keybase1.UserOrTeamID, readers []keybase1.UserOrTeamID) bool {
   446  	return checkUIDEquality(h.Writers, writers) &&
   447  		checkUIDEquality(h.Readers, readers)
   448  }