mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Calendar config updates -- policy table now has calendar_events_enabled (#17645)
# Checklist for submitter - [ ] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Added/updated tests - [x] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [x] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [x] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
d3e1716572
commit
63e9d49dfc
@ -452,20 +452,6 @@ spec:
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Apply calendar integration
|
// Apply calendar integration
|
||||||
validPolicyID := uint(10)
|
|
||||||
validPolicyName := "validPolicy"
|
|
||||||
ds.PoliciesByNameFunc = func(ctx context.Context, names []string, teamID uint) (map[string]*fleet.Policy, error) {
|
|
||||||
var policies = make(map[string]*fleet.Policy)
|
|
||||||
for _, name := range names {
|
|
||||||
if name != validPolicyName {
|
|
||||||
return nil, ¬FoundError{}
|
|
||||||
}
|
|
||||||
policies[name] = &fleet.Policy{
|
|
||||||
PolicyData: fleet.PolicyData{ID: validPolicyID, TeamID: &teamsByName["team1"].ID, Name: validPolicyName},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return policies, nil
|
|
||||||
}
|
|
||||||
filename = writeTmpYml(
|
filename = writeTmpYml(
|
||||||
t, `
|
t, `
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@ -477,8 +463,6 @@ spec:
|
|||||||
google_calendar:
|
google_calendar:
|
||||||
email: `+googleCalEmail+`
|
email: `+googleCalEmail+`
|
||||||
enable_calendar_events: true
|
enable_calendar_events: true
|
||||||
policies:
|
|
||||||
- name: `+validPolicyName+`
|
|
||||||
webhook_url: https://example.com/webhook
|
webhook_url: https://example.com/webhook
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
@ -488,7 +472,6 @@ spec:
|
|||||||
t, fleet.TeamGoogleCalendarIntegration{
|
t, fleet.TeamGoogleCalendarIntegration{
|
||||||
Email: googleCalEmail,
|
Email: googleCalEmail,
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Policies: []*fleet.PolicyRef{{Name: validPolicyName, ID: validPolicyID}},
|
|
||||||
WebhookURL: "https://example.com/webhook",
|
WebhookURL: "https://example.com/webhook",
|
||||||
}, *teamsByName["team1"].Config.Integrations.GoogleCalendar,
|
}, *teamsByName["team1"].Config.Integrations.GoogleCalendar,
|
||||||
)
|
)
|
||||||
@ -505,8 +488,6 @@ spec:
|
|||||||
google_calendar:
|
google_calendar:
|
||||||
email: not_present_globally@example.com
|
email: not_present_globally@example.com
|
||||||
enable_calendar_events: true
|
enable_calendar_events: true
|
||||||
policies:
|
|
||||||
- name: `+validPolicyName+`
|
|
||||||
webhook_url: https://example.com/webhook
|
webhook_url: https://example.com/webhook
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
@ -514,26 +495,6 @@ spec:
|
|||||||
_, err = runAppNoChecks([]string{"apply", "-f", filename})
|
_, err = runAppNoChecks([]string{"apply", "-f", filename})
|
||||||
assert.ErrorContains(t, err, "email must match a global Google Calendar integration email")
|
assert.ErrorContains(t, err, "email must match a global Google Calendar integration email")
|
||||||
|
|
||||||
// Apply calendar integration -- invalid policy name
|
|
||||||
filename = writeTmpYml(
|
|
||||||
t, `
|
|
||||||
apiVersion: v1
|
|
||||||
kind: team
|
|
||||||
spec:
|
|
||||||
team:
|
|
||||||
name: team1
|
|
||||||
integrations:
|
|
||||||
google_calendar:
|
|
||||||
email: `+googleCalEmail+`
|
|
||||||
enable_calendar_events: true
|
|
||||||
policies:
|
|
||||||
- name: invalidPolicy
|
|
||||||
webhook_url: https://example.com/webhook
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
_, err = runAppNoChecks([]string{"apply", "-f", filename})
|
|
||||||
assert.ErrorContains(t, err, "name is invalid")
|
|
||||||
|
|
||||||
// Apply calendar integration -- invalid webhook destination
|
// Apply calendar integration -- invalid webhook destination
|
||||||
filename = writeTmpYml(
|
filename = writeTmpYml(
|
||||||
t, `
|
t, `
|
||||||
@ -546,8 +507,6 @@ spec:
|
|||||||
google_calendar:
|
google_calendar:
|
||||||
email: `+googleCalEmail+`
|
email: `+googleCalEmail+`
|
||||||
enable_calendar_events: true
|
enable_calendar_events: true
|
||||||
policies:
|
|
||||||
- name: `+validPolicyName+`
|
|
||||||
webhook_url: bozo
|
webhook_url: bozo
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
|
@ -340,6 +340,7 @@ func TestGetHosts(t *testing.T) {
|
|||||||
AuthorEmail: "alice@example.com",
|
AuthorEmail: "alice@example.com",
|
||||||
Resolution: ptr.String("Some resolution"),
|
Resolution: ptr.String("Some resolution"),
|
||||||
TeamID: ptr.Uint(1),
|
TeamID: ptr.Uint(1),
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
},
|
},
|
||||||
Response: "passes",
|
Response: "passes",
|
||||||
},
|
},
|
||||||
@ -354,6 +355,7 @@ func TestGetHosts(t *testing.T) {
|
|||||||
AuthorEmail: "alice@example.com",
|
AuthorEmail: "alice@example.com",
|
||||||
Resolution: nil,
|
Resolution: nil,
|
||||||
TeamID: nil,
|
TeamID: nil,
|
||||||
|
CalendarEventsEnabled: false,
|
||||||
},
|
},
|
||||||
Response: "fails",
|
Response: "fails",
|
||||||
},
|
},
|
||||||
|
@ -466,12 +466,6 @@ func TestFullTeamGitOps(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
ds.PoliciesByNameFunc = func(ctx context.Context, names []string, teamID uint) (map[string]*fleet.Policy, error) {
|
|
||||||
if slices.Contains(names, "policy1") && slices.Contains(names, "policy2") {
|
|
||||||
return map[string]*fleet.Policy{"policy1": &policy, "policy2": &policy}, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
ds.DeleteTeamPoliciesFunc = func(ctx context.Context, teamID uint, IDs []uint) ([]uint, error) {
|
ds.DeleteTeamPoliciesFunc = func(ctx context.Context, teamID uint, IDs []uint) ([]uint, error) {
|
||||||
policyDeleted = true
|
policyDeleted = true
|
||||||
assert.Equal(t, []uint{policy.ID}, IDs)
|
assert.Equal(t, []uint{policy.ID}, IDs)
|
||||||
@ -554,7 +548,6 @@ func TestFullTeamGitOps(t *testing.T) {
|
|||||||
require.NotNil(t, savedTeam.Config.Integrations.GoogleCalendar)
|
require.NotNil(t, savedTeam.Config.Integrations.GoogleCalendar)
|
||||||
assert.Equal(t, "service@example.com", savedTeam.Config.Integrations.GoogleCalendar.Email)
|
assert.Equal(t, "service@example.com", savedTeam.Config.Integrations.GoogleCalendar.Email)
|
||||||
assert.True(t, savedTeam.Config.Integrations.GoogleCalendar.Enable)
|
assert.True(t, savedTeam.Config.Integrations.GoogleCalendar.Enable)
|
||||||
assert.Len(t, savedTeam.Config.Integrations.GoogleCalendar.Policies, 2)
|
|
||||||
|
|
||||||
// Now clear the settings
|
// Now clear the settings
|
||||||
tmpFile, err := os.CreateTemp(t.TempDir(), "*.yml")
|
tmpFile, err := os.CreateTemp(t.TempDir(), "*.yml")
|
||||||
|
@ -76,7 +76,8 @@
|
|||||||
"team_id": 1,
|
"team_id": 1,
|
||||||
"updated_at": "0001-01-01T00:00:00Z",
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
"created_at": "0001-01-01T00:00:00Z",
|
"created_at": "0001-01-01T00:00:00Z",
|
||||||
"critical": false
|
"critical": false,
|
||||||
|
"calendar_events_enabled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@ -91,7 +92,8 @@
|
|||||||
"team_id": null,
|
"team_id": null,
|
||||||
"updated_at": "0001-01-01T00:00:00Z",
|
"updated_at": "0001-01-01T00:00:00Z",
|
||||||
"created_at": "0001-01-01T00:00:00Z",
|
"created_at": "0001-01-01T00:00:00Z",
|
||||||
"critical": false
|
"critical": false,
|
||||||
|
"calendar_events_enabled": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"status": "offline",
|
"status": "offline",
|
||||||
|
@ -62,6 +62,7 @@ spec:
|
|||||||
created_at: "0001-01-01T00:00:00Z"
|
created_at: "0001-01-01T00:00:00Z"
|
||||||
updated_at: "0001-01-01T00:00:00Z"
|
updated_at: "0001-01-01T00:00:00Z"
|
||||||
critical: false
|
critical: false
|
||||||
|
calendar_events_enabled: true
|
||||||
- author_email: "alice@example.com"
|
- author_email: "alice@example.com"
|
||||||
author_id: 1
|
author_id: 1
|
||||||
author_name: Alice
|
author_name: Alice
|
||||||
@ -75,6 +76,7 @@ spec:
|
|||||||
created_at: "0001-01-01T00:00:00Z"
|
created_at: "0001-01-01T00:00:00Z"
|
||||||
updated_at: "0001-01-01T00:00:00Z"
|
updated_at: "0001-01-01T00:00:00Z"
|
||||||
critical: false
|
critical: false
|
||||||
|
calendar_events_enabled: false
|
||||||
policy_updated_at: "0001-01-01T00:00:00Z"
|
policy_updated_at: "0001-01-01T00:00:00Z"
|
||||||
public_ip: ""
|
public_ip: ""
|
||||||
primary_ip: ""
|
primary_ip: ""
|
||||||
|
@ -19,9 +19,6 @@ team_settings:
|
|||||||
google_calendar:
|
google_calendar:
|
||||||
email: service@example.com
|
email: service@example.com
|
||||||
enable_calendar_events: true
|
enable_calendar_events: true
|
||||||
policies:
|
|
||||||
- name: policy1
|
|
||||||
- name: policy2
|
|
||||||
webhook_url: https://example.com/google_calendar_webhook
|
webhook_url: https://example.com/google_calendar_webhook
|
||||||
agent_options:
|
agent_options:
|
||||||
command_line_flags:
|
command_line_flags:
|
||||||
@ -97,6 +94,7 @@ policies:
|
|||||||
description: This policy should always fail.
|
description: This policy should always fail.
|
||||||
resolution: There is no resolution for this policy.
|
resolution: There is no resolution for this policy.
|
||||||
query: SELECT 1 FROM osquery_info WHERE start_time < 0;
|
query: SELECT 1 FROM osquery_info WHERE start_time < 0;
|
||||||
|
calendar_events_enabled: true
|
||||||
- name: Passing policy
|
- name: Passing policy
|
||||||
platform: linux,windows,darwin,chrome
|
platform: linux,windows,darwin,chrome
|
||||||
description: This policy should always pass.
|
description: This policy should always pass.
|
||||||
|
@ -197,6 +197,7 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
if payload.Integrations != nil {
|
if payload.Integrations != nil {
|
||||||
|
if payload.Integrations.Jira != nil || payload.Integrations.Zendesk != nil {
|
||||||
// the team integrations must reference an existing global config integration.
|
// the team integrations must reference an existing global config integration.
|
||||||
if _, err := payload.Integrations.MatchWithIntegrations(appCfg.Integrations); err != nil {
|
if _, err := payload.Integrations.MatchWithIntegrations(appCfg.Integrations); err != nil {
|
||||||
return nil, fleet.NewInvalidArgumentError("integrations", err.Error())
|
return nil, fleet.NewInvalidArgumentError("integrations", err.Error())
|
||||||
@ -209,7 +210,8 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
|
|||||||
|
|
||||||
team.Config.Integrations.Jira = payload.Integrations.Jira
|
team.Config.Integrations.Jira = payload.Integrations.Jira
|
||||||
team.Config.Integrations.Zendesk = payload.Integrations.Zendesk
|
team.Config.Integrations.Zendesk = payload.Integrations.Zendesk
|
||||||
// Only update the google calendar integration if it's not nil
|
}
|
||||||
|
// Only update the calendar integration if it's not nil
|
||||||
if payload.Integrations.GoogleCalendar != nil {
|
if payload.Integrations.GoogleCalendar != nil {
|
||||||
invalid := &fleet.InvalidArgumentError{}
|
invalid := &fleet.InvalidArgumentError{}
|
||||||
_ = svc.validateTeamCalendarIntegrations(ctx, team, payload.Integrations.GoogleCalendar, appCfg, invalid)
|
_ = svc.validateTeamCalendarIntegrations(ctx, team, payload.Integrations.GoogleCalendar, appCfg, invalid)
|
||||||
@ -1179,35 +1181,6 @@ func (svc *Service) validateTeamCalendarIntegrations(
|
|||||||
} else if u.Scheme != "https" && u.Scheme != "http" {
|
} else if u.Scheme != "https" && u.Scheme != "http" {
|
||||||
invalid.Append("integrations.google_calendar.webhook_url", "webhook_url must be https or http")
|
invalid.Append("integrations.google_calendar.webhook_url", "webhook_url must be https or http")
|
||||||
}
|
}
|
||||||
// Validate policy ids
|
|
||||||
if len(calendarIntegration.Policies) == 0 {
|
|
||||||
invalid.Append("integrations.google_calendar.policies", "policies are required")
|
|
||||||
}
|
|
||||||
if len(calendarIntegration.Policies) > 0 {
|
|
||||||
for _, policy := range calendarIntegration.Policies {
|
|
||||||
policy.Name = strings.TrimSpace(policy.Name)
|
|
||||||
}
|
|
||||||
calendarIntegration.Policies = server.RemoveDuplicatesFromSlice(calendarIntegration.Policies)
|
|
||||||
policyNames := make([]string, 0, len(calendarIntegration.Policies))
|
|
||||||
for _, policy := range calendarIntegration.Policies {
|
|
||||||
policyNames = append(policyNames, policy.Name)
|
|
||||||
}
|
|
||||||
// Policies must be team policies. Global policies are not allowed.
|
|
||||||
policyMap, err := svc.ds.PoliciesByName(ctx, policyNames, team.ID)
|
|
||||||
if err != nil {
|
|
||||||
level.Error(svc.logger).Log("msg", "error getting policies by name", "names", policyNames, "err", err)
|
|
||||||
if fleet.IsNotFound(err) {
|
|
||||||
invalid.Append("integrations.google_calendar.policies[].name", "name is invalid")
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// PoliciesByName guarantees that all policies are present
|
|
||||||
for _, policy := range calendarIntegration.Policies {
|
|
||||||
policy.ID = policyMap[policy.Name].ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package tables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
MigrationClient.AddMigration(Up_20240314151747, Down_20240314151747)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Up_20240314151747(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec(`ALTER TABLE policies ADD COLUMN calendar_events_enabled TINYINT(1) UNSIGNED NOT NULL DEFAULT '0'`)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add calendar_events_enabled to policies: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Down_20240314151747(_ *sql.Tx) error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package tables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUp_20240314151747(t *testing.T) {
|
||||||
|
db := applyUpToPrev(t)
|
||||||
|
|
||||||
|
policy1 := execNoErrLastID(
|
||||||
|
t, db, "INSERT INTO policies (name, query, description, checksum) VALUES (?,?,?,?)", "policy", "", "", "checksum",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Apply current migration.
|
||||||
|
applyNext(t, db)
|
||||||
|
|
||||||
|
var policyCheck []struct {
|
||||||
|
ID int64 `db:"id"`
|
||||||
|
CalEnabled bool `db:"calendar_events_enabled"`
|
||||||
|
}
|
||||||
|
err := db.SelectContext(context.Background(), &policyCheck, `SELECT id, calendar_events_enabled FROM policies ORDER BY id`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, policyCheck, 1)
|
||||||
|
assert.Equal(t, policy1, policyCheck[0].ID)
|
||||||
|
assert.Equal(t, false, policyCheck[0].CalEnabled)
|
||||||
|
|
||||||
|
policy2 := execNoErrLastID(
|
||||||
|
t, db, "INSERT INTO policies (name, query, description, checksum, calendar_events_enabled) VALUES (?,?,?,?,?)", "policy2", "", "",
|
||||||
|
"checksum2", 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
policyCheck = nil
|
||||||
|
err = db.SelectContext(context.Background(), &policyCheck, `SELECT id, calendar_events_enabled FROM policies WHERE id = ?`, policy2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, policyCheck, 1)
|
||||||
|
assert.Equal(t, policy2, policyCheck[0].ID)
|
||||||
|
assert.Equal(t, true, policyCheck[0].CalEnabled)
|
||||||
|
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
"sort"
|
"sort"
|
||||||
@ -20,7 +19,7 @@ import (
|
|||||||
|
|
||||||
const policyCols = `
|
const policyCols = `
|
||||||
p.id, p.team_id, p.resolution, p.name, p.query, p.description,
|
p.id, p.team_id, p.resolution, p.name, p.query, p.description,
|
||||||
p.author_id, p.platforms, p.created_at, p.updated_at, p.critical
|
p.author_id, p.platforms, p.created_at, p.updated_at, p.critical, p.calendar_events_enabled
|
||||||
`
|
`
|
||||||
|
|
||||||
var policySearchColumns = []string{"p.name"}
|
var policySearchColumns = []string{"p.name"}
|
||||||
@ -116,10 +115,12 @@ func (ds *Datastore) SavePolicy(ctx context.Context, p *fleet.Policy, shouldRemo
|
|||||||
p.Name = norm.NFC.String(p.Name)
|
p.Name = norm.NFC.String(p.Name)
|
||||||
sql := `
|
sql := `
|
||||||
UPDATE policies
|
UPDATE policies
|
||||||
SET name = ?, query = ?, description = ?, resolution = ?, platforms = ?, critical = ?, checksum = ` + policiesChecksumComputedColumn() + `
|
SET name = ?, query = ?, description = ?, resolution = ?, platforms = ?, critical = ?, calendar_events_enabled = ?, checksum = ` + policiesChecksumComputedColumn() + `
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`
|
`
|
||||||
result, err := ds.writer(ctx).ExecContext(ctx, sql, p.Name, p.Query, p.Description, p.Resolution, p.Platform, p.Critical, p.ID)
|
result, err := ds.writer(ctx).ExecContext(
|
||||||
|
ctx, sql, p.Name, p.Query, p.Description, p.Resolution, p.Platform, p.Critical, p.CalendarEventsEnabled, p.ID,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctxerr.Wrap(ctx, err, "updating policy")
|
return ctxerr.Wrap(ctx, err, "updating policy")
|
||||||
}
|
}
|
||||||
@ -445,42 +446,6 @@ func (ds *Datastore) PoliciesByID(ctx context.Context, ids []uint) (map[uint]*fl
|
|||||||
return policiesByID, nil
|
return policiesByID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *Datastore) PoliciesByName(ctx context.Context, names []string, teamID uint) (map[string]*fleet.Policy, error) {
|
|
||||||
sqlQuery := `SELECT ` + policyCols + `
|
|
||||||
FROM policies p
|
|
||||||
WHERE p.team_id = ? AND p.name IN (?)`
|
|
||||||
query, args, err := sqlx.In(sqlQuery, teamID, names)
|
|
||||||
if err != nil {
|
|
||||||
return nil, ctxerr.Wrap(ctx, err, "building query to get policies by name")
|
|
||||||
}
|
|
||||||
|
|
||||||
var policies []*fleet.Policy
|
|
||||||
err = sqlx.SelectContext(
|
|
||||||
ctx,
|
|
||||||
ds.reader(ctx),
|
|
||||||
&policies,
|
|
||||||
query, args...,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return nil, ctxerr.Wrap(ctx, notFound("Policy").WithName(fmt.Sprintf("%v", names)))
|
|
||||||
}
|
|
||||||
return nil, ctxerr.Wrap(ctx, err, "getting policies by name")
|
|
||||||
}
|
|
||||||
|
|
||||||
policiesByName := make(map[string]*fleet.Policy, len(names))
|
|
||||||
for _, p := range policies {
|
|
||||||
policiesByName[p.Name] = p
|
|
||||||
}
|
|
||||||
for _, name := range names {
|
|
||||||
if policiesByName[name] == nil {
|
|
||||||
return nil, ctxerr.Wrap(ctx, notFound("Policy").WithName(name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return policiesByName, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ds *Datastore) DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error) {
|
func (ds *Datastore) DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error) {
|
||||||
return deletePolicyDB(ctx, ds.writer(ctx), ids, nil)
|
return deletePolicyDB(ctx, ds.writer(ctx), ids, nil)
|
||||||
}
|
}
|
||||||
@ -562,10 +527,11 @@ func (ds *Datastore) NewTeamPolicy(ctx context.Context, teamID uint, authorID *u
|
|||||||
nameUnicode := norm.NFC.String(args.Name)
|
nameUnicode := norm.NFC.String(args.Name)
|
||||||
res, err := ds.writer(ctx).ExecContext(ctx,
|
res, err := ds.writer(ctx).ExecContext(ctx,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
`INSERT INTO policies (name, query, description, team_id, resolution, author_id, platforms, critical, checksum) VALUES (?, ?, ?, ?, ?, ?, ?, ?, %s)`,
|
`INSERT INTO policies (name, query, description, team_id, resolution, author_id, platforms, critical, calendar_events_enabled, checksum) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, %s)`,
|
||||||
policiesChecksumComputedColumn(),
|
policiesChecksumComputedColumn(),
|
||||||
),
|
),
|
||||||
nameUnicode, args.Query, args.Description, teamID, args.Resolution, authorID, args.Platform, args.Critical,
|
nameUnicode, args.Query, args.Description, teamID, args.Resolution, authorID, args.Platform, args.Critical,
|
||||||
|
args.CalendarEventsEnabled,
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
@ -623,15 +589,17 @@ func (ds *Datastore) ApplyPolicySpecs(ctx context.Context, authorID uint, specs
|
|||||||
team_id,
|
team_id,
|
||||||
platforms,
|
platforms,
|
||||||
critical,
|
critical,
|
||||||
|
calendar_events_enabled,
|
||||||
checksum
|
checksum
|
||||||
) VALUES ( ?, ?, ?, ?, ?, (SELECT IFNULL(MIN(id), NULL) FROM teams WHERE name = ?), ?, ?, %s)
|
) VALUES ( ?, ?, ?, ?, ?, (SELECT IFNULL(MIN(id), NULL) FROM teams WHERE name = ?), ?, ?, ?, %s)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
query = VALUES(query),
|
query = VALUES(query),
|
||||||
description = VALUES(description),
|
description = VALUES(description),
|
||||||
author_id = VALUES(author_id),
|
author_id = VALUES(author_id),
|
||||||
resolution = VALUES(resolution),
|
resolution = VALUES(resolution),
|
||||||
platforms = VALUES(platforms),
|
platforms = VALUES(platforms),
|
||||||
critical = VALUES(critical)
|
critical = VALUES(critical),
|
||||||
|
calendar_events_enabled = VALUES(calendar_events_enabled)
|
||||||
`, policiesChecksumComputedColumn(),
|
`, policiesChecksumComputedColumn(),
|
||||||
)
|
)
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
@ -640,6 +608,7 @@ func (ds *Datastore) ApplyPolicySpecs(ctx context.Context, authorID uint, specs
|
|||||||
spec.Name = norm.NFC.String(spec.Name)
|
spec.Name = norm.NFC.String(spec.Name)
|
||||||
res, err := tx.ExecContext(ctx,
|
res, err := tx.ExecContext(ctx,
|
||||||
query, spec.Name, spec.Query, spec.Description, authorID, spec.Resolution, spec.Team, spec.Platform, spec.Critical,
|
query, spec.Name, spec.Query, spec.Description, authorID, spec.Resolution, spec.Team, spec.Platform, spec.Critical,
|
||||||
|
spec.CalendarEventsEnabled,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctxerr.Wrap(ctx, err, "exec ApplyPolicySpecs insert")
|
return ctxerr.Wrap(ctx, err, "exec ApplyPolicySpecs insert")
|
||||||
|
@ -38,7 +38,6 @@ func TestPolicies(t *testing.T) {
|
|||||||
{"PolicyQueriesForHost", testPolicyQueriesForHost},
|
{"PolicyQueriesForHost", testPolicyQueriesForHost},
|
||||||
{"PolicyQueriesForHostPlatforms", testPolicyQueriesForHostPlatforms},
|
{"PolicyQueriesForHostPlatforms", testPolicyQueriesForHostPlatforms},
|
||||||
{"PoliciesByID", testPoliciesByID},
|
{"PoliciesByID", testPoliciesByID},
|
||||||
{"PoliciesByName", testPoliciesByName},
|
|
||||||
{"TeamPolicyTransfer", testTeamPolicyTransfer},
|
{"TeamPolicyTransfer", testTeamPolicyTransfer},
|
||||||
{"ApplyPolicySpec", testApplyPolicySpec},
|
{"ApplyPolicySpec", testApplyPolicySpec},
|
||||||
{"Save", testPoliciesSave},
|
{"Save", testPoliciesSave},
|
||||||
@ -587,6 +586,7 @@ func testTeamPolicyProprietary(t *testing.T, ds *Datastore) {
|
|||||||
Query: "select 1;",
|
Query: "select 1;",
|
||||||
Description: "query1 desc",
|
Description: "query1 desc",
|
||||||
Resolution: "query1 resolution",
|
Resolution: "query1 resolution",
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -616,6 +616,7 @@ func testTeamPolicyProprietary(t *testing.T, ds *Datastore) {
|
|||||||
assert.Equal(t, "query1 resolution", *p.Resolution)
|
assert.Equal(t, "query1 resolution", *p.Resolution)
|
||||||
require.NotNil(t, p.AuthorID)
|
require.NotNil(t, p.AuthorID)
|
||||||
assert.Equal(t, user1.ID, *p.AuthorID)
|
assert.Equal(t, user1.ID, *p.AuthorID)
|
||||||
|
assert.True(t, p.CalendarEventsEnabled)
|
||||||
|
|
||||||
globalPolicies, err := ds.ListGlobalPolicies(ctx, fleet.ListOptions{})
|
globalPolicies, err := ds.ListGlobalPolicies(ctx, fleet.ListOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1115,47 +1116,6 @@ func testPoliciesByID(t *testing.T, ds *Datastore) {
|
|||||||
require.ErrorAs(t, err, &nfe)
|
require.ErrorAs(t, err, &nfe)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPoliciesByName(t *testing.T, ds *Datastore) {
|
|
||||||
ctx := context.Background()
|
|
||||||
user1 := test.NewUser(t, ds, "Alice", "alice@example.com", true)
|
|
||||||
policyName1 := "policy1"
|
|
||||||
policyName2 := "policy2"
|
|
||||||
_ = newTestPolicy(t, ds, user1, policyName1, "darwin", nil)
|
|
||||||
_ = newTestPolicy(t, ds, user1, policyName2, "darwin", nil)
|
|
||||||
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: t.Name() + "team1"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// No names provided
|
|
||||||
_, err = ds.PoliciesByName(context.Background(), []string{}, team1.ID)
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
// Policies don't belong to a team
|
|
||||||
_, err = ds.PoliciesByName(context.Background(), []string{policyName1, policyName2}, team1.ID)
|
|
||||||
require.Error(t, err)
|
|
||||||
var nfe fleet.NotFoundError
|
|
||||||
require.ErrorAs(t, err, &nfe)
|
|
||||||
|
|
||||||
policy1 := newTestPolicy(t, ds, user1, policyName1, "darwin", &team1.ID)
|
|
||||||
policy2 := newTestPolicy(t, ds, user1, policyName2, "darwin", &team1.ID)
|
|
||||||
|
|
||||||
policiesByName, err := ds.PoliciesByName(context.Background(), []string{policyName1, policyName2}, team1.ID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, policiesByName, 2)
|
|
||||||
assert.Equal(t, policiesByName[policyName1].ID, policy1.ID)
|
|
||||||
assert.Equal(t, policiesByName[policyName2].ID, policy2.ID)
|
|
||||||
assert.Equal(t, policiesByName[policyName1].Name, policy1.Name)
|
|
||||||
assert.Equal(t, policiesByName[policyName2].Name, policy2.Name)
|
|
||||||
|
|
||||||
// Policy does not exist
|
|
||||||
_, err = ds.PoliciesByName(context.Background(), []string{"doesn't exist"}, team1.ID)
|
|
||||||
assert.ErrorAs(t, err, &nfe)
|
|
||||||
|
|
||||||
// One exists and one doesn't
|
|
||||||
_, err = ds.PoliciesByName(context.Background(), []string{policyName1, "doesn't exist"}, team1.ID)
|
|
||||||
assert.ErrorAs(t, err, &nfe)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func testTeamPolicyTransfer(t *testing.T, ds *Datastore) {
|
func testTeamPolicyTransfer(t *testing.T, ds *Datastore) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
user1 := test.NewUser(t, ds, "Alice", "alice@example.com", true)
|
user1 := test.NewUser(t, ds, "Alice", "alice@example.com", true)
|
||||||
@ -1292,6 +1252,7 @@ func testApplyPolicySpec(t *testing.T, ds *Datastore) {
|
|||||||
Resolution: "some other resolution",
|
Resolution: "some other resolution",
|
||||||
Team: "team1",
|
Team: "team1",
|
||||||
Platform: "darwin",
|
Platform: "darwin",
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "query3",
|
Name: "query3",
|
||||||
@ -1326,6 +1287,7 @@ func testApplyPolicySpec(t *testing.T, ds *Datastore) {
|
|||||||
require.NotNil(t, teamPolicies[0].Resolution)
|
require.NotNil(t, teamPolicies[0].Resolution)
|
||||||
assert.Equal(t, "some other resolution", *teamPolicies[0].Resolution)
|
assert.Equal(t, "some other resolution", *teamPolicies[0].Resolution)
|
||||||
assert.Equal(t, "darwin", teamPolicies[0].Platform)
|
assert.Equal(t, "darwin", teamPolicies[0].Platform)
|
||||||
|
assert.True(t, teamPolicies[0].CalendarEventsEnabled)
|
||||||
|
|
||||||
assert.Equal(t, "query3", teamPolicies[1].Name)
|
assert.Equal(t, "query3", teamPolicies[1].Name)
|
||||||
assert.Equal(t, "select 3;", teamPolicies[1].Query)
|
assert.Equal(t, "select 3;", teamPolicies[1].Query)
|
||||||
@ -1335,6 +1297,7 @@ func testApplyPolicySpec(t *testing.T, ds *Datastore) {
|
|||||||
require.NotNil(t, teamPolicies[1].Resolution)
|
require.NotNil(t, teamPolicies[1].Resolution)
|
||||||
assert.Equal(t, "some other good resolution", *teamPolicies[1].Resolution)
|
assert.Equal(t, "some other good resolution", *teamPolicies[1].Resolution)
|
||||||
assert.Equal(t, "windows,linux", teamPolicies[1].Platform)
|
assert.Equal(t, "windows,linux", teamPolicies[1].Platform)
|
||||||
|
assert.False(t, teamPolicies[1].CalendarEventsEnabled)
|
||||||
|
|
||||||
// Make sure apply is idempotent
|
// Make sure apply is idempotent
|
||||||
require.NoError(t, ds.ApplyPolicySpecs(ctx, user1.ID, []*fleet.PolicySpec{
|
require.NoError(t, ds.ApplyPolicySpecs(ctx, user1.ID, []*fleet.PolicySpec{
|
||||||
@ -1353,6 +1316,7 @@ func testApplyPolicySpec(t *testing.T, ds *Datastore) {
|
|||||||
Resolution: "some other resolution",
|
Resolution: "some other resolution",
|
||||||
Team: "team1",
|
Team: "team1",
|
||||||
Platform: "darwin",
|
Platform: "darwin",
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "query3",
|
Name: "query3",
|
||||||
@ -1388,6 +1352,7 @@ func testApplyPolicySpec(t *testing.T, ds *Datastore) {
|
|||||||
Resolution: "some other resolution updated",
|
Resolution: "some other resolution updated",
|
||||||
Team: "team1", // No error, team did not change
|
Team: "team1", // No error, team did not change
|
||||||
Platform: "windows",
|
Platform: "windows",
|
||||||
|
CalendarEventsEnabled: false,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
policies, err = ds.ListGlobalPolicies(ctx, fleet.ListOptions{})
|
policies, err = ds.ListGlobalPolicies(ctx, fleet.ListOptions{})
|
||||||
@ -1402,6 +1367,7 @@ func testApplyPolicySpec(t *testing.T, ds *Datastore) {
|
|||||||
require.NotNil(t, policies[0].Resolution)
|
require.NotNil(t, policies[0].Resolution)
|
||||||
assert.Equal(t, "some resolution updated", *policies[0].Resolution)
|
assert.Equal(t, "some resolution updated", *policies[0].Resolution)
|
||||||
assert.Equal(t, "", policies[0].Platform)
|
assert.Equal(t, "", policies[0].Platform)
|
||||||
|
assert.False(t, policies[0].CalendarEventsEnabled)
|
||||||
|
|
||||||
teamPolicies, _, err = ds.ListTeamPolicies(ctx, team1.ID, fleet.ListOptions{}, fleet.ListOptions{})
|
teamPolicies, _, err = ds.ListTeamPolicies(ctx, team1.ID, fleet.ListOptions{}, fleet.ListOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1486,6 +1452,7 @@ func testPoliciesSave(t *testing.T, ds *Datastore) {
|
|||||||
Description: "team1 query desc",
|
Description: "team1 query desc",
|
||||||
Resolution: "team1 query resolution",
|
Resolution: "team1 query resolution",
|
||||||
Critical: true,
|
Critical: true,
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
}
|
}
|
||||||
tp1, err := ds.NewTeamPolicy(ctx, team1.ID, &user1.ID, payload)
|
tp1, err := ds.NewTeamPolicy(ctx, team1.ID, &user1.ID, payload)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1494,6 +1461,7 @@ func testPoliciesSave(t *testing.T, ds *Datastore) {
|
|||||||
require.Equal(t, tp1.Description, payload.Description)
|
require.Equal(t, tp1.Description, payload.Description)
|
||||||
require.Equal(t, *tp1.Resolution, payload.Resolution)
|
require.Equal(t, *tp1.Resolution, payload.Resolution)
|
||||||
require.Equal(t, tp1.Critical, payload.Critical)
|
require.Equal(t, tp1.Critical, payload.Critical)
|
||||||
|
assert.Equal(t, tp1.CalendarEventsEnabled, payload.CalendarEventsEnabled)
|
||||||
var teamChecksum []uint8
|
var teamChecksum []uint8
|
||||||
err = ds.writer(context.Background()).Get(&teamChecksum, `SELECT checksum FROM policies WHERE id = ?`, tp1.ID)
|
err = ds.writer(context.Background()).Get(&teamChecksum, `SELECT checksum FROM policies WHERE id = ?`, tp1.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1522,6 +1490,7 @@ func testPoliciesSave(t *testing.T, ds *Datastore) {
|
|||||||
tp2.Description = "team1 query desc updated"
|
tp2.Description = "team1 query desc updated"
|
||||||
tp2.Resolution = ptr.String("team1 query resolution updated")
|
tp2.Resolution = ptr.String("team1 query resolution updated")
|
||||||
tp2.Critical = false
|
tp2.Critical = false
|
||||||
|
tp2.CalendarEventsEnabled = false
|
||||||
err = ds.SavePolicy(ctx, &tp2, true)
|
err = ds.SavePolicy(ctx, &tp2, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tp1, err = ds.Policy(ctx, tp1.ID)
|
tp1, err = ds.Policy(ctx, tp1.ID)
|
||||||
|
@ -588,7 +588,6 @@ type Datastore interface {
|
|||||||
|
|
||||||
ListGlobalPolicies(ctx context.Context, opts ListOptions) ([]*Policy, error)
|
ListGlobalPolicies(ctx context.Context, opts ListOptions) ([]*Policy, error)
|
||||||
PoliciesByID(ctx context.Context, ids []uint) (map[uint]*Policy, error)
|
PoliciesByID(ctx context.Context, ids []uint) (map[uint]*Policy, error)
|
||||||
PoliciesByName(ctx context.Context, names []string, teamID uint) (map[string]*Policy, error)
|
|
||||||
DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error)
|
DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error)
|
||||||
CountPolicies(ctx context.Context, teamID *uint, matchQuery string) (int, error)
|
CountPolicies(ctx context.Context, teamID *uint, matchQuery string) (int, error)
|
||||||
UpdateHostPolicyCounts(ctx context.Context) error
|
UpdateHostPolicyCounts(ctx context.Context) error
|
||||||
|
@ -114,13 +114,8 @@ func (z TeamZendeskIntegration) UniqueKey() string {
|
|||||||
type TeamGoogleCalendarIntegration struct {
|
type TeamGoogleCalendarIntegration struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Enable bool `json:"enable_calendar_events"`
|
Enable bool `json:"enable_calendar_events"`
|
||||||
Policies []*PolicyRef `json:"policies"`
|
|
||||||
WebhookURL string `json:"webhook_url"`
|
WebhookURL string `json:"webhook_url"`
|
||||||
}
|
}
|
||||||
type PolicyRef struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
ID uint `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// JiraIntegration configures an instance of an integration with the Jira
|
// JiraIntegration configures an instance of an integration with the Jira
|
||||||
// system.
|
// system.
|
||||||
@ -380,7 +375,7 @@ func ValidateEnabledHostStatusIntegrations(webhook HostStatusWebhookSettings, in
|
|||||||
|
|
||||||
func ValidateGoogleCalendarIntegrations(intgs []*GoogleCalendarIntegration, invalid *InvalidArgumentError) {
|
func ValidateGoogleCalendarIntegrations(intgs []*GoogleCalendarIntegration, invalid *InvalidArgumentError) {
|
||||||
if len(intgs) > 1 {
|
if len(intgs) > 1 {
|
||||||
invalid.Append("integrations.google_calendar", "only one Google Calendar integration is allowed at this time")
|
invalid.Append("integrations.google_calendar", "integrating with >1 Google Workspace service account is not yet supported.")
|
||||||
}
|
}
|
||||||
for _, intg := range intgs {
|
for _, intg := range intgs {
|
||||||
intg.Email = strings.TrimSpace(intg.Email)
|
intg.Email = strings.TrimSpace(intg.Email)
|
||||||
|
@ -30,6 +30,8 @@ type PolicyPayload struct {
|
|||||||
//
|
//
|
||||||
// Empty string targets all platforms.
|
// Empty string targets all platforms.
|
||||||
Platform string
|
Platform string
|
||||||
|
// CalendarEventsEnabled indicates whether calendar events are enabled for the policy. Only applies to team policies.
|
||||||
|
CalendarEventsEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -107,6 +109,8 @@ type ModifyPolicyPayload struct {
|
|||||||
Platform *string `json:"platform"`
|
Platform *string `json:"platform"`
|
||||||
// Critical marks the policy as high impact.
|
// Critical marks the policy as high impact.
|
||||||
Critical *bool `json:"critical" premium:"true"`
|
Critical *bool `json:"critical" premium:"true"`
|
||||||
|
// CalendarEventsEnabled indicates whether calendar events are enabled for the policy. Only applies to team policies.
|
||||||
|
CalendarEventsEnabled *bool `json:"calendar_events_enabled" premium:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify verifies the policy payload is valid.
|
// Verify verifies the policy payload is valid.
|
||||||
@ -159,6 +163,8 @@ type PolicyData struct {
|
|||||||
// Empty string targets all platforms.
|
// Empty string targets all platforms.
|
||||||
Platform string `json:"platform" db:"platforms"`
|
Platform string `json:"platform" db:"platforms"`
|
||||||
|
|
||||||
|
CalendarEventsEnabled bool `json:"calendar_events_enabled" db:"calendar_events_enabled"`
|
||||||
|
|
||||||
UpdateCreateTimestamps
|
UpdateCreateTimestamps
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +218,8 @@ type PolicySpec struct {
|
|||||||
//
|
//
|
||||||
// Empty string targets all platforms.
|
// Empty string targets all platforms.
|
||||||
Platform string `json:"platform,omitempty"`
|
Platform string `json:"platform,omitempty"`
|
||||||
|
// CalendarEventsEnabled indicates whether calendar events are enabled for the policy. Only applies to team policies.
|
||||||
|
CalendarEventsEnabled bool `json:"calendar_events_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify verifies the policy data is valid.
|
// Verify verifies the policy data is valid.
|
||||||
|
@ -451,6 +451,11 @@ func TeamSpecFromTeam(t *Team) (*TeamSpec, error) {
|
|||||||
webhookSettings.HostStatusWebhook = t.Config.WebhookSettings.HostStatusWebhook
|
webhookSettings.HostStatusWebhook = t.Config.WebhookSettings.HostStatusWebhook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var integrations TeamSpecIntegrations
|
||||||
|
if t.Config.Integrations.GoogleCalendar != nil {
|
||||||
|
integrations.GoogleCalendar = t.Config.Integrations.GoogleCalendar
|
||||||
|
}
|
||||||
|
|
||||||
return &TeamSpec{
|
return &TeamSpec{
|
||||||
Name: t.Name,
|
Name: t.Name,
|
||||||
AgentOptions: agentOptions,
|
AgentOptions: agentOptions,
|
||||||
@ -459,5 +464,6 @@ func TeamSpecFromTeam(t *Team) (*TeamSpec, error) {
|
|||||||
MDM: mdmSpec,
|
MDM: mdmSpec,
|
||||||
HostExpirySettings: &t.Config.HostExpirySettings,
|
HostExpirySettings: &t.Config.HostExpirySettings,
|
||||||
WebhookSettings: webhookSettings,
|
WebhookSettings: webhookSettings,
|
||||||
|
Integrations: integrations,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -432,8 +432,6 @@ type ListGlobalPoliciesFunc func(ctx context.Context, opts fleet.ListOptions) ([
|
|||||||
|
|
||||||
type PoliciesByIDFunc func(ctx context.Context, ids []uint) (map[uint]*fleet.Policy, error)
|
type PoliciesByIDFunc func(ctx context.Context, ids []uint) (map[uint]*fleet.Policy, error)
|
||||||
|
|
||||||
type PoliciesByNameFunc func(ctx context.Context, names []string, teamID uint) (map[string]*fleet.Policy, error)
|
|
||||||
|
|
||||||
type DeleteGlobalPoliciesFunc func(ctx context.Context, ids []uint) ([]uint, error)
|
type DeleteGlobalPoliciesFunc func(ctx context.Context, ids []uint) ([]uint, error)
|
||||||
|
|
||||||
type CountPoliciesFunc func(ctx context.Context, teamID *uint, matchQuery string) (int, error)
|
type CountPoliciesFunc func(ctx context.Context, teamID *uint, matchQuery string) (int, error)
|
||||||
@ -1482,9 +1480,6 @@ type DataStore struct {
|
|||||||
PoliciesByIDFunc PoliciesByIDFunc
|
PoliciesByIDFunc PoliciesByIDFunc
|
||||||
PoliciesByIDFuncInvoked bool
|
PoliciesByIDFuncInvoked bool
|
||||||
|
|
||||||
PoliciesByNameFunc PoliciesByNameFunc
|
|
||||||
PoliciesByNameFuncInvoked bool
|
|
||||||
|
|
||||||
DeleteGlobalPoliciesFunc DeleteGlobalPoliciesFunc
|
DeleteGlobalPoliciesFunc DeleteGlobalPoliciesFunc
|
||||||
DeleteGlobalPoliciesFuncInvoked bool
|
DeleteGlobalPoliciesFuncInvoked bool
|
||||||
|
|
||||||
@ -3576,13 +3571,6 @@ func (s *DataStore) PoliciesByID(ctx context.Context, ids []uint) (map[uint]*fle
|
|||||||
return s.PoliciesByIDFunc(ctx, ids)
|
return s.PoliciesByIDFunc(ctx, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DataStore) PoliciesByName(ctx context.Context, names []string, teamID uint) (map[string]*fleet.Policy, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
s.PoliciesByNameFuncInvoked = true
|
|
||||||
s.mu.Unlock()
|
|
||||||
return s.PoliciesByNameFunc(ctx, names, teamID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DataStore) DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error) {
|
func (s *DataStore) DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.DeleteGlobalPoliciesFuncInvoked = true
|
s.DeleteGlobalPoliciesFuncInvoked = true
|
||||||
|
@ -880,7 +880,6 @@ func (c *Client) DoGitOps(
|
|||||||
}
|
}
|
||||||
var mdmAppConfig map[string]interface{}
|
var mdmAppConfig map[string]interface{}
|
||||||
var team map[string]interface{}
|
var team map[string]interface{}
|
||||||
var teamCalendarIntegration map[string]interface{}
|
|
||||||
if config.TeamName == nil {
|
if config.TeamName == nil {
|
||||||
group.AppConfig = config.OrgSettings
|
group.AppConfig = config.OrgSettings
|
||||||
group.EnrollSecret = &fleet.EnrollSecretSpec{Secrets: config.OrgSettings["secrets"].([]*fleet.EnrollSecret)}
|
group.EnrollSecret = &fleet.EnrollSecretSpec{Secrets: config.OrgSettings["secrets"].([]*fleet.EnrollSecret)}
|
||||||
@ -968,20 +967,14 @@ func (c *Client) DoGitOps(
|
|||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("team_settings.integrations config is not a map")
|
return errors.New("team_settings.integrations config is not a map")
|
||||||
}
|
}
|
||||||
if calendar, ok := integrations.(map[string]interface{})["google_calendar"]; ok {
|
if googleCal, ok := integrations.(map[string]interface{})["google_calendar"]; !ok || googleCal == nil {
|
||||||
if calendar == nil {
|
integrations.(map[string]interface{})["google_calendar"] = map[string]interface{}{}
|
||||||
calendar = map[string]interface{}{}
|
} else {
|
||||||
integrations.(map[string]interface{})["google_calendar"] = calendar
|
_, ok = googleCal.(map[string]interface{})
|
||||||
}
|
|
||||||
teamCalendarIntegration, ok = calendar.(map[string]interface{})
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("team_settings.integrations.google_calendar config is not a map")
|
return errors.New("team_settings.integrations.google_calendar config is not a map")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We clear the calendar integration and re-apply it after updating policies.
|
|
||||||
// This is needed because the calendar integration may be referencing policies that need to be
|
|
||||||
// created/updated.
|
|
||||||
integrations.(map[string]interface{})["google_calendar"] = map[string]interface{}{}
|
|
||||||
|
|
||||||
team["mdm"] = map[string]interface{}{}
|
team["mdm"] = map[string]interface{}{}
|
||||||
mdmAppConfig = team["mdm"].(map[string]interface{})
|
mdmAppConfig = team["mdm"].(map[string]interface{})
|
||||||
@ -1087,23 +1080,6 @@ func (c *Client) DoGitOps(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply calendar integration
|
|
||||||
if len(teamCalendarIntegration) > 0 {
|
|
||||||
group = spec.Group{}
|
|
||||||
team = make(map[string]interface{})
|
|
||||||
team["name"] = *config.TeamName
|
|
||||||
team["integrations"] = map[string]interface{}{"google_calendar": teamCalendarIntegration}
|
|
||||||
rawTeam, err := json.Marshal(team)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error marshalling team spec: %w", err)
|
|
||||||
}
|
|
||||||
group.Teams = []json.RawMessage{rawTeam}
|
|
||||||
_, err = c.ApplyGroup(ctx, &group, baseDir, logf, fleet.ApplySpecOptions{DryRun: dryRun})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.doGitOpsQueries(config, logFn, dryRun)
|
err = c.doGitOpsQueries(config, logFn, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -199,11 +199,6 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
|||||||
"google_calendar": map[string]any{
|
"google_calendar": map[string]any{
|
||||||
"email": calendarEmail,
|
"email": calendarEmail,
|
||||||
"enable_calendar_events": true,
|
"enable_calendar_events": true,
|
||||||
"policies": []any{
|
|
||||||
map[string]any{
|
|
||||||
"name": teamPolicy.Name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"webhook_url": calendarWebhookUrl,
|
"webhook_url": calendarWebhookUrl,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -218,8 +213,6 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
|||||||
assert.Equal(t, calendarEmail, team.Config.Integrations.GoogleCalendar.Email)
|
assert.Equal(t, calendarEmail, team.Config.Integrations.GoogleCalendar.Email)
|
||||||
assert.Equal(t, calendarWebhookUrl, team.Config.Integrations.GoogleCalendar.WebhookURL)
|
assert.Equal(t, calendarWebhookUrl, team.Config.Integrations.GoogleCalendar.WebhookURL)
|
||||||
assert.True(t, team.Config.Integrations.GoogleCalendar.Enable)
|
assert.True(t, team.Config.Integrations.GoogleCalendar.Enable)
|
||||||
require.Len(t, team.Config.Integrations.GoogleCalendar.Policies, 1)
|
|
||||||
assert.Equal(t, teamPolicy.ID, team.Config.Integrations.GoogleCalendar.Policies[0].ID)
|
|
||||||
|
|
||||||
// dry-run with invalid windows updates
|
// dry-run with invalid windows updates
|
||||||
teamSpecs = map[string]any{
|
teamSpecs = map[string]any{
|
||||||
@ -3686,7 +3679,7 @@ func (s *integrationEnterpriseTestSuite) TestGlobalPolicyCreateReadPatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *integrationEnterpriseTestSuite) TestTeamPolicyCreateReadPatch() {
|
func (s *integrationEnterpriseTestSuite) TestTeamPolicyCreateReadPatch() {
|
||||||
fields := []string{"Query", "Name", "Description", "Resolution", "Platform", "Critical"}
|
fields := []string{"Query", "Name", "Description", "Resolution", "Platform", "Critical", "CalendarEventsEnabled"}
|
||||||
|
|
||||||
team1, err := s.ds.NewTeam(context.Background(), &fleet.Team{
|
team1, err := s.ds.NewTeam(context.Background(), &fleet.Team{
|
||||||
ID: 42,
|
ID: 42,
|
||||||
@ -3703,6 +3696,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicyCreateReadPatch() {
|
|||||||
Resolution: "resolution",
|
Resolution: "resolution",
|
||||||
Platform: "linux",
|
Platform: "linux",
|
||||||
Critical: true,
|
Critical: true,
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
}
|
}
|
||||||
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), createPol1Req, http.StatusOK, &createPol1)
|
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), createPol1Req, http.StatusOK, &createPol1)
|
||||||
allEqual(s.T(), createPol1Req, createPol1.Policy, fields...)
|
allEqual(s.T(), createPol1Req, createPol1.Policy, fields...)
|
||||||
@ -3715,6 +3709,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicyCreateReadPatch() {
|
|||||||
Resolution: "resolution",
|
Resolution: "resolution",
|
||||||
Platform: "linux",
|
Platform: "linux",
|
||||||
Critical: false,
|
Critical: false,
|
||||||
|
CalendarEventsEnabled: false,
|
||||||
}
|
}
|
||||||
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), createPol2Req, http.StatusOK, &createPol2)
|
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), createPol2Req, http.StatusOK, &createPol2)
|
||||||
allEqual(s.T(), createPol2Req, createPol2.Policy, fields...)
|
allEqual(s.T(), createPol2Req, createPol2.Policy, fields...)
|
||||||
@ -3736,6 +3731,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicyCreateReadPatch() {
|
|||||||
Resolution: ptr.String("newResolution"),
|
Resolution: ptr.String("newResolution"),
|
||||||
Platform: ptr.String("windows"),
|
Platform: ptr.String("windows"),
|
||||||
Critical: ptr.Bool(false),
|
Critical: ptr.Bool(false),
|
||||||
|
CalendarEventsEnabled: ptr.Bool(false),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
patchPol1 := &modifyTeamPolicyResponse{}
|
patchPol1 := &modifyTeamPolicyResponse{}
|
||||||
@ -3750,6 +3746,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicyCreateReadPatch() {
|
|||||||
Resolution: ptr.String("newResolution"),
|
Resolution: ptr.String("newResolution"),
|
||||||
Platform: ptr.String("windows"),
|
Platform: ptr.String("windows"),
|
||||||
Critical: ptr.Bool(true),
|
Critical: ptr.Bool(true),
|
||||||
|
CalendarEventsEnabled: ptr.Bool(true),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
patchPol2 := &modifyTeamPolicyResponse{}
|
patchPol2 := &modifyTeamPolicyResponse{}
|
||||||
|
@ -28,6 +28,7 @@ type teamPolicyRequest struct {
|
|||||||
Resolution string `json:"resolution"`
|
Resolution string `json:"resolution"`
|
||||||
Platform string `json:"platform"`
|
Platform string `json:"platform"`
|
||||||
Critical bool `json:"critical" premium:"true"`
|
Critical bool `json:"critical" premium:"true"`
|
||||||
|
CalendarEventsEnabled bool `json:"calendar_events_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type teamPolicyResponse struct {
|
type teamPolicyResponse struct {
|
||||||
@ -47,6 +48,7 @@ func teamPolicyEndpoint(ctx context.Context, request interface{}, svc fleet.Serv
|
|||||||
Resolution: req.Resolution,
|
Resolution: req.Resolution,
|
||||||
Platform: req.Platform,
|
Platform: req.Platform,
|
||||||
Critical: req.Critical,
|
Critical: req.Critical,
|
||||||
|
CalendarEventsEnabled: req.CalendarEventsEnabled,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return teamPolicyResponse{Err: err}, nil
|
return teamPolicyResponse{Err: err}, nil
|
||||||
@ -390,6 +392,9 @@ func (svc *Service) modifyPolicy(ctx context.Context, teamID *uint, id uint, p f
|
|||||||
if p.Critical != nil {
|
if p.Critical != nil {
|
||||||
policy.Critical = *p.Critical
|
policy.Critical = *p.Critical
|
||||||
}
|
}
|
||||||
|
if p.CalendarEventsEnabled != nil {
|
||||||
|
policy.CalendarEventsEnabled = *p.CalendarEventsEnabled
|
||||||
|
}
|
||||||
logging.WithExtras(ctx, "name", policy.Name, "sql", policy.Query)
|
logging.WithExtras(ctx, "name", policy.Name, "sql", policy.Query)
|
||||||
|
|
||||||
err = svc.ds.SavePolicy(ctx, policy, shouldRemoveAll)
|
err = svc.ds.SavePolicy(ctx, policy, shouldRemoveAll)
|
||||||
|
@ -124,7 +124,8 @@ func TestTriggerFailingPoliciesWebhookBasic(t *testing.T) {
|
|||||||
"passing_host_count": 0,
|
"passing_host_count": 0,
|
||||||
"failing_host_count": 0,
|
"failing_host_count": 0,
|
||||||
"host_count_updated_at": null,
|
"host_count_updated_at": null,
|
||||||
"critical": true
|
"critical": true,
|
||||||
|
"calendar_events_enabled": false
|
||||||
},
|
},
|
||||||
"hosts": [
|
"hosts": [
|
||||||
{
|
{
|
||||||
@ -193,6 +194,7 @@ func TestTriggerFailingPoliciesWebhookTeam(t *testing.T) {
|
|||||||
TeamID: &teamID,
|
TeamID: &teamID,
|
||||||
Resolution: ptr.String("policy1 resolution"),
|
Resolution: ptr.String("policy1 resolution"),
|
||||||
Platform: "darwin",
|
Platform: "darwin",
|
||||||
|
CalendarEventsEnabled: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
@ -309,7 +311,8 @@ func TestTriggerFailingPoliciesWebhookTeam(t *testing.T) {
|
|||||||
"passing_host_count": 0,
|
"passing_host_count": 0,
|
||||||
"failing_host_count": 0,
|
"failing_host_count": 0,
|
||||||
"host_count_updated_at": null,
|
"host_count_updated_at": null,
|
||||||
"critical": false
|
"critical": false,
|
||||||
|
"calendar_events_enabled": true
|
||||||
},
|
},
|
||||||
"hosts": [
|
"hosts": [
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user