2021-09-15 19:27:53 +00:00
package service
import (
"context"
2022-01-25 14:34:00 +00:00
"crypto/rand"
2022-01-13 19:57:44 +00:00
"encoding/base64"
2021-11-24 17:16:42 +00:00
"encoding/json"
2021-09-15 19:27:53 +00:00
"fmt"
"io/ioutil"
"net/http"
2021-12-15 14:35:40 +00:00
"net/url"
2021-11-24 17:16:42 +00:00
"reflect"
2021-12-21 14:53:15 +00:00
"strconv"
2021-12-15 14:35:40 +00:00
"strings"
2021-09-15 19:27:53 +00:00
"testing"
"time"
2022-03-08 16:27:38 +00:00
"github.com/fleetdm/fleet/v4/server/datastore/mysql"
2021-09-15 19:27:53 +00:00
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/ptr"
2021-10-15 10:34:30 +00:00
"github.com/fleetdm/fleet/v4/server/test"
2021-09-15 19:27:53 +00:00
"github.com/ghodss/yaml"
2021-12-14 21:34:11 +00:00
"github.com/google/uuid"
2022-03-08 16:27:38 +00:00
"github.com/jmoiron/sqlx"
2021-09-15 19:27:53 +00:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
2021-11-11 20:33:06 +00:00
"gopkg.in/guregu/null.v3"
2021-09-15 19:27:53 +00:00
)
type integrationTestSuite struct {
suite . Suite
withServer
}
func ( s * integrationTestSuite ) SetupSuite ( ) {
s . withServer . SetupSuite ( "integrationTestSuite" )
}
2021-09-29 16:13:23 +00:00
func ( s * integrationTestSuite ) TearDownTest ( ) {
2022-01-10 19:43:39 +00:00
t := s . T ( )
ctx := context . Background ( )
2021-09-29 16:13:23 +00:00
u := s . users [ "admin1@example.com" ]
filter := fleet . TeamFilter { User : & u }
2022-01-10 19:43:39 +00:00
hosts , err := s . ds . ListHosts ( ctx , filter , fleet . HostListOptions { } )
require . NoError ( t , err )
2021-09-29 16:13:23 +00:00
for _ , host := range hosts {
2022-02-09 15:16:50 +00:00
require . NoError ( t , s . ds . UpdateHostSoftware ( context . Background ( ) , host . ID , nil ) )
2022-03-09 21:13:56 +00:00
require . NoError ( t , s . ds . DeleteHost ( ctx , host . ID ) )
2022-01-10 19:43:39 +00:00
}
2021-12-21 14:53:15 +00:00
2022-02-09 15:16:50 +00:00
// recalculate software counts will remove the software entries
require . NoError ( t , s . ds . CalculateHostsPerSoftware ( context . Background ( ) , time . Now ( ) ) )
2022-01-10 19:43:39 +00:00
lbls , err := s . ds . ListLabels ( ctx , fleet . TeamFilter { } , fleet . ListOptions { } )
require . NoError ( t , err )
2021-12-21 14:53:15 +00:00
for _ , lbl := range lbls {
if lbl . LabelType != fleet . LabelTypeBuiltIn {
2022-01-10 19:43:39 +00:00
err := s . ds . DeleteLabel ( ctx , lbl . Name )
require . NoError ( t , err )
}
}
users , err := s . ds . ListUsers ( ctx , fleet . UserListOptions { } )
require . NoError ( t , err )
for _ , u := range users {
if _ , ok := s . users [ u . Email ] ; ! ok {
err := s . ds . DeleteUser ( ctx , u . ID )
require . NoError ( t , err )
2021-12-21 14:53:15 +00:00
}
}
2022-01-18 16:18:40 +00:00
2022-02-15 20:22:19 +00:00
teams , err := s . ds . ListTeams ( ctx , fleet . TeamFilter { User : & u } , fleet . ListOptions { } )
require . NoError ( t , err )
for _ , tm := range teams {
err := s . ds . DeleteTeam ( ctx , tm . ID )
require . NoError ( t , err )
}
2022-01-18 16:18:40 +00:00
globalPolicies , err := s . ds . ListGlobalPolicies ( ctx )
require . NoError ( t , err )
if len ( globalPolicies ) > 0 {
var globalPolicyIDs [ ] uint
for _ , gp := range globalPolicies {
globalPolicyIDs = append ( globalPolicyIDs , gp . ID )
}
_ , err = s . ds . DeleteGlobalPolicies ( ctx , globalPolicyIDs )
require . NoError ( t , err )
}
2022-02-14 18:13:44 +00:00
// CalculateHostsPerSoftware performs a cleanup.
err = s . ds . CalculateHostsPerSoftware ( ctx , time . Now ( ) )
require . NoError ( t , err )
2021-09-29 16:13:23 +00:00
}
2021-09-15 19:27:53 +00:00
func TestIntegrations ( t * testing . T ) {
testingSuite := new ( integrationTestSuite )
testingSuite . s = & testingSuite . Suite
suite . Run ( t , testingSuite )
}
func ( s * integrationTestSuite ) TestDoubleUserCreationErrors ( ) {
t := s . T ( )
params := fleet . UserPayload {
Name : ptr . String ( "user1" ) ,
Email : ptr . String ( "email@asd.com" ) ,
Password : ptr . String ( "pass" ) ,
GlobalRole : ptr . String ( fleet . RoleObserver ) ,
}
s . Do ( "POST" , "/api/v1/fleet/users/admin" , & params , http . StatusOK )
respSecond := s . Do ( "POST" , "/api/v1/fleet/users/admin" , & params , http . StatusConflict )
assertBodyContains ( t , respSecond , ` Error 1062: Duplicate entry 'email@asd.com' ` )
}
func ( s * integrationTestSuite ) TestUserWithoutRoleErrors ( ) {
t := s . T ( )
params := fleet . UserPayload {
Name : ptr . String ( "user1" ) ,
Email : ptr . String ( "email@asd.com" ) ,
Password : ptr . String ( "pass" ) ,
}
resp := s . Do ( "POST" , "/api/v1/fleet/users/admin" , & params , http . StatusUnprocessableEntity )
assertErrorCodeAndMessage ( t , resp , fleet . ErrNoRoleNeeded , "either global role or team role needs to be defined" )
}
func ( s * integrationTestSuite ) TestUserWithWrongRoleErrors ( ) {
t := s . T ( )
params := fleet . UserPayload {
Name : ptr . String ( "user1" ) ,
Email : ptr . String ( "email@asd.com" ) ,
Password : ptr . String ( "pass" ) ,
GlobalRole : ptr . String ( "wrongrole" ) ,
}
resp := s . Do ( "POST" , "/api/v1/fleet/users/admin" , & params , http . StatusUnprocessableEntity )
assertErrorCodeAndMessage ( t , resp , fleet . ErrNoRoleNeeded , "GlobalRole role can only be admin, observer, or maintainer." )
}
func ( s * integrationTestSuite ) TestUserCreationWrongTeamErrors ( ) {
t := s . T ( )
teams := [ ] fleet . UserTeam {
{
Team : fleet . Team {
ID : 9999 ,
} ,
Role : fleet . RoleObserver ,
} ,
}
params := fleet . UserPayload {
Name : ptr . String ( "user2" ) ,
Email : ptr . String ( "email2@asd.com" ) ,
Password : ptr . String ( "pass" ) ,
Teams : & teams ,
}
resp := s . Do ( "POST" , "/api/v1/fleet/users/admin" , & params , http . StatusUnprocessableEntity )
assertBodyContains ( t , resp , ` Error 1452: Cannot add or update a child row: a foreign key constraint fails ` )
}
func ( s * integrationTestSuite ) TestQueryCreationLogsActivity ( ) {
t := s . T ( )
admin1 := s . users [ "admin1@example.com" ]
admin1 . GravatarURL = "http://iii.com"
err := s . ds . SaveUser ( context . Background ( ) , & admin1 )
require . NoError ( t , err )
params := fleet . QueryPayload {
Name : ptr . String ( "user1" ) ,
Query : ptr . String ( "select * from time;" ) ,
}
2022-01-31 21:35:22 +00:00
var createQueryResp createQueryResponse
s . DoJSON ( "POST" , "/api/v1/fleet/queries" , & params , http . StatusOK , & createQueryResp )
defer cleanupQuery ( s , createQueryResp . Query . ID )
2021-09-15 19:27:53 +00:00
activities := listActivitiesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/activities" , nil , http . StatusOK , & activities )
2021-11-24 17:16:42 +00:00
assert . GreaterOrEqual ( t , len ( activities . Activities ) , 1 )
found := false
for _ , activity := range activities . Activities {
if activity . Type == "created_saved_query" {
found = true
assert . Equal ( t , "Test Name admin1@example.com" , activity . ActorFullName )
require . NotNil ( t , activity . ActorGravatar )
assert . Equal ( t , "http://iii.com" , * activity . ActorGravatar )
}
}
require . True ( t , found )
2021-09-15 19:27:53 +00:00
}
2021-11-11 16:45:39 +00:00
2022-02-16 15:33:56 +00:00
func ( s * integrationTestSuite ) TestPolicyDeletionLogsActivity ( ) {
t := s . T ( )
admin1 := s . users [ "admin1@example.com" ]
admin1 . GravatarURL = "http://iii.com"
err := s . ds . SaveUser ( context . Background ( ) , & admin1 )
require . NoError ( t , err )
testPolicies := [ ] fleet . PolicyPayload { {
Name : "policy1" ,
Query : "select * from time;" ,
} , {
Name : "policy2" ,
Query : "select * from osquery_info;" ,
} }
var policyIDs [ ] uint
for _ , policy := range testPolicies {
var resp globalPolicyResponse
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , policy , http . StatusOK , & resp )
policyIDs = append ( policyIDs , resp . Policy . PolicyData . ID )
}
prevActivities := listActivitiesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/activities" , nil , http . StatusOK , & prevActivities )
require . GreaterOrEqual ( t , len ( prevActivities . Activities ) , 2 )
var deletePoliciesResp deleteGlobalPoliciesResponse
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies/delete" , deleteGlobalPoliciesRequest { policyIDs } , http . StatusOK , & deletePoliciesResp )
require . Equal ( t , len ( policyIDs ) , len ( deletePoliciesResp . Deleted ) )
newActivities := listActivitiesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/activities" , nil , http . StatusOK , & newActivities )
require . Equal ( t , len ( newActivities . Activities ) , ( len ( prevActivities . Activities ) + 2 ) )
var prevDeletes [ ] * fleet . Activity
for _ , a := range prevActivities . Activities {
if a . Type == "deleted_policy" {
prevDeletes = append ( prevDeletes , a )
}
}
var newDeletes [ ] * fleet . Activity
for _ , a := range newActivities . Activities {
if a . Type == "deleted_policy" {
newDeletes = append ( newDeletes , a )
}
}
require . Equal ( t , len ( newDeletes ) , ( len ( prevDeletes ) + 2 ) )
type policyDetails struct {
PolicyID uint ` json:"policy_id" `
PolicyName string ` json:"policy_name" `
}
for _ , id := range policyIDs {
found := false
for _ , d := range newDeletes {
var details policyDetails
err := json . Unmarshal ( [ ] byte ( * d . Details ) , & details )
require . NoError ( t , err )
require . NotNil ( t , details . PolicyID )
if id == details . PolicyID {
found = true
}
}
require . True ( t , found )
}
for _ , p := range testPolicies {
found := false
for _ , d := range newDeletes {
var details policyDetails
err := json . Unmarshal ( [ ] byte ( * d . Details ) , & details )
require . NoError ( t , err )
require . NotNil ( t , details . PolicyName )
if p . Name == details . PolicyName {
found = true
}
}
require . True ( t , found )
}
}
2021-09-15 19:27:53 +00:00
func ( s * integrationTestSuite ) TestAppConfigAdditionalQueriesCanBeRemoved ( ) {
t := s . T ( )
spec := [ ] byte ( `
host_expiry_settings :
host_expiry_enabled : true
host_expiry_window : 0
host_settings :
additional_queries :
time : SELECT * FROM time
enable_host_users : true
` )
s . applyConfig ( spec )
spec = [ ] byte ( `
host_settings :
enable_host_users : true
additional_queries : null
` )
s . applyConfig ( spec )
config := s . getConfig ( )
assert . Nil ( t , config . HostSettings . AdditionalQueries )
assert . True ( t , config . HostExpirySettings . HostExpiryEnabled )
}
func ( s * integrationTestSuite ) TestAppConfigDefaultValues ( ) {
config := s . getConfig ( )
s . Run ( "Update interval" , func ( ) {
require . Equal ( s . T ( ) , 1 * time . Hour , config . UpdateInterval . OSQueryDetail )
} )
s . Run ( "has logging" , func ( ) {
require . NotNil ( s . T ( ) , config . Logging )
} )
}
func ( s * integrationTestSuite ) TestUserRolesSpec ( ) {
t := s . T ( )
_ , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
email := t . Name ( ) + "@asd.com"
u := & fleet . User {
Password : [ ] byte ( "asd" ) ,
Name : t . Name ( ) ,
Email : email ,
GravatarURL : "http://asd.com" ,
GlobalRole : ptr . String ( fleet . RoleObserver ) ,
}
user , err := s . ds . NewUser ( context . Background ( ) , u )
require . NoError ( t , err )
assert . Len ( t , user . Teams , 0 )
spec := [ ] byte ( fmt . Sprintf ( `
roles :
% s :
global_role : null
teams :
- role : maintainer
team : team1
` ,
email ) )
var userRoleSpec applyUserRoleSpecsRequest
err = yaml . Unmarshal ( spec , & userRoleSpec . Spec )
require . NoError ( t , err )
s . Do ( "POST" , "/api/v1/fleet/users/roles/spec" , & userRoleSpec , http . StatusOK )
user , err = s . ds . UserByEmail ( context . Background ( ) , email )
require . NoError ( t , err )
require . Len ( t , user . Teams , 1 )
assert . Equal ( t , fleet . RoleMaintainer , user . Teams [ 0 ] . Role )
}
func ( s * integrationTestSuite ) TestGlobalSchedule ( ) {
t := s . T ( )
2021-12-15 14:06:34 +00:00
// list the existing global schedules (none yet)
2021-09-15 19:27:53 +00:00
gs := fleet . GlobalSchedulePayload { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/schedule" , nil , http . StatusOK , & gs )
require . Len ( t , gs . GlobalSchedule , 0 )
2021-12-15 14:06:34 +00:00
// create a query that can be scheduled
2021-09-15 19:27:53 +00:00
qr , err := s . ds . NewQuery ( context . Background ( ) , & fleet . Query {
Name : "TestQuery1" ,
Description : "Some description" ,
Query : "select * from osquery;" ,
ObserverCanRun : true ,
} )
require . NoError ( t , err )
2021-12-15 14:06:34 +00:00
// schedule that query
2021-09-15 19:27:53 +00:00
gsParams := fleet . ScheduledQueryPayload { QueryID : ptr . Uint ( qr . ID ) , Interval : ptr . Uint ( 42 ) }
r := globalScheduleQueryResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/schedule" , gsParams , http . StatusOK , & r )
2021-12-15 14:06:34 +00:00
// list the scheduled queries, get the one just created
2021-09-15 19:27:53 +00:00
gs = fleet . GlobalSchedulePayload { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/schedule" , nil , http . StatusOK , & gs )
require . Len ( t , gs . GlobalSchedule , 1 )
assert . Equal ( t , uint ( 42 ) , gs . GlobalSchedule [ 0 ] . Interval )
assert . Equal ( t , "TestQuery1" , gs . GlobalSchedule [ 0 ] . Name )
id := gs . GlobalSchedule [ 0 ] . ID
2021-12-15 14:06:34 +00:00
// list page 2, should be empty
s . DoJSON ( "GET" , "/api/v1/fleet/global/schedule" , nil , http . StatusOK , & gs , "page" , "2" , "per_page" , "4" )
require . Len ( t , gs . GlobalSchedule , 0 )
// update the scheduled query
2021-09-15 19:27:53 +00:00
gs = fleet . GlobalSchedulePayload { }
gsParams = fleet . ScheduledQueryPayload { Interval : ptr . Uint ( 55 ) }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/global/schedule/%d" , id ) , gsParams , http . StatusOK , & gs )
2021-12-15 14:06:34 +00:00
// update a non-existing schedule
gsParams = fleet . ScheduledQueryPayload { Interval : ptr . Uint ( 66 ) }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/global/schedule/%d" , id + 1 ) , gsParams , http . StatusNotFound , & gs )
// read back that updated scheduled query
2021-09-15 19:27:53 +00:00
gs = fleet . GlobalSchedulePayload { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/schedule" , nil , http . StatusOK , & gs )
require . Len ( t , gs . GlobalSchedule , 1 )
2021-12-15 14:06:34 +00:00
assert . Equal ( t , id , gs . GlobalSchedule [ 0 ] . ID )
2021-09-15 19:27:53 +00:00
assert . Equal ( t , uint ( 55 ) , gs . GlobalSchedule [ 0 ] . Interval )
2021-12-15 14:06:34 +00:00
// delete the scheduled query
2021-09-15 19:27:53 +00:00
r = globalScheduleQueryResponse { }
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/global/schedule/%d" , id ) , nil , http . StatusOK , & r )
2021-12-15 14:06:34 +00:00
// delete a non-existing schedule
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/global/schedule/%d" , id + 1 ) , nil , http . StatusNotFound , & r )
// list the scheduled queries, back to none
2021-09-15 19:27:53 +00:00
gs = fleet . GlobalSchedulePayload { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/schedule" , nil , http . StatusOK , & gs )
require . Len ( t , gs . GlobalSchedule , 0 )
}
func ( s * integrationTestSuite ) TestTranslator ( ) {
t := s . T ( )
payload := translatorResponse { }
params := translatorRequest { List : [ ] fleet . TranslatePayload {
{
Type : fleet . TranslatorTypeUserEmail ,
Payload : fleet . StringIdentifierToIDPayload { Identifier : "admin1@example.com" } ,
} ,
} }
s . DoJSON ( "POST" , "/api/v1/fleet/translate" , & params , http . StatusOK , & payload )
require . Len ( t , payload . List , 1 )
assert . Equal ( t , s . users [ payload . List [ 0 ] . Payload . Identifier ] . ID , payload . List [ 0 ] . Payload . ID )
}
func ( s * integrationTestSuite ) TestVulnerableSoftware ( ) {
t := s . T ( )
host , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
2021-09-27 19:27:38 +00:00
PolicyUpdatedAt : time . Now ( ) ,
2021-09-15 19:27:53 +00:00
SeenTime : time . Now ( ) ,
NodeKey : t . Name ( ) + "1" ,
UUID : t . Name ( ) + "1" ,
2021-10-20 21:01:20 +00:00
Hostname : t . Name ( ) + "foo.local" ,
2021-09-15 19:27:53 +00:00
PrimaryIP : "192.168.1.1" ,
PrimaryMac : "30-65-EC-6F-C4-58" ,
} )
require . NoError ( t , err )
require . NotNil ( t , host )
2022-01-18 01:52:09 +00:00
software := [ ] fleet . Software {
{ Name : "foo" , Version : "0.0.1" , Source : "chrome_extensions" } ,
{ Name : "bar" , Version : "0.0.3" , Source : "apps" } ,
2022-02-07 20:57:55 +00:00
{ Name : "baz" , Version : "0.0.4" , Source : "apps" } ,
2021-09-15 19:27:53 +00:00
}
2022-01-18 01:52:09 +00:00
require . NoError ( t , s . ds . UpdateHostSoftware ( context . Background ( ) , host . ID , software ) )
2021-09-15 19:27:53 +00:00
require . NoError ( t , s . ds . LoadHostSoftware ( context . Background ( ) , host ) )
soft1 := host . Software [ 0 ]
if soft1 . Name != "bar" {
soft1 = host . Software [ 1 ]
}
require . NoError ( t , s . ds . AddCPEForSoftware ( context . Background ( ) , soft1 , "somecpe" ) )
2022-02-02 21:34:37 +00:00
_ , err = s . ds . InsertCVEForCPE ( context . Background ( ) , "cve-123-123-132" , [ ] string { "somecpe" } )
require . NoError ( t , err )
2021-09-15 19:27:53 +00:00
resp := s . Do ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , host . ID ) , nil , http . StatusOK )
bodyBytes , err := ioutil . ReadAll ( resp . Body )
require . NoError ( t , err )
expectedJSONSoft2 := ` "name" : "bar" ,
"version" : "0.0.3" ,
"source" : "apps" ,
"generated_cpe" : "somecpe" ,
"vulnerabilities" : [
{
"cve" : "cve-123-123-132" ,
"details_link" : "https://nvd.nist.gov/vuln/detail/cve-123-123-132"
}
] `
expectedJSONSoft1 := ` "name" : "foo" ,
"version" : "0.0.1" ,
"source" : "chrome_extensions" ,
"generated_cpe" : "" ,
"vulnerabilities" : null `
// We are doing Contains instead of equals to test the output for software in particular
// ignoring other things like timestamps and things that are outside the cope of this ticket
assert . Contains ( t , string ( bodyBytes ) , expectedJSONSoft2 )
assert . Contains ( t , string ( bodyBytes ) , expectedJSONSoft1 )
2021-10-20 21:01:20 +00:00
2021-12-03 13:54:17 +00:00
countReq := countSoftwareRequest { }
countResp := countSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software/count" , countReq , http . StatusOK , & countResp )
2022-02-07 20:57:55 +00:00
assert . Equal ( t , 3 , countResp . Count )
2021-12-03 13:54:17 +00:00
2022-01-26 14:47:56 +00:00
// no software host counts have been calculated yet, so this returns nothing
var lsResp listSoftwareResponse
2022-01-31 22:08:03 +00:00
resp = s . Do ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , "vulnerable" , "true" , "order_key" , "generated_cpe" , "order_direction" , "desc" )
bodyBytes , err = ioutil . ReadAll ( resp . Body )
require . NoError ( t , err )
assert . Contains ( t , string ( bodyBytes ) , ` "counts_updated_at": null ` )
require . NoError ( t , json . Unmarshal ( bodyBytes , & lsResp ) )
2022-01-26 14:47:56 +00:00
require . Len ( t , lsResp . Software , 0 )
2022-01-31 22:08:03 +00:00
assert . Nil ( t , lsResp . CountsUpdatedAt )
2021-11-11 11:49:17 +00:00
2022-01-26 14:47:56 +00:00
// the software/count endpoint is different, it doesn't care about hosts counts
2022-02-07 20:57:55 +00:00
countResp = countSoftwareResponse { }
2021-12-03 13:54:17 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/software/count" , countReq , http . StatusOK , & countResp , "vulnerable" , "true" , "order_key" , "generated_cpe" , "order_direction" , "desc" )
assert . Equal ( t , 1 , countResp . Count )
2022-01-26 14:47:56 +00:00
// calculate hosts counts
hostsCountTs := time . Now ( ) . UTC ( )
require . NoError ( t , s . ds . CalculateHostsPerSoftware ( context . Background ( ) , hostsCountTs ) )
// now the list software endpoint returns the software
2022-02-07 20:57:55 +00:00
lsResp = listSoftwareResponse { }
2022-01-26 14:47:56 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "vulnerable" , "true" , "order_key" , "generated_cpe" , "order_direction" , "desc" )
require . Len ( t , lsResp . Software , 1 )
2021-11-11 11:49:17 +00:00
assert . Equal ( t , soft1 . ID , lsResp . Software [ 0 ] . ID )
assert . Len ( t , lsResp . Software [ 0 ] . Vulnerabilities , 1 )
2022-01-31 22:08:03 +00:00
require . NotNil ( t , lsResp . CountsUpdatedAt )
assert . WithinDuration ( t , hostsCountTs , * lsResp . CountsUpdatedAt , time . Second )
2022-01-26 14:47:56 +00:00
// the count endpoint still returns 1
2022-02-07 20:57:55 +00:00
countResp = countSoftwareResponse { }
2022-01-26 14:47:56 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/software/count" , countReq , http . StatusOK , & countResp , "vulnerable" , "true" , "order_key" , "generated_cpe" , "order_direction" , "desc" )
assert . Equal ( t , 1 , countResp . Count )
// default sort, not only vulnerable
2022-02-07 20:57:55 +00:00
lsResp = listSoftwareResponse { }
2022-01-26 14:47:56 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp )
require . True ( t , len ( lsResp . Software ) >= len ( software ) )
2022-01-31 22:08:03 +00:00
require . NotNil ( t , lsResp . CountsUpdatedAt )
assert . WithinDuration ( t , hostsCountTs , * lsResp . CountsUpdatedAt , time . Second )
2022-02-07 20:57:55 +00:00
// request with a per_page limit (see #4058)
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "page" , "0" , "per_page" , "2" , "order_key" , "hosts_count" , "order_direction" , "desc" )
require . Len ( t , lsResp . Software , 2 )
require . NotNil ( t , lsResp . CountsUpdatedAt )
assert . WithinDuration ( t , hostsCountTs , * lsResp . CountsUpdatedAt , time . Second )
// request next page, with per_page limit
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "2" , "page" , "1" , "order_key" , "hosts_count" , "order_direction" , "desc" )
require . Len ( t , lsResp . Software , 1 )
require . NotNil ( t , lsResp . CountsUpdatedAt )
assert . WithinDuration ( t , hostsCountTs , * lsResp . CountsUpdatedAt , time . Second )
// request one past the last page
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "2" , "page" , "2" , "order_key" , "hosts_count" , "order_direction" , "desc" )
require . Len ( t , lsResp . Software , 0 )
require . Nil ( t , lsResp . CountsUpdatedAt )
2021-09-15 19:27:53 +00:00
}
func ( s * integrationTestSuite ) TestGlobalPolicies ( ) {
t := s . T ( )
for i := 0 ; i < 3 ; i ++ {
_ , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
2021-09-27 19:27:38 +00:00
PolicyUpdatedAt : time . Now ( ) ,
2021-09-15 19:27:53 +00:00
SeenTime : time . Now ( ) . Add ( - time . Duration ( i ) * time . Minute ) ,
2021-09-29 16:13:23 +00:00
OsqueryHostID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
NodeKey : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
UUID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
Hostname : fmt . Sprintf ( "%sfoo.local%d" , t . Name ( ) , i ) ,
2021-09-15 19:27:53 +00:00
} )
require . NoError ( t , err )
}
qr , err := s . ds . NewQuery ( context . Background ( ) , & fleet . Query {
Name : "TestQuery3" ,
Description : "Some description" ,
Query : "select * from osquery;" ,
ObserverCanRun : true ,
} )
require . NoError ( t , err )
2021-11-24 17:16:42 +00:00
gpParams := globalPolicyRequest {
QueryID : & qr . ID ,
Resolution : "some global resolution" ,
}
2021-09-15 19:27:53 +00:00
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams , http . StatusOK , & gpResp )
require . NotNil ( t , gpResp . Policy )
2021-11-24 17:16:42 +00:00
assert . Equal ( t , qr . Name , gpResp . Policy . Name )
assert . Equal ( t , qr . Query , gpResp . Policy . Query )
assert . Equal ( t , qr . Description , gpResp . Policy . Description )
2021-10-28 13:10:03 +00:00
require . NotNil ( t , gpResp . Policy . Resolution )
assert . Equal ( t , "some global resolution" , * gpResp . Policy . Resolution )
2021-09-15 19:27:53 +00:00
policiesResponse := listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/policies" , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 1 )
2021-11-24 17:16:42 +00:00
assert . Equal ( t , qr . Name , policiesResponse . Policies [ 0 ] . Name )
assert . Equal ( t , qr . Query , policiesResponse . Policies [ 0 ] . Query )
assert . Equal ( t , qr . Description , policiesResponse . Policies [ 0 ] . Description )
2021-09-15 19:27:53 +00:00
2022-01-12 13:04:16 +00:00
// Get an unexistent policy
s . Do ( "GET" , fmt . Sprintf ( "/api/v1/fleet/global/policies/%d" , 9999 ) , nil , http . StatusNotFound )
2021-09-15 19:27:53 +00:00
singlePolicyResponse := getPolicyByIDResponse { }
singlePolicyURL := fmt . Sprintf ( "/api/v1/fleet/global/policies/%d" , policiesResponse . Policies [ 0 ] . ID )
s . DoJSON ( "GET" , singlePolicyURL , nil , http . StatusOK , & singlePolicyResponse )
2021-11-24 17:16:42 +00:00
assert . Equal ( t , qr . Name , singlePolicyResponse . Policy . Name )
assert . Equal ( t , qr . Query , singlePolicyResponse . Policy . Query )
assert . Equal ( t , qr . Description , singlePolicyResponse . Policy . Description )
2021-09-15 19:27:53 +00:00
listHostsURL := fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp := listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 3 )
h1 := listHostsResp . Hosts [ 0 ]
h2 := listHostsResp . Hosts [ 1 ]
listHostsURL = fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d&policy_response=passing" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp = listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 0 )
2021-11-08 14:42:37 +00:00
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , h1 . Host , map [ uint ] * bool { policiesResponse . Policies [ 0 ] . ID : ptr . Bool ( true ) } , time . Now ( ) , false ) )
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , h2 . Host , map [ uint ] * bool { policiesResponse . Policies [ 0 ] . ID : nil } , time . Now ( ) , false ) )
2021-09-15 19:27:53 +00:00
listHostsURL = fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d&policy_response=passing" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp = listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 1 )
deletePolicyParams := deleteGlobalPoliciesRequest { IDs : [ ] uint { policiesResponse . Policies [ 0 ] . ID } }
deletePolicyResp := deleteGlobalPoliciesResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies/delete" , deletePolicyParams , http . StatusOK , & deletePolicyResp )
policiesResponse = listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/policies" , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 0 )
}
2021-09-29 16:13:23 +00:00
func ( s * integrationTestSuite ) TestBulkDeleteHostsFromTeam ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team { Name : t . Name ( ) + "team1" } )
require . NoError ( t , err )
2022-01-19 13:28:08 +00:00
p , err := s . ds . NewPack ( context . Background ( ) , & fleet . Pack {
Name : t . Name ( ) ,
Hosts : [ ] fleet . Target {
{
Type : fleet . TargetHost ,
TargetID : hosts [ 0 ] . ID ,
} ,
} ,
} )
require . NoError ( t , err )
2021-09-29 16:13:23 +00:00
require . NoError ( t , s . ds . AddHostsToTeam ( context . Background ( ) , & team1 . ID , [ ] uint { hosts [ 0 ] . ID } ) )
req := deleteHostsRequest {
Filters : struct {
MatchQuery string ` json:"query" `
Status fleet . HostStatus ` json:"status" `
LabelID * uint ` json:"label_id" `
TeamID * uint ` json:"team_id" `
} { TeamID : ptr . Uint ( team1 . ID ) } ,
}
resp := deleteHostsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/hosts/delete" , req , http . StatusOK , & resp )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 0 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . Error ( t , err )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 1 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . NoError ( t , err )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 2 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . NoError ( t , err )
err = s . ds . DeleteHosts ( context . Background ( ) , [ ] uint { hosts [ 1 ] . ID , hosts [ 2 ] . ID } )
require . NoError ( t , err )
2022-01-19 13:28:08 +00:00
newP , err := s . ds . Pack ( context . Background ( ) , p . ID )
require . NoError ( t , err )
require . Empty ( t , newP . Hosts )
require . NoError ( t , s . ds . DeletePack ( context . Background ( ) , newP . Name ) )
2021-09-29 16:13:23 +00:00
}
func ( s * integrationTestSuite ) TestBulkDeleteHostsInLabel ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
label := & fleet . Label {
Name : "foo" ,
Query : "select * from foo;" ,
}
label , err := s . ds . NewLabel ( context . Background ( ) , label )
require . NoError ( t , err )
2021-11-08 14:42:37 +00:00
require . NoError ( t , s . ds . RecordLabelQueryExecutions ( context . Background ( ) , hosts [ 1 ] , map [ uint ] * bool { label . ID : ptr . Bool ( true ) } , time . Now ( ) , false ) )
require . NoError ( t , s . ds . RecordLabelQueryExecutions ( context . Background ( ) , hosts [ 2 ] , map [ uint ] * bool { label . ID : ptr . Bool ( true ) } , time . Now ( ) , false ) )
2021-09-29 16:13:23 +00:00
req := deleteHostsRequest {
Filters : struct {
MatchQuery string ` json:"query" `
Status fleet . HostStatus ` json:"status" `
LabelID * uint ` json:"label_id" `
TeamID * uint ` json:"team_id" `
} { LabelID : ptr . Uint ( label . ID ) } ,
}
resp := deleteHostsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/hosts/delete" , req , http . StatusOK , & resp )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 0 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . NoError ( t , err )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 1 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . Error ( t , err )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 2 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . Error ( t , err )
err = s . ds . DeleteHosts ( context . Background ( ) , [ ] uint { hosts [ 0 ] . ID } )
require . NoError ( t , err )
}
func ( s * integrationTestSuite ) TestBulkDeleteHostByIDs ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
req := deleteHostsRequest {
IDs : [ ] uint { hosts [ 0 ] . ID , hosts [ 1 ] . ID } ,
}
resp := deleteHostsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/hosts/delete" , req , http . StatusOK , & resp )
2021-11-18 17:36:35 +00:00
_ , err := s . ds . Host ( context . Background ( ) , hosts [ 0 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . Error ( t , err )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 1 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . Error ( t , err )
2021-11-18 17:36:35 +00:00
_ , err = s . ds . Host ( context . Background ( ) , hosts [ 2 ] . ID , false )
2021-09-29 16:13:23 +00:00
require . NoError ( t , err )
err = s . ds . DeleteHosts ( context . Background ( ) , [ ] uint { hosts [ 2 ] . ID } )
require . NoError ( t , err )
}
func ( s * integrationTestSuite ) createHosts ( t * testing . T ) [ ] * fleet . Host {
var hosts [ ] * fleet . Host
2022-02-07 16:50:36 +00:00
platforms := [ ] string { "debian" , "rhel" , "linux" }
2021-09-29 16:13:23 +00:00
for i := 0 ; i < 3 ; i ++ {
host , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - time . Duration ( i ) * time . Minute ) ,
OsqueryHostID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
NodeKey : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
2021-12-14 21:34:11 +00:00
UUID : uuid . New ( ) . String ( ) ,
2021-09-29 16:13:23 +00:00
Hostname : fmt . Sprintf ( "%sfoo.local%d" , t . Name ( ) , i ) ,
2022-02-07 16:50:36 +00:00
Platform : platforms [ i ] ,
2021-09-29 16:13:23 +00:00
} )
require . NoError ( t , err )
hosts = append ( hosts , host )
}
return hosts
}
func ( s * integrationTestSuite ) TestBulkDeleteHostsErrors ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
req := deleteHostsRequest {
IDs : [ ] uint { hosts [ 0 ] . ID , hosts [ 1 ] . ID } ,
Filters : struct {
MatchQuery string ` json:"query" `
Status fleet . HostStatus ` json:"status" `
LabelID * uint ` json:"label_id" `
TeamID * uint ` json:"team_id" `
} { LabelID : ptr . Uint ( 1 ) } ,
}
resp := deleteHostsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/hosts/delete" , req , http . StatusBadRequest , & resp )
}
2021-10-07 11:25:35 +00:00
func ( s * integrationTestSuite ) TestCountSoftware ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
label := & fleet . Label {
Name : t . Name ( ) + "foo" ,
Query : "select * from foo;" ,
}
label , err := s . ds . NewLabel ( context . Background ( ) , label )
require . NoError ( t , err )
2021-11-08 14:42:37 +00:00
require . NoError ( t , s . ds . RecordLabelQueryExecutions ( context . Background ( ) , hosts [ 0 ] , map [ uint ] * bool { label . ID : ptr . Bool ( true ) } , time . Now ( ) , false ) )
2021-10-07 11:25:35 +00:00
req := countHostsRequest { }
resp := countHostsResponse { }
s . DoJSON (
"GET" , "/api/v1/fleet/hosts/count" , req , http . StatusOK , & resp ,
"additional_info_filters" , "*" ,
)
assert . Equal ( t , 3 , resp . Count )
req = countHostsRequest { }
resp = countHostsResponse { }
s . DoJSON (
"GET" , "/api/v1/fleet/hosts/count" , req , http . StatusOK , & resp ,
"additional_info_filters" , "*" ,
"label_id" , fmt . Sprint ( label . ID ) ,
)
assert . Equal ( t , 1 , resp . Count )
}
2021-10-11 14:17:21 +00:00
2021-12-15 14:35:40 +00:00
func ( s * integrationTestSuite ) TestPacks ( ) {
2021-10-11 14:17:21 +00:00
t := s . T ( )
var packResp getPackResponse
2021-12-15 14:35:40 +00:00
// get non-existing pack
s . Do ( "GET" , "/api/v1/fleet/packs/999" , nil , http . StatusNotFound )
// create some packs
packs := make ( [ ] fleet . Pack , 3 )
for i := range packs {
req := & createPackRequest {
PackPayload : fleet . PackPayload {
Name : ptr . String ( fmt . Sprintf ( "%s_%d" , strings . ReplaceAll ( t . Name ( ) , "/" , "_" ) , i ) ) ,
} ,
}
var createResp createPackResponse
s . DoJSON ( "POST" , "/api/v1/fleet/packs" , req , http . StatusOK , & createResp )
packs [ i ] = createResp . Pack . Pack
}
2021-10-11 14:17:21 +00:00
2021-12-15 14:35:40 +00:00
// get existing pack
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/packs/%d" , packs [ 0 ] . ID ) , nil , http . StatusOK , & packResp )
require . Equal ( t , packs [ 0 ] . ID , packResp . Pack . ID )
// list packs
var listResp listPacksResponse
s . DoJSON ( "GET" , "/api/v1/fleet/packs" , nil , http . StatusOK , & listResp , "per_page" , "2" , "order_key" , "name" )
require . Len ( t , listResp . Packs , 2 )
assert . Equal ( t , packs [ 0 ] . ID , listResp . Packs [ 0 ] . ID )
assert . Equal ( t , packs [ 1 ] . ID , listResp . Packs [ 1 ] . ID )
// get page 1
s . DoJSON ( "GET" , "/api/v1/fleet/packs" , nil , http . StatusOK , & listResp , "page" , "1" , "per_page" , "2" , "order_key" , "name" )
require . Len ( t , listResp . Packs , 1 )
assert . Equal ( t , packs [ 2 ] . ID , listResp . Packs [ 0 ] . ID )
// get page 2, empty
s . DoJSON ( "GET" , "/api/v1/fleet/packs" , nil , http . StatusOK , & listResp , "page" , "2" , "per_page" , "2" , "order_key" , "name" )
require . Len ( t , listResp . Packs , 0 )
var delResp deletePackResponse
// delete non-existing pack by name
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/packs/%s" , "zzz" ) , nil , http . StatusNotFound , & delResp )
// delete existing pack by name
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/packs/%s" , url . PathEscape ( packs [ 0 ] . Name ) ) , nil , http . StatusOK , & delResp )
// delete non-existing pack by id
var delIDResp deletePackByIDResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/packs/id/%d" , packs [ 2 ] . ID + 1 ) , nil , http . StatusNotFound , & delIDResp )
// delete existing pack by id
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/packs/id/%d" , packs [ 1 ] . ID ) , nil , http . StatusOK , & delIDResp )
var modResp modifyPackResponse
// modify non-existing pack
req := & fleet . PackPayload { Name : ptr . String ( "updated_" + packs [ 2 ] . Name ) }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/packs/%d" , packs [ 2 ] . ID + 1 ) , req , http . StatusNotFound , & modResp )
// modify existing pack
req = & fleet . PackPayload { Name : ptr . String ( "updated_" + packs [ 2 ] . Name ) }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/packs/%d" , packs [ 2 ] . ID ) , req , http . StatusOK , & modResp )
require . Equal ( t , packs [ 2 ] . ID , modResp . Pack . ID )
require . Contains ( t , modResp . Pack . Name , "updated_" )
// list packs, only packs[2] remains
s . DoJSON ( "GET" , "/api/v1/fleet/packs" , nil , http . StatusOK , & listResp , "per_page" , "2" , "order_key" , "name" )
require . Len ( t , listResp . Packs , 1 )
assert . Equal ( t , packs [ 2 ] . ID , listResp . Packs [ 0 ] . ID )
2021-10-11 14:17:21 +00:00
}
2021-10-11 14:37:48 +00:00
func ( s * integrationTestSuite ) TestListHosts ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
var resp listHostsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & resp )
require . Len ( t , resp . Hosts , len ( hosts ) )
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & resp , "per_page" , "1" )
require . Len ( t , resp . Hosts , 1 )
2021-10-12 18:59:01 +00:00
assert . Nil ( t , resp . Software )
2021-10-12 14:38:12 +00:00
2021-11-29 18:06:00 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & resp , "order_key" , "h.id" , "after" , fmt . Sprint ( hosts [ 1 ] . ID ) )
require . Len ( t , resp . Hosts , len ( hosts ) - 2 )
2021-10-12 14:38:12 +00:00
host := hosts [ 2 ]
2022-01-18 01:52:09 +00:00
software := [ ] fleet . Software {
{ Name : "foo" , Version : "0.0.1" , Source : "chrome_extensions" } ,
2021-10-12 14:38:12 +00:00
}
2022-01-18 01:52:09 +00:00
require . NoError ( t , s . ds . UpdateHostSoftware ( context . Background ( ) , host . ID , software ) )
2021-10-12 14:38:12 +00:00
require . NoError ( t , s . ds . LoadHostSoftware ( context . Background ( ) , host ) )
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & resp , "software_id" , fmt . Sprint ( host . Software [ 0 ] . ID ) )
require . Len ( t , resp . Hosts , 1 )
assert . Equal ( t , host . ID , resp . Hosts [ 0 ] . ID )
2021-10-12 18:59:01 +00:00
assert . Equal ( t , "foo" , resp . Software . Name )
2021-10-15 10:34:30 +00:00
2021-11-24 17:16:42 +00:00
user1 := test . NewUser ( t , s . ds , "Alice" , "alice@example.com" , true )
2021-10-15 10:34:30 +00:00
q := test . NewQuery ( t , s . ds , "query1" , "select 1" , 0 , true )
2022-01-31 21:35:22 +00:00
defer cleanupQuery ( s , q . ID )
2021-11-24 17:16:42 +00:00
p , err := s . ds . NewGlobalPolicy ( context . Background ( ) , & user1 . ID , fleet . PolicyPayload {
QueryID : & q . ID ,
} )
2021-10-15 10:34:30 +00:00
require . NoError ( t , err )
2021-11-08 14:42:37 +00:00
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , host , map [ uint ] * bool { p . ID : ptr . Bool ( false ) } , time . Now ( ) , false ) )
2021-10-15 10:34:30 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & resp , "software_id" , fmt . Sprint ( host . Software [ 0 ] . ID ) )
require . Len ( t , resp . Hosts , 1 )
assert . Equal ( t , 1 , resp . Hosts [ 0 ] . HostIssues . FailingPoliciesCount )
assert . Equal ( t , 1 , resp . Hosts [ 0 ] . HostIssues . TotalIssuesCount )
2021-11-29 21:04:33 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & resp , "software_id" , fmt . Sprint ( host . Software [ 0 ] . ID ) , "disable_failing_policies" , "true" )
require . Len ( t , resp . Hosts , 1 )
assert . Equal ( t , 0 , resp . Hosts [ 0 ] . HostIssues . FailingPoliciesCount )
assert . Equal ( t , 0 , resp . Hosts [ 0 ] . HostIssues . TotalIssuesCount )
2021-10-11 14:37:48 +00:00
}
2021-11-09 14:35:36 +00:00
2021-11-11 20:33:06 +00:00
func ( s * integrationTestSuite ) TestInvites ( ) {
t := s . T ( )
team , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
Name : t . Name ( ) + "team1" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
2022-02-15 20:22:19 +00:00
// list invites, none yet
var listResp listInvitesResponse
s . DoJSON ( "GET" , "/api/v1/fleet/invites" , nil , http . StatusOK , & listResp )
require . Len ( t , listResp . Invites , 0 )
// create valid invite
createInviteReq := createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : ptr . String ( "some email" ) ,
Name : ptr . String ( "some name" ) ,
GlobalRole : null . StringFrom ( fleet . RoleAdmin ) ,
} }
2021-11-11 20:33:06 +00:00
createInviteResp := createInviteResponse { }
2022-02-15 20:22:19 +00:00
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusOK , & createInviteResp )
2021-11-11 20:33:06 +00:00
require . NotNil ( t , createInviteResp . Invite )
require . NotZero ( t , createInviteResp . Invite . ID )
2022-02-15 20:22:19 +00:00
validInvite := * createInviteResp . Invite
2021-11-11 20:33:06 +00:00
2022-03-08 16:27:38 +00:00
// create user from valid invite - the token was not returned via the
// response's json, must get it from the db
inv , err := s . ds . Invite ( context . Background ( ) , validInvite . ID )
require . NoError ( t , err )
validInviteToken := inv . Token
// verify the token with valid invite
var verifyInvResp verifyInviteResponse
s . DoJSON ( "GET" , "/api/v1/fleet/invites/" + validInviteToken , nil , http . StatusOK , & verifyInvResp )
require . Equal ( t , validInvite . ID , verifyInvResp . Invite . ID )
// verify the token with an invalid invite
s . DoJSON ( "GET" , "/api/v1/fleet/invites/invalid" , nil , http . StatusNotFound , & verifyInvResp )
2022-02-15 20:22:19 +00:00
// create invite without an email
createInviteReq = createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : nil ,
Name : ptr . String ( "some other name" ) ,
GlobalRole : null . StringFrom ( fleet . RoleObserver ) ,
} }
createInviteResp = createInviteResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusUnprocessableEntity , & createInviteResp )
// create invite for an existing user
existingEmail := "admin1@example.com"
createInviteReq = createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : ptr . String ( existingEmail ) ,
Name : ptr . String ( "some other name" ) ,
GlobalRole : null . StringFrom ( fleet . RoleObserver ) ,
} }
createInviteResp = createInviteResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusUnprocessableEntity , & createInviteResp )
// create invite for an existing user with email ALL CAPS
createInviteReq = createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : ptr . String ( strings . ToUpper ( existingEmail ) ) ,
Name : ptr . String ( "some other name" ) ,
GlobalRole : null . StringFrom ( fleet . RoleObserver ) ,
} }
createInviteResp = createInviteResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusUnprocessableEntity , & createInviteResp )
// list invites, we have one now
listResp = listInvitesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/invites" , nil , http . StatusOK , & listResp )
require . Len ( t , listResp . Invites , 1 )
require . Equal ( t , validInvite . ID , listResp . Invites [ 0 ] . ID )
// list invites, next page is empty
listResp = listInvitesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/invites" , nil , http . StatusOK , & listResp , "page" , "1" , "per_page" , "2" )
require . Len ( t , listResp . Invites , 0 )
// update a non-existing invite
updateInviteReq := updateInviteRequest { InvitePayload : fleet . InvitePayload {
Teams : [ ] fleet . UserTeam {
{ Team : fleet . Team { ID : team . ID } , Role : fleet . RoleObserver } ,
} } }
2021-11-11 20:33:06 +00:00
updateInviteResp := updateInviteResponse { }
2022-02-15 20:22:19 +00:00
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , validInvite . ID + 1 ) , updateInviteReq , http . StatusNotFound , & updateInviteResp )
2021-11-11 20:33:06 +00:00
2022-02-15 20:22:19 +00:00
// update the valid invite created earlier, make it an observer of a team
updateInviteResp = updateInviteResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , validInvite . ID ) , updateInviteReq , http . StatusOK , & updateInviteResp )
2022-02-28 12:34:44 +00:00
// update the valid invite: set an email that already exists for a user
updateInviteReq = updateInviteRequest {
InvitePayload : fleet . InvitePayload {
Email : ptr . String ( s . users [ "admin1@example.com" ] . Email ) ,
Teams : [ ] fleet . UserTeam {
{ Team : fleet . Team { ID : team . ID } , Role : fleet . RoleObserver } ,
} ,
} ,
}
updateInviteResp = updateInviteResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , validInvite . ID ) , updateInviteReq , http . StatusConflict , & updateInviteResp )
// update the valid invite: set an email that already exists for another invite
createInviteReq = createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : ptr . String ( "some@other.email" ) ,
Name : ptr . String ( "some name" ) ,
GlobalRole : null . StringFrom ( fleet . RoleAdmin ) ,
} }
createInviteResp = createInviteResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusOK , & createInviteResp )
updateInviteReq = updateInviteRequest {
InvitePayload : fleet . InvitePayload {
Email : createInviteReq . Email ,
Teams : [ ] fleet . UserTeam {
{ Team : fleet . Team { ID : team . ID } , Role : fleet . RoleObserver } ,
} ,
} ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , validInvite . ID ) , updateInviteReq , http . StatusConflict , & updateInviteResp )
// update the valid invite to an email that is ok
updateInviteReq = updateInviteRequest {
InvitePayload : fleet . InvitePayload {
Email : ptr . String ( "something@nonexistent.yet123" ) ,
Teams : [ ] fleet . UserTeam {
{ Team : fleet . Team { ID : team . ID } , Role : fleet . RoleObserver } ,
} ,
} ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , validInvite . ID ) , updateInviteReq , http . StatusOK , & updateInviteResp )
2022-02-15 20:22:19 +00:00
verify , err := s . ds . Invite ( context . Background ( ) , validInvite . ID )
2021-11-11 20:33:06 +00:00
require . NoError ( t , err )
require . Equal ( t , "" , verify . GlobalRole . String )
require . Len ( t , verify . Teams , 1 )
assert . Equal ( t , team . ID , verify . Teams [ 0 ] . ID )
2022-02-15 20:22:19 +00:00
2022-03-08 16:27:38 +00:00
var createFromInviteResp createUserResponse
s . DoJSON ( "POST" , "/api/v1/fleet/users" , fleet . UserPayload {
Name : ptr . String ( "Full Name" ) ,
Password : ptr . String ( "pass1word!" ) ,
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( validInviteToken ) ,
} , http . StatusOK , & createFromInviteResp )
// keep the invite token from the other valid invite (before deleting it)
inv , err = s . ds . Invite ( context . Background ( ) , createInviteResp . Invite . ID )
require . NoError ( t , err )
deletedInviteToken := inv . Token
2022-02-15 20:22:19 +00:00
// delete an existing invite
var delResp deleteInviteResponse
2022-02-28 12:34:44 +00:00
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , createInviteResp . Invite . ID ) , nil , http . StatusOK , & delResp )
2022-02-15 20:22:19 +00:00
// list invites, is now empty
listResp = listInvitesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/invites" , nil , http . StatusOK , & listResp )
require . Len ( t , listResp . Invites , 0 )
// delete a now non-existing invite
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , validInvite . ID ) , nil , http . StatusNotFound , & delResp )
2022-03-08 16:27:38 +00:00
// create user from never used but deleted invite
s . DoJSON ( "POST" , "/api/v1/fleet/users" , fleet . UserPayload {
Name : ptr . String ( "Full Name" ) ,
Password : ptr . String ( "pass1word!" ) ,
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( deletedInviteToken ) ,
} , http . StatusNotFound , & createFromInviteResp )
}
func ( s * integrationTestSuite ) TestCreateUserFromInviteErrors ( ) {
t := s . T ( )
// create a valid invite
createInviteReq := createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : ptr . String ( "a@b.c" ) ,
Name : ptr . String ( "A" ) ,
GlobalRole : null . StringFrom ( fleet . RoleObserver ) ,
} }
createInviteResp := createInviteResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusOK , & createInviteResp )
// make sure to delete it on exit
defer func ( ) {
var delResp deleteInviteResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/invites/%d" , createInviteResp . Invite . ID ) , nil , http . StatusOK , & delResp )
} ( )
// the token is not returned via the response's json, must get it from the db
invite , err := s . ds . Invite ( context . Background ( ) , createInviteResp . Invite . ID )
require . NoError ( t , err )
cases := [ ] struct {
desc string
pld fleet . UserPayload
want int
} {
{
"empty name" ,
fleet . UserPayload {
Name : ptr . String ( "" ) ,
Password : ptr . String ( "pass1word!" ) ,
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( invite . Token ) ,
} ,
http . StatusUnprocessableEntity ,
} ,
{
"empty email" ,
fleet . UserPayload {
Name : ptr . String ( "Name" ) ,
Password : ptr . String ( "pass1word!" ) ,
Email : ptr . String ( "" ) ,
InviteToken : ptr . String ( invite . Token ) ,
} ,
http . StatusUnprocessableEntity ,
} ,
{
"empty password" ,
fleet . UserPayload {
Name : ptr . String ( "Name" ) ,
Password : ptr . String ( "" ) ,
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( invite . Token ) ,
} ,
http . StatusUnprocessableEntity ,
} ,
{
"empty token" ,
fleet . UserPayload {
Name : ptr . String ( "Name" ) ,
Password : ptr . String ( "pass1word!" ) ,
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( "" ) ,
} ,
http . StatusUnprocessableEntity ,
} ,
{
"invalid token" ,
fleet . UserPayload {
Name : ptr . String ( "Name" ) ,
Password : ptr . String ( "pass1word!" ) ,
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( "invalid" ) ,
} ,
http . StatusNotFound ,
} ,
{
"invalid password" ,
fleet . UserPayload {
Name : ptr . String ( "Name" ) ,
Password : ptr . String ( "password" ) , // no number or symbol
Email : ptr . String ( "a@b.c" ) ,
InviteToken : ptr . String ( invite . Token ) ,
} ,
http . StatusUnprocessableEntity ,
} ,
}
for _ , c := range cases {
t . Run ( c . desc , func ( t * testing . T ) {
var resp createUserResponse
s . DoJSON ( "POST" , "/api/v1/fleet/users" , c . pld , c . want , & resp )
} )
}
2021-11-11 20:33:06 +00:00
}
2021-11-09 14:35:36 +00:00
func ( s * integrationTestSuite ) TestGetHostSummary ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team { Name : t . Name ( ) + "team1" } )
require . NoError ( t , err )
team2 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team { Name : t . Name ( ) + "team2" } )
require . NoError ( t , err )
require . NoError ( t , s . ds . AddHostsToTeam ( context . Background ( ) , & team1 . ID , [ ] uint { hosts [ 0 ] . ID } ) )
var resp getHostSummaryResponse
// no team filter
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp )
require . Equal ( t , resp . TotalsHostsCount , uint ( len ( hosts ) ) )
2022-02-07 16:50:36 +00:00
require . Len ( t , resp . Platforms , 3 )
gotPlatforms , wantPlatforms := make ( [ ] string , 3 ) , [ ] string { "linux" , "debian" , "rhel" }
for i , p := range resp . Platforms {
gotPlatforms [ i ] = p . Platform
// each platform has a count of 1
require . Equal ( t , uint ( 1 ) , p . HostsCount )
}
require . ElementsMatch ( t , wantPlatforms , gotPlatforms )
2021-11-15 14:56:13 +00:00
require . Nil ( t , resp . TeamID )
2021-11-09 14:35:36 +00:00
// team filter, no host
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp , "team_id" , fmt . Sprint ( team2 . ID ) )
require . Equal ( t , resp . TotalsHostsCount , uint ( 0 ) )
require . Len ( t , resp . Platforms , 0 )
2021-11-15 14:56:13 +00:00
require . Equal ( t , team2 . ID , * resp . TeamID )
2021-11-09 14:35:36 +00:00
// team filter, one host
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp , "team_id" , fmt . Sprint ( team1 . ID ) )
require . Equal ( t , resp . TotalsHostsCount , uint ( 1 ) )
require . Len ( t , resp . Platforms , 1 )
2022-02-07 16:50:36 +00:00
require . Equal ( t , "debian" , resp . Platforms [ 0 ] . Platform )
2021-11-09 14:35:36 +00:00
require . Equal ( t , uint ( 1 ) , resp . Platforms [ 0 ] . HostsCount )
2021-11-15 14:56:13 +00:00
require . Equal ( t , team1 . ID , * resp . TeamID )
2022-01-24 17:49:21 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp , "team_id" , fmt . Sprint ( team1 . ID ) , "platform" , "linux" )
require . Equal ( t , resp . TotalsHostsCount , uint ( 1 ) )
2022-02-07 16:50:36 +00:00
require . Equal ( t , "debian" , resp . Platforms [ 0 ] . Platform )
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp , "platform" , "rhel" )
require . Equal ( t , resp . TotalsHostsCount , uint ( 1 ) )
require . Equal ( t , "rhel" , resp . Platforms [ 0 ] . Platform )
2022-01-24 17:49:21 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp , "platform" , "linux" )
require . Equal ( t , resp . TotalsHostsCount , uint ( 3 ) )
2022-02-07 16:50:36 +00:00
require . Len ( t , resp . Platforms , 3 )
for i , p := range resp . Platforms {
gotPlatforms [ i ] = p . Platform
// each platform has a count of 1
require . Equal ( t , uint ( 1 ) , p . HostsCount )
}
require . ElementsMatch ( t , wantPlatforms , gotPlatforms )
2022-01-24 17:49:21 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/host_summary" , nil , http . StatusOK , & resp , "platform" , "darwin" )
require . Equal ( t , resp . TotalsHostsCount , uint ( 0 ) )
2022-02-07 16:50:36 +00:00
require . Len ( t , resp . Platforms , 0 )
2021-11-09 14:35:36 +00:00
}
2021-11-24 17:16:42 +00:00
func ( s * integrationTestSuite ) TestGlobalPoliciesProprietary ( ) {
t := s . T ( )
for i := 0 ; i < 3 ; i ++ {
_ , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - time . Duration ( i ) * time . Minute ) ,
OsqueryHostID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
NodeKey : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
UUID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
Hostname : fmt . Sprintf ( "%sfoo.local%d" , t . Name ( ) , i ) ,
2021-12-03 18:33:33 +00:00
Platform : "darwin" ,
2021-11-24 17:16:42 +00:00
} )
require . NoError ( t , err )
}
qr , err := s . ds . NewQuery ( context . Background ( ) , & fleet . Query {
Name : "TestQuery321" ,
Description : "Some description" ,
Query : "select * from osquery;" ,
ObserverCanRun : true ,
} )
require . NoError ( t , err )
// Cannot set both QueryID and Query.
gpParams0 := globalPolicyRequest {
QueryID : & qr . ID ,
Query : "select * from osquery;" ,
}
gpResp0 := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams0 , http . StatusBadRequest , & gpResp0 )
require . Nil ( t , gpResp0 . Policy )
gpParams := globalPolicyRequest {
Name : "TestQuery3" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some global resolution" ,
2021-12-10 16:55:49 +00:00
Platform : "darwin" ,
2021-11-24 17:16:42 +00:00
}
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams , http . StatusOK , & gpResp )
require . NotNil ( t , gpResp . Policy )
require . NotEmpty ( t , gpResp . Policy . ID )
assert . Equal ( t , "TestQuery3" , gpResp . Policy . Name )
assert . Equal ( t , "select * from osquery;" , gpResp . Policy . Query )
assert . Equal ( t , "Some description" , gpResp . Policy . Description )
require . NotNil ( t , gpResp . Policy . Resolution )
assert . Equal ( t , "some global resolution" , * gpResp . Policy . Resolution )
assert . NotNil ( t , gpResp . Policy . AuthorID )
assert . Equal ( t , "Test Name admin1@example.com" , gpResp . Policy . AuthorName )
assert . Equal ( t , "admin1@example.com" , gpResp . Policy . AuthorEmail )
2021-12-06 16:56:28 +00:00
assert . Equal ( t , "darwin" , gpResp . Policy . Platform )
2021-11-24 17:16:42 +00:00
mgpParams := modifyGlobalPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
Name : ptr . String ( "TestQuery4" ) ,
Query : ptr . String ( "select * from osquery_info;" ) ,
Description : ptr . String ( "Some description updated" ) ,
Resolution : ptr . String ( "some global resolution updated" ) ,
} ,
}
mgpResp := modifyGlobalPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/global/policies/%d" , gpResp . Policy . ID ) , mgpParams , http . StatusOK , & mgpResp )
require . NotNil ( t , gpResp . Policy )
assert . Equal ( t , "TestQuery4" , mgpResp . Policy . Name )
assert . Equal ( t , "select * from osquery_info;" , mgpResp . Policy . Query )
assert . Equal ( t , "Some description updated" , mgpResp . Policy . Description )
require . NotNil ( t , mgpResp . Policy . Resolution )
assert . Equal ( t , "some global resolution updated" , * mgpResp . Policy . Resolution )
2021-12-07 16:01:00 +00:00
assert . Equal ( t , "darwin" , mgpResp . Policy . Platform )
2021-11-24 17:16:42 +00:00
ggpResp := getPolicyByIDResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/global/policies/%d" , gpResp . Policy . ID ) , getPolicyByIDRequest { } , http . StatusOK , & ggpResp )
require . NotNil ( t , ggpResp . Policy )
assert . Equal ( t , "TestQuery4" , ggpResp . Policy . Name )
assert . Equal ( t , "select * from osquery_info;" , ggpResp . Policy . Query )
assert . Equal ( t , "Some description updated" , ggpResp . Policy . Description )
require . NotNil ( t , ggpResp . Policy . Resolution )
assert . Equal ( t , "some global resolution updated" , * ggpResp . Policy . Resolution )
2021-12-07 16:01:00 +00:00
assert . Equal ( t , "darwin" , mgpResp . Policy . Platform )
2021-11-24 17:16:42 +00:00
policiesResponse := listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/policies" , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 1 )
assert . Equal ( t , "TestQuery4" , policiesResponse . Policies [ 0 ] . Name )
assert . Equal ( t , "select * from osquery_info;" , policiesResponse . Policies [ 0 ] . Query )
assert . Equal ( t , "Some description updated" , policiesResponse . Policies [ 0 ] . Description )
require . NotNil ( t , policiesResponse . Policies [ 0 ] . Resolution )
assert . Equal ( t , "some global resolution updated" , * policiesResponse . Policies [ 0 ] . Resolution )
2021-12-07 16:01:00 +00:00
assert . Equal ( t , "darwin" , policiesResponse . Policies [ 0 ] . Platform )
2021-11-24 17:16:42 +00:00
listHostsURL := fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp := listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 3 )
h1 := listHostsResp . Hosts [ 0 ]
h2 := listHostsResp . Hosts [ 1 ]
listHostsURL = fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d&policy_response=passing" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp = listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 0 )
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , h1 . Host , map [ uint ] * bool { policiesResponse . Policies [ 0 ] . ID : ptr . Bool ( true ) } , time . Now ( ) , false ) )
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , h2 . Host , map [ uint ] * bool { policiesResponse . Policies [ 0 ] . ID : nil } , time . Now ( ) , false ) )
listHostsURL = fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d&policy_response=passing" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp = listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 1 )
deletePolicyParams := deleteGlobalPoliciesRequest { IDs : [ ] uint { policiesResponse . Policies [ 0 ] . ID } }
deletePolicyResp := deleteGlobalPoliciesResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies/delete" , deletePolicyParams , http . StatusOK , & deletePolicyResp )
policiesResponse = listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/policies" , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 0 )
}
func ( s * integrationTestSuite ) TestTeamPoliciesProprietary ( ) {
t := s . T ( )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1-policies" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
hosts := make ( [ ] uint , 2 )
for i := 0 ; i < 2 ; i ++ {
h , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) . Add ( - time . Duration ( i ) * time . Minute ) ,
OsqueryHostID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
NodeKey : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
UUID : fmt . Sprintf ( "%s%d" , t . Name ( ) , i ) ,
Hostname : fmt . Sprintf ( "%sfoo.local%d" , t . Name ( ) , i ) ,
2021-12-03 18:33:33 +00:00
Platform : "darwin" ,
2021-11-24 17:16:42 +00:00
} )
require . NoError ( t , err )
hosts [ i ] = h . ID
}
err = s . ds . AddHostsToTeam ( context . Background ( ) , & team1 . ID , hosts )
require . NoError ( t , err )
tpParams := teamPolicyRequest {
Name : "TestQuery3" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some team resolution" ,
2021-12-10 16:55:49 +00:00
Platform : "darwin" ,
2021-11-24 17:16:42 +00:00
}
tpResp := teamPolicyResponse { }
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , tpParams , http . StatusOK , & tpResp )
require . NotNil ( t , tpResp . Policy )
require . NotEmpty ( t , tpResp . Policy . ID )
assert . Equal ( t , "TestQuery3" , tpResp . Policy . Name )
assert . Equal ( t , "select * from osquery;" , tpResp . Policy . Query )
assert . Equal ( t , "Some description" , tpResp . Policy . Description )
require . NotNil ( t , tpResp . Policy . Resolution )
assert . Equal ( t , "some team resolution" , * tpResp . Policy . Resolution )
assert . NotNil ( t , tpResp . Policy . AuthorID )
assert . Equal ( t , "Test Name admin1@example.com" , tpResp . Policy . AuthorName )
assert . Equal ( t , "admin1@example.com" , tpResp . Policy . AuthorEmail )
mtpParams := modifyTeamPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
Name : ptr . String ( "TestQuery4" ) ,
Query : ptr . String ( "select * from osquery_info;" ) ,
Description : ptr . String ( "Some description updated" ) ,
Resolution : ptr . String ( "some team resolution updated" ) ,
} ,
}
mtpResp := modifyTeamPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies/%d" , team1 . ID , tpResp . Policy . ID ) , mtpParams , http . StatusOK , & mtpResp )
require . NotNil ( t , mtpResp . Policy )
assert . Equal ( t , "TestQuery4" , mtpResp . Policy . Name )
assert . Equal ( t , "select * from osquery_info;" , mtpResp . Policy . Query )
assert . Equal ( t , "Some description updated" , mtpResp . Policy . Description )
require . NotNil ( t , mtpResp . Policy . Resolution )
assert . Equal ( t , "some team resolution updated" , * mtpResp . Policy . Resolution )
2021-12-07 16:01:00 +00:00
assert . Equal ( t , "darwin" , mtpResp . Policy . Platform )
2021-11-24 17:16:42 +00:00
gtpResp := getPolicyByIDResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies/%d" , team1 . ID , tpResp . Policy . ID ) , getPolicyByIDRequest { } , http . StatusOK , & gtpResp )
require . NotNil ( t , gtpResp . Policy )
assert . Equal ( t , "TestQuery4" , gtpResp . Policy . Name )
assert . Equal ( t , "select * from osquery_info;" , gtpResp . Policy . Query )
assert . Equal ( t , "Some description updated" , gtpResp . Policy . Description )
require . NotNil ( t , gtpResp . Policy . Resolution )
assert . Equal ( t , "some team resolution updated" , * gtpResp . Policy . Resolution )
2021-12-07 16:01:00 +00:00
assert . Equal ( t , "darwin" , gtpResp . Policy . Platform )
2021-11-24 17:16:42 +00:00
policiesResponse := listTeamPoliciesResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 1 )
assert . Equal ( t , "TestQuery4" , policiesResponse . Policies [ 0 ] . Name )
assert . Equal ( t , "select * from osquery_info;" , policiesResponse . Policies [ 0 ] . Query )
assert . Equal ( t , "Some description updated" , policiesResponse . Policies [ 0 ] . Description )
require . NotNil ( t , policiesResponse . Policies [ 0 ] . Resolution )
assert . Equal ( t , "some team resolution updated" , * policiesResponse . Policies [ 0 ] . Resolution )
2021-12-07 16:01:00 +00:00
assert . Equal ( t , "darwin" , policiesResponse . Policies [ 0 ] . Platform )
2021-11-24 17:16:42 +00:00
listHostsURL := fmt . Sprintf ( "/api/v1/fleet/hosts?policy_id=%d" , policiesResponse . Policies [ 0 ] . ID )
listHostsResp := listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 2 )
h1 := listHostsResp . Hosts [ 0 ]
h2 := listHostsResp . Hosts [ 1 ]
listHostsURL = fmt . Sprintf ( "/api/v1/fleet/hosts?team_id=%d&policy_id=%d&policy_response=passing" , team1 . ID , policiesResponse . Policies [ 0 ] . ID )
listHostsResp = listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 0 )
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , h1 . Host , map [ uint ] * bool { policiesResponse . Policies [ 0 ] . ID : ptr . Bool ( true ) } , time . Now ( ) , false ) )
require . NoError ( t , s . ds . RecordPolicyQueryExecutions ( context . Background ( ) , h2 . Host , map [ uint ] * bool { policiesResponse . Policies [ 0 ] . ID : nil } , time . Now ( ) , false ) )
listHostsURL = fmt . Sprintf ( "/api/v1/fleet/hosts?team_id=%d&policy_id=%d&policy_response=passing" , team1 . ID , policiesResponse . Policies [ 0 ] . ID )
listHostsResp = listHostsResponse { }
s . DoJSON ( "GET" , listHostsURL , nil , http . StatusOK , & listHostsResp )
require . Len ( t , listHostsResp . Hosts , 1 )
deletePolicyParams := deleteTeamPoliciesRequest { IDs : [ ] uint { policiesResponse . Policies [ 0 ] . ID } }
deletePolicyResp := deleteTeamPoliciesResponse { }
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies/delete" , team1 . ID ) , deletePolicyParams , http . StatusOK , & deletePolicyResp )
policiesResponse = listTeamPoliciesResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 0 )
}
func ( s * integrationTestSuite ) TestTeamPoliciesProprietaryInvalid ( ) {
t := s . T ( )
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team1-policies-2" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
tpParams := teamPolicyRequest {
Name : "TestQuery3-Team" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some team resolution" ,
}
tpResp := teamPolicyResponse { }
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , tpParams , http . StatusOK , & tpResp )
require . NotNil ( t , tpResp . Policy )
teamPolicyID := tpResp . Policy . ID
gpParams := globalPolicyRequest {
Name : "TestQuery3-Global" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some global resolution" ,
}
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams , http . StatusOK , & gpResp )
require . NotNil ( t , gpResp . Policy )
require . NotEmpty ( t , gpResp . Policy . ID )
globalPolicyID := gpResp . Policy . ID
for _ , tc := range [ ] struct {
tname string
testUpdate bool
queryID * uint
name string
query string
2021-12-03 18:33:33 +00:00
platforms string
2021-11-24 17:16:42 +00:00
} {
{
tname : "set both QueryID and Query" ,
testUpdate : false ,
queryID : ptr . Uint ( 1 ) ,
name : "Some name" ,
query : "select * from osquery;" ,
} ,
{
tname : "empty query" ,
testUpdate : true ,
name : "Some name" ,
query : "" ,
} ,
{
tname : "empty name" ,
testUpdate : true ,
name : "" ,
query : "select 1;" ,
} ,
2022-01-19 19:07:58 +00:00
{
tname : "empty with space" ,
testUpdate : true ,
name : " " , // #3704
query : "select 1;" ,
} ,
2021-11-24 17:16:42 +00:00
{
tname : "Invalid query" ,
testUpdate : true ,
name : "Invalid query" ,
query : "ATTACH 'foo' AS bar;" ,
} ,
} {
t . Run ( tc . tname , func ( t * testing . T ) {
tpReq := teamPolicyRequest {
2021-12-10 16:55:49 +00:00
QueryID : tc . queryID ,
Name : tc . name ,
Query : tc . query ,
Platform : tc . platforms ,
2021-11-24 17:16:42 +00:00
}
tpResp := teamPolicyResponse { }
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , tpReq , http . StatusBadRequest , & tpResp )
require . Nil ( t , tpResp . Policy )
testUpdate := tc . queryID == nil
if testUpdate {
tpReq := modifyTeamPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
2021-12-07 16:01:00 +00:00
Name : ptr . String ( tc . name ) ,
Query : ptr . String ( tc . query ) ,
2021-11-24 17:16:42 +00:00
} ,
}
tpResp := modifyTeamPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies/%d" , team1 . ID , teamPolicyID ) , tpReq , http . StatusBadRequest , & tpResp )
require . Nil ( t , tpResp . Policy )
}
gpReq := globalPolicyRequest {
2021-12-10 16:55:49 +00:00
QueryID : tc . queryID ,
Name : tc . name ,
Query : tc . query ,
Platform : tc . platforms ,
2021-11-24 17:16:42 +00:00
}
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpReq , http . StatusBadRequest , & gpResp )
require . Nil ( t , tpResp . Policy )
if testUpdate {
gpReq := modifyGlobalPolicyRequest {
ModifyPolicyPayload : fleet . ModifyPolicyPayload {
2021-12-07 16:01:00 +00:00
Name : ptr . String ( tc . name ) ,
Query : ptr . String ( tc . query ) ,
2021-11-24 17:16:42 +00:00
} ,
}
gpResp := modifyGlobalPolicyResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/global/policies/%d" , globalPolicyID ) , gpReq , http . StatusBadRequest , & gpResp )
require . Nil ( t , tpResp . Policy )
}
} )
}
}
func ( s * integrationTestSuite ) TestHostDetailsPolicies ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
host1 := hosts [ 0 ]
team1 , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "HostDetailsPolicies-Team" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
err = s . ds . AddHostsToTeam ( context . Background ( ) , & team1 . ID , [ ] uint { host1 . ID } )
require . NoError ( t , err )
gpParams := globalPolicyRequest {
Name : "HostDetailsPolicies" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some global resolution" ,
}
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams , http . StatusOK , & gpResp )
require . NotNil ( t , gpResp . Policy )
require . NotEmpty ( t , gpResp . Policy . ID )
tpParams := teamPolicyRequest {
Name : "HostDetailsPolicies-Team" ,
Query : "select * from osquery;" ,
Description : "Some description" ,
Resolution : "some team resolution" ,
}
tpResp := teamPolicyResponse { }
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , tpParams , http . StatusOK , & tpResp )
require . NotNil ( t , tpResp . Policy )
require . NotEmpty ( t , tpResp . Policy . ID )
err = s . ds . RecordPolicyQueryExecutions (
context . Background ( ) ,
host1 ,
map [ uint ] * bool { gpResp . Policy . ID : ptr . Bool ( true ) } ,
time . Now ( ) ,
false ,
)
require . NoError ( t , err )
resp := s . Do ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , host1 . ID ) , nil , http . StatusOK )
b , err := ioutil . ReadAll ( resp . Body )
require . NoError ( t , err )
var r struct {
Host * HostDetailResponse ` json:"host" `
Err error ` json:"error,omitempty" `
}
err = json . Unmarshal ( b , & r )
require . NoError ( t , err )
require . Nil ( t , r . Err )
hd := r . Host . HostDetail
require . Len ( t , hd . Policies , 2 )
require . True ( t , reflect . DeepEqual ( gpResp . Policy . PolicyData , hd . Policies [ 0 ] . PolicyData ) )
require . Equal ( t , hd . Policies [ 0 ] . Response , "pass" )
require . True ( t , reflect . DeepEqual ( tpResp . Policy . PolicyData , hd . Policies [ 1 ] . PolicyData ) )
require . Equal ( t , hd . Policies [ 1 ] . Response , "" ) // policy didn't "run"
// Try to create a global policy with an existing name.
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams , http . StatusConflict , & gpResp )
// Try to create a team policy with an existing name.
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , team1 . ID ) , tpParams , http . StatusConflict , & tpResp )
}
2021-11-29 13:12:22 +00:00
func ( s * integrationTestSuite ) TestListActivities ( ) {
t := s . T ( )
ctx := context . Background ( )
u := s . users [ "admin1@example.com" ]
details := make ( map [ string ] interface { } )
2022-02-16 15:33:56 +00:00
prevActivities , err := s . ds . ListActivities ( ctx , fleet . ListOptions { } )
require . NoError ( t , err )
err = s . ds . NewActivity ( ctx , & u , fleet . ActivityTypeAppliedSpecPack , & details )
2021-11-29 13:12:22 +00:00
require . NoError ( t , err )
err = s . ds . NewActivity ( ctx , & u , fleet . ActivityTypeDeletedPack , & details )
require . NoError ( t , err )
err = s . ds . NewActivity ( ctx , & u , fleet . ActivityTypeEditedPack , & details )
require . NoError ( t , err )
2022-02-16 15:33:56 +00:00
lenPage := len ( prevActivities ) + 2
2021-11-29 13:12:22 +00:00
var listResp listActivitiesResponse
2022-02-16 15:33:56 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/activities" , nil , http . StatusOK , & listResp , "per_page" , strconv . Itoa ( lenPage ) , "order_key" , "id" )
require . Len ( t , listResp . Activities , lenPage )
assert . Equal ( t , fleet . ActivityTypeAppliedSpecPack , listResp . Activities [ lenPage - 2 ] . Type )
assert . Equal ( t , fleet . ActivityTypeDeletedPack , listResp . Activities [ lenPage - 1 ] . Type )
2021-11-29 13:12:22 +00:00
2022-02-16 15:33:56 +00:00
s . DoJSON ( "GET" , "/api/v1/fleet/activities" , nil , http . StatusOK , & listResp , "per_page" , strconv . Itoa ( lenPage ) , "order_key" , "id" , "page" , "1" )
2021-11-29 13:12:22 +00:00
require . Len ( t , listResp . Activities , 1 )
assert . Equal ( t , fleet . ActivityTypeEditedPack , listResp . Activities [ 0 ] . Type )
s . DoJSON ( "GET" , "/api/v1/fleet/activities" , nil , http . StatusOK , & listResp , "per_page" , "1" , "order_key" , "id" , "order_direction" , "desc" )
require . Len ( t , listResp . Activities , 1 )
assert . Equal ( t , fleet . ActivityTypeEditedPack , listResp . Activities [ 0 ] . Type )
}
2021-12-01 20:45:29 +00:00
func ( s * integrationTestSuite ) TestListGetCarves ( ) {
t := s . T ( )
ctx := context . Background ( )
hosts := s . createHosts ( t )
c1 , err := s . ds . NewCarve ( ctx , & fleet . CarveMetadata {
CreatedAt : time . Now ( ) ,
HostId : hosts [ 0 ] . ID ,
Name : t . Name ( ) + "_1" ,
SessionId : "ssn1" ,
} )
require . NoError ( t , err )
c2 , err := s . ds . NewCarve ( ctx , & fleet . CarveMetadata {
CreatedAt : time . Now ( ) ,
HostId : hosts [ 1 ] . ID ,
Name : t . Name ( ) + "_2" ,
SessionId : "ssn2" ,
} )
require . NoError ( t , err )
c3 , err := s . ds . NewCarve ( ctx , & fleet . CarveMetadata {
CreatedAt : time . Now ( ) ,
HostId : hosts [ 2 ] . ID ,
Name : t . Name ( ) + "_3" ,
SessionId : "ssn3" ,
} )
require . NoError ( t , err )
// set c1 max block
c1 . MaxBlock = 3
require . NoError ( t , s . ds . UpdateCarve ( ctx , c1 ) )
// make c2 expired, set max block
c2 . Expired = true
c2 . MaxBlock = 3
require . NoError ( t , s . ds . UpdateCarve ( ctx , c2 ) )
var listResp listCarvesResponse
s . DoJSON ( "GET" , "/api/v1/fleet/carves" , nil , http . StatusOK , & listResp , "per_page" , "2" , "order_key" , "id" )
require . Len ( t , listResp . Carves , 2 )
assert . Equal ( t , c1 . ID , listResp . Carves [ 0 ] . ID )
assert . Equal ( t , c3 . ID , listResp . Carves [ 1 ] . ID )
// include expired
s . DoJSON ( "GET" , "/api/v1/fleet/carves" , nil , http . StatusOK , & listResp , "per_page" , "2" , "order_key" , "id" , "expired" , "1" )
require . Len ( t , listResp . Carves , 2 )
assert . Equal ( t , c1 . ID , listResp . Carves [ 0 ] . ID )
assert . Equal ( t , c2 . ID , listResp . Carves [ 1 ] . ID )
// empty page
s . DoJSON ( "GET" , "/api/v1/fleet/carves" , nil , http . StatusOK , & listResp , "page" , "3" , "per_page" , "2" , "order_key" , "id" , "expired" , "1" )
require . Len ( t , listResp . Carves , 0 )
// get specific carve
var getResp getCarveResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/carves/%d" , c2 . ID ) , nil , http . StatusOK , & getResp )
require . Equal ( t , c2 . ID , getResp . Carve . ID )
require . True ( t , getResp . Carve . Expired )
// get non-existing carve
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/carves/%d" , c3 . ID + 1 ) , nil , http . StatusNotFound , & getResp )
// get expired carve block
var blkResp getCarveBlockResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/carves/%d/block/%d" , c2 . ID , 1 ) , nil , http . StatusInternalServerError , & blkResp )
// get valid carve block, but block not inserted yet
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/carves/%d/block/%d" , c1 . ID , 1 ) , nil , http . StatusNotFound , & blkResp )
require . NoError ( t , s . ds . NewBlock ( ctx , c1 , 1 , [ ] byte ( "block1" ) ) )
require . NoError ( t , s . ds . NewBlock ( ctx , c1 , 2 , [ ] byte ( "block2" ) ) )
require . NoError ( t , s . ds . NewBlock ( ctx , c1 , 3 , [ ] byte ( "block3" ) ) )
// get valid carve block
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/carves/%d/block/%d" , c1 . ID , 1 ) , nil , http . StatusOK , & blkResp )
require . Equal ( t , "block1" , string ( blkResp . Data ) )
}
2021-12-14 21:34:11 +00:00
func ( s * integrationTestSuite ) TestHostsAddToTeam ( ) {
t := s . T ( )
ctx := context . Background ( )
tm1 , err := s . ds . NewTeam ( ctx , & fleet . Team {
Name : uuid . New ( ) . String ( ) ,
} )
require . NoError ( t , err )
tm2 , err := s . ds . NewTeam ( ctx , & fleet . Team {
Name : uuid . New ( ) . String ( ) ,
} )
require . NoError ( t , err )
hosts := s . createHosts ( t )
var refetchResp refetchHostResponse
// refetch existing
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/refetch" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & refetchResp )
require . NoError ( t , refetchResp . Err )
// refetch unknown
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/refetch" , hosts [ 2 ] . ID + 1 ) , nil , http . StatusNotFound , & refetchResp )
// get by identifier unknown
var getResp getHostResponse
s . DoJSON ( "GET" , "/api/v1/fleet/hosts/identifier/no-such-host" , nil , http . StatusNotFound , & getResp )
// get by identifier valid
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/identifier/%s" , hosts [ 0 ] . UUID ) , nil , http . StatusOK , & getResp )
require . Equal ( t , hosts [ 0 ] . ID , getResp . Host . ID )
require . Nil ( t , getResp . Host . TeamID )
// assign hosts to team 1
var addResp addHostsToTeamResponse
s . DoJSON ( "POST" , "/api/v1/fleet/hosts/transfer" , addHostsToTeamRequest {
TeamID : & tm1 . ID ,
HostIDs : [ ] uint { hosts [ 0 ] . ID , hosts [ 1 ] . ID } ,
} , http . StatusOK , & addResp )
// check that hosts are now part of that team
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & getResp )
require . NotNil ( t , getResp . Host . TeamID )
require . Equal ( t , tm1 . ID , * getResp . Host . TeamID )
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 1 ] . ID ) , nil , http . StatusOK , & getResp )
require . NotNil ( t , getResp . Host . TeamID )
require . Equal ( t , tm1 . ID , * getResp . Host . TeamID )
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 2 ] . ID ) , nil , http . StatusOK , & getResp )
require . Nil ( t , getResp . Host . TeamID )
// assign host to team 2 with filter
var addfResp addHostsToTeamByFilterResponse
req := addHostsToTeamByFilterRequest { TeamID : & tm2 . ID }
req . Filters . MatchQuery = hosts [ 2 ] . Hostname
s . DoJSON ( "POST" , "/api/v1/fleet/hosts/transfer/filter" , req , http . StatusOK , & addfResp )
// check that host 2 is now part of team 2
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 2 ] . ID ) , nil , http . StatusOK , & getResp )
require . NotNil ( t , getResp . Host . TeamID )
require . Equal ( t , tm2 . ID , * getResp . Host . TeamID )
// delete host 0
var delResp deleteHostResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & delResp )
// delete non-existing host
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 2 ] . ID + 1 ) , nil , http . StatusNotFound , & delResp )
// list the hosts
var listResp listHostsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & listResp , "per_page" , "3" )
require . Len ( t , listResp . Hosts , 2 )
ids := [ ] uint { listResp . Hosts [ 0 ] . ID , listResp . Hosts [ 1 ] . ID }
require . ElementsMatch ( t , ids , [ ] uint { hosts [ 1 ] . ID , hosts [ 2 ] . ID } )
}
2021-12-15 15:23:08 +00:00
func ( s * integrationTestSuite ) TestScheduledQueries ( ) {
t := s . T ( )
// create a pack
var createPackResp createPackResponse
reqPack := & createPackRequest {
PackPayload : fleet . PackPayload {
Name : ptr . String ( strings . ReplaceAll ( t . Name ( ) , "/" , "_" ) ) ,
} ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/packs" , reqPack , http . StatusOK , & createPackResp )
pack := createPackResp . Pack . Pack
2022-01-12 13:04:16 +00:00
// try a non existent query
s . Do ( "GET" , fmt . Sprintf ( "/api/v1/fleet/queries/%d" , 9999 ) , nil , http . StatusNotFound )
2022-01-31 21:35:22 +00:00
// list queries
var listQryResp listQueriesResponse
s . DoJSON ( "GET" , "/api/v1/fleet/queries" , nil , http . StatusOK , & listQryResp )
assert . Len ( t , listQryResp . Queries , 0 )
2021-12-15 15:23:08 +00:00
// create a query
var createQueryResp createQueryResponse
reqQuery := & fleet . QueryPayload {
2022-01-31 21:35:22 +00:00
Name : ptr . String ( strings . ReplaceAll ( t . Name ( ) , "/" , "_" ) ) ,
2021-12-15 15:23:08 +00:00
Query : ptr . String ( "select * from time;" ) ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/queries" , reqQuery , http . StatusOK , & createQueryResp )
query := createQueryResp . Query
2022-01-31 21:35:22 +00:00
// listing returns that query
s . DoJSON ( "GET" , "/api/v1/fleet/queries" , nil , http . StatusOK , & listQryResp )
require . Len ( t , listQryResp . Queries , 1 )
assert . Equal ( t , query . Name , listQryResp . Queries [ 0 ] . Name )
// next page returns nothing
s . DoJSON ( "GET" , "/api/v1/fleet/queries" , nil , http . StatusOK , & listQryResp , "per_page" , "2" , "page" , "1" )
require . Len ( t , listQryResp . Queries , 0 )
// getting that query works
var getQryResp getQueryResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/queries/%d" , query . ID ) , nil , http . StatusOK , & getQryResp )
assert . Equal ( t , query . ID , getQryResp . Query . ID )
2021-12-15 15:23:08 +00:00
// list scheduled queries in pack, none yet
var getInPackResp getScheduledQueriesInPackResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/packs/%d/scheduled" , pack . ID ) , nil , http . StatusOK , & getInPackResp )
assert . Len ( t , getInPackResp . Scheduled , 0 )
// list scheduled queries in non-existing pack
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/packs/%d/scheduled" , pack . ID + 1 ) , nil , http . StatusOK , & getInPackResp )
assert . Len ( t , getInPackResp . Scheduled , 0 )
// create scheduled query
var createResp scheduleQueryResponse
reqSQ := & scheduleQueryRequest {
PackID : pack . ID ,
QueryID : query . ID ,
Interval : 1 ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/schedule" , reqSQ , http . StatusOK , & createResp )
sq1 := createResp . Scheduled . ScheduledQuery
assert . NotZero ( t , sq1 . ID )
assert . Equal ( t , uint ( 1 ) , sq1 . Interval )
// create scheduled query with invalid pack
reqSQ = & scheduleQueryRequest {
PackID : pack . ID + 1 ,
QueryID : query . ID ,
Interval : 2 ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/schedule" , reqSQ , http . StatusUnprocessableEntity , & createResp )
// create scheduled query with invalid query
reqSQ = & scheduleQueryRequest {
PackID : pack . ID ,
QueryID : query . ID + 1 ,
Interval : 3 ,
}
2022-01-12 13:04:16 +00:00
s . DoJSON ( "POST" , "/api/v1/fleet/schedule" , reqSQ , http . StatusNotFound , & createResp )
2021-12-15 15:23:08 +00:00
// list scheduled queries in pack
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/packs/%d/scheduled" , pack . ID ) , nil , http . StatusOK , & getInPackResp )
require . Len ( t , getInPackResp . Scheduled , 1 )
assert . Equal ( t , sq1 . ID , getInPackResp . Scheduled [ 0 ] . ID )
// list scheduled queries in pack, next page
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/packs/%d/scheduled" , pack . ID ) , nil , http . StatusOK , & getInPackResp , "page" , "1" , "per_page" , "2" )
require . Len ( t , getInPackResp . Scheduled , 0 )
// get non-existing scheduled query
var getResp getScheduledQueryResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID + 1 ) , nil , http . StatusNotFound , & getResp )
// get existing scheduled query
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID ) , nil , http . StatusOK , & getResp )
assert . Equal ( t , sq1 . ID , getResp . Scheduled . ID )
assert . Equal ( t , sq1 . Interval , getResp . Scheduled . Interval )
// modify scheduled query
var modResp modifyScheduledQueryResponse
reqMod := fleet . ScheduledQueryPayload {
Interval : ptr . Uint ( 4 ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID ) , reqMod , http . StatusOK , & modResp )
assert . Equal ( t , sq1 . ID , modResp . Scheduled . ID )
assert . Equal ( t , uint ( 4 ) , modResp . Scheduled . Interval )
// modify non-existing scheduled query
reqMod = fleet . ScheduledQueryPayload {
Interval : ptr . Uint ( 5 ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID + 1 ) , reqMod , http . StatusNotFound , & modResp )
// delete non-existing scheduled query
var delResp deleteScheduledQueryResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID + 1 ) , nil , http . StatusNotFound , & delResp )
// delete existing scheduled query
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID ) , nil , http . StatusOK , & delResp )
// get the now-deleted scheduled query
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/schedule/%d" , sq1 . ID ) , nil , http . StatusNotFound , & getResp )
2022-01-31 21:35:22 +00:00
// modify the query
var modQryResp modifyQueryResponse
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/queries/%d" , query . ID ) , fleet . QueryPayload { Description : ptr . String ( "updated" ) } , http . StatusOK , & modQryResp )
assert . Equal ( t , "updated" , modQryResp . Query . Description )
// modify a non-existing query
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/queries/%d" , query . ID + 1 ) , fleet . QueryPayload { Description : ptr . String ( "updated" ) } , http . StatusNotFound , & modQryResp )
// delete the query by name
var delByNameResp deleteQueryResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/queries/%s" , query . Name ) , nil , http . StatusOK , & delByNameResp )
// delete unknown query by name (i.e. the same, now deleted)
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/queries/%s" , query . Name ) , nil , http . StatusNotFound , & delByNameResp )
// create another query
reqQuery = & fleet . QueryPayload {
Name : ptr . String ( strings . ReplaceAll ( t . Name ( ) , "/" , "_" ) + "_2" ) ,
Query : ptr . String ( "select 2" ) ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/queries" , reqQuery , http . StatusOK , & createQueryResp )
query2 := createQueryResp . Query
// delete it by id
var delByIDResp deleteQueryByIDResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/queries/id/%d" , query2 . ID ) , nil , http . StatusOK , & delByIDResp )
// delete unknown query by id (same id just deleted)
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/queries/id/%d" , query2 . ID ) , nil , http . StatusNotFound , & delByIDResp )
// create another query
reqQuery = & fleet . QueryPayload {
Name : ptr . String ( strings . ReplaceAll ( t . Name ( ) , "/" , "_" ) + "_3" ) ,
Query : ptr . String ( "select 3" ) ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/queries" , reqQuery , http . StatusOK , & createQueryResp )
query3 := createQueryResp . Query
// batch-delete by id, 3 ids, only one exists
var delBatchResp deleteQueriesResponse
s . DoJSON ( "POST" , "/api/v1/fleet/queries/delete" , map [ string ] interface { } {
2022-02-14 18:13:44 +00:00
"ids" : [ ] uint { query . ID , query2 . ID , query3 . ID } ,
} , http . StatusOK , & delBatchResp )
2022-01-31 21:35:22 +00:00
assert . Equal ( t , uint ( 1 ) , delBatchResp . Deleted )
// batch-delete by id, none exist
delBatchResp . Deleted = 0
s . DoJSON ( "POST" , "/api/v1/fleet/queries/delete" , map [ string ] interface { } {
2022-02-14 18:13:44 +00:00
"ids" : [ ] uint { query . ID , query2 . ID , query3 . ID } ,
} , http . StatusNotFound , & delBatchResp )
2022-01-31 21:35:22 +00:00
assert . Equal ( t , uint ( 0 ) , delBatchResp . Deleted )
2021-12-15 15:23:08 +00:00
}
2021-12-21 12:37:58 +00:00
2021-12-21 20:36:19 +00:00
func ( s * integrationTestSuite ) TestHostDeviceMapping ( ) {
t := s . T ( )
ctx := context . Background ( )
hosts := s . createHosts ( t )
// get host device mappings of invalid host
var listResp listHostDeviceMappingResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/device_mapping" , hosts [ 2 ] . ID + 1 ) , nil , http . StatusNotFound , & listResp )
// existing host but none yet
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/device_mapping" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & listResp )
require . Len ( t , listResp . DeviceMapping , 0 )
// create some mappings
s . ds . ReplaceHostDeviceMapping ( ctx , hosts [ 0 ] . ID , [ ] * fleet . HostDeviceMapping {
{ HostID : hosts [ 0 ] . ID , Email : "a@b.c" , Source : "google_chrome_profiles" } ,
{ HostID : hosts [ 0 ] . ID , Email : "b@b.c" , Source : "google_chrome_profiles" } ,
} )
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/device_mapping" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & listResp )
require . Len ( t , listResp . DeviceMapping , 2 )
require . Equal ( t , "a@b.c" , listResp . DeviceMapping [ 0 ] . Email )
require . Equal ( t , "google_chrome_profiles" , listResp . DeviceMapping [ 0 ] . Source )
require . Zero ( t , listResp . DeviceMapping [ 0 ] . HostID )
require . Equal ( t , "b@b.c" , listResp . DeviceMapping [ 1 ] . Email )
require . Equal ( t , "google_chrome_profiles" , listResp . DeviceMapping [ 1 ] . Source )
require . Zero ( t , listResp . DeviceMapping [ 1 ] . HostID )
require . Equal ( t , hosts [ 0 ] . ID , listResp . HostID )
// other host still has none
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/device_mapping" , hosts [ 1 ] . ID ) , nil , http . StatusOK , & listResp )
require . Len ( t , listResp . DeviceMapping , 0 )
// search host by email address finds the corresponding host
var listHosts listHostsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & listHosts , "query" , "a@b.c" )
require . Len ( t , listHosts . Hosts , 1 )
assert . Equal ( t , hosts [ 0 ] . ID , listHosts . Hosts [ 0 ] . ID )
s . DoJSON ( "GET" , "/api/v1/fleet/hosts" , nil , http . StatusOK , & listHosts , "query" , "c@b.c" )
require . Len ( t , listHosts . Hosts , 0 )
}
2021-12-21 12:37:58 +00:00
func ( s * integrationTestSuite ) TestGetMacadminsData ( ) {
t := s . T ( )
ctx := context . Background ( )
2021-12-23 19:57:43 +00:00
hostAll , err := s . ds . NewHost ( ctx , & fleet . Host {
2021-12-21 12:37:58 +00:00
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) ,
NodeKey : t . Name ( ) + "1" ,
UUID : t . Name ( ) + "1" ,
Hostname : t . Name ( ) + "foo.local" ,
PrimaryIP : "192.168.1.1" ,
PrimaryMac : "30-65-EC-6F-C4-58" ,
2021-12-23 19:57:43 +00:00
OsqueryHostID : "1" ,
2021-12-21 12:37:58 +00:00
} )
require . NoError ( t , err )
2021-12-23 19:57:43 +00:00
require . NotNil ( t , hostAll )
hostNothing , err := s . ds . NewHost ( ctx , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) ,
NodeKey : t . Name ( ) + "2" ,
UUID : t . Name ( ) + "2" ,
Hostname : t . Name ( ) + "foo.local2" ,
PrimaryIP : "192.168.1.2" ,
PrimaryMac : "30-65-EC-6F-C4-59" ,
OsqueryHostID : "2" ,
} )
require . NoError ( t , err )
require . NotNil ( t , hostNothing )
hostOnlyMunki , err := s . ds . NewHost ( ctx , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) ,
NodeKey : t . Name ( ) + "3" ,
UUID : t . Name ( ) + "3" ,
Hostname : t . Name ( ) + "foo.local3" ,
PrimaryIP : "192.168.1.3" ,
PrimaryMac : "30-65-EC-6F-C4-5F" ,
OsqueryHostID : "3" ,
} )
require . NoError ( t , err )
require . NotNil ( t , hostOnlyMunki )
hostOnlyMDM , err := s . ds . NewHost ( ctx , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) ,
NodeKey : t . Name ( ) + "4" ,
UUID : t . Name ( ) + "4" ,
Hostname : t . Name ( ) + "foo.local4" ,
PrimaryIP : "192.168.1.4" ,
PrimaryMac : "30-65-EC-6F-C4-5A" ,
OsqueryHostID : "4" ,
} )
require . NoError ( t , err )
require . NotNil ( t , hostOnlyMDM )
2021-12-21 12:37:58 +00:00
2021-12-23 19:57:43 +00:00
require . NoError ( t , s . ds . SetOrUpdateMDMData ( ctx , hostAll . ID , true , "url" , false ) )
require . NoError ( t , s . ds . SetOrUpdateMunkiVersion ( ctx , hostAll . ID , "1.3.0" ) )
2021-12-21 12:37:58 +00:00
macadminsData := getMacadminsDataResponse { }
2021-12-23 19:57:43 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hostAll . ID ) , nil , http . StatusOK , & macadminsData )
2021-12-21 12:37:58 +00:00
require . NotNil ( t , macadminsData . Macadmins )
assert . Equal ( t , "url" , macadminsData . Macadmins . MDM . ServerURL )
assert . Equal ( t , "Enrolled (manual)" , macadminsData . Macadmins . MDM . EnrollmentStatus )
assert . Equal ( t , "1.3.0" , macadminsData . Macadmins . Munki . Version )
2021-12-23 19:57:43 +00:00
require . NoError ( t , s . ds . SetOrUpdateMDMData ( ctx , hostAll . ID , true , "url2" , true ) )
require . NoError ( t , s . ds . SetOrUpdateMunkiVersion ( ctx , hostAll . ID , "1.5.0" ) )
2021-12-21 12:37:58 +00:00
macadminsData = getMacadminsDataResponse { }
2021-12-23 19:57:43 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hostAll . ID ) , nil , http . StatusOK , & macadminsData )
2021-12-21 12:37:58 +00:00
require . NotNil ( t , macadminsData . Macadmins )
assert . Equal ( t , "url2" , macadminsData . Macadmins . MDM . ServerURL )
assert . Equal ( t , "Enrolled (automated)" , macadminsData . Macadmins . MDM . EnrollmentStatus )
assert . Equal ( t , "1.5.0" , macadminsData . Macadmins . Munki . Version )
2021-12-23 19:57:43 +00:00
require . NoError ( t , s . ds . SetOrUpdateMDMData ( ctx , hostAll . ID , false , "url2" , false ) )
2021-12-21 12:37:58 +00:00
macadminsData = getMacadminsDataResponse { }
2021-12-23 19:57:43 +00:00
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hostAll . ID ) , nil , http . StatusOK , & macadminsData )
2021-12-21 12:37:58 +00:00
require . NotNil ( t , macadminsData . Macadmins )
assert . Equal ( t , "Unenrolled" , macadminsData . Macadmins . MDM . EnrollmentStatus )
2021-12-23 19:57:43 +00:00
// nothing returns null
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hostNothing . ID ) , nil , http . StatusOK , & macadminsData )
require . Nil ( t , macadminsData . Macadmins )
// only munki info returns null on mdm
require . NoError ( t , s . ds . SetOrUpdateMunkiVersion ( ctx , hostOnlyMunki . ID , "3.2.0" ) )
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hostOnlyMunki . ID ) , nil , http . StatusOK , & macadminsData )
require . NotNil ( t , macadminsData . Macadmins )
require . Nil ( t , macadminsData . Macadmins . MDM )
require . NotNil ( t , macadminsData . Macadmins . Munki )
assert . Equal ( t , "3.2.0" , macadminsData . Macadmins . Munki . Version )
// only mdm returns null on munki info
require . NoError ( t , s . ds . SetOrUpdateMDMData ( ctx , hostOnlyMDM . ID , true , "AAA" , true ) )
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hostOnlyMDM . ID ) , nil , http . StatusOK , & macadminsData )
require . NotNil ( t , macadminsData . Macadmins )
require . NotNil ( t , macadminsData . Macadmins . MDM )
require . Nil ( t , macadminsData . Macadmins . Munki )
assert . Equal ( t , "AAA" , macadminsData . Macadmins . MDM . ServerURL )
assert . Equal ( t , "Enrolled (automated)" , macadminsData . Macadmins . MDM . EnrollmentStatus )
2022-01-26 20:55:07 +00:00
// generate aggregated data
require . NoError ( t , s . ds . GenerateAggregatedMunkiAndMDM ( context . Background ( ) ) )
agg := getAggregatedMacadminsDataResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/macadmins" , nil , http . StatusOK , & agg )
require . NotNil ( t , agg . Macadmins )
2022-02-07 17:53:33 +00:00
assert . NotZero ( t , agg . Macadmins . CountsUpdatedAt )
2022-01-26 20:55:07 +00:00
assert . Len ( t , agg . Macadmins . MunkiVersions , 2 )
assert . ElementsMatch ( t , agg . Macadmins . MunkiVersions , [ ] fleet . AggregatedMunkiVersion {
{
HostMunkiInfo : fleet . HostMunkiInfo { Version : "1.5.0" } ,
HostsCount : 1 ,
} ,
{
HostMunkiInfo : fleet . HostMunkiInfo { Version : "3.2.0" } ,
HostsCount : 1 ,
} ,
} )
assert . Equal ( t , agg . Macadmins . MDMStatus . EnrolledManualHostsCount , 0 )
assert . Equal ( t , agg . Macadmins . MDMStatus . EnrolledAutomatedHostsCount , 1 )
assert . Equal ( t , agg . Macadmins . MDMStatus . UnenrolledHostsCount , 1 )
assert . Equal ( t , agg . Macadmins . MDMStatus . HostsCount , 2 )
team , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
Name : "team1" + t . Name ( ) ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
agg = getAggregatedMacadminsDataResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/macadmins" , nil , http . StatusOK , & agg , "team_id" , fmt . Sprint ( team . ID ) )
require . NotNil ( t , agg . Macadmins )
require . Empty ( t , agg . Macadmins . MunkiVersions )
require . Empty ( t , agg . Macadmins . MDMStatus )
agg = getAggregatedMacadminsDataResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/macadmins" , nil , http . StatusNotFound , & agg , "team_id" , "9999999" )
2021-12-21 12:37:58 +00:00
}
2021-12-21 14:53:15 +00:00
func ( s * integrationTestSuite ) TestLabels ( ) {
t := s . T ( )
// list labels, has the built-in ones
var listResp listLabelsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/labels" , nil , http . StatusOK , & listResp )
assert . True ( t , len ( listResp . Labels ) > 0 )
for _ , lbl := range listResp . Labels {
assert . Equal ( t , fleet . LabelTypeBuiltIn , lbl . LabelType )
}
builtInsCount := len ( listResp . Labels )
// create a label without name, an error
var createResp createLabelResponse
s . DoJSON ( "POST" , "/api/v1/fleet/labels" , & fleet . LabelPayload { Query : ptr . String ( "select 1" ) } , http . StatusUnprocessableEntity , & createResp )
// create a valid label
s . DoJSON ( "POST" , "/api/v1/fleet/labels" , & fleet . LabelPayload { Name : ptr . String ( t . Name ( ) ) , Query : ptr . String ( "select 1" ) } , http . StatusOK , & createResp )
assert . NotZero ( t , createResp . Label . ID )
assert . Equal ( t , t . Name ( ) , createResp . Label . Name )
lbl1 := createResp . Label . Label
// get the label
var getResp getLabelResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/labels/%d" , lbl1 . ID ) , nil , http . StatusOK , & getResp )
assert . Equal ( t , lbl1 . ID , getResp . Label . ID )
// get a non-existing label
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/labels/%d" , lbl1 . ID + 1 ) , nil , http . StatusNotFound , & getResp )
// modify that label
var modResp modifyLabelResponse
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/labels/%d" , lbl1 . ID ) , & fleet . ModifyLabelPayload { Name : ptr . String ( t . Name ( ) + "zzz" ) } , http . StatusOK , & modResp )
assert . Equal ( t , lbl1 . ID , modResp . Label . ID )
assert . NotEqual ( t , lbl1 . Name , modResp . Label . Name )
// modify a non-existing label
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/labels/%d" , lbl1 . ID + 1 ) , & fleet . ModifyLabelPayload { Name : ptr . String ( "zzz" ) } , http . StatusNotFound , & modResp )
// list labels
s . DoJSON ( "GET" , "/api/v1/fleet/labels" , nil , http . StatusOK , & listResp , "per_page" , strconv . Itoa ( builtInsCount + 1 ) )
assert . Len ( t , listResp . Labels , builtInsCount + 1 )
// next page is empty
s . DoJSON ( "GET" , "/api/v1/fleet/labels" , nil , http . StatusOK , & listResp , "per_page" , "2" , "page" , "1" , "query" , t . Name ( ) )
assert . Len ( t , listResp . Labels , 0 )
// create another label
s . DoJSON ( "POST" , "/api/v1/fleet/labels" , & fleet . LabelPayload { Name : ptr . String ( strings . ReplaceAll ( t . Name ( ) , "/" , "_" ) ) , Query : ptr . String ( "select 1" ) } , http . StatusOK , & createResp )
assert . NotZero ( t , createResp . Label . ID )
lbl2 := createResp . Label . Label
// create hosts and add them to that label
hosts := s . createHosts ( t )
for _ , h := range hosts {
err := s . ds . RecordLabelQueryExecutions ( context . Background ( ) , h , map [ uint ] * bool { lbl2 . ID : ptr . Bool ( true ) } , time . Now ( ) , false )
require . NoError ( t , err )
}
// list hosts in label
var listHostsResp listHostsResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/labels/%d/hosts" , lbl2 . ID ) , nil , http . StatusOK , & listHostsResp )
assert . Len ( t , listHostsResp . Hosts , len ( hosts ) )
// lists hosts in label without hosts
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/labels/%d/hosts" , lbl1 . ID ) , nil , http . StatusOK , & listHostsResp )
assert . Len ( t , listHostsResp . Hosts , 0 )
// lists hosts in invalid label
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/labels/%d/hosts" , lbl2 . ID + 1 ) , nil , http . StatusOK , & listHostsResp )
assert . Len ( t , listHostsResp . Hosts , 0 )
// delete a label by id
var delIDResp deleteLabelByIDResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/labels/id/%d" , lbl1 . ID ) , nil , http . StatusOK , & delIDResp )
// delete a non-existing label by id
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/labels/id/%d" , lbl2 . ID + 1 ) , nil , http . StatusNotFound , & delIDResp )
// delete a label by name
var delResp deleteLabelResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/labels/%s" , url . PathEscape ( lbl2 . Name ) ) , nil , http . StatusOK , & delResp )
// delete a non-existing label by name
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/labels/%s" , url . PathEscape ( lbl2 . Name ) ) , nil , http . StatusNotFound , & delResp )
// list labels, only the built-ins remain
s . DoJSON ( "GET" , "/api/v1/fleet/labels" , nil , http . StatusOK , & listResp , "per_page" , strconv . Itoa ( builtInsCount + 1 ) )
assert . Len ( t , listResp . Labels , builtInsCount )
for _ , lbl := range listResp . Labels {
assert . Equal ( t , fleet . LabelTypeBuiltIn , lbl . LabelType )
}
}
func ( s * integrationTestSuite ) TestLabelSpecs ( ) {
t := s . T ( )
// list label specs, only those of the built-ins
var listResp getLabelSpecsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/spec/labels" , nil , http . StatusOK , & listResp )
assert . True ( t , len ( listResp . Specs ) > 0 )
for _ , spec := range listResp . Specs {
assert . Equal ( t , fleet . LabelTypeBuiltIn , spec . LabelType )
}
builtInsCount := len ( listResp . Specs )
name := strings . ReplaceAll ( t . Name ( ) , "/" , "_" )
// apply an invalid label spec - dynamic membership with host specified
var applyResp applyLabelSpecsResponse
s . DoJSON ( "POST" , "/api/v1/fleet/spec/labels" , applyLabelSpecsRequest {
Specs : [ ] * fleet . LabelSpec {
{
Name : name ,
Query : "select 1" ,
Platform : "linux" ,
LabelMembershipType : fleet . LabelMembershipTypeDynamic ,
Hosts : [ ] string { "abc" } ,
} ,
} ,
} , http . StatusInternalServerError , & applyResp )
// apply an invalid label spec - manual membership without a host specified
s . DoJSON ( "POST" , "/api/v1/fleet/spec/labels" , applyLabelSpecsRequest {
Specs : [ ] * fleet . LabelSpec {
{
Name : name ,
Query : "select 1" ,
Platform : "linux" ,
LabelMembershipType : fleet . LabelMembershipTypeManual ,
} ,
} ,
} , http . StatusInternalServerError , & applyResp )
// apply a valid label spec
s . DoJSON ( "POST" , "/api/v1/fleet/spec/labels" , applyLabelSpecsRequest {
Specs : [ ] * fleet . LabelSpec {
{
Name : name ,
Query : "select 1" ,
Platform : "linux" ,
LabelMembershipType : fleet . LabelMembershipTypeDynamic ,
} ,
} ,
} , http . StatusOK , & applyResp )
// list label specs, has the newly created one
s . DoJSON ( "GET" , "/api/v1/fleet/spec/labels" , nil , http . StatusOK , & listResp )
assert . Len ( t , listResp . Specs , builtInsCount + 1 )
// get a specific label spec
var getResp getLabelSpecResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/spec/labels/%s" , url . PathEscape ( name ) ) , nil , http . StatusOK , & getResp )
assert . Equal ( t , name , getResp . Spec . Name )
// get a non-existing label spec
s . DoJSON ( "GET" , "/api/v1/fleet/spec/labels/zzz" , nil , http . StatusNotFound , & getResp )
}
2021-12-23 21:26:55 +00:00
2022-01-10 19:43:39 +00:00
func ( s * integrationTestSuite ) TestUsers ( ) {
2022-03-08 16:27:38 +00:00
// ensure that on exit, the admin token is used
defer func ( ) { s . token = s . getTestAdminToken ( ) } ( )
2022-01-10 19:43:39 +00:00
t := s . T ( )
// list existing users
var listResp listUsersResponse
s . DoJSON ( "GET" , "/api/v1/fleet/users" , nil , http . StatusOK , & listResp )
assert . Len ( t , listResp . Users , len ( s . users ) )
2022-01-13 19:57:44 +00:00
// test available teams returned by `/me` endpoint for existing user
var getMeResp getUserResponse
2022-01-25 14:34:00 +00:00
ssn := createSession ( t , 1 , s . ds )
2022-01-13 19:57:44 +00:00
resp := s . DoRawWithHeaders ( "GET" , "/api/v1/fleet/me" , [ ] byte ( "" ) , http . StatusOK , map [ string ] string {
2022-01-25 14:34:00 +00:00
"Authorization" : fmt . Sprintf ( "Bearer %s" , ssn . Key ) ,
2022-01-13 19:57:44 +00:00
} )
2022-01-25 14:34:00 +00:00
err := json . NewDecoder ( resp . Body ) . Decode ( & getMeResp )
2022-01-13 19:57:44 +00:00
require . NoError ( t , err )
assert . Equal ( t , uint ( 1 ) , getMeResp . User . ID )
assert . NotNil ( t , getMeResp . User . GlobalRole )
assert . Len ( t , getMeResp . User . Teams , 0 )
assert . Len ( t , getMeResp . AvailableTeams , 0 )
2022-01-10 19:43:39 +00:00
// create a new user
var createResp createUserResponse
2022-03-08 16:27:38 +00:00
userRawPwd := "pass"
2022-01-10 19:43:39 +00:00
params := fleet . UserPayload {
Name : ptr . String ( "extra" ) ,
Email : ptr . String ( "extra@asd.com" ) ,
2022-03-08 16:27:38 +00:00
Password : ptr . String ( userRawPwd ) ,
2022-01-10 19:43:39 +00:00
GlobalRole : ptr . String ( fleet . RoleObserver ) ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/users/admin" , params , http . StatusOK , & createResp )
assert . NotZero ( t , createResp . User . ID )
2022-03-08 16:27:38 +00:00
assert . True ( t , createResp . User . AdminForcedPasswordReset )
2022-01-10 19:43:39 +00:00
u := * createResp . User
2022-01-13 19:57:44 +00:00
// login as that user and check that teams info is empty
var loginResp loginResponse
s . DoJSON ( "POST" , "/api/v1/fleet/login" , params , http . StatusOK , & loginResp )
require . Equal ( t , loginResp . User . ID , u . ID )
assert . Len ( t , loginResp . User . Teams , 0 )
assert . Len ( t , loginResp . AvailableTeams , 0 )
// get that user from `/users` endpoint and check that teams info is empty
2022-01-10 19:43:39 +00:00
var getResp getUserResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , nil , http . StatusOK , & getResp )
assert . Equal ( t , u . ID , getResp . User . ID )
2022-01-13 19:57:44 +00:00
assert . Len ( t , getResp . User . Teams , 0 )
assert . Len ( t , getResp . AvailableTeams , 0 )
2022-01-10 19:43:39 +00:00
// get non-existing user
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID + 1 ) , nil , http . StatusNotFound , & getResp )
// modify that user - simple name change
var modResp modifyUserResponse
params = fleet . UserPayload {
Name : ptr . String ( "extraz" ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , params , http . StatusOK , & modResp )
assert . Equal ( t , u . ID , modResp . User . ID )
assert . Equal ( t , u . Name + "z" , modResp . User . Name )
2022-02-28 12:34:44 +00:00
// modify that user - set an existing email
params = fleet . UserPayload {
Email : & getMeResp . User . Email ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , params , http . StatusConflict , & modResp )
2022-02-28 16:17:10 +00:00
// modify that user - set an email that has an invite for it
createInviteReq := createInviteRequest { InvitePayload : fleet . InvitePayload {
Email : ptr . String ( "colliding@email.com" ) ,
Name : ptr . String ( "some name" ) ,
GlobalRole : null . StringFrom ( fleet . RoleAdmin ) ,
} }
createInviteResp := createInviteResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/invites" , createInviteReq , http . StatusOK , & createInviteResp )
params = fleet . UserPayload {
Email : ptr . String ( "colliding@email.com" ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , params , http . StatusConflict , & modResp )
2022-02-28 12:34:44 +00:00
// modify that user - set a non existent email
params = fleet . UserPayload {
Email : ptr . String ( "someemail@qowieuowh.com" ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , params , http . StatusOK , & modResp )
2022-01-10 19:43:39 +00:00
// modify user - email change, password does not match
params = fleet . UserPayload {
Email : ptr . String ( "extra2@asd.com" ) ,
Password : ptr . String ( "wrongpass" ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , params , http . StatusForbidden , & modResp )
// modify user - email change, password ok
params = fleet . UserPayload {
Email : ptr . String ( "extra2@asd.com" ) ,
Password : ptr . String ( "pass" ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , params , http . StatusOK , & modResp )
assert . Equal ( t , u . ID , modResp . User . ID )
assert . NotEqual ( t , u . ID , modResp . User . Email )
// modify invalid user
params = fleet . UserPayload {
Name : ptr . String ( "nosuchuser" ) ,
}
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID + 1 ) , params , http . StatusNotFound , & modResp )
2022-03-08 16:27:38 +00:00
// perform a required password change as the user themselves
s . token = s . getTestToken ( u . Email , userRawPwd )
var perfPwdResetResp performRequiredPasswordResetResponse
newRawPwd := "new_password!"
s . DoJSON ( "POST" , "/api/v1/fleet/perform_required_password_reset" , performRequiredPasswordResetRequest {
Password : newRawPwd ,
ID : u . ID ,
} , http . StatusOK , & perfPwdResetResp )
assert . False ( t , perfPwdResetResp . User . AdminForcedPasswordReset )
oldUserRawPwd := userRawPwd
userRawPwd = newRawPwd
// perform a required password change again, this time it fails as there is no request pending
perfPwdResetResp = performRequiredPasswordResetResponse { }
newRawPwd = "new_password2!"
s . DoJSON ( "POST" , "/api/v1/fleet/perform_required_password_reset" , performRequiredPasswordResetRequest {
Password : newRawPwd ,
ID : u . ID ,
} , http . StatusInternalServerError , & perfPwdResetResp ) // TODO: should be 40?, see #4406
s . token = s . getTestAdminToken ( )
// login as that user to verify that the new password is active (userRawPwd was updated to the new pwd)
loginResp = loginResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/login" , loginRequest { Email : u . Email , Password : userRawPwd } , http . StatusOK , & loginResp )
require . Equal ( t , loginResp . User . ID , u . ID )
// logout for that user
s . token = loginResp . Token
var logoutResp logoutResponse
s . DoJSON ( "POST" , "/api/v1/fleet/logout" , nil , http . StatusOK , & logoutResp )
// logout again, even though not logged in
s . DoJSON ( "POST" , "/api/v1/fleet/logout" , nil , http . StatusInternalServerError , & logoutResp ) // TODO: should be OK even if not logged in, see #4406.
s . token = s . getTestAdminToken ( )
// login as that user with previous pwd fails
loginResp = loginResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/login" , loginRequest { Email : u . Email , Password : oldUserRawPwd } , http . StatusUnauthorized , & loginResp )
2022-01-10 19:43:39 +00:00
// require a password reset
var reqResetResp requirePasswordResetResponse
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/users/%d/require_password_reset" , u . ID ) , map [ string ] bool { "require" : true } , http . StatusOK , & reqResetResp )
assert . Equal ( t , u . ID , reqResetResp . User . ID )
assert . True ( t , reqResetResp . User . AdminForcedPasswordReset )
// require a password reset to invalid user
s . DoJSON ( "POST" , fmt . Sprintf ( "/api/v1/fleet/users/%d/require_password_reset" , u . ID + 1 ) , map [ string ] bool { "require" : true } , http . StatusNotFound , & reqResetResp )
// delete user
var delResp deleteUserResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , nil , http . StatusOK , & delResp )
// delete invalid user
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/users/%d" , u . ID ) , nil , http . StatusNotFound , & delResp )
}
2021-12-23 21:26:55 +00:00
func ( s * integrationTestSuite ) TestGlobalPoliciesAutomationConfig ( ) {
t := s . T ( )
gpParams := globalPolicyRequest {
Name : "policy1" ,
Query : "select 41;" ,
}
gpResp := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams , http . StatusOK , & gpResp )
2022-01-18 16:18:40 +00:00
require . NotNil ( t , gpResp . Policy )
2021-12-23 21:26:55 +00:00
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( fmt . Sprintf ( ` {
"webhook_settings" : {
"failing_policies_webhook" : {
"enable_failing_policies_webhook" : true ,
"destination_url" : "http://some/url" ,
"policy_ids" : [ % d ] ,
"host_batch_size" : 1000
} ,
"interval" : "1h"
}
} ` , gpResp . Policy . ID ) ) , http . StatusOK )
config := s . getConfig ( )
require . True ( t , config . WebhookSettings . FailingPoliciesWebhook . Enable )
require . Equal ( t , "http://some/url" , config . WebhookSettings . FailingPoliciesWebhook . DestinationURL )
require . Equal ( t , [ ] uint { gpResp . Policy . ID } , config . WebhookSettings . FailingPoliciesWebhook . PolicyIDs )
require . Equal ( t , 1 * time . Hour , config . WebhookSettings . Interval . Duration )
require . Equal ( t , 1000 , config . WebhookSettings . FailingPoliciesWebhook . HostBatchSize )
deletePolicyParams := deleteGlobalPoliciesRequest { IDs : [ ] uint { gpResp . Policy . ID } }
deletePolicyResp := deleteGlobalPoliciesResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies/delete" , deletePolicyParams , http . StatusOK , & deletePolicyResp )
config = s . getConfig ( )
require . Empty ( t , config . WebhookSettings . FailingPoliciesWebhook . PolicyIDs )
}
2022-01-18 16:18:40 +00:00
2022-01-27 13:48:46 +00:00
func ( s * integrationTestSuite ) TestVulnerabilitiesWebhookConfig ( ) {
t := s . T ( )
s . DoRaw ( "PATCH" , "/api/v1/fleet/config" , [ ] byte ( ` {
"webhook_settings" : {
"vulnerabilities_webhook" : {
"enable_vulnerabilities_webhook" : true ,
"destination_url" : "http://some/url" ,
"host_batch_size" : 1234
} ,
"interval" : "1h"
}
} ` ) , http . StatusOK )
config := s . getConfig ( )
require . True ( t , config . WebhookSettings . VulnerabilitiesWebhook . Enable )
require . Equal ( t , "http://some/url" , config . WebhookSettings . VulnerabilitiesWebhook . DestinationURL )
require . Equal ( t , 1234 , config . WebhookSettings . VulnerabilitiesWebhook . HostBatchSize )
require . Equal ( t , 1 * time . Hour , config . WebhookSettings . Interval . Duration )
}
2022-01-19 19:07:58 +00:00
func ( s * integrationTestSuite ) TestQueriesBadRequests ( ) {
t := s . T ( )
reqQuery := & fleet . QueryPayload {
Name : ptr . String ( "existing query" ) ,
Query : ptr . String ( "select 42;" ) ,
}
createQueryResp := createQueryResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/queries" , reqQuery , http . StatusOK , & createQueryResp )
require . NotNil ( t , createQueryResp . Query )
existingQueryID := createQueryResp . Query . ID
2022-01-31 21:35:22 +00:00
defer cleanupQuery ( s , existingQueryID )
2022-01-19 19:07:58 +00:00
for _ , tc := range [ ] struct {
tname string
name string
query string
} {
{
tname : "empty name" ,
name : " " , // #3704
query : "select 42;" ,
} ,
{
tname : "empty query" ,
name : "Some name" ,
query : "" ,
} ,
{
tname : "Invalid query" ,
name : "Invalid query" ,
query : "ATTACH 'foo' AS bar;" ,
} ,
} {
t . Run ( tc . tname , func ( t * testing . T ) {
reqQuery := & fleet . QueryPayload {
Name : ptr . String ( tc . name ) ,
Query : ptr . String ( tc . query ) ,
}
createQueryResp := createQueryResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/queries" , reqQuery , http . StatusBadRequest , & createQueryResp )
require . Nil ( t , createQueryResp . Query )
payload := fleet . QueryPayload {
Name : ptr . String ( tc . name ) ,
Query : ptr . String ( tc . query ) ,
}
mResp := modifyQueryResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/queries/%d" , existingQueryID ) , & payload , http . StatusBadRequest , & mResp )
require . Nil ( t , mResp . Query )
} )
}
}
func ( s * integrationTestSuite ) TestPacksBadRequests ( ) {
t := s . T ( )
reqPacks := & fleet . PackPayload {
Name : ptr . String ( "existing pack" ) ,
}
createPackResp := createPackResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/packs" , reqPacks , http . StatusOK , & createPackResp )
existingPackID := createPackResp . Pack . ID
for _ , tc := range [ ] struct {
tname string
name string
} {
{
tname : "empty name" ,
name : " " , // #3704
} ,
} {
t . Run ( tc . tname , func ( t * testing . T ) {
reqQuery := & fleet . PackPayload {
Name : ptr . String ( tc . name ) ,
}
createPackResp := createQueryResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/packs" , reqQuery , http . StatusBadRequest , & createPackResp )
payload := fleet . PackPayload {
Name : ptr . String ( tc . name ) ,
}
mResp := modifyPackResponse { }
s . DoJSON ( "PATCH" , fmt . Sprintf ( "/api/v1/fleet/packs/%d" , existingPackID ) , & payload , http . StatusBadRequest , & mResp )
} )
}
}
2022-01-19 15:52:14 +00:00
func ( s * integrationTestSuite ) TestTeamsEndpointsWithoutLicense ( ) {
t := s . T ( )
// list teams, none
var listResp listTeamsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/teams" , nil , http . StatusPaymentRequired , & listResp )
assert . Len ( t , listResp . Teams , 0 )
2022-02-04 17:33:22 +00:00
// get team
var getResp getTeamResponse
s . DoJSON ( "GET" , "/api/v1/fleet/teams/123" , nil , http . StatusPaymentRequired , & getResp )
assert . Nil ( t , getResp . Team )
2022-01-19 15:52:14 +00:00
// create team
var tmResp teamResponse
s . DoJSON ( "POST" , "/api/v1/fleet/teams" , & createTeamRequest { } , http . StatusPaymentRequired , & tmResp )
assert . Nil ( t , tmResp . Team )
// modify team
s . DoJSON ( "PATCH" , "/api/v1/fleet/teams/123" , fleet . TeamPayload { } , http . StatusPaymentRequired , & tmResp )
assert . Nil ( t , tmResp . Team )
// delete team
var delResp deleteTeamResponse
s . DoJSON ( "DELETE" , "/api/v1/fleet/teams/123" , nil , http . StatusPaymentRequired , & delResp )
2022-02-03 19:24:03 +00:00
// apply team specs
2022-01-19 15:52:14 +00:00
var specResp applyTeamSpecsResponse
teamSpecs := applyTeamSpecsRequest { Specs : [ ] * fleet . TeamSpec { { Name : "newteam" , Secrets : [ ] fleet . EnrollSecret { { Secret : "ABC" } } } } }
2022-02-03 19:24:03 +00:00
s . DoJSON ( "POST" , "/api/v1/fleet/spec/teams" , teamSpecs , http . StatusPaymentRequired , & specResp )
2022-01-19 15:52:14 +00:00
// modify team agent options
s . DoJSON ( "POST" , "/api/v1/fleet/teams/123/agent_options" , nil , http . StatusPaymentRequired , & tmResp )
assert . Nil ( t , tmResp . Team )
// list team users
var usersResp listUsersResponse
s . DoJSON ( "GET" , "/api/v1/fleet/teams/123/users" , nil , http . StatusPaymentRequired , & usersResp , "page" , "1" )
assert . Len ( t , usersResp . Users , 0 )
// add team users
s . DoJSON ( "PATCH" , "/api/v1/fleet/teams/123/users" , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : fleet . User { ID : 1 } } } } , http . StatusPaymentRequired , & tmResp )
assert . Nil ( t , tmResp . Team )
// delete team users
s . DoJSON ( "DELETE" , "/api/v1/fleet/teams/123/users" , modifyTeamUsersRequest { Users : [ ] fleet . TeamUser { { User : fleet . User { ID : 1 } } } } , http . StatusPaymentRequired , & tmResp )
assert . Nil ( t , tmResp . Team )
// get team enroll secrets
var secResp teamEnrollSecretsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/teams/123/secrets" , nil , http . StatusPaymentRequired , & secResp )
assert . Len ( t , secResp . Secrets , 0 )
// modify team enroll secrets
s . DoJSON ( "PATCH" , "/api/v1/fleet/teams/123/secrets" , modifyTeamEnrollSecretsRequest { Secrets : [ ] fleet . EnrollSecret { { Secret : "DEF" } } } , http . StatusPaymentRequired , & secResp )
assert . Len ( t , secResp . Secrets , 0 )
}
2022-01-18 16:18:40 +00:00
// TestGlobalPoliciesBrowsing tests that team users can browse (read) global policies (see #3722).
func ( s * integrationTestSuite ) TestGlobalPoliciesBrowsing ( ) {
t := s . T ( )
team , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
ID : 42 ,
Name : "team_for_global_policies_browsing" ,
Description : "desc team1" ,
} )
require . NoError ( t , err )
gpParams0 := globalPolicyRequest {
Name : "global policy" ,
Query : "select * from osquery;" ,
}
gpResp0 := globalPolicyResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/global/policies" , gpParams0 , http . StatusOK , & gpResp0 )
require . NotNil ( t , gpResp0 . Policy )
email := "team.observer@example.com"
teamObserver := & fleet . User {
Name : "team observer user" ,
Email : email ,
GlobalRole : nil ,
Teams : [ ] fleet . UserTeam {
{
Team : * team ,
Role : fleet . RoleObserver ,
} ,
} ,
}
password := "p4ssw0rd."
require . NoError ( t , teamObserver . SetPassword ( password , 10 , 10 ) )
_ , err = s . ds . NewUser ( context . Background ( ) , teamObserver )
require . NoError ( t , err )
oldToken := s . token
s . token = s . getTestToken ( email , password )
t . Cleanup ( func ( ) {
s . token = oldToken
} )
policiesResponse := listGlobalPoliciesResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/global/policies" , nil , http . StatusOK , & policiesResponse )
require . Len ( t , policiesResponse . Policies , 1 )
assert . Equal ( t , "global policy" , policiesResponse . Policies [ 0 ] . Name )
assert . Equal ( t , "select * from osquery;" , policiesResponse . Policies [ 0 ] . Query )
}
2022-01-19 21:17:42 +00:00
func ( s * integrationTestSuite ) TestTeamPoliciesTeamNotExists ( ) {
t := s . T ( )
teamPoliciesResponse := listTeamPoliciesResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/teams/%d/policies" , 9999999 ) , nil , http . StatusNotFound , & teamPoliciesResponse )
require . Len ( t , teamPoliciesResponse . Policies , 0 )
}
2022-01-25 14:34:00 +00:00
func ( s * integrationTestSuite ) TestSessionInfo ( ) {
t := s . T ( )
ssn := createSession ( t , 1 , s . ds )
var meResp getUserResponse
resp := s . DoRawWithHeaders ( "GET" , "/api/v1/fleet/me" , nil , http . StatusOK , map [ string ] string {
"Authorization" : fmt . Sprintf ( "Bearer %s" , ssn . Key ) ,
} )
require . NoError ( t , json . NewDecoder ( resp . Body ) . Decode ( & meResp ) )
assert . Equal ( t , uint ( 1 ) , meResp . User . ID )
// get info about session
var getResp getInfoAboutSessionResponse
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/sessions/%d" , ssn . ID ) , nil , http . StatusOK , & getResp )
assert . Equal ( t , ssn . ID , getResp . SessionID )
assert . Equal ( t , uint ( 1 ) , getResp . UserID )
// get info about session - non-existing: appears to deliberately return 500 due to forbidden,
// which takes precedence vs the not found returned by the datastore (it still shouldn't be a
// 500 though).
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/sessions/%d" , ssn . ID + 1 ) , nil , http . StatusInternalServerError , & getResp )
// delete session
var delResp deleteSessionResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/sessions/%d" , ssn . ID ) , nil , http . StatusOK , & delResp )
// delete session - non-existing: again, 500 due to forbidden instead of 404.
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/sessions/%d" , ssn . ID ) , nil , http . StatusInternalServerError , & delResp )
}
func ( s * integrationTestSuite ) TestAppConfig ( ) {
t := s . T ( )
// get the app config
var acResp appConfigResponse
s . DoJSON ( "GET" , "/api/v1/fleet/config" , nil , http . StatusOK , & acResp )
assert . Equal ( t , "free" , acResp . License . Tier )
assert . Equal ( t , "" , acResp . OrgInfo . OrgName )
// no server settings set for the URL, so not possible to test the
// certificate endpoint
acResp = appConfigResponse { }
s . DoJSON ( "PATCH" , "/api/v1/fleet/config" , json . RawMessage ( ` {
"org_info" : {
"org_name" : "test"
}
} ` ) , http . StatusOK , & acResp )
assert . Equal ( t , "test" , acResp . OrgInfo . OrgName )
var verResp versionResponse
s . DoJSON ( "GET" , "/api/v1/fleet/version" , nil , http . StatusOK , & verResp )
assert . NotEmpty ( t , verResp . Branch )
// get enroll secrets, none yet
var specResp getEnrollSecretSpecResponse
s . DoJSON ( "GET" , "/api/v1/fleet/spec/enroll_secret" , nil , http . StatusOK , & specResp )
assert . Empty ( t , specResp . Spec . Secrets )
// apply spec, one secret
var applyResp applyEnrollSecretSpecResponse
s . DoJSON ( "POST" , "/api/v1/fleet/spec/enroll_secret" , applyEnrollSecretSpecRequest {
Spec : & fleet . EnrollSecretSpec {
Secrets : [ ] * fleet . EnrollSecret { { Secret : "XYZ" } } ,
} ,
} , http . StatusOK , & applyResp )
// get enroll secrets, one
s . DoJSON ( "GET" , "/api/v1/fleet/spec/enroll_secret" , nil , http . StatusOK , & specResp )
require . Len ( t , specResp . Spec . Secrets , 1 )
assert . Equal ( t , "XYZ" , specResp . Spec . Secrets [ 0 ] . Secret )
// remove secret just to prevent affecting other tests
s . DoJSON ( "POST" , "/api/v1/fleet/spec/enroll_secret" , applyEnrollSecretSpecRequest {
Spec : & fleet . EnrollSecretSpec { } ,
} , http . StatusOK , & applyResp )
s . DoJSON ( "GET" , "/api/v1/fleet/spec/enroll_secret" , nil , http . StatusOK , & specResp )
require . Len ( t , specResp . Spec . Secrets , 0 )
}
2022-01-31 21:35:22 +00:00
func ( s * integrationTestSuite ) TestQuerySpecs ( ) {
t := s . T ( )
// list specs, none yet
var getSpecsResp getQuerySpecsResponse
s . DoJSON ( "GET" , "/api/v1/fleet/spec/queries" , nil , http . StatusOK , & getSpecsResp )
assert . Len ( t , getSpecsResp . Specs , 0 )
// get unknown one
var getSpecResp getQuerySpecResponse
s . DoJSON ( "GET" , "/api/v1/fleet/spec/queries/nonesuch" , nil , http . StatusNotFound , & getSpecResp )
// create some queries via apply specs
q1 := strings . ReplaceAll ( t . Name ( ) , "/" , "_" )
q2 := q1 + "_2"
var applyResp applyQuerySpecsResponse
s . DoJSON ( "POST" , "/api/v1/fleet/spec/queries" , applyQuerySpecsRequest {
Specs : [ ] * fleet . QuerySpec {
{ Name : q1 , Query : "SELECT 1" } ,
{ Name : q2 , Query : "SELECT 2" } ,
} ,
} , http . StatusOK , & applyResp )
// get the queries back
var listQryResp listQueriesResponse
s . DoJSON ( "GET" , "/api/v1/fleet/queries" , nil , http . StatusOK , & listQryResp , "order_key" , "name" )
require . Len ( t , listQryResp . Queries , 2 )
assert . Equal ( t , q1 , listQryResp . Queries [ 0 ] . Name )
assert . Equal ( t , q2 , listQryResp . Queries [ 1 ] . Name )
q1ID , q2ID := listQryResp . Queries [ 0 ] . ID , listQryResp . Queries [ 1 ] . ID
// list specs
s . DoJSON ( "GET" , "/api/v1/fleet/spec/queries" , nil , http . StatusOK , & getSpecsResp )
require . Len ( t , getSpecsResp . Specs , 2 )
names := [ ] string { getSpecsResp . Specs [ 0 ] . Name , getSpecsResp . Specs [ 1 ] . Name }
assert . ElementsMatch ( t , [ ] string { q1 , q2 } , names )
// get specific spec
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/spec/queries/%s" , q1 ) , nil , http . StatusOK , & getSpecResp )
assert . Equal ( t , getSpecResp . Spec . Query , "SELECT 1" )
// apply specs again - create q3 and update q2
q3 := q1 + "_3"
s . DoJSON ( "POST" , "/api/v1/fleet/spec/queries" , applyQuerySpecsRequest {
Specs : [ ] * fleet . QuerySpec {
{ Name : q2 , Query : "SELECT -2" } ,
{ Name : q3 , Query : "SELECT 3" } ,
} ,
} , http . StatusOK , & applyResp )
// list specs - has 3, not 4 (one was an update)
s . DoJSON ( "GET" , "/api/v1/fleet/spec/queries" , nil , http . StatusOK , & getSpecsResp )
require . Len ( t , getSpecsResp . Specs , 3 )
names = [ ] string { getSpecsResp . Specs [ 0 ] . Name , getSpecsResp . Specs [ 1 ] . Name , getSpecsResp . Specs [ 2 ] . Name }
assert . ElementsMatch ( t , [ ] string { q1 , q2 , q3 } , names )
// get the queries back again
s . DoJSON ( "GET" , "/api/v1/fleet/queries" , nil , http . StatusOK , & listQryResp , "order_key" , "name" )
require . Len ( t , listQryResp . Queries , 3 )
assert . Equal ( t , q1ID , listQryResp . Queries [ 0 ] . ID )
assert . Equal ( t , q2ID , listQryResp . Queries [ 1 ] . ID )
assert . Equal ( t , "SELECT -2" , listQryResp . Queries [ 1 ] . Query )
q3ID := listQryResp . Queries [ 2 ] . ID
// delete all queries created
var delBatchResp deleteQueriesResponse
s . DoJSON ( "POST" , "/api/v1/fleet/queries/delete" , map [ string ] interface { } {
2022-02-14 18:13:44 +00:00
"ids" : [ ] uint { q1ID , q2ID , q3ID } ,
} , http . StatusOK , & delBatchResp )
2022-01-31 21:35:22 +00:00
assert . Equal ( t , uint ( 3 ) , delBatchResp . Deleted )
}
2022-02-09 15:16:50 +00:00
func ( s * integrationTestSuite ) TestPaginateListSoftware ( ) {
t := s . T ( )
// create a few hosts specific to this test
hosts := make ( [ ] * fleet . Host , 20 )
for i := range hosts {
host , err := s . ds . NewHost ( context . Background ( ) , & fleet . Host {
DetailUpdatedAt : time . Now ( ) ,
LabelUpdatedAt : time . Now ( ) ,
PolicyUpdatedAt : time . Now ( ) ,
SeenTime : time . Now ( ) ,
NodeKey : t . Name ( ) + strconv . Itoa ( i ) ,
OsqueryHostID : t . Name ( ) + strconv . Itoa ( i ) ,
UUID : t . Name ( ) + strconv . Itoa ( i ) ,
Hostname : t . Name ( ) + "foo" + strconv . Itoa ( i ) + ".local" ,
PrimaryIP : "192.168.1." + strconv . Itoa ( i ) ,
PrimaryMac : fmt . Sprintf ( "30-65-EC-6F-C4-%02d" , i ) ,
} )
require . NoError ( t , err )
require . NotNil ( t , host )
hosts [ i ] = host
}
// create a bunch of software
sws := make ( [ ] fleet . Software , 20 )
for i := range sws {
sw := fleet . Software { Name : "sw" + strconv . Itoa ( i ) , Version : "0.0." + strconv . Itoa ( i ) , Source : "apps" }
sws [ i ] = sw
}
// mark them as installed on the hosts, with host at index 0 having all 20,
// at index 1 having 19, index 2 = 18, etc. until index 19 = 1. So software
// sws[0] is only used by 1 host, while sws[19] is used by all.
for i , h := range hosts {
require . NoError ( t , s . ds . UpdateHostSoftware ( context . Background ( ) , h . ID , sws [ i : ] ) )
require . NoError ( t , s . ds . LoadHostSoftware ( context . Background ( ) , h ) )
if i == 0 {
// this host has all software, refresh the list so we have the software.ID filled
sws = h . Software
}
}
for i , sw := range sws {
cpe := "somecpe" + strconv . Itoa ( i )
require . NoError ( t , s . ds . AddCPEForSoftware ( context . Background ( ) , sw , cpe ) )
if i < 10 {
// add CVEs for the first 10 software, which are the least used (lower hosts_count)
_ , err := s . ds . InsertCVEForCPE ( context . Background ( ) , fmt . Sprintf ( "cve-123-123-%03d" , i ) , [ ] string { cpe } )
require . NoError ( t , err )
}
}
2022-02-28 18:55:14 +00:00
// create a team and make the last 3 hosts part of it (meaning 3 that use
// sws[19], 2 for sws[18], and 1 for sws[17])
tm , err := s . ds . NewTeam ( context . Background ( ) , & fleet . Team {
Name : t . Name ( ) ,
} )
require . NoError ( t , err )
require . NoError ( t , s . ds . AddHostsToTeam ( context . Background ( ) , & tm . ID , [ ] uint { hosts [ 19 ] . ID , hosts [ 18 ] . ID , hosts [ 17 ] . ID } ) )
2022-02-09 15:16:50 +00:00
assertResp := func ( resp listSoftwareResponse , want [ ] fleet . Software , ts time . Time , counts ... int ) {
require . Len ( t , resp . Software , len ( want ) )
for i := range resp . Software {
wantID , gotID := want [ i ] . ID , resp . Software [ i ] . ID
assert . Equal ( t , wantID , gotID )
wantCount , gotCount := counts [ i ] , resp . Software [ i ] . HostsCount
assert . Equal ( t , wantCount , gotCount )
}
if ts . IsZero ( ) {
assert . Nil ( t , resp . CountsUpdatedAt )
} else {
require . NotNil ( t , resp . CountsUpdatedAt )
assert . WithinDuration ( t , ts , * resp . CountsUpdatedAt , time . Second )
}
}
// no software host counts have been calculated yet, so this returns nothing
var lsResp listSoftwareResponse
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , nil , time . Time { } )
2022-02-28 18:55:14 +00:00
// same with a team filter
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "order_key" , "hosts_count" , "order_direction" , "desc" , "team_id" , fmt . Sprintf ( "%d" , tm . ID ) )
assertResp ( lsResp , nil , time . Time { } )
2022-02-09 15:16:50 +00:00
// calculate hosts counts
hostsCountTs := time . Now ( ) . UTC ( )
require . NoError ( t , s . ds . CalculateHostsPerSoftware ( context . Background ( ) , hostsCountTs ) )
// now the list software endpoint returns the software, get the first page without vulns
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "5" , "page" , "0" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 19 ] , sws [ 18 ] , sws [ 17 ] , sws [ 16 ] , sws [ 15 ] } , hostsCountTs , 20 , 19 , 18 , 17 , 16 )
// second page (page=1)
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "5" , "page" , "1" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 14 ] , sws [ 13 ] , sws [ 12 ] , sws [ 11 ] , sws [ 10 ] } , hostsCountTs , 15 , 14 , 13 , 12 , 11 )
// third page (page=2)
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "5" , "page" , "2" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 9 ] , sws [ 8 ] , sws [ 7 ] , sws [ 6 ] , sws [ 5 ] } , hostsCountTs , 10 , 9 , 8 , 7 , 6 )
// last page (page=3)
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "5" , "page" , "3" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 4 ] , sws [ 3 ] , sws [ 2 ] , sws [ 1 ] , sws [ 0 ] } , hostsCountTs , 5 , 4 , 3 , 2 , 1 )
// past the end
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "5" , "page" , "4" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , nil , time . Time { } )
// no explicit sort order, defaults to hosts_count DESC
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "2" , "page" , "0" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 19 ] , sws [ 18 ] } , hostsCountTs , 20 , 19 )
// hosts_count ascending
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "3" , "page" , "0" , "order_key" , "hosts_count" , "order_direction" , "asc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 0 ] , sws [ 1 ] , sws [ 2 ] } , hostsCountTs , 1 , 2 , 3 )
// vulnerable software only
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "vulnerable" , "true" , "per_page" , "5" , "page" , "0" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 9 ] , sws [ 8 ] , sws [ 7 ] , sws [ 6 ] , sws [ 5 ] } , hostsCountTs , 10 , 9 , 8 , 7 , 6 )
// vulnerable software only, next page
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "vulnerable" , "true" , "per_page" , "5" , "page" , "1" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , [ ] fleet . Software { sws [ 4 ] , sws [ 3 ] , sws [ 2 ] , sws [ 1 ] , sws [ 0 ] } , hostsCountTs , 5 , 4 , 3 , 2 , 1 )
// vulnerable software only, past last page
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "vulnerable" , "true" , "per_page" , "5" , "page" , "2" , "order_key" , "hosts_count" , "order_direction" , "desc" )
assertResp ( lsResp , nil , time . Time { } )
2022-02-28 18:55:14 +00:00
// filter by the team, 2 by page
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "2" , "page" , "0" , "order_key" , "hosts_count" , "order_direction" , "desc" , "team_id" , fmt . Sprintf ( "%d" , tm . ID ) )
assertResp ( lsResp , [ ] fleet . Software { sws [ 19 ] , sws [ 18 ] } , hostsCountTs , 3 , 2 )
// filter by the team, 2 by page, next page
lsResp = listSoftwareResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/software" , nil , http . StatusOK , & lsResp , "per_page" , "2" , "page" , "1" , "order_key" , "hosts_count" , "order_direction" , "desc" , "team_id" , fmt . Sprintf ( "%d" , tm . ID ) )
assertResp ( lsResp , [ ] fleet . Software { sws [ 17 ] } , hostsCountTs , 1 )
2022-02-09 15:16:50 +00:00
}
2022-02-15 20:22:19 +00:00
func ( s * integrationTestSuite ) TestChangeUserEmail ( ) {
t := s . T ( )
// create a new test user
user := & fleet . User {
Name : t . Name ( ) ,
Email : "testchangeemail@example.com" ,
GlobalRole : ptr . String ( fleet . RoleObserver ) ,
}
userRawPwd := "foobarbaz1234!"
err := user . SetPassword ( userRawPwd , 10 , 10 )
require . Nil ( t , err )
user , err = s . ds . NewUser ( context . Background ( ) , user )
require . Nil ( t , err )
// try to change email with an invalid token
var changeResp changeEmailResponse
s . DoJSON ( "GET" , "/api/v1/fleet/email/change/invalidtoken" , nil , http . StatusNotFound , & changeResp )
// create a valid token for the test user
err = s . ds . PendingEmailChange ( context . Background ( ) , user . ID , "testchangeemail2@example.com" , "validtoken" )
require . Nil ( t , err )
// try to change email with a valid token, but request made from different user
changeResp = changeEmailResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/email/change/validtoken" , nil , http . StatusNotFound , & changeResp )
// switch to the test user and make the change email request
s . token = s . getTestToken ( user . Email , userRawPwd )
defer func ( ) { s . token = s . getTestAdminToken ( ) } ( )
changeResp = changeEmailResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/email/change/validtoken" , nil , http . StatusOK , & changeResp )
require . Equal ( t , "testchangeemail2@example.com" , changeResp . NewEmail )
// using the token consumes it, so making another request with the same token fails
changeResp = changeEmailResponse { }
s . DoJSON ( "GET" , "/api/v1/fleet/email/change/validtoken" , nil , http . StatusNotFound , & changeResp )
}
func ( s * integrationTestSuite ) TestSearchTargets ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
lblIDs , err := s . ds . LabelIDsByName ( context . Background ( ) , [ ] string { "All Hosts" } )
require . NoError ( t , err )
require . Len ( t , lblIDs , 1 )
// no search criteria
var searchResp searchTargetsResponse
s . DoJSON ( "POST" , "/api/v1/fleet/targets" , searchTargetsRequest { } , http . StatusOK , & searchResp )
require . Equal ( t , uint ( 0 ) , searchResp . TargetsCount )
require . Len ( t , searchResp . Targets . Hosts , len ( hosts ) ) // the HostTargets.HostIDs are actually host IDs to *omit* from the search
require . Len ( t , searchResp . Targets . Labels , 1 )
require . Len ( t , searchResp . Targets . Teams , 0 )
searchResp = searchTargetsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/targets" , searchTargetsRequest { Selected : fleet . HostTargets { LabelIDs : lblIDs } } , http . StatusOK , & searchResp )
require . Equal ( t , uint ( 0 ) , searchResp . TargetsCount )
require . Len ( t , searchResp . Targets . Hosts , len ( hosts ) ) // no omitted host id
require . Len ( t , searchResp . Targets . Labels , 0 ) // labels have been omitted
require . Len ( t , searchResp . Targets . Teams , 0 )
searchResp = searchTargetsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/targets" , searchTargetsRequest { Selected : fleet . HostTargets { HostIDs : [ ] uint { hosts [ 1 ] . ID } } } , http . StatusOK , & searchResp )
require . Equal ( t , uint ( 1 ) , searchResp . TargetsCount )
require . Len ( t , searchResp . Targets . Hosts , len ( hosts ) - 1 ) // one omitted host id
require . Len ( t , searchResp . Targets . Labels , 1 ) // labels have not been omitted
require . Len ( t , searchResp . Targets . Teams , 0 )
searchResp = searchTargetsResponse { }
s . DoJSON ( "POST" , "/api/v1/fleet/targets" , searchTargetsRequest { MatchQuery : "foo.local1" } , http . StatusOK , & searchResp )
require . Equal ( t , uint ( 0 ) , searchResp . TargetsCount )
require . Len ( t , searchResp . Targets . Hosts , 1 )
require . Len ( t , searchResp . Targets . Labels , 1 )
require . Len ( t , searchResp . Targets . Teams , 0 )
require . Contains ( t , searchResp . Targets . Hosts [ 0 ] . Hostname , "foo.local1" )
}
func ( s * integrationTestSuite ) TestStatus ( ) {
var statusResp statusResponse
s . DoJSON ( "GET" , "/api/v1/fleet/status/result_store" , nil , http . StatusOK , & statusResp )
s . DoJSON ( "GET" , "/api/v1/fleet/status/live_query" , nil , http . StatusOK , & statusResp )
}
2022-03-07 18:10:55 +00:00
func ( s * integrationTestSuite ) TestOsqueryConfig ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
req := getClientConfigRequest { NodeKey : hosts [ 0 ] . NodeKey }
var resp getClientConfigResponse
s . DoJSON ( "POST" , "/api/v1/osquery/config" , req , http . StatusOK , & resp )
// test with invalid node key
var errRes map [ string ] interface { }
req . NodeKey += "zzzz"
s . DoJSON ( "POST" , "/api/v1/osquery/config" , req , http . StatusUnauthorized , & errRes )
assert . Contains ( t , errRes [ "error" ] , "invalid node key" )
}
2022-03-08 16:27:38 +00:00
func ( s * integrationTestSuite ) TestEnrollHost ( ) {
t := s . T ( )
// set the enroll secret
var applyResp applyEnrollSecretSpecResponse
s . DoJSON ( "POST" , "/api/v1/fleet/spec/enroll_secret" , applyEnrollSecretSpecRequest {
Spec : & fleet . EnrollSecretSpec {
Secrets : [ ] * fleet . EnrollSecret { { Secret : t . Name ( ) } } ,
} ,
} , http . StatusOK , & applyResp )
// invalid enroll secret fails
j , err := json . Marshal ( & enrollAgentRequest {
EnrollSecret : "nosuchsecret" ,
HostIdentifier : "abcd" ,
} )
require . NoError ( t , err )
s . DoRawNoAuth ( "POST" , "/api/v1/osquery/enroll" , j , http . StatusUnauthorized )
// valid enroll secret succeeds
j , err = json . Marshal ( & enrollAgentRequest {
EnrollSecret : t . Name ( ) ,
HostIdentifier : t . Name ( ) ,
} )
require . NoError ( t , err )
var resp enrollAgentResponse
hres := s . DoRawNoAuth ( "POST" , "/api/v1/osquery/enroll" , j , http . StatusOK )
defer hres . Body . Close ( )
require . NoError ( t , json . NewDecoder ( hres . Body ) . Decode ( & resp ) )
require . NotEmpty ( t , resp . NodeKey )
}
func ( s * integrationTestSuite ) TestCarve ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
// begin a carve with an invalid node key
var errRes map [ string ] interface { }
s . DoJSON ( "POST" , "/api/v1/osquery/carve/begin" , carveBeginRequest {
NodeKey : hosts [ 0 ] . NodeKey + "zzz" ,
BlockCount : 1 ,
BlockSize : 1 ,
CarveSize : 1 ,
CarveId : "c1" ,
} , http . StatusUnauthorized , & errRes )
assert . Contains ( t , errRes [ "error" ] , "invalid node key" )
// invalid carve size
s . DoJSON ( "POST" , "/api/v1/osquery/carve/begin" , carveBeginRequest {
NodeKey : hosts [ 0 ] . NodeKey ,
BlockCount : 3 ,
BlockSize : 3 ,
CarveSize : 0 ,
CarveId : "c1" ,
} , http . StatusInternalServerError , & errRes ) // TODO: should be 4xx, see #4406
assert . Contains ( t , errRes [ "error" ] , "carve_size must be greater" )
// invalid block size too big
s . DoJSON ( "POST" , "/api/v1/osquery/carve/begin" , carveBeginRequest {
NodeKey : hosts [ 0 ] . NodeKey ,
BlockCount : 3 ,
BlockSize : maxBlockSize + 1 ,
CarveSize : maxCarveSize ,
CarveId : "c1" ,
} , http . StatusInternalServerError , & errRes ) // TODO: should be 4xx, see #4406
assert . Contains ( t , errRes [ "error" ] , "block_size exceeds max" )
// invalid carve size too big
s . DoJSON ( "POST" , "/api/v1/osquery/carve/begin" , carveBeginRequest {
NodeKey : hosts [ 0 ] . NodeKey ,
BlockCount : 3 ,
BlockSize : maxBlockSize ,
CarveSize : maxCarveSize + 1 ,
CarveId : "c1" ,
} , http . StatusInternalServerError , & errRes ) // TODO: should be 4xx, see #4406
assert . Contains ( t , errRes [ "error" ] , "carve_size exceeds max" )
// invalid carve size, does not match blocks
s . DoJSON ( "POST" , "/api/v1/osquery/carve/begin" , carveBeginRequest {
NodeKey : hosts [ 0 ] . NodeKey ,
BlockCount : 3 ,
BlockSize : 3 ,
CarveSize : 1 ,
CarveId : "c1" ,
} , http . StatusInternalServerError , & errRes ) // TODO: should be 4xx, see #4406
assert . Contains ( t , errRes [ "error" ] , "carve_size does not match" )
// valid carve begin
var beginResp carveBeginResponse
s . DoJSON ( "POST" , "/api/v1/osquery/carve/begin" , carveBeginRequest {
NodeKey : hosts [ 0 ] . NodeKey ,
BlockCount : 3 ,
BlockSize : 3 ,
CarveSize : 8 ,
CarveId : "c1" ,
RequestId : "r1" ,
} , http . StatusOK , & beginResp )
require . NotEmpty ( t , beginResp . SessionId )
sid := beginResp . SessionId
// sending a block with invalid session id
var blockResp carveBlockResponse
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 1 ,
SessionId : sid + "zz" ,
RequestId : "??" ,
Data : [ ] byte ( "p1." ) ,
} , http . StatusNotFound , & blockResp )
// sending a block with valid session id but invalid request id
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 1 ,
SessionId : sid ,
RequestId : "??" ,
Data : [ ] byte ( "p1." ) ,
} , http . StatusInternalServerError , & blockResp ) // TODO: should be 400, see #4406
// sending a block with unexpected block id (expects 0, got 1)
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 1 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p1." ) ,
} , http . StatusInternalServerError , & blockResp ) // TODO: should be 400, see #4406
// sending a block with valid payload, block 0
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 0 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p1." ) ,
} , http . StatusOK , & blockResp )
require . True ( t , blockResp . Success )
// sending next block
blockResp = carveBlockResponse { }
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 1 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p2." ) ,
} , http . StatusOK , & blockResp )
require . True ( t , blockResp . Success )
// sending already-sent block again
blockResp = carveBlockResponse { }
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 1 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p2." ) ,
} , http . StatusInternalServerError , & blockResp ) // TODO: should be 400, see #4406
// sending final block with too many bytes
blockResp = carveBlockResponse { }
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 2 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p3extra" ) ,
} , http . StatusInternalServerError , & blockResp ) // TODO: should be 400, see #4406
// sending actual final block
blockResp = carveBlockResponse { }
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 2 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p3" ) ,
} , http . StatusOK , & blockResp )
require . True ( t , blockResp . Success )
// sending unexpected block
blockResp = carveBlockResponse { }
s . DoJSON ( "POST" , "/api/v1/osquery/carve/block" , carveBlockRequest {
BlockId : 3 ,
SessionId : sid ,
RequestId : "r1" ,
Data : [ ] byte ( "p4." ) ,
} , http . StatusInternalServerError , & blockResp ) // TODO: should be 400, see #4406
}
func ( s * integrationTestSuite ) TestPasswordReset ( ) {
t := s . T ( )
// create a new user
var createResp createUserResponse
userRawPwd := "passw0rd!"
params := fleet . UserPayload {
Name : ptr . String ( "forgotpwd" ) ,
Email : ptr . String ( "forgotpwd@example.com" ) ,
Password : ptr . String ( userRawPwd ) ,
GlobalRole : ptr . String ( fleet . RoleObserver ) ,
}
s . DoJSON ( "POST" , "/api/v1/fleet/users/admin" , params , http . StatusOK , & createResp )
require . NotZero ( t , createResp . User . ID )
u := * createResp . User
_ = u
// request forgot password, invalid email
res := s . DoRawNoAuth ( "POST" , "/api/v1/fleet/forgot_password" , jsonMustMarshal ( t , forgotPasswordRequest { Email : "invalid@asd.com" } ) , http . StatusAccepted )
res . Body . Close ( )
// TODO: tested manually (adds too much time to the test), works but hitting the rate
// limit returns 500 instead of 429, see #4406. We get the authz check missing error instead.
//// trigger the rate limit with a batch of requests in a short burst
//for i := 0; i < 20; i++ {
// s.DoJSON("POST", "/api/v1/fleet/forgot_password", forgotPasswordRequest{Email: "invalid@asd.com"}, http.StatusAccepted, &forgotResp)
//}
// request forgot password, valid email
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/forgot_password" , jsonMustMarshal ( t , forgotPasswordRequest { Email : u . Email } ) , http . StatusAccepted )
res . Body . Close ( )
var token string
mysql . ExecAdhocSQL ( t , s . ds , func ( db sqlx . ExtContext ) error {
return sqlx . GetContext ( context . Background ( ) , db , & token , "SELECT token FROM password_reset_requests WHERE user_id = ?" , u . ID )
} )
// proceed with reset password
userNewPwd := "newpassw0rd!"
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/reset_password" , jsonMustMarshal ( t , resetPasswordRequest { PasswordResetToken : token , NewPassword : userNewPwd } ) , http . StatusOK )
res . Body . Close ( )
// attempt it again with already-used token
userUnusedPwd := "unusedpassw0rd!"
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/reset_password" , jsonMustMarshal ( t , resetPasswordRequest { PasswordResetToken : token , NewPassword : userUnusedPwd } ) , http . StatusInternalServerError ) // TODO: should be 40x, see #4406
res . Body . Close ( )
// login with the old password, should not succeed
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/login" , jsonMustMarshal ( t , loginRequest { Email : u . Email , Password : userRawPwd } ) , http . StatusUnauthorized )
res . Body . Close ( )
// login with the new password, should succeed
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/login" , jsonMustMarshal ( t , loginRequest { Email : u . Email , Password : userNewPwd } ) , http . StatusOK )
res . Body . Close ( )
}
2022-03-09 21:13:56 +00:00
func ( s * integrationTestSuite ) TestDeviceAuthenticatedEndpoints ( ) {
t := s . T ( )
hosts := s . createHosts ( t )
// create some mappings and MDM/Munki data
s . ds . ReplaceHostDeviceMapping ( context . Background ( ) , hosts [ 0 ] . ID , [ ] * fleet . HostDeviceMapping {
{ HostID : hosts [ 0 ] . ID , Email : "a@b.c" , Source : "google_chrome_profiles" } ,
{ HostID : hosts [ 0 ] . ID , Email : "b@b.c" , Source : "google_chrome_profiles" } ,
} )
require . NoError ( t , s . ds . SetOrUpdateMDMData ( context . Background ( ) , hosts [ 0 ] . ID , true , "url" , false ) )
require . NoError ( t , s . ds . SetOrUpdateMunkiVersion ( context . Background ( ) , hosts [ 0 ] . ID , "1.3.0" ) )
// create an auth token for hosts[0]
token := "much_valid"
mysql . ExecAdhocSQL ( t , s . ds , func ( db sqlx . ExtContext ) error {
_ , err := db . ExecContext ( context . Background ( ) , ` INSERT INTO host_device_auth (host_id, token) VALUES (?, ?) ` , hosts [ 0 ] . ID , token )
return err
} )
// get host without token
res := s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/" , nil , http . StatusNotFound )
res . Body . Close ( )
// get host with invalid token
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/no_such_token" , nil , http . StatusUnauthorized )
res . Body . Close ( )
// get host with valid token
var getHostResp getHostResponse
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/" + token , nil , http . StatusOK )
json . NewDecoder ( res . Body ) . Decode ( & getHostResp )
res . Body . Close ( )
require . Equal ( t , hosts [ 0 ] . ID , getHostResp . Host . ID )
require . False ( t , getHostResp . Host . RefetchRequested )
hostDevResp := getHostResp . Host
// make request for same host on the host details API endpoint, responses should match
getHostResp = getHostResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & getHostResp )
require . Equal ( t , hostDevResp , getHostResp . Host )
// request a refetch for that valid host
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/device/" + token + "/refetch" , nil , http . StatusOK )
res . Body . Close ( )
// host should have that flag turned to true
getHostResp = getHostResponse { }
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/" + token , nil , http . StatusOK )
json . NewDecoder ( res . Body ) . Decode ( & getHostResp )
res . Body . Close ( )
require . True ( t , getHostResp . Host . RefetchRequested )
// request a refetch for an invalid token
res = s . DoRawNoAuth ( "POST" , "/api/v1/fleet/device/no_such_token/refetch" , nil , http . StatusUnauthorized )
res . Body . Close ( )
// list device mappings for valid token
var listDMResp listHostDeviceMappingResponse
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/" + token + "/device_mapping" , nil , http . StatusOK )
json . NewDecoder ( res . Body ) . Decode ( & listDMResp )
res . Body . Close ( )
require . Equal ( t , hosts [ 0 ] . ID , listDMResp . HostID )
require . Len ( t , listDMResp . DeviceMapping , 2 )
devDMs := listDMResp . DeviceMapping
// compare response with standard list device mapping API for that same host
listDMResp = listHostDeviceMappingResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/device_mapping" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & listDMResp )
require . Equal ( t , hosts [ 0 ] . ID , listDMResp . HostID )
require . Equal ( t , devDMs , listDMResp . DeviceMapping )
// list device mappings for invalid token
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/no_such_token/device_mapping" , nil , http . StatusUnauthorized )
res . Body . Close ( )
// get macadmins for valid token
var getMacadm getMacadminsDataResponse
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/" + token + "/macadmins" , nil , http . StatusOK )
json . NewDecoder ( res . Body ) . Decode ( & getMacadm )
res . Body . Close ( )
require . Equal ( t , "1.3.0" , getMacadm . Macadmins . Munki . Version )
devMacadm := getMacadm . Macadmins
// compare response with standard macadmins API for that same host
getMacadm = getMacadminsDataResponse { }
s . DoJSON ( "GET" , fmt . Sprintf ( "/api/v1/fleet/hosts/%d/macadmins" , hosts [ 0 ] . ID ) , nil , http . StatusOK , & getMacadm )
require . Equal ( t , devMacadm , getMacadm . Macadmins )
// get macadmins for invalid token
res = s . DoRawNoAuth ( "GET" , "/api/v1/fleet/device/no_such_token/macadmins" , nil , http . StatusUnauthorized )
res . Body . Close ( )
}
2022-01-25 14:34:00 +00:00
// creates a session and returns it, its key is to be passed as authorization header.
func createSession ( t * testing . T , uid uint , ds fleet . Datastore ) * fleet . Session {
key := make ( [ ] byte , 64 )
_ , err := rand . Read ( key )
require . NoError ( t , err )
sessionKey := base64 . StdEncoding . EncodeToString ( key )
session := & fleet . Session {
UserID : uid ,
Key : sessionKey ,
AccessedAt : time . Now ( ) . UTC ( ) ,
}
ssn , err := ds . NewSession ( context . Background ( ) , session )
require . NoError ( t , err )
return ssn
}
2022-01-31 21:35:22 +00:00
func cleanupQuery ( s * integrationTestSuite , queryID uint ) {
var delResp deleteQueryByIDResponse
s . DoJSON ( "DELETE" , fmt . Sprintf ( "/api/v1/fleet/queries/id/%d" , queryID ) , nil , http . StatusOK , & delResp )
}
2022-03-08 16:27:38 +00:00
func jsonMustMarshal ( t testing . TB , v interface { } ) [ ] byte {
b , err := json . Marshal ( v )
require . NoError ( t , err )
return b
}