2021-07-16 18:28:13 +00:00
package main
import (
2023-05-02 19:03:10 +00:00
"bytes"
2021-09-14 12:11:07 +00:00
"context"
2023-05-02 19:03:10 +00:00
"crypto/sha256"
2021-07-19 19:48:49 +00:00
"database/sql"
"encoding/json"
2022-02-18 18:42:39 +00:00
"errors"
2023-04-12 19:11:04 +00:00
"fmt"
2023-05-02 19:03:10 +00:00
"io"
"net/http"
"net/http/httptest"
2021-07-16 18:28:13 +00:00
"os"
2023-04-17 15:08:55 +00:00
"path/filepath"
2023-04-25 13:36:01 +00:00
"sort"
2023-05-02 19:03:10 +00:00
"strconv"
"sync"
2021-07-16 18:28:13 +00:00
"testing"
"time"
2023-05-02 19:03:10 +00:00
"github.com/fleetdm/fleet/v4/pkg/optjson"
2021-07-16 18:28:13 +00:00
"github.com/fleetdm/fleet/v4/server/fleet"
2022-09-19 17:53:44 +00:00
"github.com/fleetdm/fleet/v4/server/mock"
2023-04-17 15:08:55 +00:00
nanomdm_mock "github.com/fleetdm/fleet/v4/server/mock/nanomdm"
2021-07-16 18:28:13 +00:00
"github.com/fleetdm/fleet/v4/server/ptr"
2021-07-19 19:48:49 +00:00
"github.com/fleetdm/fleet/v4/server/service"
2023-04-17 15:08:55 +00:00
"github.com/google/uuid"
2021-07-16 18:28:13 +00:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var userRoleSpecList = [ ] * fleet . User {
2021-09-14 12:11:07 +00:00
{
2021-07-16 18:28:13 +00:00
UpdateCreateTimestamps : fleet . UpdateCreateTimestamps {
CreateTimestamp : fleet . CreateTimestamp { CreatedAt : time . Now ( ) } ,
UpdateTimestamp : fleet . UpdateTimestamp { UpdatedAt : time . Now ( ) } ,
} ,
ID : 42 ,
Name : "Test Name admin1@example.com" ,
Email : "admin1@example.com" ,
GlobalRole : ptr . String ( fleet . RoleAdmin ) ,
} ,
2021-09-14 12:11:07 +00:00
{
2021-07-16 18:28:13 +00:00
UpdateCreateTimestamps : fleet . UpdateCreateTimestamps {
CreateTimestamp : fleet . CreateTimestamp { CreatedAt : time . Now ( ) } ,
UpdateTimestamp : fleet . UpdateTimestamp { UpdatedAt : time . Now ( ) } ,
} ,
ID : 23 ,
Name : "Test Name2 admin2@example.com" ,
Email : "admin2@example.com" ,
GlobalRole : nil ,
Teams : [ ] fleet . UserTeam { } ,
} ,
}
func TestApplyUserRoles ( t * testing . T ) {
2021-09-15 19:27:53 +00:00
_ , ds := runServerWithMockedDS ( t )
2021-07-16 18:28:13 +00:00
2021-09-14 12:11:07 +00:00
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
2021-07-16 18:28:13 +00:00
return userRoleSpecList , nil
}
2021-09-14 12:11:07 +00:00
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
2021-07-16 18:28:13 +00:00
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
2021-09-14 12:11:07 +00:00
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
2021-07-16 18:28:13 +00:00
return & fleet . Team {
ID : 1 ,
CreatedAt : time . Now ( ) ,
Name : "team1" ,
} , nil
}
2021-09-14 12:11:07 +00:00
ds . SaveUsersFunc = func ( ctx context . Context , users [ ] * fleet . User ) error {
2021-07-16 18:28:13 +00:00
for _ , u := range users {
switch u . Email {
case "admin1@example.com" :
userRoleList [ 0 ] = u
case "admin2@example.com" :
userRoleList [ 1 ] = u
}
}
return nil
}
2023-01-24 16:20:02 +00:00
tmpFile , err := os . CreateTemp ( os . TempDir ( ) , "*.yml" )
2021-07-16 18:28:13 +00:00
require . NoError ( t , err )
defer os . Remove ( tmpFile . Name ( ) )
2022-12-05 22:50:49 +00:00
_ , err = tmpFile . WriteString ( `
2021-07-16 18:28:13 +00:00
-- -
apiVersion : v1
kind : user_roles
spec :
roles :
admin1 @ example . com :
global_role : admin
teams : null
admin2 @ example . com :
global_role : null
teams :
- role : maintainer
team : team1
` )
2022-12-05 22:50:49 +00:00
require . NoError ( t , err )
2021-07-16 18:28:13 +00:00
assert . Equal ( t , "[+] applied user roles\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , tmpFile . Name ( ) } ) )
require . Len ( t , userRoleSpecList [ 1 ] . Teams , 1 )
assert . Equal ( t , fleet . RoleMaintainer , userRoleSpecList [ 1 ] . Teams [ 0 ] . Role )
}
2021-07-19 19:48:49 +00:00
func TestApplyTeamSpecs ( t * testing . T ) {
2021-09-03 16:05:23 +00:00
license := & fleet . LicenseInfo { Tier : fleet . TierPremium , Expiration : time . Now ( ) . Add ( 24 * time . Hour ) }
2022-05-10 15:29:17 +00:00
_ , ds := runServerWithMockedDS ( t , & service . TestServerOpts { License : license } )
2021-07-19 19:48:49 +00:00
teamsByName := map [ string ] * fleet . Team {
2021-09-15 19:27:53 +00:00
"team1" : {
2021-07-19 19:48:49 +00:00
ID : 42 ,
Name : "team1" ,
Description : "team1 description" ,
} ,
}
2021-09-14 12:11:07 +00:00
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
2021-07-19 19:48:49 +00:00
team , ok := teamsByName [ name ]
if ! ok {
return nil , sql . ErrNoRows
}
return team , nil
}
i := 1
2021-09-14 12:11:07 +00:00
ds . NewTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
2021-07-19 19:48:49 +00:00
team . ID = uint ( i )
i ++
teamsByName [ team . Name ] = team
return team , nil
}
agentOpts := json . RawMessage ( ` { "config": { "foo":"bar"},"overrides": { "platforms": { "darwin": { "foo":"override"}}}} ` )
2021-09-14 12:11:07 +00:00
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
2023-06-06 18:31:33 +00:00
return & fleet . AppConfig { AgentOptions : & agentOpts , MDM : fleet . MDM { EnabledAndConfigured : true } } , nil
2021-07-19 19:48:49 +00:00
}
2021-09-14 12:11:07 +00:00
ds . SaveTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
2021-07-19 19:48:49 +00:00
teamsByName [ team . Name ] = team
return team , nil
}
enrolledSecretsCalled := make ( map [ uint ] [ ] * fleet . EnrollSecret )
2021-09-14 12:11:07 +00:00
ds . ApplyEnrollSecretsFunc = func ( ctx context . Context , teamID * uint , secrets [ ] * fleet . EnrollSecret ) error {
2021-07-19 19:48:49 +00:00
enrolledSecretsCalled [ * teamID ] = secrets
return nil
}
2023-06-06 18:31:33 +00:00
ds . BatchSetMDMAppleProfilesFunc = func ( ctx context . Context , tmID * uint , profiles [ ] * fleet . MDMAppleConfigProfile ) error {
return nil
}
ds . BulkSetPendingMDMAppleHostProfilesFunc = func ( ctx context . Context , hostIDs , teamIDs , profileIDs [ ] uint , hostUUIDs [ ] string ) error {
return nil
}
2022-12-23 16:05:16 +00:00
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
2022-08-24 12:32:45 +00:00
return nil
}
2022-08-02 13:51:03 +00:00
filename := writeTmpYml ( t , `
2021-07-19 19:48:49 +00:00
-- -
apiVersion : v1
kind : team
spec :
team :
name : team2
-- -
apiVersion : v1
kind : team
spec :
team :
agent_options :
config :
2022-09-19 17:53:44 +00:00
views :
foo : bar
2021-07-19 19:48:49 +00:00
name : team1
secrets :
- secret : AAA
2023-01-24 16:20:02 +00:00
mdm :
macos_updates :
minimum_version : 12.3 .1
deadline : 2011 - 03 - 01
2021-07-19 19:48:49 +00:00
` )
2022-09-19 17:53:44 +00:00
newAgentOpts := json . RawMessage ( ` { "config": { "views": { "foo":"bar"}}} ` )
2023-01-24 16:20:02 +00:00
newMDMSettings := fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
2023-06-06 18:31:33 +00:00
MinimumVersion : optjson . SetString ( "12.3.1" ) ,
Deadline : optjson . SetString ( "2011-03-01" ) ,
2023-01-24 16:20:02 +00:00
} ,
}
2022-08-02 13:51:03 +00:00
require . Equal ( t , "[+] applied 2 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , filename } ) )
2022-03-21 19:16:47 +00:00
assert . JSONEq ( t , string ( agentOpts ) , string ( * teamsByName [ "team2" ] . Config . AgentOptions ) )
assert . JSONEq ( t , string ( newAgentOpts ) , string ( * teamsByName [ "team1" ] . Config . AgentOptions ) )
2021-07-19 19:48:49 +00:00
assert . Equal ( t , [ ] * fleet . EnrollSecret { { Secret : "AAA" } } , enrolledSecretsCalled [ uint ( 42 ) ] )
2023-01-24 16:20:02 +00:00
assert . Equal ( t , fleet . TeamMDM { } , teamsByName [ "team2" ] . Config . MDM )
assert . Equal ( t , newMDMSettings , teamsByName [ "team1" ] . Config . MDM )
2022-08-02 13:51:03 +00:00
assert . True ( t , ds . ApplyEnrollSecretsFuncInvoked )
ds . ApplyEnrollSecretsFuncInvoked = false
2023-06-06 18:31:33 +00:00
mobileCfgPath := writeTmpMobileconfig ( t , "N1" )
filename = writeTmpYml ( t , fmt . Sprintf ( `
2022-08-02 13:51:03 +00:00
apiVersion : v1
kind : team
spec :
team :
name : team1
2023-06-06 18:31:33 +00:00
mdm :
macos_settings :
custom_settings :
- % s
` , mobileCfgPath ) )
newMDMSettings = fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : optjson . SetString ( "12.3.1" ) ,
Deadline : optjson . SetString ( "2011-03-01" ) ,
} ,
MacOSSettings : fleet . MacOSSettings {
CustomSettings : [ ] string { mobileCfgPath } ,
} ,
}
2022-08-02 13:51:03 +00:00
require . Equal ( t , "[+] applied 1 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , filename } ) )
2023-06-06 18:31:33 +00:00
// enroll secret not provided, so left unchanged
2022-08-02 13:51:03 +00:00
assert . Equal ( t , [ ] * fleet . EnrollSecret { { Secret : "AAA" } } , enrolledSecretsCalled [ uint ( 42 ) ] )
assert . False ( t , ds . ApplyEnrollSecretsFuncInvoked )
2022-11-01 19:22:45 +00:00
// agent options not provided, so left unchanged
assert . JSONEq ( t , string ( newAgentOpts ) , string ( * teamsByName [ "team1" ] . Config . AgentOptions ) )
2023-06-06 18:31:33 +00:00
// macos updates options not provided, left unchanged, and macos custom settings added
assert . Equal ( t , newMDMSettings , teamsByName [ "team1" ] . Config . MDM )
2022-08-02 13:51:03 +00:00
filename = writeTmpYml ( t , `
apiVersion : v1
kind : team
spec :
team :
agent_options :
config :
2022-09-19 17:53:44 +00:00
views :
foo : qux
2022-08-02 13:51:03 +00:00
name : team1
2023-01-24 16:20:02 +00:00
mdm :
macos_updates :
minimum_version : 10.10 .10
deadline : 1992 - 03 - 01
2022-08-02 13:51:03 +00:00
secrets :
- secret : BBB
` )
2023-01-24 16:20:02 +00:00
newMDMSettings = fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
2023-06-06 18:31:33 +00:00
MinimumVersion : optjson . SetString ( "10.10.10" ) ,
Deadline : optjson . SetString ( "1992-03-01" ) ,
} ,
MacOSSettings : fleet . MacOSSettings { // macos settings not provided, so not cleared
CustomSettings : [ ] string { mobileCfgPath } ,
2023-01-24 16:20:02 +00:00
} ,
}
2022-09-19 17:53:44 +00:00
newAgentOpts = json . RawMessage ( ` { "config": { "views": { "foo":"qux"}}} ` )
2022-08-02 13:51:03 +00:00
require . Equal ( t , "[+] applied 1 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , filename } ) )
assert . JSONEq ( t , string ( newAgentOpts ) , string ( * teamsByName [ "team1" ] . Config . AgentOptions ) )
2023-01-24 16:20:02 +00:00
assert . Equal ( t , newMDMSettings , teamsByName [ "team1" ] . Config . MDM )
2022-08-02 13:51:03 +00:00
assert . Equal ( t , [ ] * fleet . EnrollSecret { { Secret : "BBB" } } , enrolledSecretsCalled [ uint ( 42 ) ] )
assert . True ( t , ds . ApplyEnrollSecretsFuncInvoked )
2022-11-01 19:22:45 +00:00
filename = writeTmpYml ( t , `
apiVersion : v1
kind : team
spec :
team :
agent_options :
name : team1
2023-06-06 18:31:33 +00:00
mdm :
macos_updates :
macos_settings :
2022-11-01 19:22:45 +00:00
` )
require . Equal ( t , "[+] applied 1 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , filename } ) )
// agent options provided but empty, clears the value
assert . Nil ( t , teamsByName [ "team1" ] . Config . AgentOptions )
2023-06-06 18:31:33 +00:00
// macos settings and updates still the same (not cleared) because only the
// top-level key is provided.
assert . Equal ( t , newMDMSettings , teamsByName [ "team1" ] . Config . MDM )
// enroll secret not cleared since not provided
assert . Equal ( t , [ ] * fleet . EnrollSecret { { Secret : "BBB" } } , enrolledSecretsCalled [ uint ( 42 ) ] )
filename = writeTmpYml ( t , `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version :
` )
// fails: minimum_version provided empty, but deadline not provided
_ , err := runAppNoChecks ( [ ] string { "apply" , "-f" , filename } )
require . ErrorContains ( t , err , "deadline is required when minimum_version is provided" )
filename = writeTmpYml ( t , `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version :
deadline :
macos_settings :
custom_settings :
` )
newMDMSettings = fleet . TeamMDM {
MacOSUpdates : fleet . MacOSUpdates {
MinimumVersion : optjson . String { Set : true } ,
Deadline : optjson . String { Set : true } ,
} ,
MacOSSettings : fleet . MacOSSettings {
CustomSettings : [ ] string { } ,
} ,
}
require . Equal ( t , "[+] applied 1 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , filename } ) )
// agent options still cleared
assert . Nil ( t , teamsByName [ "team1" ] . Config . AgentOptions )
// macos settings and updates are now cleared.
assert . Equal ( t , newMDMSettings , teamsByName [ "team1" ] . Config . MDM )
// enroll secret not cleared since not provided
assert . Equal ( t , [ ] * fleet . EnrollSecret { { Secret : "BBB" } } , enrolledSecretsCalled [ uint ( 42 ) ] )
2021-07-19 19:48:49 +00:00
}
2021-08-11 17:56:11 +00:00
func writeTmpYml ( t * testing . T , contents string ) string {
2023-01-24 16:20:02 +00:00
tmpFile , err := os . CreateTemp ( t . TempDir ( ) , "*.yml" )
2021-08-11 17:56:11 +00:00
require . NoError ( t , err )
_ , err = tmpFile . WriteString ( contents )
require . NoError ( t , err )
return tmpFile . Name ( )
}
2023-04-25 13:36:01 +00:00
func writeTmpJSON ( t * testing . T , v any ) string {
tmpFile , err := os . CreateTemp ( t . TempDir ( ) , "*.json" )
require . NoError ( t , err )
err = json . NewEncoder ( tmpFile ) . Encode ( v )
require . NoError ( t , err )
return tmpFile . Name ( )
}
2021-08-11 17:56:11 +00:00
func TestApplyAppConfig ( t * testing . T ) {
2023-01-24 16:20:02 +00:00
license := & fleet . LicenseInfo { Tier : fleet . TierPremium , Expiration : time . Now ( ) . Add ( 24 * time . Hour ) }
_ , ds := runServerWithMockedDS ( t , & service . TestServerOpts { License : license } )
2021-08-11 17:56:11 +00:00
2021-09-14 12:11:07 +00:00
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
2021-08-11 17:56:11 +00:00
return userRoleSpecList , nil
}
2022-12-23 16:05:16 +00:00
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
2022-11-01 19:22:45 +00:00
return nil
}
2021-09-14 12:11:07 +00:00
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
2021-08-11 17:56:11 +00:00
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
2023-01-24 16:20:02 +00:00
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
return & fleet . Team { ID : 123 } , nil
}
2021-08-11 17:56:11 +00:00
2022-11-01 19:22:45 +00:00
defaultAgentOpts := json . RawMessage ( ` { "config": { "foo":"bar"}} ` )
2021-09-14 12:11:07 +00:00
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
2022-11-01 19:22:45 +00:00
return & fleet . AppConfig {
OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } ,
ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } ,
AgentOptions : & defaultAgentOpts ,
} , nil
2021-08-11 17:56:11 +00:00
}
var savedAppConfig * fleet . AppConfig
2021-09-14 12:11:07 +00:00
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
2021-08-11 17:56:11 +00:00
savedAppConfig = config
return nil
}
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
2022-08-25 16:41:50 +00:00
features :
2021-08-11 17:56:11 +00:00
enable_host_users : false
2021-08-11 18:57:53 +00:00
enable_software_inventory : false
2023-01-24 16:20:02 +00:00
mdm :
apple_bm_default_team : "team1"
macos_updates :
minimum_version : 12.1 .1
deadline : 2011 - 02 - 01
2021-08-11 17:56:11 +00:00
` )
2023-01-24 16:20:02 +00:00
newMDMSettings := fleet . MDM {
AppleBMDefaultTeam : "team1" ,
AppleBMTermsExpired : false ,
MacOSUpdates : fleet . MacOSUpdates {
2023-06-06 18:31:33 +00:00
MinimumVersion : optjson . SetString ( "12.1.1" ) ,
Deadline : optjson . SetString ( "2011-02-01" ) ,
2023-01-24 16:20:02 +00:00
} ,
}
2021-08-11 17:56:11 +00:00
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
require . NotNil ( t , savedAppConfig )
2022-08-25 16:41:50 +00:00
assert . False ( t , savedAppConfig . Features . EnableHostUsers )
assert . False ( t , savedAppConfig . Features . EnableSoftwareInventory )
2023-01-24 16:20:02 +00:00
assert . Equal ( t , newMDMSettings , savedAppConfig . MDM )
2022-11-01 19:22:45 +00:00
// agent options were not modified, since they were not provided
assert . Equal ( t , string ( defaultAgentOpts ) , string ( * savedAppConfig . AgentOptions ) )
2021-08-11 17:56:11 +00:00
name = writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
2022-08-25 16:41:50 +00:00
features :
2021-08-11 17:56:11 +00:00
enable_host_users : true
2021-08-11 18:57:53 +00:00
enable_software_inventory : true
2022-11-01 19:22:45 +00:00
agent_options :
2023-01-24 16:20:02 +00:00
mdm :
macos_updates :
2021-08-11 17:56:11 +00:00
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
require . NotNil ( t , savedAppConfig )
2022-08-25 16:41:50 +00:00
assert . True ( t , savedAppConfig . Features . EnableHostUsers )
assert . True ( t , savedAppConfig . Features . EnableSoftwareInventory )
2022-11-01 19:22:45 +00:00
// agent options were cleared, provided but empty
assert . Nil ( t , savedAppConfig . AgentOptions )
2023-01-24 16:20:02 +00:00
assert . Equal ( t , newMDMSettings , savedAppConfig . MDM )
2021-08-11 17:56:11 +00:00
}
2021-11-19 15:37:36 +00:00
2022-10-17 19:03:49 +00:00
func TestApplyAppConfigDryRunIssue ( t * testing . T ) {
// reproduces the bug fixed by https://github.com/fleetdm/fleet/pull/8194
_ , ds := runServerWithMockedDS ( t )
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
return userRoleSpecList , nil
}
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
2022-12-23 16:05:16 +00:00
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
2022-10-17 19:03:49 +00:00
return nil
}
2022-12-05 22:50:49 +00:00
currentAppConfig := & fleet . AppConfig {
2022-10-17 19:03:49 +00:00
OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } , ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } ,
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
return currentAppConfig , nil
}
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
currentAppConfig = config
return nil
}
// first, set the default app config's agent options as set after fleetctl setup
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
agent_options :
config :
decorators :
load :
- SELECT uuid AS host_uuid FROM system_info ;
- SELECT hostname AS hostname FROM system_info ;
options :
disable_distributed : false
distributed_interval : 10
distributed_plugin : tls
distributed_tls_max_attempts : 3
logger_tls_endpoint : / api / osquery / log
logger_tls_period : 10
pack_delimiter : /
overrides : { }
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
// then, dry-run a valid app config's agent options, which made the original
// app config's agent options invalid JSON (when it shouldn't have modified
// it at all - the issue was in the cached_mysql datastore, it did not clone
// the app config properly).
name = writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
agent_options :
overrides :
platforms :
darwin :
auto_table_construction :
tcc_system_entries :
query : "SELECT service, client, allowed, prompt_count, last_modified FROM access"
path : "/Library/Application Support/com.apple.TCC/TCC.db"
columns :
- "service"
- "client"
- "allowed"
- "prompt_count"
- "last_modified"
` )
assert . Equal ( t , "[+] would've applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "--dry-run" , "-f" , name } ) )
// the saved app config was left unchanged, still equal to the original agent
// options
got := runAppForTest ( t , [ ] string { "get" , "config" } )
assert . Contains ( t , got , ` agent_options :
config :
decorators :
load :
- SELECT uuid AS host_uuid FROM system_info ;
- SELECT hostname AS hostname FROM system_info ;
options :
disable_distributed : false
distributed_interval : 10
distributed_plugin : tls
distributed_tls_max_attempts : 3
logger_tls_endpoint : / api / osquery / log
logger_tls_period : 10
pack_delimiter : /
overrides : { } ` )
}
2021-11-19 15:37:36 +00:00
func TestApplyAppConfigUnknownFields ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
return userRoleSpecList , nil
}
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
return & fleet . AppConfig { } , nil
}
var savedAppConfig * fleet . AppConfig
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
savedAppConfig = config
return nil
}
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
2022-08-25 16:41:50 +00:00
features :
2021-11-19 15:37:36 +00:00
enabled_software_inventory : false # typo , correct config is enable_software_inventory
` )
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } ,
2022-10-12 21:10:50 +00:00
"applying fleet config: PATCH /api/latest/fleet/config received status 400 Bad Request: unsupported key provided: \"enabled_software_inventory\"" ,
2021-11-19 15:37:36 +00:00
)
require . Nil ( t , savedAppConfig )
}
2022-01-11 14:04:29 +00:00
2022-08-25 16:41:50 +00:00
func TestApplyAppConfigDeprecatedFields ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
return userRoleSpecList , nil
}
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
2022-09-19 17:53:44 +00:00
return & fleet . AppConfig { OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } , ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } } , nil
2022-08-25 16:41:50 +00:00
}
var savedAppConfig * fleet . AppConfig
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
savedAppConfig = config
return nil
}
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
host_settings :
enable_host_users : false
enable_software_inventory : false
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
require . NotNil ( t , savedAppConfig )
assert . False ( t , savedAppConfig . Features . EnableHostUsers )
assert . False ( t , savedAppConfig . Features . EnableSoftwareInventory )
name = writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
host_settings :
enable_host_users : true
enable_software_inventory : true
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
require . NotNil ( t , savedAppConfig )
assert . True ( t , savedAppConfig . Features . EnableHostUsers )
assert . True ( t , savedAppConfig . Features . EnableSoftwareInventory )
}
2023-04-12 19:11:04 +00:00
const (
policySpec = ` -- -
2022-01-11 14:04:29 +00:00
apiVersion : v1
kind : policy
spec :
name : Is Gatekeeper enabled on macOS devices ?
query : SELECT 1 FROM gatekeeper WHERE assessments_enabled = 1 ;
description : Checks to make sure that the Gatekeeper feature is enabled on macOS devices . Gatekeeper tries to ensure only trusted software is run on a mac machine .
resolution : "Run the following command in the Terminal app: /usr/sbin/spctl --master-enable"
platform : darwin
team : Team1
-- -
apiVersion : v1
kind : policy
spec :
name : Is disk encryption enabled on Windows devices ?
query : SELECT 1 FROM bitlocker_info where protection_status = 1 ;
description : Checks to make sure that device encryption is enabled on Windows devices .
resolution : "Option 1: Select the Start button. Select Settings > Update & Security > Device encryption. If Device encryption doesn't appear, skip to Option 2. If device encryption is turned off, select Turn on. Option 2: Select the Start button. Under Windows System, select Control Panel. Select System and Security. Under BitLocker Drive Encryption, select Manage BitLocker. Select Turn on BitLocker and then follow the instructions."
platform : windows
-- -
apiVersion : v1
kind : policy
spec :
name : Is Filevault enabled on macOS devices ?
query : SELECT 1 FROM disk_encryption WHERE user_uuid IS NOT “ ” AND filevault_status = ‘ on ’ LIMIT 1 ;
description : Checks to make sure that the Filevault feature is enabled on macOS devices .
resolution : "Choose Apple menu > System Preferences, then click Security & Privacy. Click the FileVault tab. Click the Lock icon, then enter an administrator name and password. Click Turn On FileVault."
platform : darwin
2023-04-12 19:11:04 +00:00
`
enrollSecretsSpec = ` -- -
apiVersion : v1
kind : enroll_secret
spec :
secrets :
- secret : RzTlxPvugG4o4O5IKS / HqEDJUmI1hwBoffff
- secret : reallyworks
- secret : thissecretwontwork !
`
labelsSpec = ` -- -
apiVersion : v1
kind : label
spec :
name : pending_updates
query : select 1 ;
platforms :
- darwin
`
packsSpec = ` -- -
apiVersion : v1
kind : pack
spec :
name : osquery_monitoring
queries :
- query : osquery_version
name : osquery_version_snapshot
interval : 7200
snapshot : true
- query : osquery_version
name : osquery_version_differential
interval : 7200
`
queriesSpec = ` -- -
apiVersion : v1
kind : query
spec :
description : Retrieves the list of application scheme / protocol - based IPC handlers .
name : app_schemes
query : select * from app_schemes ;
`
)
func TestApplyPolicies ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
var appliedPolicySpecs [ ] * fleet . PolicySpec
ds . ApplyPolicySpecsFunc = func ( ctx context . Context , authorID uint , specs [ ] * fleet . PolicySpec ) error {
appliedPolicySpecs = specs
return nil
}
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
if name == "Team1" {
return & fleet . Team { ID : 123 } , nil
}
return nil , errors . New ( "unexpected team name!" )
}
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
return nil
}
name := writeTmpYml ( t , policySpec )
assert . Equal ( t , "[+] applied 3 policies\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyPolicySpecsFuncInvoked )
assert . Len ( t , appliedPolicySpecs , 3 )
for _ , p := range appliedPolicySpecs {
assert . NotEmpty ( t , p . Platform )
}
assert . True ( t , ds . TeamByNameFuncInvoked )
}
2023-04-17 15:08:55 +00:00
func mobileconfigForTest ( name , identifier string ) [ ] byte {
return [ ] byte ( fmt . Sprintf ( ` < ? xml version = "1.0" encoding = "UTF-8" ? >
< ! DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
< plist version = "1.0" >
< dict >
< key > PayloadContent < / key >
< array / >
< key > PayloadDisplayName < / key >
< string > % s < / string >
< key > PayloadIdentifier < / key >
< string > % s < / string >
< key > PayloadType < / key >
< string > Configuration < / string >
< key > PayloadUUID < / key >
< string > % s < / string >
< key > PayloadVersion < / key >
< integer > 1 < / integer >
< / dict >
< / plist >
` , name , identifier , uuid . New ( ) . String ( ) ) )
}
2023-04-12 19:11:04 +00:00
func TestApplyAsGitOps ( t * testing . T ) {
2023-04-17 15:08:55 +00:00
enqueuer := new ( nanomdm_mock . Storage )
2023-04-12 19:11:04 +00:00
license := & fleet . LicenseInfo { Tier : fleet . TierPremium , Expiration : time . Now ( ) . Add ( 24 * time . Hour ) }
2023-04-17 15:08:55 +00:00
_ , ds := runServerWithMockedDS ( t , & service . TestServerOpts {
License : license ,
MDMStorage : enqueuer ,
MDMPusher : mockPusher { } ,
} )
2023-04-12 19:11:04 +00:00
gitOps := & fleet . User {
Name : "GitOps" ,
Password : [ ] byte ( "p4ssw0rd.123" ) ,
Email : "gitops1@example.com" ,
GlobalRole : ptr . String ( fleet . RoleGitOps ) ,
}
gitOps , err := ds . NewUser ( context . Background ( ) , gitOps )
require . NoError ( t , err )
ds . SessionByKeyFunc = func ( ctx context . Context , key string ) ( * fleet . Session , error ) {
return & fleet . Session {
CreateTimestamp : fleet . CreateTimestamp { CreatedAt : time . Now ( ) } ,
ID : 1 ,
AccessedAt : time . Now ( ) ,
UserID : gitOps . ID ,
Key : key ,
} , nil
}
ds . UserByIDFunc = func ( ctx context . Context , id uint ) ( * fleet . User , error ) {
return gitOps , nil
}
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
return nil
}
// Apply global config.
currentAppConfig := & fleet . AppConfig {
2023-04-17 15:08:55 +00:00
OrgInfo : fleet . OrgInfo {
OrgName : "Fleet" ,
} ,
ServerSettings : fleet . ServerSettings {
ServerURL : "https://example.org" ,
} ,
MDM : fleet . MDM {
EnabledAndConfigured : true ,
} ,
2023-04-12 19:11:04 +00:00
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
return currentAppConfig , nil
}
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
currentAppConfig = config
return nil
}
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
features :
enable_host_users : true
enable_software_inventory : true
agent_options :
config :
decorators :
load :
- SELECT uuid AS host_uuid FROM system_info ;
- SELECT hostname AS hostname FROM system_info ;
options :
disable_distributed : false
distributed_interval : 10
distributed_plugin : tls
distributed_tls_max_attempts : 3
logger_tls_endpoint : / api / osquery / log
logger_tls_period : 10
pack_delimiter : /
overrides : { }
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , currentAppConfig . Features . EnableHostUsers )
// Apply team config.
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
if name == "Team1" {
return & fleet . Team { ID : 123 } , nil
}
return nil , errors . New ( "unexpected team name!" )
}
var savedTeam * fleet . Team
ds . SaveTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
savedTeam = team
return team , nil
}
var teamEnrollSecrets [ ] * fleet . EnrollSecret
ds . ApplyEnrollSecretsFunc = func ( ctx context . Context , teamID * uint , secrets [ ] * fleet . EnrollSecret ) error {
if teamID == nil || * teamID != 123 {
return fmt . Errorf ( "unexpected data: %+v" , teamID )
}
teamEnrollSecrets = secrets
return nil
}
2023-04-17 15:08:55 +00:00
ds . BatchSetMDMAppleProfilesFunc = func ( ctx context . Context , teamID * uint , profiles [ ] * fleet . MDMAppleConfigProfile ) error {
return nil
}
ds . BulkSetPendingMDMAppleHostProfilesFunc = func ( ctx context . Context , hostIDs , teamIDs , profileIDs [ ] uint , hostUUIDs [ ] string ) error {
return nil
}
mobileConfig := mobileconfigForTest ( "foo" , "bar" )
mobileConfigPath := filepath . Join ( t . TempDir ( ) , "foo.mobileconfig" )
err = os . WriteFile ( mobileConfigPath , mobileConfig , 0 o644 )
require . NoError ( t , err )
name = writeTmpYml ( t , fmt . Sprintf ( `
2023-04-12 19:11:04 +00:00
apiVersion : v1
kind : team
spec :
team :
agent_options :
config :
views :
foo : qux
name : Team1
2023-04-17 15:08:55 +00:00
mdm :
macos_updates :
minimum_version : 10.10 .10
deadline : 1992 - 03 - 01
macos_settings :
custom_settings :
- % s
enable_disk_encryption : false
2023-04-12 19:11:04 +00:00
secrets :
- secret : BBB
2023-04-17 15:08:55 +00:00
` , mobileConfigPath ) )
2022-01-11 14:04:29 +00:00
2023-04-12 19:11:04 +00:00
require . Equal ( t , "[+] applied 1 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . JSONEq ( t , string ( json . RawMessage ( ` { "config": { "views": { "foo":"qux"}}} ` ) ) , string ( * savedTeam . Config . AgentOptions ) )
2023-04-17 15:08:55 +00:00
assert . Equal ( t , fleet . TeamMDM {
MacOSSettings : fleet . MacOSSettings {
CustomSettings : [ ] string { mobileConfigPath } ,
EnableDiskEncryption : false ,
} ,
MacOSUpdates : fleet . MacOSUpdates {
2023-06-06 18:31:33 +00:00
MinimumVersion : optjson . SetString ( "10.10.10" ) ,
Deadline : optjson . SetString ( "1992-03-01" ) ,
2023-04-17 15:08:55 +00:00
} ,
} , savedTeam . Config . MDM )
2023-04-12 19:11:04 +00:00
assert . Equal ( t , [ ] * fleet . EnrollSecret { { Secret : "BBB" } } , teamEnrollSecrets )
assert . True ( t , ds . ApplyEnrollSecretsFuncInvoked )
2023-04-17 15:08:55 +00:00
assert . True ( t , ds . BatchSetMDMAppleProfilesFuncInvoked )
2023-04-12 19:11:04 +00:00
// Apply policies.
var appliedPolicySpecs [ ] * fleet . PolicySpec
ds . ApplyPolicySpecsFunc = func ( ctx context . Context , authorID uint , specs [ ] * fleet . PolicySpec ) error {
appliedPolicySpecs = specs
return nil
}
name = writeTmpYml ( t , policySpec )
2022-01-11 14:04:29 +00:00
assert . Equal ( t , "[+] applied 3 policies\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyPolicySpecsFuncInvoked )
assert . Len ( t , appliedPolicySpecs , 3 )
for _ , p := range appliedPolicySpecs {
assert . NotEmpty ( t , p . Platform )
}
assert . True ( t , ds . TeamByNameFuncInvoked )
2023-04-12 19:11:04 +00:00
// Apply enroll secrets.
var appliedSecrets [ ] * fleet . EnrollSecret
ds . ApplyEnrollSecretsFunc = func ( ctx context . Context , teamID * uint , secrets [ ] * fleet . EnrollSecret ) error {
appliedSecrets = secrets
return nil
}
name = writeTmpYml ( t , enrollSecretsSpec )
assert . Equal ( t , "[+] applied enroll secrets\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyEnrollSecretsFuncInvoked )
assert . Len ( t , appliedSecrets , 3 )
for _ , s := range appliedSecrets {
assert . NotEmpty ( t , s . Secret )
}
// Apply labels.
var appliedLabels [ ] * fleet . LabelSpec
ds . ApplyLabelSpecsFunc = func ( ctx context . Context , specs [ ] * fleet . LabelSpec ) error {
appliedLabels = specs
return nil
}
name = writeTmpYml ( t , labelsSpec )
assert . Equal ( t , "[+] applied 1 labels\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyLabelSpecsFuncInvoked )
require . Len ( t , appliedLabels , 1 )
assert . Equal ( t , "pending_updates" , appliedLabels [ 0 ] . Name )
assert . Equal ( t , "select 1;" , appliedLabels [ 0 ] . Query )
// Apply packs.
var appliedPacks [ ] * fleet . PackSpec
ds . ApplyPackSpecsFunc = func ( ctx context . Context , specs [ ] * fleet . PackSpec ) error {
appliedPacks = specs
return nil
}
ds . ListPacksFunc = func ( ctx context . Context , opt fleet . PackListOptions ) ( [ ] * fleet . Pack , error ) {
return nil , nil
}
name = writeTmpYml ( t , packsSpec )
assert . Equal ( t , "[+] applied 1 packs\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyPackSpecsFuncInvoked )
require . Len ( t , appliedPacks , 1 )
assert . Equal ( t , "osquery_monitoring" , appliedPacks [ 0 ] . Name )
require . Len ( t , appliedPacks [ 0 ] . Queries , 2 )
// Apply queries.
var appliedQueries [ ] * fleet . Query
ds . QueryByNameFunc = func ( ctx context . Context , name string , opts ... fleet . OptionalArg ) ( * fleet . Query , error ) {
return nil , sql . ErrNoRows
}
ds . ApplyQueriesFunc = func ( ctx context . Context , authorID uint , queries [ ] * fleet . Query ) error {
appliedQueries = queries
return nil
}
name = writeTmpYml ( t , queriesSpec )
assert . Equal ( t , "[+] applied 1 queries\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyQueriesFuncInvoked )
require . Len ( t , appliedQueries , 1 )
assert . Equal ( t , "app_schemes" , appliedQueries [ 0 ] . Name )
assert . Equal ( t , "select * from app_schemes;" , appliedQueries [ 0 ] . Query )
2022-01-11 14:04:29 +00:00
}
2022-01-24 19:40:51 +00:00
func TestApplyEnrollSecrets ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
var appliedSecrets [ ] * fleet . EnrollSecret
ds . ApplyEnrollSecretsFunc = func ( ctx context . Context , teamID * uint , secrets [ ] * fleet . EnrollSecret ) error {
appliedSecrets = secrets
return nil
}
2023-04-12 19:11:04 +00:00
name := writeTmpYml ( t , enrollSecretsSpec )
2022-01-24 19:40:51 +00:00
assert . Equal ( t , "[+] applied enroll secrets\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyEnrollSecretsFuncInvoked )
assert . Len ( t , appliedSecrets , 3 )
for _ , s := range appliedSecrets {
assert . NotEmpty ( t , s . Secret )
}
}
func TestApplyLabels ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
var appliedLabels [ ] * fleet . LabelSpec
ds . ApplyLabelSpecsFunc = func ( ctx context . Context , specs [ ] * fleet . LabelSpec ) error {
appliedLabels = specs
return nil
}
2023-04-12 19:11:04 +00:00
name := writeTmpYml ( t , labelsSpec )
2022-01-24 19:40:51 +00:00
assert . Equal ( t , "[+] applied 1 labels\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyLabelSpecsFuncInvoked )
require . Len ( t , appliedLabels , 1 )
assert . Equal ( t , "pending_updates" , appliedLabels [ 0 ] . Name )
assert . Equal ( t , "select 1;" , appliedLabels [ 0 ] . Query )
}
func TestApplyPacks ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
ds . ListPacksFunc = func ( ctx context . Context , opt fleet . PackListOptions ) ( [ ] * fleet . Pack , error ) {
return nil , nil
}
2022-12-23 16:05:16 +00:00
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
2022-01-24 19:40:51 +00:00
return nil
}
var appliedPacks [ ] * fleet . PackSpec
ds . ApplyPackSpecsFunc = func ( ctx context . Context , specs [ ] * fleet . PackSpec ) error {
appliedPacks = specs
return nil
}
2023-04-12 19:11:04 +00:00
name := writeTmpYml ( t , packsSpec )
2022-01-24 19:40:51 +00:00
assert . Equal ( t , "[+] applied 1 packs\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyPackSpecsFuncInvoked )
require . Len ( t , appliedPacks , 1 )
assert . Equal ( t , "osquery_monitoring" , appliedPacks [ 0 ] . Name )
require . Len ( t , appliedPacks [ 0 ] . Queries , 2 )
2022-05-26 21:54:21 +00:00
interval := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : pack
spec :
name : test_bad_interval
queries :
- query : good_interval
name : good_interval
interval : 7200
- query : bad_interval
name : bad_interval
interval : 604801
` )
expectedErrMsg := "applying packs: POST /api/latest/fleet/spec/packs received status 400 Bad request: pack payload verification: pack scheduled query interval must be an integer greater than 1 and less than 604800"
_ , err := runAppNoChecks ( [ ] string { "apply" , "-f" , interval } )
assert . Error ( t , err )
require . Equal ( t , expectedErrMsg , err . Error ( ) )
2022-01-24 19:40:51 +00:00
}
func TestApplyQueries ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
var appliedQueries [ ] * fleet . Query
2022-02-08 16:47:48 +00:00
ds . QueryByNameFunc = func ( ctx context . Context , name string , opts ... fleet . OptionalArg ) ( * fleet . Query , error ) {
return nil , sql . ErrNoRows
}
2022-01-24 19:40:51 +00:00
ds . ApplyQueriesFunc = func ( ctx context . Context , authorID uint , queries [ ] * fleet . Query ) error {
appliedQueries = queries
return nil
}
2022-12-23 16:05:16 +00:00
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
2022-01-24 19:40:51 +00:00
return nil
}
2023-04-12 19:11:04 +00:00
name := writeTmpYml ( t , queriesSpec )
2022-01-24 19:40:51 +00:00
assert . Equal ( t , "[+] applied 1 queries\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . ApplyQueriesFuncInvoked )
require . Len ( t , appliedQueries , 1 )
assert . Equal ( t , "app_schemes" , appliedQueries [ 0 ] . Name )
assert . Equal ( t , "select * from app_schemes;" , appliedQueries [ 0 ] . Query )
}
2022-04-19 13:29:50 +00:00
func TestCanApplyIntervalsInNanoseconds ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
// Stubs
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
return userRoleSpecList , nil
}
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
2022-09-19 17:53:44 +00:00
return & fleet . AppConfig { OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } , ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } } , nil
2022-04-19 13:29:50 +00:00
}
var savedAppConfig * fleet . AppConfig
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
savedAppConfig = config
return nil
}
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
webhook_settings :
interval : 30000000000
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
require . Equal ( t , savedAppConfig . WebhookSettings . Interval . Duration , 30 * time . Second )
}
func TestCanApplyIntervalsUsingDurations ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t )
// Stubs
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
return userRoleSpecList , nil
}
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
2022-09-19 17:53:44 +00:00
return & fleet . AppConfig { OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } , ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } } , nil
2022-04-19 13:29:50 +00:00
}
var savedAppConfig * fleet . AppConfig
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
savedAppConfig = config
return nil
}
name := writeTmpYml ( t , ` -- -
apiVersion : v1
kind : config
spec :
webhook_settings :
interval : 30 s
` )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
require . Equal ( t , savedAppConfig . WebhookSettings . Interval . Duration , 30 * time . Second )
}
2022-09-19 17:53:44 +00:00
2023-04-26 21:09:21 +00:00
func TestApplyMacosSetup ( t * testing . T ) {
2023-05-02 19:03:10 +00:00
mockStore := struct {
sync . Mutex
appConfig * fleet . AppConfig
metaHash [ ] byte
} { }
2023-04-26 21:09:21 +00:00
2023-04-25 13:36:01 +00:00
setupServer := func ( t * testing . T , premium bool ) * mock . Store {
tier := fleet . TierFree
if premium {
tier = fleet . TierPremium
}
license := & fleet . LicenseInfo { Tier : tier , Expiration : time . Now ( ) . Add ( 24 * time . Hour ) }
_ , ds := runServerWithMockedDS ( t , & service . TestServerOpts { License : license } )
2023-04-26 14:37:03 +00:00
tm1 := & fleet . Team { ID : 1 , Name : "tm1" }
2023-04-25 13:36:01 +00:00
teamsByName := map [ string ] * fleet . Team {
2023-04-26 14:37:03 +00:00
"tm1" : tm1 ,
}
teamsByID := map [ uint ] * fleet . Team {
tm1 . ID : tm1 ,
2023-04-25 13:36:01 +00:00
}
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
return nil
}
2023-05-15 18:06:09 +00:00
ds . NewJobFunc = func ( ctx context . Context , job * fleet . Job ) ( * fleet . Job , error ) {
return job , nil
}
2023-04-25 13:36:01 +00:00
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
team , ok := teamsByName [ name ]
if ! ok {
2023-04-26 14:37:03 +00:00
// TeamByName in the real Datastore does not return notFoundError, it
// returns ErrNoRows directly, we're a bit inconsistent with that at
// the moment. This is important as ApplyTeamSpecs checks if TeamByName
// returns an error that wraps ErrNoRows (and not an IsNotFound).
2023-04-25 13:36:01 +00:00
return nil , sql . ErrNoRows
}
clone := * team
return & clone , nil
}
tmID := 1 // new teams will start at 2
ds . NewTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
tmID ++
team . ID = uint ( tmID )
2023-04-26 14:37:03 +00:00
clone := * team
2023-04-25 13:36:01 +00:00
teamsByName [ team . Name ] = & clone
2023-04-26 14:37:03 +00:00
teamsByID [ team . ID ] = & clone
2023-04-25 13:36:01 +00:00
return team , nil
}
2023-04-26 14:37:03 +00:00
ds . TeamFunc = func ( ctx context . Context , id uint ) ( * fleet . Team , error ) {
tm , ok := teamsByID [ id ]
if ! ok {
return nil , & notFoundError { }
}
clone := * tm
return & clone , nil
}
2023-04-25 13:36:01 +00:00
ds . ListTeamsFunc = func ( ctx context . Context , filter fleet . TeamFilter , opt fleet . ListOptions ) ( [ ] * fleet . Team , error ) {
tms := make ( [ ] * fleet . Team , 0 , len ( teamsByName ) )
for _ , tm := range teamsByName {
clone := * tm
tms = append ( tms , & clone )
}
sort . Slice ( tms , func ( i , j int ) bool {
l , r := tms [ i ] , tms [ j ]
return l . Name < r . Name
} )
return tms , nil
}
2023-05-02 19:03:10 +00:00
// initialize mockConfig
mockStore . Lock ( )
mockStore . appConfig = & fleet . AppConfig {
2023-04-25 13:36:01 +00:00
OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } ,
ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } ,
MDM : fleet . MDM { EnabledAndConfigured : true } ,
}
2023-05-02 19:03:10 +00:00
mockStore . Unlock ( )
2023-04-25 13:36:01 +00:00
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
2023-05-02 19:03:10 +00:00
mockStore . Lock ( )
defer mockStore . Unlock ( )
clone , err := mockStore . appConfig . Clone ( )
2023-04-25 13:36:01 +00:00
return clone . ( * fleet . AppConfig ) , err
}
ds . SaveAppConfigFunc = func ( ctx context . Context , info * fleet . AppConfig ) error {
2023-05-02 19:03:10 +00:00
mockStore . Lock ( )
defer mockStore . Unlock ( )
2023-04-25 13:36:01 +00:00
clone , err := info . Clone ( )
if err != nil {
return err
}
2023-05-02 19:03:10 +00:00
mockStore . appConfig = clone . ( * fleet . AppConfig )
2023-04-25 13:36:01 +00:00
return nil
}
ds . SaveTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
teamsByName [ team . Name ] = team
2023-04-26 14:37:03 +00:00
teamsByID [ team . ID ] = team
2023-04-25 13:36:01 +00:00
return team , nil
}
asstsByTeam := make ( map [ uint ] * fleet . MDMAppleSetupAssistant )
asstID := 0
ds . SetOrUpdateMDMAppleSetupAssistantFunc = func ( ctx context . Context , asst * fleet . MDMAppleSetupAssistant ) ( * fleet . MDMAppleSetupAssistant , error ) {
asstID ++
asst . ID = uint ( asstID )
asst . UploadedAt = time . Now ( )
var tmID uint
if asst . TeamID != nil {
tmID = * asst . TeamID
}
asstsByTeam [ tmID ] = asst
return asst , nil
}
ds . DeleteMDMAppleSetupAssistantFunc = func ( ctx context . Context , teamID * uint ) error {
var tmID uint
if teamID != nil {
tmID = * teamID
}
delete ( asstsByTeam , tmID )
return nil
}
ds . GetMDMAppleSetupAssistantFunc = func ( ctx context . Context , teamID * uint ) ( * fleet . MDMAppleSetupAssistant , error ) {
var tmID uint
if teamID != nil {
tmID = * teamID
}
if asst , ok := asstsByTeam [ tmID ] ; ok {
return asst , nil
}
2023-04-26 14:37:03 +00:00
return nil , & notFoundError { }
2023-04-25 13:36:01 +00:00
}
2023-05-02 19:03:10 +00:00
ds . InsertMDMAppleBootstrapPackageFunc = func ( ctx context . Context , bp * fleet . MDMAppleBootstrapPackage ) error {
return nil
}
ds . DeleteMDMAppleBootstrapPackageFunc = func ( ctx context . Context , teamID uint ) error {
return nil
}
ds . GetMDMAppleBootstrapPackageMetaFunc = func ( ctx context . Context , teamID uint ) ( * fleet . MDMAppleBootstrapPackage , error ) {
return nil , nil
}
2023-04-25 13:36:01 +00:00
return ds
}
emptyMacosSetup := writeTmpJSON ( t , map [ string ] any { } )
invalidWebURLMacosSetup := writeTmpJSON ( t , map [ string ] any {
"configuration_web_url" : "https://example.com" ,
} )
invalidURLMacosSetup := writeTmpJSON ( t , map [ string ] any {
"url" : "https://example.com" ,
} )
const (
appConfigSpec = `
apiVersion : v1
kind : config
spec :
mdm :
macos_setup :
2023-04-26 21:09:21 +00:00
bootstrap_package : % s
2023-04-25 13:36:01 +00:00
macos_setup_assistant : % s
`
appConfigNoKeySpec = `
apiVersion : v1
kind : config
spec :
mdm :
macos_setup :
2023-05-10 20:22:08 +00:00
`
appConfigSpecEnableEndUserAuth = `
apiVersion : v1
kind : config
spec :
mdm :
macos_setup :
enable_end_user_authentication : % s
2023-04-25 13:36:01 +00:00
`
team1Spec = `
apiVersion : v1
kind : team
spec :
team :
name : tm1
mdm :
macos_setup :
2023-04-26 21:09:21 +00:00
bootstrap_package : % s
2023-04-25 13:36:01 +00:00
macos_setup_assistant : % s
`
team1NoKeySpec = `
apiVersion : v1
kind : team
spec :
team :
name : tm1
mdm :
macos_setup :
`
team1And2Spec = `
apiVersion : v1
kind : team
spec :
team :
name : tm1
mdm :
macos_setup :
2023-04-26 21:09:21 +00:00
bootstrap_package : % s
2023-04-25 13:36:01 +00:00
macos_setup_assistant : % s
-- -
apiVersion : v1
kind : team
spec :
team :
name : tm2
mdm :
macos_setup :
2023-04-26 21:09:21 +00:00
bootstrap_package : % s
2023-04-25 13:36:01 +00:00
macos_setup_assistant : % s
2023-05-10 20:22:08 +00:00
`
team1SpecEnableEndUserAuth = `
apiVersion : v1
kind : team
spec :
team :
name : tm1
mdm :
macos_setup :
enable_end_user_authentication : % s
2023-04-25 13:36:01 +00:00
`
)
t . Run ( "free license" , func ( t * testing . T ) {
ds := setupServer ( t , false )
// appconfig macos setup assistant
2023-04-26 21:09:21 +00:00
name := writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , emptyMacosSetup ) )
2023-04-25 13:36:01 +00:00
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } , ` applying fleet config: missing or invalid license ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
2023-05-02 19:03:10 +00:00
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "https://example.com" , "" ) )
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } , ` applying fleet config: missing or invalid license ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
2023-04-25 13:36:01 +00:00
assert . False ( t , ds . SaveAppConfigFuncInvoked )
// team macos setup assistant
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1Spec , "" , emptyMacosSetup ) )
2023-04-25 13:36:01 +00:00
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } , ` applying teams: missing or invalid license ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
2023-05-02 19:03:10 +00:00
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
name = writeTmpYml ( t , fmt . Sprintf ( team1Spec , "https://example.com" , "" ) )
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } , ` applying teams: missing or invalid license ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
2023-04-25 13:36:01 +00:00
assert . False ( t , ds . SaveTeamFuncInvoked )
2023-05-10 20:22:08 +00:00
// enable_end_user_authentication is premium only
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpecEnableEndUserAuth , "true" ) )
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } , ` applying fleet config: PATCH /api/latest/fleet/config received status 422 Validation Failed: missing or invalid license ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
name = writeTmpYml ( t , fmt . Sprintf ( team1SpecEnableEndUserAuth , "true" ) )
runAppCheckErr ( t , [ ] string { "apply" , "-f" , name } , ` applying teams: missing or invalid license ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
2023-04-25 13:36:01 +00:00
} )
2023-05-02 19:03:10 +00:00
t . Run ( "setup assistant invalid file, not json, invalid json" , func ( t * testing . T ) {
2023-04-25 13:36:01 +00:00
ds := setupServer ( t , true )
// create invalid json file
tmpFile , err := os . CreateTemp ( t . TempDir ( ) , "*.json" )
require . NoError ( t , err )
_ , err = tmpFile . WriteString ( ` not json ` )
require . NoError ( t , err )
invalidJSON := tmpFile . Name ( )
// appconfig invalid file
2023-04-26 21:09:21 +00:00
name := writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , "no_such_file.json" ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , ` no such file ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
// appconfig not .json
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , "no_such_file.txt" ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , ` Couldn’ t edit macos_setup_assistant. The file should be a .json file. ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
// appconfig invalid json
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , invalidJSON ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , ` The file should include valid JSON ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
// team invalid file
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1Spec , "" , "no_such_file.json" ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , ` no such file ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
// team not .json
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1Spec , "" , "no_such_file.txt" ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , ` Couldn’ t edit macos_setup_assistant. The file should be a .json file. ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
// team invalid json
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1Spec , "" , invalidJSON ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , ` The file should include valid JSON ` )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
} )
2023-05-02 19:03:10 +00:00
t . Run ( "setup assistant get and apply roundtrip" , func ( t * testing . T ) {
2023-04-25 13:36:01 +00:00
ds := setupServer ( t , true )
2023-04-26 21:09:21 +00:00
b , err := os . ReadFile ( filepath . Join ( "testdata" , "macosSetupExpectedAppConfigEmpty.yml" ) )
2023-04-25 13:36:01 +00:00
require . NoError ( t , err )
expectedEmptyAppCfg := string ( b )
2023-04-26 21:09:21 +00:00
b , err = os . ReadFile ( filepath . Join ( "testdata" , "macosSetupExpectedAppConfigSet.yml" ) )
2023-04-25 13:36:01 +00:00
require . NoError ( t , err )
2023-04-26 21:09:21 +00:00
expectedAppCfgSet := fmt . Sprintf ( string ( b ) , "" , emptyMacosSetup )
2023-04-25 13:36:01 +00:00
2023-04-26 21:09:21 +00:00
b , err = os . ReadFile ( filepath . Join ( "testdata" , "macosSetupExpectedTeam1Empty.yml" ) )
2023-04-25 13:36:01 +00:00
require . NoError ( t , err )
expectedEmptyTm1 := string ( b )
2023-04-26 21:09:21 +00:00
b , err = os . ReadFile ( filepath . Join ( "testdata" , "macosSetupExpectedTeam1And2Empty.yml" ) )
2023-04-25 13:36:01 +00:00
require . NoError ( t , err )
expectedEmptyTm1And2 := string ( b )
2023-04-26 21:09:21 +00:00
b , err = os . ReadFile ( filepath . Join ( "testdata" , "macosSetupExpectedTeam1And2Set.yml" ) )
2023-04-25 13:36:01 +00:00
require . NoError ( t , err )
2023-04-26 21:09:21 +00:00
expectedTm1And2Set := fmt . Sprintf ( string ( b ) , "" , emptyMacosSetup )
2023-04-25 13:36:01 +00:00
// get without setup assistant set
assert . YAMLEq ( t , expectedEmptyAppCfg , runAppForTest ( t , [ ] string { "get" , "config" , "--yaml" } ) )
assert . YAMLEq ( t , expectedEmptyTm1 , runAppForTest ( t , [ ] string { "get" , "teams" , "--yaml" } ) )
// apply with dry-run, appconfig
2023-04-26 21:09:21 +00:00
name := writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , emptyMacosSetup ) )
2023-04-25 13:36:01 +00:00
assert . Equal ( t , "[+] would've applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "--dry-run" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
// apply with dry-run, teams
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1And2Spec , "" , emptyMacosSetup , "" , emptyMacosSetup ) )
2023-04-25 13:36:01 +00:00
assert . Equal ( t , "[+] would've applied 2 teams\n" , runAppForTest ( t , [ ] string { "apply" , "--dry-run" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
// get, setup assistant still not set
assert . YAMLEq ( t , expectedEmptyAppCfg , runAppForTest ( t , [ ] string { "get" , "config" , "--yaml" } ) )
assert . YAMLEq ( t , expectedEmptyTm1 , runAppForTest ( t , [ ] string { "get" , "teams" , "--yaml" } ) )
// apply appconfig for real
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , emptyMacosSetup ) )
2023-04-25 13:36:01 +00:00
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
// apply teams for real
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1And2Spec , "" , emptyMacosSetup , "" , emptyMacosSetup ) )
2023-04-25 13:36:01 +00:00
ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked = false
assert . Equal ( t , "[+] applied 2 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . True ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . SaveTeamFuncInvoked )
// get, setup assistant is now set
assert . YAMLEq ( t , expectedAppCfgSet , runAppForTest ( t , [ ] string { "get" , "config" , "--yaml" } ) )
assert . YAMLEq ( t , expectedTm1And2Set , runAppForTest ( t , [ ] string { "get" , "teams" , "--yaml" } ) )
// clear with dry-run, appconfig
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , "" ) )
2023-04-25 13:36:01 +00:00
ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked = false
ds . SaveAppConfigFuncInvoked = false
assert . Equal ( t , "[+] would've applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "--dry-run" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
// clear with dry-run, teams
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1And2Spec , "" , "" , "" , "" ) )
2023-04-25 13:36:01 +00:00
ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked = false
ds . SaveTeamFuncInvoked = false
assert . Equal ( t , "[+] would've applied 2 teams\n" , runAppForTest ( t , [ ] string { "apply" , "--dry-run" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . SaveTeamFuncInvoked )
// apply appconfig without the setup assistant key
name = writeTmpYml ( t , appConfigNoKeySpec )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
// apply team 1 without the setup assistant key
name = writeTmpYml ( t , team1NoKeySpec )
assert . Equal ( t , "[+] applied 1 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . SaveTeamFuncInvoked )
// get, results unchanged
assert . YAMLEq ( t , expectedAppCfgSet , runAppForTest ( t , [ ] string { "get" , "config" , "--yaml" } ) )
assert . YAMLEq ( t , expectedTm1And2Set , runAppForTest ( t , [ ] string { "get" , "teams" , "--yaml" } ) )
// clear appconfig for real
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , "" ) )
2023-04-25 13:36:01 +00:00
ds . SaveAppConfigFuncInvoked = false
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . DeleteMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
// clear teams for real
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1And2Spec , "" , "" , "" , "" ) )
2023-04-25 13:36:01 +00:00
ds . SaveTeamFuncInvoked = false
assert . Equal ( t , "[+] applied 2 teams\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , name } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . DeleteMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . SaveTeamFuncInvoked )
// get, results now empty
assert . YAMLEq ( t , expectedEmptyAppCfg , runAppForTest ( t , [ ] string { "get" , "config" , "--yaml" } ) )
assert . YAMLEq ( t , expectedEmptyTm1And2 , runAppForTest ( t , [ ] string { "get" , "teams" , "--yaml" } ) )
// apply appconfig with invalid key #1
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , invalidWebURLMacosSetup ) )
2023-04-25 13:36:01 +00:00
ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked = false
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , "The automatic enrollment profile can’ t include configuration_web_url." )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
// apply appconfig with invalid key #3
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , invalidURLMacosSetup ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , "The automatic enrollment profile can’ t include url." )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
// apply teams with invalid key #1
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1And2Spec , "" , invalidWebURLMacosSetup , "" , invalidWebURLMacosSetup ) )
2023-04-25 13:36:01 +00:00
ds . SaveTeamFuncInvoked = false
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , "The automatic enrollment profile can’ t include configuration_web_url." )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
// apply teams with invalid key #3
2023-04-26 21:09:21 +00:00
name = writeTmpYml ( t , fmt . Sprintf ( team1And2Spec , "" , invalidURLMacosSetup , "" , invalidURLMacosSetup ) )
2023-04-25 13:36:01 +00:00
_ , err = runAppNoChecks ( [ ] string { "apply" , "-f" , name } )
require . ErrorContains ( t , err , "The automatic enrollment profile can’ t include url." )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
} )
2023-05-02 19:03:10 +00:00
t . Run ( "new bootstrap package" , func ( t * testing . T ) {
cases := [ ] struct {
pkgName string
expectedErr error
} {
{ "signed.pkg" , nil } ,
{ "unsigned.pkg" , errors . New ( "applying fleet config: Couldn’ t edit bootstrap_package. The bootstrap_package must be signed. Learn how to sign the package in the Fleet documentation: https://fleetdm.com/docs/using-fleet/mdm-macos-setup#step-2-sign-the-package" ) } ,
{ "invalid.tar.gz" , errors . New ( "applying fleet config: Couldn’ t edit bootstrap_package. The file must be a package (.pkg)." ) } ,
{ "wrong-toc.pkg" , errors . New ( "applying fleet config: checking package signature: decompressing TOC: unexpected EOF" ) } ,
}
for _ , c := range cases {
t . Run ( c . pkgName , func ( t * testing . T ) {
pkgBytes , err := os . ReadFile ( filepath . Join ( "../../server/service/testdata/bootstrap-packages" , c . pkgName ) )
require . NoError ( t , err )
srv := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Length" , strconv . Itoa ( len ( pkgBytes ) ) )
w . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
w . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` attachment;filename="%s" ` , c . pkgName ) )
if n , err := w . Write ( pkgBytes ) ; err != nil {
require . NoError ( t , err )
require . Equal ( t , len ( pkgBytes ) , n )
}
} ) )
defer srv . Close ( )
ds := setupServer ( t , true )
ds . InsertMDMAppleBootstrapPackageFunc = func ( ctx context . Context , bp * fleet . MDMAppleBootstrapPackage ) error {
require . Equal ( t , len ( bp . Bytes ) , len ( pkgBytes ) )
return nil
}
ds . GetMDMAppleBootstrapPackageMetaFunc = func ( ctx context . Context , teamID uint ) ( * fleet . MDMAppleBootstrapPackage , error ) {
return nil , & notFoundError { }
}
mockStore . Lock ( )
assert . Equal ( t , "" , mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage . Value )
mockStore . Unlock ( )
// create the app config yaml with server url for bootstrap package
tmpFilename := writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , srv . URL , "" ) )
if c . expectedErr != nil {
runAppCheckErr ( t , [ ] string { "apply" , "-f" , tmpFilename } , c . expectedErr . Error ( ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . False ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . SaveAppConfigFuncInvoked )
mockStore . Lock ( )
assert . Equal ( t , "" , mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage . Value )
mockStore . Unlock ( )
} else {
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , tmpFilename } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . True ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
mockStore . Lock ( )
assert . Equal ( t , srv . URL , mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage . Value )
mockStore . Unlock ( )
}
} )
}
} )
t . Run ( "replace bootstrap package" , func ( t * testing . T ) {
pkgName := "signed.pkg"
pkgBytes , err := os . ReadFile ( filepath . Join ( "../../server/service/testdata/bootstrap-packages" , pkgName ) )
require . NoError ( t , err )
pkgHash := sha256 . New ( )
n , err := io . Copy ( pkgHash , bytes . NewReader ( pkgBytes ) )
require . NoError ( t , err )
require . Equal ( t , int64 ( len ( pkgBytes ) ) , n )
srv := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Length" , strconv . Itoa ( len ( pkgBytes ) ) )
w . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
w . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` attachment;filename="%s" ` , pkgName ) )
if n , err := w . Write ( pkgBytes ) ; err != nil {
require . NoError ( t , err )
require . Equal ( t , len ( pkgBytes ) , n )
}
} ) )
defer srv . Close ( )
ds := setupServer ( t , true )
ds . InsertMDMAppleBootstrapPackageFunc = func ( ctx context . Context , bp * fleet . MDMAppleBootstrapPackage ) error {
mockStore . Lock ( )
defer mockStore . Unlock ( )
require . Equal ( t , pkgName , bp . Name )
require . Equal ( t , len ( bp . Bytes ) , len ( pkgBytes ) )
require . Equal ( t , pkgHash . Sum ( nil ) , bp . Sha256 )
mockStore . metaHash = bp . Sha256
return nil
}
ds . DeleteMDMAppleBootstrapPackageFunc = func ( ctx context . Context , teamID uint ) error {
require . Equal ( t , uint ( 0 ) , teamID )
return nil
}
ds . GetMDMAppleBootstrapPackageMetaFunc = func ( ctx context . Context , teamID uint ) ( * fleet . MDMAppleBootstrapPackage , error ) {
mockStore . Lock ( )
defer mockStore . Unlock ( )
return & fleet . MDMAppleBootstrapPackage {
TeamID : 0 ,
Name : pkgName ,
Sha256 : mockStore . metaHash ,
Token : "token" ,
CreatedAt : time . Now ( ) . Add ( - 1 * time . Hour ) ,
UpdatedAt : time . Now ( ) . Add ( - 1 * time . Hour ) ,
} , nil
}
mockStore . Lock ( )
mockStore . metaHash = [ ] byte ( "foobar" ) // initial hash is a throwaway
mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage = optjson . SetString ( "https://example.com" ) // initial value is a throwaway
mockStore . Unlock ( )
// upload a new package
tmpFilename := writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , srv . URL , "" ) )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , tmpFilename } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . True ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . True ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
mockStore . Lock ( )
assert . Equal ( t , srv . URL , mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage . Value )
mockStore . Unlock ( )
ds . GetMDMAppleBootstrapPackageMetaFuncInvoked = false
ds . InsertMDMAppleBootstrapPackageFuncInvoked = false
ds . DeleteMDMAppleBootstrapPackageFuncInvoked = false
ds . SaveAppConfigFuncInvoked = false
// running again should not re-upload
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , tmpFilename } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . False ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
mockStore . Lock ( )
assert . Equal ( t , srv . URL , mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage . Value )
mockStore . Unlock ( )
ds . GetMDMAppleBootstrapPackageMetaFuncInvoked = false
ds . InsertMDMAppleBootstrapPackageFuncInvoked = false
ds . DeleteMDMAppleBootstrapPackageFuncInvoked = false
ds . SaveAppConfigFuncInvoked = false
// empty server url should delete the package
tmpFilename = writeTmpYml ( t , fmt . Sprintf ( appConfigSpec , "" , "" ) )
assert . Equal ( t , "[+] applied fleet config\n" , runAppForTest ( t , [ ] string { "apply" , "-f" , tmpFilename } ) )
assert . False ( t , ds . SetOrUpdateMDMAppleSetupAssistantFuncInvoked )
assert . True ( t , ds . GetMDMAppleBootstrapPackageMetaFuncInvoked )
assert . False ( t , ds . InsertMDMAppleBootstrapPackageFuncInvoked )
assert . True ( t , ds . DeleteMDMAppleBootstrapPackageFuncInvoked )
assert . True ( t , ds . SaveAppConfigFuncInvoked )
mockStore . Lock ( )
assert . Equal ( t , "" , mockStore . appConfig . MDM . MacOSSetup . BootstrapPackage . Value )
mockStore . Unlock ( )
} )
2023-05-10 20:22:08 +00:00
// // TODO: restore this test when we have a way to mock the Apple Business Manager API in
// // fleetctl tests
// t.Run("enable end user authentication", func(t *testing.T) {
// ds := setupServer(t, true)
// // setup app config
// b, err := os.ReadFile(filepath.Join("testdata", "macosSetupExpectedAppConfigEmpty.yml"))
// require.NoError(t, err)
// expectedNotSetAppConfg := string(b)
// assert.YAMLEq(t, expectedNotSetAppConfg, runAppForTest(t, []string{"get", "config", "--yaml"}))
// // enable end user auth in app config
// name := writeTmpYml(t, fmt.Sprintf(appConfigSpecEnableEndUserAuth, "true"))
// _, err = runAppNoChecks([]string{"apply", "-f", name})
// require.NoError(t, err)
// assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// assert.False(t, ds.GetMDMAppleBootstrapPackageMetaFuncInvoked)
// assert.False(t, ds.InsertMDMAppleBootstrapPackageFuncInvoked)
// assert.False(t, ds.DeleteMDMAppleBootstrapPackageFuncInvoked)
// assert.True(t, ds.SaveAppConfigFuncInvoked)
// expectedSetAppCfg := strings.ReplaceAll(expectedNotSetAppConfg, "enable_end_user_authentication: false", "enable_end_user_authentication: true")
// assert.YAMLEq(t, expectedSetAppCfg, runAppForTest(t, []string{"get", "config", "--yaml"}))
// ds.SaveAppConfigFuncInvoked = false
// // disable end user auth in app config
// name = writeTmpYml(t, fmt.Sprintf(appConfigSpecEnableEndUserAuth, "false"))
// _, err = runAppNoChecks([]string{"apply", "-f", name})
// require.NoError(t, err)
// assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// assert.False(t, ds.GetMDMAppleBootstrapPackageMetaFuncInvoked)
// assert.False(t, ds.InsertMDMAppleBootstrapPackageFuncInvoked)
// assert.False(t, ds.DeleteMDMAppleBootstrapPackageFuncInvoked)
// assert.True(t, ds.SaveAppConfigFuncInvoked)
// assert.YAMLEq(t, expectedNotSetAppConfg, runAppForTest(t, []string{"get", "config", "--yaml"}))
// ds.SaveAppConfigFuncInvoked = false
// // setup team config
// assert.False(t, ds.SaveTeamFuncInvoked)
// b, err = os.ReadFile(filepath.Join("testdata", "macosSetupExpectedTeam1Empty.yml"))
// require.NoError(t, err)
// expectedNotSetTeam1 := string(b)
// assert.YAMLEq(t, expectedNotSetTeam1, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// // enable end user auth in team config
// name = writeTmpYml(t, fmt.Sprintf(team1SpecEnableEndUserAuth, "true"))
// _, err = runAppNoChecks([]string{"apply", "-f", name})
// require.NoError(t, err)
// assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// assert.False(t, ds.GetMDMAppleBootstrapPackageMetaFuncInvoked)
// assert.False(t, ds.InsertMDMAppleBootstrapPackageFuncInvoked)
// assert.False(t, ds.DeleteMDMAppleBootstrapPackageFuncInvoked)
// assert.False(t, ds.SaveAppConfigFuncInvoked)
// assert.True(t, ds.SaveTeamFuncInvoked)
// expectedSetTeam1 := strings.ReplaceAll(expectedNotSetTeam1, "enable_end_user_authentication: false", "enable_end_user_authentication: true")
// expectedSetTeam1 = strings.ReplaceAll(expectedSetTeam1, "enable_host_users: false", "enable_host_users: true")
// expectedSetTeam1 = strings.ReplaceAll(expectedSetTeam1, "enable_software_inventory: false", "enable_software_inventory: true")
// assert.YAMLEq(t, expectedSetTeam1, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// ds.SaveTeamFuncInvoked = false
// // disable end user auth in team config
// name = writeTmpYml(t, fmt.Sprintf(team1SpecEnableEndUserAuth, "false"))
// _, err = runAppNoChecks([]string{"apply", "-f", name})
// require.NoError(t, err)
// assert.False(t, ds.SetOrUpdateMDMAppleSetupAssistantFuncInvoked)
// assert.False(t, ds.GetMDMAppleBootstrapPackageMetaFuncInvoked)
// assert.False(t, ds.InsertMDMAppleBootstrapPackageFuncInvoked)
// assert.False(t, ds.DeleteMDMAppleBootstrapPackageFuncInvoked)
// assert.False(t, ds.SaveAppConfigFuncInvoked)
// assert.True(t, ds.SaveTeamFuncInvoked)
// expectedSetTeam1 = strings.ReplaceAll(expectedSetTeam1, "enable_end_user_authentication: true", "enable_end_user_authentication: false")
// assert.YAMLEq(t, expectedSetTeam1, runAppForTest(t, []string{"get", "teams", "--yaml"}))
// ds.SaveTeamFuncInvoked = false
// })
2023-04-25 13:36:01 +00:00
}
2022-09-19 17:53:44 +00:00
func TestApplySpecs ( t * testing . T ) {
2023-04-25 13:36:01 +00:00
// create a macos setup json file (content not important)
macSetupFile := writeTmpJSON ( t , map [ string ] any { } )
2022-09-19 17:53:44 +00:00
setupDS := func ( ds * mock . Store ) {
// labels
ds . ApplyLabelSpecsFunc = func ( ctx context . Context , specs [ ] * fleet . LabelSpec ) error {
return nil
}
// teams - team ID 1 already exists
teamsByName := map [ string ] * fleet . Team {
"team1" : {
ID : 1 ,
Name : "team1" ,
Description : "team1 description" ,
} ,
}
ds . TeamByNameFunc = func ( ctx context . Context , name string ) ( * fleet . Team , error ) {
team , ok := teamsByName [ name ]
if ! ok {
return nil , sql . ErrNoRows
}
return team , nil
}
i := 1 // new teams will start at 2
ds . NewTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
i ++
team . ID = uint ( i )
teamsByName [ team . Name ] = team
return team , nil
}
agentOpts := json . RawMessage ( ` { "config": { }} ` )
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
return & fleet . AppConfig { AgentOptions : & agentOpts } , nil
}
ds . SaveTeamFunc = func ( ctx context . Context , team * fleet . Team ) ( * fleet . Team , error ) {
teamsByName [ team . Name ] = team
return team , nil
}
ds . ApplyEnrollSecretsFunc = func ( ctx context . Context , teamID * uint , secrets [ ] * fleet . EnrollSecret ) error {
return nil
}
// activities
2022-12-23 16:05:16 +00:00
ds . NewActivityFunc = func ( ctx context . Context , user * fleet . User , activity fleet . ActivityDetails ) error {
2022-09-19 17:53:44 +00:00
return nil
}
// app config
ds . ListUsersFunc = func ( ctx context . Context , opt fleet . UserListOptions ) ( [ ] * fleet . User , error ) {
return userRoleSpecList , nil
}
ds . UserByEmailFunc = func ( ctx context . Context , email string ) ( * fleet . User , error ) {
if email == "admin1@example.com" {
return userRoleSpecList [ 0 ] , nil
}
return userRoleSpecList [ 1 ] , nil
}
ds . AppConfigFunc = func ( ctx context . Context ) ( * fleet . AppConfig , error ) {
return & fleet . AppConfig { OrgInfo : fleet . OrgInfo { OrgName : "Fleet" } , ServerSettings : fleet . ServerSettings { ServerURL : "https://example.org" } } , nil
}
ds . SaveAppConfigFunc = func ( ctx context . Context , config * fleet . AppConfig ) error {
return nil
}
}
cases := [ ] struct {
desc string
flags [ ] string
spec string
wantOutput string
wantErr string
} {
{
desc : "empty team spec" ,
spec : `
apiVersion : v1
kind : team
spec :
` ,
wantOutput : "[+] applied 1 teams" ,
} ,
{
desc : "empty team name" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : ""
` ,
wantErr : ` 422 Validation Failed: name may not be empty ` ,
} ,
{
desc : "invalid agent options for existing team" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
agent_options :
config :
blah : nope
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "blah" ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "invalid top-level key for team" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
blah : nope
` ,
2022-10-24 12:49:44 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "blah" ` ,
} ,
{
desc : "invalid known key's value type for team cannot be forced" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : 123
` ,
flags : [ ] string { "--force" } ,
wantErr : ` 400 Bad Request: invalid value type at 'specs.name': expected string but got number ` ,
} ,
{
desc : "unknown key for team can be forced" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
blah : true
` ,
flags : [ ] string { "--force" } ,
wantOutput : ` [+] applied 1 teams ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "invalid agent options for new team" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
config :
blah : nope
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "blah" ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "invalid agent options dry-run" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
config :
blah : nope
` ,
flags : [ ] string { "--dry-run" } ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "blah" ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "invalid agent options force" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
config :
blah : nope
` ,
flags : [ ] string { "--force" } ,
wantOutput : ` [+] applied 1 teams ` ,
} ,
{
desc : "invalid agent options field type" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
config :
options :
aws_debug : 123
` ,
flags : [ ] string { "--dry-run" } ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: invalid value type at 'options.aws_debug': expected bool but got number ` ,
2022-09-19 17:53:44 +00:00
} ,
2022-10-03 12:29:41 +00:00
{
desc : "invalid team agent options command-line flag" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
command_line_flags :
no_such_flag : 123
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "no_such_flag" ` ,
2022-10-03 12:29:41 +00:00
} ,
{
desc : "valid team agent options command-line flag" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
command_line_flags :
enable_tables : "abc"
` ,
wantOutput : ` [+] applied 1 teams ` ,
} ,
2022-09-19 17:53:44 +00:00
{
desc : "invalid agent options field type in overrides" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
agent_options :
config :
options :
aws_debug : true
overrides :
platforms :
darwin :
options :
aws_debug : 123
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: invalid value type at 'options.aws_debug': expected bool but got number ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "empty config" ,
spec : `
apiVersion : v1
kind : config
spec :
` ,
wantOutput : ` ` , // no output for empty config
} ,
{
desc : "config with blank required org name" ,
spec : `
apiVersion : v1
kind : config
spec :
org_info :
org_name : ""
` ,
wantErr : ` 422 Validation Failed: organization name must be present ` ,
} ,
{
desc : "config with blank required server url" ,
spec : `
apiVersion : v1
kind : config
spec :
server_settings :
server_url : ""
` ,
wantErr : ` 422 Validation Failed: Fleet server URL must be present ` ,
} ,
{
desc : "config with unknown key" ,
spec : `
apiVersion : v1
kind : config
spec :
server_settings :
foo : bar
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "foo" ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "config with invalid key type" ,
spec : `
apiVersion : v1
kind : config
spec :
server_settings :
server_url : 123
` ,
Add UUID to Fleet errors and clean up error msgs (#10411)
#8129
Apart from fixing the issue in #8129, this change also introduces UUIDs
to Fleet errors. To be able to match a returned error from the API to a
error in the Fleet logs. See
https://fleetdm.slack.com/archives/C019WG4GH0A/p1677780622769939 for
more context.
Samples with the changes in this PR:
```
curl -k -H "Authorization: Bearer $TEST_TOKEN" -H 'Content-Type:application/json' "https://localhost:8080/api/v1/fleet/sso" -d ''
{
"message": "Bad request",
"errors": [
{
"name": "base",
"reason": "Expected JSON Body"
}
],
"uuid": "a01f6e10-354c-4ff0-b96e-1f64adb500b0"
}
```
```
curl -k -H "Authorization: Bearer $TEST_TOKEN" -H 'Content-Type:application/json' "https://localhost:8080/api/v1/fleet/sso" -d 'asd'
{
"message": "Bad request",
"errors": [
{
"name": "base",
"reason": "json decoder error"
}
],
"uuid": "5f716a64-7550-464b-a1dd-e6a505a9f89d"
}
```
```
curl -k -X GET -H "Authorization: Bearer badtoken" "https://localhost:8080/api/latest/fleet/teams"
{
"message": "Authentication required",
"errors": [
{
"name": "base",
"reason": "Authentication required"
}
],
"uuid": "efe45bc0-f956-4bf9-ba4f-aa9020a9aaaf"
}
```
```
curl -k -X PATCH -H "Authorization: Bearer $TEST_TOKEN" "https://localhost:8080/api/latest/fleet/users/14" -d '{"name": "Manuel2", "password": "what", "new_password": "p4ssw0rd.12345"}'
{
"message": "Authorization header required",
"errors": [
{
"name": "base",
"reason": "Authorization header required"
}
],
"uuid": "57f78cd0-4559-464f-9df7-36c9ef7c89b3"
}
```
```
curl -k -X PATCH -H "Authorization: Bearer $TEST_TOKEN" "https://localhost:8080/api/latest/fleet/users/14" -d '{"name": "Manuel2", "password": "what", "new_password": "p4ssw0rd.12345"}'
{
"message": "Permission Denied",
"uuid": "7f0220ad-6de7-4faf-8b6c-8d7ff9d2ca06"
}
```
- [X] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [X] Documented any API changes (docs/Using-Fleet/REST-API.md or
docs/Contributing/API-for-contributors.md)
- ~[ ] Documented any permissions changes~
- ~[ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)~
- ~[ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for
new osquery data ingestion features.~
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
- For Orbit and Fleet Desktop changes:
- [X] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.
- ~[ ] Auto-update manual QA, from released version of component to new
version (see [tools/tuf/test](../tools/tuf/test/README.md)).~
2023-03-13 16:44:06 +00:00
wantErr : ` 400 Bad request: failed to decode app config ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "config with invalid agent options in dry-run" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
foo : bar
` ,
flags : [ ] string { "--dry-run" } ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "foo" ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "config with invalid agent options data type in dry-run" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
config :
options :
aws_debug : 123
` ,
flags : [ ] string { "--dry-run" } ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: invalid value type at 'options.aws_debug': expected bool but got number ` ,
2022-09-19 17:53:44 +00:00
} ,
{
desc : "config with invalid agent options data type with force" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
config :
options :
aws_debug : 123
` ,
flags : [ ] string { "--force" } ,
wantOutput : ` [+] applied fleet config ` ,
} ,
2022-10-03 12:29:41 +00:00
{
desc : "config with invalid agent options command-line flags" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
command_line_flags :
enable_tables : "foo"
no_such_flag : false
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: unsupported key provided: "no_such_flag" ` ,
2022-10-03 12:29:41 +00:00
} ,
{
desc : "config with invalid value for agent options command-line flags" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
command_line_flags :
enable_tables : 123
` ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad Request: invalid value type at 'enable_tables': expected string but got number ` ,
2022-10-03 12:29:41 +00:00
} ,
{
desc : "config with valid agent options command-line flags" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
command_line_flags :
enable_tables : "abc"
` ,
wantOutput : ` [+] applied fleet config ` ,
} ,
2022-09-19 17:53:44 +00:00
{
desc : "dry-run set with unsupported spec" ,
spec : `
apiVersion : v1
kind : label
spec :
name : label1
query : SELECT 1
` ,
flags : [ ] string { "--dry-run" } ,
wantOutput : ` [!] ignoring labels, dry run mode only supported for 'config' and 'team' specs ` ,
} ,
{
desc : "dry-run set with various specs, appconfig warning for legacy" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
-- -
apiVersion : v1
kind : label
spec :
name : label1
query : SELECT 1
-- -
apiVersion : v1
kind : config
spec :
host_settings :
enable_software_inventory : true
` ,
flags : [ ] string { "--dry-run" } ,
2022-10-12 21:10:50 +00:00
wantErr : ` 400 Bad request: warning: deprecated settings were used in the configuration: [host_settings] ` ,
2022-09-19 17:53:44 +00:00
wantOutput : ` [!] ignoring labels, dry run mode only supported for 'config' and 'team' spec ` ,
} ,
{
desc : "dry-run set with various specs, no errors" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : teamNEW
-- -
apiVersion : v1
kind : label
spec :
name : label1
query : SELECT 1
-- -
apiVersion : v1
kind : config
spec :
features :
enable_software_inventory : true
` ,
flags : [ ] string { "--dry-run" } ,
wantOutput : ` [ ! ] ignoring labels , dry run mode only supported for ' config ' and ' team ' specs
[ + ] would ' ve applied fleet config
[ + ] would ' ve applied 1 teams ` ,
} ,
2023-01-24 16:20:02 +00:00
{
desc : "macos_updates deadline set but minimum_version empty" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
deadline : 2022 - 01 - 04
` ,
wantErr : ` 422 Validation Failed: minimum_version is required when deadline is provided ` ,
} ,
{
desc : "macos_updates minimum_version set but deadline empty" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version : "12.2"
` ,
wantErr : ` 422 Validation Failed: deadline is required when minimum_version is provided ` ,
} ,
{
desc : "macos_updates.minimum_version with build version" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version : "12.2 (ABCD)"
deadline : 1892 - 01 - 01
` ,
wantErr : ` 422 Validation Failed: minimum_version accepts version numbers only. (E.g., "13.0.1.") NOT "Ventura 13" or "13.0.1 (22A400)" ` ,
} ,
{
desc : "macos_updates.deadline with timestamp" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version : "12.2"
deadline : "1892-01-01T00:00:00Z"
` ,
wantErr : ` 422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.") ` ,
} ,
{
desc : "macos_updates.deadline with invalid date" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version : "12.2"
deadline : "18-01-01"
` ,
wantErr : ` 422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.") ` ,
} ,
{
desc : "macos_updates.deadline with incomplete date" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_updates :
minimum_version : "12.2"
deadline : "2022-01"
` ,
wantErr : ` 422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.") ` ,
} ,
2022-09-19 17:53:44 +00:00
{
desc : "missing required sso entity_id" ,
spec : `
apiVersion : v1
kind : config
spec :
sso_settings :
enable_sso : true
entity_id : ""
issuer_uri : "http://localhost:8080/simplesaml/saml2/idp/SSOService.php"
idp_name : "SimpleSAML"
metadata_url : "http://localhost:9080/simplesaml/saml2/idp/metadata.php"
` ,
wantErr : ` 422 Validation Failed: required ` ,
} ,
{
desc : "missing required sso idp_name" ,
spec : `
apiVersion : v1
kind : config
spec :
sso_settings :
enable_sso : true
entity_id : "https://localhost:8080"
issuer_uri : "http://localhost:8080/simplesaml/saml2/idp/SSOService.php"
idp_name : ""
metadata_url : "http://localhost:9080/simplesaml/saml2/idp/metadata.php"
` ,
wantErr : ` 422 Validation Failed: required ` ,
} ,
{
desc : "missing required failing policies destination_url" ,
spec : `
apiVersion : v1
kind : config
spec :
webhook_settings :
failing_policies_webhook :
enable_failing_policies_webhook : true
destination_url : ""
policy_ids :
- 1
host_batch_size : 1000
interval : 1 h
` ,
wantErr : ` 422 Validation Failed: destination_url is required to enable the failing policies webhook ` ,
} ,
{
desc : "missing required vulnerabilities destination_url" ,
spec : `
apiVersion : v1
kind : config
spec :
webhook_settings :
vulnerabilities_webhook :
enable_vulnerabilities_webhook : true
destination_url : ""
host_batch_size : 1000
interval : 1 h
` ,
wantErr : ` 422 Validation Failed: destination_url is required to enable the vulnerabilities webhook ` ,
} ,
{
desc : "missing required host status destination_url" ,
spec : `
apiVersion : v1
kind : config
spec :
webhook_settings :
host_status_webhook :
enable_host_status_webhook : true
destination_url : ""
days_count : 10
host_percentage : 10
interval : 1 h
` ,
wantErr : ` 422 Validation Failed: destination_url is required to enable the host status webhook ` ,
} ,
{
desc : "missing required host status days_count" ,
spec : `
apiVersion : v1
kind : config
spec :
webhook_settings :
host_status_webhook :
enable_host_status_webhook : true
destination_url : "http://some/url"
days_count : 0
host_percentage : 10
interval : 1 h
` ,
wantErr : ` 422 Validation Failed: days_count must be > 0 to enable the host status webhook ` ,
} ,
{
desc : "missing required host status host_percentage" ,
spec : `
apiVersion : v1
kind : config
spec :
webhook_settings :
host_status_webhook :
enable_host_status_webhook : true
destination_url : "http://some/url"
days_count : 10
host_percentage : - 1
interval : 1 h
` ,
wantErr : ` 422 Validation Failed: host_percentage must be > 0 to enable the host status webhook ` ,
} ,
2022-11-14 20:50:41 +00:00
{
desc : "config with FIM values for agent options (#8699)" ,
spec : `
apiVersion : v1
kind : config
spec :
agent_options :
config :
file_paths :
ssh :
- / home / % / . ssh / authorized_keys
exclude_paths :
ssh :
- / home / ubuntu / . ssh / authorized_keys
` ,
wantOutput : ` [+] applied fleet config ` ,
} ,
2023-01-24 16:20:02 +00:00
{
desc : "app config macos_updates deadline set but minimum_version empty" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_updates :
deadline : 2022 - 01 - 04
` ,
wantErr : ` 422 Validation Failed: minimum_version is required when deadline is provided ` ,
} ,
{
desc : "app config macos_updates minimum_version set but deadline empty" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_updates :
minimum_version : "12.2"
` ,
wantErr : ` 422 Validation Failed: deadline is required when minimum_version is provided ` ,
} ,
{
desc : "app config macos_updates.minimum_version with build version" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_updates :
minimum_version : "12.2 (ABCD)"
deadline : 1892 - 01 - 01
` ,
wantErr : ` 422 Validation Failed: minimum_version accepts version numbers only. (E.g., "13.0.1.") NOT "Ventura 13" or "13.0.1 (22A400)" ` ,
} ,
{
desc : "app config macos_updates.deadline with timestamp" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_updates :
minimum_version : "12.2"
deadline : "1892-01-01T00:00:00Z"
` ,
wantErr : ` 422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.") ` ,
} ,
{
desc : "app config macos_updates.deadline with invalid date" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_updates :
minimum_version : "12.2"
deadline : "18-01-01"
` ,
wantErr : ` 422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.") ` ,
} ,
{
desc : "app config macos_updates.deadline with incomplete date" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_updates :
minimum_version : "12.2"
deadline : "2022-01"
` ,
wantErr : ` 422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.") ` ,
} ,
2023-02-28 20:34:46 +00:00
{
desc : "app config macos_settings.enable_disk_encryption without a value" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_settings :
enable_disk_encryption :
` ,
wantOutput : ` [+] applied fleet config ` ,
} ,
{
desc : "app config macos_settings.enable_disk_encryption with invalid value type" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_settings :
enable_disk_encryption : 123
` ,
Add UUID to Fleet errors and clean up error msgs (#10411)
#8129
Apart from fixing the issue in #8129, this change also introduces UUIDs
to Fleet errors. To be able to match a returned error from the API to a
error in the Fleet logs. See
https://fleetdm.slack.com/archives/C019WG4GH0A/p1677780622769939 for
more context.
Samples with the changes in this PR:
```
curl -k -H "Authorization: Bearer $TEST_TOKEN" -H 'Content-Type:application/json' "https://localhost:8080/api/v1/fleet/sso" -d ''
{
"message": "Bad request",
"errors": [
{
"name": "base",
"reason": "Expected JSON Body"
}
],
"uuid": "a01f6e10-354c-4ff0-b96e-1f64adb500b0"
}
```
```
curl -k -H "Authorization: Bearer $TEST_TOKEN" -H 'Content-Type:application/json' "https://localhost:8080/api/v1/fleet/sso" -d 'asd'
{
"message": "Bad request",
"errors": [
{
"name": "base",
"reason": "json decoder error"
}
],
"uuid": "5f716a64-7550-464b-a1dd-e6a505a9f89d"
}
```
```
curl -k -X GET -H "Authorization: Bearer badtoken" "https://localhost:8080/api/latest/fleet/teams"
{
"message": "Authentication required",
"errors": [
{
"name": "base",
"reason": "Authentication required"
}
],
"uuid": "efe45bc0-f956-4bf9-ba4f-aa9020a9aaaf"
}
```
```
curl -k -X PATCH -H "Authorization: Bearer $TEST_TOKEN" "https://localhost:8080/api/latest/fleet/users/14" -d '{"name": "Manuel2", "password": "what", "new_password": "p4ssw0rd.12345"}'
{
"message": "Authorization header required",
"errors": [
{
"name": "base",
"reason": "Authorization header required"
}
],
"uuid": "57f78cd0-4559-464f-9df7-36c9ef7c89b3"
}
```
```
curl -k -X PATCH -H "Authorization: Bearer $TEST_TOKEN" "https://localhost:8080/api/latest/fleet/users/14" -d '{"name": "Manuel2", "password": "what", "new_password": "p4ssw0rd.12345"}'
{
"message": "Permission Denied",
"uuid": "7f0220ad-6de7-4faf-8b6c-8d7ff9d2ca06"
}
```
- [X] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [X] Documented any API changes (docs/Using-Fleet/REST-API.md or
docs/Contributing/API-for-contributors.md)
- ~[ ] Documented any permissions changes~
- ~[ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)~
- ~[ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for
new osquery data ingestion features.~
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
- For Orbit and Fleet Desktop changes:
- [X] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.
- ~[ ] Auto-update manual QA, from released version of component to new
version (see [tools/tuf/test](../tools/tuf/test/README.md)).~
2023-03-13 16:44:06 +00:00
wantErr : ` 400 Bad request: failed to decode app config ` ,
2023-02-28 20:34:46 +00:00
} ,
{
desc : "app config macos_settings.enable_disk_encryption true" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_settings :
enable_disk_encryption : true
` ,
wantErr : ` Couldn't update macos_settings because MDM features aren't turned on in Fleet. ` ,
} ,
{
desc : "app config macos_settings.enable_disk_encryption false" ,
spec : `
apiVersion : v1
kind : config
spec :
mdm :
macos_settings :
enable_disk_encryption : false
` ,
wantOutput : ` [+] applied fleet config ` ,
} ,
{
desc : "team config macos_settings.enable_disk_encryption without a value" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_settings :
enable_disk_encryption :
` ,
wantErr : ` 400 Bad Request: invalid value type at 'macos_settings.enable_disk_encryption': expected bool but got <nil> ` ,
} ,
{
desc : "team config macos_settings.enable_disk_encryption with invalid value type" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_settings :
enable_disk_encryption : 123
` ,
wantErr : ` 400 Bad Request: invalid value type at 'macos_settings.enable_disk_encryption': expected bool but got float64 ` ,
} ,
{
desc : "team config macos_settings.enable_disk_encryption true" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_settings :
enable_disk_encryption : true
` ,
wantErr : ` Couldn't update macos_settings because MDM features aren't turned on in Fleet. ` ,
} ,
{
desc : "team config macos_settings.enable_disk_encryption false" ,
spec : `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_settings :
enable_disk_encryption : false
` ,
wantOutput : ` [+] applied 1 teams ` ,
} ,
2023-04-25 13:36:01 +00:00
{
desc : "team config mac setup assistant" ,
spec : fmt . Sprintf ( `
apiVersion : v1
kind : team
spec :
team :
name : team1
mdm :
macos_setup :
macos_setup_assistant : % s
` , macSetupFile ) ,
wantErr : ` MDM features aren't turned on. ` ,
} ,
{
desc : "app config macos setup assistant" ,
spec : fmt . Sprintf ( `
apiVersion : v1
kind : config
spec :
mdm :
macos_setup :
macos_setup_assistant : % s
` , macSetupFile ) ,
wantErr : ` MDM features aren't turned on. ` ,
} ,
2022-09-19 17:53:44 +00:00
}
// NOTE: Integrations required fields are not tested (Jira/Zendesk) because
// they require a complex setup to mock the client that would communicate
// with the external API. However, we make a test API call when enabling an
// integration, ensuring that any missing configuration field results in an
// error. Same for smtp_settings (a test email is sent when enabling).
license := & fleet . LicenseInfo { Tier : fleet . TierPremium , Expiration : time . Now ( ) . Add ( 24 * time . Hour ) }
for _ , c := range cases {
t . Run ( c . desc , func ( t * testing . T ) {
_ , ds := runServerWithMockedDS ( t , & service . TestServerOpts { License : license } )
setupDS ( ds )
filename := writeTmpYml ( t , c . spec )
var got string
if c . wantErr == "" {
got = runAppForTest ( t , append ( [ ] string { "apply" , "-f" , filename } , c . flags ... ) )
} else {
buf , err := runAppNoChecks ( append ( [ ] string { "apply" , "-f" , filename } , c . flags ... ) )
require . ErrorContains ( t , err , c . wantErr )
got = buf . String ( )
}
if c . wantOutput == "" {
require . Empty ( t , got )
} else {
require . Contains ( t , got , c . wantOutput )
}
} )
}
}