Skip to content

Commit

Permalink
method for revokation by id with user key checking added
Browse files Browse the repository at this point in the history
  • Loading branch information
swithek committed Feb 28, 2020
1 parent 86a2690 commit 9fd02f8
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
25 changes: 25 additions & 0 deletions manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,31 @@ func (m *Manager) RevokeByID(ctx context.Context, id string) error {
return m.store.DeleteByID(ctx, id)
}

// RevokeByIDExt deletes session by its ID after checking if it
// belongs to the same user as the one in the context.
// Function will be no-op and return nil, if no session is found.
func (m *Manager) RevokeByIDExt(ctx context.Context, id string) error {
s1, ok := FromContext(ctx)
if !ok {
return nil
}

s2, ok, err := m.store.FetchByID(ctx, id)
if err != nil {
return err
}

if !ok {
return nil
}

if s2.UserKey != s1.UserKey {
return errors.New("session can be revoked only by its owner")
}

return m.store.DeleteByID(ctx, id)
}

// RevokeOther deletes all sessions of the same user key as session stored in the
// context currently has. Context session will be excluded.
// Function will be no-op and return nil, if context session is not set.
Expand Down
138 changes: 138 additions & 0 deletions manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,144 @@ func TestRevokeByID(t *testing.T) {
}
}

func TestRevokeByIDExt(t *testing.T) {
type check func(*testing.T, *StoreMock, error)

checks := func(cc ...check) []check { return cc }

hasErr := func(e bool) check {
return func(t *testing.T, _ *StoreMock, err error) {
if e && err == nil {
t.Error("want non-nil, got nil")
} else if !e && err != nil {
t.Errorf("want nil, got %v", err)
}
}
}

wasFetchByIDCalled := func(count int, id string) check {
return func(t *testing.T, s *StoreMock, _ error) {
ff := s.FetchByIDCalls()
if len(ff) != count {
t.Errorf("want %d, got %d", count, len(ff))
}

if len(ff) > 0 && ff[0].ID != id {
t.Errorf("want %q, got %q", id, ff[0].ID)
}
}
}

wasDeleteByIDCalled := func(count int, id string) check {
return func(t *testing.T, s *StoreMock, _ error) {
ff := s.DeleteByIDCalls()
if len(ff) != count {
t.Errorf("want %d, got %d", count, len(ff))
}

if len(ff) > 0 && ff[0].ID != id {
t.Errorf("want %q, got %q", id, ff[0].ID)
}
}
}

storeStub := func(ses Session, isSes bool, err1, err2 error) *StoreMock {
return &StoreMock{
FetchByIDFunc: func(_ context.Context, _ string) (Session, bool, error) {
return ses, isSes, err1
},
DeleteByIDFunc: func(_ context.Context, _ string) error {
return err2
},
}
}

ses := Session{ID: "123", UserKey: "user123"}
cc := map[string]struct {
Store *StoreMock
Ctx context.Context
ID string
Checks []check
}{
"No active session": {
Store: storeStub(ses, true, nil, nil),
Ctx: context.Background(),
ID: ses.ID,
Checks: checks(
hasErr(false),
wasFetchByIDCalled(0, ""),
wasDeleteByIDCalled(0, ""),
),
},
"Error returned by store.FetchByID": {
Store: storeStub(Session{}, false, errors.New("error"), nil),
Ctx: NewContext(context.Background(), ses),
ID: ses.ID,
Checks: checks(
hasErr(true),
wasFetchByIDCalled(1, ses.ID),
wasDeleteByIDCalled(0, ""),
),
},
"Session not found": {
Store: storeStub(Session{}, false, nil, nil),
Ctx: NewContext(context.Background(), ses),
ID: ses.ID,
Checks: checks(
hasErr(false),
wasFetchByIDCalled(1, ses.ID),
wasDeleteByIDCalled(0, ""),
),
},
"User key mismatch": {
Store: storeStub(func() Session {
tmp := ses
tmp.UserKey = "user222"
return tmp
}(), true, nil, nil),
Ctx: NewContext(context.Background(), ses),
ID: ses.ID,
Checks: checks(
hasErr(true),
wasFetchByIDCalled(1, ses.ID),
wasDeleteByIDCalled(0, ""),
),
},
"Error returned by store.DeleteByID": {
Store: storeStub(ses, true, nil, errors.New("error")),
Ctx: NewContext(context.Background(), ses),
ID: ses.ID,
Checks: checks(
hasErr(true),
wasFetchByIDCalled(1, ses.ID),
wasDeleteByIDCalled(1, ses.ID),
),
},
"Successful revoke": {
Store: storeStub(ses, true, nil, nil),
Ctx: NewContext(context.Background(), ses),
ID: ses.ID,
Checks: checks(
hasErr(false),
wasFetchByIDCalled(1, ses.ID),
wasDeleteByIDCalled(1, ses.ID),
),
},
}

for cn, c := range cc {
c := c
t.Run(cn, func(t *testing.T) {
t.Parallel()
m := Manager{store: c.Store}
err := m.RevokeByIDExt(c.Ctx, c.ID)
for _, ch := range c.Checks {
ch(t, c.Store, err)
}
})
}
}

func TestRevokeOther(t *testing.T) {
type check func(*testing.T, *StoreMock, error)

Expand Down

0 comments on commit 9fd02f8

Please sign in to comment.