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

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package engine
     5  
     6  import (
     7  	"github.com/keybase/client/go/libkb"
     8  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
     9  )
    10  
    11  type UntrackEngineArg struct {
    12  	Username   libkb.NormalizedUsername
    13  	Me         *libkb.User
    14  	SigVersion libkb.SigVersion
    15  }
    16  
    17  type UntrackEngine struct {
    18  	arg                   *UntrackEngineArg
    19  	signingKeyPub         libkb.GenericKey
    20  	untrackStatementBytes []byte
    21  	untrackStatement      *libkb.ProofMetadataRes
    22  	libkb.Contextified
    23  }
    24  
    25  // NewUntrackEngine creates a default UntrackEngine for tracking theirName.
    26  func NewUntrackEngine(g *libkb.GlobalContext, arg *UntrackEngineArg) *UntrackEngine {
    27  	if arg.SigVersion == libkb.KeybaseNullSigVersion {
    28  		arg.SigVersion = libkb.GetDefaultSigVersion(g)
    29  	}
    30  
    31  	return &UntrackEngine{
    32  		arg:          arg,
    33  		Contextified: libkb.NewContextified(g),
    34  	}
    35  }
    36  
    37  func (e *UntrackEngine) Name() string {
    38  	return "Untrack"
    39  }
    40  
    41  func (e *UntrackEngine) Prereqs() Prereqs {
    42  	return Prereqs{
    43  		Device: true,
    44  	}
    45  }
    46  
    47  func (e *UntrackEngine) RequiredUIs() []libkb.UIKind {
    48  	return []libkb.UIKind{}
    49  }
    50  
    51  func (e *UntrackEngine) SubConsumers() []libkb.UIConsumer {
    52  	return []libkb.UIConsumer{}
    53  }
    54  
    55  func (e *UntrackEngine) Run(m libkb.MetaContext) (err error) {
    56  	defer m.Trace("UntrackEngine#Run", &err)()
    57  
    58  	e.arg.Me, err = e.loadMe()
    59  	if err != nil {
    60  		return
    61  	}
    62  
    63  	them, remoteLink, localLink, err := e.loadThem(m)
    64  	if err != nil {
    65  		return
    66  	}
    67  
    68  	e.signingKeyPub, err = e.arg.Me.SigningKeyPub()
    69  	if err != nil {
    70  		return
    71  	}
    72  
    73  	e.untrackStatement, err = e.arg.Me.UntrackingProofFor(m, e.signingKeyPub, e.arg.SigVersion, them)
    74  	if err != nil {
    75  		return
    76  	}
    77  
    78  	e.untrackStatementBytes, err = e.untrackStatement.J.Marshal()
    79  	if err != nil {
    80  		return
    81  	}
    82  
    83  	e.G().Log.Debug("| Untracking statement: %s", string(e.untrackStatementBytes))
    84  
    85  	didUntrack := false
    86  
    87  	if localLink != nil {
    88  		err = e.storeLocalUntrack(m, them)
    89  		if err != nil {
    90  			return
    91  		}
    92  		didUntrack = true
    93  	}
    94  
    95  	if remoteLink != nil && !remoteLink.IsRevoked() {
    96  		err = e.storeRemoteUntrack(m, them)
    97  		if err != nil {
    98  			return
    99  		}
   100  		didUntrack = true
   101  	}
   102  
   103  	if !didUntrack {
   104  		err = libkb.NewUntrackError("User %s is already untracked", e.arg.Username)
   105  		return
   106  	}
   107  
   108  	e.G().UserChanged(m.Ctx(), e.arg.Me.GetUID())
   109  	e.G().UserChanged(m.Ctx(), them.GetUID())
   110  
   111  	e.G().NotifyRouter.HandleTrackingChanged(e.arg.Me.GetUID(), e.arg.Me.GetNormalizedName(), false)
   112  	e.G().NotifyRouter.HandleTrackingChanged(them.GetUID(), them.GetNormalizedName(), false)
   113  	m.G().IdentifyDispatch.NotifyTrackingSuccess(m, them.GetUID())
   114  
   115  	return
   116  }
   117  
   118  func (e *UntrackEngine) loadMe() (me *libkb.User, err error) {
   119  	return libkb.LoadMe(libkb.NewLoadUserArg(e.G()))
   120  }
   121  
   122  func (e *UntrackEngine) loadThem(m libkb.MetaContext) (them *libkb.User, remoteLink, localLink *libkb.TrackChainLink, err error) {
   123  	var rLink *libkb.TrackChainLink
   124  	trackMap := e.arg.Me.IDTable().GetTrackMap()
   125  	if links, ok := trackMap[e.arg.Username]; ok && (len(links) > 0) {
   126  		rLink = links[len(links)-1]
   127  	}
   128  
   129  	var uid keybase1.UID
   130  	uidTrusted := false
   131  	if rLink != nil {
   132  		if uid, err = rLink.GetTrackedUID(); err != nil {
   133  			return
   134  		}
   135  		uidTrusted = true
   136  	}
   137  
   138  	if uid.IsNil() {
   139  		res := e.G().Resolver.Resolve(m, e.arg.Username.String())
   140  		if err = res.GetError(); err != nil {
   141  			return
   142  		}
   143  
   144  		// This is an untrusted uid.
   145  		uid = res.GetUID()
   146  		if uid.IsNil() {
   147  			err = libkb.NewUntrackError("Could not resolve uid for @%s", e.arg.Username)
   148  			return
   149  		}
   150  	}
   151  
   152  	lLink, err := libkb.LocalTrackChainLinkFor(m, e.arg.Me.GetUID(), uid)
   153  	if err != nil {
   154  		return
   155  	}
   156  
   157  	if rLink == nil && lLink == nil {
   158  		err = libkb.NewUntrackError("You are not tracking %s", e.arg.Username)
   159  		return
   160  	}
   161  
   162  	if !uidTrusted {
   163  		if lLink == nil {
   164  			err = libkb.NewUntrackError("Could not verify resolved uid for @%s", e.arg.Username)
   165  			return
   166  		}
   167  
   168  		var trackedUsername libkb.NormalizedUsername
   169  		trackedUsername, err = lLink.GetTrackedUsername()
   170  		if err != nil {
   171  			return
   172  		}
   173  
   174  		if !e.arg.Username.Eq(trackedUsername) {
   175  			err = libkb.NewUntrackError("Username mismatch: expected @%s, got @%s", e.arg.Username, trackedUsername)
   176  			return
   177  		}
   178  	}
   179  
   180  	them = libkb.NewUserThin(e.arg.Username.String(), uid)
   181  	remoteLink = rLink
   182  	localLink = lLink
   183  	return
   184  }
   185  
   186  func (e *UntrackEngine) storeLocalUntrack(m libkb.MetaContext, them *libkb.User) error {
   187  	// Also do a removal in case of expiring local tracks
   188  	return libkb.RemoveLocalTracks(m, e.arg.Me.GetUID(), them.GetUID())
   189  }
   190  
   191  func (e *UntrackEngine) storeRemoteUntrack(m libkb.MetaContext, them *libkb.User) (err error) {
   192  	defer m.Trace("UntrackEngine#StoreRemoteUntrack", &err)()
   193  
   194  	me := e.arg.Me
   195  	arg := libkb.SecretKeyArg{
   196  		Me:      me,
   197  		KeyType: libkb.DeviceSigningKeyType,
   198  	}
   199  	var signingKey libkb.GenericKey
   200  	if signingKey, err = e.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(arg, "untracking signature")); err != nil {
   201  		return
   202  	}
   203  
   204  	sigVersion := e.arg.SigVersion
   205  	sig, sigID, linkID, err := libkb.MakeSig(
   206  		m,
   207  		signingKey,
   208  		libkb.LinkTypeUntrack,
   209  		e.untrackStatementBytes,
   210  		false, /* hasRevokes */
   211  		keybase1.SeqType_PUBLIC,
   212  		false, /* ignoreIfUnsupported */
   213  		me,
   214  		sigVersion)
   215  
   216  	if err != nil {
   217  		return
   218  	}
   219  
   220  	httpsArgs := libkb.HTTPArgs{
   221  		"sig_id_base":  libkb.S{Val: sigID.StripSuffix().String()},
   222  		"sig_id_short": libkb.S{Val: sigID.ToShortID()},
   223  		"sig":          libkb.S{Val: sig},
   224  		"uid":          libkb.UIDArg(them.GetUID()),
   225  		"type":         libkb.S{Val: "untrack"},
   226  		"signing_kid":  e.signingKeyPub.GetKID(),
   227  	}
   228  
   229  	if sigVersion == libkb.KeybaseSignatureV2 {
   230  		httpsArgs["sig_inner"] = libkb.S{Val: string(e.untrackStatementBytes)}
   231  	}
   232  
   233  	_, err = m.G().API.Post(m, libkb.APIArg{
   234  		Endpoint:    "follow",
   235  		SessionType: libkb.APISessionTypeREQUIRED,
   236  		Args:        httpsArgs,
   237  	})
   238  	if err != nil {
   239  		return err
   240  	}
   241  	return libkb.MerkleCheckPostedUserSig(m, me.GetUID(), e.untrackStatement.Seqno, linkID)
   242  }