Add activity types for applied team spec and edited agent options (#7355)

This commit is contained in:
Martin Angers
2022-08-24 08:32:45 -04:00
committed by GitHub
parent 714afaa5ad
commit dfd7d3f244
8 changed files with 118 additions and 7 deletions
@@ -0,0 +1 @@
* Added activities for when a team spec is applied and when the agent options are edited.
+4
View File
@@ -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
+39 -2
View File
@@ -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
+5
View File
@@ -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 {
+21
View File
@@ -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
}
+19
View File
@@ -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)