mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 00:49:03 +00:00
An incorrect authorization check allowed non-admin users to modify the details of other users. We now enforce the appropriate authorization so that unprivileged users can only modify their own details. Thanks to 'Quikke' for the report.
326 lines
6.3 KiB
Go
326 lines
6.3 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-kit/kit/log"
|
|
"github.com/gorilla/mux"
|
|
"github.com/kolide/fleet/server/config"
|
|
"github.com/kolide/fleet/server/datastore/inmem"
|
|
"github.com/kolide/fleet/server/kolide"
|
|
"github.com/kolide/fleet/server/mock"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestAPIRoutes(t *testing.T) {
|
|
ds, err := inmem.New(config.TestConfig())
|
|
assert.Nil(t, err)
|
|
|
|
svc, err := newTestService(ds, nil)
|
|
assert.Nil(t, err)
|
|
|
|
r := mux.NewRouter()
|
|
ke := MakeKolideServerEndpoints(svc, "CHANGEME")
|
|
kh := makeKolideKitHandlers(ke, nil)
|
|
attachKolideAPIRoutes(r, kh)
|
|
handler := mux.NewRouter()
|
|
handler.PathPrefix("/").Handler(r)
|
|
|
|
var routes = []struct {
|
|
verb string
|
|
uri string
|
|
}{
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/users",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/users",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/users/1",
|
|
},
|
|
{
|
|
verb: "PATCH",
|
|
uri: "/api/v1/kolide/users/1",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/login",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/forgot_password",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/reset_password",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/me",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/config",
|
|
},
|
|
{
|
|
verb: "PATCH",
|
|
uri: "/api/v1/kolide/config",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/invites",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/invites",
|
|
},
|
|
{
|
|
verb: "DELETE",
|
|
uri: "/api/v1/kolide/invites/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/queries/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/queries",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/queries",
|
|
},
|
|
{
|
|
verb: "PATCH",
|
|
uri: "/api/v1/kolide/queries/1",
|
|
},
|
|
{
|
|
verb: "DELETE",
|
|
uri: "/api/v1/kolide/queries/1",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/queries/delete",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/queries/run",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/packs/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/packs",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/packs",
|
|
},
|
|
{
|
|
verb: "PATCH",
|
|
uri: "/api/v1/kolide/packs/1",
|
|
},
|
|
{
|
|
verb: "DELETE",
|
|
uri: "/api/v1/kolide/packs/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/packs/1/scheduled",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/schedule",
|
|
},
|
|
{
|
|
verb: "DELETE",
|
|
uri: "/api/v1/kolide/schedule/1",
|
|
},
|
|
{
|
|
verb: "PATCH",
|
|
uri: "/api/v1/kolide/schedule/1",
|
|
}, {
|
|
verb: "POST",
|
|
uri: "/api/v1/osquery/enroll",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/osquery/config",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/osquery/distributed/read",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/osquery/distributed/write",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/osquery/log",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/labels/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/labels",
|
|
},
|
|
{
|
|
verb: "POST",
|
|
uri: "/api/v1/kolide/labels",
|
|
},
|
|
{
|
|
verb: "DELETE",
|
|
uri: "/api/v1/kolide/labels/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/hosts/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/hosts",
|
|
},
|
|
{
|
|
verb: "DELETE",
|
|
uri: "/api/v1/kolide/hosts/1",
|
|
},
|
|
{
|
|
verb: "GET",
|
|
uri: "/api/v1/kolide/host_summary",
|
|
},
|
|
}
|
|
|
|
for _, route := range routes {
|
|
t.Run(fmt.Sprintf(": %v", route.uri), func(st *testing.T) {
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(
|
|
recorder,
|
|
httptest.NewRequest(route.verb, route.uri, nil),
|
|
)
|
|
assert.NotEqual(st, 404, recorder.Code)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestModifyUserPermissions(t *testing.T) {
|
|
var (
|
|
admin, enabled bool
|
|
uid uint
|
|
)
|
|
ms := new(mock.Store)
|
|
ms.SessionByKeyFunc = func(key string) (*kolide.Session, error) {
|
|
return &kolide.Session{AccessedAt: time.Now(), UserID: uid}, nil
|
|
}
|
|
ms.DestroySessionFunc = func(session *kolide.Session) error {
|
|
return nil
|
|
}
|
|
ms.MarkSessionAccessedFunc = func(session *kolide.Session) error {
|
|
return nil
|
|
}
|
|
ms.UserByIDFunc = func(id uint) (*kolide.User, error) {
|
|
return &kolide.User{ID: id, Enabled: enabled, Admin: admin}, nil
|
|
}
|
|
ms.SaveUserFunc = func(u *kolide.User) error {
|
|
// Return an error so that the endpoint returns
|
|
return errors.New("foo")
|
|
}
|
|
|
|
svc, err := newTestService(ms, nil)
|
|
assert.Nil(t, err)
|
|
|
|
handler := MakeHandler(svc, "CHANGEME", log.NewNopLogger())
|
|
|
|
testCases := []struct {
|
|
ActingUserID uint
|
|
ActingUserAdmin bool
|
|
ActingUserEnabled bool
|
|
TargetUserID uint
|
|
Authorized bool
|
|
}{
|
|
// Disabled regular user
|
|
{
|
|
ActingUserID: 1,
|
|
ActingUserAdmin: false,
|
|
ActingUserEnabled: false,
|
|
TargetUserID: 1,
|
|
Authorized: false,
|
|
},
|
|
// Enabled regular user acting on self
|
|
{
|
|
ActingUserID: 1,
|
|
ActingUserAdmin: false,
|
|
ActingUserEnabled: true,
|
|
TargetUserID: 1,
|
|
Authorized: true,
|
|
},
|
|
// Enabled regular user acting on other
|
|
{
|
|
ActingUserID: 2,
|
|
ActingUserAdmin: false,
|
|
ActingUserEnabled: true,
|
|
TargetUserID: 1,
|
|
Authorized: false,
|
|
},
|
|
// Disabled admin user
|
|
{
|
|
ActingUserID: 1,
|
|
ActingUserAdmin: true,
|
|
ActingUserEnabled: false,
|
|
TargetUserID: 1,
|
|
Authorized: false,
|
|
},
|
|
// Enabled admin user acting on self
|
|
{
|
|
ActingUserID: 1,
|
|
ActingUserAdmin: true,
|
|
ActingUserEnabled: true,
|
|
TargetUserID: 1,
|
|
Authorized: true,
|
|
},
|
|
// Enabled admin user acting on other
|
|
{
|
|
ActingUserID: 2,
|
|
ActingUserAdmin: true,
|
|
ActingUserEnabled: true,
|
|
TargetUserID: 1,
|
|
Authorized: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range testCases {
|
|
t.Run("", func(t *testing.T) {
|
|
// Set user params
|
|
uid = tt.ActingUserID
|
|
admin, enabled = tt.ActingUserAdmin, tt.ActingUserEnabled
|
|
|
|
recorder := httptest.NewRecorder()
|
|
path := fmt.Sprintf("/api/v1/kolide/users/%d", tt.TargetUserID)
|
|
request := httptest.NewRequest("PATCH", path, bytes.NewBufferString("{}"))
|
|
// Bearer token generated with session key CHANGEME on jwt.io
|
|
request.Header.Add("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2tleSI6ImZsb29wIn0.ukCPTFvgSJrXbHH2QeAMx3EKwoMh1OmhP3xXxy5I-Wk")
|
|
|
|
handler.ServeHTTP(recorder, request)
|
|
if tt.Authorized {
|
|
assert.NotEqual(t, 403, recorder.Code)
|
|
} else {
|
|
assert.Equal(t, 403, recorder.Code)
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
}
|