// If a UID appears multiple times with different TeamMemberStatus, only show the 'ACTIVE' version.
// This can happen when an owner resets and is re-added as an admin by an admin.
// Mutates `members`
339131
func membersHideInactiveDuplicates(ctx context.Context, g *libkb.GlobalContext, members *keybase1.TeamMembersDetails) {
	// If a UID appears multiple times with different TeamMemberStatus, only show the 'ACTIVE' version.
	// This can happen when an owner resets and is re-added as an admin by an admin.
	seenActive := make(map[keybase1.UID]bool)
	lists := []*[]keybase1.TeamMemberDetails{
		&members.Owners,
		&members.Admins,
		&members.Writers,
		&members.Readers,
	}
	// Scan for active rows
	for _, rows := range lists {
		for _, row := range *rows {
			if row.Status == keybase1.TeamMemberStatus_ACTIVE {
				seenActive[row.Uv.Uid] = true
			}
		}
	}
	// Filter out superseded inactive rows
	for _, rows := range lists {
		filtered := []keybase1.TeamMemberDetails{}
		for _, row := range *rows {
			if row.Status == keybase1.TeamMemberStatus_ACTIVE || !seenActive[row.Uv.Uid] {
				filtered = append(filtered, row)
			} else {
				g.Log.CDebugf(ctx, "membersHideInactiveDuplicates filtered out row: %v %v", row.Uv, row.Status)
			}
		}
		*rows = filtered
	}
}
1210427
func NewMemberChangeNameDetails(NewValue *UserNameLogInfo) *MemberChangeNameDetails {
	s := new(MemberChangeNameDetails)
	s.NewValue = NewValue
	return s
}
293026
func (t TeamID) IsSubTeam() bool {
	suffix := t[len(t)-2:]
	switch suffix {
	case SUB_TEAMID_PRIVATE_SUFFIX_HEX, SUB_TEAMID_PUBLIC_SUFFIX_HEX:
		return true
	}
	return false
}
404928
func (org *User) RemoveMember(uid int64) error {
	return RemoveOrgUser(org.ID, uid)
}
1327139
func (m *Manager) IsMember(globalID, username string) (result bool, err error) {
	if !m.Exists(globalID) {
		return
	}
	result, err = m.isDirectMember(globalID, username)
	if result || err != nil {
		return
	}
	// If not a direct member, check the membership in the parent organization
	lastSubOrgSeperator := strings.LastIndex(globalID, ".")
	if lastSubOrgSeperator > 0 {
		result, err = m.IsMember(globalID[:lastSubOrgSeperator], username)
		if result || err != nil {
			return
		}
	}
	// If not a direct or inherited member, iterate through the list of member organizations
	var org Organization
	err = m.collection.Find(bson.M{"globalid": globalID}).Select(bson.M{"orgmembers": 1, "includesuborgsof": 1}).One(&org)
	if err != nil {
		if mgo.ErrNotFound == err {
			err = nil
		}
		return
	}
	excludelist := make(map[string]bool)
	excludelist[globalID] = true

	// Also get the suborganizations if they have been added
	var includesuborgs []string
	for _, member := range org.OrgMembers {
		for _, include := range org.IncludeSubOrgsOf {
			if member == include {
				includesuborgs = append(includesuborgs, include)
			}
		}
	}
	var subOrgs []Organization
	for _, subOrg := range includesuborgs {
		subOrganizations, err := m.GetSubOrganizations(subOrg)
		if err != nil && err != mgo.ErrNotFound {
			return false, err
		}
		subOrgs = append(subOrgs, subOrganizations...)
	}
	var orgs []string
	for _, suborganization := range subOrgs {
		orgs = append(orgs, suborganization.Globalid)
	}
	for _, owningOrganization := range append(org.OrgMembers, orgs...) {
		result, err = m.isOwnerOrMember(owningOrganization, username, excludelist)
		if result || err != nil {
			return
		}
	}

	return
}
1087487
func (k *KeybaseDaemonRPC) TeamAbandoned(
	ctx context.Context, tid keybase1.TeamID) error {
	k.log.CDebugf(ctx, "Implicit team %s abandoned", tid)
	k.setCachedTeamInfo(tid, idutil.TeamInfo{})
	k.config.KBFSOps().TeamAbandoned(ctx, tid)
	return nil
}
1009680
func NewMemberDevices(TeamMemberId string) *MemberDevices {
	s := new(MemberDevices)
	s.TeamMemberId = TeamMemberId
	return s
}
511177
func verifyMemberRoleInTeam(ctx context.Context, userID keybase1.UID, expectedRole keybase1.TeamRole,
	team *Team) (res keybase1.UserVersion, err error) {
	if expectedRole == keybase1.TeamRole_NONE {
		return res, nil
	}

	memberUV, err := team.chain().GetLatestUVWithUID(userID)
	if err != nil {
		return res, err
	}
	role, err := team.chain().GetUserRole(memberUV)
	if err != nil {
		return res, err
	}
	if role != expectedRole {
		return res, fmt.Errorf("unexpected member role: expected %v but actual role is %v", expectedRole, role)
	}
	return memberUV, nil
}
356964
func (l *TeamLoader) isImplicitAdminOf(ctx context.Context, teamID keybase1.TeamID, ancestorID *keybase1.TeamID,
	me keybase1.UserVersion, uv keybase1.UserVersion) (bool, error) {

	// IDs of ancestors that were not freshly polled.
	// Check them again with forceRepoll if the affirmative is not found cached.
	checkAgain := make(map[keybase1.TeamID]bool)

	check1 := func(chain *TeamSigChainState) bool {
		role, err := chain.GetUserRole(uv)
		if err != nil {
			return false
		}
		return role.IsAdminOrAbove()
	}

	i := 0
	for {
		i++
		if i >= 100 {
			// Break in case there's a bug in this loop.
			return false, fmt.Errorf("stuck in a loop while checking for implicit admin: %v", ancestorID)
		}

		// Use load2 so that we can use subteam-reader and get secretless teams.
		ancestor, err := l.load2(ctx, load2ArgT{
			teamID:        *ancestorID,
			reason:        "isImplicitAdminOf-1",
			me:            me,
			readSubteamID: &teamID,
		})
		if err != nil {
			return false, err
		}
		// Be wary, `ancestor` could be, and is likely, a secretless team.
		// Do not let it out of sight.
		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}

		if !ancestor.didRepoll {
			checkAgain[ancestorChain.GetID()] = true
		}

		if check1(&ancestorChain) {
			return true, nil
		}

		if !ancestorChain.IsSubteam() {
			break
		}
		// Get the next level up.
		ancestorID = ancestorChain.GetParentID()
	}

	// The answer was not found to be yes in the cache.
	// Try again with the teams that were not polled as they might have unseen updates.
	for ancestorID := range checkAgain {
		ancestor, err := l.load2(ctx, load2ArgT{
			teamID:        ancestorID,
			reason:        "isImplicitAdminOf-again",
			me:            me,
			forceRepoll:   true, // Get the latest info.
			readSubteamID: &teamID,
		})
		if err != nil {
			return false, err
		}
		// Be wary, `ancestor` could be, and is likely, a secretless team.
		// Do not let it out of sight.
		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
		if check1(&ancestorChain) {
			return true, nil
		}
	}

	return false, nil
}
610091
func (t *teamSigchainPlayer) sanityCheckMembers(members SCTeamMembers, options sanityCheckMembersOptions) (chainRoleUpdates, error) {
	type assignment struct {
		m    SCTeamMember
		role keybase1.TeamRole
	}
	var all []assignment

	if options.requireOwners {
		if members.Owners == nil {
			return nil, fmt.Errorf("team has no owner list")
		}
		if len(*members.Owners) < 1 {
			return nil, fmt.Errorf("team has no owners")
		}
	}
	if options.disallowOwners {
		if members.Owners != nil && len(*members.Owners) > 0 {
			return nil, fmt.Errorf("team has owners")
		}
	}
	if !options.allowRemovals {
		if members.None != nil && len(*members.None) != 0 {
			return nil, fmt.Errorf("team has removals in link")
		}
	}

	// Map from roles to users.
	res := make(map[keybase1.TeamRole][]keybase1.UserVersion)

	if members.Owners != nil && len(*members.Owners) > 0 {
		res[keybase1.TeamRole_OWNER] = nil
		for _, m := range *members.Owners {
			all = append(all, assignment{m, keybase1.TeamRole_OWNER})
		}
	}
	if members.Admins != nil && len(*members.Admins) > 0 {
		if options.onlyOwnersOrReaders {
			return nil, NewImplicitTeamOperationError("encountered add admin")
		}
		res[keybase1.TeamRole_ADMIN] = nil
		for _, m := range *members.Admins {
			all = append(all, assignment{m, keybase1.TeamRole_ADMIN})
		}
	}
	if members.Writers != nil && len(*members.Writers) > 0 {
		if options.onlyOwnersOrReaders {
			return nil, NewImplicitTeamOperationError("encountered add writer")
		}
		res[keybase1.TeamRole_WRITER] = nil
		for _, m := range *members.Writers {
			all = append(all, assignment{m, keybase1.TeamRole_WRITER})
		}
	}
	if members.Readers != nil && len(*members.Readers) > 0 {
		res[keybase1.TeamRole_READER] = nil
		for _, m := range *members.Readers {
			all = append(all, assignment{m, keybase1.TeamRole_READER})
		}
	}
	if members.None != nil && len(*members.None) > 0 {
		res[keybase1.TeamRole_NONE] = nil
		for _, m := range *members.None {
			all = append(all, assignment{m, keybase1.TeamRole_NONE})
		}
	}

	// Set of users who have already been seen.
	seen := make(map[keybase1.UserVersion]bool)

	for _, pair := range all {
		uv := keybase1.UserVersion(pair.m)

		if seen[uv] {
			return nil, fmt.Errorf("duplicate UID in members: %s", uv.Uid)
		}

		res[pair.role] = append(res[pair.role], uv)

		seen[uv] = true
	}

	return res, nil
}
401512
func (tx *AddMemberTx) addMemberByUPKV2(ctx context.Context, user keybase1.UserPlusKeysV2, role keybase1.TeamRole) (invite bool, err error) {
	team := tx.team
	g := team.G()

	uv := NewUserVersion(user.Uid, user.EldestSeqno)
	defer g.CTrace(ctx, fmt.Sprintf("AddMemberTx.addMemberByUPKV2(name:%q uv:%v, %v) to team: %q",
		user.Username, uv, role, team.Name()), func() error { return err })()

	if user.Status == keybase1.StatusCode_SCDeleted {
		return false, fmt.Errorf("User %q (%s) is deleted", user.Username, uv.Uid)
	}

	if role == keybase1.TeamRole_OWNER && team.IsSubteam() {
		return false, NewSubteamOwnersError()
	}

	if err := assertValidNewTeamMemberRole(role); err != nil {
		return false, err
	}

	hasPUK := len(user.PerUserKeys) > 0
	if !hasPUK {
		g.Log.CDebugf(ctx, "Invite required for %v", uv)
	}

	normalizedUsername := libkb.NewNormalizedUsername(user.Username)

	if team.IsMember(ctx, uv) {
		if !hasPUK {
			return false, fmt.Errorf("user %s is already a member of %q, yet they don't have a PUK",
				normalizedUsername, team.Name())
		}
		return false, libkb.ExistsError{Msg: fmt.Sprintf("user %s is already a member of team %q",
			normalizedUsername, team.Name())}
	}

	if existingUV, err := team.UserVersionByUID(ctx, uv.Uid); err == nil {
		// TODO: Might be able to collapse the two assertions together
		// - the one above with team.IsMember and this one which
		// checking Uid/Eldest.

		// There is an edge case where user is in the middle of
		// resetting (after reset, before provisioning) and has
		// EldestSeqno=0.
		if hasPUK && existingUV.EldestSeqno > uv.EldestSeqno {
			return false, fmt.Errorf("newer version of user %s (uid:%s) already exists in the team %q (%v > %v)",
				normalizedUsername, uv.Uid, team.Name(), existingUV.EldestSeqno, uv.EldestSeqno)
		}
	}

	curInvite, err := team.chain().FindActiveInvite(uv.TeamInviteName(), keybase1.NewTeamInviteTypeDefault(keybase1.TeamInviteCategory_KEYBASE))
	if err != nil {
		if _, ok := err.(libkb.NotFoundError); !ok {
			return false, err
		}
		curInvite = nil
		err = nil
	}
	if curInvite != nil && !hasPUK {
		return false, libkb.ExistsError{Msg: fmt.Sprintf("user %s is already invited to team %q",
			normalizedUsername, team.Name())}
	}

	// No going back after this point!

	tx.sweepKeybaseInvites(uv.Uid)

	// An admin is only allowed to remove an owner UV when, in the same link, replacing them with
	// a 'newer' UV with a greater eldest seqno.
	// So, if we're an admin re-adding an owner who does not yet have a PUK
	// then don't try to remove the owner's pre-reset UV.
	exceptAdminsRemovingOwners := !hasPUK
	tx.sweepCryptoMembers(ctx, uv.Uid, exceptAdminsRemovingOwners)

	if !hasPUK {
		tx.createKeybaseInvite(uv, role)
		return true, nil
	}
	tx.addMember(uv, role)
	return false, nil
}
112801
func (tx *AddMemberTx) AddMemberByUsername(ctx context.Context, username string, role keybase1.TeamRole) (err error) {
	team := tx.team
	g := team.G()
	m := libkb.NewMetaContext(ctx, g)

	defer m.Trace(fmt.Sprintf("AddMemberTx.AddMemberByUsername(%s,%v) to team %q", username, role, team.Name()), func() error { return err })()

	upak, err := engine.ResolveAndCheck(m, username, true /* useTracking */)
	if err != nil {
		return err
	}
	_, err = tx.addMemberByUPKV2(ctx, upak, role)
	return err
}
264533
func (mc *MembershipChanges) Changed() bool {
	return len(mc.AddedNodes) > 0 || len(mc.RemovedNodes) > 0
}
300337
func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error {
	return inTransaction(func(sess *DBSession) error {
		member, err := getTeamMember(sess, cmd.OrgId, cmd.TeamId, cmd.UserId)
		if err != nil {
			return err
		}

		if cmd.ProtectLastAdmin {
			_, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId)
			if err != nil {
				return err
			}
		}

		if cmd.Permission != m.PERMISSION_ADMIN { // make sure we don't get invalid permission levels in store
			cmd.Permission = 0
		}

		member.Permission = cmd.Permission
		_, err = sess.Cols("permission").Where("org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId).Update(member)

		return err
	})
}
294370
func ComputeMembershipChanges(oldMetadata *etcdraft.BlockMetadata, oldConsenters map[uint64]*etcdraft.Consenter, newConsenters []*etcdraft.Consenter) (mc *MembershipChanges, err error) {
	result := &MembershipChanges{
		NewConsenters:    map[uint64]*etcdraft.Consenter{},
		NewBlockMetadata: proto.Clone(oldMetadata).(*etcdraft.BlockMetadata),
		AddedNodes:       []*etcdraft.Consenter{},
		RemovedNodes:     []*etcdraft.Consenter{},
	}

	result.NewBlockMetadata.ConsenterIds = make([]uint64, len(newConsenters))

	var addedNodeIndex int
	currentConsentersSet := MembershipByCert(oldConsenters)
	for i, c := range newConsenters {
		if nodeID, exists := currentConsentersSet[string(c.ClientTlsCert)]; exists {
			result.NewBlockMetadata.ConsenterIds[i] = nodeID
			result.NewConsenters[nodeID] = c
			continue
		}
		addedNodeIndex = i
		result.AddedNodes = append(result.AddedNodes, c)
	}

	var deletedNodeID uint64
	newConsentersSet := ConsentersToMap(newConsenters)
	for nodeID, c := range oldConsenters {
		if _, exists := newConsentersSet[string(c.ClientTlsCert)]; !exists {
			result.RemovedNodes = append(result.RemovedNodes, c)
			deletedNodeID = nodeID
		}
	}

	switch {
	case len(result.AddedNodes) == 1 && len(result.RemovedNodes) == 1:
		// cert rotation
		result.RotatedNode = deletedNodeID
		result.NewBlockMetadata.ConsenterIds[addedNodeIndex] = deletedNodeID
		result.NewConsenters[deletedNodeID] = result.AddedNodes[0]
	case len(result.AddedNodes) == 1 && len(result.RemovedNodes) == 0:
		// new node
		nodeID := result.NewBlockMetadata.NextConsenterId
		result.NewConsenters[nodeID] = result.AddedNodes[0]
		result.NewBlockMetadata.ConsenterIds[addedNodeIndex] = nodeID
		result.NewBlockMetadata.NextConsenterId++
		result.ConfChange = &raftpb.ConfChange{
			NodeID: nodeID,
			Type:   raftpb.ConfChangeAddNode,
		}
	case len(result.AddedNodes) == 0 && len(result.RemovedNodes) == 1:
		// removed node
		nodeID := deletedNodeID
		result.ConfChange = &raftpb.ConfChange{
			Type:   raftpb.ConfChangeRemoveNode,
			NodeID: nodeID,
		}
		delete(result.NewConsenters, nodeID)
	case len(result.AddedNodes) == 0 && len(result.RemovedNodes) == 0:
		// no change
	default:
		// len(result.AddedNodes) > 1 || len(result.RemovedNodes) > 1 {
		return nil, errors.Errorf("update of more than one consenter at a time is not supported, requested changes: %s", result)
	}

	return result, nil
}
135784
func (s *Service) TeamMemberCreateOrUpdate(ctx context.Context, teamIdentity string, o TeamMemberCreateOrUpdateOpts) (*TeamMember, error) {
	var teamMember TeamMember
	return &teamMember, s.Put(ctx, &teamMember, fmt.Sprintf("/teams/%v/members", teamIdentity), o)
}
251182
func (hs *HTTPServer) UpdateTeamPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
	teamId := c.ParamsInt64(":teamId")
	orgId := c.OrgId

	if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil {
		return Error(403, "Not allowed to update team preferences.", err)
	}

	return updatePreferencesFor(orgId, 0, teamId, &dtoCmd)
}