Skip to content
This repository has been archived by the owner on Jul 7, 2020. It is now read-only.

Commit

Permalink
RPC: teams.findNextMerkleRootAfterTeamRemovalBySigningKey (keybase#12360
Browse files Browse the repository at this point in the history
)

- a thin wrapper around libkb.FindNextMerkleRootAfterTeamRemoval, for use from KBFS,
who knows the user's UID and signing key from the MD, and wants to work backward to when,
if ever, this user was kicked out of the team, or downgraded to writer.
  • Loading branch information
maxtaco authored Jun 14, 2018
1 parent 182d1ee commit a099cad
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 7 deletions.
35 changes: 35 additions & 0 deletions go/protocol/keybase1/teams.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions go/service/teams.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,3 +549,10 @@ func (h *TeamsHandler) FindNextMerkleRootAfterTeamRemoval(ctx context.Context, a
mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
return libkb.FindNextMerkleRootAfterTeamRemoval(mctx, arg)
}

func (h *TeamsHandler) FindNextMerkleRootAfterTeamRemovalBySigningKey(ctx context.Context, arg keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg) (res keybase1.NextMerkleRootRes, err error) {
ctx = libkb.WithLogTag(ctx, "TM")
defer h.G().CTraceTimed(ctx, fmt.Sprintf("FindNextMerkleRootAfterTeamRemovalBySigningKey(%+v)", arg), func() error { return err })()
mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
return teams.FindNextMerkleRootAfterRemoval(mctx, arg)
}
16 changes: 16 additions & 0 deletions go/teams/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ func (t TeamSigChainState) GetUserLogPoint(user keybase1.UserVersion) *keybase1.
return &tmp
}

// GetLastUserLogPointWithPredicate gets the last user logpoint in the series for which the given
// predicate is true.
func (t TeamSigChainState) GetLastUserLogPointWithPredicate(user keybase1.UserVersion, f func(keybase1.UserLogPoint) bool) *keybase1.UserLogPoint {
points := t.inner.UserLog[user]
if len(points) == 0 {
return nil
}
for i := len(points) - 1; i >= 0; i-- {
if f(points[i]) {
tmp := points[i].DeepCopy()
return &tmp
}
}
return nil
}

func (t TeamSigChainState) GetAdminUserLogPoint(user keybase1.UserVersion) *keybase1.UserLogPoint {
ret := t.GetUserLogPoint(user)
if ret == nil {
Expand Down
39 changes: 34 additions & 5 deletions go/teams/member_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,39 @@ func TestMemberAddInvalidRole(t *testing.T) {
assertRole(tc, name, other.Username, keybase1.TeamRole_NONE)
}

// testFindNextMerkleRootAfterRemoval tests teams.FindNextMerkleRootAfterRemoval, which is
// a thin wrapper around libkb.FindNextMerkleRootAfterTeamRemoval. Test that the plumbing works
// properly. Pass in the values from the libkb inner function, to confirm that this function
// returns the same.
func testFindNextMerkleRootAfterRemoval(t *testing.T, tc libkb.TestContext, user *kbtest.FakeUser, id keybase1.TeamID, seqno keybase1.Seqno) {
m := libkb.NewMetaContextForTest(tc)
upak, _, err := tc.G.GetUPAKLoader().LoadV2(libkb.NewLoadUserArgWithMetaContext(m).WithUID(user.GetUID()))
require.NoError(t, err)
require.NotNil(t, upak)
var signingKey keybase1.KID
for kid, obj := range upak.Current.DeviceKeys {
if obj.Base.IsSibkey {
signingKey = kid
break
}
}
require.False(t, signingKey.IsNil())
res, err := FindNextMerkleRootAfterRemoval(m, keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg{
Uid: user.GetUID(),
SigningKey: signingKey,
IsPublic: false,
Team: id,
})
require.NoError(t, err)
require.NotNil(t, res.Res)
require.Equal(t, res.Res.Seqno, seqno)
}

// Check that `libkb.FindNextMerkleRootAfterTeamRemoval` works. To do so,
// find the logpoint on the team where the user was removed, and pass it in.
// Check for success simply by asserting that the Merkle Root seqno bumps
// forward after the removal went into the team sigchain.
func pollForNextMerkleRootAfterRemoval(t *testing.T, tc libkb.TestContext, user *kbtest.FakeUser, teamName string) {
func pollForNextMerkleRootAfterRemovalViaLibkb(t *testing.T, tc libkb.TestContext, user *kbtest.FakeUser, teamName string) (tid keybase1.TeamID, seqno keybase1.Seqno) {

m := libkb.NewMetaContextForTest(tc)
team, err := GetForTestByStringName(context.TODO(), tc.G, teamName)
Expand All @@ -195,14 +223,14 @@ func pollForNextMerkleRootAfterRemoval(t *testing.T, tc libkb.TestContext, user
if err == nil {
require.NotNil(t, res.Res)
require.True(t, res.Res.Seqno > logPoint.SigMeta.PrevMerkleRootSigned.Seqno)
return
return team.ID, res.Res.Seqno
}

if merr, ok := err.(libkb.MerkleClientError); ok && merr.IsNotFound() {
t.Logf("Failed to find a root, trying again to wait for merkled")
} else {
require.NoError(t, err)
return
return tid, seqno
}

if delay < time.Second {
Expand All @@ -212,6 +240,7 @@ func pollForNextMerkleRootAfterRemoval(t *testing.T, tc libkb.TestContext, user
time.Sleep(delay)
}
t.Fatalf("failed to find a suitable merkle root with team removal")
return tid, seqno
}

func TestMemberRemove(t *testing.T) {
Expand All @@ -232,8 +261,8 @@ func TestMemberRemove(t *testing.T) {
assertRole(tc, name, owner.Username, keybase1.TeamRole_OWNER)
assertRole(tc, name, other.Username, keybase1.TeamRole_NONE)

pollForNextMerkleRootAfterRemoval(t, tc, other, name)

teamID, seqno := pollForNextMerkleRootAfterRemovalViaLibkb(t, tc, other, name)
testFindNextMerkleRootAfterRemoval(t, tc, other, teamID, seqno)
}

func TestMemberChangeRole(t *testing.T) {
Expand Down
42 changes: 42 additions & 0 deletions go/teams/service_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1531,3 +1531,45 @@ func ChangeTeamAvatar(mctx libkb.MetaContext, arg keybase1.UploadTeamAvatarArg)
}
return nil
}

func FindNextMerkleRootAfterRemoval(mctx libkb.MetaContext, arg keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg) (res keybase1.NextMerkleRootRes, err error) {
defer mctx.CTrace(fmt.Sprintf("teams.FindNextMerkleRootAfterRemoval(%+v)", arg), func() error { return err })()

team, err := Load(mctx.Ctx(), mctx.G(), keybase1.LoadTeamArg{
ID: arg.Team,
Public: arg.IsPublic,
ForceRepoll: false,
NeedAdmin: false,
})
if err != nil {
return res, err
}
upak, _, err := mctx.G().GetUPAKLoader().LoadV2(libkb.NewLoadUserArgWithMetaContext(mctx).
WithUID(arg.Uid).
WithPublicKeyOptional().
WithForcePoll(false))
if err != nil {
return res, err
}

vers, _ := upak.FindKID(arg.SigningKey)
if vers == nil {
return res, libkb.NotFoundError{Msg: fmt.Sprintf("KID %s not found for %s", arg.SigningKey, arg.Uid)}
}

uv := vers.ToUserVersion()
logPoint := team.chain().GetLastUserLogPointWithPredicate(uv, func(p keybase1.UserLogPoint) bool {
return p.Role == keybase1.TeamRole_NONE || p.Role == keybase1.TeamRole_READER
})
if logPoint == nil {
return res, libkb.NotFoundError{Msg: fmt.Sprintf("no downgraded log point for user found")}
}

return libkb.FindNextMerkleRootAfterTeamRemoval(mctx, keybase1.FindNextMerkleRootAfterTeamRemovalArg{
Uid: arg.Uid,
Team: arg.Team,
IsPublic: arg.IsPublic,
TeamSigchainSeqno: logPoint.SigMeta.SigChainLocation.Seqno,
Prev: logPoint.SigMeta.PrevMerkleRootSigned,
})
}
7 changes: 7 additions & 0 deletions protocol/avdl/keybase1/teams.avdl
Original file line number Diff line number Diff line change
Expand Up @@ -852,4 +852,11 @@ protocol teams {
*/
NextMerkleRootRes findNextMerkleRootAfterTeamRemoval(UID uid, TeamID team, boolean isPublic, Seqno teamSigchainSeqno, MerkleRootV2 prev);

/**
FindNextMerkleRootAfterTeamRemovalBySigningKey find the first Merkle root that contains the user
with the given signing key being removed from the given team. If there are several such instances,
we will return just the last one.
*/
NextMerkleRootRes findNextMerkleRootAfterTeamRemovalBySigningKey(UID uid, KID signingKey, TeamID team, boolean isPublic);

}
Loading

0 comments on commit a099cad

Please sign in to comment.