github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/teams/chain.go (about) 1 package teams 2 3 import ( 4 "errors" 5 "fmt" 6 7 "golang.org/x/net/context" 8 9 "github.com/keybase/client/go/libkb" 10 "github.com/keybase/client/go/protocol/keybase1" 11 "github.com/keybase/client/go/teams/hidden" 12 jsonw "github.com/keybase/go-jsonw" 13 ) 14 15 // Create a new user/version pair. 16 func NewUserVersion(uid keybase1.UID, eldestSeqno keybase1.Seqno) keybase1.UserVersion { 17 return keybase1.NewUserVersion(uid, eldestSeqno) 18 } 19 20 const TeamSigChainPlayerSupportedLinkVersion = 2 21 22 // Accessor wrapper for keybase1.TeamSigChainState 23 type TeamSigChainState struct { 24 inner keybase1.TeamSigChainState 25 hidden *keybase1.HiddenTeamChain 26 } 27 28 func newTeamSigChainState(t Teamer) TeamSigChainState { 29 ret := TeamSigChainState{hidden: t.HiddenChain()} 30 if t.MainChain() != nil { 31 ret.inner = t.MainChain().Chain 32 } 33 return ret 34 } 35 36 func (t TeamSigChainState) DeepCopy() TeamSigChainState { 37 ret := TeamSigChainState{ 38 inner: t.inner.DeepCopy(), 39 } 40 if t.hidden != nil { 41 tmp := t.hidden.DeepCopy() 42 ret.hidden = &tmp 43 } 44 return ret 45 } 46 47 func (t TeamSigChainState) DeepCopyToPtr() *TeamSigChainState { 48 t2 := t.DeepCopy() 49 return &t2 50 } 51 52 func (t TeamSigChainState) GetID() keybase1.TeamID { 53 return t.inner.Id 54 } 55 56 func (t TeamSigChainState) IsSubteam() bool { 57 return t.inner.ParentID != nil 58 } 59 60 func (t TeamSigChainState) IsImplicit() bool { 61 return t.inner.Implicit 62 } 63 64 func (t TeamSigChainState) IsPublic() bool { 65 return t.inner.Public 66 } 67 68 func (t TeamSigChainState) IsOpen() bool { 69 return t.inner.Open 70 } 71 72 func (t TeamSigChainState) LatestLastNamePart() keybase1.TeamNamePart { 73 return t.inner.NameLog[len(t.inner.NameLog)-1].LastPart 74 } 75 76 // Only non-nil if this is a subteam. 77 func (t TeamSigChainState) GetParentID() *keybase1.TeamID { 78 return t.inner.ParentID 79 } 80 81 func (t TeamSigChainState) GetLatestSeqno() keybase1.Seqno { 82 return t.inner.LastSeqno 83 } 84 85 func (t TeamSigChainState) GetLatestHiddenSeqno() keybase1.Seqno { 86 if t.hidden == nil { 87 return keybase1.Seqno(0) 88 } 89 return t.hidden.Last 90 } 91 92 func (t TeamSigChainState) GetLatestLinkID() keybase1.LinkID { 93 return t.inner.LastLinkID 94 } 95 96 func (t TeamSigChainState) GetLatestHighSeqno() keybase1.Seqno { 97 return t.inner.LastHighSeqno 98 } 99 100 func (t TeamSigChainState) GetLatestHighLinkID() keybase1.LinkID { 101 return t.inner.LastHighLinkID 102 } 103 104 func (t TeamSigChainState) GetLatestLibkbLinkID() (libkb.LinkID, error) { 105 return libkb.ImportLinkID(t.GetLatestLinkID()) 106 } 107 108 func (t TeamSigChainState) GetLinkIDBySeqno(seqno keybase1.Seqno) (keybase1.LinkID, error) { 109 l1, ok := t.inner.LinkIDs[seqno] 110 if !ok { 111 return l1, fmt.Errorf("seqno %v not in chain", seqno) 112 } 113 return l1, nil 114 } 115 116 func (t TeamSigChainState) GetLibkbLinkIDBySeqno(seqno keybase1.Seqno) (l2 libkb.LinkID, err error) { 117 l1, err := t.GetLinkIDBySeqno(seqno) 118 if err != nil { 119 return l2, err 120 } 121 return libkb.ImportLinkID(l1) 122 } 123 124 func (t TeamSigChainState) GetLatestGeneration() keybase1.PerTeamKeyGeneration { 125 ret := t.inner.MaxPerTeamKeyGeneration 126 if h := t.hidden.MaxReaderPerTeamKeyGeneration(); h > ret { 127 ret = h 128 } 129 return ret 130 } 131 132 func (t TeamSigChainState) GetLatestKBFSGeneration(appType keybase1.TeamApplication) (int, error) { 133 info, ok := t.inner.TlfLegacyUpgrade[appType] 134 if !ok { 135 return 0, errors.New("no KBFS keys available") 136 } 137 return info.LegacyGeneration, nil 138 } 139 140 func (t TeamSigChainState) makeHiddenRatchet(mctx libkb.MetaContext) (ret *hidden.Ratchet, err error) { 141 return hidden.MakeRatchet(mctx, t.hidden) 142 } 143 144 func (t TeamSigChainState) GetUserRole(user keybase1.UserVersion) (keybase1.TeamRole, error) { 145 return t.getUserRole(user), nil 146 } 147 148 // Get the user's role right after link at seqno was processed. 149 func (t TeamSigChainState) GetUserRoleAtSeqno(user keybase1.UserVersion, seqno keybase1.Seqno) (keybase1.TeamRole, error) { 150 role := keybase1.TeamRole_NONE 151 if seqno <= 0 { 152 return role, fmt.Errorf("seqno %v is less than 1", seqno) 153 } 154 for _, point := range t.inner.UserLog[user] { 155 if point.SigMeta.SigChainLocation.Seqno > seqno { 156 return role, nil 157 } 158 role = point.Role 159 } 160 return role, nil 161 } 162 163 func (t TeamSigChainState) MemberCtime(user keybase1.UserVersion) *keybase1.Time { 164 points := t.inner.UserLog[user] 165 if len(points) == 0 { 166 return nil 167 } 168 // see if the user ever left the team so we return their most recent join 169 // time. 170 for i := len(points) - 1; i > 0; i-- { 171 // if we left the team at some point, return our later join time 172 if points[i].Role == keybase1.TeamRole_NONE { 173 if i < len(points)-1 && points[i+1].Role != keybase1.TeamRole_NONE { 174 return &points[i+1].SigMeta.Time 175 } 176 } 177 } 178 // we never left the team, give our original join time. 179 return &points[0].SigMeta.Time 180 } 181 182 func (t TeamSigChainState) GetUserLogPoint(user keybase1.UserVersion) *keybase1.UserLogPoint { 183 points := t.inner.UserLog[user] 184 if len(points) == 0 { 185 return nil 186 } 187 tmp := points[len(points)-1].DeepCopy() 188 return &tmp 189 } 190 191 func (t TeamSigChainState) GetAdminUserLogPoint(user keybase1.UserVersion) *keybase1.UserLogPoint { 192 ret := t.GetUserLogPoint(user) 193 if ret == nil { 194 return nil 195 } 196 if ret.Role != keybase1.TeamRole_ADMIN && ret.Role != keybase1.TeamRole_OWNER { 197 return nil 198 } 199 return ret 200 } 201 202 func (t TeamSigChainState) getUserRole(user keybase1.UserVersion) keybase1.TeamRole { 203 return t.inner.UserRole(user) 204 } 205 206 func (t TeamSigChainState) GetUserLastJoinTime(user keybase1.UserVersion) (time keybase1.Time, err error) { 207 return t.inner.GetUserLastJoinTime(user) 208 } 209 210 // GetUserLastRoleChangeTime returns the time of the last role change for user 211 // in team. If the user left the team as a last change, the time of such leave 212 // event is returned. If the user was never in the team, then this function 213 // returns time=0 and wasMember=false. 214 func (t TeamSigChainState) GetUserLastRoleChangeTime(user keybase1.UserVersion) (time keybase1.Time, wasMember bool) { 215 return t.inner.GetUserLastRoleChangeTime(user) 216 } 217 218 // NewStyle invites are completed in the `used_invites` field in the change 219 // membership link, can optionally specify an expiration time, and a maximum 220 // number of uses (potentially infinite). 221 func IsNewStyleInvite(invite keybase1.TeamInvite) (bool, error) { 222 return invite.MaxUses != nil, nil 223 } 224 225 // assertBecameAdminAt asserts that the user (uv) became admin at the SigChainLocation given. 226 // Figure out when this admin permission was revoked, if at all. If the promotion event 227 // wasn't found as specified, then return an AdminPermissionError. In addition, we return 228 // a bookend object, in the success case, to convey when the adminship was downgraded, if at all. 229 func (t TeamSigChainState) assertBecameAdminAt(uv keybase1.UserVersion, scl keybase1.SigChainLocation) (ret proofTermBookends, err error) { 230 points := t.inner.UserLog[uv] 231 linkMap := t.inner.LinkIDs 232 for i := len(points) - 1; i >= 0; i-- { 233 point := points[i] 234 if point.SigMeta.SigChainLocation.Eq(scl) { 235 if !point.Role.IsAdminOrAbove() { 236 return ret, NewAdminPermissionError(t.GetID(), uv, "not admin permission") 237 } 238 ret.left = newProofTerm(t.GetID().AsUserOrTeam(), point.SigMeta, linkMap) 239 r := findRoleDowngrade(points[(i+1):], keybase1.TeamRole_ADMIN) 240 if r != nil { 241 tmp := newProofTerm(t.GetID().AsUserOrTeam(), *r, linkMap) 242 ret.right = &tmp 243 } 244 return ret, nil 245 } 246 } 247 return ret, NewAdminPermissionError(t.GetID(), uv, "not found") 248 } 249 250 // Find a point where the role is taken away. 251 func findRoleDowngrade(points []keybase1.UserLogPoint, role keybase1.TeamRole) *keybase1.SignatureMetadata { 252 for _, p := range points { 253 if !p.Role.IsOrAbove(role) { 254 return &p.SigMeta 255 } 256 } 257 return nil 258 } 259 260 // AssertWasRoleOrAboveAt asserts that user `uv` had `role` or above on the 261 // team just after the given SigChainLocation `scl`. 262 // We start at the point given, go backwards until we find a promotion, 263 // then go forwards to make sure there wasn't a demotion before the specified time. 264 // If there was, return a PermissionError. If no adminship was found at all, return a PermissionError. 265 func (t TeamSigChainState) AssertWasRoleOrAboveAt(uv keybase1.UserVersion, 266 role keybase1.TeamRole, scl keybase1.SigChainLocation) (err error) { 267 mkErr := func(format string, args ...interface{}) error { 268 msg := fmt.Sprintf(format, args...) 269 if role.IsOrAbove(keybase1.TeamRole_ADMIN) { 270 return NewAdminPermissionError(t.GetID(), uv, msg) 271 } 272 return NewPermissionError(t.GetID(), uv, msg) 273 } 274 if scl.Seqno < keybase1.Seqno(0) { 275 return mkErr("negative seqno: %v", scl.Seqno) 276 } 277 points := t.inner.UserLog[uv] 278 for i := len(points) - 1; i >= 0; i-- { 279 point := points[i] 280 if err := point.SigMeta.SigChainLocation.Comparable(scl); err != nil { 281 return mkErr(err.Error()) 282 } 283 if point.SigMeta.SigChainLocation.LessThanOrEqualTo(scl) && point.Role.IsOrAbove(role) { 284 // OK great, we found a point with the role in the log that's less than or equal to the given one. 285 // But now we reverse and go forward, and check that it wasn't revoked or downgraded. 286 // If so, that's a problem! 287 if right := findRoleDowngrade(points[(i+1):], role); right != nil && right.SigChainLocation.LessThanOrEqualTo(scl) { 288 return mkErr("%v permission was downgraded too soon!", role) 289 } 290 return nil 291 } 292 } 293 return mkErr("%v role point not found", role) 294 } 295 296 func (t TeamSigChainState) AssertWasWriterAt(uv keybase1.UserVersion, scl keybase1.SigChainLocation) (err error) { 297 return t.AssertWasRoleOrAboveAt(uv, keybase1.TeamRole_WRITER, scl) 298 } 299 300 func (t TeamSigChainState) AssertWasAdminAt(uv keybase1.UserVersion, scl keybase1.SigChainLocation) (err error) { 301 return t.AssertWasRoleOrAboveAt(uv, keybase1.TeamRole_ADMIN, scl) 302 } 303 304 func (t TeamSigChainState) GetUsersWithRole(role keybase1.TeamRole) (res []keybase1.UserVersion, err error) { 305 if role == keybase1.TeamRole_NONE { 306 return nil, errors.New("cannot get users with NONE role") 307 } 308 for uv := range t.inner.UserLog { 309 if t.getUserRole(uv) == role { 310 res = append(res, uv) 311 } 312 } 313 return res, nil 314 } 315 316 func (t TeamSigChainState) GetUsersWithRoleOrAbove(role keybase1.TeamRole) (res []keybase1.UserVersion, err error) { 317 if role == keybase1.TeamRole_NONE { 318 return nil, errors.New("cannot get users with NONE role") 319 } 320 for uv := range t.inner.UserLog { 321 if t.getUserRole(uv).IsOrAbove(role) { 322 res = append(res, uv) 323 } 324 } 325 return res, nil 326 } 327 328 func (t TeamSigChainState) GetLatestUVWithUID(uid keybase1.UID) (res keybase1.UserVersion, err error) { 329 found := false 330 for uv := range t.inner.UserLog { 331 if uv.Uid == uid && t.getUserRole(uv) != keybase1.TeamRole_NONE && (!found || res.EldestSeqno < uv.EldestSeqno) { 332 res = uv 333 found = true 334 } 335 } 336 337 if !found { 338 return res, errors.New("did not find user with given uid") 339 } 340 return res.DeepCopy(), nil 341 } 342 343 func (t TeamSigChainState) GetAllUVsWithUID(uid keybase1.UID) (res []keybase1.UserVersion) { 344 for uv := range t.inner.UserLog { 345 if uv.Uid == uid && t.getUserRole(uv) != keybase1.TeamRole_NONE { 346 res = append(res, uv) 347 } 348 } 349 return res 350 } 351 352 func (t TeamSigChainState) GetAllUVs() (res []keybase1.UserVersion) { 353 return t.inner.GetAllUVs() 354 } 355 356 func (t TeamSigChainState) GetLatestPerTeamKey(mctx libkb.MetaContext) (res keybase1.PerTeamKey, err error) { 357 res, _, err = t.getLatestPerTeamKeyWithMerkleSeqno(mctx) 358 return res, err 359 } 360 361 func (t TeamSigChainState) getLatestPerTeamKeyWithMerkleSeqno(mctx libkb.MetaContext) (res keybase1.PerTeamKey, mr keybase1.MerkleRootV2, err error) { 362 var hk *keybase1.PerTeamKey 363 if t.hidden != nil { 364 hk = t.hidden.MaxReaderPerTeamKey() 365 } 366 var ok bool 367 res, ok = t.inner.PerTeamKeys[t.inner.MaxPerTeamKeyGeneration] 368 369 if hk == nil && ok { 370 return res, t.inner.MerkleRoots[res.Seqno], nil 371 } 372 if !ok && hk != nil { 373 return *hk, t.hidden.MerkleRoots[hk.Seqno], nil 374 } 375 if !ok && hk == nil { 376 // if this happens it's a programming error 377 mctx.Debug("PTK not found error debug dump: inner %+v", t.inner) 378 if t.hidden != nil { 379 mctx.Debug("PTK not found error debug dump: hidden: %+v", *t.hidden) 380 } 381 return res, mr, fmt.Errorf("per-team-key not found for latest generation %d", t.inner.MaxPerTeamKeyGeneration) 382 } 383 if hk.Gen > res.Gen { 384 return *hk, t.hidden.MerkleRoots[hk.Seqno], nil 385 } 386 return res, t.inner.MerkleRoots[res.Seqno], nil 387 } 388 389 // checkNewPTK takes an existing state (t) and checks that a new PTK found (ptk) doesn't clash 390 // against any PTKs already in the state. 391 func (t *TeamSigChainState) checkNewPTK(ptk SCPerTeamKey) error { 392 // If there was no prior state, then the new PTK is OK 393 if t == nil { 394 return nil 395 } 396 gen := ptk.Generation 397 chk := func(existing keybase1.PerTeamKey, found bool) error { 398 if !found { 399 return nil 400 } 401 if !existing.SigKID.Equal(ptk.SigKID) || !existing.EncKID.Equal(ptk.EncKID) { 402 return fmt.Errorf("PTK clash at generation %d", gen) 403 } 404 return nil 405 } 406 if t.hidden != nil { 407 tmp, found := t.hidden.GetReaderPerTeamKeyAtGeneration(gen) 408 if err := chk(tmp, found); err != nil { 409 return err 410 } 411 } 412 tmp, found := t.inner.PerTeamKeys[gen] 413 if err := chk(tmp, found); err != nil { 414 return err 415 } 416 return nil 417 } 418 419 func (t *TeamSigChainState) GetLatestPerTeamKeyCTime() keybase1.UnixTime { 420 return t.inner.PerTeamKeyCTime 421 } 422 423 func (t TeamSigChainState) GetPerTeamKeyAtGeneration(gen keybase1.PerTeamKeyGeneration) (keybase1.PerTeamKey, error) { 424 res, ok := t.inner.PerTeamKeys[gen] 425 if ok { 426 return res, nil 427 } 428 res, ok = t.hidden.GetReaderPerTeamKeyAtGeneration(gen) 429 if ok { 430 return res, nil 431 } 432 return keybase1.PerTeamKey{}, libkb.NotFoundError{Msg: fmt.Sprintf("per-team-key not found for generation %d", gen)} 433 } 434 435 func (t TeamSigChainState) HasAnyStubbedLinks() bool { 436 for _, v := range t.inner.StubbedLinks { 437 if v { 438 return true 439 } 440 } 441 return false 442 } 443 444 // Whether the link has been processed and is not stubbed. 445 func (t TeamSigChainState) IsLinkFilled(seqno keybase1.Seqno) bool { 446 if seqno > t.inner.LastSeqno { 447 return false 448 } 449 if seqno < 0 { 450 return false 451 } 452 return !t.inner.StubbedLinks[seqno] 453 } 454 455 func (t TeamSigChainState) HasStubbedSeqno(seqno keybase1.Seqno) bool { 456 return t.inner.StubbedLinks[seqno] 457 } 458 459 func (t TeamSigChainState) GetSubteamName(id keybase1.TeamID) (*keybase1.TeamName, error) { 460 lastPoint := t.getLastSubteamPoint(id) 461 if lastPoint != nil { 462 return &lastPoint.Name, nil 463 } 464 return nil, fmt.Errorf("subteam not found: %v", id.String()) 465 } 466 467 // Inform the UserLog and Bots of a user's role. 468 // Mutates the UserLog and Bots. 469 // Must be called with seqno's and events in correct order. 470 func (t *TeamSigChainState) inform(u keybase1.UserVersion, role keybase1.TeamRole, sigMeta keybase1.SignatureMetadata) { 471 t.inner.UserLog[u] = append(t.inner.UserLog[u], keybase1.UserLogPoint{ 472 Role: role, 473 SigMeta: sigMeta, 474 }) 475 // Clear an entry in Bots if any 476 if !role.IsRestrictedBot() { 477 delete(t.inner.Bots, u) 478 } 479 } 480 481 func (t *TeamSigChainState) informNewInvite(i keybase1.TeamInvite, teamSigMeta keybase1.TeamSignatureMetadata) { 482 t.inner.InviteMetadatas[i.Id] = keybase1.NewTeamInviteMetadata(i, teamSigMeta) 483 } 484 485 func (t *TeamSigChainState) informCanceledInvite(i keybase1.TeamInviteID, 486 cancelTeamSigMeta keybase1.TeamSignatureMetadata) { 487 inviteMD, ok := t.inner.InviteMetadatas[i] 488 if !ok { 489 return 490 } 491 inviteMD.Status = keybase1.NewTeamInviteMetadataStatusWithCancelled(keybase1.TeamInviteMetadataCancel{ 492 TeamSigMeta: cancelTeamSigMeta, 493 }) 494 t.inner.InviteMetadatas[i] = inviteMD 495 } 496 497 func (t *TeamSigChainState) informCompletedInvite(i keybase1.TeamInviteID, 498 completeTeamSigMeta keybase1.TeamSignatureMetadata) { 499 inviteMD, ok := t.inner.InviteMetadatas[i] 500 if !ok { 501 return 502 } 503 inviteMD.Status = keybase1.NewTeamInviteMetadataStatusWithCompleted(keybase1.TeamInviteMetadataCompleted{ 504 TeamSigMeta: completeTeamSigMeta, 505 }) 506 t.inner.InviteMetadatas[i] = inviteMD 507 } 508 509 func (t *TeamSigChainState) getLastSubteamPoint(id keybase1.TeamID) *keybase1.SubteamLogPoint { 510 if len(t.inner.SubteamLog[id]) > 0 { 511 return &t.inner.SubteamLog[id][len(t.inner.SubteamLog[id])-1] 512 } 513 return nil 514 } 515 516 // Inform the SubteamLog of a subteam name change. 517 // Links must be added in order by seqno for each subteam (asserted here). 518 // Links for different subteams can interleave. 519 // Mutates the SubteamLog. 520 // Name collisions are allowed here. They are allowed because you might 521 // be removed a subteam and then its future name changes and deletion would be stubbed. 522 // See ListSubteams for details. 523 func (t *TeamSigChainState) informSubteam(id keybase1.TeamID, name keybase1.TeamName, seqno keybase1.Seqno) error { 524 lastPoint := t.getLastSubteamPoint(id) 525 if lastPoint != nil && lastPoint.Seqno.Eq(seqno) { 526 return fmt.Errorf("re-entry into subteam log for seqno: %v", seqno) 527 } 528 if lastPoint != nil && seqno < lastPoint.Seqno { 529 return fmt.Errorf("cannot add to subteam log out of order: %v < %v", seqno, lastPoint.Seqno) 530 } 531 if lastPoint != nil && lastPoint.Name.IsNil() { 532 return fmt.Errorf("cannot process subteam rename because %v was deleted at seqno %v", id, lastPoint.Seqno) 533 } 534 t.inner.SubteamLog[id] = append(t.inner.SubteamLog[id], keybase1.SubteamLogPoint{ 535 Name: name, 536 Seqno: seqno, 537 }) 538 return nil 539 } 540 541 // Inform the SubteamLog of a subteam deletion. 542 // Links must be added in order by seqno for each subteam (asserted here). 543 // Links for different subteams can interleave. 544 // Mutates the SubteamLog. 545 func (t *TeamSigChainState) informSubteamDelete(id keybase1.TeamID, seqno keybase1.Seqno) error { 546 lastPoint := t.getLastSubteamPoint(id) 547 if lastPoint != nil && lastPoint.Seqno.Eq(seqno) { 548 return fmt.Errorf("re-entry into subteam log for seqno: %v", seqno) 549 } 550 if lastPoint != nil && seqno < lastPoint.Seqno { 551 return fmt.Errorf("cannot add to subteam log out of order: %v < %v", seqno, lastPoint.Seqno) 552 } 553 // Don't check for deleting the same team twice. Just allow it. 554 t.inner.SubteamLog[id] = append(t.inner.SubteamLog[id], keybase1.SubteamLogPoint{ 555 Seqno: seqno, 556 }) 557 return nil 558 } 559 560 // Only call this on a Team that has been loaded with NeedAdmin. 561 // Otherwise, you might get incoherent answers due to links that 562 // were stubbed over the life of the cached object. 563 // 564 // For subteams that you were removed from, this list may 565 // still include them because your removal was stubbed. 566 // The list will not contain duplicate names. 567 // Since this should only be called when you are an admin, 568 // none of that should really come up, but it's here just to be less fragile. 569 func (t *TeamSigChainState) ListSubteams() (res []keybase1.TeamIDAndName) { 570 return t.inner.ListSubteams() 571 } 572 573 // Check that a subteam rename occurred just so. 574 // That the subteam `subteamID` got a new name `newName` at exactly `seqno` in this, 575 // the parent, chain. 576 // Note this only checks against the last part of `newName` because mid-team renames are such a pain. 577 // This is currently linear in the number of times that subteam has been renamed. 578 // It should be easy to add an index if need be. 579 func (t *TeamSigChainState) SubteamRenameOccurred( 580 subteamID keybase1.TeamID, newName keybase1.TeamName, seqno keybase1.Seqno) error { 581 582 points := t.inner.SubteamLog[subteamID] 583 if len(points) == 0 { 584 return fmt.Errorf("subteam %v has no name log", subteamID) 585 } 586 for _, point := range points { 587 if point.Seqno == seqno { 588 if point.Name.LastPart().Eq(newName.LastPart()) { 589 // found it! 590 return nil 591 } 592 } 593 if point.Seqno > seqno { 594 break 595 } 596 } 597 return fmt.Errorf("subteam %v did not have rename entry in log: %v %v", 598 subteamID, newName, seqno) 599 } 600 601 func (t *TeamSigChainState) ActiveInvites() (ret []keybase1.TeamInviteMetadata) { 602 for _, md := range t.inner.InviteMetadatas { 603 if code, err := md.Status.Code(); err == nil && 604 code == keybase1.TeamInviteMetadataStatusCode_ACTIVE { 605 ret = append(ret, md) 606 } 607 } 608 return ret 609 } 610 611 func (t *TeamSigChainState) NumActiveInvites() int { 612 return len(t.ActiveInvites()) 613 } 614 615 func (t *TeamSigChainState) HasActiveInvite(name keybase1.TeamInviteName, typ keybase1.TeamInviteType) (bool, error) { 616 i, err := t.FindActiveInvite(name, typ) 617 if err != nil { 618 if _, ok := err.(libkb.NotFoundError); ok { 619 return false, nil 620 } 621 return false, err 622 } 623 if i != nil { 624 return true, nil 625 } 626 return false, nil 627 } 628 629 func (t *TeamSigChainState) FindActiveInvite(name keybase1.TeamInviteName, typ keybase1.TeamInviteType) (*keybase1.TeamInvite, error) { 630 for _, inviteMD := range t.ActiveInvites() { 631 if inviteMD.Invite.Name == name && inviteMD.Invite.Type.Eq(typ) { 632 return &inviteMD.Invite, nil 633 } 634 } 635 return nil, libkb.NotFoundError{} 636 } 637 638 func (t *TeamSigChainState) FindActiveInviteString(mctx libkb.MetaContext, name string, typ string) (ret keybase1.TeamInvite, err error) { 639 itype, err := TeamInviteTypeFromString(mctx, typ) 640 if err != nil { 641 return ret, err 642 } 643 invPtr, err := t.FindActiveInvite(keybase1.TeamInviteName(name), itype) 644 switch { 645 case err != nil: 646 return ret, err 647 case invPtr == nil: 648 return ret, libkb.NotFoundError{} 649 } 650 return *invPtr, nil 651 } 652 653 // FindActiveInviteMDByID returns potentially expired invites that have not been 654 // explicitly cancelled, since the sigchain player is agnostic to the concept 655 // of time. We treat invite expiration times as advisory for admin clients 656 // completing invites, but do not check them in the sigchain player. 657 func (t *TeamSigChainState) FindActiveInviteMDByID( 658 id keybase1.TeamInviteID) (inviteMD keybase1.TeamInviteMetadata, found bool) { 659 res, found := t.inner.InviteMetadatas[id] 660 if !found { 661 return inviteMD, false 662 } 663 code, err := res.Status.Code() 664 if err != nil { 665 return inviteMD, false 666 } 667 if code != keybase1.TeamInviteMetadataStatusCode_ACTIVE { 668 return inviteMD, false 669 } 670 return res, true 671 } 672 673 func (t *TeamSigChainState) IsInviteObsolete(id keybase1.TeamInviteID) bool { 674 inviteMD, found := t.inner.InviteMetadatas[id] 675 if !found { 676 return false 677 } 678 code, err := inviteMD.Status.Code() 679 if err != nil { 680 return false 681 } 682 return code == keybase1.TeamInviteMetadataStatusCode_OBSOLETE 683 } 684 685 // FindActiveKeybaseInvite finds and returns a Keybase-type 686 // invite for given UID. Ordering here is not guaranteed, caller 687 // shouldn't assume that returned invite will be the oldest/newest one 688 // for the UID. 689 func (t *TeamSigChainState) FindActiveKeybaseInvite(uid keybase1.UID) (keybase1.TeamInvite, keybase1.UserVersion, bool) { 690 for _, inviteMD := range t.ActiveInvites() { 691 if inviteUv, err := inviteMD.Invite.KeybaseUserVersion(); err == nil { 692 if inviteUv.Uid.Equal(uid) { 693 return inviteMD.Invite, inviteUv, true 694 } 695 } 696 } 697 return keybase1.TeamInvite{}, keybase1.UserVersion{}, false 698 } 699 700 func (t *TeamSigChainState) GetMerkleRoots() map[keybase1.Seqno]keybase1.MerkleRootV2 { 701 return t.inner.MerkleRoots 702 } 703 704 func (t TeamSigChainState) TeamBotSettings() map[keybase1.UserVersion]keybase1.TeamBotSettings { 705 return t.inner.Bots 706 } 707 708 // -------------------------------------------------- 709 710 // AppendChainLink process a chain link. 711 // It must have already been partially verified by TeamLoader. 712 // `reader` is the user who is processing the chain. 713 // `state` is moved into this function. There must exist no live references into it from now on. 714 // If `state` is nil this is the first link of the chain. 715 // `signer` may be nil iff link is stubbed. 716 func AppendChainLink(ctx context.Context, g *libkb.GlobalContext, reader keybase1.UserVersion, state *TeamSigChainState, 717 link *ChainLinkUnpacked, signer *SignerX) (res TeamSigChainState, err error) { 718 t := &teamSigchainPlayer{ 719 Contextified: libkb.NewContextified(g), 720 reader: reader, 721 } 722 var latestSeqno keybase1.Seqno 723 if state != nil { 724 latestSeqno = state.GetLatestSeqno() 725 } 726 res, err = t.appendChainLinkHelper(libkb.NewMetaContext(ctx, g), state, link, signer) 727 if err != nil { 728 return TeamSigChainState{}, NewAppendLinkError(link, latestSeqno, err) 729 } 730 return res, err 731 } 732 733 // InflateLink adds the full inner link for a link that has already been added in stubbed form. 734 // `state` is moved into this function. There must exist no live references into it from now on. 735 func InflateLink(ctx context.Context, g *libkb.GlobalContext, reader keybase1.UserVersion, state TeamSigChainState, 736 link *ChainLinkUnpacked, signer SignerX) (res TeamSigChainState, err error) { 737 if link.isStubbed() { 738 return TeamSigChainState{}, NewStubbedError(link) 739 } 740 if link.Seqno() > state.GetLatestSeqno() { 741 return TeamSigChainState{}, NewInflateErrorWithNote(link, 742 fmt.Sprintf("seqno off the chain %v > %v", link.Seqno(), state.GetLatestSeqno())) 743 } 744 745 // Check the that the link id matches our stubbed record. 746 seenLinkID, err := state.GetLibkbLinkIDBySeqno(link.Seqno()) 747 if err != nil { 748 return TeamSigChainState{}, err 749 } 750 if !seenLinkID.Eq(link.LinkID()) { 751 return TeamSigChainState{}, NewInflateErrorWithNote(link, 752 fmt.Sprintf("link id mismatch: %v != %v", link.LinkID().String(), seenLinkID.String())) 753 } 754 755 // Check that the link has not already been inflated. 756 if _, ok := state.inner.StubbedLinks[link.Seqno()]; !ok { 757 return TeamSigChainState{}, NewInflateErrorWithNote(link, "already inflated") 758 } 759 760 t := &teamSigchainPlayer{ 761 Contextified: libkb.NewContextified(g), 762 reader: reader, 763 } 764 iRes, err := t.addInnerLink(libkb.NewMetaContext(ctx, g), &state, link, signer, true) 765 if err != nil { 766 return TeamSigChainState{}, err 767 } 768 769 delete(iRes.newState.inner.StubbedLinks, link.Seqno()) 770 771 return iRes.newState, nil 772 773 } 774 775 // Helper struct for playing sigchains. 776 type teamSigchainPlayer struct { 777 libkb.Contextified 778 reader keybase1.UserVersion // the user processing the chain 779 } 780 781 // Add a chain link to the end. 782 // `prevState` is moved into this function. There must exist no live references into it from now on. 783 // `signer` may be nil iff link is stubbed. 784 // If `prevState` is nil this is the first chain link. 785 func (t *teamSigchainPlayer) appendChainLinkHelper( 786 mctx libkb.MetaContext, prevState *TeamSigChainState, link *ChainLinkUnpacked, signer *SignerX) ( 787 res TeamSigChainState, err error) { 788 789 err = t.checkOuterLink(mctx.Ctx(), prevState, link) 790 if err != nil { 791 return res, fmt.Errorf("team sigchain outer link: %s", err) 792 } 793 794 var newState *TeamSigChainState 795 if link.isStubbed() { 796 if prevState == nil { 797 return res, NewStubbedErrorWithNote(link, "first link cannot be stubbed") 798 } 799 newState2 := prevState.DeepCopy() 800 newState = &newState2 801 } else { 802 if signer == nil || !signer.signer.Uid.Exists() { 803 return res, NewInvalidLink(link, "signing user not provided for team link") 804 } 805 iRes, err := t.addInnerLink(mctx, prevState, link, *signer, false) 806 if err != nil { 807 return res, err 808 } 809 newState = &iRes.newState 810 } 811 812 newState.inner.LastSeqno = link.Seqno() 813 newState.inner.LastLinkID = link.LinkID().Export() 814 newState.inner.LinkIDs[link.Seqno()] = link.LinkID().Export() 815 816 if link.isStubbed() { 817 newState.inner.StubbedLinks[link.Seqno()] = true 818 } 819 820 // Store the head merkle sequence to the DB, for use in the audit mechanism. 821 // For the purposes of testing, we disable this feature, so we can check 822 // the lazy-repopulation scheme for old stored teams (without a full cache bust). 823 if link.Seqno() == keybase1.Seqno(1) && !link.isStubbed() && !t.G().Env.Test.TeamNoHeadMerkleStore { 824 tmp := link.inner.Body.MerkleRoot.ToMerkleRootV2() 825 newState.inner.HeadMerkle = &tmp 826 } 827 828 if !link.isStubbed() && newState.inner.MerkleRoots != nil { 829 newState.inner.MerkleRoots[link.Seqno()] = link.inner.Body.MerkleRoot.ToMerkleRootV2() 830 } 831 832 return *newState, nil 833 } 834 835 func (t *teamSigchainPlayer) checkOuterLink(ctx context.Context, prevState *TeamSigChainState, link *ChainLinkUnpacked) (err error) { 836 if prevState == nil { 837 if link.Seqno() != 1 { 838 return NewUnexpectedSeqnoError(keybase1.Seqno(1), link.Seqno()) 839 } 840 } else { 841 if link.Seqno() != prevState.inner.LastSeqno+1 { 842 return NewUnexpectedSeqnoError(prevState.inner.LastSeqno+1, link.Seqno()) 843 } 844 } 845 846 if prevState == nil { 847 if len(link.Prev()) != 0 { 848 return fmt.Errorf("expected outer nil prev but got:%s", link.Prev()) 849 } 850 } else { 851 prevStateLastLinkID, err := libkb.ImportLinkID(prevState.inner.LastLinkID) 852 if err != nil { 853 return fmt.Errorf("invalid prev last link id: %v", err) 854 } 855 if !link.Prev().Eq(prevStateLastLinkID) { 856 return fmt.Errorf("wrong outer prev: %s != %s", link.Prev(), prevState.inner.LastLinkID) 857 } 858 } 859 860 return nil 861 } 862 863 type checkInnerLinkResult struct { 864 newState TeamSigChainState 865 } 866 867 // Check and add the inner link. 868 // `isInflate` is false if this is a new link and true if it is a link which has already been added as stubbed. 869 // Does not modify `prevState` but returns a new state. 870 func (t *teamSigchainPlayer) addInnerLink(mctx libkb.MetaContext, 871 prevState *TeamSigChainState, link *ChainLinkUnpacked, signer SignerX, 872 isInflate bool) (res checkInnerLinkResult, err error) { 873 874 if link.inner == nil { 875 return res, NewStubbedError(link) 876 } 877 payload := *link.inner 878 879 if !signer.signer.Uid.Exists() { 880 return res, NewInvalidLink(link, "empty link signer") 881 } 882 883 // This may be superfluous. 884 err = link.AssertInnerOuterMatch() 885 if err != nil { 886 return res, err 887 } 888 889 // completely ignore these fields 890 _ = payload.ExpireIn 891 _ = payload.SeqType 892 893 if payload.Tag != "signature" { 894 return res, NewInvalidLink(link, "unrecognized tag: '%s'", payload.Tag) 895 } 896 897 if payload.Body.Team == nil { 898 return res, NewInvalidLink(link, "missing team section") 899 } 900 team := payload.Body.Team 901 902 if len(team.ID) == 0 { 903 return res, NewInvalidLink(link, "missing team id") 904 } 905 teamID, err := keybase1.TeamIDFromString(string(team.ID)) 906 if err != nil { 907 return res, err 908 } 909 910 if teamID.IsPublic() != team.Public { 911 return res, fmt.Errorf("link specified public:%v but ID is public:%v", 912 team.Public, teamID.IsPublic()) 913 } 914 915 if prevState != nil && !prevState.inner.Id.Equal(teamID) { 916 return res, fmt.Errorf("wrong team id: %s != %s", teamID.String(), prevState.inner.Id.String()) 917 } 918 919 if prevState != nil && prevState.IsImplicit() != team.Implicit { 920 return res, fmt.Errorf("link specified implicit:%v but team was already implicit:%v", 921 team.Implicit, prevState.IsImplicit()) 922 } 923 924 if prevState != nil && prevState.IsPublic() != team.Public { 925 return res, fmt.Errorf("link specified public:%v but team was already public:%v", 926 team.Implicit, prevState.IsImplicit()) 927 } 928 929 if team.Public && (!team.Implicit || (prevState != nil && !prevState.IsImplicit())) { 930 return res, fmt.Errorf("public non-implicit teams are not supported") 931 } 932 933 err = t.checkSeqnoToAdd(prevState, link.Seqno(), isInflate) 934 if err != nil { 935 return res, err 936 } 937 // When isInflate then it is likely prevSeqno != prevState.GetLatestSeqno() 938 prevSeqno := link.Seqno() - 1 939 940 allowInflate := func(allow bool) error { 941 if isInflate && !allow { 942 return fmt.Errorf("inflating link type not supported: %v", payload.Body.Type) 943 } 944 return nil 945 } 946 allowInImplicitTeam := func(allow bool) error { 947 if team.Implicit && !allow { 948 return NewImplicitTeamOperationError(payload.Body.Type) 949 } 950 return nil 951 } 952 enforceFirstInChain := func(firstInChain bool) error { 953 if firstInChain { 954 if prevState != nil { 955 return fmt.Errorf("link type '%s' unexpected at seqno:%v", payload.Body.Type, prevState.inner.LastSeqno+1) 956 } 957 } else { 958 if prevState == nil { 959 return fmt.Errorf("link type '%s' unexpected at beginning", payload.Body.Type) 960 } 961 } 962 return nil 963 } 964 enforceGeneric := func(name string, rule Tristate, hasReal bool) error { 965 switch rule { 966 case TristateDisallow: 967 if hasReal { 968 return fmt.Errorf("sigchain link contains unexpected '%s'", name) 969 } 970 case TristateRequire: 971 if !hasReal { 972 return fmt.Errorf("sigchain link missing %s", name) 973 } 974 case TristateOptional: 975 default: 976 return fmt.Errorf("unsupported tristate (fault): %v", rule) 977 } 978 return nil 979 } 980 enforce := func(rules LinkRules) error { 981 return libkb.PickFirstError( 982 enforceGeneric("name", rules.Name, team.Name != nil), 983 enforceGeneric("members", rules.Members, team.Members != nil), 984 enforceGeneric("parent", rules.Parent, team.Parent != nil), 985 enforceGeneric("subteam", rules.Subteam, team.Subteam != nil), 986 enforceGeneric("per-team-key", rules.PerTeamKey, team.PerTeamKey != nil), 987 enforceGeneric("admin", rules.Admin, team.Admin != nil), 988 enforceGeneric("invites", rules.Invites, team.Invites != nil), 989 enforceGeneric("completed-invites", rules.CompletedInvites, team.CompletedInvites != nil), 990 enforceGeneric("settings", rules.Settings, team.Settings != nil), 991 enforceGeneric("kbfs", rules.KBFS, team.KBFS != nil), 992 enforceGeneric("box-summary-hash", rules.BoxSummaryHash, team.BoxSummaryHash != nil), 993 enforceGeneric("bot_settings", rules.BotSettings, team.BotSettings != nil), 994 allowInImplicitTeam(rules.AllowInImplicitTeam), 995 allowInflate(rules.AllowInflate), 996 enforceFirstInChain(rules.FirstInChain), 997 ) 998 } 999 1000 checkAdmin := func(op string) (signerIsExplicitOwner bool, err error) { 1001 signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno) 1002 if err != nil { 1003 signerRole = keybase1.TeamRole_NONE 1004 } 1005 signerIsExplicitOwner = signerRole == keybase1.TeamRole_OWNER 1006 if signerRole.IsAdminOrAbove() || signer.implicitAdmin { 1007 return signerIsExplicitOwner, nil 1008 } 1009 return signerIsExplicitOwner, fmt.Errorf("link signer does not have permission to %s: %v is a %v", op, signer, signerRole) 1010 } 1011 1012 checkExplicitWriter := func(op string) (err error) { 1013 signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno) 1014 if err != nil { 1015 signerRole = keybase1.TeamRole_NONE 1016 } 1017 if !signerRole.IsWriterOrAbove() { 1018 return fmt.Errorf("link signer does not have writer permission to %s: %v is a %v", op, signer, signerRole) 1019 } 1020 return nil 1021 } 1022 1023 moveState := func() { 1024 // Move prevState to res.newState. 1025 // Re-use the object without deep copying. There must be no other live references into prevState. 1026 res.newState = *prevState 1027 prevState = nil 1028 } 1029 isHighLink := false 1030 1031 teamSigMeta := keybase1.NewTeamSigMeta(payload.SignatureMetadata(), signer.signer) 1032 1033 switch libkb.LinkType(payload.Body.Type) { 1034 case libkb.LinkTypeTeamRoot: 1035 isHighLink = true 1036 err = enforce(LinkRules{ 1037 Name: TristateRequire, 1038 Members: TristateRequire, 1039 PerTeamKey: TristateRequire, 1040 BoxSummaryHash: TristateOptional, 1041 Invites: TristateOptional, 1042 Settings: TristateOptional, 1043 AllowInImplicitTeam: true, 1044 FirstInChain: true, 1045 }) 1046 if err != nil { 1047 return res, err 1048 } 1049 1050 // Check the team name 1051 teamName, err := keybase1.TeamNameFromString(string(*team.Name)) 1052 if err != nil { 1053 return res, err 1054 } 1055 if !teamName.IsRootTeam() { 1056 return res, fmt.Errorf("root team has subteam name: %s", teamName) 1057 } 1058 1059 // Whether this is an implicit team 1060 isImplicit := teamName.IsImplicit() 1061 if isImplicit != team.Implicit { 1062 return res, fmt.Errorf("link specified implicit:%v but name specified implicit:%v", 1063 team.Implicit, isImplicit) 1064 } 1065 1066 // Check the team ID 1067 // assert that team_name = hash(team_id) 1068 // this is only true for root teams 1069 if !teamID.Equal(teamName.ToTeamID(team.Public)) { 1070 return res, fmt.Errorf("team id:%s does not match team name:%s", teamID, teamName) 1071 } 1072 if teamID.IsSubTeam() { 1073 return res, fmt.Errorf("malformed root team id") 1074 } 1075 1076 roleUpdates, err := t.sanityCheckMembers(*team.Members, sanityCheckMembersOptions{ 1077 requireOwners: true, 1078 allowRemovals: false, 1079 onlyOwnersOrReaders: isImplicit, 1080 }) 1081 if err != nil { 1082 return res, err 1083 } 1084 1085 perTeamKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, nil) 1086 if err != nil { 1087 return res, err 1088 } 1089 1090 perTeamKeys := make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKey) 1091 perTeamKeys[keybase1.PerTeamKeyGeneration(1)] = perTeamKey 1092 1093 res.newState = TeamSigChainState{ 1094 inner: keybase1.TeamSigChainState{ 1095 Reader: t.reader, 1096 Id: teamID, 1097 Implicit: isImplicit, 1098 Public: team.Public, 1099 RootAncestor: teamName.RootAncestorName(), 1100 NameDepth: teamName.Depth(), 1101 NameLog: []keybase1.TeamNameLogPoint{{ 1102 LastPart: teamName.LastPart(), 1103 Seqno: 1, 1104 }}, 1105 LastSeqno: 1, 1106 LastLinkID: link.LinkID().Export(), 1107 ParentID: nil, 1108 UserLog: make(map[keybase1.UserVersion][]keybase1.UserLogPoint), 1109 SubteamLog: make(map[keybase1.TeamID][]keybase1.SubteamLogPoint), 1110 PerTeamKeys: perTeamKeys, 1111 MaxPerTeamKeyGeneration: keybase1.PerTeamKeyGeneration(1), 1112 PerTeamKeyCTime: keybase1.UnixTime(payload.Ctime), 1113 LinkIDs: make(map[keybase1.Seqno]keybase1.LinkID), 1114 StubbedLinks: make(map[keybase1.Seqno]bool), 1115 InviteMetadatas: make(map[keybase1.TeamInviteID]keybase1.TeamInviteMetadata), 1116 TlfLegacyUpgrade: make(map[keybase1.TeamApplication]keybase1.TeamLegacyTLFUpgradeChainInfo), 1117 MerkleRoots: make(map[keybase1.Seqno]keybase1.MerkleRootV2), 1118 Bots: make(map[keybase1.UserVersion]keybase1.TeamBotSettings), 1119 }} 1120 1121 t.updateMembership(&res.newState, roleUpdates, payload.SignatureMetadata()) 1122 if team.Invites != nil { 1123 if isImplicit { 1124 signerIsExplicitOwner := true 1125 additions, cancelations, err := t.sanityCheckInvites(mctx, signer.signer, signerIsExplicitOwner, 1126 *team.Invites, link.SigID(), sanityCheckInvitesOptions{ 1127 isRootTeam: true, 1128 implicitTeam: isImplicit, 1129 }) 1130 if err != nil { 1131 return res, err 1132 } 1133 t.updateInvites(&res.newState, additions, cancelations, teamSigMeta) 1134 } else { 1135 return res, fmt.Errorf("invites not allowed in root link") 1136 } 1137 } 1138 1139 // check that the signer is an owner 1140 if res.newState.getUserRole(signer.signer) != keybase1.TeamRole_OWNER { 1141 return res, fmt.Errorf("signer is not an owner: %v (%v)", signer, team.Members.Owners) 1142 } 1143 1144 if settings := team.Settings; settings != nil { 1145 err = t.parseTeamSettings(settings, &res.newState) 1146 if err != nil { 1147 return res, err 1148 } 1149 } 1150 case libkb.LinkTypeChangeMembership: 1151 err = enforce(LinkRules{ 1152 Members: TristateRequire, 1153 PerTeamKey: TristateOptional, 1154 Admin: TristateOptional, 1155 CompletedInvites: TristateOptional, 1156 BoxSummaryHash: TristateOptional, 1157 AllowInImplicitTeam: true, 1158 }) 1159 if err != nil { 1160 return res, err 1161 } 1162 1163 // Check that the signer is at least an ADMIN or is an IMPLICIT ADMIN to have permission to make this link. 1164 var signerIsExplicitOwner bool 1165 signerIsExplicitOwner, err = checkAdmin("change membership") 1166 if err != nil { 1167 return res, err 1168 } 1169 1170 roleUpdates, err := t.sanityCheckMembers(*team.Members, sanityCheckMembersOptions{ 1171 disallowOwners: prevState.IsSubteam(), 1172 allowRemovals: true, 1173 onlyOwnersOrReaders: prevState.IsImplicit(), 1174 }) 1175 if err != nil { 1176 return res, err 1177 } 1178 1179 // Only owners can add more owners. 1180 if (len(roleUpdates[keybase1.TeamRole_OWNER]) > 0) && !signerIsExplicitOwner { 1181 return res, fmt.Errorf("non-owner cannot add owners") 1182 } 1183 1184 // Only owners can remove owners. 1185 if t.roleUpdatesDemoteOwners(prevState, roleUpdates) && !signerIsExplicitOwner { 1186 return res, fmt.Errorf("non-owner cannot demote owners") 1187 } 1188 1189 if prevState.IsImplicit() { 1190 // In implicit teams there are only 3 kinds of membership changes allowed: 1191 // 1. Resolve an invite. Adds 1 user and completes 1 invite. 1192 // Though sometimes a new user is not added, due to a conflict. 1193 // 2. Accept a reset user. Adds 1 user and removes 1 user. 1194 // Where the new one has the same UID and role as the old and a greater EldestSeqno. 1195 // 3. Add/remove a bot user. The bot can be a RESTRICTEDBOT or regular BOT member. 1196 1197 // Here's a case that is not straightforward: 1198 // There is an impteam alice,leland%2,bob@twitter. 1199 // Leland resets and then proves bob@twitter. On the team chain alice accepts Leland's reset. 1200 // So she removes leland%2, adds leland%3, and completes the bob@twitter invite. 1201 1202 // Here's another: 1203 // There is an impteam leland#bob@twitter. 1204 // Leland proves bob@twitter. On the team chain leland completes the invite. 1205 // Now it's just leland. 1206 1207 // Check that the invites being completed are all active. 1208 // For non-implicit teams we are more lenient, but here we need the counts to match up. 1209 invitees := make(map[keybase1.UID]bool) 1210 parsedCompletedInvites := make(map[keybase1.TeamInviteID]keybase1.UserVersion) 1211 for inviteID, invitee := range team.CompletedInvites { 1212 _, found := prevState.FindActiveInviteMDByID(inviteID) 1213 if !found { 1214 return res, NewImplicitTeamOperationError("completed invite %v but was not active", 1215 inviteID) 1216 } 1217 uv, err := keybase1.ParseUserVersion(invitee) 1218 if err != nil { 1219 return res, err 1220 } 1221 invitees[uv.Uid] = true 1222 parsedCompletedInvites[inviteID] = uv 1223 } 1224 nCompleted := len(team.CompletedInvites) 1225 1226 // Check these properties: 1227 // - Every removal must come with an addition of a successor. Ignore role. 1228 // - Every addition must either be paired with a removal, or 1229 // resolve an invite. Ignore role when not dealing with bots. 1230 // This is a coarse check that ignores role changes. 1231 1232 type removal struct { 1233 uv keybase1.UserVersion 1234 satisfied bool 1235 } 1236 removals := make(map[keybase1.UID]removal) 1237 for _, uv := range roleUpdates[keybase1.TeamRole_NONE] { 1238 removals[uv.Uid] = removal{uv: uv} 1239 } 1240 var nCompletedExpected int 1241 additions := make(map[keybase1.UID]bool) 1242 // Every addition must either be paired with a removal or resolve an invite. 1243 for _, uv := range append(roleUpdates[keybase1.TeamRole_OWNER], roleUpdates[keybase1.TeamRole_READER]...) { 1244 removal, ok := removals[uv.Uid] 1245 if ok { 1246 if removal.uv.EldestSeqno >= uv.EldestSeqno { 1247 return res, NewImplicitTeamOperationError("replaced with older eldest seqno: %v -> %v", 1248 removal.uv.EldestSeqno, uv.EldestSeqno) 1249 } 1250 removal.satisfied = true 1251 removals[uv.Uid] = removal 1252 if invitees[uv.Uid] && uv.EldestSeqno > removal.uv.EldestSeqno { 1253 // If we are removing someone that is also a completed invite, then it must 1254 // be replacing a reset user with a new version. Expect an invite in this case. 1255 nCompletedExpected++ 1256 additions[uv.Uid] = true 1257 } 1258 } else { 1259 // This is a new user, so must be a completed invite. 1260 nCompletedExpected++ 1261 additions[uv.Uid] = true 1262 } 1263 } 1264 // All removals must have come with successor. 1265 for _, r := range removals { 1266 role := prevState.getUserRole(r.uv) 1267 if !(r.satisfied || role.IsBotLike()) { 1268 return res, NewImplicitTeamOperationError("removal without addition for %v", r.uv) 1269 } 1270 } 1271 // Completed invites that do not bring in new members mean 1272 // SBS consolidations. 1273 for _, uv := range parsedCompletedInvites { 1274 _, ok := additions[uv.Uid] 1275 if !ok { 1276 if prevState.getUserRole(uv) == keybase1.TeamRole_NONE { 1277 return res, NewImplicitTeamOperationError("trying to moot invite but there is no member for %v", uv) 1278 } 1279 nCompleted-- 1280 } 1281 } 1282 // The number of completed invites must match. 1283 if nCompletedExpected != nCompleted { 1284 return res, NewImplicitTeamOperationError("illegal membership change: %d != %d", 1285 nCompletedExpected, nCompleted) 1286 } 1287 } 1288 1289 isHighLink, err = t.roleUpdateChangedHighSet(prevState, roleUpdates) 1290 if err != nil { 1291 return res, fmt.Errorf("could not determine if high user set changed") 1292 } 1293 1294 moveState() 1295 t.updateMembership(&res.newState, roleUpdates, payload.SignatureMetadata()) 1296 1297 if err := t.completeInvites(&res.newState, team.CompletedInvites, teamSigMeta); err != nil { 1298 return res, fmt.Errorf("illegal completed_invites: %s", err) 1299 } 1300 t.obsoleteActiveInvites(&res.newState, roleUpdates, payload.SignatureMetadata()) 1301 1302 if err := t.useInvites(&res.newState, roleUpdates, team.UsedInvites); err != nil { 1303 return res, fmt.Errorf("illegal used_invites: %s", err) 1304 } 1305 1306 // Note: If someone was removed, the per-team-key should be rotated. This is not checked though. 1307 1308 if team.PerTeamKey != nil { 1309 newKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, &res.newState) 1310 if err != nil { 1311 return res, err 1312 } 1313 res.newState.inner.PerTeamKeys[newKey.Gen] = newKey 1314 if newKey.Gen > res.newState.inner.MaxPerTeamKeyGeneration { 1315 res.newState.inner.MaxPerTeamKeyGeneration = newKey.Gen 1316 } 1317 res.newState.inner.PerTeamKeyCTime = keybase1.UnixTime(payload.Ctime) 1318 } 1319 case libkb.LinkTypeRotateKey: 1320 err = enforce(LinkRules{ 1321 PerTeamKey: TristateRequire, 1322 Admin: TristateOptional, 1323 BoxSummaryHash: TristateOptional, 1324 AllowInImplicitTeam: true, 1325 }) 1326 if err != nil { 1327 return res, err 1328 } 1329 1330 // Check that the signer is at least a writer to have permission to make this link. 1331 if !signer.implicitAdmin { 1332 signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno) 1333 if err != nil { 1334 return res, err 1335 } 1336 switch signerRole { 1337 case keybase1.TeamRole_WRITER, keybase1.TeamRole_ADMIN, keybase1.TeamRole_OWNER: 1338 // ok 1339 default: 1340 return res, fmt.Errorf("link signer does not have permission to rotate key: %v is a %v", signer, signerRole) 1341 } 1342 } 1343 1344 newKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, prevState) 1345 if err != nil { 1346 return res, err 1347 } 1348 1349 moveState() 1350 res.newState.inner.PerTeamKeys[newKey.Gen] = newKey 1351 res.newState.inner.PerTeamKeyCTime = keybase1.UnixTime(payload.Ctime) 1352 if newKey.Gen > res.newState.inner.MaxPerTeamKeyGeneration { 1353 res.newState.inner.MaxPerTeamKeyGeneration = newKey.Gen 1354 } 1355 case libkb.LinkTypeLeave: 1356 err = enforce(LinkRules{ /* Just about everything is restricted. */ }) 1357 if err != nil { 1358 return res, err 1359 } 1360 // Key rotation should never be allowed since FullVerify sometimes does not run on leave links. 1361 1362 // Check that the signer is at least a bot. 1363 // Implicit admins cannot leave a subteam. 1364 signerRole, err := prevState.GetUserRoleAtSeqno(signer.signer, prevSeqno) 1365 if err != nil { 1366 return res, err 1367 } 1368 switch signerRole { 1369 case keybase1.TeamRole_RESTRICTEDBOT, 1370 keybase1.TeamRole_BOT, 1371 keybase1.TeamRole_READER, 1372 keybase1.TeamRole_WRITER, 1373 keybase1.TeamRole_ADMIN, 1374 keybase1.TeamRole_OWNER: 1375 // ok 1376 default: 1377 return res, fmt.Errorf("link signer does not have permission to leave: %v is a %v", signer, signerRole) 1378 } 1379 1380 // The last owner of a team should not leave. 1381 // But that's really up to them and the server. We're just reading what has happened. 1382 1383 moveState() 1384 res.newState.inform(signer.signer, keybase1.TeamRole_NONE, payload.SignatureMetadata()) 1385 case libkb.LinkTypeNewSubteam: 1386 err = enforce(LinkRules{ 1387 Subteam: TristateRequire, 1388 Admin: TristateOptional, 1389 AllowInflate: true, 1390 }) 1391 if err != nil { 1392 return res, err 1393 } 1394 1395 // Check the subteam ID 1396 subteamID, err := t.assertIsSubteamID(string(team.Subteam.ID)) 1397 if err != nil { 1398 return res, err 1399 } 1400 1401 // Check the subteam name 1402 subteamName, err := t.assertSubteamName(prevState, link.Seqno(), string(team.Subteam.Name)) 1403 if err != nil { 1404 return res, err 1405 } 1406 1407 _, err = checkAdmin("make subteam") 1408 if err != nil { 1409 return res, err 1410 } 1411 1412 moveState() 1413 1414 // informSubteam will take care of asserting that these links are inflated 1415 // in order for each subteam. 1416 err = res.newState.informSubteam(subteamID, subteamName, link.Seqno()) 1417 if err != nil { 1418 return res, fmt.Errorf("adding new subteam: %v", err) 1419 } 1420 case libkb.LinkTypeSubteamHead: 1421 isHighLink = true 1422 1423 err = enforce(LinkRules{ 1424 Name: TristateRequire, 1425 Members: TristateRequire, 1426 Parent: TristateRequire, 1427 PerTeamKey: TristateRequire, 1428 Admin: TristateOptional, 1429 Settings: TristateOptional, 1430 BoxSummaryHash: TristateOptional, 1431 FirstInChain: true, 1432 }) 1433 if err != nil { 1434 return res, err 1435 } 1436 1437 if team.Public { 1438 return res, fmt.Errorf("public subteams are not supported") 1439 } 1440 1441 // Check the subteam ID 1442 if !teamID.IsSubTeam() { 1443 return res, fmt.Errorf("malformed subteam id") 1444 } 1445 1446 // Check parent ID 1447 parentID, err := keybase1.TeamIDFromString(string(team.Parent.ID)) 1448 if err != nil { 1449 return res, fmt.Errorf("invalid parent id: %v", err) 1450 } 1451 1452 // Check the initial subteam name 1453 teamName, err := keybase1.TeamNameFromString(string(*team.Name)) 1454 if err != nil { 1455 return res, err 1456 } 1457 if teamName.IsRootTeam() { 1458 return res, fmt.Errorf("subteam has root team name: %s", teamName) 1459 } 1460 if teamName.IsImplicit() || team.Implicit { 1461 return res, NewImplicitTeamOperationError(payload.Body.Type) 1462 } 1463 1464 roleUpdates, err := t.sanityCheckMembers(*team.Members, sanityCheckMembersOptions{ 1465 disallowOwners: true, 1466 allowRemovals: false, 1467 }) 1468 if err != nil { 1469 return res, err 1470 } 1471 1472 perTeamKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, nil) 1473 if err != nil { 1474 return res, err 1475 } 1476 1477 perTeamKeys := make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKey) 1478 perTeamKeys[keybase1.PerTeamKeyGeneration(1)] = perTeamKey 1479 1480 res.newState = TeamSigChainState{ 1481 inner: keybase1.TeamSigChainState{ 1482 Reader: t.reader, 1483 Id: teamID, 1484 Implicit: false, 1485 Public: false, 1486 RootAncestor: teamName.RootAncestorName(), 1487 NameDepth: teamName.Depth(), 1488 NameLog: []keybase1.TeamNameLogPoint{{ 1489 LastPart: teamName.LastPart(), 1490 Seqno: 1, 1491 }}, 1492 LastSeqno: 1, 1493 LastLinkID: link.LinkID().Export(), 1494 ParentID: &parentID, 1495 UserLog: make(map[keybase1.UserVersion][]keybase1.UserLogPoint), 1496 SubteamLog: make(map[keybase1.TeamID][]keybase1.SubteamLogPoint), 1497 PerTeamKeys: perTeamKeys, 1498 MaxPerTeamKeyGeneration: keybase1.PerTeamKeyGeneration(1), 1499 PerTeamKeyCTime: keybase1.UnixTime(payload.Ctime), 1500 LinkIDs: make(map[keybase1.Seqno]keybase1.LinkID), 1501 StubbedLinks: make(map[keybase1.Seqno]bool), 1502 InviteMetadatas: make(map[keybase1.TeamInviteID]keybase1.TeamInviteMetadata), 1503 MerkleRoots: make(map[keybase1.Seqno]keybase1.MerkleRootV2), 1504 Bots: make(map[keybase1.UserVersion]keybase1.TeamBotSettings), 1505 }} 1506 1507 t.updateMembership(&res.newState, roleUpdates, payload.SignatureMetadata()) 1508 if settings := team.Settings; settings != nil { 1509 err = t.parseTeamSettings(settings, &res.newState) 1510 if err != nil { 1511 return res, err 1512 } 1513 } 1514 case libkb.LinkTypeRenameSubteam: 1515 err = enforce(LinkRules{ 1516 Subteam: TristateRequire, 1517 Admin: TristateOptional, 1518 AllowInflate: true, 1519 }) 1520 if err != nil { 1521 return res, err 1522 } 1523 1524 _, err = checkAdmin("rename subteam") 1525 if err != nil { 1526 return res, err 1527 } 1528 1529 // Check the subteam ID 1530 subteamID, err := t.assertIsSubteamID(string(team.Subteam.ID)) 1531 if err != nil { 1532 return res, err 1533 } 1534 1535 // Check the subteam name 1536 subteamName, err := t.assertSubteamName(prevState, link.Seqno(), string(team.Subteam.Name)) 1537 if err != nil { 1538 return res, err 1539 } 1540 1541 moveState() 1542 1543 // informSubteam will take care of asserting that these links are inflated 1544 // in order for each subteam. 1545 err = res.newState.informSubteam(subteamID, subteamName, link.Seqno()) 1546 if err != nil { 1547 return res, fmt.Errorf("adding new subteam: %v", err) 1548 } 1549 case libkb.LinkTypeRenameUpPointer: 1550 err = enforce(LinkRules{ 1551 Name: TristateRequire, 1552 Parent: TristateRequire, 1553 Admin: TristateOptional, 1554 }) 1555 if err != nil { 1556 return res, err 1557 } 1558 1559 // These links only occur in subteam. 1560 if !prevState.IsSubteam() { 1561 return res, fmt.Errorf("got %v in root team", payload.Body.Type) 1562 } 1563 1564 // Sanity check that the parent doesn't claim to have changed. 1565 parentID, err := keybase1.TeamIDFromString(string(team.Parent.ID)) 1566 if err != nil { 1567 return res, fmt.Errorf("invalid parent team id: %v", err) 1568 } 1569 if !parentID.Eq(*prevState.GetParentID()) { 1570 return res, fmt.Errorf("wrong parent team ID: %s != %s", parentID, prevState.GetParentID()) 1571 } 1572 1573 // Ideally we would assert that the name 1574 // But we may not have an up-to-date picture at this time of the parent's name. 1575 // So assert this: 1576 // - The root team name is the same. 1577 // - The depth of the new name is the same. 1578 newName, err := keybase1.TeamNameFromString(string(*team.Name)) 1579 if err != nil { 1580 return res, fmt.Errorf("invalid team name '%s': %v", *team.Name, err) 1581 } 1582 if newName.IsRootTeam() { 1583 return res, fmt.Errorf("cannot rename to root team name: %v", newName.String()) 1584 } 1585 if !newName.RootAncestorName().Eq(prevState.inner.RootAncestor) { 1586 return res, fmt.Errorf("rename cannot change root ancestor team name: %v -> %v", prevState.inner.RootAncestor, newName) 1587 } 1588 if newName.Depth() != prevState.inner.NameDepth { 1589 return res, fmt.Errorf("rename cannot change team nesting depth: %v -> %v", prevState.inner.NameDepth, newName) 1590 } 1591 1592 moveState() 1593 1594 res.newState.inner.NameLog = append(res.newState.inner.NameLog, keybase1.TeamNameLogPoint{ 1595 LastPart: newName.LastPart(), 1596 Seqno: link.Seqno(), 1597 }) 1598 case libkb.LinkTypeDeleteSubteam: 1599 err = enforce(LinkRules{ 1600 Subteam: TristateRequire, 1601 Admin: TristateOptional, 1602 AllowInflate: true, 1603 }) 1604 if err != nil { 1605 return res, err 1606 } 1607 1608 _, err = checkAdmin("delete subteam") 1609 if err != nil { 1610 return res, err 1611 } 1612 1613 // Check the subteam ID 1614 subteamID, err := t.assertIsSubteamID(string(team.Subteam.ID)) 1615 if err != nil { 1616 return res, err 1617 } 1618 1619 // Check the subteam name 1620 _, err = t.assertSubteamName(prevState, link.Seqno(), string(team.Subteam.Name)) 1621 if err != nil { 1622 return res, err 1623 } 1624 1625 moveState() 1626 1627 err = res.newState.informSubteamDelete(subteamID, link.Seqno()) 1628 if err != nil { 1629 return res, fmt.Errorf("error deleting subteam: %v", err) 1630 } 1631 case libkb.LinkTypeInvite: 1632 err = enforce(LinkRules{ 1633 Admin: TristateOptional, 1634 Invites: TristateRequire, 1635 AllowInImplicitTeam: true, 1636 }) 1637 if err != nil { 1638 return res, err 1639 } 1640 1641 signerIsExplicitOwner, err := checkAdmin("invite") 1642 if err != nil { 1643 return res, err 1644 } 1645 1646 additions, cancelations, err := t.sanityCheckInvites(mctx, signer.signer, signerIsExplicitOwner, 1647 *team.Invites, link.SigID(), sanityCheckInvitesOptions{ 1648 isRootTeam: !prevState.IsSubteam(), 1649 implicitTeam: prevState.IsImplicit(), 1650 }) 1651 if err != nil { 1652 return res, err 1653 } 1654 1655 if prevState.IsImplicit() { 1656 // Check to see if the additions were previously members of the team 1657 checkImpteamInvites := func() error { 1658 addedUIDs := make(map[keybase1.UID]bool) 1659 for _, invites := range additions { 1660 for _, invite := range invites { 1661 cat, err := invite.Type.C() 1662 if err != nil { 1663 return err 1664 } 1665 if cat == keybase1.TeamInviteCategory_KEYBASE { 1666 uv, err := invite.KeybaseUserVersion() 1667 if err != nil { 1668 return err 1669 } 1670 addedUIDs[uv.Uid] = true 1671 _, err = prevState.GetLatestUVWithUID(uv.Uid) 1672 if err == nil { 1673 // Found crypto member in previous 1674 // state, we are good! 1675 continue 1676 } 1677 _, _, found := prevState.FindActiveKeybaseInvite(uv.Uid) 1678 if found { 1679 // Found PUKless member in previous 1680 // state, still fine! 1681 continue 1682 } 1683 // Neither crypto member nor PUKless member 1684 // found, we can't allow this addition. 1685 return fmt.Errorf("Not found previous version of user %s", uv.Uid) 1686 } 1687 return fmt.Errorf("invalid invite type in implicit team: %v", cat) 1688 } 1689 } 1690 1691 var cancelledUVs []keybase1.UserVersion 1692 for _, inviteID := range cancelations { 1693 inviteMD, found := prevState.FindActiveInviteMDByID(inviteID) 1694 if !found { 1695 // This is harmless and also we might be canceling 1696 // an obsolete invite. 1697 continue 1698 } 1699 inviteUv, err := inviteMD.Invite.KeybaseUserVersion() 1700 if err != nil { 1701 return fmt.Errorf("cancelled invite is not valid keybase-type invite: %v", err) 1702 } 1703 cancelledUVs = append(cancelledUVs, inviteUv) 1704 } 1705 1706 for _, uv := range cancelledUVs { 1707 if !addedUIDs[uv.Uid] { 1708 return fmt.Errorf("cancelling invite for %v without inviting back a new version", uv) 1709 } 1710 } 1711 return nil 1712 } 1713 if err := checkImpteamInvites(); err != nil { 1714 return res, NewImplicitTeamOperationError("Error in link %q: %v", payload.Body.Type, err) 1715 } 1716 } 1717 1718 moveState() 1719 t.updateInvites(&res.newState, additions, cancelations, teamSigMeta) 1720 case libkb.LinkTypeSettings: 1721 err = enforce(LinkRules{ 1722 Admin: TristateOptional, 1723 Settings: TristateRequire, 1724 // Allow key rotation in settings link. Closing an open team 1725 // should rotate team key. 1726 PerTeamKey: TristateOptional, 1727 // At the moment the only team setting is banned in implicit teams. 1728 // But in the future there could be allowed settings that also use this link type. 1729 AllowInImplicitTeam: true, 1730 }) 1731 if err != nil { 1732 return res, err 1733 } 1734 1735 _, err = checkAdmin("change settings") 1736 if err != nil { 1737 return res, err 1738 } 1739 1740 moveState() 1741 err = t.parseTeamSettings(team.Settings, &res.newState) 1742 if err != nil { 1743 return res, err 1744 } 1745 1746 // When team is changed from open to closed, per-team-key should be rotated. But 1747 // this is not enforced. 1748 if team.PerTeamKey != nil { 1749 newKey, err := t.checkPerTeamKey(*link.source, *team.PerTeamKey, &res.newState) 1750 if err != nil { 1751 return res, err 1752 } 1753 res.newState.inner.PerTeamKeys[newKey.Gen] = newKey 1754 res.newState.inner.PerTeamKeyCTime = keybase1.UnixTime(payload.Ctime) 1755 if newKey.Gen > res.newState.inner.MaxPerTeamKeyGeneration { 1756 res.newState.inner.MaxPerTeamKeyGeneration = newKey.Gen 1757 } 1758 } 1759 case libkb.LinkTypeDeleteRoot: 1760 return res, NewTeamDeletedError() 1761 case libkb.LinkTypeDeleteUpPointer: 1762 return res, NewTeamDeletedError() 1763 case libkb.LinkTypeKBFSSettings: 1764 err = enforce(LinkRules{ 1765 Admin: TristateOptional, 1766 KBFS: TristateRequire, 1767 AllowInImplicitTeam: true, 1768 }) 1769 if err != nil { 1770 return res, err 1771 } 1772 1773 err = checkExplicitWriter("change KBFS settings") 1774 if err != nil { 1775 return res, err 1776 } 1777 1778 moveState() 1779 err = t.parseKBFSTLFUpgrade(team.KBFS, &res.newState) 1780 if err != nil { 1781 return res, err 1782 } 1783 case libkb.LinkTypeTeamBotSettings: 1784 if err = enforce(LinkRules{ 1785 Admin: TristateOptional, 1786 BotSettings: TristateRequire, 1787 AllowInImplicitTeam: true, 1788 }); err != nil { 1789 return res, err 1790 } 1791 1792 if _, err = checkAdmin("change bots"); err != nil { 1793 return res, err 1794 } 1795 1796 moveState() 1797 if err = t.parseTeamBotSettings(*team.BotSettings, &res.newState); err != nil { 1798 return res, err 1799 } 1800 case "": 1801 return res, errors.New("empty body type") 1802 default: 1803 if link.outerLink.IgnoreIfUnsupported { 1804 moveState() 1805 } else { 1806 return res, fmt.Errorf("unsupported link type: %s", payload.Body.Type) 1807 } 1808 } 1809 1810 if isHighLink { 1811 res.newState.inner.LastHighLinkID = link.LinkID().Export() 1812 res.newState.inner.LastHighSeqno = link.Seqno() 1813 } 1814 return res, nil 1815 } 1816 1817 func (t *teamSigchainPlayer) roleUpdateChangedHighSet(prevState *TeamSigChainState, roleUpdates chainRoleUpdates) (bool, error) { 1818 // The high set of users can be changed by promotion to Admin/Owner or 1819 // demotion from Admin/Owner or any movement between those two roles. 1820 for newRole, uvs := range roleUpdates { 1821 if newRole.IsAdminOrAbove() { 1822 return true, nil 1823 } 1824 // were any of these users previously an admin or above 1825 for _, uv := range uvs { 1826 prevRole, err := prevState.GetUserRole(uv) 1827 if err != nil { 1828 return false, err 1829 } 1830 if prevRole.IsAdminOrAbove() { 1831 return true, nil 1832 } 1833 } 1834 1835 } 1836 return false, nil 1837 } 1838 1839 func (t *teamSigchainPlayer) checkSeqnoToAdd(prevState *TeamSigChainState, linkSeqno keybase1.Seqno, isInflate bool) error { 1840 if linkSeqno < 1 { 1841 return fmt.Errorf("link seqno (%v) cannot be less than 1", linkSeqno) 1842 } 1843 if prevState == nil { 1844 if isInflate { 1845 return fmt.Errorf("cannot inflate link %v with no previous state", linkSeqno) 1846 } 1847 if linkSeqno != 1 { 1848 return fmt.Errorf("first team link must have seqno 1 but got %v", linkSeqno) 1849 } 1850 } else { 1851 if isInflate { 1852 if prevState.IsLinkFilled(linkSeqno) { 1853 return fmt.Errorf("link %v is already filled", linkSeqno) 1854 } 1855 } else { 1856 if linkSeqno != prevState.GetLatestSeqno()+1 { 1857 return fmt.Errorf("link had unexpected seqno %v != %v", linkSeqno, prevState.GetLatestSeqno()+1) 1858 } 1859 } 1860 if linkSeqno > prevState.GetLatestSeqno()+1 { 1861 return fmt.Errorf("link had far-future seqno %v > %v", linkSeqno, prevState.GetLatestSeqno()+1) 1862 } 1863 } 1864 return nil 1865 } 1866 1867 type sanityCheckInvitesOptions struct { 1868 isRootTeam bool 1869 implicitTeam bool 1870 } 1871 1872 func assertIsKeybaseInvite(mctx libkb.MetaContext, i SCTeamInvite) bool { 1873 typ, err := TeamInviteTypeFromString(mctx, i.Type) 1874 if err != nil { 1875 mctx.Info("bad invite type: %s", err) 1876 return false 1877 } 1878 cat, err := typ.C() 1879 if err != nil { 1880 mctx.Info("bad invite category: %s", err) 1881 return false 1882 } 1883 return cat == keybase1.TeamInviteCategory_KEYBASE 1884 } 1885 1886 // These signatures contain non-owners inviting owners. 1887 // They slipped in before that was banned. They are excepted from the rule. 1888 var hardcodedInviteRuleExceptionSigIDs = map[keybase1.SigIDMapKey]bool{ 1889 "c06e8da2959d8c8054fb10e005910716f776b3c3df9ef2eb4c4b8584f45e187f0f": true, 1890 "e800db474fa75f39503e9241990c3707121c7c414687a7b1f5ef579a625eaf820f": true, 1891 "46d9f2700b8d4287a2dc46dae00974a794b5778149214cf91fa4b69229a6abbc0f": true, 1892 } 1893 1894 // sanityCheckInvites sanity checks a raw SCTeamInvites section and coerces it into a 1895 // format that we can use. It checks: 1896 // - inviting owners is sometimes banned 1897 // - invite IDs aren't repeated 1898 // - <name,type> pairs aren't reused 1899 // - IDs parse into proper keybase1.TeamInviteIDs 1900 // - the invite type parses into proper TeamInviteType, or that it's an unknown 1901 // invite that we're OK to not act upon. 1902 // 1903 // Implicit teams are different: 1904 // - owners and readers are the only allowed roles 1905 func (t *teamSigchainPlayer) sanityCheckInvites(mctx libkb.MetaContext, 1906 signer keybase1.UserVersion, signerIsExplicitOwner bool, invites SCTeamInvites, sigID keybase1.SigID, 1907 options sanityCheckInvitesOptions, 1908 ) (additions map[keybase1.TeamRole][]keybase1.TeamInvite, cancelations []keybase1.TeamInviteID, err error) { 1909 1910 type assignment struct { 1911 i SCTeamInvite 1912 role keybase1.TeamRole 1913 } 1914 var all []assignment 1915 additions = make(map[keybase1.TeamRole][]keybase1.TeamInvite) 1916 1917 if invites.Owners != nil && len(*invites.Owners) > 0 { 1918 additions[keybase1.TeamRole_OWNER] = nil 1919 for _, i := range *invites.Owners { 1920 if !options.isRootTeam { 1921 return nil, nil, fmt.Errorf("encountered invite of owner in non-root team") 1922 } 1923 if !signerIsExplicitOwner { 1924 if !hardcodedInviteRuleExceptionSigIDs[sigID.ToMapKey()] { 1925 return nil, nil, fmt.Errorf("encountered invite of owner by non-owner") 1926 } 1927 } 1928 if !(options.implicitTeam || assertIsKeybaseInvite(mctx, i)) { 1929 return nil, nil, fmt.Errorf("encountered a disallowed owner invite") 1930 } 1931 all = append(all, assignment{i, keybase1.TeamRole_OWNER}) 1932 } 1933 } 1934 1935 if invites.Admins != nil && len(*invites.Admins) > 0 { 1936 if options.implicitTeam { 1937 return nil, nil, NewImplicitTeamOperationError("encountered admin invite") 1938 } 1939 additions[keybase1.TeamRole_ADMIN] = nil 1940 for _, i := range *invites.Admins { 1941 all = append(all, assignment{i, keybase1.TeamRole_ADMIN}) 1942 } 1943 } 1944 1945 if invites.Writers != nil && len(*invites.Writers) > 0 { 1946 if options.implicitTeam { 1947 return nil, nil, NewImplicitTeamOperationError("encountered writer invite") 1948 } 1949 additions[keybase1.TeamRole_WRITER] = nil 1950 for _, i := range *invites.Writers { 1951 all = append(all, assignment{i, keybase1.TeamRole_WRITER}) 1952 } 1953 } 1954 1955 if invites.Readers != nil && len(*invites.Readers) > 0 { 1956 additions[keybase1.TeamRole_READER] = nil 1957 for _, i := range *invites.Readers { 1958 all = append(all, assignment{i, keybase1.TeamRole_READER}) 1959 } 1960 } 1961 1962 // Set to `true` if it was an addition and `false` if it was a deletion 1963 byID := make(map[keybase1.TeamInviteID]bool) 1964 byName := make(map[string]bool) 1965 1966 keyFunc := func(i SCTeamInvite) string { 1967 return fmt.Sprintf("%s:%s", i.Type, i.Name) 1968 } 1969 1970 if invites.Cancel != nil { 1971 for _, c := range *invites.Cancel { 1972 id, err := c.TeamInviteID() 1973 if err != nil { 1974 return nil, nil, err 1975 } 1976 if byID[id] { 1977 return nil, nil, NewInviteError(id, fmt.Errorf("ID %s appears twice as a cancellation", c)) 1978 } 1979 byID[id] = false 1980 cancelations = append(cancelations, id) 1981 } 1982 } 1983 1984 for _, invite := range all { 1985 res, err := invite.i.TeamInvite(mctx, invite.role, signer) 1986 if err != nil { 1987 return nil, nil, err 1988 } 1989 id := res.Id 1990 _, seen := byID[id] 1991 if seen { 1992 return nil, nil, NewInviteError(id, fmt.Errorf("appears twice in invite set")) 1993 } 1994 key := keyFunc(invite.i) 1995 if byName[key] { 1996 return nil, nil, NewInviteError(id, fmt.Errorf("invite %s appears twice in invite set", key)) 1997 } 1998 1999 isNewStyle, err := IsNewStyleInvite(res) 2000 if err != nil { 2001 return nil, nil, NewInviteError(id, fmt.Errorf("failed to check if invite is new-style: %s", err)) 2002 } 2003 if isNewStyle { 2004 if options.implicitTeam { 2005 return nil, nil, NewInviteError(id, fmt.Errorf("new-style in implicit team")) 2006 } 2007 if res.MaxUses == nil { 2008 return nil, nil, NewInviteError(id, fmt.Errorf("new-style but has no max-uses")) 2009 } 2010 if !res.MaxUses.IsNotNilAndValid() { 2011 return nil, nil, NewInviteError(id, fmt.Errorf("invalid max_uses %d", *res.MaxUses)) 2012 } 2013 if res.Etime != nil { 2014 if *res.Etime <= 0 { 2015 return nil, nil, NewInviteError(id, fmt.Errorf("invalid etime %d", *res.Etime)) 2016 } 2017 } 2018 } 2019 2020 // NOTE: We are allowing etime without max_uses right now in sigchain 2021 // player. It would rejected on the server though. We want to allow 2022 // this now in case we do expiring invites in the future. 2023 2024 category, categoryErr := res.Type.C() 2025 // Do not error out if there's a new invite category we don't recognize 2026 if categoryErr == nil && category == keybase1.TeamInviteCategory_INVITELINK { 2027 if res.Role.IsAdminOrAbove() { 2028 return nil, nil, NewInviteError(id, NewInvitelinkBadRoleError(res.Role)) 2029 } 2030 } 2031 2032 byName[key] = true 2033 byID[id] = true 2034 additions[res.Role] = append(additions[res.Role], res) 2035 } 2036 2037 return additions, cancelations, nil 2038 } 2039 2040 // A map describing an intent to change users' roles. 2041 // Each item means: change that user to that role. 2042 // To be clear: An omission does NOT mean to remove the existing role. 2043 type chainRoleUpdates map[keybase1.TeamRole][]keybase1.UserVersion 2044 2045 type sanityCheckMembersOptions struct { 2046 // At least one owner must be added 2047 requireOwners bool 2048 // Adding owners is blocked 2049 disallowOwners bool 2050 // Removals are allowed, blocked if false 2051 allowRemovals bool 2052 // Only additions of OWNER or READER are allowed. Does not affect removals. 2053 onlyOwnersOrReaders bool 2054 } 2055 2056 // Check that all the users are formatted correctly. 2057 // Check that there are no duplicate members. 2058 // Do not check that all removals are members. That should be true, but not strictly enforced when reading. 2059 func (t *teamSigchainPlayer) sanityCheckMembers(members SCTeamMembers, options sanityCheckMembersOptions) (chainRoleUpdates, error) { 2060 type assignment struct { 2061 m SCTeamMember 2062 role keybase1.TeamRole 2063 } 2064 var all []assignment 2065 2066 if options.requireOwners { 2067 if members.Owners == nil { 2068 return nil, fmt.Errorf("team has no owner list") 2069 } 2070 if len(*members.Owners) < 1 { 2071 return nil, fmt.Errorf("team has no owners") 2072 } 2073 } 2074 if options.disallowOwners { 2075 if members.Owners != nil && len(*members.Owners) > 0 { 2076 return nil, fmt.Errorf("team has owners") 2077 } 2078 } 2079 if !options.allowRemovals { 2080 if members.None != nil && len(*members.None) != 0 { 2081 return nil, fmt.Errorf("team has removals in link") 2082 } 2083 } 2084 2085 // Map from roles to users. 2086 res := make(map[keybase1.TeamRole][]keybase1.UserVersion) 2087 2088 if members.Owners != nil && len(*members.Owners) > 0 { 2089 res[keybase1.TeamRole_OWNER] = nil 2090 for _, m := range *members.Owners { 2091 all = append(all, assignment{m, keybase1.TeamRole_OWNER}) 2092 } 2093 } 2094 if members.Admins != nil && len(*members.Admins) > 0 { 2095 if options.onlyOwnersOrReaders { 2096 return nil, NewImplicitTeamOperationError("encountered add admin") 2097 } 2098 res[keybase1.TeamRole_ADMIN] = nil 2099 for _, m := range *members.Admins { 2100 all = append(all, assignment{m, keybase1.TeamRole_ADMIN}) 2101 } 2102 } 2103 if members.Writers != nil && len(*members.Writers) > 0 { 2104 if options.onlyOwnersOrReaders { 2105 return nil, NewImplicitTeamOperationError("encountered add writer") 2106 } 2107 res[keybase1.TeamRole_WRITER] = nil 2108 for _, m := range *members.Writers { 2109 all = append(all, assignment{m, keybase1.TeamRole_WRITER}) 2110 } 2111 } 2112 if members.Readers != nil && len(*members.Readers) > 0 { 2113 res[keybase1.TeamRole_READER] = nil 2114 for _, m := range *members.Readers { 2115 all = append(all, assignment{m, keybase1.TeamRole_READER}) 2116 } 2117 } 2118 if members.Bots != nil && len(*members.Bots) > 0 { 2119 res[keybase1.TeamRole_BOT] = nil 2120 for _, m := range *members.Bots { 2121 all = append(all, assignment{m, keybase1.TeamRole_BOT}) 2122 } 2123 } 2124 if members.RestrictedBots != nil && len(*members.RestrictedBots) > 0 { 2125 res[keybase1.TeamRole_RESTRICTEDBOT] = nil 2126 for _, m := range *members.RestrictedBots { 2127 all = append(all, assignment{m, keybase1.TeamRole_RESTRICTEDBOT}) 2128 } 2129 } 2130 if members.None != nil && len(*members.None) > 0 { 2131 res[keybase1.TeamRole_NONE] = nil 2132 for _, m := range *members.None { 2133 all = append(all, assignment{m, keybase1.TeamRole_NONE}) 2134 } 2135 } 2136 2137 // Set of users who have already been seen. 2138 seen := make(map[keybase1.UserVersion]bool) 2139 2140 for _, pair := range all { 2141 uv := keybase1.UserVersion(pair.m) 2142 2143 if seen[uv] { 2144 return nil, fmt.Errorf("duplicate UID in members: %s", uv.Uid) 2145 } 2146 2147 res[pair.role] = append(res[pair.role], uv) 2148 2149 seen[uv] = true 2150 } 2151 2152 return res, nil 2153 } 2154 2155 // Whether the roleUpdates would demote any current owner to a lesser role. 2156 func (t *teamSigchainPlayer) roleUpdatesDemoteOwners(prev *TeamSigChainState, roleUpdates map[keybase1.TeamRole][]keybase1.UserVersion) bool { 2157 2158 // It is OK to readmit an owner if the owner reset and is coming in at a lower permission 2159 // level. So check that case here. 2160 readmittingResetUser := func(uv keybase1.UserVersion) bool { 2161 for toRole, uvs := range roleUpdates { 2162 if toRole == keybase1.TeamRole_NONE { 2163 continue 2164 } 2165 for _, newUV := range uvs { 2166 if newUV.Uid.Equal(uv.Uid) && newUV.EldestSeqno > uv.EldestSeqno { 2167 return true 2168 } 2169 } 2170 } 2171 return false 2172 } 2173 2174 for toRole, uvs := range roleUpdates { 2175 if toRole == keybase1.TeamRole_OWNER { 2176 continue 2177 } 2178 for _, uv := range uvs { 2179 fromRole, err := prev.GetUserRole(uv) 2180 if err != nil { 2181 continue // ignore error, user not in team 2182 } 2183 if toRole == keybase1.TeamRole_NONE && fromRole == keybase1.TeamRole_OWNER && readmittingResetUser(uv) { 2184 continue 2185 } 2186 if fromRole == keybase1.TeamRole_OWNER { 2187 // This is an intent to demote an owner. 2188 return true 2189 } 2190 } 2191 } 2192 return false 2193 } 2194 2195 func (t *teamSigchainPlayer) checkPerTeamKey(link SCChainLink, perTeamKey SCPerTeamKey, prevState *TeamSigChainState) (res keybase1.PerTeamKey, err error) { 2196 2197 // Check that the new key doesn't conflict with keys we already have. 2198 err = prevState.checkNewPTK(perTeamKey) 2199 if err != nil { 2200 return res, err 2201 } 2202 2203 // validate signing kid 2204 sigKey, err := libkb.ImportNaclSigningKeyPairFromHex(perTeamKey.SigKID.String()) 2205 if err != nil { 2206 return res, fmt.Errorf("invalid per-team-key signing KID: %s", perTeamKey.SigKID) 2207 } 2208 2209 // validate encryption kid 2210 _, err = libkb.ImportNaclDHKeyPairFromHex(perTeamKey.EncKID.String()) 2211 if err != nil { 2212 return res, fmt.Errorf("invalid per-team-key encryption KID: %s", perTeamKey.EncKID) 2213 } 2214 2215 // verify the reverse sig 2216 // jw is the expected reverse sig contents 2217 jw, err := jsonw.Unmarshal([]byte(link.Payload)) 2218 if err != nil { 2219 return res, libkb.NewReverseSigError("per-team-key reverse sig: failed to parse payload: %s", err) 2220 } 2221 err = libkb.VerifyReverseSig(t.G(), sigKey, "body.team.per_team_key.reverse_sig", jw, perTeamKey.ReverseSig) 2222 if err != nil { 2223 return res, libkb.NewReverseSigError("per-team-key reverse sig: %s", err) 2224 } 2225 2226 return keybase1.PerTeamKey{ 2227 Gen: perTeamKey.Generation, 2228 Seqno: link.Seqno, 2229 SigKID: perTeamKey.SigKID, 2230 EncKID: perTeamKey.EncKID, 2231 }, nil 2232 } 2233 2234 // Update `userLog` with the membership in roleUpdates. 2235 // The `NONE` list removes users. 2236 // The other lists add users. 2237 func (t *teamSigchainPlayer) updateMembership(stateToUpdate *TeamSigChainState, roleUpdates chainRoleUpdates, sigMeta keybase1.SignatureMetadata) { 2238 for role, uvs := range roleUpdates { 2239 for _, uv := range uvs { 2240 stateToUpdate.inform(uv, role, sigMeta) 2241 } 2242 } 2243 } 2244 2245 func (t *teamSigchainPlayer) updateInvites(stateToUpdate *TeamSigChainState, additions map[keybase1.TeamRole][]keybase1.TeamInvite, cancelations []keybase1.TeamInviteID, teamSigMeta keybase1.TeamSignatureMetadata) { 2246 for _, invites := range additions { 2247 for _, invite := range invites { 2248 stateToUpdate.informNewInvite(invite, teamSigMeta) 2249 } 2250 } 2251 for _, cancelation := range cancelations { 2252 stateToUpdate.informCanceledInvite(cancelation, teamSigMeta) 2253 } 2254 } 2255 2256 func (t *teamSigchainPlayer) completeInvites(stateToUpdate *TeamSigChainState, 2257 completed map[keybase1.TeamInviteID]keybase1.UserVersionPercentForm, 2258 teamSigMeta keybase1.TeamSignatureMetadata) error { 2259 for id := range completed { 2260 inviteMD, found := stateToUpdate.FindActiveInviteMDByID(id) 2261 if !found { 2262 // Invite doesn't exist or we don't know about it because invite 2263 // links were stubbed. We could do a similar check here that we do 2264 // in teamSigchainPlayer.useInvites, but we haven't been doing it 2265 // in the past, so we might have links with issues like that in the 2266 // wild. 2267 continue 2268 } 2269 isNewStyle, err := IsNewStyleInvite(inviteMD.Invite) 2270 if err != nil { 2271 return err 2272 } 2273 if isNewStyle { 2274 return fmt.Errorf("`completed_invites` for a new-style invite (id: %q)", id) 2275 } 2276 stateToUpdate.informCompletedInvite(id, teamSigMeta) 2277 } 2278 return nil 2279 } 2280 2281 func (t *teamSigchainPlayer) obsoleteActiveInvites(stateToUpdate *TeamSigChainState, roleUpdates chainRoleUpdates, sigMeta keybase1.SignatureMetadata) { 2282 if len(stateToUpdate.inner.InviteMetadatas) == 0 { 2283 return 2284 } 2285 2286 m := make(map[keybase1.UID]struct{}) 2287 for _, uvs := range roleUpdates { 2288 for _, uv := range uvs { 2289 m[uv.Uid] = struct{}{} 2290 } 2291 } 2292 2293 for _, inviteMD := range stateToUpdate.ActiveInvites() { 2294 if inviteUv, err := inviteMD.Invite.KeybaseUserVersion(); err == nil { 2295 if _, ok := m[inviteUv.Uid]; ok { 2296 inviteMD.Status = keybase1.NewTeamInviteMetadataStatusWithObsolete() 2297 stateToUpdate.inner.InviteMetadatas[inviteMD.Invite.Id] = inviteMD 2298 } 2299 } 2300 } 2301 } 2302 2303 func (t *teamSigchainPlayer) useInvites(stateToUpdate *TeamSigChainState, roleUpdates chainRoleUpdates, used []SCMapInviteIDUVPair) error { 2304 if len(used) == 0 { 2305 return nil 2306 } 2307 2308 seenUVs := make(map[keybase1.UserVersion]bool) 2309 hasStubbedLinks := stateToUpdate.HasAnyStubbedLinks() 2310 for _, pair := range used { 2311 inviteID, err := pair.InviteID.TeamInviteID() 2312 if err != nil { 2313 return err 2314 } 2315 uv, err := keybase1.ParseUserVersion(pair.UV) 2316 if err != nil { 2317 return err 2318 } 2319 2320 inviteMD, foundInvite := stateToUpdate.FindActiveInviteMDByID(inviteID) 2321 if !foundInvite { 2322 if hasStubbedLinks { 2323 // We didn't find it, possibly because server stubbed it out (we're not allowed to 2324 // see it; didn't load with admin perms). 2325 continue 2326 } else { 2327 // We couldn't find the invite, and we have no stubbed links, which 2328 // means that inviteID is invalid. 2329 return fmt.Errorf("could not find active invite ID in used_invites: %s", inviteID) 2330 } 2331 } 2332 2333 isNewStyle, err := IsNewStyleInvite(inviteMD.Invite) 2334 if err != nil { 2335 return err 2336 } 2337 if !isNewStyle { 2338 return fmt.Errorf("`used_invites` for a non-new-style invite (id: %q)", inviteID) 2339 } 2340 2341 alreadyUsed := len(inviteMD.UsedInvites) 2342 // Note that we append to inviteMD.UsedInvites at the end of this for loop, so alreadyUsed 2343 // updates correctly when processing multiple invite pairs. 2344 if inviteMD.Invite.IsUsedUp(alreadyUsed) { 2345 return fmt.Errorf("invite %s is expired after %d uses", inviteID, alreadyUsed) 2346 } 2347 2348 // We explicitly don't check invite.Etime here; it's used as a hint for admins. 2349 // but not checked in the sigchain player. 2350 2351 // If we have the invite, also check if invite role matches role 2352 // added. 2353 var foundUV bool 2354 for _, updatedUV := range roleUpdates[inviteMD.Invite.Role] { 2355 if uv.Eq(updatedUV) { 2356 foundUV = true 2357 break 2358 } 2359 } 2360 if !foundUV { 2361 return fmt.Errorf("used_invite for UV %s that was not added as role %s", pair.UV, 2362 inviteMD.Invite.Role.HumanString()) 2363 } 2364 2365 if seen := seenUVs[uv]; seen { 2366 return fmt.Errorf("duplicate used_invite for UV %s", pair.UV) 2367 } 2368 seenUVs[uv] = true 2369 2370 // Because we use information from the UserLog here, useInvites should be called after 2371 // updateMembership. 2372 logPoint := len(stateToUpdate.inner.UserLog[uv]) - 1 2373 if logPoint < 0 { 2374 return fmt.Errorf("used_invite for UV %s that was not added to to the team", pair.UV) 2375 } 2376 inviteMD.UsedInvites = append(inviteMD.UsedInvites, 2377 keybase1.TeamUsedInviteLogPoint{ 2378 Uv: uv, 2379 LogPoint: logPoint, 2380 }) 2381 stateToUpdate.inner.InviteMetadatas[inviteID] = inviteMD 2382 } 2383 return nil 2384 } 2385 2386 // Check that the subteam name is valid and kind of is a child of this chain. 2387 // Returns the parsed subteam name. 2388 func (t *teamSigchainPlayer) assertSubteamName(parent *TeamSigChainState, parentSeqno keybase1.Seqno, subteamNameStr string) (keybase1.TeamName, error) { 2389 // Ideally, we would assert the team name is a direct child of this team's name. 2390 // But the middle parts of the names might be out of date. 2391 // Instead assert: 2392 // - The root team name is same. 2393 // - The subteam is 1 level deeper. 2394 // - The last part of the parent team's name matched 2395 // at the parent-seqno that this subteam name was mentioned. 2396 // (If the subteam is a.b.c.d then c should match) 2397 // The reason this is pegged at seqno instead of simply the latest parent name is 2398 // hard to explain, see TestRenameInflateSubteamAfterRenameParent. 2399 2400 subteamName, err := keybase1.TeamNameFromString(subteamNameStr) 2401 if err != nil { 2402 return subteamName, fmt.Errorf("invalid subteam team name '%s': %v", subteamNameStr, err) 2403 } 2404 2405 if !parent.inner.RootAncestor.Eq(subteamName.RootAncestorName()) { 2406 return subteamName, fmt.Errorf("subteam is of a different root team: %v != %v", 2407 subteamName.RootAncestorName(), 2408 parent.inner.RootAncestor) 2409 } 2410 2411 expectedDepth := parent.inner.NameDepth + 1 2412 if subteamName.Depth() != expectedDepth { 2413 return subteamName, fmt.Errorf("subteam name has depth %v but expected %v", 2414 subteamName.Depth(), expectedDepth) 2415 } 2416 2417 // The last part of the parent name at parentSeqno. 2418 var parentLastPart keybase1.TeamNamePart 2419 for _, point := range parent.inner.NameLog { 2420 if point.Seqno > parentSeqno { 2421 break 2422 } 2423 parentLastPart = point.LastPart 2424 } 2425 2426 subteamSecondToLastPart := subteamName.Parts[len(subteamName.Parts)-2] 2427 if !subteamSecondToLastPart.Eq(parentLastPart) { 2428 return subteamName, fmt.Errorf("subteam name has wrong name for us: %v != %v", 2429 subteamSecondToLastPart, parentLastPart) 2430 } 2431 2432 return subteamName, nil 2433 } 2434 2435 func (t *teamSigchainPlayer) assertIsSubteamID(subteamIDStr string) (keybase1.TeamID, error) { 2436 // Check the subteam ID 2437 subteamID, err := keybase1.TeamIDFromString(subteamIDStr) 2438 if err != nil { 2439 return subteamID, fmt.Errorf("invalid subteam id: %v", err) 2440 } 2441 if !subteamID.IsSubTeam() { 2442 return subteamID, fmt.Errorf("malformed subteam id") 2443 } 2444 return subteamID, nil 2445 } 2446 2447 func (t *teamSigchainPlayer) parseTeamSettings(settings *SCTeamSettings, newState *TeamSigChainState) error { 2448 if open := settings.Open; open != nil { 2449 if newState.inner.Implicit { 2450 return fmt.Errorf("implicit team cannot be open") 2451 } 2452 2453 newState.inner.Open = open.Enabled 2454 if options := open.Options; options != nil { 2455 if !open.Enabled { 2456 return fmt.Errorf("closed team shouldn't define team.settings.open.options") 2457 } 2458 2459 switch options.JoinAs { 2460 case "reader": 2461 newState.inner.OpenTeamJoinAs = keybase1.TeamRole_READER 2462 case "writer": 2463 newState.inner.OpenTeamJoinAs = keybase1.TeamRole_WRITER 2464 default: 2465 return fmt.Errorf("invalid join_as role in open team: %s", options.JoinAs) 2466 } 2467 } else if open.Enabled { 2468 return fmt.Errorf("team set to open but team.settings.open.options is missing") 2469 } 2470 } 2471 2472 return nil 2473 } 2474 2475 func (t *teamSigchainPlayer) parseTeamBotSettings(bots []SCTeamBot, newState *TeamSigChainState) error { 2476 2477 for _, bot := range bots { 2478 // Bots listed here must have the RESTRICTEDBOT role 2479 role, err := newState.GetUserRole(bot.Bot.ToUserVersion()) 2480 if err != nil { 2481 return err 2482 } 2483 if !role.IsRestrictedBot() { 2484 return fmt.Errorf("found bot settings for %v. Expected role RESTRICTEDBOT, found %v", bot, role) 2485 } 2486 2487 var convs, triggers []string 2488 if bot.Triggers != nil { 2489 triggers = *bot.Triggers 2490 } 2491 if bot.Convs != nil { 2492 convs = *bot.Convs 2493 } 2494 if newState.inner.Bots == nil { 2495 // If an old client cached this as nil, then just make a new map here for this link 2496 newState.inner.Bots = make(map[keybase1.UserVersion]keybase1.TeamBotSettings) 2497 } 2498 newState.inner.Bots[bot.Bot.ToUserVersion()] = keybase1.TeamBotSettings{ 2499 Cmds: bot.Cmds, 2500 Mentions: bot.Mentions, 2501 Triggers: triggers, 2502 Convs: convs, 2503 } 2504 } 2505 return nil 2506 } 2507 2508 func (t *teamSigchainPlayer) parseKBFSTLFUpgrade(upgrade *SCTeamKBFS, newState *TeamSigChainState) error { 2509 if upgrade.TLF != nil { 2510 newState.inner.TlfIDs = append(newState.inner.TlfIDs, upgrade.TLF.ID) 2511 } 2512 if upgrade.Keyset != nil { 2513 if newState.inner.TlfLegacyUpgrade == nil { 2514 // If an old client cached this as nil, then just make a new map here for this link 2515 newState.inner.TlfLegacyUpgrade = 2516 make(map[keybase1.TeamApplication]keybase1.TeamLegacyTLFUpgradeChainInfo) 2517 } 2518 newState.inner.TlfLegacyUpgrade[upgrade.Keyset.AppType] = keybase1.TeamLegacyTLFUpgradeChainInfo{ 2519 KeysetHash: upgrade.Keyset.KeysetHash, 2520 TeamGeneration: upgrade.Keyset.TeamGeneration, 2521 LegacyGeneration: upgrade.Keyset.LegacyGeneration, 2522 AppType: upgrade.Keyset.AppType, 2523 } 2524 } 2525 return nil 2526 } 2527 2528 type Tristate int 2529 2530 const ( 2531 TristateDisallow Tristate = 0 // default 2532 TristateRequire Tristate = 1 2533 TristateOptional Tristate = 2 2534 ) 2535 2536 // LinkRules describes what fields and properties are required for a link type. 2537 // Default values are the strictest. 2538 // Keep this in sync with `func enforce`. 2539 type LinkRules struct { 2540 // Sections 2541 Name Tristate 2542 Members Tristate 2543 Parent Tristate 2544 Subteam Tristate 2545 PerTeamKey Tristate 2546 Admin Tristate 2547 Invites Tristate 2548 CompletedInvites Tristate 2549 Settings Tristate 2550 KBFS Tristate 2551 BoxSummaryHash Tristate 2552 BotSettings Tristate 2553 2554 AllowInImplicitTeam bool // whether this link is allowed in implicit team chains 2555 AllowInflate bool // whether this link is allowed to be filled later 2556 FirstInChain bool // whether this link must be the beginning of the chain 2557 }