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 }