Add activity types for applied team spec and edited agent options (#7355)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* Added activities for when a team spec is applied and when the agent options are edited.
|
||||
@@ -144,6 +144,10 @@ func TestApplyTeamSpecs(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
filename := writeTmpYml(t, `
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -147,7 +147,21 @@ func (svc *Service) ModifyTeamAgentOptions(ctx context.Context, teamID uint, opt
|
||||
team.Config.AgentOptions = nil
|
||||
}
|
||||
|
||||
return svc.ds.SaveTeam(ctx, team)
|
||||
tm, err := svc.ds.SaveTeam(ctx, team)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.ds.NewActivity(
|
||||
ctx,
|
||||
authz.UserFromContext(ctx),
|
||||
fleet.ActivityTypeEditedAgentOptions,
|
||||
&map[string]interface{}{"global": false, "team_id": team.ID, "team_name": team.Name},
|
||||
); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "create edited agent options activity")
|
||||
}
|
||||
|
||||
return tm, nil
|
||||
}
|
||||
|
||||
func (svc *Service) AddTeamUsers(ctx context.Context, teamID uint, users []fleet.TeamUser) (*fleet.Team, error) {
|
||||
@@ -369,6 +383,12 @@ func (svc Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec)
|
||||
return err
|
||||
}
|
||||
|
||||
type activityDetail struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
var details []activityDetail
|
||||
|
||||
for _, spec := range specs {
|
||||
var secrets []*fleet.EnrollSecret
|
||||
for _, secret := range spec.Secrets {
|
||||
@@ -384,7 +404,7 @@ func (svc Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec)
|
||||
if agentOptions == nil {
|
||||
agentOptions = config.AgentOptions
|
||||
}
|
||||
_, err = svc.ds.NewTeam(ctx, &fleet.Team{
|
||||
tm, err := svc.ds.NewTeam(ctx, &fleet.Team{
|
||||
Name: spec.Name,
|
||||
Config: fleet.TeamConfig{
|
||||
AgentOptions: agentOptions,
|
||||
@@ -394,6 +414,10 @@ func (svc Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
details = append(details, activityDetail{
|
||||
ID: tm.ID,
|
||||
Name: tm.Name,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -418,7 +442,20 @@ func (svc Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
details = append(details, activityDetail{
|
||||
ID: team.ID,
|
||||
Name: team.Name,
|
||||
})
|
||||
}
|
||||
|
||||
if err := svc.ds.NewActivity(
|
||||
ctx,
|
||||
authz.UserFromContext(ctx),
|
||||
fleet.ActivityTypeAppliedSpecTeam,
|
||||
&map[string]interface{}{"teams": details},
|
||||
); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "create applied team spec activity")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -40,6 +40,11 @@ const (
|
||||
// ActivityTypeUserAddedBySSO is the activity type for new users added
|
||||
// via SSO JIT provisioning
|
||||
ActivityTypeUserAddedBySSO = "user_added_by_sso"
|
||||
// ActivityTypeEditedAgentOptions is the activity type for when the agent
|
||||
// options are edited (either globally or for a team).
|
||||
ActivityTypeEditedAgentOptions = "edited_agent_options"
|
||||
// ActivityTypeAppliedSpecTeam is the activity type for a team spec applied
|
||||
ActivityTypeAppliedSpecTeam = "applied_spec_team"
|
||||
)
|
||||
|
||||
type Activity struct {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/authz"
|
||||
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
|
||||
@@ -201,6 +202,10 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte) (*fleet.AppCo
|
||||
}
|
||||
|
||||
oldSmtpSettings := appConfig.SMTPSettings
|
||||
oldAgentOptions := ""
|
||||
if appConfig.AgentOptions != nil {
|
||||
oldAgentOptions = string(*appConfig.AgentOptions)
|
||||
}
|
||||
|
||||
storedJiraByProjectKey, err := fleet.IndexJiraIntegrations(appConfig.Integrations.Jira)
|
||||
if err != nil {
|
||||
@@ -306,6 +311,22 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte) (*fleet.AppCo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if the agent options changed, create the corresponding activity
|
||||
newAgentOptions := ""
|
||||
if obfuscatedConfig.AgentOptions != nil {
|
||||
newAgentOptions = string(*obfuscatedConfig.AgentOptions)
|
||||
}
|
||||
if oldAgentOptions != newAgentOptions {
|
||||
if err := svc.ds.NewActivity(
|
||||
ctx,
|
||||
authz.UserFromContext(ctx),
|
||||
fleet.ActivityTypeEditedAgentOptions,
|
||||
&map[string]interface{}{"global": true, "team_id": nil, "team_name": nil},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return obfuscatedConfig, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4119,6 +4119,25 @@ func (s *integrationTestSuite) TestAppConfig() {
|
||||
}`), http.StatusOK, &acResp)
|
||||
assert.Equal(t, "test", acResp.OrgInfo.OrgName)
|
||||
|
||||
// the global agent options were not modified by the last call, so the
|
||||
// corresponding activity should not have been created.
|
||||
var listActivities listActivitiesResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
|
||||
if !assert.Len(t, listActivities.Activities, 0) {
|
||||
// if there is an activity, make sure it is not edited_agent_options
|
||||
require.NotEqual(t, fleet.ActivityTypeEditedAgentOptions, listActivities.Activities[0].Type)
|
||||
}
|
||||
|
||||
// test a change that does modify the agent options.
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"agent_options": { "foo": 1 }
|
||||
}`), http.StatusOK, &acResp)
|
||||
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
|
||||
require.True(t, len(listActivities.Activities) > 0)
|
||||
require.Equal(t, fleet.ActivityTypeEditedAgentOptions, listActivities.Activities[0].Type)
|
||||
require.NotNil(t, listActivities.Activities[0].Details)
|
||||
assert.JSONEq(t, `{"global": true, "team_id": null, "team_name": null}`, string(*listActivities.Activities[0].Details))
|
||||
|
||||
var verResp versionResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/version", nil, http.StatusOK, &verResp)
|
||||
assert.NotEmpty(t, verResp.Branch)
|
||||
|
||||
@@ -74,6 +74,14 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
assert.Len(t, team.Secrets, 1)
|
||||
require.JSONEq(t, string(agentOpts), string(*team.Config.AgentOptions))
|
||||
|
||||
// an activity was created for team spec applied
|
||||
var listActivities listActivitiesResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
|
||||
require.True(t, len(listActivities.Activities) > 0)
|
||||
assert.Equal(t, fleet.ActivityTypeAppliedSpecTeam, listActivities.Activities[0].Type)
|
||||
require.NotNil(t, listActivities.Activities[0].Details)
|
||||
assert.JSONEq(t, fmt.Sprintf(`{"teams": [{"id": %d, "name": %q}]}`, team.ID, team.Name), string(*listActivities.Activities[0].Details))
|
||||
|
||||
// creates a team with default agent options
|
||||
user, err := s.ds.UserByEmail(context.Background(), "admin1@example.com")
|
||||
require.NoError(t, err)
|
||||
@@ -97,6 +105,13 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
require.NotNil(t, team.Config.AgentOptions)
|
||||
require.JSONEq(t, defaultOpts, string(*team.Config.AgentOptions))
|
||||
|
||||
// an activity was created for the newly created team via the applied spec
|
||||
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
|
||||
require.True(t, len(listActivities.Activities) > 0)
|
||||
assert.Equal(t, fleet.ActivityTypeAppliedSpecTeam, listActivities.Activities[0].Type)
|
||||
require.NotNil(t, listActivities.Activities[0].Details)
|
||||
assert.JSONEq(t, fmt.Sprintf(`{"teams": [{"id": %d, "name": %q}]}`, team.ID, team.Name), string(*listActivities.Activities[0].Details))
|
||||
|
||||
// updates secrets
|
||||
teamSpecs = applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{Name: "team2", Secrets: []fleet.EnrollSecret{{Secret: "ABC"}}}}}
|
||||
s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK)
|
||||
@@ -448,6 +463,14 @@ func (s *integrationEnterpriseTestSuite) TestTeamEndpoints() {
|
||||
require.NoError(t, json.Unmarshal(*tmResp.Team.Config.AgentOptions, &m))
|
||||
assert.Equal(t, opts, m)
|
||||
|
||||
// list activities, it should have created one for edited_agent_options
|
||||
var listActivities listActivitiesResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
|
||||
require.True(t, len(listActivities.Activities) > 0)
|
||||
assert.Equal(t, fleet.ActivityTypeEditedAgentOptions, listActivities.Activities[0].Type)
|
||||
require.NotNil(t, listActivities.Activities[0].Details)
|
||||
assert.JSONEq(t, fmt.Sprintf(`{"global": false, "team_id": %d, "team_name": %q}`, tm1ID, team.Name), string(*listActivities.Activities[0].Details))
|
||||
|
||||
// modify team agent options - unknown team
|
||||
tmResp.Team = nil
|
||||
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/agent_options", tm1ID+1), opts, http.StatusNotFound, &tmResp)
|
||||
|
||||
Reference in New Issue
Block a user