Make activities typed and auto-generate docs for each type (#9069)

* Make activities type and auto-generate docs for each type

* Add pageOrderInSection to not break site

* Add do not edit note to generated file

* Add make generate-doc step

* Fix main merge
This commit is contained in:
Lucas Manuel Rodriguez 2022-12-23 13:05:16 -03:00 committed by GitHub
parent 54e8b3e250
commit 39f1029390
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1412 additions and 199 deletions

View file

@ -176,6 +176,9 @@ generate-mock: .prefix
go install github.com/groob/mockimpl@latest
go generate github.com/fleetdm/fleet/v4/server/mock github.com/fleetdm/fleet/v4/server/mock/mockresult
generate-doc: .prefix
go generate github.com/fleetdm/fleet/v4/server/fleet
deps: deps-js deps-go
deps-js:
@ -400,4 +403,4 @@ db-replica-reset: fleet
# db-replica-run runs fleet serve with one main and one read MySQL instance.
db-replica-run: fleet
FLEET_MYSQL_ADDRESS=127.0.0.1:3308 FLEET_MYSQL_READ_REPLICA_ADDRESS=127.0.0.1:3309 FLEET_MYSQL_READ_REPLICA_USERNAME=fleet FLEET_MYSQL_READ_REPLICA_DATABASE=fleet FLEET_MYSQL_READ_REPLICA_PASSWORD=insecure ./build/fleet serve --dev --dev_license
FLEET_MYSQL_ADDRESS=127.0.0.1:3308 FLEET_MYSQL_READ_REPLICA_ADDRESS=127.0.0.1:3309 FLEET_MYSQL_READ_REPLICA_USERNAME=fleet FLEET_MYSQL_READ_REPLICA_DATABASE=fleet FLEET_MYSQL_READ_REPLICA_PASSWORD=insecure ./build/fleet serve --dev --dev_license

View file

@ -145,7 +145,7 @@ func TestApplyTeamSpecs(t *testing.T) {
return nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -241,7 +241,7 @@ func TestApplyAppConfig(t *testing.T) {
return userRoleSpecList, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -316,7 +316,7 @@ func TestApplyAppConfigDryRunIssue(t *testing.T) {
return userRoleSpecList[1], nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -507,7 +507,7 @@ func TestApplyPolicies(t *testing.T) {
}
return nil, errors.New("unexpected team name!")
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -609,7 +609,7 @@ func TestApplyPacks(t *testing.T) {
ds.ListPacksFunc = func(ctx context.Context, opt fleet.PackListOptions) ([]*fleet.Pack, error) {
return nil, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -672,7 +672,7 @@ func TestApplyQueries(t *testing.T) {
appliedQueries = queries
return nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -809,7 +809,7 @@ func TestApplySpecs(t *testing.T) {
}
// activities
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}

View file

@ -52,7 +52,7 @@ func TestDeletePack(t *testing.T) {
Disabled: false,
}, true, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -95,7 +95,7 @@ func TestDeleteQuery(t *testing.T) {
ObserverCanRun: false,
}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}

View file

@ -64,7 +64,7 @@ func TestLiveQuery(t *testing.T) {
ds.CountHostsInTargetsFunc = func(ctx context.Context, filter fleet.TeamFilter, targets fleet.HostTargets, now time.Time) (fleet.TargetMetrics, error) {
return fleet.TargetMetrics{TotalHosts: 1, OnlineHosts: 1}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}

View file

@ -34,8 +34,8 @@ func TestUserDelete(t *testing.T) {
deletedUser = id
return nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
assert.Equal(t, fleet.ActivityTypeDeletedUser, activityType)
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
assert.Equal(t, fleet.ActivityTypeDeletedUser{}.ActivityName(), activity.ActivityName())
return nil
}
@ -66,7 +66,7 @@ func TestUserCreateForcePasswordReset(t *testing.T) {
ds.InviteByEmailFunc = func(ctx context.Context, email string) (*fleet.Invite, error) {
return nil, &notFoundError{}
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -124,7 +124,7 @@ func TestCreateBulkUsers(t *testing.T) {
ds.InviteByEmailFunc = func(ctx context.Context, email string) (*fleet.Invite, error) {
return nil, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
@ -142,12 +142,11 @@ func TestCreateBulkUsers(t *testing.T) {
assert.Equal(t, "", runAppForTest(t, []string{"user", "create-users", "--csv", csvFile}))
assert.Equal(t, expectedText, runAppForTest(t, []string{"get", "user_roles", "--json"}))
}
func TestDeleteBulkUsers(t *testing.T) {
_, ds := runServerWithMockedDS(t)
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
csvFilePath := writeTmpCsv(t,

View file

@ -0,0 +1,512 @@
<!-- DO NOT EDIT. This document is automatically generated. -->
# Audit Activities
Fleet logs the following information for administrative actions (in JSON):
- `created_at`: Timestamp of the event.
- `id`: Unique ID of the generated event in Fleet.
- `actor_full_name`: Author user name (missing if the user was deleted).
- `actor_id`: Unique ID of the author in Fleet (missing if the user was deleted).
- `actor_gravatar`: Gravatar URL of the author (missing if the user was deleted).
- `actor_email`: E-mail of the author (missing if the user was deleted).
- `type`: Type of the activity (see all types below).
- `details`: Specific details depending on the type of activity (see details for each activity type below).
Example:
```json
{
"created_at": "2022-12-20T14:54:17Z",
"id": 6,
"actor_full_name": "Gandalf",
"actor_id": 2,
"actor_gravatar": "foo@example.com",
"actor_email": "foo@example.com",
"type": "edited_saved_query",
"details":{
"query_id": 42,
"query_name": "Some query name"
}
}
```
## List of activities and their specific details
### Type `created_pack`
Generated when creating scheduled query packs.
This activity contains the following fields:
- "pack_id": the id of the created pack.
- "pack_name": the name of the created pack.
#### Example
```json
{
"pack_id": 123,
"pack_name": "foo"
}
```
### Type `edited_pack`
Generated when editing scheduled query packs.
This activity contains the following fields:
- "pack_id": the id of the edited pack.
- "pack_name": the name of the edited pack.
#### Example
```json
{
"pack_id": 123,
"pack_name": "foo"
}
```
### Type `deleted_pack`
Generated when deleting scheduled query packs.
This activity contains the following fields:
- "pack_name": the name of the created pack.
#### Example
```json
{
"pack_name": "foo"
}
```
### Type `applied_spec_pack`
Generated when applying a scheduled query pack spec.
This activity does not contain any detail fields.
### Type `created_policy`
Generated when creating policies.
This activity contains the following fields:
- "policy_id": the ID of the created policy.
- "policy_name": the name of the created policy.
#### Example
```json
{
"policy_id": 123,
"policy_name": "foo"
}
```
### Type `edited_policy`
Generated when editing policies.
This activity contains the following fields:
- "policy_id": the ID of the edited policy.
- "policy_name": the name of the edited policy.
#### Example
```json
{
"policy_id": 123,
"policy_name": "foo"
}
```
### Type `deleted_policy`
Generated when deleting policies.
This activity contains the following fields:
- "policy_id": the ID of the deleted policy.
- "policy_name": the name of the deleted policy.
#### Example
```json
{
"policy_id": 123,
"policy_name": "foo"
}
```
### Type `applied_spec_policy`
Generated when applying policy specs.
This activity contains a field "policies" where each item is a policy spec with the following fields:
- "name": Name of the applied policy.
- "query": SQL query of the policy.
- "description": Description of the policy.
- "critical": Marks the policy as high impact.
- "resolution": Describes how to solve a failing policy.
- "team": Name of the team this policy belongs to.
- "platform": Comma-separated string to indicate the target platforms.
#### Example
```json
{
"policies": [
{
"name":"Gatekeeper enabled (macOS)",
"query":"SELECT 1 FROM gatekeeper WHERE assessments_enabled = 1;",
"critical":false,
"platform":"darwin",
"resolution":"To enable Gatekeeper, on the failing device [...]",
"description":"Checks to make sure that the Gatekeeper feature is [...]"
},
{
"name":"Full disk encryption enabled (Windows)",
"query":"SELECT 1 FROM bitlocker_info WHERE drive_letter='C:' AND protection_status=1;",
"critical":false,
"platform":"windows",
"resolution":"To get additional information, run the following osquery [...]",
"description":"Checks to make sure that full disk encryption is enabled on Windows devices."
}
]
}
```
### Type `created_saved_query`
Generated when creating a new query.
This activity contains the following fields:
- "query_id": the ID of the created query.
- "query_name": the name of the created query.
#### Example
```json
{
"query_id": 123,
"query_name": "foo"
}
```
### Type `edited_saved_query`
Generated when editing a saved query.
This activity contains the following fields:
- "query_id": the ID of the query being edited.
- "query_name": the name of the query being edited.
#### Example
```json
{
"query_id": 123,
"query_name": "foo"
}
```
### Type `deleted_saved_query`
Generated when deleting a saved query.
This activity contains the following fields:
- "query_name": the name of the query being deleted.
#### Example
```json
{
"query_name": "foo"
}
```
### Type `deleted_multiple_saved_query`
Generated when deleting multiple saved queries.
This activity contains the following fields:
- "query_ids": list of IDs of the deleted saved queries.
#### Example
```json
{
"query_ids": [1, 42, 100]
}
```
### Type `applied_spec_saved_query`
Generated when applying a query spec.
This activity contains a field "specs" where each item is a query spec with the following fields:
- "name": Name of the query.
- "description": Description of the query.
- "query": SQL query.
#### Example
```json
{
"specs": [
{
"name":"Get OpenSSL versions",
"query":"SELECT name AS name, version AS version, 'deb_packages' AS source FROM [...]",
"description":"Retrieves the OpenSSL version."
}
]
}
```
### Type `created_team`
Generated when creating teams.
This activity contains the following fields:
- "team_id": unique ID of the created team.
- "team_name": the name of the created team.
#### Example
```json
{
"team_id": 123,
"team_name": "foo"
}
```
### Type `deleted_team`
Generated when deleting teams.
This activity contains the following fields:
- "team_id": unique ID of the deleted team.
- "team_name": the name of the deleted team.
#### Example
```json
{
"team_id": 123,
"team_name": "foo"
}
```
### Type `applied_spec_team`
Generated when applying team specs.
This activity contains a field "teams" where each item contains the team details with the following fields:
- "id": Unique ID of the team.
- "name": Name of the team.
#### Example
```json
{
"teams": [
{
"id": 123,
"name": "foo"
}
]
}
```
### Type `edited_agent_options`
Generated when agent options are edited (either globally or for a team).
This activity contains the following fields:
- "global": "true" if the user updated the global agent options, "false" if the agent options of a team were updated.
- "team_id": unique ID of the team for which the agent options were updated (null if global is true).
- "team_name": the name of the team for which the agent options were updated (null if global is true).
#### Example
```json
{
"team_id": 123,
"team_name": "foo",
"global": false
}
```
### Type `live_query`
Generated when running live queries.
This activity contains the following fields:
- "targets_count": Number of hosts where the live query was targeted to run.
- "query_sql": The SQL query to run on hosts.
- "query_name": Name of the query (this field is not set if this was not a saved query).
#### Example
```json
{
"targets_count": 5000,
"query_sql": "SELECT * from osquery_info;",
"query_name": "foo"
}
```
### Type `user_added_by_sso`
Generated when new users are added via SSO JIT provisioning
This activity does not contain any detail fields.
### Type `user_logged_in`
Generated when users successfully log in to Fleet.
This activity contains the following fields:
- "public_ip": Public IP of the login request.
#### Example
```json
{
"public_ip": "168.226.215.82"
}
```
### Type `created_user`
Generated when a user is created.
This activity contains the following fields:
- "user_id": Unique ID of the created user in Fleet.
- "user_name": Name of the created user.
- "user_email": E-mail of the created user.
#### Example
```json
{
"user_id": 42,
"user_name": "Foo",
"user_email": "foo@example.com"
}
```
### Type `deleted_user`
Generated when a user is deleted.
This activity contains the following fields:
- "user_id": Unique ID of the deleted user in Fleet.
- "user_name": Name of the deleted user.
- "user_email": E-mail of the deleted user.
#### Example
```json
{
"user_id": 42,
"user_name": "Foo",
"user_email": "foo@example.com"
}
```
### Type `changed_user_global_role`
Generated when user global roles are changed.
This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": New global role of the edited user.
#### Example
```json
{
"user_id": 42,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Observer"
}
```
### Type `deleted_user_global_role`
Generated when user global roles are deleted.
This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": Deleted global role of the edited user.
#### Example
```json
{
"user_id": 43,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Maintainer"
}
```
### Type `changed_user_team_role`
Generated when user team roles are changed.
This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": Team role set to the edited user.
- "team_id": Unique ID of the team of the changed role.
- "team_name": Name of the team of the changed role.
#### Example
```json
{
"user_id": 43,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Maintainer",
"team_id": 5,
"team_name": "Bar"
}
```
### Type `deleted_user_team_role`
Generated when user team roles are deleted.
This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": Team role deleted from the edited user.
- "team_id": Unique ID of the team of the deleted role.
- "team_name": Name of the team of the deleted role.
#### Example
```json
{
"user_id": 44,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Observer",
"team_id": 2,
"team_name": "Zoo"
}
```
<meta name="pageOrderInSection" value="1400">

View file

@ -69,8 +69,10 @@ func (svc *Service) NewTeam(ctx context.Context, p fleet.TeamPayload) (*fleet.Te
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeCreatedTeam,
&map[string]interface{}{"team_id": team.ID, "team_name": team.Name},
fleet.ActivityTypeCreatedTeam{
ID: team.ID,
Name: team.Name,
},
); err != nil {
return nil, err
}
@ -177,8 +179,11 @@ func (svc *Service) ModifyTeamAgentOptions(ctx context.Context, teamID uint, tea
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedAgentOptions,
&map[string]interface{}{"global": false, "team_id": team.ID, "team_name": team.Name},
fleet.ActivityTypeEditedAgentOptions{
Global: false,
TeamID: &team.ID,
TeamName: &team.Name,
},
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create edited agent options activity")
}
@ -330,8 +335,10 @@ func (svc *Service) DeleteTeam(ctx context.Context, teamID uint) error {
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedTeam,
&map[string]interface{}{"team_id": teamID, "team_name": name},
fleet.ActivityTypeDeletedTeam{
ID: teamID,
Name: name,
},
)
}
@ -410,11 +417,7 @@ 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
var details []fleet.TeamActivityDetail
for _, spec := range specs {
var secrets []*fleet.EnrollSecret
@ -462,7 +465,7 @@ func (svc *Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec,
if err != nil {
return ctxerr.Wrap(ctx, err, "creating team from spec")
}
details = append(details, activityDetail{
details = append(details, fleet.TeamActivityDetail{
ID: team.ID,
Name: team.Name,
})
@ -473,7 +476,7 @@ func (svc *Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec,
return ctxerr.Wrap(ctx, err, "editing team from spec")
}
details = append(details, activityDetail{
details = append(details, fleet.TeamActivityDetail{
ID: team.ID,
Name: team.Name,
})
@ -483,8 +486,9 @@ func (svc *Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec,
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeAppliedSpecTeam,
&map[string]interface{}{"teams": details},
fleet.ActivityTypeAppliedSpecTeam{
Teams: details,
},
); err != nil {
return ctxerr.Wrap(ctx, err, "create applied team spec activity")
}

View file

@ -55,8 +55,7 @@ func (svc *Service) GetSSOUser(ctx context.Context, auth fleet.Auth) (*fleet.Use
err = svc.ds.NewActivity(
ctx,
user,
fleet.ActivityTypeUserAddedBySSO,
&map[string]interface{}{},
fleet.ActivityTypeUserAddedBySSO{},
)
if err != nil {
return nil, err

View file

@ -11,8 +11,8 @@ import (
)
// NewActivity stores an activity item that the user performed
func (ds *Datastore) NewActivity(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
detailsBytes, err := json.Marshal(details)
func (ds *Datastore) NewActivity(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
detailsBytes, err := json.Marshal(activity)
if err != nil {
return ctxerr.Wrap(ctx, err, "marshaling activity details")
}
@ -20,7 +20,7 @@ func (ds *Datastore) NewActivity(ctx context.Context, user *fleet.User, activity
`INSERT INTO activities (user_id, user_name, activity_type, details) VALUES(?,?,?,?)`,
user.ID,
user.Name,
activityType,
activity.ActivityName(),
detailsBytes,
)
if err != nil {

View file

@ -2,6 +2,7 @@ package mysql
import (
"context"
"encoding/json"
"testing"
"github.com/fleetdm/fleet/v4/server/fleet"
@ -28,6 +29,27 @@ func TestActivity(t *testing.T) {
}
}
type dummyActivity struct {
name string `json:"-"`
details map[string]interface{}
}
func (d dummyActivity) MarshalJSON() ([]byte, error) {
b, err := json.Marshal(d.details)
if err != nil {
return nil, err
}
return b, nil
}
func (d dummyActivity) ActivityName() string {
return d.name
}
func (d dummyActivity) Documentation() (activity string, details string, detailsExample string) {
return "", "", ""
}
func testActivityUsernameChange(t *testing.T, ds *Datastore) {
u := &fleet.User{
Password: []byte("asd"),
@ -38,8 +60,14 @@ func testActivityUsernameChange(t *testing.T, ds *Datastore) {
}
_, err := ds.NewUser(context.Background(), u)
require.Nil(t, err)
require.NoError(t, ds.NewActivity(context.Background(), u, "test1", &map[string]interface{}{"detail": 1, "sometext": "aaa"}))
require.NoError(t, ds.NewActivity(context.Background(), u, "test2", &map[string]interface{}{"detail": 2}))
require.NoError(t, ds.NewActivity(context.Background(), u, dummyActivity{
name: "test1",
details: map[string]interface{}{"detail": 1, "sometext": "aaa"},
}))
require.NoError(t, ds.NewActivity(context.Background(), u, dummyActivity{
name: "test2",
details: map[string]interface{}{"detail": 2},
}))
activities, err := ds.ListActivities(context.Background(), fleet.ListOptions{})
require.NoError(t, err)
@ -76,8 +104,14 @@ func testActivityNew(t *testing.T, ds *Datastore) {
}
_, err := ds.NewUser(context.Background(), u)
require.Nil(t, err)
require.NoError(t, ds.NewActivity(context.Background(), u, "test1", &map[string]interface{}{"detail": 1, "sometext": "aaa"}))
require.NoError(t, ds.NewActivity(context.Background(), u, "test2", &map[string]interface{}{"detail": 2}))
require.NoError(t, ds.NewActivity(context.Background(), u, dummyActivity{
name: "test1",
details: map[string]interface{}{"detail": 1, "sometext": "aaa"},
}))
require.NoError(t, ds.NewActivity(context.Background(), u, dummyActivity{
name: "test2",
details: map[string]interface{}{"detail": 2},
}))
opt := fleet.ListOptions{
Page: 0,

View file

@ -4,62 +4,427 @@ import (
"encoding/json"
)
const (
// ActivityTypeUserLoggedIn is the activity type for logging in a user
ActivityTypeUserLoggedIn = "user_logged_in"
// ActivityTypeCreatedPack is the activity type for created packs
ActivityTypeCreatedPack = "created_pack"
// ActivityTypeEditedPack is the activity type for edited packs
ActivityTypeEditedPack = "edited_pack"
// ActivityTypeDeletedPack is the activity type for deleted packs
ActivityTypeDeletedPack = "deleted_pack"
// ActivityTypeAppliedSpecPack is the activity type for pack specs applied
ActivityTypeAppliedSpecPack = "applied_spec_pack"
// ActivityTypeCreatedPolicy is the activity type for created policies
ActivityTypeCreatedPolicy = "created_policy"
// ActivityTypeEditedPolicy is the activity type for edited policies
ActivityTypeEditedPolicy = "edited_policy"
// ActivityTypeDeletedPolicy is the activity type for deleted policies
ActivityTypeDeletedPolicy = "deleted_policy"
// ActivityTypeAppliedSpecPolicy is the activity type for saved queries spec applied
ActivityTypeAppliedSpecPolicy = "applied_spec_policy"
// ActivityTypeCreatedSavedQuery is the activity type for created saved queries
ActivityTypeCreatedSavedQuery = "created_saved_query"
// ActivityTypeEditedSavedQuery is the activity type for edited saved queries
ActivityTypeEditedSavedQuery = "edited_saved_query"
// ActivityTypeDeletedSavedQuery is the activity type for deleted saved queries
ActivityTypeDeletedSavedQuery = "deleted_saved_query"
// ActivityTypeDeletedMultipleSavedQuery is the activity type for multiple deleted saved queries
ActivityTypeDeletedMultipleSavedQuery = "deleted_multiple_saved_query"
// ActivityTypeAppliedSpecSavedQuery is the activity type for saved queries spec applied
ActivityTypeAppliedSpecSavedQuery = "applied_spec_saved_query"
// ActivityTypeCreatedTeam is the activity type for created team
ActivityTypeCreatedTeam = "created_team"
// ActivityTypeDeletedTeam is the activity type for deleted team
ActivityTypeDeletedTeam = "deleted_team"
// ActivityTypeLiveQuery is the activity type for live queries
ActivityTypeLiveQuery = "live_query"
// 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"
// ActivityTypeCreatedUser is the activity type for created users.
ActivityTypeCreatedUser = "created_user"
// ActivityTypeDeletedUser is the activity type for deleted users.
ActivityTypeDeletedUser = "deleted_user"
// ActivityTypeChangedUserGlobalRole is the activity type for changed user global role.
ActivityTypeChangedUserGlobalRole = "changed_user_global_role"
// ActivityTypeDeletedUserGlobalRole is the activity type for deleted user global role.
ActivityTypeDeletedUserGlobalRole = "deleted_user_global_role"
// ActivityTypeChangedUserTeamRole is the activity type for changed user team role.
ActivityTypeChangedUserTeamRole = "changed_user_team_role"
// ActivityTypeDeletedUserTeamRole is the activity type for deleted user team role.
ActivityTypeDeletedUserTeamRole = "deleted_user_team_role"
)
//go:generate go run gen_activity_doc.go ../../docs/Using-Fleet/Audit-Activities.md
// ActivityDetailsList is used to generate documentation.
var ActivityDetailsList = []ActivityDetails{
ActivityTypeCreatedPack{},
ActivityTypeEditedPack{},
ActivityTypeDeletedPack{},
ActivityTypeAppliedSpecPack{},
ActivityTypeCreatedPolicy{},
ActivityTypeEditedPolicy{},
ActivityTypeDeletedPolicy{},
ActivityTypeAppliedSpecPolicy{},
ActivityTypeCreatedSavedQuery{},
ActivityTypeEditedSavedQuery{},
ActivityTypeDeletedSavedQuery{},
ActivityTypeDeletedMultipleSavedQuery{},
ActivityTypeAppliedSpecSavedQuery{},
ActivityTypeCreatedTeam{},
ActivityTypeDeletedTeam{},
ActivityTypeAppliedSpecTeam{},
ActivityTypeEditedAgentOptions{},
ActivityTypeLiveQuery{},
ActivityTypeUserAddedBySSO{},
ActivityTypeUserLoggedIn{},
ActivityTypeCreatedUser{},
ActivityTypeDeletedUser{},
ActivityTypeChangedUserGlobalRole{},
ActivityTypeDeletedUserGlobalRole{},
ActivityTypeChangedUserTeamRole{},
ActivityTypeDeletedUserTeamRole{},
}
type ActivityDetails interface {
// ActivityName is the name/type of the activity.
ActivityName() string
// Documentation is used by "go generate" to generate markdown docs.
Documentation() (activity string, details string, detailsExample string)
}
type ActivityTypeCreatedPack struct {
ID uint `json:"pack_id"`
Name string `json:"pack_name"`
}
func (a ActivityTypeCreatedPack) ActivityName() string {
return "created_pack"
}
func (a ActivityTypeCreatedPack) Documentation() (activity string, details string, detailsExample string) {
return `Generated when creating scheduled query packs.`,
`This activity contains the following fields:
- "pack_id": the id of the created pack.
- "pack_name": the name of the created pack.`, `{
"pack_id": 123,
"pack_name": "foo"
}`
}
type ActivityTypeEditedPack struct {
ID uint `json:"pack_id"`
Name string `json:"pack_name"`
}
func (a ActivityTypeEditedPack) ActivityName() string {
return "edited_pack"
}
func (a ActivityTypeEditedPack) Documentation() (activity string, details string, detailsExample string) {
return `Generated when editing scheduled query packs.`,
`This activity contains the following fields:
- "pack_id": the id of the edited pack.
- "pack_name": the name of the edited pack.`, `{
"pack_id": 123,
"pack_name": "foo"
}`
}
type ActivityTypeDeletedPack struct {
Name string `json:"pack_name"`
}
func (a ActivityTypeDeletedPack) ActivityName() string {
return "deleted_pack"
}
func (a ActivityTypeDeletedPack) Documentation() (activity string, details string, detailsExample string) {
return `Generated when deleting scheduled query packs.`,
`This activity contains the following fields:
- "pack_name": the name of the created pack.`, `{
"pack_name": "foo"
}`
}
type ActivityTypeAppliedSpecPack struct{}
func (a ActivityTypeAppliedSpecPack) ActivityName() string {
return "applied_spec_pack"
}
func (a ActivityTypeAppliedSpecPack) Documentation() (activity string, details string, detailsExample string) {
return `Generated when applying a scheduled query pack spec.`,
`This activity does not contain any detail fields.`, ""
}
type ActivityTypeCreatedPolicy struct {
ID uint `json:"policy_id"`
Name string `json:"policy_name"`
}
func (a ActivityTypeCreatedPolicy) ActivityName() string {
return "created_policy"
}
func (a ActivityTypeCreatedPolicy) Documentation() (activity string, details string, detailsExample string) {
return `Generated when creating policies.`,
`This activity contains the following fields:
- "policy_id": the ID of the created policy.
- "policy_name": the name of the created policy.`, `{
"policy_id": 123,
"policy_name": "foo"
}`
}
type ActivityTypeEditedPolicy struct {
ID uint `json:"policy_id"`
Name string `json:"policy_name"`
}
func (a ActivityTypeEditedPolicy) ActivityName() string {
return "edited_policy"
}
func (a ActivityTypeEditedPolicy) Documentation() (activity string, details string, detailsExample string) {
return `Generated when editing policies.`,
`This activity contains the following fields:
- "policy_id": the ID of the edited policy.
- "policy_name": the name of the edited policy.`, `{
"policy_id": 123,
"policy_name": "foo"
}`
}
type ActivityTypeDeletedPolicy struct {
ID uint `json:"policy_id"`
Name string `json:"policy_name"`
}
func (a ActivityTypeDeletedPolicy) ActivityName() string {
return "deleted_policy"
}
func (a ActivityTypeDeletedPolicy) Documentation() (activity string, details string, detailsExample string) {
return `Generated when deleting policies.`,
`This activity contains the following fields:
- "policy_id": the ID of the deleted policy.
- "policy_name": the name of the deleted policy.`, `{
"policy_id": 123,
"policy_name": "foo"
}`
}
type ActivityTypeAppliedSpecPolicy struct {
Policies []*PolicySpec `json:"policies"`
}
func (a ActivityTypeAppliedSpecPolicy) ActivityName() string {
return "applied_spec_policy"
}
func (a ActivityTypeAppliedSpecPolicy) Documentation() (activity string, details string, detailsExample string) {
return `Generated when applying policy specs.`,
`This activity contains a field "policies" where each item is a policy spec with the following fields:
- "name": Name of the applied policy.
- "query": SQL query of the policy.
- "description": Description of the policy.
- "critical": Marks the policy as high impact.
- "resolution": Describes how to solve a failing policy.
- "team": Name of the team this policy belongs to.
- "platform": Comma-separated string to indicate the target platforms.
`, `{
"policies": [
{
"name":"Gatekeeper enabled (macOS)",
"query":"SELECT 1 FROM gatekeeper WHERE assessments_enabled = 1;",
"critical":false,
"platform":"darwin",
"resolution":"To enable Gatekeeper, on the failing device [...]",
"description":"Checks to make sure that the Gatekeeper feature is [...]"
},
{
"name":"Full disk encryption enabled (Windows)",
"query":"SELECT 1 FROM bitlocker_info WHERE drive_letter='C:' AND protection_status=1;",
"critical":false,
"platform":"windows",
"resolution":"To get additional information, run the following osquery [...]",
"description":"Checks to make sure that full disk encryption is enabled on Windows devices."
}
]
}`
}
type ActivityTypeCreatedSavedQuery struct {
ID uint `json:"query_id"`
Name string `json:"query_name"`
}
func (a ActivityTypeCreatedSavedQuery) ActivityName() string {
return "created_saved_query"
}
func (a ActivityTypeCreatedSavedQuery) Documentation() (activity string, details string, detailsExample string) {
return `Generated when creating a new query.`,
`This activity contains the following fields:
- "query_id": the ID of the created query.
- "query_name": the name of the created query.`, `{
"query_id": 123,
"query_name": "foo"
}`
}
type ActivityTypeEditedSavedQuery struct {
ID uint `json:"query_id"`
Name string `json:"query_name"`
}
func (a ActivityTypeEditedSavedQuery) ActivityName() string {
return "edited_saved_query"
}
func (a ActivityTypeEditedSavedQuery) Documentation() (activity string, details string, detailsExample string) {
return `Generated when editing a saved query.`,
`This activity contains the following fields:
- "query_id": the ID of the query being edited.
- "query_name": the name of the query being edited.`, `{
"query_id": 123,
"query_name": "foo"
}`
}
type ActivityTypeDeletedSavedQuery struct {
Name string `json:"query_name"`
}
func (a ActivityTypeDeletedSavedQuery) ActivityName() string {
return "deleted_saved_query"
}
func (a ActivityTypeDeletedSavedQuery) Documentation() (activity string, details string, detailsExample string) {
return `Generated when deleting a saved query.`,
`This activity contains the following fields:
- "query_name": the name of the query being deleted.`, `{
"query_name": "foo"
}`
}
type ActivityTypeDeletedMultipleSavedQuery struct {
IDs []uint `json:"query_ids"`
}
func (a ActivityTypeDeletedMultipleSavedQuery) ActivityName() string {
return "deleted_multiple_saved_query"
}
func (a ActivityTypeDeletedMultipleSavedQuery) Documentation() (activity string, details string, detailsExample string) {
return `Generated when deleting multiple saved queries.`,
`This activity contains the following fields:
- "query_ids": list of IDs of the deleted saved queries.`, `{
"query_ids": [1, 42, 100]
}`
}
type ActivityTypeAppliedSpecSavedQuery struct {
Specs []*QuerySpec `json:"specs"`
}
func (a ActivityTypeAppliedSpecSavedQuery) ActivityName() string {
return "applied_spec_saved_query"
}
func (a ActivityTypeAppliedSpecSavedQuery) Documentation() (activity string, details string, detailsExample string) {
return `Generated when applying a query spec.`,
`This activity contains a field "specs" where each item is a query spec with the following fields:
- "name": Name of the query.
- "description": Description of the query.
- "query": SQL query.`, `{
"specs": [
{
"name":"Get OpenSSL versions",
"query":"SELECT name AS name, version AS version, 'deb_packages' AS source FROM [...]",
"description":"Retrieves the OpenSSL version."
}
]
}`
}
type ActivityTypeCreatedTeam struct {
ID uint `json:"team_id"`
Name string `json:"team_name"`
}
func (a ActivityTypeCreatedTeam) ActivityName() string {
return "created_team"
}
func (a ActivityTypeCreatedTeam) Documentation() (activity string, details string, detailsExample string) {
return `Generated when creating teams.`,
`This activity contains the following fields:
- "team_id": unique ID of the created team.
- "team_name": the name of the created team.`, `{
"team_id": 123,
"team_name": "foo"
}`
}
type ActivityTypeDeletedTeam struct {
ID uint `json:"team_id"`
Name string `json:"team_name"`
}
func (a ActivityTypeDeletedTeam) ActivityName() string {
return "deleted_team"
}
func (a ActivityTypeDeletedTeam) Documentation() (activity string, details string, detailsExample string) {
return `Generated when deleting teams.`,
`This activity contains the following fields:
- "team_id": unique ID of the deleted team.
- "team_name": the name of the deleted team.`, `{
"team_id": 123,
"team_name": "foo"
}`
}
type TeamActivityDetail struct {
ID uint `json:"id"`
Name string `json:"name"`
}
type ActivityTypeAppliedSpecTeam struct {
Teams []TeamActivityDetail `json:"teams"`
}
func (a ActivityTypeAppliedSpecTeam) ActivityName() string {
return "applied_spec_team"
}
func (a ActivityTypeAppliedSpecTeam) Documentation() (activity string, details string, detailsExample string) {
return `Generated when applying team specs.`,
`This activity contains a field "teams" where each item contains the team details with the following fields:
- "id": Unique ID of the team.
- "name": Name of the team.`, `{
"teams": [
{
"id": 123,
"name": "foo"
}
]
}`
}
type ActivityTypeEditedAgentOptions struct {
Global bool `json:"global"`
TeamID *uint `json:"team_id"`
TeamName *string `json:"team_name"`
}
func (a ActivityTypeEditedAgentOptions) ActivityName() string {
return "edited_agent_options"
}
func (a ActivityTypeEditedAgentOptions) Documentation() (activity string, details string, detailsExample string) {
return `Generated when agent options are edited (either globally or for a team).`,
`This activity contains the following fields:
- "global": "true" if the user updated the global agent options, "false" if the agent options of a team were updated.
- "team_id": unique ID of the team for which the agent options were updated (null if global is true).
- "team_name": the name of the team for which the agent options were updated (null if global is true).`, `{
"team_id": 123,
"team_name": "foo",
"global": false
}`
}
type ActivityTypeLiveQuery struct {
TargetsCount uint `json:"targets_count"`
QuerySQL string `json:"query_sql"`
QueryName *string `json:"query_name,omitempty"`
}
func (a ActivityTypeLiveQuery) ActivityName() string {
return "live_query"
}
func (a ActivityTypeLiveQuery) Documentation() (activity string, details string, detailsExample string) {
return `Generated when running live queries.`,
`This activity contains the following fields:
- "targets_count": Number of hosts where the live query was targeted to run.
- "query_sql": The SQL query to run on hosts.
- "query_name": Name of the query (this field is not set if this was not a saved query).`, `{
"targets_count": 5000,
"query_sql": "SELECT * from osquery_info;",
"query_name": "foo"
}`
}
type ActivityTypeUserAddedBySSO struct{}
func (a ActivityTypeUserAddedBySSO) ActivityName() string {
return "user_added_by_sso"
}
func (a ActivityTypeUserAddedBySSO) Documentation() (activity string, details string, detailsExample string) {
return `Generated when new users are added via SSO JIT provisioning`,
`This activity does not contain any detail fields.`, ""
}
type Activity struct {
CreateTimestamp
@ -72,6 +437,178 @@ type Activity struct {
Details *json.RawMessage `json:"details" db:"details"`
}
type ActivityTypeUserLoggedIn struct {
PublicIP string `json:"public_ip"`
}
func (a ActivityTypeUserLoggedIn) ActivityName() string {
return "user_logged_in"
}
func (a ActivityTypeUserLoggedIn) Documentation() (activity string, details string, detailsExample string) {
return `Generated when users successfully log in to Fleet.`,
`This activity contains the following fields:
- "public_ip": Public IP of the login request.`, `{
"public_ip": "168.226.215.82"
}`
}
type ActivityTypeCreatedUser struct {
UserID uint `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
}
func (a ActivityTypeCreatedUser) ActivityName() string {
return "created_user"
}
func (a ActivityTypeCreatedUser) Documentation() (activity string, details string, detailsExample string) {
return `Generated when a user is created.`,
`This activity contains the following fields:
- "user_id": Unique ID of the created user in Fleet.
- "user_name": Name of the created user.
- "user_email": E-mail of the created user.`, `{
"user_id": 42,
"user_name": "Foo",
"user_email": "foo@example.com"
}`
}
type ActivityTypeDeletedUser struct {
UserID uint `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
}
func (a ActivityTypeDeletedUser) ActivityName() string {
return "deleted_user"
}
func (a ActivityTypeDeletedUser) Documentation() (activity string, details string, detailsExample string) {
return `Generated when a user is deleted.`,
`This activity contains the following fields:
- "user_id": Unique ID of the deleted user in Fleet.
- "user_name": Name of the deleted user.
- "user_email": E-mail of the deleted user.`, `{
"user_id": 42,
"user_name": "Foo",
"user_email": "foo@example.com"
}`
}
type ActivityTypeChangedUserGlobalRole struct {
UserID uint `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
Role string `json:"role"`
}
func (a ActivityTypeChangedUserGlobalRole) ActivityName() string {
return "changed_user_global_role"
}
func (a ActivityTypeChangedUserGlobalRole) Documentation() (activity string, details string, detailsExample string) {
return `Generated when user global roles are changed.`,
`This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": New global role of the edited user.`, `{
"user_id": 42,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Observer"
}`
}
type ActivityTypeDeletedUserGlobalRole struct {
UserID uint `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
OldRole string `json:"role"`
}
func (a ActivityTypeDeletedUserGlobalRole) ActivityName() string {
return "deleted_user_global_role"
}
func (a ActivityTypeDeletedUserGlobalRole) Documentation() (activity string, details string, detailsExample string) {
return `Generated when user global roles are deleted.`,
`This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": Deleted global role of the edited user.`, `{
"user_id": 43,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Maintainer"
}`
}
type ActivityTypeChangedUserTeamRole struct {
UserID uint `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
Role string `json:"role"`
TeamID uint `json:"team_id"`
TeamName string `json:"team_name"`
}
func (a ActivityTypeChangedUserTeamRole) ActivityName() string {
return "changed_user_team_role"
}
func (a ActivityTypeChangedUserTeamRole) Documentation() (activity string, details string, detailsExample string) {
return `Generated when user team roles are changed.`,
`This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": Team role set to the edited user.
- "team_id": Unique ID of the team of the changed role.
- "team_name": Name of the team of the changed role.`, `{
"user_id": 43,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Maintainer",
"team_id": 5,
"team_name": "Bar"
}`
}
type ActivityTypeDeletedUserTeamRole struct {
UserID uint `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
Role string `json:"role"`
TeamID uint `json:"team_id"`
TeamName string `json:"team_name"`
}
func (a ActivityTypeDeletedUserTeamRole) ActivityName() string {
return "deleted_user_team_role"
}
func (a ActivityTypeDeletedUserTeamRole) Documentation() (activity string, details string, detailsExample string) {
return `Generated when user team roles are deleted.`,
`This activity contains the following fields:
- "user_id": Unique ID of the edited user in Fleet.
- "user_name": Name of the edited user.
- "user_email": E-mail of the edited user.
- "role": Team role deleted from the edited user.
- "team_id": Unique ID of the team of the deleted role.
- "team_name": Name of the team of the deleted role.`, `{
"user_id": 44,
"user_name": "Foo",
"user_email": "foo@example.com",
"role": "Observer",
"team_id": 2,
"team_name": "Zoo"
}`
}
// AuthzType implement AuthzTyper to be able to verify access to activities
func (*Activity) AuthzType() string {
return "activity"

View file

@ -437,7 +437,7 @@ type Datastore interface {
///////////////////////////////////////////////////////////////////////////////
// ActivitiesStore
NewActivity(ctx context.Context, user *User, activityType string, details *map[string]interface{}) error
NewActivity(ctx context.Context, user *User, activity ActivityDetails) error
ListActivities(ctx context.Context, opt ListOptions) ([]*Activity, error)
///////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,73 @@
//go:build ignore
// +build ignore
package main
import (
"fmt"
"os"
"strings"
"github.com/fleetdm/fleet/v4/server/fleet"
)
func main() {
var b strings.Builder
b.WriteString(`<!-- DO NOT EDIT. This document is automatically generated. -->
# Audit Activities
Fleet logs the following information for administrative actions (in JSON):
- ` + "`" + `created_at` + "`" + `: Timestamp of the event.
- ` + "`" + `id` + "`" + `: Unique ID of the generated event in Fleet.
- ` + "`" + `actor_full_name` + "`" + `: Author user name (missing if the user was deleted).
- ` + "`" + `actor_id` + "`" + `: Unique ID of the author in Fleet (missing if the user was deleted).
- ` + "`" + `actor_gravatar` + "`" + `: Gravatar URL of the author (missing if the user was deleted).
- ` + "`" + `actor_email` + "`" + `: E-mail of the author (missing if the user was deleted).
- ` + "`" + `type` + "`" + `: Type of the activity (see all types below).
- ` + "`" + `details` + "`" + `: Specific details depending on the type of activity (see details for each activity type below).
Example:
` + "```" + `json
{
"created_at": "2022-12-20T14:54:17Z",
"id": 6,
"actor_full_name": "Gandalf",
"actor_id": 2,
"actor_gravatar": "foo@example.com",
"actor_email": "foo@example.com",
"type": "edited_saved_query",
"details":{
"query_id": 42,
"query_name": "Some query name"
}
}
` + "```" + `
## List of activities and their specific details
`)
activityMap := map[string]struct{}{}
for _, activity := range fleet.ActivityDetailsList {
if _, ok := activityMap[activity.ActivityName()]; ok {
panic(fmt.Sprintf("type %s already used", activity.ActivityName()))
}
activityMap[activity.ActivityName()] = struct{}{}
fmt.Fprintf(&b, "### Type `%s`\n\n", activity.ActivityName())
activityTypeDoc, detailsDoc, detailsExampleDoc := activity.Documentation()
fmt.Fprintf(&b, activityTypeDoc+"\n\n"+detailsDoc+"\n\n")
if detailsExampleDoc != "" {
fmt.Fprintf(&b, "#### Example\n\n```json\n%s\n```\n\n", detailsExampleDoc)
}
}
b.WriteString(`
<meta name="pageOrderInSection" value="1400">`)
if err := os.WriteFile(os.Args[1], []byte(b.String()), 0600); err != nil {
panic(err)
}
}

View file

@ -335,7 +335,7 @@ type UpdateHostOperatingSystemFunc func(ctx context.Context, hostID uint, hostOS
type CleanupHostOperatingSystemsFunc func(ctx context.Context) error
type NewActivityFunc func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error
type NewActivityFunc func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error
type ListActivitiesFunc func(ctx context.Context, opt fleet.ListOptions) ([]*fleet.Activity, error)
@ -2070,9 +2070,9 @@ func (s *DataStore) CleanupHostOperatingSystems(ctx context.Context) error {
return s.CleanupHostOperatingSystemsFunc(ctx)
}
func (s *DataStore) NewActivity(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
func (s *DataStore) NewActivity(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
s.NewActivityFuncInvoked = true
return s.NewActivityFunc(ctx, user, activityType, details)
return s.NewActivityFunc(ctx, user, activity)
}
func (s *DataStore) ListActivities(ctx context.Context, opt fleet.ListOptions) ([]*fleet.Activity, error) {

View file

@ -45,8 +45,12 @@ func logRoleChangeActivities(ctx context.Context, ds fleet.Datastore, adminUser
if err := ds.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeChangedUserGlobalRole,
&map[string]interface{}{"user_name": user.Name, "user_id": user.ID, "user_email": user.Email, "role": *user.GlobalRole},
fleet.ActivityTypeChangedUserGlobalRole{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
Role: *user.GlobalRole,
},
); err != nil {
return err
}
@ -55,8 +59,12 @@ func logRoleChangeActivities(ctx context.Context, ds fleet.Datastore, adminUser
if err := ds.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeDeletedUserGlobalRole,
&map[string]interface{}{"user_name": user.Name, "user_id": user.ID, "user_email": user.Email, "role": *oldRole},
fleet.ActivityTypeDeletedUserGlobalRole{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
OldRole: *oldRole,
},
); err != nil {
return err
}
@ -76,8 +84,14 @@ func logRoleChangeActivities(ctx context.Context, ds fleet.Datastore, adminUser
if err := ds.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeChangedUserTeamRole,
&map[string]interface{}{"user_name": user.Name, "user_id": user.ID, "user_email": user.Email, "team_name": t.Name, "team_id": t.ID, "role": t.Role},
fleet.ActivityTypeChangedUserTeamRole{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
Role: t.Role,
TeamID: t.ID,
TeamName: t.Name,
},
); err != nil {
return err
}
@ -89,8 +103,14 @@ func logRoleChangeActivities(ctx context.Context, ds fleet.Datastore, adminUser
if err := ds.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeDeletedUserTeamRole,
&map[string]interface{}{"user_name": user.Name, "user_id": user.ID, "user_email": user.Email, "team_name": o.Name, "team_id": o.ID, "role": o.Role},
fleet.ActivityTypeDeletedUserTeamRole{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
Role: o.Role,
TeamID: o.ID,
TeamName: o.Name,
},
); err != nil {
return err
}

View file

@ -107,8 +107,8 @@ func Test_logRoleChangeActivities(t *testing.T) {
ctx := context.Background()
ds := new(mock.Store)
var activities []string
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
activities = append(activities, activityType)
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
activities = append(activities, activity.ActivityName())
return nil
}
for _, tt := range tests {

View file

@ -412,8 +412,9 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedAgentOptions,
&map[string]interface{}{"global": true, "team_id": nil, "team_name": nil},
fleet.ActivityTypeEditedAgentOptions{
Global: true,
},
); err != nil {
return nil, err
}

View file

@ -162,18 +162,17 @@ func (svc *Service) NewDistributedQueryCampaign(ctx context.Context, queryString
return nil, ctxerr.Wrap(ctx, err, "counting hosts")
}
activityData := map[string]interface{}{
"targets_count": campaign.Metrics.TotalHosts,
"query_sql": query.Query,
activityData := fleet.ActivityTypeLiveQuery{
TargetsCount: campaign.Metrics.TotalHosts,
QuerySQL: query.Query,
}
if queryID != nil {
activityData["query_name"] = query.Name
activityData.QueryName = &query.Name
}
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeLiveQuery,
&activityData,
activityData,
); err != nil {
return nil, err
}

View file

@ -2,10 +2,11 @@ package service
import (
"context"
"github.com/stretchr/testify/require"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mock"
@ -85,14 +86,15 @@ func TestLiveQueryAuth(t *testing.T) {
return fleet.TargetMetrics{}, nil
}
var queryName, querySQL string
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
name := (*details)["query_name"]
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
act := activity.(fleet.ActivityTypeLiveQuery)
name := act.QueryName
if name == nil {
queryName = ""
} else {
queryName = name.(string)
queryName = *name
}
querySQL = (*details)["query_sql"].(string)
querySQL = act.QuerySQL
return nil
}
ds.QueryFunc = func(ctx context.Context, id uint) (*fleet.Query, error) {

View file

@ -73,8 +73,10 @@ func (svc Service) NewGlobalPolicy(ctx context.Context, p fleet.PolicyPayload) (
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeCreatedPolicy,
&map[string]interface{}{"policy_id": policy.ID, "policy_name": policy.Name},
fleet.ActivityTypeCreatedPolicy{
ID: policy.ID,
Name: policy.Name,
},
); err != nil {
return nil, err
}
@ -211,8 +213,10 @@ func (svc Service) DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedPolicy,
&map[string]interface{}{"policy_id": id, "policy_name": policiesByID[id].Name},
fleet.ActivityTypeDeletedPolicy{
ID: id,
Name: policiesByID[id].Name,
},
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "adding new activity for deleted policy")
}
@ -479,7 +483,8 @@ func (svc *Service) ApplyPolicySpecs(ctx context.Context, policies []*fleet.Poli
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeAppliedSpecPolicy,
&map[string]interface{}{"policies": policies},
fleet.ActivityTypeAppliedSpecPolicy{
Policies: policies,
},
)
}

View file

@ -40,7 +40,7 @@ func TestGlobalPoliciesAuth(t *testing.T) {
ds.ApplyPolicySpecsFunc = func(ctx context.Context, authorID uint, specs []*fleet.PolicySpec) error {
return nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
ds.SavePolicyFunc = func(ctx context.Context, p *fleet.Policy) error {

View file

@ -172,7 +172,7 @@ func setupAuthTest(t *testing.T) (fleet.Datastore, map[string]fleet.User, *httpt
sessions[sessionKey] = session
return session, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
return ds, usersMap, server

View file

@ -2047,18 +2047,17 @@ func (s *integrationTestSuite) TestListActivities() {
ctx := context.Background()
u := s.users["admin1@example.com"]
details := make(map[string]interface{})
prevActivities, err := s.ds.ListActivities(ctx, fleet.ListOptions{})
require.NoError(t, err)
err = s.ds.NewActivity(ctx, &u, fleet.ActivityTypeAppliedSpecPack, &details)
err = s.ds.NewActivity(ctx, &u, fleet.ActivityTypeAppliedSpecPack{})
require.NoError(t, err)
err = s.ds.NewActivity(ctx, &u, fleet.ActivityTypeDeletedPack, &details)
err = s.ds.NewActivity(ctx, &u, fleet.ActivityTypeDeletedPack{})
require.NoError(t, err)
err = s.ds.NewActivity(ctx, &u, fleet.ActivityTypeEditedPack, &details)
err = s.ds.NewActivity(ctx, &u, fleet.ActivityTypeEditedPack{})
require.NoError(t, err)
lenPage := len(prevActivities) + 2
@ -2066,16 +2065,16 @@ func (s *integrationTestSuite) TestListActivities() {
var listResp listActivitiesResponse
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listResp, "per_page", strconv.Itoa(lenPage), "order_key", "id")
require.Len(t, listResp.Activities, lenPage)
assert.Equal(t, fleet.ActivityTypeAppliedSpecPack, listResp.Activities[lenPage-2].Type)
assert.Equal(t, fleet.ActivityTypeDeletedPack, listResp.Activities[lenPage-1].Type)
assert.Equal(t, fleet.ActivityTypeAppliedSpecPack{}.ActivityName(), listResp.Activities[lenPage-2].Type)
assert.Equal(t, fleet.ActivityTypeDeletedPack{}.ActivityName(), listResp.Activities[lenPage-1].Type)
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listResp, "per_page", strconv.Itoa(lenPage), "order_key", "id", "page", "1")
require.Len(t, listResp.Activities, 1)
assert.Equal(t, fleet.ActivityTypeEditedPack, listResp.Activities[0].Type)
assert.Equal(t, fleet.ActivityTypeEditedPack{}.ActivityName(), listResp.Activities[0].Type)
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listResp, "per_page", "1", "order_key", "id", "order_direction", "desc")
require.Len(t, listResp.Activities, 1)
assert.Equal(t, fleet.ActivityTypeEditedPack, listResp.Activities[0].Type)
assert.Equal(t, fleet.ActivityTypeEditedPack{}.ActivityName(), listResp.Activities[0].Type)
}
func (s *integrationTestSuite) TestListGetCarves() {
@ -4468,7 +4467,7 @@ func (s *integrationTestSuite) TestAppConfig() {
s.DoJSON("GET", "/api/latest/fleet/activities", nil, http.StatusOK, &listActivities, "order_key", "id", "order_direction", "desc")
if !assert.Len(t, listActivities.Activities, 1) {
// if there is an activity, make sure it is not edited_agent_options
require.NotEqual(t, fleet.ActivityTypeEditedAgentOptions, listActivities.Activities[0].Type)
require.NotEqual(t, fleet.ActivityTypeEditedAgentOptions{}.ActivityName(), listActivities.Activities[0].Type)
}
// and it did not update the appconfig
@ -4488,7 +4487,7 @@ func (s *integrationTestSuite) TestAppConfig() {
}`), 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) > 1)
require.Equal(t, fleet.ActivityTypeEditedAgentOptions, listActivities.Activities[0].Type)
require.Equal(t, fleet.ActivityTypeEditedAgentOptions{}.ActivityName(), 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))

View file

@ -102,7 +102,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
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)
assert.Equal(t, fleet.ActivityTypeAppliedSpecTeam{}.ActivityName(), 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))
@ -217,7 +217,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
// 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)
assert.Equal(t, fleet.ActivityTypeAppliedSpecTeam{}.ActivityName(), 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))
@ -667,7 +667,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamEndpoints() {
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)
assert.Equal(t, fleet.ActivityTypeEditedAgentOptions{}.ActivityName(), 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))
@ -1475,7 +1475,7 @@ func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() {
require.NotEmpty(t, activitiesResp.Activities)
require.Condition(t, func() bool {
for _, a := range activitiesResp.Activities {
if a.Type == fleet.ActivityTypeUserAddedBySSO && *a.ActorEmail == auth.UserID() {
if (a.Type == fleet.ActivityTypeUserAddedBySSO{}.ActivityName()) && *a.ActorEmail == auth.UserID() {
return true
}
}

View file

@ -139,7 +139,7 @@ func (s *integrationSSOTestSuite) TestSSOLogin() {
require.NotEmpty(t, activitiesResp.Activities)
require.Condition(t, func() bool {
for _, a := range activitiesResp.Activities {
if a.Type == fleet.ActivityTypeUserLoggedIn && *a.ActorEmail == auth.UserID() {
if (a.Type == fleet.ActivityTypeUserLoggedIn{}.ActivityName()) && *a.ActorEmail == auth.UserID() {
return true
}
}

View file

@ -1429,7 +1429,7 @@ func TestNewDistributedQueryCampaign(t *testing.T) {
},
})
q := "select year, month, day, hour, minutes, seconds from time"
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
campaign, err := svc.NewDistributedQueryCampaign(viewerCtx, q, nil, fleet.HostTargets{HostIDs: []uint{2}, LabelIDs: []uint{1}})
@ -2196,7 +2196,7 @@ func TestObserversCanOnlyRunDistributedCampaigns(t *testing.T) {
})
q := "select year, month, day, hour, minutes, seconds from time"
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
_, err := svc.NewDistributedQueryCampaign(viewerCtx, q, nil, fleet.HostTargets{HostIDs: []uint{2}, LabelIDs: []uint{1}})
@ -2230,7 +2230,7 @@ func TestObserversCanOnlyRunDistributedCampaigns(t *testing.T) {
ds.HostIDsInTargetsFunc = func(ctx context.Context, filter fleet.TeamFilter, targets fleet.HostTargets) ([]uint, error) {
return []uint{1, 3, 5}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
lq.On("RunQuery", "21", "select 1;", []uint{1, 3, 5}).Return(nil)
@ -2270,7 +2270,7 @@ func TestTeamMaintainerCanRunNewDistributedCampaigns(t *testing.T) {
})
q := "select year, month, day, hour, minutes, seconds from time"
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
// var gotQuery *fleet.Query
@ -2288,7 +2288,7 @@ func TestTeamMaintainerCanRunNewDistributedCampaigns(t *testing.T) {
ds.HostIDsInTargetsFunc = func(ctx context.Context, filter fleet.TeamFilter, targets fleet.HostTargets) ([]uint, error) {
return []uint{1, 3, 5}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
lq.On("RunQuery", "0", "select year, month, day, hour, minutes, seconds from time", []uint{1, 3, 5}).Return(nil)

View file

@ -170,8 +170,10 @@ func (svc *Service) NewPack(ctx context.Context, p fleet.PackPayload) (*fleet.Pa
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeCreatedPack,
&map[string]interface{}{"pack_id": pack.ID, "pack_name": pack.Name},
fleet.ActivityTypeCreatedPack{
ID: pack.ID,
Name: pack.Name,
},
); err != nil {
return nil, err
}
@ -264,8 +266,10 @@ func (svc *Service) ModifyPack(ctx context.Context, id uint, p fleet.PackPayload
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedPack,
&map[string]interface{}{"pack_id": pack.ID, "pack_name": pack.Name},
fleet.ActivityTypeEditedPack{
ID: pack.ID,
Name: pack.Name,
},
); err != nil {
return nil, err
}
@ -358,8 +362,9 @@ func (svc *Service) DeletePack(ctx context.Context, name string) error {
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedPack,
&map[string]interface{}{"pack_name": name},
fleet.ActivityTypeDeletedPack{
Name: pack.Name,
},
)
}
@ -405,8 +410,9 @@ func (svc *Service) DeletePackByID(ctx context.Context, id uint) error {
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedPack,
&map[string]interface{}{"pack_name": pack.Name},
fleet.ActivityTypeDeletedPack{
Name: pack.Name,
},
)
}
@ -480,8 +486,7 @@ func (svc *Service) ApplyPackSpecs(ctx context.Context, specs []*fleet.PackSpec)
return result, svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeAppliedSpecPack,
&map[string]interface{}{},
fleet.ActivityTypeAppliedSpecPack{},
)
}

View file

@ -41,7 +41,7 @@ func TestNewPackSavesTargets(t *testing.T) {
ds.NewPackFunc = func(ctx context.Context, pack *fleet.Pack, opts ...fleet.OptionalArg) (*fleet.Pack, error) {
return pack, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}

View file

@ -185,8 +185,10 @@ func (svc *Service) NewQuery(ctx context.Context, p fleet.QueryPayload) (*fleet.
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeCreatedSavedQuery,
&map[string]interface{}{"query_id": query.ID, "query_name": query.Name},
fleet.ActivityTypeCreatedSavedQuery{
ID: query.ID,
Name: query.Name,
},
); err != nil {
return nil, err
}
@ -266,8 +268,10 @@ func (svc *Service) ModifyQuery(ctx context.Context, id uint, p fleet.QueryPaylo
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedSavedQuery,
&map[string]interface{}{"query_id": query.ID, "query_name": query.Name},
fleet.ActivityTypeEditedSavedQuery{
ID: query.ID,
Name: query.Name,
},
); err != nil {
return nil, err
}
@ -321,8 +325,9 @@ func (svc *Service) DeleteQuery(ctx context.Context, name string) error {
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedSavedQuery,
&map[string]interface{}{"query_name": name},
fleet.ActivityTypeDeletedSavedQuery{
Name: name,
},
)
}
@ -372,8 +377,9 @@ func (svc *Service) DeleteQueryByID(ctx context.Context, id uint) error {
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedSavedQuery,
&map[string]interface{}{"query_name": query.Name},
fleet.ActivityTypeDeletedSavedQuery{
Name: query.Name,
},
)
}
@ -427,8 +433,9 @@ func (svc *Service) DeleteQueries(ctx context.Context, ids []uint) (uint, error)
err = svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedMultipleSavedQuery,
&map[string]interface{}{"query_ids": ids},
fleet.ActivityTypeDeletedMultipleSavedQuery{
IDs: ids,
},
)
if err != nil {
return n, err
@ -502,8 +509,9 @@ func (svc *Service) ApplyQuerySpecs(ctx context.Context, specs []*fleet.QuerySpe
return svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeAppliedSpecSavedQuery,
&map[string]interface{}{"specs": specs},
fleet.ActivityTypeAppliedSpecSavedQuery{
Specs: specs,
},
)
}

View file

@ -104,7 +104,7 @@ func TestQueryAuth(t *testing.T) {
}
return &fleet.Query{ID: 8888, AuthorID: ptr.Uint(6666)}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
ds.QueryFunc = func(ctx context.Context, id uint) (*fleet.Query, error) {

View file

@ -56,7 +56,7 @@ func TestStreamCampaignResultsClosesReditOnWSClose(t *testing.T) {
ds.CountHostsInTargetsFunc = func(ctx context.Context, filter fleet.TeamFilter, targets fleet.HostTargets, now time.Time) (fleet.TargetMetrics, error) {
return fleet.TargetMetrics{TotalHosts: 1}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
ds.SessionByKeyFunc = func(ctx context.Context, key string) (*fleet.Session, error) {

View file

@ -48,8 +48,11 @@ func (svc *Service) NewUser(ctx context.Context, p fleet.UserPayload) (*fleet.Us
if err := svc.ds.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeCreatedUser,
&map[string]interface{}{"user_name": user.Name, "user_id": user.ID, "user_email": user.Email},
fleet.ActivityTypeCreatedUser{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
},
); err != nil {
return nil, err
}

View file

@ -192,8 +192,8 @@ func (svc *Service) Login(ctx context.Context, email, password string) (*fleet.U
return nil, nil, fleet.NewAuthFailedError(err.Error())
}
if err := svc.ds.NewActivity(ctx, user, fleet.ActivityTypeUserLoggedIn, &map[string]interface{}{
"public_ip": publicip.FromContext(ctx),
if err := svc.ds.NewActivity(ctx, user, fleet.ActivityTypeUserLoggedIn{
PublicIP: publicip.FromContext(ctx),
}); err != nil {
return nil, nil, err
}
@ -515,9 +515,8 @@ func (svc *Service) LoginSSOUser(ctx context.Context, user *fleet.User, redirect
err = svc.ds.NewActivity(
ctx,
user,
fleet.ActivityTypeUserLoggedIn,
&map[string]interface{}{
"public_ip": publicip.FromContext(ctx),
fleet.ActivityTypeUserLoggedIn{
PublicIP: publicip.FromContext(ctx),
},
)
if err != nil {

View file

@ -81,8 +81,10 @@ func (svc Service) NewTeamPolicy(ctx context.Context, teamID uint, p fleet.Polic
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeCreatedPolicy,
&map[string]interface{}{"policy_id": policy.ID, "policy_name": policy.Name},
fleet.ActivityTypeCreatedPolicy{
ID: policy.ID,
Name: policy.Name,
},
); err != nil {
return nil, err
}
@ -244,8 +246,10 @@ func (svc Service) DeleteTeamPolicies(ctx context.Context, teamID uint, ids []ui
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeDeletedPolicy,
&map[string]interface{}{"policy_id": id, "policy_name": policiesByID[id].Name},
fleet.ActivityTypeDeletedPolicy{
ID: id,
Name: policiesByID[id].Name,
},
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "adding new activity for deleted policy")
}
@ -337,8 +341,10 @@ func (svc *Service) modifyPolicy(ctx context.Context, teamID *uint, id uint, p f
if err := svc.ds.NewActivity(
ctx,
authz.UserFromContext(ctx),
fleet.ActivityTypeEditedPolicy,
&map[string]interface{}{"policy_id": policy.ID, "policy_name": policy.Name},
fleet.ActivityTypeEditedPolicy{
ID: policy.ID,
Name: policy.Name,
},
); err != nil {
return nil, err
}

View file

@ -56,7 +56,7 @@ func TestTeamPoliciesAuth(t *testing.T) {
ds.ApplyPolicySpecsFunc = func(ctx context.Context, authorID uint, specs []*fleet.PolicySpec) error {
return nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
ds.TeamFunc = func(ctx context.Context, tid uint) (*fleet.Team, error) {

View file

@ -25,7 +25,7 @@ func TestTeamAuth(t *testing.T) {
ds.NewTeamFunc = func(ctx context.Context, team *fleet.Team) (*fleet.Team, error) {
return &fleet.Team{}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
ds.TeamFunc = func(ctx context.Context, tid uint) (*fleet.Team, error) {
@ -264,8 +264,9 @@ func TestApplyTeamSpecs(t *testing.T) {
return team, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
require.Len(t, (*details)["teams"], 1)
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
act := activity.(fleet.ActivityTypeAppliedSpecTeam)
require.Len(t, act.Teams, 1)
return nil
}
@ -344,8 +345,9 @@ func TestApplyTeamSpecs(t *testing.T) {
return &fleet.Team{}, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
require.Len(t, (*details)["teams"], 1)
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
act := activity.(fleet.ActivityTypeAppliedSpecTeam)
require.Len(t, act.Teams, 1)
return nil
}

View file

@ -431,8 +431,11 @@ func (svc *Service) DeleteUser(ctx context.Context, id uint) error {
if err := svc.ds.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeDeletedUser,
&map[string]interface{}{"user_name": user.Name, "user_id": user.ID, "user_email": user.Email},
fleet.ActivityTypeDeletedUser{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
},
); err != nil {
return err
}

View file

@ -77,7 +77,7 @@ func TestUserAuth(t *testing.T) {
ds.ListSessionsForUserFunc = func(ctx context.Context, id uint) ([]*fleet.Session, error) {
return nil, nil
}
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activityType string, details *map[string]interface{}) error {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}