github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/team_handler.go (about)

     1  // Copyright 2017 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  // Handlers for team-related gregor messages
     5  
     6  package service
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"strings"
    12  	"sync"
    13  
    14  	"golang.org/x/net/context"
    15  
    16  	"github.com/keybase/client/go/badges"
    17  	"github.com/keybase/client/go/gregor"
    18  	"github.com/keybase/client/go/libkb"
    19  	"github.com/keybase/client/go/protocol/gregor1"
    20  	"github.com/keybase/client/go/protocol/keybase1"
    21  	"github.com/keybase/client/go/teams"
    22  )
    23  
    24  const teamHandlerName = "teamHandler"
    25  
    26  type teamHandler struct {
    27  	libkb.Contextified
    28  	badger *badges.Badger
    29  
    30  	// Some work that comes from team gregor messages is done in
    31  	// background goroutine: rotateTeam and reset user badge
    32  	// dismissing, for now. Use a mutex to limit this to only one
    33  	// job at time.
    34  	teamHandlerBackgroundJob sync.Mutex
    35  }
    36  
    37  var _ libkb.GregorInBandMessageHandler = (*teamHandler)(nil)
    38  
    39  func newTeamHandler(g *libkb.GlobalContext, badger *badges.Badger) *teamHandler {
    40  	return &teamHandler{
    41  		Contextified: libkb.NewContextified(g),
    42  		badger:       badger,
    43  	}
    44  }
    45  
    46  func (r *teamHandler) Create(ctx context.Context, cli gregor1.IncomingInterface, category string, item gregor.Item) (bool, error) {
    47  	switch category {
    48  	case "team.clkr":
    49  		return true, r.rotateTeam(ctx, cli, item)
    50  	case "team.sbs":
    51  		return true, r.sharingBeforeSignup(ctx, cli, item)
    52  	case "team.openreq":
    53  		return true, r.openTeamAccessRequest(ctx, cli, item)
    54  	case "team.opensweep":
    55  		return true, r.openTeamSweepResetUsersRequest(ctx, cli, item)
    56  	case "team.change":
    57  		return true, r.changeTeam(ctx, cli, category, item, keybase1.TeamChangeSet{})
    58  	case "team.force_repoll":
    59  		return true, r.gotForceRepoll(ctx, cli, item)
    60  	case "team.rename":
    61  		return true, r.changeTeam(ctx, cli, category, item, keybase1.TeamChangeSet{Renamed: true})
    62  	case "team.delete":
    63  		return true, r.deleteTeam(ctx, cli, item)
    64  	case "team.exit":
    65  		return true, r.exitTeam(ctx, cli, item)
    66  	case "team.seitan":
    67  		return true, r.seitanCompletion(ctx, cli, item)
    68  	case "team.member_out_from_reset":
    69  		return true, r.memberOutFromReset(ctx, cli, item)
    70  	case "team.abandoned":
    71  		return true, r.abandonTeam(ctx, cli, item)
    72  	case "team.newly_added_to_team":
    73  		return true, r.newlyAddedToTeam(ctx, cli, item)
    74  	case "team.user_team_version":
    75  		return true, r.userTeamVersion(ctx, cli, item)
    76  	case "team.member_showcase_change":
    77  		return true, r.memberShowcaseChange(ctx, cli, item)
    78  	default:
    79  		if strings.HasPrefix(category, "team.") {
    80  			return false, fmt.Errorf("unknown teamHandler category: %q", category)
    81  		}
    82  		return false, nil
    83  	}
    84  }
    85  
    86  func (r *teamHandler) rotateTeam(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
    87  	r.G().Log.CDebugf(ctx, "teamHandler: team.clkr received")
    88  	var msg keybase1.TeamCLKRMsg
    89  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
    90  		r.G().Log.CDebugf(ctx, "error unmarshaling team.clkr item: %s", err)
    91  		return err
    92  	}
    93  	r.G().Log.CDebugf(ctx, "team.clkr unmarshaled: %+v", msg)
    94  
    95  	for _, uv := range msg.ResetUsersUntrusted {
    96  		// We don't use UIDMapper in HandleRotateRequest, but since
    97  		// server just told us that these users have reset, we might
    98  		// as well use that knowledge to refresh cache.
    99  
   100  		// Use ClearUIDAtEldestSeqno instead of InformOfEldestSeqno
   101  		// because usually uv.UserEldestSeqno (the "new EldestSeqno")
   102  		// will be 0, because user has just reset and hasn't
   103  		// reprovisioned yet
   104  
   105  		err := r.G().UIDMapper.ClearUIDAtEldestSeqno(ctx, r.G(), uv.Uid, uv.MemberEldestSeqno)
   106  		if err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	go func() {
   112  		r.teamHandlerBackgroundJob.Lock()
   113  		defer r.teamHandlerBackgroundJob.Unlock()
   114  
   115  		if err := teams.HandleRotateRequest(ctx, r.G(), msg); err != nil {
   116  			r.G().Log.CDebugf(ctx, "HandleRotateRequest failed with error: %s", err)
   117  			return
   118  		}
   119  
   120  		r.G().Log.CDebugf(ctx, "dismissing team.clkr item since rotate succeeded")
   121  		err := r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   122  		if err != nil {
   123  			r.G().Log.CDebugf(ctx, "error dismissing team.clkr item: %+v", err)
   124  		}
   125  	}()
   126  
   127  	return nil
   128  }
   129  
   130  func (r *teamHandler) memberOutFromReset(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   131  	nm := "team.member_out_from_reset"
   132  	r.G().Log.CDebugf(ctx, "teamHandler: %s received", nm)
   133  	var msg keybase1.TeamMemberOutFromReset
   134  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   135  		r.G().Log.CDebugf(ctx, "error unmarshaling %s item: %s", nm, err)
   136  		return err
   137  	}
   138  	r.G().Log.CDebugf(ctx, "%s unmarshaled: %+v", nm, msg)
   139  
   140  	if err := r.G().UIDMapper.ClearUIDAtEldestSeqno(ctx, r.G(), msg.ResetUser.Uid, msg.ResetUser.EldestSeqno); err != nil {
   141  		return err
   142  	}
   143  	// Favorites is misused to let people know when there are reset team
   144  	// members. This busts the relevant cache.
   145  	r.G().NotifyRouter.HandleFavoritesChanged(r.G().GetMyUID())
   146  	r.G().Log.CDebugf(ctx, "%s: cleared UIDMap cache for %s%%%d", nm, msg.ResetUser.Uid, msg.ResetUser.EldestSeqno)
   147  	return nil
   148  }
   149  
   150  type abandonMsg struct {
   151  	TeamID keybase1.TeamID `json:"team_id"`
   152  }
   153  
   154  func (r *teamHandler) abandonTeam(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   155  	nm := "team.abandoned"
   156  	r.G().Log.CDebugf(ctx, "teamHandler.abandonTeam: %s received", nm)
   157  	var msg abandonMsg
   158  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   159  		r.G().Log.CDebugf(ctx, "error unmarshaling %s item: %s", nm, err)
   160  		return err
   161  	}
   162  	r.G().Log.CDebugf(ctx, "teamHandler.abandonTeam: %s unmarshaled: %+v", nm, msg)
   163  
   164  	r.G().NotifyRouter.HandleTeamAbandoned(ctx, msg.TeamID)
   165  
   166  	r.G().Log.CDebugf(ctx, "teamHandler.abandonTeam: locally dismissing %s", nm)
   167  	if err := r.G().GregorState.LocalDismissItem(ctx, item.Metadata().MsgID()); err != nil {
   168  		r.G().Log.CDebugf(ctx, "teamHandler.abandonTeam: failed to locally dismiss msg %v", item.Metadata().MsgID())
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  func (r *teamHandler) gotForceRepoll(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   175  	r.G().Log.CDebugf(ctx, "teamHandler: gotForceRepoll received")
   176  	return teams.HandleForceRepollNotification(ctx, r.G(), item.DTime())
   177  }
   178  
   179  func (r *teamHandler) changeTeam(ctx context.Context, cli gregor1.IncomingInterface, category string,
   180  	item gregor.Item, changes keybase1.TeamChangeSet) error {
   181  	var rows []keybase1.TeamChangeRow
   182  	r.G().Log.CDebugf(ctx, "teamHandler: changeTeam received")
   183  	if err := json.Unmarshal(item.Body().Bytes(), &rows); err != nil {
   184  		r.G().Log.CDebugf(ctx, "error unmarshaling %s item: %s", category, err)
   185  		return err
   186  	}
   187  	r.G().Log.CDebugf(ctx, "%s unmarshaled: %+v", category, rows)
   188  	if err := teams.HandleChangeNotification(ctx, r.G(), rows, changes); err != nil {
   189  		return err
   190  	}
   191  
   192  	// Locally dismiss this now that we have processed it so we can
   193  	// avoid replaying it over and over.
   194  	if err := r.G().GregorState.LocalDismissItem(ctx, item.Metadata().MsgID()); err != nil {
   195  		r.G().Log.CDebugf(ctx, "failed to local dismiss team change: %s", err)
   196  	}
   197  	return nil
   198  }
   199  
   200  func (r *teamHandler) deleteTeam(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   201  	var rows []keybase1.TeamChangeRow
   202  	if err := json.Unmarshal(item.Body().Bytes(), &rows); err != nil {
   203  		r.G().Log.CDebugf(ctx, "error unmarshaling team.(change|rename) item: %s", err)
   204  		return err
   205  	}
   206  	r.G().Log.CDebugf(ctx, "teamHandler: team.delete unmarshaled: %+v", rows)
   207  
   208  	return teams.HandleDeleteNotification(ctx, r.G(), rows)
   209  }
   210  
   211  func (r *teamHandler) exitTeam(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   212  	var rows []keybase1.TeamExitRow
   213  	if err := json.Unmarshal(item.Body().Bytes(), &rows); err != nil {
   214  		r.G().Log.CDebugf(ctx, "error unmarshaling team.exit item: %s", err)
   215  		return err
   216  	}
   217  	r.G().Log.CDebugf(ctx, "teamHandler: team.exit unmarshaled: %+v", rows)
   218  	if err := teams.HandleExitNotification(ctx, r.G(), rows); err != nil {
   219  		return err
   220  	}
   221  
   222  	r.G().Log.Debug("dismissing team.exit: %v", item.Metadata().MsgID().String())
   223  	return r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   224  }
   225  
   226  func (r *teamHandler) userTeamVersion(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) (err error) {
   227  	mctx := libkb.NewMetaContext(ctx, r.G())
   228  	nm := "team.user_team_version"
   229  	defer mctx.Trace("teamHandler#userTeamVersion", &err)()
   230  	var obj keybase1.UserTeamVersionUpdate
   231  	err = json.Unmarshal(item.Body().Bytes(), &obj)
   232  	if err != nil {
   233  		mctx.Debug("Error unmarshaling %s item: %s", nm, err)
   234  		return err
   235  	}
   236  	return r.G().GetTeamRoleMapManager().Update(mctx, obj.Version)
   237  
   238  }
   239  
   240  func (r *teamHandler) newlyAddedToTeam(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   241  	nm := "team.newly_added_to_team"
   242  	r.G().Log.CDebugf(ctx, "teamHandler.newlyAddedToTeam: %s received", nm)
   243  	var rows []keybase1.TeamNewlyAddedRow
   244  	if err := json.Unmarshal(item.Body().Bytes(), &rows); err != nil {
   245  		r.G().Log.CDebugf(ctx, "error unmarshaling %s item: %s", nm, err)
   246  		return err
   247  	}
   248  	r.G().Log.CDebugf(ctx, "teamHandler.newlyAddedToTeam: %s unmarshaled: %+v", nm, rows)
   249  	if err := teams.HandleNewlyAddedToTeamNotification(ctx, r.G(), rows); err != nil {
   250  		return err
   251  	}
   252  
   253  	// Note there used to be a local dismissal here, but the newly_added_to_team needs
   254  	// to stay in the gregor state for badging to work.
   255  
   256  	return nil
   257  }
   258  
   259  func (r *teamHandler) sharingBeforeSignup(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   260  	r.G().Log.CDebugf(ctx, "teamHandler: team.sbs received")
   261  	var msg keybase1.TeamSBSMsg
   262  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   263  		r.G().Log.CDebugf(ctx, "error unmarshaling team.sbs item: %s", err)
   264  		return err
   265  	}
   266  	r.G().Log.CDebugf(ctx, "team.sbs unmarshaled: %+v", msg)
   267  
   268  	if err := teams.HandleSBSRequest(ctx, r.G(), msg); err != nil {
   269  		return err
   270  	}
   271  
   272  	r.G().Log.Debug("dismissing team.sbs item since it succeeded")
   273  	return r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   274  }
   275  
   276  func (r *teamHandler) openTeamAccessRequest(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   277  	r.G().Log.CDebugf(ctx, "teamHandler: team.openreq received")
   278  	var msg keybase1.TeamOpenReqMsg
   279  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   280  		r.G().Log.CDebugf(ctx, "error unmarshaling team.openreq item: %s", err)
   281  		return err
   282  	}
   283  	r.G().Log.CDebugf(ctx, "team.openreq unmarshaled: %+v", msg)
   284  
   285  	if err := teams.HandleOpenTeamAccessRequest(ctx, r.G(), msg); err != nil {
   286  		return err
   287  	}
   288  
   289  	r.G().Log.CDebugf(ctx, "dismissing team.openreq item since it succeeded")
   290  	return r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   291  }
   292  
   293  func (r *teamHandler) openTeamSweepResetUsersRequest(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   294  	r.G().Log.CDebugf(ctx, "teamHandler: team.opensweep received")
   295  	var msg keybase1.TeamOpenSweepMsg
   296  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   297  		r.G().Log.CDebugf(ctx, "error unmarshaling team.opensweep item: %s", err)
   298  		return err
   299  	}
   300  	r.G().Log.CDebugf(ctx, "team.opensweep unmarshaled: %+v", msg)
   301  
   302  	if err := teams.HandleOpenTeamSweepRequest(ctx, r.G(), msg); err != nil {
   303  		return err
   304  	}
   305  
   306  	r.G().Log.CDebugf(ctx, "dismissing team.opensweep item since it succeeded")
   307  	return r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   308  }
   309  
   310  func (r *teamHandler) memberShowcaseChange(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   311  	r.G().Log.CDebugf(ctx, "teamHandler: team.member_showchase_change received")
   312  
   313  	if err := teams.HandleTeamMemberShowcaseChange(ctx, r.G()); err != nil {
   314  		return err
   315  	}
   316  
   317  	r.G().Log.CDebugf(ctx, "dismissing team.member_showcase_change item since it succeeded")
   318  	return r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   319  }
   320  
   321  func (r *teamHandler) seitanCompletion(ctx context.Context, cli gregor1.IncomingInterface, item gregor.Item) error {
   322  	r.G().Log.CDebugf(ctx, "teamHandler: team.seitan received")
   323  	var msg keybase1.TeamSeitanMsg
   324  	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
   325  		r.G().Log.CDebugf(ctx, "error unmarshaling team.seitan item: %s", err)
   326  		return err
   327  	}
   328  	r.G().Log.CDebugf(ctx, "team.seitan unmarshaled: %+v", msg)
   329  
   330  	if err := teams.HandleTeamSeitan(ctx, r.G(), msg); err != nil {
   331  		return err
   332  	}
   333  
   334  	r.G().Log.CDebugf(ctx, "dismissing team.seitan item since it succeeded")
   335  	return r.G().GregorState.DismissItem(ctx, cli, item.Metadata().MsgID())
   336  }
   337  
   338  func (r *teamHandler) Dismiss(ctx context.Context, cli gregor1.IncomingInterface, category string, item gregor.Item) (bool, error) {
   339  	return false, nil
   340  }
   341  
   342  func (r *teamHandler) IsAlive() bool {
   343  	return true
   344  }
   345  
   346  func (r *teamHandler) Name() string {
   347  	return teamHandlerName
   348  }