mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 00:49:03 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #39303 (child of #25080). - Added `inherited_query_count` to `ListQueriesResponse` (thought of adding a brand new endpoint just for counting, but felt like extending the current one was good enough). In the parent task, [it was suggested](https://github.com/fleetdm/fleet/issues/25080#issuecomment-3326071574) to `"Depend on team list entity endpoint's count field / team entity count endpoint for whether or not to disable the manage automations button"`, which Rachael approved, so I went for this approach. - The `ManageQueryAutomationsModal` now fetches its own data with `merge_inherited = false` (meaning it only fetches non-inherited queries only). Previously, queries were passed down as props to it, which would not show the queries available to automate if the first page of queries were all inherited and the second page contained queries for that team (the user would have to navigate to the second page for the button to be enabled). ^ The fact that the modal fetches its own data is similar behavior to what is currently done in `Policies`. For queries, I noticed that we would need to add pagination within the `Manage Automations` modal, but that can be a follow-up. <img width="2480" height="1309" alt="Screenshot 2026-02-04 at 11 48 42 AM" src="https://github.com/user-attachments/assets/ebac79a5-a793-4708-9313-d9a697dfd7de" /> # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] QA'd all new/changed functionality manually https://github.com/user-attachments/assets/119f03b9-dde1-4bb9-9fee-6204b1a58879
113 lines
3.3 KiB
Go
113 lines
3.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/fleetdm/fleet/v4/server/mock"
|
|
"github.com/fleetdm/fleet/v4/server/ptr"
|
|
)
|
|
|
|
func TestGlobalScheduleAuth(t *testing.T) {
|
|
ds := new(mock.Store)
|
|
svc, ctx := newTestService(t, ds, nil, nil)
|
|
|
|
//
|
|
// All global schedule query methods use queries datastore methods.
|
|
//
|
|
|
|
ds.QueryFunc = func(ctx context.Context, id uint) (*fleet.Query, error) {
|
|
return &fleet.Query{
|
|
Name: "foobar",
|
|
Query: "SELECT 1;",
|
|
}, nil
|
|
}
|
|
ds.SaveQueryFunc = func(ctx context.Context, query *fleet.Query, shouldDiscardResults bool, shouldDeleteStats bool) error {
|
|
return nil
|
|
}
|
|
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
|
return &fleet.AppConfig{}, nil
|
|
}
|
|
ds.NewActivityFunc = func(
|
|
ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, details []byte, createdAt time.Time,
|
|
) error {
|
|
return nil
|
|
}
|
|
ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, int, *fleet.PaginationMetadata, error) {
|
|
return nil, 0, 0, nil, nil
|
|
}
|
|
ds.NewQueryFunc = func(ctx context.Context, query *fleet.Query, opts ...fleet.OptionalArg) (*fleet.Query, error) {
|
|
return &fleet.Query{}, nil
|
|
}
|
|
ds.DeleteQueryFunc = func(ctx context.Context, teamID *uint, name string) error {
|
|
return nil
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
user *fleet.User
|
|
shouldFailWrite bool
|
|
shouldFailRead bool
|
|
}{
|
|
{
|
|
name: "global admin",
|
|
user: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
|
|
shouldFailWrite: false,
|
|
shouldFailRead: false,
|
|
},
|
|
{
|
|
name: "global maintainer",
|
|
user: &fleet.User{GlobalRole: ptr.String(fleet.RoleMaintainer)},
|
|
shouldFailWrite: false,
|
|
shouldFailRead: false,
|
|
},
|
|
{
|
|
name: "global observer",
|
|
user: &fleet.User{GlobalRole: ptr.String(fleet.RoleObserver)},
|
|
shouldFailWrite: true,
|
|
shouldFailRead: false,
|
|
},
|
|
{
|
|
name: "team admin",
|
|
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleAdmin}}},
|
|
shouldFailWrite: true,
|
|
shouldFailRead: false,
|
|
},
|
|
{
|
|
name: "team maintainer",
|
|
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleMaintainer}}},
|
|
shouldFailWrite: true,
|
|
shouldFailRead: false,
|
|
},
|
|
{
|
|
name: "team observer",
|
|
user: &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserver}}},
|
|
shouldFailWrite: true,
|
|
shouldFailRead: false,
|
|
},
|
|
}
|
|
for _, tt := range testCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx := viewer.NewContext(ctx, viewer.Viewer{User: tt.user})
|
|
|
|
_, err := svc.GetGlobalScheduledQueries(ctx, fleet.ListOptions{})
|
|
checkAuthErr(t, tt.shouldFailRead, err)
|
|
|
|
_, err = svc.GlobalScheduleQuery(ctx, &fleet.ScheduledQuery{
|
|
Name: "query",
|
|
QueryName: "query",
|
|
Interval: 10,
|
|
})
|
|
checkAuthErr(t, tt.shouldFailWrite, err)
|
|
|
|
_, err = svc.ModifyGlobalScheduledQueries(ctx, 1, fleet.ScheduledQueryPayload{})
|
|
checkAuthErr(t, tt.shouldFailWrite, err)
|
|
|
|
err = svc.DeleteGlobalScheduledQueries(ctx, 1)
|
|
checkAuthErr(t, tt.shouldFailWrite, err)
|
|
})
|
|
}
|
|
}
|