2021-09-15 19:27:53 +00:00
package service
import (
"context"
2022-01-13 19:57:44 +00:00
"encoding/base64"
2021-09-15 19:27:53 +00:00
"encoding/json"
"fmt"
2022-09-19 17:53:44 +00:00
"io"
2021-09-15 19:27:53 +00:00
"net/http"
2022-12-09 18:23:08 +00:00
"reflect"
"sort"
2022-01-19 15:52:14 +00:00
"strings"
2021-09-15 19:27:53 +00:00
"testing"
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
"time"
2021-09-15 19:27:53 +00:00
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
"github.com/fleetdm/fleet/v4/server/datastore/mysql"
2022-08-15 17:42:33 +00:00
"github.com/fleetdm/fleet/v4/server/datastore/redis/redistest"
2021-09-15 19:27:53 +00:00
"github.com/fleetdm/fleet/v4/server/fleet"
2022-08-30 11:13:09 +00:00
"github.com/fleetdm/fleet/v4/server/live_query/live_query_mock"
2021-09-15 19:27:53 +00:00
"github.com/fleetdm/fleet/v4/server/ptr"
2022-05-18 17:03:00 +00:00
"github.com/fleetdm/fleet/v4/server/test"
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
2021-09-15 19:27:53 +00:00
"github.com/stretchr/testify/assert"
2022-08-30 11:13:09 +00:00
"github.com/stretchr/testify/mock"
2021-09-15 19:27:53 +00:00
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
func TestIntegrationsEnterprise ( t * testing . T ) {
testingSuite := new ( integrationEnterpriseTestSuite )
testingSuite . s = & testingSuite . Suite
suite . Run ( t , testingSuite )
}
type integrationEnterpriseTestSuite struct {
withServer
suite . Suite
2022-08-15 17:42:33 +00:00
redisPool fleet . RedisPool
2022-08-30 11:13:09 +00:00
lq * live_query_mock . MockLiveQuery
2021-09-15 19:27:53 +00:00
}
func ( s * integrationEnterpriseTestSuite ) SetupSuite ( ) {
s . withDS . SetupSuite ( "integrationEnterpriseTestSuite" )
2022-08-15 17:42:33 +00:00
s . redisPool = redistest . SetupRedis ( s . T ( ) , "integration_enterprise" , false , false , false )
2022-08-30 11:13:09 +00:00
s . lq = live_query_mock . New ( s . T ( ) )
2022-08-15 17:42:33 +00:00
config := TestServerOpts {
License : & fleet . LicenseInfo {
Tier : fleet . TierPremium ,
} ,
Pool : s . redisPool ,
2022-08-30 11:13:09 +00:00
Lq : s . lq ,
2022-08-15 17:42:33 +00:00
}
users , server := RunServerForTestsWithDS ( s . T ( ) , s . ds , & config )
2021-09-15 19:27:53 +00:00
s . server = server
s . users = users
s . token = s . getTestAdminToken ( )
}
2022-08-30 11:13:09 +00:00
func ( s * integrationEnterpriseTestSuite ) TearDownTest ( ) {
// reset the mock
s . lq . Mock = mock . Mock { }
2022-09-21 19:16:31 +00:00
s . withServer . commonTearDownTest ( s . T ( ) )
2022-08-30 11:13:09 +00:00
}
2021-09-15 19:27:53 +00:00
func ( s * integrationEnterpriseTestSuite ) TestTeamSpecs ( ) {
t := s . T ( )
// create a team through the service so it initializes the agent ops
teamName := t . Name ( ) + "team1"
team := & fleet . Team {
Name : teamName ,
Description : "desc team1" ,
}
2022-04-05 15:35:53 +00:00
s . Do ( "POST" , "/api/latest/fleet/teams" , team , http . StatusOK )
2021-09-15 19:27:53 +00:00
2022-08-02 13:51:03 +00:00
// updates a team, no secret is provided so it will keep the one generated
// automatically when the team was created.
2022-09-19 17:53:44 +00:00
agentOpts := json . RawMessage ( ` { "config": { "views": { "foo": "bar"}}, "overrides": { "platforms": { "darwin": { "views": { "bar": "qux"}}}}} ` )
2023-01-24 16:20:02 +00:00
mdm := fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : "10.15.0" ,
Deadline : "2021-01-01" ,
} ,
}
2022-08-31 20:30:46 +00:00
features := json . RawMessage ( ` {
"enable_host_users" : false ,
"enable_software_inventory" : false ,
"additional_queries" : { "foo" : "bar" }
} ` )
2023-01-24 16:20:02 +00:00
teamSpecs := applyTeamSpecsRequest {
Specs : [ ] * fleet . TeamSpec {
{
Name : teamName ,
AgentOptions : agentOpts ,
Features : & features ,
MDM : mdm ,
} ,
} ,
}
2022-04-05 15:35:53 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK )
2021-09-15 19:27:53 +00:00
team , err := s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
2022-08-02 13:51:03 +00:00
assert . Len ( t , team . Secrets , 1 )
2022-03-21 19:16:47 +00:00
require . JSONEq ( t , string ( agentOpts ) , string ( * team . Config . AgentOptions ) )
2022-08-31 20:30:46 +00:00
require . Equal ( t , fleet . Features {
EnableHostUsers : false ,
EnableSoftwareInventory : false ,
AdditionalQueries : ptr . RawMessage ( json . RawMessage ( ` { "foo": "bar"} ` ) ) ,
} , team . Config . Features )
2023-01-24 16:20:02 +00:00
require . Equal ( t , mdm , team . Config . MDM )
2021-09-15 19:27:53 +00:00
2022-08-24 12:32:45 +00:00
// 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 )
2022-12-23 16:05:16 +00:00
assert . Equal ( t , fleet . ActivityTypeAppliedSpecTeam { } . ActivityName ( ) , listActivities . Activities [ 0 ] . Type )
2022-08-24 12:32:45 +00:00
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 ) )
2022-09-19 17:53:44 +00:00
// dry-run with invalid agent options
agentOpts = json . RawMessage ( ` { "config": { "nope": 1}} ` )
2022-11-01 19:22:45 +00:00
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName , AgentOptions : agentOpts } } }
2022-09-19 17:53:44 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusBadRequest , "dry_run" , "true" )
2022-10-24 12:49:44 +00:00
// dry-run with empty body
res := s . DoRaw ( "POST" , "/api/latest/fleet/spec/teams" , nil , http . StatusBadRequest , "force" , "true" )
errBody , err := io . ReadAll ( res . Body )
require . NoError ( t , err )
require . Contains ( t , string ( errBody ) , ` "Expected JSON Body" ` )
// dry-run with invalid top-level key
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , json . RawMessage ( ` {
"specs" : [
{ "name" : "team_name_1" , "unknown_key" : true }
]
} ` ) , http . StatusBadRequest , "dry_run" , "true" )
2022-09-19 17:53:44 +00:00
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
require . Contains ( t , string ( * team . Config . AgentOptions ) , ` "foo": "bar" ` ) // unchanged
// dry-run with valid agent options
agentOpts = json . RawMessage ( ` { "config": { "views": { "foo": "qux"}}} ` )
2022-11-01 19:22:45 +00:00
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName , AgentOptions : agentOpts } } }
2022-09-19 17:53:44 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK , "dry_run" , "true" )
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
require . Contains ( t , string ( * team . Config . AgentOptions ) , ` "foo": "bar" ` ) // unchanged
2022-11-01 19:22:45 +00:00
// apply without agent options specified
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName } } }
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK )
// agent options are unchanged, not cleared
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
require . Contains ( t , string ( * team . Config . AgentOptions ) , ` "foo": "bar" ` ) // unchanged
// apply with agent options specified but null
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName , AgentOptions : json . RawMessage ( ` null ` ) } } }
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK )
// agent options are cleared
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
require . Nil ( t , team . Config . AgentOptions )
2022-09-19 17:53:44 +00:00
// force with invalid agent options
agentOpts = json . RawMessage ( ` { "config": { "foo": "qux"}} ` )
2022-11-01 19:22:45 +00:00
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName , AgentOptions : agentOpts } } }
2022-09-19 17:53:44 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK , "force" , "true" )
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
require . Contains ( t , string ( * team . Config . AgentOptions ) , ` "foo": "qux" ` )
2022-10-24 12:49:44 +00:00
// force create new team with invalid top-level key
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , json . RawMessage ( ` {
"specs" : [
{ "name" : "team_with_invalid_key" , "unknown_key" : true }
]
} ` ) , http . StatusOK , "force" , "true" )
2023-01-24 16:20:02 +00:00
_ , err = s . ds . TeamByName ( context . Background ( ) , "team_with_invalid_key" )
2022-10-24 12:49:44 +00:00
require . NoError ( t , err )
2022-10-03 12:29:41 +00:00
// invalid agent options command-line flag
agentOpts = json . RawMessage ( ` { "command_line_flags": { "nope": 1}} ` )
2022-11-01 19:22:45 +00:00
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName , AgentOptions : agentOpts } } }
2022-10-03 12:29:41 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusBadRequest )
// valid agent options command-line flag
agentOpts = json . RawMessage ( ` { "command_line_flags": { "enable_tables": "abcd"}} ` )
2022-11-01 19:22:45 +00:00
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : teamName , AgentOptions : agentOpts } } }
2022-10-03 12:29:41 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK )
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
require . Contains ( t , string ( * team . Config . AgentOptions ) , ` "enable_tables": "abcd" ` )
2021-09-15 19:27:53 +00:00
// creates a team with default agent options
user , err := s . ds . UserByEmail ( context . Background ( ) , "admin1@example.com" )
require . NoError ( t , err )
teams , err := s . ds . ListTeams ( context . Background ( ) , fleet . TeamFilter { User : user } , fleet . ListOptions { } )
require . NoError ( t , err )
require . True ( t , len ( teams ) >= 1 )
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : "team2" } } }
2022-04-05 15:35:53 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK )
2021-09-15 19:27:53 +00:00
teams , err = s . ds . ListTeams ( context . Background ( ) , fleet . TeamFilter { User : user } , fleet . ListOptions { } )
require . NoError ( t , err )
assert . True ( t , len ( teams ) >= 2 )
team , err = s . ds . TeamByName ( context . Background ( ) , "team2" )
require . NoError ( t , err )
2022-08-30 11:13:09 +00:00
appConfig , err := s . ds . AppConfig ( context . Background ( ) )
require . NoError ( t , err )
2022-04-05 15:35:53 +00:00
defaultOpts := ` { "config": { "options": { "logger_plugin": "tls", "pack_delimiter": "/", "logger_tls_period": 10, "distributed_plugin": "tls", "disable_distributed": false, "logger_tls_endpoint": "/api/osquery/log", "distributed_interval": 10, "distributed_tls_max_attempts": 3}, "decorators": { "load": ["SELECT uuid AS host_uuid FROM system_info;", "SELECT hostname AS hostname FROM system_info;"]}}, "overrides": { }} `
2022-08-02 13:51:03 +00:00
assert . Len ( t , team . Secrets , 0 ) // no secret gets created automatically when creating a team via apply spec
2022-03-21 19:16:47 +00:00
require . NotNil ( t , team . Config . AgentOptions )
require . JSONEq ( t , defaultOpts , string ( * team . Config . AgentOptions ) )
2022-08-30 11:13:09 +00:00
require . Equal ( t , appConfig . Features , team . Config . Features )
2021-09-15 19:27:53 +00:00
2022-08-24 12:32:45 +00:00
// 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 )
2022-12-23 16:05:16 +00:00
assert . Equal ( t , fleet . ActivityTypeAppliedSpecTeam { } . ActivityName ( ) , listActivities . Activities [ 0 ] . Type )
2022-08-24 12:32:45 +00:00
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 ) )
2022-08-31 20:30:46 +00:00
// updates
teamSpecs = applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { {
Name : "team2" ,
Secrets : [ ] fleet . EnrollSecret { { Secret : "ABC" } } ,
Features : nil ,
} } }
2022-04-05 15:35:53 +00:00
s . Do ( "POST" , "/api/latest/fleet/spec/teams" , teamSpecs , http . StatusOK )
2021-09-15 19:27:53 +00:00
team , err = s . ds . TeamByName ( context . Background ( ) , "team2" )
require . NoError ( t , err )
require . Len ( t , team . Secrets , 1 )
assert . Equal ( t , "ABC" , team . Secrets [ 0 ] . Secret )
}
func ( s * integrationEnterpriseTestSuite ) TestTeamSchedule ( ) {
t := s . T ( )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
ts := getTeamScheduleResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-15 19:27:53 +00:00
require . Len ( t , ts . Scheduled , 0 )
2021-09-20 14:00:57 +00:00
qr , err := s . ds . NewQuery (
context . Background ( ) ,
& fleet . Query { Name : "TestQueryTeamPolicy" , Description : "Some description" , Query : "select * from osquery;" , ObserverCanRun : true } ,
)
2021-09-15 19:27:53 +00:00
require . NoError ( t , err )
gsParams := teamScheduleQueryRequest { ScheduledQueryPayload : fleet . ScheduledQueryPayload { QueryID : & qr . ID , Interval : ptr . Uint ( 42 ) } }
r := teamScheduleQueryResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule" , team1 . ID ) , gsParams , http . StatusOK , & r )
2021-09-15 19:27:53 +00:00
ts = getTeamScheduleResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-15 19:27:53 +00:00
require . Len ( t , ts . Scheduled , 1 )
assert . Equal ( t , uint ( 42 ) , ts . Scheduled [ 0 ] . Interval )
2021-09-20 14:00:57 +00:00
assert . Equal ( t , "TestQueryTeamPolicy" , ts . Scheduled [ 0 ] . Name )
2021-09-15 19:27:53 +00:00
assert . Equal ( t , qr . ID , ts . Scheduled [ 0 ] . QueryID )
id := ts . Scheduled [ 0 ] . ID
modifyResp := modifyTeamScheduleResponse { }
modifyParams := modifyTeamScheduleRequest { ScheduledQueryPayload : fleet . ScheduledQueryPayload { Interval : ptr . Uint ( 55 ) } }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule/%d" , team1 . ID , id ) , modifyParams , http . StatusOK , & modifyResp )
2021-09-15 19:27:53 +00:00
// just to satisfy my paranoia, wanted to make sure the contents of the json would work
2022-04-05 15:35:53 +00:00
s . DoRaw ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule/%d" , team1 . ID , id ) , [ ] byte ( ` { "interval": 77} ` ) , http . StatusOK )
2021-09-15 19:27:53 +00:00
ts = getTeamScheduleResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-15 19:27:53 +00:00
require . Len ( t , ts . Scheduled , 1 )
assert . Equal ( t , uint ( 77 ) , ts . Scheduled [ 0 ] . Interval )
deleteResp := deleteTeamScheduleResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule/%d" , team1 . ID , id ) , nil , http . StatusOK , & deleteResp )
2021-09-15 19:27:53 +00:00
ts = getTeamScheduleResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/schedule" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-15 19:27:53 +00:00
require . Len ( t , ts . Scheduled , 0 )
}
2021-09-20 14:00:57 +00:00
func ( s * integrationEnterpriseTestSuite ) TestTeamPolicies ( ) {
t := s . T ( )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1" + t . Name ( ) ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
oldToken := s . token
t . Cleanup ( func ( ) {
s . token = oldToken
} )
2022-05-18 17:03:00 +00:00
password := test . GoodPassword
2021-09-20 14:00:57 +00:00
email := "testteam@user.com"
u := & fleet . User {
Name : "test team user" ,
Email : email ,
GlobalRole : nil ,
Teams : [ ] fleet . UserTeam {
{
Team : * team1 ,
Role : fleet . RoleMaintainer ,
} ,
} ,
}
require . NoError ( t , u . SetPassword ( password , 10 , 10 ) )
_ , err = s . ds . NewUser ( context . Background ( ) , u )
require . NoError ( t , err )
s . token = s . getTestToken ( email , password )
ts := listTeamPoliciesResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-20 14:00:57 +00:00
require . Len ( t , ts . Policies , 0 )
2022-10-12 12:35:36 +00:00
require . Len ( t , ts . InheritedPolicies , 0 )
// create a global policy
gpol , err := s . ds . NewGlobalPolicy ( context . Background ( ) , nil , fleet . PolicyPayload { Name : "TestGlobalPolicy" , Query : "SELECT 1" } )
require . NoError ( t , err )
defer func ( ) {
_ , err := s . ds . DeleteGlobalPolicies ( context . Background ( ) , [ ] uint { gpol . ID } )
require . NoError ( t , err )
} ( )
2021-09-20 14:00:57 +00:00
qr , err := s . ds . NewQuery ( context . Background ( ) , & fleet . Query { Name : "TestQuery2" , Description : "Some description" , Query : "select * from osquery;" , ObserverCanRun : true } )
require . NoError ( t , err )
2021-11-24 17:16:42 +00:00
tpParams := teamPolicyRequest {
QueryID : & qr . ID ,
Resolution : "some team resolution" ,
}
2021-09-20 14:00:57 +00:00
r := teamPolicyResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , tpParams , http . StatusOK , & r )
2021-09-20 14:00:57 +00:00
ts = listTeamPoliciesResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-20 14:00:57 +00:00
require . Len ( t , ts . Policies , 1 )
2021-11-24 17:16:42 +00:00
assert . Equal ( t , "TestQuery2" , ts . Policies [ 0 ] . Name )
assert . Equal ( t , "select * from osquery;" , ts . Policies [ 0 ] . Query )
assert . Equal ( t , "Some description" , ts . Policies [ 0 ] . Description )
2021-10-28 13:10:03 +00:00
require . NotNil ( t , ts . Policies [ 0 ] . Resolution )
assert . Equal ( t , "some team resolution" , * ts . Policies [ 0 ] . Resolution )
2022-10-12 12:35:36 +00:00
require . Len ( t , ts . InheritedPolicies , 1 )
assert . Equal ( t , gpol . Name , ts . InheritedPolicies [ 0 ] . Name )
assert . Equal ( t , gpol . ID , ts . InheritedPolicies [ 0 ] . ID )
2021-09-20 14:00:57 +00:00
deletePolicyParams := deleteTeamPoliciesRequest { IDs : [ ] uint { ts . Policies [ 0 ] . ID } }
deletePolicyResp := deleteTeamPoliciesResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies/delete" , team1 . ID ) , deletePolicyParams , http . StatusOK , & deletePolicyResp )
2021-09-20 14:00:57 +00:00
ts = listTeamPoliciesResponse { }
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , & ts )
2021-09-20 14:00:57 +00:00
require . Len ( t , ts . Policies , 0 )
}
2021-11-11 16:45:39 +00:00
func ( s * integrationEnterpriseTestSuite ) TestModifyTeamEnrollSecrets ( ) {
t := s . T ( )
// Create new team and set initial secret
teamName := t . Name ( ) + "secretTeam"
team := & fleet . Team {
Name : teamName ,
Description : "secretTeam description" ,
Secrets : [ ] * fleet . EnrollSecret { { Secret : "initialSecret" } } ,
}
2022-04-05 15:35:53 +00:00
s . Do ( "POST" , "/api/latest/fleet/teams" , team , http . StatusOK )
2021-11-11 16:45:39 +00:00
team , err := s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
assert . Equal ( t , team . Secrets [ 0 ] . Secret , "initialSecret" )
// Test replace existing secrets
req := json . RawMessage ( ` { "secrets": [ { "secret": "testSecret1"}, { "secret": "testSecret2"}]} ` )
var resp teamEnrollSecretsResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , team . ID ) , req , http . StatusOK , & resp )
2021-11-11 16:45:39 +00:00
require . Len ( t , resp . Secrets , 2 )
team , err = s . ds . TeamByName ( context . Background ( ) , teamName )
require . NoError ( t , err )
assert . Equal ( t , "testSecret1" , team . Secrets [ 0 ] . Secret )
assert . Equal ( t , "testSecret2" , team . Secrets [ 1 ] . Secret )
2021-11-17 16:26:24 +00:00
// Test delete all enroll secrets
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , team . ID ) , json . RawMessage ( ` { "secrets": []} ` ) , http . StatusOK , & resp )
2021-11-17 16:26:24 +00:00
require . Len ( t , resp . Secrets , 0 )
// Test bad requests
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , team . ID ) , json . RawMessage ( ` { "foo": [ { "secret": "testSecret3"}]} ` ) , http . StatusUnprocessableEntity , & resp )
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , team . ID ) , json . RawMessage ( ` { } ` ) , http . StatusUnprocessableEntity , & resp )
2022-10-05 12:35:36 +00:00
// too many secrets
secrets := createEnrollSecrets ( t , fleet . MaxEnrollSecretsCount + 1 )
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , team . ID ) , json . RawMessage ( ` { "secrets": ` + string ( jsonMustMarshal ( t , secrets ) ) + ` } ` ) , http . StatusUnprocessableEntity , & resp )
2021-11-11 16:45:39 +00:00
}
2022-01-13 19:57:44 +00:00
func ( s * integrationEnterpriseTestSuite ) TestAvailableTeams ( ) {
t := s . T ( )
// create a new team
team := & fleet . Team {
Name : "Available Team" ,
Description : "Available Team description" ,
}
2022-04-05 15:35:53 +00:00
s . Do ( "POST" , "/api/latest/fleet/teams" , team , http . StatusOK )
2022-01-13 19:57:44 +00:00
team , err := s . ds . TeamByName ( context . Background ( ) , "Available Team" )
require . NoError ( t , err )
// create a new user
user := & fleet . User {
Name : "Available Teams User" ,
Email : "available@example.com" ,
GlobalRole : ptr . String ( "observer" ) ,
}
2022-05-18 17:03:00 +00:00
err = user . SetPassword ( test . GoodPassword , 10 , 10 )
2022-01-13 19:57:44 +00:00
require . Nil ( t , err )
user , err = s . ds . NewUser ( context . Background ( ) , user )
require . Nil ( t , err )
// test available teams for user assigned to global role
var getResp getUserResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/users/%d" , user . ID ) , nil , http . StatusOK , & getResp )
2022-01-13 19:57:44 +00:00
assert . Equal ( t , user . ID , getResp . User . ID )
assert . Equal ( t , ptr . String ( "observer" ) , getResp . User . GlobalRole )
assert . Len ( t , getResp . User . Teams , 0 ) // teams is empty if user has a global role
assert . Len ( t , getResp . AvailableTeams , 1 ) // available teams includes all teams if user has a global role
assert . Equal ( t , getResp . AvailableTeams [ 0 ] . Name , "Available Team" )
// assign user to a team
user . GlobalRole = nil
user . Teams = [ ] fleet . UserTeam { { Team : * team , Role : "maintainer" } }
err = s . ds . SaveUser ( context . Background ( ) , user )
require . NoError ( t , err )
// test available teams for user assigned to team role
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/users/%d" , user . ID ) , nil , http . StatusOK , & getResp )
2022-01-13 19:57:44 +00:00
assert . Equal ( t , user . ID , getResp . User . ID )
assert . Nil ( t , getResp . User . GlobalRole )
assert . Len ( t , getResp . User . Teams , 1 )
assert . Equal ( t , getResp . User . Teams [ 0 ] . Name , "Available Team" )
assert . Len ( t , getResp . AvailableTeams , 1 )
assert . Equal ( t , getResp . AvailableTeams [ 0 ] . Name , "Available Team" )
// test available teams returned by `/me` endpoint
key := make ( [ ] byte , 64 )
sessionKey := base64 . StdEncoding . EncodeToString ( key )
Add read replica testing helpers and fix non-sso login bug (#4908)
not set on the INSERT.
- OUT: Only sets the ID on the passed session and returns it. (`CreatedAt`, `AccessedAt`, are not set.)
New version:
```go
func (ds *Datastore) NewSession(ctx context.Context, userID uint, sessionKey string) (*fleet.Session, error) {
sqlStatement := `
INSERT INTO sessions (
user_id,
` + "`key`" + `
)
VALUES(?,?)
`
result, err := ds.writer.ExecContext(ctx, sqlStatement, userID, sessionKey)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "inserting session")
}
id, _ := result.LastInsertId() // cannot fail with the mysql driver
return ds.sessionByID(ctx, ds.writer, uint(id))
}
```
- IN: Define arguments that are truly used when creating a session.
- OUT: Load and return the fleet.Session struct with all values set (using the `ds.writer` to support read replicas correctly).
PS: The new `NewSession` version mimics what we already do with other entities, like policies (`Datastore.NewGlobalPolicy`).
2022-04-04 23:52:05 +00:00
_ , err = s . ds . NewSession ( context . Background ( ) , user . ID , sessionKey )
2022-01-13 19:57:44 +00:00
require . NoError ( t , err )
2022-04-05 15:35:53 +00:00
resp := s . DoRawWithHeaders ( "GET" , "/api/latest/fleet/me" , [ ] byte ( "" ) , http . StatusOK , map [ string ] string {
2022-01-13 19:57:44 +00:00
"Authorization" : fmt . Sprintf ( "Bearer %s" , sessionKey ) ,
} )
err = json . NewDecoder ( resp . Body ) . Decode ( & getResp )
require . NoError ( t , err )
assert . Equal ( t , user . ID , getResp . User . ID )
assert . Nil ( t , getResp . User . GlobalRole )
assert . Len ( t , getResp . User . Teams , 1 )
assert . Equal ( t , getResp . User . Teams [ 0 ] . Name , "Available Team" )
assert . Len ( t , getResp . AvailableTeams , 1 )
assert . Equal ( t , getResp . AvailableTeams [ 0 ] . Name , "Available Team" )
}
2022-01-19 15:52:14 +00:00
func ( s * integrationEnterpriseTestSuite ) TestTeamEndpoints ( ) {
t := s . T ( )
name := strings . ReplaceAll ( t . Name ( ) , "/" , "_" )
// create a new team
team := & fleet . Team {
Name : name ,
Description : "Team description" ,
Secrets : [ ] * fleet . EnrollSecret { { Secret : "DEF" } } ,
}
var tmResp teamResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "POST" , "/api/latest/fleet/teams" , team , http . StatusOK , & tmResp )
2022-01-19 15:52:14 +00:00
assert . Equal ( t , team . Name , tmResp . Team . Name )
require . Len ( t , tmResp . Team . Secrets , 1 )
assert . Equal ( t , "DEF" , tmResp . Team . Secrets [ 0 ] . Secret )
// create a duplicate team (same name)
team2 := & fleet . Team {
Name : name ,
Description : "Team2 description" ,
Secrets : [ ] * fleet . EnrollSecret { { Secret : "GHI" } } ,
}
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "POST" , "/api/latest/fleet/teams" , team2 , http . StatusConflict , & tmResp )
2022-01-19 15:52:14 +00:00
2022-10-05 12:35:36 +00:00
// create a team with too many secrets
team3 := & fleet . Team {
Name : name + "lots_of_secrets" ,
Description : "Team3 description" ,
Secrets : createEnrollSecrets ( t , fleet . MaxEnrollSecretsCount + 1 ) ,
}
tmResp . Team = nil
s . DoJSON ( "POST" , "/api/latest/fleet/teams" , team3 , http . StatusUnprocessableEntity , & tmResp )
2022-01-19 15:52:14 +00:00
// list teams
var listResp listTeamsResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , "/api/latest/fleet/teams" , nil , http . StatusOK , & listResp , "query" , name , "per_page" , "2" )
2022-01-19 15:52:14 +00:00
require . Len ( t , listResp . Teams , 1 )
2022-02-04 17:33:22 +00:00
assert . Equal ( t , team . Name , listResp . Teams [ 0 ] . Name )
2022-06-06 14:41:51 +00:00
assert . NotNil ( t , listResp . Teams [ 0 ] . Config . AgentOptions )
2022-01-19 15:52:14 +00:00
tm1ID := listResp . Teams [ 0 ] . ID
2022-02-04 17:33:22 +00:00
// get team
var getResp getTeamResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID ) , nil , http . StatusOK , & getResp )
2022-02-04 17:33:22 +00:00
assert . Equal ( t , team . Name , getResp . Team . Name )
2022-06-06 14:41:51 +00:00
assert . NotNil ( t , getResp . Team . Config . AgentOptions )
2022-02-04 17:33:22 +00:00
2022-01-19 15:52:14 +00:00
// modify team
team . Description = "Alt " + team . Description
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID ) , team , http . StatusOK , & tmResp )
2022-01-19 15:52:14 +00:00
assert . Contains ( t , tmResp . Team . Description , "Alt " )
2022-10-14 22:27:37 +00:00
// modify a team with a NULL config
defaultFeatures := fleet . Features { }
defaultFeatures . ApplyDefaultsForNewInstalls ( )
mysql . ExecAdhocSQL ( t , s . ds , func ( db sqlx . ExtContext ) error {
_ , err := db . ExecContext ( context . Background ( ) , ` UPDATE teams SET config = NULL WHERE id = ? ` , team . ID )
return err
} )
tmResp . Team = nil
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID ) , team , http . StatusOK , & tmResp )
assert . Equal ( t , defaultFeatures , tmResp . Team . Config . Features )
// modify a team with an empty config
mysql . ExecAdhocSQL ( t , s . ds , func ( db sqlx . ExtContext ) error {
_ , err := db . ExecContext ( context . Background ( ) , ` UPDATE teams SET config = ' { }' WHERE id = ? ` , team . ID )
return err
} )
tmResp . Team = nil
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID ) , team , http . StatusOK , & tmResp )
assert . Equal ( t , defaultFeatures , tmResp . Team . Config . Features )
2022-01-19 15:52:14 +00:00
// modify non-existing team
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID + 1 ) , team , http . StatusNotFound , & tmResp )
2022-01-19 15:52:14 +00:00
// list team users
var usersResp listUsersResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , nil , http . StatusOK , & usersResp )
2022-01-19 15:52:14 +00:00
assert . Len ( t , usersResp . Users , 0 )
// list team users - non-existing team
usersResp . Users = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID + 1 ) , nil , http . StatusNotFound , & usersResp )
2022-01-19 15:52:14 +00:00
// create a new user
user := & fleet . User {
Name : "Team User" ,
Email : "user@example.com" ,
GlobalRole : ptr . String ( "observer" ) ,
}
2022-05-18 17:03:00 +00:00
require . NoError ( t , user . SetPassword ( test . GoodPassword , 10 , 10 ) )
2022-01-19 15:52:14 +00:00
user , err := s . ds . NewUser ( context . Background ( ) , user )
require . NoError ( t , err )
// add a team user
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : * user , Role : fleet . RoleObserver } } } , http . StatusOK , & tmResp )
2022-01-19 15:52:14 +00:00
require . Len ( t , tmResp . Team . Users , 1 )
assert . Equal ( t , user . ID , tmResp . Team . Users [ 0 ] . ID )
// add a team user - non-existing team
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID + 1 ) , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : * user , Role : fleet . RoleObserver } } } , http . StatusNotFound , & tmResp )
2022-01-19 15:52:14 +00:00
// add a team user - invalid user role
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : * user , Role : "foobar" } } } , http . StatusUnprocessableEntity , & tmResp )
2022-01-19 15:52:14 +00:00
// search for that user
usersResp . Users = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , nil , http . StatusOK , & usersResp , "query" , "user" )
2022-01-19 15:52:14 +00:00
require . Len ( t , usersResp . Users , 1 )
assert . Equal ( t , user . ID , usersResp . Users [ 0 ] . ID )
// search for unknown user
usersResp . Users = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , nil , http . StatusOK , & usersResp , "query" , "notauser" )
2022-01-19 15:52:14 +00:00
require . Len ( t , usersResp . Users , 0 )
// delete team user
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : fleet . User { ID : user . ID } } } } , http . StatusOK , & tmResp )
2022-01-19 15:52:14 +00:00
require . Len ( t , tmResp . Team . Users , 0 )
// delete team user - unknown user
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID ) , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : fleet . User { ID : user . ID + 1 } } } } , http . StatusOK , & tmResp )
2022-01-19 15:52:14 +00:00
require . Len ( t , tmResp . Team . Users , 0 )
// delete team user - unknown team
tmResp . Team = nil
2022-04-05 15:35:53 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/users" , tm1ID + 1 ) , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : fleet . User { ID : user . ID } } } } , http . StatusNotFound , & tmResp )
2022-01-19 15:52:14 +00:00
2022-09-19 17:53:44 +00:00
// modify team agent options with invalid options
tmResp . Team = nil
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/agent_options" , tm1ID ) , json . RawMessage ( ` {
"x" : "y"
} ` ) , http . StatusBadRequest , & tmResp )
// modify team agent options with invalid options, but force-apply them
tmResp . Team = nil
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/agent_options" , tm1ID ) , json . RawMessage ( ` {
"config" : {
"x" : "y"
}
} ` ) , http . StatusOK , & tmResp , "force" , "true" )
require . Contains ( t , string ( * tmResp . Team . Config . AgentOptions ) , ` "x": "y" ` )
// modify team agent options with valid options
tmResp . Team = nil
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/agent_options" , tm1ID ) , json . RawMessage ( ` {
"config" : {
"options" : {
"aws_debug" : true
}
}
} ` ) , http . StatusOK , & tmResp )
require . Contains ( t , string ( * tmResp . Team . Config . AgentOptions ) , ` "aws_debug": true ` )
// modify team agent using invalid options with dry-run
2022-01-19 15:52:14 +00:00
tmResp . Team = nil
2022-09-19 17:53:44 +00:00
resp := s . DoRaw ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/agent_options" , tm1ID ) , json . RawMessage ( ` {
"config" : {
"options" : {
"aws_debug" : "not-a-bool"
}
}
} ` ) , http . StatusBadRequest , "dry_run" , "true" )
body , err := io . ReadAll ( resp . Body )
require . NoError ( t , err )
2022-10-12 21:10:50 +00:00
require . Contains ( t , string ( body ) , "invalid value type at 'options.aws_debug': expected bool but got string" )
2022-09-19 17:53:44 +00:00
// modify team agent using valid options with dry-run
tmResp . Team = nil
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/agent_options" , tm1ID ) , json . RawMessage ( ` {
"config" : {
"options" : {
"aws_debug" : false
}
}
} ` ) , http . StatusOK , & tmResp , "dry_run" , "true" )
require . Contains ( t , string ( * tmResp . Team . Config . AgentOptions ) , ` "aws_debug": true ` ) // left unchanged
2022-01-19 15:52:14 +00:00
2022-08-24 12:32:45 +00:00
// 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 )
2022-12-23 16:05:16 +00:00
assert . Equal ( t , fleet . ActivityTypeEditedAgentOptions { } . ActivityName ( ) , listActivities . Activities [ 0 ] . Type )
2022-08-24 12:32:45 +00:00
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 ) )
2022-01-19 15:52:14 +00:00
// modify team agent options - unknown team
tmResp . Team = nil
2022-09-19 17:53:44 +00:00
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/agent_options" , tm1ID + 1 ) , json . RawMessage ( ` { } ` ) , http . StatusNotFound , & tmResp )
2022-01-19 15:52:14 +00:00
// get team enroll secrets
var secResp teamEnrollSecretsResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , tm1ID ) , nil , http . StatusOK , & secResp )
2022-01-19 15:52:14 +00:00
require . Len ( t , secResp . Secrets , 1 )
assert . Equal ( t , team . Secrets [ 0 ] . Secret , secResp . Secrets [ 0 ] . Secret )
// get team enroll secrets- unknown team: does not return 404 because reads directly
// the secrets table, does not load the team first (which would be unnecessary except
// for checking that it exists)
2022-04-05 15:35:53 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/secrets" , tm1ID + 1 ) , nil , http . StatusOK , & secResp )
2022-01-19 15:52:14 +00:00
assert . Len ( t , secResp . Secrets , 0 )
// delete team
var delResp deleteTeamResponse
2022-04-05 15:35:53 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID ) , nil , http . StatusOK , & delResp )
2022-01-19 15:52:14 +00:00
// delete team again, now an unknown team
2022-04-05 15:35:53 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , tm1ID ) , nil , http . StatusNotFound , & delResp )
2022-01-19 15:52:14 +00:00
}
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
2022-06-06 14:41:51 +00:00
func ( s * integrationEnterpriseTestSuite ) TestExternalIntegrationsTeamConfig ( ) {
t := s . T ( )
// create a test http server to act as the Jira and Zendesk server
srvURL := startExternalServiceWebServer ( t )
// create a new team
team := & fleet . Team {
Name : t . Name ( ) ,
Description : "Team description" ,
Secrets : [ ] * fleet . EnrollSecret { { Secret : "XYZ" } } ,
}
var tmResp teamResponse
s . DoJSON ( "POST" , "/api/latest/fleet/teams" , team , http . StatusOK , & tmResp )
require . Equal ( t , team . Name , tmResp . Team . Name )
require . Len ( t , tmResp . Team . Secrets , 1 )
require . Equal ( t , "XYZ" , tmResp . Team . Secrets [ 0 ] . Secret )
team . ID = tmResp . Team . ID
// modify the team's config - enable the webhook
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : true ,
DestinationURL : "http://example.com" ,
} ,
} } , http . StatusOK , & tmResp )
require . True ( t , tmResp . Team . Config . WebhookSettings . FailingPoliciesWebhook . Enable )
require . Equal ( t , "http://example.com" , tmResp . Team . Config . WebhookSettings . FailingPoliciesWebhook . DestinationURL )
2022-06-13 14:04:47 +00:00
// add an unknown automation - does not exist at the global level
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : false ,
} ,
} ,
} } , http . StatusUnprocessableEntity , & tmResp )
// add a couple Jira integrations at the global level (qux and qux2)
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( fmt . Sprintf ( ` {
"integrations" : {
"jira" : [
{
"url" : % q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux"
} ,
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux2"
}
]
}
} ` , srvURL ) ) , http . StatusOK )
2022-06-06 14:41:51 +00:00
// enable an automation - should fail as the webhook is enabled too
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
} ,
} ,
} } , http . StatusUnprocessableEntity , & tmResp )
// get the team, no integration was saved
var getResp getTeamResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , nil , http . StatusOK , & getResp )
require . Len ( t , getResp . Team . Config . Integrations . Jira , 0 )
require . Len ( t , getResp . Team . Config . Integrations . Zendesk , 0 )
2022-06-13 14:04:47 +00:00
// disable the webhook and enable the automation
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
} ,
} ,
} ,
WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : false ,
DestinationURL : "http://example.com" ,
} ,
} ,
} , http . StatusOK , & tmResp )
require . Len ( t , tmResp . Team . Config . Integrations . Jira , 1 )
2022-06-13 14:04:47 +00:00
require . Equal ( t , "qux" , tmResp . Team . Config . Integrations . Jira [ 0 ] . ProjectKey )
2022-06-06 14:41:51 +00:00
// enable the webhook without changing the integration should fail (an integration is already enabled)
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : true ,
DestinationURL : "http://example.com" ,
} ,
} } , http . StatusUnprocessableEntity , & tmResp )
// add a second, disabled Jira integration
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
ProjectKey : "qux2" ,
EnableFailingPolicies : false ,
} ,
} ,
} ,
} , http . StatusOK , & tmResp )
require . Len ( t , tmResp . Team . Config . Integrations . Jira , 2 )
2022-06-13 14:04:47 +00:00
require . Equal ( t , "qux" , tmResp . Team . Config . Integrations . Jira [ 0 ] . ProjectKey )
require . Equal ( t , "qux2" , tmResp . Team . Config . Integrations . Jira [ 1 ] . ProjectKey )
2022-06-06 14:41:51 +00:00
// enabling the second without disabling the first fails
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
ProjectKey : "qux2" ,
EnableFailingPolicies : true ,
} ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
2022-06-13 14:04:47 +00:00
// updating to use the same project key fails (must be unique)
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : false ,
} ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
// remove second integration, disable first so that nothing is enabled now
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : false ,
} ,
} ,
} ,
} , http . StatusOK , & tmResp )
// enable the webhook now works
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : true ,
DestinationURL : "http://example.com" ,
} ,
} } , http . StatusOK , & tmResp )
// set environmental varible to use Zendesk test client
2022-07-18 17:22:28 +00:00
t . Setenv ( "TEST_ZENDESK_CLIENT" , "true" )
2022-06-06 14:41:51 +00:00
2022-06-13 14:04:47 +00:00
// add an unknown automation - does not exist at the global level
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : false ,
} ,
} ,
} } , http . StatusUnprocessableEntity , & tmResp )
// add a couple Zendesk integrations at the global level (122 and 123), keep the jira ones too
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( fmt . Sprintf ( ` {
"integrations" : {
"zendesk" : [
{
"url" : % q ,
"email" : "a@b.c" ,
"api_token" : "ok" ,
"group_id" : 122
} ,
{
"url" : % [ 1 ] q ,
"email" : "b@b.c" ,
"api_token" : "ok" ,
"group_id" : 123
}
] ,
"jira" : [
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux"
} ,
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux2"
}
]
}
} ` , srvURL ) ) , http . StatusOK )
2022-06-06 14:41:51 +00:00
// enable a Zendesk automation - should fail as the webhook is enabled too
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : true ,
} ,
} ,
} } , http . StatusUnprocessableEntity , & tmResp )
2022-06-13 14:04:47 +00:00
// disable the webhook and enable the automation
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : true ,
} ,
} ,
} ,
WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : false ,
DestinationURL : "http://example.com" ,
} ,
} ,
} , http . StatusOK , & tmResp )
require . Len ( t , tmResp . Team . Config . Integrations . Zendesk , 1 )
2022-06-13 14:04:47 +00:00
require . Equal ( t , int64 ( 122 ) , tmResp . Team . Config . Integrations . Zendesk [ 0 ] . GroupID )
2022-06-06 14:41:51 +00:00
// enable the webhook without changing the integration should fail (an integration is already enabled)
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : true ,
DestinationURL : "http://example.com" ,
} ,
} } , http . StatusUnprocessableEntity , & tmResp )
// add a second, disabled Zendesk integration
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
GroupID : 123 ,
EnableFailingPolicies : false ,
} ,
} ,
} ,
} , http . StatusOK , & tmResp )
require . Len ( t , tmResp . Team . Config . Integrations . Zendesk , 2 )
2022-06-13 14:04:47 +00:00
require . Equal ( t , int64 ( 122 ) , tmResp . Team . Config . Integrations . Zendesk [ 0 ] . GroupID )
require . Equal ( t , int64 ( 123 ) , tmResp . Team . Config . Integrations . Zendesk [ 1 ] . GroupID )
2022-06-06 14:41:51 +00:00
// enabling the second without disabling the first fails
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
GroupID : 123 ,
EnableFailingPolicies : true ,
} ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
2022-06-13 14:04:47 +00:00
// updating to use the same group ID fails (must be unique per group ID)
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
2022-06-13 14:04:47 +00:00
GroupID : 123 ,
2022-06-06 14:41:51 +00:00
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
GroupID : 123 ,
EnableFailingPolicies : false ,
} ,
} ,
} ,
2022-06-13 14:04:47 +00:00
} , http . StatusUnprocessableEntity , & tmResp )
2022-06-06 14:41:51 +00:00
2022-06-13 14:04:47 +00:00
// remove second Zendesk integration, add disabled Jira integration
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : true ,
} ,
2022-06-13 14:04:47 +00:00
} ,
Jira : [ ] * fleet . TeamJiraIntegration {
2022-06-06 14:41:51 +00:00
{
URL : srvURL ,
2022-06-13 14:04:47 +00:00
ProjectKey : "qux" ,
2022-06-06 14:41:51 +00:00
EnableFailingPolicies : false ,
} ,
} ,
} ,
2022-06-13 14:04:47 +00:00
} , http . StatusOK , & tmResp )
require . Len ( t , tmResp . Team . Config . Integrations . Jira , 1 )
require . Equal ( t , "qux" , tmResp . Team . Config . Integrations . Jira [ 0 ] . ProjectKey )
require . Len ( t , tmResp . Team . Config . Integrations . Zendesk , 1 )
require . Equal ( t , int64 ( 122 ) , tmResp . Team . Config . Integrations . Zendesk [ 0 ] . GroupID )
2022-06-06 14:41:51 +00:00
2022-06-13 14:04:47 +00:00
// enabling a Jira integration when a Zendesk one is enabled fails
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
2022-06-13 14:04:47 +00:00
GroupID : 122 ,
2022-06-06 14:41:51 +00:00
EnableFailingPolicies : true ,
} ,
2022-06-13 14:04:47 +00:00
} ,
Jira : [ ] * fleet . TeamJiraIntegration {
2022-06-06 14:41:51 +00:00
{
URL : srvURL ,
2022-06-13 14:04:47 +00:00
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
2022-06-06 14:41:51 +00:00
} ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
2022-06-13 14:04:47 +00:00
// set additional integrations on the team
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
GroupID : 122 ,
EnableFailingPolicies : true ,
} ,
{
URL : srvURL ,
2022-06-13 14:04:47 +00:00
GroupID : 123 ,
2022-06-06 14:41:51 +00:00
EnableFailingPolicies : false ,
} ,
} ,
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : false ,
} ,
} ,
} ,
} , http . StatusOK , & tmResp )
2022-06-13 14:04:47 +00:00
// removing Zendesk 122 from the global config removes it from the team too
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( fmt . Sprintf ( ` {
"integrations" : {
"zendesk" : [
{
"url" : % [ 1 ] q ,
"email" : "b@b.c" ,
"api_token" : "ok" ,
"group_id" : 123
}
] ,
"jira" : [
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux"
} ,
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux2"
}
]
}
} ` , srvURL ) ) , http . StatusOK )
// get the team, only one Zendesk integration remains, none are enabled
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , nil , http . StatusOK , & getResp )
require . Len ( t , getResp . Team . Config . Integrations . Jira , 1 )
require . Equal ( t , "qux" , getResp . Team . Config . Integrations . Jira [ 0 ] . ProjectKey )
require . False ( t , getResp . Team . Config . Integrations . Jira [ 0 ] . EnableFailingPolicies )
require . Len ( t , getResp . Team . Config . Integrations . Zendesk , 1 )
require . Equal ( t , int64 ( 123 ) , getResp . Team . Config . Integrations . Zendesk [ 0 ] . GroupID )
require . False ( t , getResp . Team . Config . Integrations . Zendesk [ 0 ] . EnableFailingPolicies )
// removing Jira qux2 from the global config does not impact the team as it is unused.
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( fmt . Sprintf ( ` {
"integrations" : {
"zendesk" : [
{
"url" : % [ 1 ] q ,
"email" : "b@b.c" ,
"api_token" : "ok" ,
"group_id" : 123
}
] ,
"jira" : [
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux"
}
]
}
} ` , srvURL ) ) , http . StatusOK )
// get the team, integrations are unchanged
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , nil , http . StatusOK , & getResp )
require . Len ( t , getResp . Team . Config . Integrations . Jira , 1 )
require . Equal ( t , "qux" , getResp . Team . Config . Integrations . Jira [ 0 ] . ProjectKey )
require . False ( t , getResp . Team . Config . Integrations . Jira [ 0 ] . EnableFailingPolicies )
require . Len ( t , getResp . Team . Config . Integrations . Zendesk , 1 )
require . Equal ( t , int64 ( 123 ) , getResp . Team . Config . Integrations . Zendesk [ 0 ] . GroupID )
require . False ( t , getResp . Team . Config . Integrations . Zendesk [ 0 ] . EnableFailingPolicies )
// enable Jira qux for the team
2022-06-06 14:41:51 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration {
{
URL : srvURL ,
2022-06-13 14:04:47 +00:00
GroupID : 123 ,
EnableFailingPolicies : false ,
2022-06-06 14:41:51 +00:00
} ,
} ,
Jira : [ ] * fleet . TeamJiraIntegration {
{
URL : srvURL ,
ProjectKey : "qux" ,
EnableFailingPolicies : true ,
} ,
} ,
} ,
2022-06-13 14:04:47 +00:00
} , http . StatusOK , & tmResp )
// removing Zendesk 123 from the global config removes it from the team but
// leaves the Jira integration enabled.
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( fmt . Sprintf ( ` {
"integrations" : {
"jira" : [
{
"url" : % [ 1 ] q ,
"username" : "ok" ,
"api_token" : "foo" ,
"project_key" : "qux"
}
]
}
} ` , srvURL ) ) , http . StatusOK )
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , nil , http . StatusOK , & getResp )
require . Len ( t , getResp . Team . Config . Integrations . Jira , 1 )
require . Equal ( t , "qux" , getResp . Team . Config . Integrations . Jira [ 0 ] . ProjectKey )
require . True ( t , getResp . Team . Config . Integrations . Jira [ 0 ] . EnableFailingPolicies )
require . Len ( t , getResp . Team . Config . Integrations . Zendesk , 0 )
2022-06-06 14:41:51 +00:00
// remove all integrations on exit, so that other tests can enable the
// webhook as needed
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
Integrations : & fleet . TeamIntegrations {
Zendesk : [ ] * fleet . TeamZendeskIntegration { } ,
Jira : [ ] * fleet . TeamJiraIntegration { } ,
} ,
WebhookSettings : & fleet . TeamWebhookSettings { } ,
} , http . StatusOK , & tmResp )
require . Len ( t , tmResp . Team . Config . Integrations . Jira , 0 )
require . Len ( t , tmResp . Team . Config . Integrations . Zendesk , 0 )
require . False ( t , tmResp . Team . Config . WebhookSettings . FailingPoliciesWebhook . Enable )
require . Empty ( t , tmResp . Team . Config . WebhookSettings . FailingPoliciesWebhook . DestinationURL )
2022-06-13 14:04:47 +00:00
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( ` {
"integrations" : { }
} ` ) , http . StatusOK )
2022-06-06 14:41:51 +00:00
}
2023-01-24 16:20:02 +00:00
func ( s * integrationEnterpriseTestSuite ) TestMacOSUpdatesConfig ( ) {
t := s . T ( )
// Create a team
team := & fleet . Team {
Name : t . Name ( ) ,
Description : "Team description" ,
Secrets : [ ] * fleet . EnrollSecret { { Secret : "XYZ" } } ,
}
var tmResp teamResponse
s . DoJSON ( "POST" , "/api/latest/fleet/teams" , team , http . StatusOK , & tmResp )
require . Equal ( t , team . Name , tmResp . Team . Name )
team . ID = tmResp . Team . ID
// modify the team's config
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : "10.15.0" ,
Deadline : "2021-01-01" ,
} ,
} ,
} , http . StatusOK , & tmResp )
require . Equal ( t , "10.15.0" , tmResp . Team . Config . MDM . MacOSUpdates . MinimumVersion )
require . Equal ( t , "2021-01-01" , tmResp . Team . Config . MDM . MacOSUpdates . Deadline )
// only update the deadline
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : "10.15.0" ,
Deadline : "2025-10-01" ,
} ,
} ,
} , http . StatusOK , & tmResp )
require . Equal ( t , "10.15.0" , tmResp . Team . Config . MDM . MacOSUpdates . MinimumVersion )
require . Equal ( t , "2025-10-01" , tmResp . Team . Config . MDM . MacOSUpdates . Deadline )
// sending a nil MacOSUpdate config doesn't modify anything
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { MDM : nil } , http . StatusOK , & tmResp )
require . Equal ( t , "10.15.0" , tmResp . Team . Config . MDM . MacOSUpdates . MinimumVersion )
require . Equal ( t , "2025-10-01" , tmResp . Team . Config . MDM . MacOSUpdates . Deadline )
// sending an empty MacOSUpdate empties both fields
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload { MDM : & fleet . TeamMDM { MacOSUpdates : fleet . MacOSUpdates { } } } , http . StatusOK , & tmResp )
require . Empty ( t , tmResp . Team . Config . MDM . MacOSUpdates . MinimumVersion )
require . Empty ( t , tmResp . Team . Config . MDM . MacOSUpdates . Deadline )
// error checks:
// try to set an invalid deadline
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : "10.15.0" ,
Deadline : "2021-01-01T00:00:00Z" ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
// try to set an invalid minimum version
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : "10.15.0 (19A583)" ,
Deadline : "2021-01-01T00:00:00Z" ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
// try to set a deadline but not a minimum version
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
Deadline : "2021-01-01T00:00:00Z" ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
// try to set a minimum version but not a deadline
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : "10.15.0 (19A583)" ,
} ,
} ,
} , http . StatusUnprocessableEntity , & tmResp )
}
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
func ( s * integrationEnterpriseTestSuite ) TestListDevicePolicies ( ) {
t := s . T ( )
only include policies in device endpoints for premium users (#6077)
This removes policy information from `GET /api/_version_/fleet/device/{token}` from non-premium Fleet instances.
Starting the server with `./build/fleet serve --dev --dev_license`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
[
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Roberto",
"author_email": "test@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-06-03T13:17:42Z",
"response": ""
}
]
```
Starting the server with `./build/fleet serve --dev`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
"not present"
```
2022-06-07 16:27:13 +00:00
ac , err := s . ds . AppConfig ( context . Background ( ) )
require . NoError ( t , err )
ac . OrgInfo . OrgLogoURL = "http://example.com/logo"
err = s . ds . SaveAppConfig ( context . Background ( ) , ac )
require . NoError ( t , err )
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
team , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 51 ,
Name : "team1-policies" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
2023-01-16 15:22:12 +00:00
token := "much_valid"
host := createHostAndDeviceToken ( t , s . ds , token )
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
err = s . ds . AddHostsToTeam ( context . Background ( ) , & team . ID , [ ] uint { host . ID } )
require . NoError ( t , err )
qr , err := s . ds . NewQuery ( context . Background ( ) , & fleet . Query {
Name : "TestQueryEnterpriseGlobalPolicy" ,
Description : "Some description" ,
Query : "select * from osquery;" ,
ObserverCanRun : true ,
} )
require . NoError ( t , err )
// add a global policy
gpParams := globalPolicyRequest {
QueryID : & qr . ID ,
Resolution : "some global resolution" ,
}
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/latest/fleet/policies" , gpParams , http . StatusOK , & gpResp )
require . NotNil ( t , gpResp . Policy )
2022-09-12 19:37:38 +00:00
// add a policy execution
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , host ,
map [ uint ] * bool { gpResp . Policy . ID : ptr . Bool ( false ) } , time . Now ( ) , false ) )
only include policies in device endpoints for premium users (#6077)
This removes policy information from `GET /api/_version_/fleet/device/{token}` from non-premium Fleet instances.
Starting the server with `./build/fleet serve --dev --dev_license`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
[
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Roberto",
"author_email": "test@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-06-03T13:17:42Z",
"response": ""
}
]
```
Starting the server with `./build/fleet serve --dev`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
"not present"
```
2022-06-07 16:27:13 +00:00
// add a policy to team
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
oldToken := s . token
t . Cleanup ( func ( ) {
s . token = oldToken
} )
password := test . GoodPassword
email := "test_enterprise_policies@user.com"
u := & fleet . User {
Name : "test team user" ,
Email : email ,
GlobalRole : nil ,
Teams : [ ] fleet . UserTeam {
{
Team : * team ,
Role : fleet . RoleMaintainer ,
} ,
} ,
}
require . NoError ( t , u . SetPassword ( password , 10 , 10 ) )
_ , err = s . ds . NewUser ( context . Background ( ) , u )
require . NoError ( t , err )
s . token = s . getTestToken ( email , password )
tpParams := teamPolicyRequest {
Name : "TestQueryEnterpriseTeamPolicy" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some team resolution" ,
Platform : "darwin" ,
}
tpResp := teamPolicyResponse { }
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team . ID ) , tpParams , http . StatusOK , & tpResp )
// try with invalid token
res := s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/invalid_token/policies" , nil , http . StatusUnauthorized )
res . Body . Close ( )
only include policies in device endpoints for premium users (#6077)
This removes policy information from `GET /api/_version_/fleet/device/{token}` from non-premium Fleet instances.
Starting the server with `./build/fleet serve --dev --dev_license`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
[
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Roberto",
"author_email": "test@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-06-03T13:17:42Z",
"response": ""
}
]
```
Starting the server with `./build/fleet serve --dev`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
"not present"
```
2022-06-07 16:27:13 +00:00
// GET `/api/_version_/fleet/device/{token}/policies`
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
listDevicePoliciesResp := listDevicePoliciesResponse { }
res = s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/" + token + "/policies" , nil , http . StatusOK )
2022-12-05 22:50:49 +00:00
json . NewDecoder ( res . Body ) . Decode ( & listDevicePoliciesResp ) //nolint:errcheck
res . Body . Close ( ) //nolint:errcheck
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
require . Len ( t , listDevicePoliciesResp . Policies , 2 )
require . NoError ( t , listDevicePoliciesResp . Err )
only include policies in device endpoints for premium users (#6077)
This removes policy information from `GET /api/_version_/fleet/device/{token}` from non-premium Fleet instances.
Starting the server with `./build/fleet serve --dev --dev_license`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
[
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Roberto",
"author_email": "test@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-06-03T13:17:42Z",
"response": ""
}
]
```
Starting the server with `./build/fleet serve --dev`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
"not present"
```
2022-06-07 16:27:13 +00:00
// GET `/api/_version_/fleet/device/{token}`
getDeviceHostResp := getDeviceHostResponse { }
res = s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/" + token , nil , http . StatusOK )
2022-12-05 22:50:49 +00:00
json . NewDecoder ( res . Body ) . Decode ( & getDeviceHostResp ) //nolint:errcheck
res . Body . Close ( ) //nolint:errcheck
only include policies in device endpoints for premium users (#6077)
This removes policy information from `GET /api/_version_/fleet/device/{token}` from non-premium Fleet instances.
Starting the server with `./build/fleet serve --dev --dev_license`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
[
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Roberto",
"author_email": "test@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-06-03T13:17:42Z",
"response": ""
}
]
```
Starting the server with `./build/fleet serve --dev`
```bash
$ curl -s https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a | jq '.host.policies // "not present"'
"not present"
```
2022-06-07 16:27:13 +00:00
require . NoError ( t , getDeviceHostResp . Err )
require . Equal ( t , host . ID , getDeviceHostResp . Host . ID )
require . False ( t , getDeviceHostResp . Host . RefetchRequested )
require . Equal ( t , "http://example.com/logo" , getDeviceHostResp . OrgLogoURL )
require . Len ( t , * getDeviceHostResp . Host . Policies , 2 )
2022-09-12 19:37:38 +00:00
// GET `/api/_version_/fleet/device/{token}/desktop`
2022-10-12 20:13:43 +00:00
getDesktopResp := fleetDesktopResponse { }
2022-09-12 19:37:38 +00:00
res = s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/" + token + "/desktop" , nil , http . StatusOK )
2022-12-05 22:50:49 +00:00
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & getDesktopResp ) )
require . NoError ( t , res . Body . Close ( ) )
2022-09-12 19:37:38 +00:00
require . NoError ( t , getDesktopResp . Err )
require . Equal ( t , * getDesktopResp . FailingPolicies , uint ( 1 ) )
add premium, device authed endpoint to retrieve policies (#5967)
This adds a new device authenticated endpoint, `/api/_version_/fleet/device/{token}/policies` to retrieve the device policies.
An example request / response looks like:
```bash
curl https://localhost:8080/api/latest/fleet/device/1804e808-171f-4dda-9bec-f695b2f2371a/policies
```
```json
{
"policies": [
{
"id": 3,
"name": "Antivirus healthy (Linux)",
"query": "SELECT score FROM (SELECT case when COUNT(*) = 2 then 1 ELSE 0 END AS score FROM processes WHERE (name = 'clamd') OR (name = 'freshclam')) WHERE score == 1;",
"description": "Checks that both ClamAV's daemon and its updater service (freshclam) are running.",
"author_id": 1,
"author_name": "Admin",
"author_email": "admin@example.com",
"team_id": null,
"resolution": "Ensure ClamAV and Freshclam are installed and running.",
"platform": "darwin,windows,linux",
"created_at": "2022-05-23T20:53:36Z",
"updated_at": "2022-05-23T20:53:36Z",
"response": "fail"
}
]
}
```
Related to [#5685](https://github.com/fleetdm/fleet/issues/5685), in another changeset I will be adding "client" endpoints so we can consume this endpoint from Fleet Desktop
2022-05-31 17:54:43 +00:00
}
2022-06-10 15:39:02 +00:00
// TestCustomTransparencyURL tests that Fleet Premium licensees can use custom transparency urls.
func ( s * integrationEnterpriseTestSuite ) TestCustomTransparencyURL ( ) {
t := s . T ( )
token := "token_test_custom_transparency_url"
2023-01-16 15:22:12 +00:00
createHostAndDeviceToken ( t , s . ds , token )
2022-06-10 15:39:02 +00:00
// confirm intitial default url
acResp := appConfigResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/config" , nil , http . StatusOK , & acResp )
require . NotNil ( t , acResp )
require . Equal ( t , fleet . DefaultTransparencyURL , acResp . FleetDesktop . TransparencyURL )
// confirm device endpoint returns initial default url
2022-06-13 19:07:08 +00:00
deviceResp := & transparencyURLResponse { }
rawResp := s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/" + token + "/transparency" , nil , http . StatusTemporaryRedirect )
2022-12-05 22:50:49 +00:00
json . NewDecoder ( rawResp . Body ) . Decode ( deviceResp ) //nolint:errcheck
rawResp . Body . Close ( ) //nolint:errcheck
2022-06-10 15:39:02 +00:00
require . NoError ( t , deviceResp . Err )
2022-06-13 19:07:08 +00:00
require . Equal ( t , fleet . DefaultTransparencyURL , rawResp . Header . Get ( "Location" ) )
2022-06-10 15:39:02 +00:00
// set custom url
acResp = appConfigResponse { }
2022-09-19 17:53:44 +00:00
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( ` { "fleet_desktop": { "transparency_url": "customURL"}} ` ) , http . StatusOK , & acResp )
2022-06-10 15:39:02 +00:00
require . NotNil ( t , acResp )
require . Equal ( t , "customURL" , acResp . FleetDesktop . TransparencyURL )
// device endpoint returns custom url
2022-06-13 19:07:08 +00:00
deviceResp = & transparencyURLResponse { }
rawResp = s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/" + token + "/transparency" , nil , http . StatusTemporaryRedirect )
2022-12-05 22:50:49 +00:00
json . NewDecoder ( rawResp . Body ) . Decode ( deviceResp ) //nolint:errcheck
rawResp . Body . Close ( ) //nolint:errcheck
2022-06-10 15:39:02 +00:00
require . NoError ( t , deviceResp . Err )
2022-06-13 19:07:08 +00:00
require . Equal ( t , "customURL" , rawResp . Header . Get ( "Location" ) )
2022-06-10 15:39:02 +00:00
// empty string applies default url
acResp = appConfigResponse { }
2022-09-19 17:53:44 +00:00
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( ` { "fleet_desktop": { "transparency_url": ""}} ` ) , http . StatusOK , & acResp )
2022-06-10 15:39:02 +00:00
require . NotNil ( t , acResp )
require . Equal ( t , fleet . DefaultTransparencyURL , acResp . FleetDesktop . TransparencyURL )
// device endpoint returns default url
2022-06-13 19:07:08 +00:00
deviceResp = & transparencyURLResponse { }
rawResp = s . DoRawNoAuth ( "GET" , "/api/latest/fleet/device/" + token + "/transparency" , nil , http . StatusTemporaryRedirect )
2022-12-05 22:50:49 +00:00
json . NewDecoder ( rawResp . Body ) . Decode ( deviceResp ) //nolint:errcheck
rawResp . Body . Close ( ) //nolint:errcheck
2022-06-10 15:39:02 +00:00
require . NoError ( t , deviceResp . Err )
2022-06-13 19:07:08 +00:00
require . Equal ( t , fleet . DefaultTransparencyURL , rawResp . Header . Get ( "Location" ) )
2022-06-10 15:39:02 +00:00
}
2022-08-15 17:42:33 +00:00
2023-01-16 14:23:32 +00:00
func ( s * integrationEnterpriseTestSuite ) TestDefaultAppleBMTeam ( ) {
t := s . T ( )
tm , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
Name : t . Name ( ) ,
Description : "desc" ,
} )
require . NoError ( s . T ( ) , err )
var acResp appConfigResponse
// try to set an invalid team name
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( ` {
"mdm" : {
"apple_bm_default_team" : "xyz"
}
} ` ) , http . StatusUnprocessableEntity , & acResp )
// get the appconfig, nothing changed
acResp = appConfigResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/config" , nil , http . StatusOK , & acResp )
require . Empty ( t , acResp . MDM . AppleBMDefaultTeam )
// set to a valid team name
acResp = appConfigResponse { }
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( fmt . Sprintf ( ` {
"mdm" : {
"apple_bm_default_team" : % q
}
} ` , tm . Name ) ) , http . StatusOK , & acResp )
require . Equal ( t , tm . Name , acResp . MDM . AppleBMDefaultTeam )
// get the appconfig, set to that team name
acResp = appConfigResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/config" , nil , http . StatusOK , & acResp )
require . Equal ( t , tm . Name , acResp . MDM . AppleBMDefaultTeam )
}
2023-01-24 16:20:02 +00:00
func ( s * integrationEnterpriseTestSuite ) TestMDMMacOSUpdates ( ) {
t := s . T ( )
checkInvalidConfig := func ( config string ) {
// try to set an invalid config
acResp := appConfigResponse { }
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( config ) , http . StatusUnprocessableEntity , & acResp )
// get the appconfig, nothing changed
acResp = appConfigResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/config" , nil , http . StatusOK , & acResp )
require . Equal ( t , fleet . MacOSUpdates { } , acResp . MDM . MacOSUpdates )
}
// missing minimum_version
checkInvalidConfig ( ` { "mdm" : {
"macos_updates" : {
"deadline" : "2022-01-01"
}
} } ` )
// missing deadline
checkInvalidConfig ( ` { "mdm" : {
"macos_updates" : {
"minimum_version" : "12.1.1"
}
} } ` )
// invalid deadline
checkInvalidConfig ( ` { "mdm" : {
"macos_updates" : {
"minimum_version" : "12.1.1" ,
"deadline" : "2022"
}
} } ` )
// deadline includes timestamp
checkInvalidConfig ( ` { "mdm" : {
"macos_updates" : {
"minimum_version" : "12.1.1" ,
"deadline" : "2022-01-01T00:00:00Z"
}
} } ` )
// minimum_version includes build info
checkInvalidConfig ( ` { "mdm" : {
"macos_updates" : {
"minimum_version" : "12.1.1 (ABCD)" ,
"deadline" : "2022-01-01"
}
} } ` )
// valid config
acResp := appConfigResponse { }
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( ` {
"mdm" : {
"macos_updates" : {
"minimum_version" : "12.3.1" ,
"deadline" : "2022-01-01"
}
}
} ` ) , http . StatusOK , & acResp )
require . Equal ( t , "12.3.1" , acResp . MDM . MacOSUpdates . MinimumVersion )
require . Equal ( t , "2022-01-01" , acResp . MDM . MacOSUpdates . Deadline )
// get the appconfig
acResp = appConfigResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/config" , nil , http . StatusOK , & acResp )
require . Equal ( t , "12.3.1" , acResp . MDM . MacOSUpdates . MinimumVersion )
require . Equal ( t , "2022-01-01" , acResp . MDM . MacOSUpdates . Deadline )
}
2022-08-15 17:42:33 +00:00
func ( s * integrationEnterpriseTestSuite ) TestSSOJITProvisioning ( ) {
t := s . T ( )
acResp := appConfigResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/config" , nil , http . StatusOK , & acResp )
require . NotNil ( t , acResp )
require . False ( t , acResp . SSOSettings . EnableJITProvisioning )
acResp = appConfigResponse { }
2022-09-19 17:53:44 +00:00
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( ` {
"sso_settings" : {
"enable_sso" : true ,
"entity_id" : "https://localhost:8080" ,
"issuer_uri" : "http://localhost:8080/simplesaml/saml2/idp/SSOService.php" ,
"idp_name" : "SimpleSAML" ,
"metadata_url" : "http://localhost:9080/simplesaml/saml2/idp/metadata.php" ,
"enable_jit_provisioning" : false
}
} ` ) , http . StatusOK , & acResp )
2022-08-15 17:42:33 +00:00
require . NotNil ( t , acResp )
require . False ( t , acResp . SSOSettings . EnableJITProvisioning )
// users can't be created if SSO is disabled
auth , body := s . LoginSSOUser ( "sso_user" , "user123#" )
require . Contains ( t , body , "/login?status=account_invalid" )
// ensure theresn't a user in the DB
_ , err := s . ds . UserByEmail ( context . Background ( ) , auth . UserID ( ) )
var nfe fleet . NotFoundError
require . ErrorAs ( t , err , & nfe )
// enable JIT provisioning
acResp = appConfigResponse { }
2022-09-19 17:53:44 +00:00
s . DoJSON ( "PATCH" , "/api/latest/fleet/config" , json . RawMessage ( ` {
"sso_settings" : {
"enable_sso" : true ,
"entity_id" : "https://localhost:8080" ,
"issuer_uri" : "http://localhost:8080/simplesaml/saml2/idp/SSOService.php" ,
"idp_name" : "SimpleSAML" ,
"metadata_url" : "http://localhost:9080/simplesaml/saml2/idp/metadata.php" ,
"enable_jit_provisioning" : true
}
} ` ) , http . StatusOK , & acResp )
2022-08-15 17:42:33 +00:00
require . NotNil ( t , acResp )
require . True ( t , acResp . SSOSettings . EnableJITProvisioning )
// a new user is created and redirected accordingly
auth , body = s . LoginSSOUser ( "sso_user" , "user123#" )
// a successful redirect has this content
require . Contains ( t , body , "Redirecting to Fleet at ..." )
user , err := s . ds . UserByEmail ( context . Background ( ) , auth . UserID ( ) )
require . NoError ( t , err )
require . Equal ( t , auth . UserID ( ) , user . Email )
// a new activity item is created
activitiesResp := listActivitiesResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/activities" , nil , http . StatusOK , & activitiesResp )
require . NoError ( t , activitiesResp . Err )
require . NotEmpty ( t , activitiesResp . Activities )
require . Condition ( t , func ( ) bool {
for _ , a := range activitiesResp . Activities {
2022-12-23 16:05:16 +00:00
if ( a . Type == fleet . ActivityTypeUserAddedBySSO { } . ActivityName ( ) ) && * a . ActorEmail == auth . UserID ( ) {
2022-08-15 17:42:33 +00:00
return true
}
}
return false
} )
}
2022-08-30 11:13:09 +00:00
func ( s * integrationEnterpriseTestSuite ) TestDistributedReadWithFeatures ( ) {
t := s . T ( )
// Global config has both features enabled
spec := [ ] byte ( `
features :
additional_queries : null
enable_host_users : true
enable_software_inventory : true
` )
s . applyConfig ( spec )
// Team config has only additional queries enabled
a := json . RawMessage ( ` { "time": "SELECT * FROM time"} ` )
team , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 8324 ,
Name : "team1_" + t . Name ( ) ,
Description : "desc team1_" + t . Name ( ) ,
Config : fleet . TeamConfig {
Features : fleet . Features {
EnableHostUsers : false ,
EnableSoftwareInventory : false ,
AdditionalQueries : & a ,
} ,
} ,
} )
require . NoError ( t , err )
// Create a host without a team
host , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - 1 * time . Minute ) ,
2022-12-26 21:32:39 +00:00
OsqueryHostID : ptr . String ( t . Name ( ) ) ,
NodeKey : ptr . String ( t . Name ( ) ) ,
2022-08-30 11:13:09 +00:00
UUID : uuid . New ( ) . String ( ) ,
Hostname : fmt . Sprintf ( "%sfoo.local" , t . Name ( ) ) ,
Platform : "darwin" ,
} )
require . NoError ( t , err )
s . lq . On ( "QueriesForHost" , host . ID ) . Return ( map [ string ] string { fmt . Sprintf ( "%d" , host . ID ) : "select 1 from osquery;" } , nil )
// ensure we can read distributed queries for the host
err = s . ds . UpdateHostRefetchRequested ( context . Background ( ) , host . ID , true )
require . NoError ( t , err )
// get distributed queries for the host
2022-12-26 21:32:39 +00:00
req := getDistributedQueriesRequest { NodeKey : * host . NodeKey }
2022-08-30 11:13:09 +00:00
var dqResp getDistributedQueriesResponse
s . DoJSON ( "POST" , "/api/osquery/distributed/read" , req , http . StatusOK , & dqResp )
require . Contains ( t , dqResp . Queries , "fleet_detail_query_users" )
require . Contains ( t , dqResp . Queries , "fleet_detail_query_software_macos" )
require . NotContains ( t , dqResp . Queries , "fleet_additional_query_time" )
// add the host to team1
err = s . ds . AddHostsToTeam ( context . Background ( ) , & team . ID , [ ] uint { host . ID } )
require . NoError ( t , err )
err = s . ds . UpdateHostRefetchRequested ( context . Background ( ) , host . ID , true )
require . NoError ( t , err )
2022-12-26 21:32:39 +00:00
req = getDistributedQueriesRequest { NodeKey : * host . NodeKey }
2022-08-30 11:13:09 +00:00
dqResp = getDistributedQueriesResponse { }
s . DoJSON ( "POST" , "/api/osquery/distributed/read" , req , http . StatusOK , & dqResp )
require . NotContains ( t , dqResp . Queries , "fleet_detail_query_users" )
require . NotContains ( t , dqResp . Queries , "fleet_detail_query_software_macos" )
require . Contains ( t , dqResp . Queries , "fleet_additional_query_time" )
}
2022-09-21 19:16:31 +00:00
func ( s * integrationEnterpriseTestSuite ) TestListHosts ( ) {
t := s . T ( )
// create a couple of hosts
host1 , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - 1 * time . Minute ) ,
2022-12-26 21:32:39 +00:00
OsqueryHostID : ptr . String ( t . Name ( ) ) ,
NodeKey : ptr . String ( t . Name ( ) ) ,
2022-09-21 19:16:31 +00:00
UUID : uuid . New ( ) . String ( ) ,
Hostname : fmt . Sprintf ( "%sfoo.local" , t . Name ( ) ) ,
Platform : "darwin" ,
} )
require . NoError ( t , err )
host2 , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - 1 * time . Minute ) ,
2022-12-26 21:32:39 +00:00
OsqueryHostID : ptr . String ( t . Name ( ) + "2" ) ,
NodeKey : ptr . String ( t . Name ( ) + "2" ) ,
2022-09-21 19:16:31 +00:00
UUID : uuid . New ( ) . String ( ) ,
Hostname : fmt . Sprintf ( "%sbar.local" , t . Name ( ) ) ,
Platform : "linux" ,
} )
require . NoError ( t , err )
host3 , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - 1 * time . Minute ) ,
2022-12-26 21:32:39 +00:00
OsqueryHostID : ptr . String ( t . Name ( ) + "3" ) ,
NodeKey : ptr . String ( t . Name ( ) + "3" ) ,
2022-09-21 19:16:31 +00:00
UUID : uuid . New ( ) . String ( ) ,
Hostname : fmt . Sprintf ( "%sbaz.local" , t . Name ( ) ) ,
Platform : "windows" ,
} )
require . NoError ( t , err )
require . NotNil ( t , host3 )
// set disk space information for some hosts (none provided for host3)
require . NoError ( t , s . ds . SetOrUpdateHostDisksSpace ( context . Background ( ) , host1 . ID , 10.0 , 2.0 ) )
require . NoError ( t , s . ds . SetOrUpdateHostDisksSpace ( context . Background ( ) , host2 . ID , 40.0 , 4.0 ) )
var resp listHostsResponse
s . DoJSON ( "GET" , "/api/latest/fleet/hosts" , nil , http . StatusOK , & resp )
require . Len ( t , resp . Hosts , 3 )
resp = listHostsResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/hosts" , nil , http . StatusOK , & resp , "low_disk_space" , "32" )
require . Len ( t , resp . Hosts , 1 )
assert . Equal ( t , host1 . ID , resp . Hosts [ 0 ] . ID )
assert . Equal ( t , 10.0 , resp . Hosts [ 0 ] . GigsDiskSpaceAvailable )
resp = listHostsResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/hosts" , nil , http . StatusOK , & resp , "low_disk_space" , "100" )
require . Len ( t , resp . Hosts , 2 )
// returns an error when the criteria is invalid (outside 1-100)
s . DoJSON ( "GET" , "/api/latest/fleet/hosts/count" , nil , http . StatusInternalServerError , & resp , "low_disk_space" , "101" ) // TODO: status code to be fixed with #4406
s . DoJSON ( "GET" , "/api/latest/fleet/hosts/count" , nil , http . StatusInternalServerError , & resp , "low_disk_space" , "0" ) // TODO: status code to be fixed with #4406
// counting hosts works with and without the filter too
var countResp countHostsResponse
s . DoJSON ( "GET" , "/api/latest/fleet/hosts/count" , nil , http . StatusOK , & countResp )
require . Equal ( t , 3 , countResp . Count )
s . DoJSON ( "GET" , "/api/latest/fleet/hosts/count" , nil , http . StatusOK , & countResp , "low_disk_space" , "32" )
require . Equal ( t , 1 , countResp . Count )
s . DoJSON ( "GET" , "/api/latest/fleet/hosts/count" , nil , http . StatusOK , & countResp , "low_disk_space" , "100" )
require . Equal ( t , 2 , countResp . Count )
2022-09-21 19:56:17 +00:00
// host summary returns counts for low disk space
var summaryResp getHostSummaryResponse
s . DoJSON ( "GET" , "/api/latest/fleet/host_summary" , nil , http . StatusOK , & summaryResp , "low_disk_space" , "32" )
require . Equal ( t , uint ( 3 ) , summaryResp . TotalsHostsCount )
require . NotNil ( t , summaryResp . LowDiskSpaceCount )
require . Equal ( t , uint ( 1 ) , * summaryResp . LowDiskSpaceCount )
summaryResp = getHostSummaryResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/host_summary" , nil , http . StatusOK , & summaryResp , "platform" , "windows" , "low_disk_space" , "32" )
require . Equal ( t , uint ( 1 ) , summaryResp . TotalsHostsCount )
require . NotNil ( t , summaryResp . LowDiskSpaceCount )
require . Equal ( t , uint ( 0 ) , * summaryResp . LowDiskSpaceCount )
// all possible filters
summaryResp = getHostSummaryResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/host_summary" , nil , http . StatusOK , & summaryResp , "team_id" , "1" , "platform" , "linux" , "low_disk_space" , "32" )
require . Equal ( t , uint ( 0 ) , summaryResp . TotalsHostsCount )
require . NotNil ( t , summaryResp . LowDiskSpaceCount )
require . Equal ( t , uint ( 0 ) , * summaryResp . LowDiskSpaceCount )
// without low_disk_space, does not return the count
summaryResp = getHostSummaryResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/host_summary" , nil , http . StatusOK , & summaryResp , "team_id" , "1" , "platform" , "linux" )
require . Equal ( t , uint ( 0 ) , summaryResp . TotalsHostsCount )
require . Nil ( t , summaryResp . LowDiskSpaceCount )
2022-09-21 19:16:31 +00:00
}
2022-12-09 18:23:08 +00:00
2022-12-12 20:45:53 +00:00
func ( s * integrationEnterpriseTestSuite ) TestAppleMDMNotConfigured ( ) {
2023-01-25 19:44:29 +00:00
var rawResp json . RawMessage
s . DoJSON ( "GET" , "/api/latest/fleet/mdm/apple" , nil , http . StatusNotFound , & rawResp )
s . DoJSON ( "GET" , "/api/latest/fleet/mdm/apple_bm" , nil , http . StatusNotFound , & rawResp )
2022-12-12 20:45:53 +00:00
}
2022-12-09 18:23:08 +00:00
func ( s * integrationEnterpriseTestSuite ) TestGlobalPolicyCreateReadPatch ( ) {
fields := [ ] string { "Query" , "Name" , "Description" , "Resolution" , "Platform" , "Critical" }
createPol1 := & globalPolicyResponse { }
createPol1Req := & globalPolicyRequest {
Query : "query" ,
Name : "name1" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : true ,
}
s . DoJSON ( "POST" , "/api/latest/fleet/policies" , createPol1Req , http . StatusOK , & createPol1 )
allEqual ( s . T ( ) , createPol1Req , createPol1 . Policy , fields ... )
createPol2 := & globalPolicyResponse { }
createPol2Req := & globalPolicyRequest {
Query : "query" ,
Name : "name2" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : false ,
}
s . DoJSON ( "POST" , "/api/latest/fleet/policies" , createPol2Req , http . StatusOK , & createPol2 )
allEqual ( s . T ( ) , createPol2Req , createPol2 . Policy , fields ... )
listPol := & listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/policies" , nil , http . StatusOK , listPol )
require . Len ( s . T ( ) , listPol . Policies , 2 )
sort . Slice ( listPol . Policies , func ( i , j int ) bool {
return listPol . Policies [ i ] . Name < listPol . Policies [ j ] . Name
} )
require . Equal ( s . T ( ) , createPol1 . Policy , listPol . Policies [ 0 ] )
require . Equal ( s . T ( ) , createPol2 . Policy , listPol . Policies [ 1 ] )
patchPol1Req := & modifyGlobalPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
Name : ptr . String ( "newName1" ) ,
Query : ptr . String ( "newQuery" ) ,
Description : ptr . String ( "newDescription" ) ,
Resolution : ptr . String ( "newResolution" ) ,
Platform : ptr . String ( "windows" ) ,
Critical : ptr . Bool ( false ) ,
} ,
}
patchPol1 := & modifyGlobalPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/policies/%d" , createPol1 . Policy . ID ) , patchPol1Req , http . StatusOK , patchPol1 )
allEqual ( s . T ( ) , patchPol1Req , patchPol1 . Policy , fields ... )
patchPol2Req := & modifyGlobalPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
Name : ptr . String ( "newName2" ) ,
Query : ptr . String ( "newQuery" ) ,
Description : ptr . String ( "newDescription" ) ,
Resolution : ptr . String ( "newResolution" ) ,
Platform : ptr . String ( "windows" ) ,
Critical : ptr . Bool ( true ) ,
} ,
}
patchPol2 := & modifyGlobalPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/policies/%d" , createPol2 . Policy . ID ) , patchPol2Req , http . StatusOK , patchPol2 )
allEqual ( s . T ( ) , patchPol2Req , patchPol2 . Policy , fields ... )
listPol = & listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/latest/fleet/policies" , nil , http . StatusOK , listPol )
require . Len ( s . T ( ) , listPol . Policies , 2 )
sort . Slice ( listPol . Policies , func ( i , j int ) bool {
return listPol . Policies [ i ] . Name < listPol . Policies [ j ] . Name
} )
// not using require.Equal because "PATCH policies" returns the wrong updated timestamp.
allEqual ( s . T ( ) , patchPol1 . Policy , listPol . Policies [ 0 ] , fields ... )
allEqual ( s . T ( ) , patchPol2 . Policy , listPol . Policies [ 1 ] , fields ... )
getPol2 := & getPolicyByIDResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/policies/%d" , createPol2 . Policy . ID ) , nil , http . StatusOK , getPol2 )
require . Equal ( s . T ( ) , listPol . Policies [ 1 ] , getPol2 . Policy )
}
func ( s * integrationEnterpriseTestSuite ) TestTeamPolicyCreateReadPatch ( ) {
fields := [ ] string { "Query" , "Name" , "Description" , "Resolution" , "Platform" , "Critical" }
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1" ,
Description : "desc team1" ,
} )
require . NoError ( s . T ( ) , err )
createPol1 := & teamPolicyResponse { }
createPol1Req := & teamPolicyRequest {
Query : "query" ,
Name : "name1" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : true ,
}
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , createPol1Req , http . StatusOK , & createPol1 )
allEqual ( s . T ( ) , createPol1Req , createPol1 . Policy , fields ... )
createPol2 := & teamPolicyResponse { }
createPol2Req := & teamPolicyRequest {
Query : "query" ,
Name : "name2" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : false ,
}
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , createPol2Req , http . StatusOK , & createPol2 )
allEqual ( s . T ( ) , createPol2Req , createPol2 . Policy , fields ... )
listPol := & listTeamPoliciesResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , listPol )
require . Len ( s . T ( ) , listPol . Policies , 2 )
sort . Slice ( listPol . Policies , func ( i , j int ) bool {
return listPol . Policies [ i ] . Name < listPol . Policies [ j ] . Name
} )
require . Equal ( s . T ( ) , createPol1 . Policy , listPol . Policies [ 0 ] )
require . Equal ( s . T ( ) , createPol2 . Policy , listPol . Policies [ 1 ] )
patchPol1Req := & modifyTeamPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
Name : ptr . String ( "newName1" ) ,
Query : ptr . String ( "newQuery" ) ,
Description : ptr . String ( "newDescription" ) ,
Resolution : ptr . String ( "newResolution" ) ,
Platform : ptr . String ( "windows" ) ,
Critical : ptr . Bool ( false ) ,
} ,
}
patchPol1 := & modifyTeamPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies/%d" , team1 . ID , createPol1 . Policy . ID ) , patchPol1Req , http . StatusOK , patchPol1 )
allEqual ( s . T ( ) , patchPol1Req , patchPol1 . Policy , fields ... )
patchPol2Req := & modifyTeamPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
Name : ptr . String ( "newName2" ) ,
Query : ptr . String ( "newQuery" ) ,
Description : ptr . String ( "newDescription" ) ,
Resolution : ptr . String ( "newResolution" ) ,
Platform : ptr . String ( "windows" ) ,
Critical : ptr . Bool ( true ) ,
} ,
}
patchPol2 := & modifyTeamPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies/%d" , team1 . ID , createPol2 . Policy . ID ) , patchPol2Req , http . StatusOK , patchPol2 )
allEqual ( s . T ( ) , patchPol2Req , patchPol2 . Policy , fields ... )
listPol = & listTeamPoliciesResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , listPol )
require . Len ( s . T ( ) , listPol . Policies , 2 )
sort . Slice ( listPol . Policies , func ( i , j int ) bool {
return listPol . Policies [ i ] . Name < listPol . Policies [ j ] . Name
} )
// not using require.Equal because "PATCH policies" returns the wrong updated timestamp.
allEqual ( s . T ( ) , patchPol1 . Policy , listPol . Policies [ 0 ] , fields ... )
allEqual ( s . T ( ) , patchPol2 . Policy , listPol . Policies [ 1 ] , fields ... )
getPol2 := & getPolicyByIDResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies/%d" , team1 . ID , createPol2 . Policy . ID ) , nil , http . StatusOK , getPol2 )
require . Equal ( s . T ( ) , listPol . Policies [ 1 ] , getPol2 . Policy )
}
2022-12-16 21:00:54 +00:00
func ( s * integrationEnterpriseTestSuite ) TestResetAutomation ( ) {
ctx := context . Background ( )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1" ,
Description : "desc team1" ,
} )
require . NoError ( s . T ( ) , err )
createPol1 := & teamPolicyResponse { }
createPol1Req := & teamPolicyRequest {
Query : "query" ,
Name : "name1" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : true ,
}
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , createPol1Req , http . StatusOK , & createPol1 )
createPol2 := & teamPolicyResponse { }
createPol2Req := & teamPolicyRequest {
Query : "query" ,
Name : "name2" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : false ,
}
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , createPol2Req , http . StatusOK , & createPol2 )
createPol3 := & teamPolicyResponse { }
createPol3Req := & teamPolicyRequest {
Query : "query" ,
Name : "name3" ,
Description : "description" ,
Resolution : "resolution" ,
Platform : "linux" ,
Critical : false ,
}
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/latest/fleet/teams/%d/policies" , team1 . ID ) , createPol3Req , http . StatusOK , & createPol3 )
var tmResp teamResponse
// modify the team's config - enable the webhook
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team1 . ID ) , fleet . TeamPayload { WebhookSettings : & fleet . TeamWebhookSettings {
FailingPoliciesWebhook : fleet . FailingPoliciesWebhookSettings {
Enable : true ,
DestinationURL : "http://127/" ,
PolicyIDs : [ ] uint { createPol1 . Policy . ID , createPol2 . Policy . ID } ,
HostBatchSize : 12345 ,
} ,
} } , http . StatusOK , & tmResp )
h1 , err := s . ds . NewHost ( ctx , & fleet . Host { } )
require . NoError ( s . T ( ) , err )
err = s . ds . RecordPolicyQueryExecutions ( ctx , h1 , map [ uint ] * bool {
createPol1 . Policy . ID : ptr . Bool ( false ) ,
createPol2 . Policy . ID : ptr . Bool ( false ) ,
createPol3 . Policy . ID : ptr . Bool ( false ) , // This policy is not activated for automation in config.
} , time . Now ( ) , false )
require . NoError ( s . T ( ) , err )
pfs , err := s . ds . OutdatedAutomationBatch ( ctx )
require . NoError ( s . T ( ) , err )
require . Empty ( s . T ( ) , pfs )
s . DoJSON ( "POST" , "/api/latest/fleet/automations/reset" , resetAutomationRequest {
TeamIDs : nil ,
PolicyIDs : [ ] uint { } ,
} , http . StatusOK , & tmResp )
pfs , err = s . ds . OutdatedAutomationBatch ( ctx )
require . NoError ( s . T ( ) , err )
require . Empty ( s . T ( ) , pfs )
s . DoJSON ( "POST" , "/api/latest/fleet/automations/reset" , resetAutomationRequest {
TeamIDs : nil ,
PolicyIDs : [ ] uint { createPol1 . Policy . ID , createPol2 . Policy . ID , createPol3 . Policy . ID } ,
} , http . StatusOK , & tmResp )
pfs , err = s . ds . OutdatedAutomationBatch ( ctx )
require . NoError ( s . T ( ) , err )
require . Len ( s . T ( ) , pfs , 2 )
s . DoJSON ( "POST" , "/api/latest/fleet/automations/reset" , resetAutomationRequest {
TeamIDs : [ ] uint { team1 . ID } ,
PolicyIDs : nil ,
} , http . StatusOK , & tmResp )
pfs , err = s . ds . OutdatedAutomationBatch ( ctx )
require . NoError ( s . T ( ) , err )
require . Len ( s . T ( ) , pfs , 2 )
s . DoJSON ( "POST" , "/api/latest/fleet/automations/reset" , resetAutomationRequest {
TeamIDs : nil ,
PolicyIDs : [ ] uint { createPol2 . Policy . ID } ,
} , http . StatusOK , & tmResp )
pfs , err = s . ds . OutdatedAutomationBatch ( ctx )
require . NoError ( s . T ( ) , err )
require . Len ( s . T ( ) , pfs , 1 )
}
2023-01-25 20:03:40 +00:00
func ( s * integrationEnterpriseTestSuite ) TestOrbitConfigNudgeSettings ( ) {
t := s . T ( )
// ensure the config is empty before starting
s . applyConfig ( [ ] byte ( `
mdm :
macos_updates :
deadline : ""
minimum_version : ""
` ) )
var resp orbitGetConfigResponse
// missing orbit key
s . DoJSON ( "POST" , "/api/fleet/orbit/config" , nil , http . StatusUnauthorized , & resp )
// nudge config is empty if macos_updates is not set
h := createOrbitEnrolledHost ( t , "darwin" , "h" , s . ds )
resp = orbitGetConfigResponse { }
s . DoJSON ( "POST" , "/api/fleet/orbit/config" , json . RawMessage ( fmt . Sprintf ( ` { "orbit_node_key": %q} ` , * h . OrbitNodeKey ) ) , http . StatusOK , & resp )
require . Empty ( t , resp . NudgeConfig )
// set macos_updates
s . applyConfig ( [ ] byte ( `
mdm :
macos_updates :
deadline : 2022 - 01 - 04
minimum_version : 12.1 .3
` ) )
resp = orbitGetConfigResponse { }
s . DoJSON ( "POST" , "/api/fleet/orbit/config" , json . RawMessage ( fmt . Sprintf ( ` { "orbit_node_key": %q} ` , * h . OrbitNodeKey ) ) , http . StatusOK , & resp )
require . JSONEq (
t ,
` {
"osVersionRequirements" : [
{
"requiredInstallationDate" : "2022-01-04" ,
"requiredMinimumOSVersion" : "12.1.3"
}
]
} ` ,
string ( resp . NudgeConfig ) ,
)
// create a team with an empty macos_updates config
team , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 4827 ,
Name : "team1_" + t . Name ( ) ,
Description : "desc team1_" + t . Name ( ) ,
} )
require . NoError ( t , err )
// add the host to the team
err = s . ds . AddHostsToTeam ( context . Background ( ) , & team . ID , [ ] uint { h . ID } )
require . NoError ( t , err )
// NudgeConfig should be empty
resp = orbitGetConfigResponse { }
s . DoJSON ( "POST" , "/api/fleet/orbit/config" , json . RawMessage ( fmt . Sprintf ( ` { "orbit_node_key": %q} ` , * h . OrbitNodeKey ) ) , http . StatusOK , & resp )
require . Empty ( t , resp . NudgeConfig )
// modify the team config, add macos_updates config
var tmResp teamResponse
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/latest/fleet/teams/%d" , team . ID ) , fleet . TeamPayload {
MDM : & fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
Deadline : "1992-01-01" ,
MinimumVersion : "13.1.1" ,
} ,
} ,
} , http . StatusOK , & tmResp )
resp = orbitGetConfigResponse { }
s . DoJSON ( "POST" , "/api/fleet/orbit/config" , json . RawMessage ( fmt . Sprintf ( ` { "orbit_node_key": %q} ` , * h . OrbitNodeKey ) ) , http . StatusOK , & resp )
require . JSONEq (
t ,
` {
"osVersionRequirements" : [
{
"requiredInstallationDate" : "1992-01-01" ,
"requiredMinimumOSVersion" : "13.1.1"
}
]
} ` ,
string ( resp . NudgeConfig ) ,
)
// create a new host, still receives the global config
h2 := createOrbitEnrolledHost ( t , "darwin" , "h2" , s . ds )
resp = orbitGetConfigResponse { }
s . DoJSON ( "POST" , "/api/fleet/orbit/config" , json . RawMessage ( fmt . Sprintf ( ` { "orbit_node_key": %q} ` , * h2 . OrbitNodeKey ) ) , http . StatusOK , & resp )
require . JSONEq (
t ,
` {
"osVersionRequirements" : [
{
"requiredInstallationDate" : "2022-01-04" ,
"requiredMinimumOSVersion" : "12.1.3"
}
]
} ` ,
string ( resp . NudgeConfig ) ,
)
}
2022-12-09 18:23:08 +00:00
// allEqual compares all fields of a struct.
// If a field is a pointer on one side but not on the other, then it follows that pointer. This is useful for optional
// arguments.
func allEqual ( t * testing . T , expect , actual interface { } , fields ... string ) {
require . NotEmpty ( t , fields )
t . Helper ( )
expV := reflect . Indirect ( reflect . ValueOf ( expect ) )
actV := reflect . Indirect ( reflect . ValueOf ( actual ) )
for _ , f := range fields {
e , a := expV . FieldByName ( f ) , actV . FieldByName ( f )
switch {
case e . Kind ( ) == reflect . Ptr && a . Kind ( ) != reflect . Ptr && ! e . IsZero ( ) :
e = e . Elem ( )
case a . Kind ( ) == reflect . Ptr && e . Kind ( ) != reflect . Ptr && ! a . IsZero ( ) :
a = a . Elem ( )
}
require . Equal ( t , e . Interface ( ) , a . Interface ( ) , "%s" , f )
}
}
2023-01-16 15:22:12 +00:00
func createHostAndDeviceToken ( t * testing . T , ds * mysql . Datastore , token string ) * fleet . Host {
host , err := ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - 1 * time . Minute ) ,
OsqueryHostID : ptr . String ( t . Name ( ) ) ,
NodeKey : ptr . String ( t . Name ( ) ) ,
UUID : uuid . New ( ) . String ( ) ,
Hostname : fmt . Sprintf ( "%sfoo.local" , t . Name ( ) ) ,
Platform : "darwin" ,
} )
require . NoError ( t , err )
mysql . ExecAdhocSQL ( t , ds , func ( db sqlx . ExtContext ) error {
_ , err := db . ExecContext ( context . Background ( ) , ` INSERT INTO host_device_auth (host_id, token) VALUES (?, ?) ` , host . ID , token )
return err
} )
return host
}