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

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package idutil
     6  
     7  import (
     8  	"fmt"
     9  	"sync"
    10  	"time"
    11  
    12  	"golang.org/x/net/context"
    13  
    14  	"github.com/keybase/client/go/externals"
    15  	"github.com/keybase/client/go/kbfs/kbfscodec"
    16  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    17  	"github.com/keybase/client/go/kbfs/kbfsmd"
    18  	"github.com/keybase/client/go/kbfs/tlf"
    19  	kbname "github.com/keybase/client/go/kbun"
    20  	"github.com/keybase/client/go/protocol/keybase1"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  func checkContext(ctx context.Context) error {
    25  	select {
    26  	case <-ctx.Done():
    27  		return errors.WithStack(ctx.Err())
    28  	default:
    29  		return nil
    30  	}
    31  }
    32  
    33  type localUserMap map[keybase1.UID]LocalUser
    34  
    35  func (m localUserMap) getLocalUser(uid keybase1.UID) (LocalUser, error) {
    36  	user, ok := m[uid]
    37  	if !ok {
    38  		return LocalUser{}, NoSuchUserError{uid.String()}
    39  	}
    40  	return user, nil
    41  }
    42  
    43  type localTeamMap map[keybase1.TeamID]TeamInfo
    44  
    45  func (m localTeamMap) getLocalTeam(tid keybase1.TeamID) (TeamInfo, error) {
    46  	team, ok := m[tid]
    47  	if !ok {
    48  		return TeamInfo{}, NoSuchTeamError{tid.String()}
    49  	}
    50  	return team, nil
    51  }
    52  
    53  type localTeamSettingsMap map[keybase1.TeamID]keybase1.KBFSTeamSettings
    54  
    55  type localImplicitTeamMap map[keybase1.TeamID]ImplicitTeamInfo
    56  
    57  // DaemonLocal implements KeybaseDaemon using an in-memory user
    58  // and session store, and a given favorite store.
    59  type DaemonLocal struct {
    60  	codec kbfscodec.Codec
    61  
    62  	// lock protects everything below.
    63  	lock               sync.Mutex
    64  	localUsers         localUserMap
    65  	localTeams         localTeamMap
    66  	localTeamSettings  localTeamSettingsMap
    67  	localImplicitTeams localImplicitTeamMap
    68  	currentUID         keybase1.UID
    69  	asserts            map[string]keybase1.UserOrTeamID
    70  	implicitAsserts    map[string]keybase1.TeamID
    71  	merkleRoot         keybase1.MerkleRootV2
    72  	merkleTime         time.Time
    73  }
    74  
    75  // SetCurrentUID sets the current UID.
    76  func (dl *DaemonLocal) SetCurrentUID(uid keybase1.UID) {
    77  	dl.lock.Lock()
    78  	defer dl.lock.Unlock()
    79  	// TODO: Send out notifications.
    80  	dl.currentUID = uid
    81  }
    82  
    83  func (dl *DaemonLocal) assertionToIDLocked(ctx context.Context,
    84  	assertion string) (id keybase1.UserOrTeamID, err error) {
    85  	expr, err := externals.AssertionParseAndOnlyStatic(ctx, assertion)
    86  	if err != nil {
    87  		return keybase1.UserOrTeamID(""), err
    88  	}
    89  	urls := expr.CollectUrls(nil)
    90  	if len(urls) == 0 {
    91  		return keybase1.UserOrTeamID(""), errors.New("No assertion URLs")
    92  	}
    93  
    94  	for _, url := range urls {
    95  		var currID keybase1.UserOrTeamID
    96  		switch {
    97  		case url.IsUID():
    98  			currID = url.ToUID().AsUserOrTeam()
    99  		case url.IsTeamID():
   100  			currID = url.ToTeamID().AsUserOrTeam()
   101  		default:
   102  			key, val := url.ToKeyValuePair()
   103  			a := fmt.Sprintf("%s@%s", val, key)
   104  			if url.IsKeybase() && key != "team" {
   105  				a = val
   106  			}
   107  			var ok bool
   108  			currID, ok = dl.asserts[a]
   109  			if !ok {
   110  				return keybase1.UserOrTeamID(""), NoSuchUserError{a}
   111  			}
   112  		}
   113  		if id != keybase1.UserOrTeamID("") && currID != id {
   114  			return keybase1.UserOrTeamID(""),
   115  				errors.New("AND assertions resolve to different UIDs")
   116  		}
   117  		id = currID
   118  	}
   119  	return id, nil
   120  }
   121  
   122  // Resolve implements the KeybaseService interface for DaemonLocal.
   123  func (dl *DaemonLocal) Resolve(
   124  	ctx context.Context, assertion string, _ keybase1.OfflineAvailability) (
   125  	kbname.NormalizedUsername, keybase1.UserOrTeamID, error) {
   126  	if err := checkContext(ctx); err != nil {
   127  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   128  	}
   129  
   130  	dl.lock.Lock()
   131  	defer dl.lock.Unlock()
   132  	id, err := dl.assertionToIDLocked(ctx, assertion)
   133  	if err != nil {
   134  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   135  	}
   136  
   137  	if id.IsUser() {
   138  		u, err := dl.localUsers.getLocalUser(id.AsUserOrBust())
   139  		if err != nil {
   140  			return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   141  		}
   142  		return u.Name, id, nil
   143  	}
   144  
   145  	// Otherwise it's a team
   146  	ti, err := dl.localTeams.getLocalTeam(id.AsTeamOrBust())
   147  	if err != nil {
   148  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   149  	}
   150  
   151  	_, ok := dl.localImplicitTeams[id.AsTeamOrBust()]
   152  	if ok {
   153  		// An implicit team exists, so Resolve shouldn't work.  The
   154  		// caller should use `ResolveImplicitTeamByID` instead.
   155  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""),
   156  			fmt.Errorf("Team ID %s is an implicit team", id)
   157  	}
   158  
   159  	return ti.Name, id, nil
   160  }
   161  
   162  // Identify implements the KeybaseService interface for DaemonLocal.
   163  func (dl *DaemonLocal) Identify(
   164  	ctx context.Context, assertion, _ string,
   165  	offline keybase1.OfflineAvailability) (
   166  	kbname.NormalizedUsername, keybase1.UserOrTeamID, error) {
   167  	// The local daemon doesn't need to distinguish resolves from
   168  	// identifies.
   169  	return dl.Resolve(ctx, assertion, offline)
   170  }
   171  
   172  // NormalizeSocialAssertion implements the KeybaseService interface
   173  // for DaemonLocal.
   174  func (dl *DaemonLocal) NormalizeSocialAssertion(
   175  	ctx context.Context, assertion string) (keybase1.SocialAssertion, error) {
   176  	socialAssertion, isSocialAssertion := externals.NormalizeSocialAssertionStatic(ctx, assertion)
   177  	if !isSocialAssertion {
   178  		return keybase1.SocialAssertion{}, fmt.Errorf("Invalid social assertion")
   179  	}
   180  	return socialAssertion, nil
   181  }
   182  
   183  func (dl *DaemonLocal) resolveForImplicitTeam(
   184  	ctx context.Context, name string, r []kbname.NormalizedUsername,
   185  	ur []keybase1.SocialAssertion,
   186  	resolvedIDs map[kbname.NormalizedUsername]keybase1.UserOrTeamID) (
   187  	[]kbname.NormalizedUsername, []keybase1.SocialAssertion, error) {
   188  	id, err := dl.assertionToIDLocked(ctx, name)
   189  	if err == nil {
   190  		u, err := dl.localUsers.getLocalUser(id.AsUserOrBust())
   191  		if err != nil {
   192  			return nil, nil, err
   193  		}
   194  		r = append(r, u.Name)
   195  		resolvedIDs[u.Name] = id
   196  	} else {
   197  		a, ok := externals.NormalizeSocialAssertionStatic(ctx, name)
   198  		if !ok {
   199  			return nil, nil, fmt.Errorf("Bad assertion: %s", name)
   200  		}
   201  		ur = append(ur, a)
   202  	}
   203  	return r, ur, nil
   204  }
   205  
   206  // ResolveIdentifyImplicitTeam implements the KeybaseService interface
   207  // for DaemonLocal.
   208  func (dl *DaemonLocal) ResolveIdentifyImplicitTeam(
   209  	ctx context.Context, assertions, suffix string, tlfType tlf.Type,
   210  	doIdentifies bool, reason string, _ keybase1.OfflineAvailability) (
   211  	ImplicitTeamInfo, error) {
   212  	if err := checkContext(ctx); err != nil {
   213  		return ImplicitTeamInfo{}, err
   214  	}
   215  
   216  	if tlfType != tlf.Private && tlfType != tlf.Public {
   217  		return ImplicitTeamInfo{}, fmt.Errorf(
   218  			"Invalid implicit team TLF type: %s", tlfType)
   219  	}
   220  
   221  	dl.lock.Lock()
   222  	defer dl.lock.Unlock()
   223  
   224  	// Canonicalize the name.
   225  	writerNames, readerNames, _, err :=
   226  		SplitAndNormalizeTLFName(assertions, tlfType)
   227  	if err != nil {
   228  		return ImplicitTeamInfo{}, err
   229  	}
   230  	var writers, readers []kbname.NormalizedUsername
   231  	var unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion
   232  	resolvedIDs := make(map[kbname.NormalizedUsername]keybase1.UserOrTeamID)
   233  	for _, w := range writerNames {
   234  		writers, unresolvedWriters, err = dl.resolveForImplicitTeam(
   235  			ctx, w, writers, unresolvedWriters, resolvedIDs)
   236  		if err != nil {
   237  			return ImplicitTeamInfo{}, err
   238  		}
   239  	}
   240  	for _, r := range readerNames {
   241  		readers, unresolvedReaders, err = dl.resolveForImplicitTeam(
   242  			ctx, r, readers, unresolvedReaders, resolvedIDs)
   243  		if err != nil {
   244  			return ImplicitTeamInfo{}, err
   245  		}
   246  	}
   247  
   248  	var extensions []tlf.HandleExtension
   249  	if len(suffix) != 0 {
   250  		extensions, err = tlf.ParseHandleExtensionSuffix(suffix)
   251  		if err != nil {
   252  			return ImplicitTeamInfo{}, err
   253  		}
   254  	}
   255  	name := tlf.MakeCanonicalName(
   256  		writers, unresolvedWriters, readers, unresolvedReaders, extensions)
   257  
   258  	key := fmt.Sprintf("%s:%s", tlfType.String(), name)
   259  	tid, ok := dl.implicitAsserts[key]
   260  	if ok {
   261  		return dl.localImplicitTeams[tid], nil
   262  	}
   263  
   264  	// If the implicit team doesn't exist, always create it.
   265  
   266  	// Need to make the team info as well, so get the list of user
   267  	// names and resolve them.  Auto-generate an implicit team name.
   268  	implicitName := kbname.NormalizedUsername(
   269  		fmt.Sprintf("_implicit_%d", len(dl.localTeams)))
   270  	teams := makeLocalTeams(
   271  		[]kbname.NormalizedUsername{implicitName}, len(dl.localTeams), tlfType)
   272  	info := teams[0]
   273  	info.Writers = make(map[keybase1.UID]bool, len(writerNames))
   274  	for _, w := range writers {
   275  		id, ok := resolvedIDs[w]
   276  		if !ok {
   277  			return ImplicitTeamInfo{}, fmt.Errorf("No resolved writer %s", w)
   278  		}
   279  		info.Writers[id.AsUserOrBust()] = true
   280  	}
   281  	if len(readerNames) > 0 {
   282  		info.Readers = make(map[keybase1.UID]bool, len(readerNames))
   283  		for _, r := range readers {
   284  			id, ok := resolvedIDs[r]
   285  			if !ok {
   286  				return ImplicitTeamInfo{}, fmt.Errorf(
   287  					"No resolved reader %s", r)
   288  
   289  			}
   290  			info.Readers[id.AsUserOrBust()] = true
   291  		}
   292  	}
   293  	// Unresolved users don't need to go in the team info, they're
   294  	// irrelvant until they're resolved.  TODO: add resolved users
   295  	// into existing teams they should be on.
   296  
   297  	tid = teams[0].TID
   298  	dl.implicitAsserts[key] = tid
   299  	dl.localTeams[tid] = info
   300  
   301  	asUserName := kbname.NormalizedUsername(name)
   302  	iteamInfo := ImplicitTeamInfo{
   303  		// TODO: use the "preferred" canonical format here by listing
   304  		// the logged-in user first?
   305  		Name: asUserName,
   306  		TID:  tid,
   307  	}
   308  	dl.localImplicitTeams[tid] = iteamInfo
   309  	return iteamInfo, nil
   310  }
   311  
   312  // ResolveImplicitTeamByID implements the KeybaseService interface
   313  // for DaemonLocal.
   314  func (dl *DaemonLocal) ResolveImplicitTeamByID(
   315  	ctx context.Context, teamID keybase1.TeamID) (name string, err error) {
   316  	if err := checkContext(ctx); err != nil {
   317  		return "", err
   318  	}
   319  
   320  	dl.lock.Lock()
   321  	defer dl.lock.Unlock()
   322  
   323  	info, ok := dl.localImplicitTeams[teamID]
   324  	if !ok {
   325  		return "", NoSuchTeamError{teamID.String()}
   326  	}
   327  	return info.Name.String(), nil
   328  }
   329  
   330  // LoadUserPlusKeys implements the KeybaseService interface for
   331  // DaemonLocal.
   332  func (dl *DaemonLocal) LoadUserPlusKeys(
   333  	ctx context.Context, uid keybase1.UID, _ keybase1.KID,
   334  	_ keybase1.OfflineAvailability) (UserInfo, error) {
   335  	if err := checkContext(ctx); err != nil {
   336  		return UserInfo{}, err
   337  	}
   338  
   339  	dl.lock.Lock()
   340  	defer dl.lock.Unlock()
   341  	u, err := dl.localUsers.getLocalUser(uid)
   342  	if err != nil {
   343  		return UserInfo{}, err
   344  	}
   345  
   346  	var infoCopy UserInfo
   347  	if err := kbfscodec.Update(dl.codec, &infoCopy, u.UserInfo); err != nil {
   348  		return UserInfo{}, err
   349  	}
   350  	return infoCopy, nil
   351  }
   352  
   353  // LoadTeamPlusKeys implements the KeybaseService interface for
   354  // DaemonLocal.
   355  func (dl *DaemonLocal) LoadTeamPlusKeys(
   356  	ctx context.Context, tid keybase1.TeamID, _ tlf.Type, _ kbfsmd.KeyGen,
   357  	_ keybase1.UserVersion, _ kbfscrypto.VerifyingKey,
   358  	_ keybase1.TeamRole, _ keybase1.OfflineAvailability) (TeamInfo, error) {
   359  	if err := checkContext(ctx); err != nil {
   360  		return TeamInfo{}, err
   361  	}
   362  
   363  	dl.lock.Lock()
   364  	defer dl.lock.Unlock()
   365  	t, err := dl.localTeams.getLocalTeam(tid)
   366  	if err != nil {
   367  		return TeamInfo{}, err
   368  	}
   369  
   370  	// Copy the info since it contains a map that might be mutated.
   371  	var infoCopy TeamInfo
   372  	if err := kbfscodec.Update(dl.codec, &infoCopy, t); err != nil {
   373  		return TeamInfo{}, err
   374  	}
   375  	return infoCopy, nil
   376  }
   377  
   378  // CreateTeamTLF implements the KeybaseService interface for
   379  // DaemonLocal.
   380  func (dl *DaemonLocal) CreateTeamTLF(
   381  	ctx context.Context, teamID keybase1.TeamID, tlfID tlf.ID) (err error) {
   382  	if err := checkContext(ctx); err != nil {
   383  		return err
   384  	}
   385  
   386  	// TODO: add check to make sure the private/public suffix of the
   387  	// team ID matches that of the tlf ID.
   388  	dl.lock.Lock()
   389  	defer dl.lock.Unlock()
   390  	iteamInfo, isImplicit := dl.localImplicitTeams[teamID]
   391  	if isImplicit {
   392  		iteamInfo.TlfID = tlfID
   393  		dl.localImplicitTeams[teamID] = iteamInfo
   394  	}
   395  	_, isRegularTeam := dl.localTeams[teamID]
   396  	if !isImplicit && !isRegularTeam {
   397  		return NoSuchTeamError{teamID.String()}
   398  	}
   399  	dl.localTeamSettings[teamID] = keybase1.KBFSTeamSettings{
   400  		TlfID: keybase1.TLFID(tlfID.String())}
   401  	return nil
   402  }
   403  
   404  // GetTeamSettings implements the KeybaseService interface for
   405  // DaemonLocal.
   406  func (dl *DaemonLocal) GetTeamSettings(
   407  	ctx context.Context, teamID keybase1.TeamID,
   408  	_ keybase1.OfflineAvailability) (
   409  	settings keybase1.KBFSTeamSettings, err error) {
   410  	if err := checkContext(ctx); err != nil {
   411  		return keybase1.KBFSTeamSettings{}, err
   412  	}
   413  
   414  	dl.lock.Lock()
   415  	defer dl.lock.Unlock()
   416  	return dl.localTeamSettings[teamID], nil
   417  }
   418  
   419  // GetCurrentMerkleRoot implements the MerkleRootGetter interface for
   420  // DaemonLocal.
   421  func (dl *DaemonLocal) GetCurrentMerkleRoot(ctx context.Context) (
   422  	keybase1.MerkleRootV2, time.Time, error) {
   423  	if err := checkContext(ctx); err != nil {
   424  		return keybase1.MerkleRootV2{}, time.Time{}, err
   425  	}
   426  
   427  	dl.lock.Lock()
   428  	defer dl.lock.Unlock()
   429  	return dl.merkleRoot, dl.merkleTime, nil
   430  }
   431  
   432  // VerifyMerkleRoot implements the MerkleRootGetter interface for
   433  // DaemonLocal.
   434  func (dl *DaemonLocal) VerifyMerkleRoot(
   435  	_ context.Context, _ keybase1.MerkleRootV2, _ keybase1.KBFSRoot) error {
   436  	return nil
   437  }
   438  
   439  // SetCurrentMerkleRoot is a helper function, useful for tests, to set
   440  // the current Merkle root.
   441  func (dl *DaemonLocal) SetCurrentMerkleRoot(
   442  	root keybase1.MerkleRootV2, rootTime time.Time) {
   443  	dl.lock.Lock()
   444  	defer dl.lock.Unlock()
   445  	dl.merkleRoot = root
   446  	dl.merkleTime = rootTime
   447  }
   448  
   449  // CurrentSession implements the KeybaseService interface for
   450  // DaemonLocal.
   451  func (dl *DaemonLocal) CurrentSession(ctx context.Context, sessionID int) (
   452  	SessionInfo, error) {
   453  	if err := checkContext(ctx); err != nil {
   454  		return SessionInfo{}, err
   455  	}
   456  
   457  	dl.lock.Lock()
   458  	defer dl.lock.Unlock()
   459  	u, err := dl.localUsers.getLocalUser(dl.currentUID)
   460  	if err != nil {
   461  		return SessionInfo{}, err
   462  	}
   463  	return SessionInfo{
   464  		Name:           u.Name,
   465  		UID:            u.UID,
   466  		CryptPublicKey: u.GetCurrentCryptPublicKey(),
   467  		VerifyingKey:   u.GetCurrentVerifyingKey(),
   468  	}, nil
   469  }
   470  
   471  // AddNewAssertionForTest makes newAssertion, which should be a single
   472  // assertion that doesn't already resolve to anything, resolve to the
   473  // same UID as oldAssertion, which should be an arbitrary assertion
   474  // that does already resolve to something.  It returns the UID of the
   475  // user associated with the given assertions.
   476  func (dl *DaemonLocal) AddNewAssertionForTest(
   477  	oldAssertion, newAssertion string) (keybase1.UID, error) {
   478  	dl.lock.Lock()
   479  	defer dl.lock.Unlock()
   480  	id, err := dl.assertionToIDLocked(context.Background(), oldAssertion)
   481  	if err != nil {
   482  		return keybase1.UID(""), err
   483  	}
   484  	uid := id.AsUserOrBust()
   485  
   486  	lu, err := dl.localUsers.getLocalUser(uid)
   487  	if err != nil {
   488  		return keybase1.UID(""), err
   489  	}
   490  	lu.Asserts = append(lu.Asserts, newAssertion)
   491  	dl.asserts[newAssertion] = id
   492  	dl.localUsers[uid] = lu
   493  	return uid, nil
   494  }
   495  
   496  // AddNewAssertionForTestOrBust is like AddNewAssertionForTest, but
   497  // panics if there's an error.
   498  func (dl *DaemonLocal) AddNewAssertionForTestOrBust(
   499  	oldAssertion, newAssertion string) keybase1.UID {
   500  	uid, err := dl.AddNewAssertionForTest(oldAssertion, newAssertion)
   501  	if err != nil {
   502  		panic(err)
   503  	}
   504  	return uid
   505  }
   506  
   507  // ChangeTeamNameForTest updates the name of an existing team.
   508  func (dl *DaemonLocal) ChangeTeamNameForTest(
   509  	oldName, newName string) (keybase1.TeamID, error) {
   510  	dl.lock.Lock()
   511  	defer dl.lock.Unlock()
   512  	oldAssert := oldName + "@team"
   513  	newAssert := newName + "@team"
   514  
   515  	id, ok := dl.asserts[oldAssert]
   516  	if !ok {
   517  		return keybase1.TeamID(""),
   518  			fmt.Errorf("No such old team name: %s", oldName)
   519  	}
   520  	tid, err := id.AsTeam()
   521  	if err != nil {
   522  		return keybase1.TeamID(""), err
   523  	}
   524  
   525  	team, ok := dl.localTeams[tid]
   526  	if !ok {
   527  		return keybase1.TeamID(""),
   528  			fmt.Errorf("No such old team name: %s/%s", oldName, tid)
   529  	}
   530  	team.Name = kbname.NormalizedUsername(newName)
   531  	dl.localTeams[tid] = team
   532  
   533  	dl.asserts[newAssert] = id
   534  	delete(dl.asserts, oldAssert)
   535  	return tid, nil
   536  }
   537  
   538  // RemoveAssertionForTest removes the given assertion.  Should only be
   539  // used by tests.
   540  func (dl *DaemonLocal) RemoveAssertionForTest(assertion string) {
   541  	dl.lock.Lock()
   542  	defer dl.lock.Unlock()
   543  	delete(dl.asserts, assertion)
   544  }
   545  
   546  // AddTeamWriterForTest adds a writer to a team.  Should only be used
   547  // by tests.
   548  func (dl *DaemonLocal) AddTeamWriterForTest(
   549  	tid keybase1.TeamID, uid keybase1.UID) (
   550  	username kbname.NormalizedUsername, isImplicit bool, err error) {
   551  	dl.lock.Lock()
   552  	defer dl.lock.Unlock()
   553  	t, err := dl.localTeams.getLocalTeam(tid)
   554  	if err != nil {
   555  		return "", false, err
   556  	}
   557  
   558  	if t.Writers == nil {
   559  		t.Writers = make(map[keybase1.UID]bool)
   560  	}
   561  	t.Writers[uid] = true
   562  	delete(t.Readers, uid)
   563  	dl.localTeams[tid] = t
   564  	_, isImplicit = dl.localImplicitTeams[tid]
   565  	return t.Name, isImplicit, nil
   566  }
   567  
   568  // RemoveTeamWriterForTest removes a writer from a team.  Should only
   569  // be used by tests.
   570  func (dl *DaemonLocal) RemoveTeamWriterForTest(
   571  	tid keybase1.TeamID, uid keybase1.UID) (kbname.NormalizedUsername, error) {
   572  	dl.lock.Lock()
   573  	defer dl.lock.Unlock()
   574  	t, err := dl.localTeams.getLocalTeam(tid)
   575  	if err != nil {
   576  		return "", err
   577  	}
   578  
   579  	if _, ok := t.Writers[uid]; ok {
   580  		u, err := dl.localUsers.getLocalUser(uid)
   581  		if err != nil {
   582  			return "", err
   583  		}
   584  		if t.LastWriters == nil {
   585  			t.LastWriters = make(
   586  				map[kbfscrypto.VerifyingKey]keybase1.MerkleRootV2)
   587  		}
   588  		for _, key := range u.VerifyingKeys {
   589  			t.LastWriters[key] = dl.merkleRoot
   590  		}
   591  	}
   592  	dl.merkleRoot.Seqno++
   593  
   594  	delete(t.Writers, uid)
   595  	delete(t.Readers, uid)
   596  
   597  	dl.localTeams[tid] = t
   598  	return t.Name, nil
   599  }
   600  
   601  // AddTeamReaderForTest adds a reader to a team.  Should only be used
   602  // by tests.
   603  func (dl *DaemonLocal) AddTeamReaderForTest(
   604  	tid keybase1.TeamID, uid keybase1.UID) (kbname.NormalizedUsername, error) {
   605  	dl.lock.Lock()
   606  	defer dl.lock.Unlock()
   607  	t, err := dl.localTeams.getLocalTeam(tid)
   608  	if err != nil {
   609  		return "", err
   610  	}
   611  
   612  	if t.Writers[uid] {
   613  		// Being a writer already implies being a reader.
   614  		return "", nil
   615  	}
   616  
   617  	if t.Readers == nil {
   618  		t.Readers = make(map[keybase1.UID]bool)
   619  	}
   620  	t.Readers[uid] = true
   621  	dl.localTeams[tid] = t
   622  	return t.Name, nil
   623  }
   624  
   625  // AddTeamKeyForTest adds a key to a team.  Should only be used by
   626  // tests.
   627  func (dl *DaemonLocal) AddTeamKeyForTest(
   628  	tid keybase1.TeamID, newKeyGen kbfsmd.KeyGen,
   629  	newKey kbfscrypto.TLFCryptKey) error {
   630  	dl.lock.Lock()
   631  	defer dl.lock.Unlock()
   632  	t, err := dl.localTeams.getLocalTeam(tid)
   633  	if err != nil {
   634  		return err
   635  	}
   636  
   637  	t.CryptKeys[newKeyGen] = newKey
   638  	if newKeyGen > t.LatestKeyGen {
   639  		t.LatestKeyGen = newKeyGen
   640  		// Only need to save back to the map if we've modified a
   641  		// non-reference type like the latest key gen.
   642  		dl.localTeams[tid] = t
   643  	}
   644  	return nil
   645  }
   646  
   647  func (dl *DaemonLocal) addTeamsForTestLocked(teams []TeamInfo) {
   648  	for _, t := range teams {
   649  		dl.localTeams[t.TID] = t
   650  		dl.asserts[string(t.Name)+"@team"] = t.TID.AsUserOrTeam()
   651  	}
   652  }
   653  
   654  // AddTeamsForTest adds teams to this daemon.  Should only be used by
   655  // tests.
   656  func (dl *DaemonLocal) AddTeamsForTest(teams []TeamInfo) {
   657  	dl.lock.Lock()
   658  	defer dl.lock.Unlock()
   659  	dl.addTeamsForTestLocked(teams)
   660  }
   661  
   662  // GetLocalUser returns the `LocalUser` object for the given UID.
   663  func (dl *DaemonLocal) GetLocalUser(uid keybase1.UID) (LocalUser, error) {
   664  	dl.lock.Lock()
   665  	defer dl.lock.Unlock()
   666  	return dl.localUsers.getLocalUser(uid)
   667  }
   668  
   669  // SetLocalUser sets the `LocalUser` object for the given UID.
   670  func (dl *DaemonLocal) SetLocalUser(uid keybase1.UID, user LocalUser) {
   671  	dl.lock.Lock()
   672  	defer dl.lock.Unlock()
   673  	dl.localUsers[uid] = user
   674  }
   675  
   676  // GetIDForAssertion returns the UID associated with the given
   677  // assertion.
   678  func (dl *DaemonLocal) GetIDForAssertion(assertion string) (
   679  	keybase1.UserOrTeamID, bool) {
   680  	dl.lock.Lock()
   681  	defer dl.lock.Unlock()
   682  	id, ok := dl.asserts[assertion]
   683  	return id, ok
   684  }
   685  
   686  // GetLocalUsers returns all of the users tracked by this daemon.
   687  func (dl *DaemonLocal) GetLocalUsers() (res []LocalUser) {
   688  	dl.lock.Lock()
   689  	defer dl.lock.Unlock()
   690  	for _, u := range dl.localUsers {
   691  		res = append(res, u)
   692  	}
   693  	return res
   694  }
   695  
   696  // NewDaemonLocal constructs a new DaemonLocal, initialized to contain
   697  // the given users and teams.
   698  func NewDaemonLocal(
   699  	currentUID keybase1.UID, users []LocalUser,
   700  	teams []TeamInfo, codec kbfscodec.Codec) *DaemonLocal {
   701  	localUserMap := make(localUserMap)
   702  	asserts := make(map[string]keybase1.UserOrTeamID)
   703  	for _, u := range users {
   704  		localUserMap[u.UID] = u
   705  		for _, a := range u.Asserts {
   706  			asserts[a] = u.UID.AsUserOrTeam()
   707  		}
   708  		asserts[string(u.Name)] = u.UID.AsUserOrTeam()
   709  	}
   710  	dl := &DaemonLocal{
   711  		codec:              codec,
   712  		localUsers:         localUserMap,
   713  		localTeams:         make(localTeamMap),
   714  		localTeamSettings:  make(localTeamSettingsMap),
   715  		localImplicitTeams: make(localImplicitTeamMap),
   716  		asserts:            asserts,
   717  		implicitAsserts:    make(map[string]keybase1.TeamID),
   718  		currentUID:         currentUID,
   719  		// TODO: let test fill in valid merkle root.
   720  	}
   721  	dl.AddTeamsForTest(teams)
   722  	return dl
   723  }