2021-06-01 00:07:51 +00:00
package service
import (
2022-11-01 19:22:45 +00:00
"bytes"
2021-06-01 00:07:51 +00:00
"context"
"encoding/json"
"fmt"
2022-10-12 21:10:50 +00:00
"net/http"
2021-07-23 20:00:26 +00:00
2021-07-19 18:08:41 +00:00
"github.com/fleetdm/fleet/v4/server"
2021-07-13 19:54:22 +00:00
"github.com/fleetdm/fleet/v4/server/authz"
2023-04-12 19:11:04 +00:00
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
2021-11-22 14:13:26 +00:00
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
2021-08-02 22:06:27 +00:00
"github.com/fleetdm/fleet/v4/server/contexts/logging"
2021-06-26 04:46:51 +00:00
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
2021-11-11 16:45:39 +00:00
"github.com/fleetdm/fleet/v4/server/ptr"
2023-05-15 18:06:09 +00:00
"github.com/fleetdm/fleet/v4/server/worker"
2022-09-19 17:53:44 +00:00
"github.com/go-kit/kit/log/level"
2021-06-01 00:07:51 +00:00
)
2023-06-08 21:30:34 +00:00
func obfuscateSecrets ( user * fleet . User , teams [ ] * fleet . Team ) error {
if user == nil {
return & authz . Forbidden { }
}
isGlobalObs := user . IsGlobalObserver ( )
teamMemberships := user . TeamMembership ( func ( t fleet . UserTeam ) bool {
return true
} )
obsMembership := user . TeamMembership ( func ( t fleet . UserTeam ) bool {
return t . Role == fleet . RoleObserver || t . Role == fleet . RoleObserverPlus
} )
for _ , t := range teams {
if t == nil {
continue
}
// User does not belong to the team or is a global/team observer/observer+
if isGlobalObs || user . GlobalRole == nil && ( ! teamMemberships [ t . ID ] || obsMembership [ t . ID ] ) {
for _ , s := range t . Secrets {
s . Secret = fleet . MaskedPassword
}
}
}
return nil
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) NewTeam ( ctx context . Context , p fleet . TeamPayload ) ( * fleet . Team , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { } , fleet . ActionWrite ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-06-24 00:32:31 +00:00
// Copy team options from global options
2021-09-14 12:11:07 +00:00
globalConfig , err := svc . ds . AppConfig ( ctx )
2021-06-24 00:32:31 +00:00
if err != nil {
return nil , err
}
team := & fleet . Team {
2022-03-21 19:16:47 +00:00
Config : fleet . TeamConfig {
AgentOptions : globalConfig . AgentOptions ,
2022-08-30 11:13:09 +00:00
Features : globalConfig . Features ,
2022-03-21 19:16:47 +00:00
} ,
2021-06-24 00:32:31 +00:00
}
2021-06-01 00:07:51 +00:00
if p . Name == nil {
2021-06-06 22:07:29 +00:00
return nil , fleet . NewInvalidArgumentError ( "name" , "missing required argument" )
2021-06-01 00:07:51 +00:00
}
if * p . Name == "" {
2021-06-06 22:07:29 +00:00
return nil , fleet . NewInvalidArgumentError ( "name" , "may not be empty" )
2021-06-01 00:07:51 +00:00
}
team . Name = * p . Name
if p . Description != nil {
team . Description = * p . Description
}
if p . Secrets != nil {
2022-10-05 12:35:36 +00:00
if len ( p . Secrets ) > fleet . MaxEnrollSecretsCount {
return nil , fleet . NewInvalidArgumentError ( "secrets" , "too many secrets" )
}
2021-06-01 00:07:51 +00:00
team . Secrets = p . Secrets
} else {
// Set up a default enroll secret
2021-07-19 18:08:41 +00:00
secret , err := server . GenerateRandomText ( fleet . EnrollSecretDefaultLength )
2021-06-01 00:07:51 +00:00
if err != nil {
2021-11-22 14:13:26 +00:00
return nil , ctxerr . Wrap ( ctx , err , "generate enroll secret string" )
2021-06-01 00:07:51 +00:00
}
2021-06-06 22:07:29 +00:00
team . Secrets = [ ] * fleet . EnrollSecret { { Secret : secret } }
2021-06-01 00:07:51 +00:00
}
2021-06-24 00:32:31 +00:00
2021-09-14 12:11:07 +00:00
team , err = svc . ds . NewTeam ( ctx , team )
2021-06-01 00:07:51 +00:00
if err != nil {
return nil , err
}
2021-07-13 19:54:22 +00:00
if err := svc . ds . NewActivity (
2021-09-14 12:11:07 +00:00
ctx ,
2021-07-13 19:54:22 +00:00
authz . UserFromContext ( ctx ) ,
2022-12-23 16:05:16 +00:00
fleet . ActivityTypeCreatedTeam {
ID : team . ID ,
Name : team . Name ,
} ,
2021-07-13 19:54:22 +00:00
) ; err != nil {
2022-12-23 22:04:13 +00:00
return nil , ctxerr . Wrap ( ctx , err , "create activity for team creation" )
2021-07-13 19:54:22 +00:00
}
2021-06-01 00:07:51 +00:00
return team , nil
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) ModifyTeam ( ctx context . Context , teamID uint , payload fleet . TeamPayload ) ( * fleet . Team , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionWrite ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-09-14 12:11:07 +00:00
team , err := svc . ds . Team ( ctx , teamID )
2021-06-01 00:07:51 +00:00
if err != nil {
return nil , err
}
if payload . Name != nil {
if * payload . Name == "" {
2021-06-06 22:07:29 +00:00
return nil , fleet . NewInvalidArgumentError ( "name" , "may not be empty" )
2021-06-01 00:07:51 +00:00
}
team . Name = * payload . Name
}
if payload . Description != nil {
team . Description = * payload . Description
}
2022-06-06 14:41:51 +00:00
2022-03-21 19:16:47 +00:00
if payload . WebhookSettings != nil {
team . Config . WebhookSettings = * payload . WebhookSettings
}
2021-06-01 00:07:51 +00:00
2023-03-27 19:30:29 +00:00
appCfg , err := svc . ds . AppConfig ( ctx )
if err != nil {
return nil , err
}
2023-05-10 20:22:08 +00:00
var macOSMinVersionUpdated , macOSDiskEncryptionUpdated , macOSEnableEndUserAuthUpdated bool
2023-01-24 16:20:02 +00:00
if payload . MDM != nil {
2023-03-06 14:54:51 +00:00
if payload . MDM . MacOSUpdates != nil {
if err := payload . MDM . MacOSUpdates . Validate ( ) ; err != nil {
return nil , fleet . NewInvalidArgumentError ( "macos_updates" , err . Error ( ) )
}
2023-06-06 18:31:33 +00:00
if payload . MDM . MacOSUpdates . MinimumVersion . Set || payload . MDM . MacOSUpdates . Deadline . Set {
macOSMinVersionUpdated = team . Config . MDM . MacOSUpdates . MinimumVersion . Value != payload . MDM . MacOSUpdates . MinimumVersion . Value ||
team . Config . MDM . MacOSUpdates . Deadline . Value != payload . MDM . MacOSUpdates . Deadline . Value
team . Config . MDM . MacOSUpdates = * payload . MDM . MacOSUpdates
}
2023-03-06 14:54:51 +00:00
}
if payload . MDM . MacOSSettings != nil {
2023-03-27 19:30:29 +00:00
if ! appCfg . MDM . EnabledAndConfigured && payload . MDM . MacOSSettings . EnableDiskEncryption {
2023-03-06 14:54:51 +00:00
return nil , fleet . NewInvalidArgumentError ( "macos_settings.enable_disk_encryption" ,
` Couldn't update macos_settings because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features. ` )
}
2023-03-08 13:31:53 +00:00
macOSDiskEncryptionUpdated = team . Config . MDM . MacOSSettings . EnableDiskEncryption != payload . MDM . MacOSSettings . EnableDiskEncryption
2023-03-06 14:54:51 +00:00
team . Config . MDM . MacOSSettings . EnableDiskEncryption = payload . MDM . MacOSSettings . EnableDiskEncryption
2023-01-24 16:20:02 +00:00
}
2023-05-10 20:22:08 +00:00
if payload . MDM . MacOSSetup != nil {
if ! appCfg . MDM . EnabledAndConfigured && team . Config . MDM . MacOSSetup . EnableEndUserAuthentication != payload . MDM . MacOSSetup . EnableEndUserAuthentication {
return nil , ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_setup.enable_end_user_authentication" ,
` Couldn't update macos_setup.enable_end_user_authentication because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features. ` ) )
}
macOSEnableEndUserAuthUpdated = team . Config . MDM . MacOSSetup . EnableEndUserAuthentication != payload . MDM . MacOSSetup . EnableEndUserAuthentication
if macOSEnableEndUserAuthUpdated && payload . MDM . MacOSSetup . EnableEndUserAuthentication && appCfg . MDM . EndUserAuthentication . IsEmpty ( ) {
// TODO: update this error message to include steps to resolve the issue once docs for IdP
// config are available
return nil , ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_setup.enable_end_user_authentication" ,
` Couldn't enable macos_setup.enable_end_user_authentication because no IdP is configured for MDM features. ` ) )
}
team . Config . MDM . MacOSSetup . EnableEndUserAuthentication = payload . MDM . MacOSSetup . EnableEndUserAuthentication
}
2023-01-24 16:20:02 +00:00
}
2022-06-06 14:41:51 +00:00
if payload . Integrations != nil {
2022-06-13 14:04:47 +00:00
// the team integrations must reference an existing global config integration.
if _ , err := payload . Integrations . MatchWithIntegrations ( appCfg . Integrations ) ; err != nil {
return nil , fleet . NewInvalidArgumentError ( "integrations" , err . Error ( ) )
2022-06-06 14:41:51 +00:00
}
2022-06-13 14:04:47 +00:00
// integrations must be unique
if err := payload . Integrations . Validate ( ) ; err != nil {
return nil , fleet . NewInvalidArgumentError ( "integrations" , err . Error ( ) )
2022-06-06 14:41:51 +00:00
}
2022-06-13 14:04:47 +00:00
team . Config . Integrations . Jira = payload . Integrations . Jira
2022-06-06 14:41:51 +00:00
team . Config . Integrations . Zendesk = payload . Integrations . Zendesk
}
if payload . WebhookSettings != nil || payload . Integrations != nil {
// must validate that at most only one automation is enabled for each
// supported feature - by now the updated payload has been applied to
// team.Config.
invalid := & fleet . InvalidArgumentError { }
fleet . ValidateEnabledFailingPoliciesTeamIntegrations (
team . Config . WebhookSettings . FailingPoliciesWebhook ,
team . Config . Integrations ,
invalid ,
)
if invalid . HasErrors ( ) {
return nil , ctxerr . Wrap ( ctx , invalid )
}
}
2023-01-31 22:36:18 +00:00
team , err = svc . ds . SaveTeam ( ctx , team )
if err != nil {
return nil , err
}
if macOSMinVersionUpdated {
if err := svc . ds . NewActivity (
ctx ,
authz . UserFromContext ( ctx ) ,
fleet . ActivityTypeEditedMacOSMinVersion {
TeamID : & team . ID ,
TeamName : & team . Name ,
2023-06-06 18:31:33 +00:00
MinimumVersion : team . Config . MDM . MacOSUpdates . MinimumVersion . Value ,
Deadline : team . Config . MDM . MacOSUpdates . Deadline . Value ,
2023-01-31 22:36:18 +00:00
} ,
) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "create activity for team macos min version edited" )
}
}
2023-03-08 13:31:53 +00:00
if macOSDiskEncryptionUpdated {
var act fleet . ActivityDetails
if team . Config . MDM . MacOSSettings . EnableDiskEncryption {
act = fleet . ActivityTypeEnabledMacosDiskEncryption { TeamID : & team . ID , TeamName : & team . Name }
if err := svc . MDMAppleEnableFileVaultAndEscrow ( ctx , & team . ID ) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "enable team filevault and escrow" )
}
} else {
act = fleet . ActivityTypeDisabledMacosDiskEncryption { TeamID : & team . ID , TeamName : & team . Name }
if err := svc . MDMAppleDisableFileVaultAndEscrow ( ctx , & team . ID ) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "disable team filevault and escrow" )
}
}
if err := svc . ds . NewActivity ( ctx , authz . UserFromContext ( ctx ) , act ) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "create activity for team macos disk encryption" )
}
}
2023-05-10 20:22:08 +00:00
if macOSEnableEndUserAuthUpdated {
if err := svc . updateMacOSSetupEnableEndUserAuth ( ctx , team . Config . MDM . MacOSSetup . EnableEndUserAuthentication , & team . ID , & team . Name ) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "update macos setup enable end user auth" )
}
}
2023-01-31 22:36:18 +00:00
return team , err
2021-06-01 00:07:51 +00:00
}
2022-09-19 17:53:44 +00:00
func ( svc * Service ) ModifyTeamAgentOptions ( ctx context . Context , teamID uint , teamOptions json . RawMessage , applyOptions fleet . ApplySpecOptions ) ( * fleet . Team , error ) {
2021-06-06 22:07:29 +00:00
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionWrite ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-09-14 12:11:07 +00:00
team , err := svc . ds . Team ( ctx , teamID )
2021-06-01 00:07:51 +00:00
if err != nil {
return nil , err
}
2022-09-19 17:53:44 +00:00
if teamOptions != nil {
if err := fleet . ValidateJSONAgentOptions ( teamOptions ) ; err != nil {
2022-10-12 21:10:50 +00:00
err = fleet . NewUserMessageError ( err , http . StatusBadRequest )
2022-09-19 17:53:44 +00:00
if applyOptions . Force && ! applyOptions . DryRun {
level . Info ( svc . logger ) . Log ( "err" , err , "msg" , "force-apply team agent options with validation errors" )
}
if ! applyOptions . Force {
2022-10-12 21:10:50 +00:00
return nil , ctxerr . Wrap ( ctx , err , "validate agent options" )
2022-09-19 17:53:44 +00:00
}
}
}
if applyOptions . DryRun {
return team , nil
}
if teamOptions != nil {
team . Config . AgentOptions = & teamOptions
2021-06-01 00:07:51 +00:00
} else {
2022-03-21 19:16:47 +00:00
team . Config . AgentOptions = nil
2021-06-01 00:07:51 +00:00
}
2022-08-24 12:32:45 +00:00
tm , err := svc . ds . SaveTeam ( ctx , team )
if err != nil {
return nil , err
}
if err := svc . ds . NewActivity (
ctx ,
authz . UserFromContext ( ctx ) ,
2022-12-23 16:05:16 +00:00
fleet . ActivityTypeEditedAgentOptions {
Global : false ,
TeamID : & team . ID ,
TeamName : & team . Name ,
} ,
2022-08-24 12:32:45 +00:00
) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "create edited agent options activity" )
}
return tm , nil
2021-06-01 00:07:51 +00:00
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) AddTeamUsers ( ctx context . Context , teamID uint , users [ ] fleet . TeamUser ) ( * fleet . Team , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionWrite ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-10-13 15:34:59 +00:00
currentUser := authz . UserFromContext ( ctx )
2021-06-06 22:07:29 +00:00
idMap := make ( map [ uint ] fleet . TeamUser )
2021-06-01 00:07:51 +00:00
for _ , user := range users {
2021-06-06 22:07:29 +00:00
if ! fleet . ValidTeamRole ( user . Role ) {
return nil , fleet . NewInvalidArgumentError ( "users" , fmt . Sprintf ( "%s is not a valid role for a team user" , user . Role ) )
2021-06-01 00:07:51 +00:00
}
idMap [ user . ID ] = user
2021-10-13 15:34:59 +00:00
fullUser , err := svc . ds . UserByID ( ctx , user . ID )
if err != nil {
2021-11-22 14:13:26 +00:00
return nil , ctxerr . Wrapf ( ctx , err , "getting full user with id %d" , user . ID )
2021-10-13 15:34:59 +00:00
}
if fullUser . GlobalRole != nil && currentUser . GlobalRole == nil {
2021-11-22 14:13:26 +00:00
return nil , ctxerr . New ( ctx , "A user with a global role cannot be added to a team by a non global user." )
2021-10-13 15:34:59 +00:00
}
2021-06-01 00:07:51 +00:00
}
2021-09-14 12:11:07 +00:00
team , err := svc . ds . Team ( ctx , teamID )
2021-06-01 00:07:51 +00:00
if err != nil {
return nil , err
}
// Replace existing
for i , existingUser := range team . Users {
if user , ok := idMap [ existingUser . ID ] ; ok {
team . Users [ i ] = user
delete ( idMap , user . ID )
}
}
// Add new (that have not already been replaced)
for _ , user := range idMap {
team . Users = append ( team . Users , user )
}
2021-08-02 22:06:27 +00:00
logging . WithExtras ( ctx , "users" , team . Users )
2021-09-14 12:11:07 +00:00
return svc . ds . SaveTeam ( ctx , team )
2021-06-01 00:07:51 +00:00
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) DeleteTeamUsers ( ctx context . Context , teamID uint , users [ ] fleet . TeamUser ) ( * fleet . Team , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionWrite ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-06-01 00:07:51 +00:00
idMap := make ( map [ uint ] bool )
for _ , user := range users {
idMap [ user . ID ] = true
}
2021-09-14 12:11:07 +00:00
team , err := svc . ds . Team ( ctx , teamID )
2021-06-01 00:07:51 +00:00
if err != nil {
return nil , err
}
2021-06-06 22:07:29 +00:00
newUsers := [ ] fleet . TeamUser { }
2021-06-01 00:07:51 +00:00
// Delete existing
for _ , existingUser := range team . Users {
if _ , ok := idMap [ existingUser . ID ] ; ! ok {
// Only add non-deleted users
newUsers = append ( newUsers , existingUser )
}
}
team . Users = newUsers
2021-08-02 22:06:27 +00:00
logging . WithExtras ( ctx , "users" , team . Users )
2021-09-14 12:11:07 +00:00
return svc . ds . SaveTeam ( ctx , team )
2021-06-01 00:07:51 +00:00
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) ListTeamUsers ( ctx context . Context , teamID uint , opt fleet . ListOptions ) ( [ ] * fleet . User , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionRead ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-09-14 12:11:07 +00:00
team , err := svc . ds . Team ( ctx , teamID )
2021-06-01 00:07:51 +00:00
if err != nil {
return nil , err
}
2021-09-14 12:11:07 +00:00
return svc . ds . ListUsers ( ctx , fleet . UserListOptions { ListOptions : opt , TeamID : team . ID } )
2021-06-01 00:07:51 +00:00
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) ListTeams ( ctx context . Context , opt fleet . ListOptions ) ( [ ] * fleet . Team , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { } , fleet . ActionRead ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2021-06-01 00:07:51 +00:00
vc , ok := viewer . FromContext ( ctx )
if ! ok {
2021-06-06 22:07:29 +00:00
return nil , fleet . ErrNoContext
2021-06-01 00:07:51 +00:00
}
2021-06-06 22:07:29 +00:00
filter := fleet . TeamFilter { User : vc . User , IncludeObserver : true }
2021-06-01 00:07:51 +00:00
2023-06-08 21:30:34 +00:00
teams , err := svc . ds . ListTeams ( ctx , filter , opt )
if err != nil {
return nil , err
}
if err = obfuscateSecrets ( vc . User , teams ) ; err != nil {
return nil , err
}
return teams , nil
2021-06-01 00:07:51 +00:00
}
2022-01-13 19:57:44 +00:00
func ( svc * Service ) ListAvailableTeamsForUser ( ctx context . Context , user * fleet . User ) ( [ ] * fleet . TeamSummary , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { } , fleet . ActionRead ) ; err != nil {
return nil , err
}
availableTeams := [ ] * fleet . TeamSummary { }
if user . GlobalRole != nil {
ts , err := svc . ds . TeamsSummary ( ctx )
if err != nil {
return nil , err
}
availableTeams = append ( availableTeams , ts ... )
} else {
for _ , t := range user . Teams {
// Convert from UserTeam to TeamSummary (i.e. omit the role, counts, agent options)
availableTeams = append ( availableTeams , & fleet . TeamSummary { ID : t . ID , Name : t . Name , Description : t . Description } )
}
}
return availableTeams , nil
}
2021-06-03 23:24:15 +00:00
func ( svc * Service ) DeleteTeam ( ctx context . Context , teamID uint ) error {
2021-06-06 22:07:29 +00:00
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionWrite ) ; err != nil {
2021-06-03 23:24:15 +00:00
return err
}
2021-09-14 12:11:07 +00:00
team , err := svc . ds . Team ( ctx , teamID )
2021-07-23 20:00:26 +00:00
if err != nil {
return err
}
name := team . Name
2023-04-09 02:23:36 +00:00
vc , ok := viewer . FromContext ( ctx )
if ! ok {
return fleet . ErrNoContext
}
2023-05-17 19:49:02 +00:00
2023-04-09 02:23:36 +00:00
filter := fleet . TeamFilter { User : vc . User , IncludeObserver : true }
hosts , err := svc . ds . ListHosts ( ctx , filter , fleet . HostListOptions { TeamFilter : & teamID } )
if err != nil {
return ctxerr . Wrap ( ctx , err , "list hosts for reconcile profiles on team change" )
}
hostIDs := make ( [ ] uint , 0 , len ( hosts ) )
2023-05-15 18:06:09 +00:00
mdmHostSerials := make ( [ ] string , 0 , len ( hosts ) )
2023-04-09 02:23:36 +00:00
for _ , host := range hosts {
hostIDs = append ( hostIDs , host . ID )
2023-05-15 18:06:09 +00:00
if host . MDMInfo . IsPendingDEPFleetEnrollment ( ) || host . MDMInfo . IsDEPFleetEnrolled ( ) {
mdmHostSerials = append ( mdmHostSerials , host . HardwareSerial )
}
2023-04-09 02:23:36 +00:00
}
2021-09-14 12:11:07 +00:00
if err := svc . ds . DeleteTeam ( ctx , teamID ) ; err != nil {
2021-07-13 19:54:22 +00:00
return err
}
2023-05-17 19:49:02 +00:00
if len ( hostIDs ) > 0 {
if err := svc . ds . BulkSetPendingMDMAppleHostProfiles ( ctx , hostIDs , nil , nil , nil ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "bulk set pending host profiles" )
}
2023-04-09 02:23:36 +00:00
2023-05-17 19:49:02 +00:00
if err := svc . ds . CleanupDiskEncryptionKeysOnTeamChange ( ctx , hostIDs , ptr . Uint ( 0 ) ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "reconcile profiles on team change cleanup disk encryption keys" )
}
if len ( mdmHostSerials ) > 0 {
if err := worker . QueueMacosSetupAssistantJob (
ctx ,
svc . ds ,
svc . logger ,
worker . MacosSetupAssistantTeamDeleted ,
nil ,
mdmHostSerials ... ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "queue macos setup assistant team deleted job" )
}
2023-05-15 18:06:09 +00:00
}
}
2021-08-02 22:06:27 +00:00
logging . WithExtras ( ctx , "id" , teamID )
2022-12-23 22:04:13 +00:00
if err := svc . ds . NewActivity (
2021-09-14 12:11:07 +00:00
ctx ,
2021-07-13 19:54:22 +00:00
authz . UserFromContext ( ctx ) ,
2022-12-23 16:05:16 +00:00
fleet . ActivityTypeDeletedTeam {
ID : teamID ,
Name : name ,
} ,
2022-12-23 22:04:13 +00:00
) ; err != nil {
return ctxerr . Wrap ( ctx , err , "create activity for team deletion" )
}
return nil
2021-06-01 00:07:51 +00:00
}
2022-02-04 17:33:22 +00:00
func ( svc * Service ) GetTeam ( ctx context . Context , teamID uint ) ( * fleet . Team , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionRead ) ; err != nil {
return nil , err
}
logging . WithExtras ( ctx , "id" , teamID )
2023-06-08 21:30:34 +00:00
vc , ok := viewer . FromContext ( ctx )
if ! ok {
return nil , fleet . ErrNoContext
}
team , err := svc . ds . Team ( ctx , teamID )
if err != nil {
return nil , err
}
if err = obfuscateSecrets ( vc . User , [ ] * fleet . Team { team } ) ; err != nil {
return nil , err
}
return team , nil
2022-02-04 17:33:22 +00:00
}
2021-06-06 22:07:29 +00:00
func ( svc * Service ) TeamEnrollSecrets ( ctx context . Context , teamID uint ) ( [ ] * fleet . EnrollSecret , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . Team { ID : teamID } , fleet . ActionRead ) ; err != nil {
2021-06-03 23:24:15 +00:00
return nil , err
}
2023-06-08 21:30:34 +00:00
vc , ok := viewer . FromContext ( ctx )
if ! ok {
return nil , fleet . ErrNoContext
}
secrets , err := svc . ds . TeamEnrollSecrets ( ctx , teamID )
if err != nil {
return nil , err
}
isGlobalObs := vc . User . IsGlobalObserver ( )
teamMemberships := vc . User . TeamMembership ( func ( t fleet . UserTeam ) bool {
return true
} )
obsMembership := vc . User . TeamMembership ( func ( t fleet . UserTeam ) bool {
return t . Role == fleet . RoleObserver || t . Role == fleet . RoleObserverPlus
} )
for _ , s := range secrets {
if s == nil {
continue
}
if isGlobalObs || vc . User . GlobalRole == nil && ( ! teamMemberships [ * s . TeamID ] || obsMembership [ * s . TeamID ] ) {
s . Secret = fleet . MaskedPassword
}
}
return secrets , nil
2021-06-01 00:07:51 +00:00
}
2021-11-11 16:45:39 +00:00
func ( svc * Service ) ModifyTeamEnrollSecrets ( ctx context . Context , teamID uint , secrets [ ] fleet . EnrollSecret ) ( [ ] * fleet . EnrollSecret , error ) {
if err := svc . authz . Authorize ( ctx , & fleet . EnrollSecret { TeamID : ptr . Uint ( teamID ) } , fleet . ActionWrite ) ; err != nil {
return nil , err
}
2021-11-17 16:26:24 +00:00
if secrets == nil {
return nil , fleet . NewInvalidArgumentError ( "secrets" , "missing required argument" )
2021-11-11 16:45:39 +00:00
}
2022-10-05 12:35:36 +00:00
if len ( secrets ) > fleet . MaxEnrollSecretsCount {
return nil , fleet . NewInvalidArgumentError ( "secrets" , "too many secrets" )
}
2021-11-11 16:45:39 +00:00
var newSecrets [ ] * fleet . EnrollSecret
for _ , secret := range secrets {
newSecrets = append ( newSecrets , & fleet . EnrollSecret {
Secret : secret . Secret ,
} )
}
if err := svc . ds . ApplyEnrollSecrets ( ctx , ptr . Uint ( teamID ) , newSecrets ) ; err != nil {
return nil , err
}
return newSecrets , nil
}
2022-02-03 19:24:03 +00:00
2023-02-16 16:53:26 +00:00
func ( svc * Service ) teamByIDOrName ( ctx context . Context , id * uint , name * string ) ( * fleet . Team , error ) {
2023-02-15 18:01:44 +00:00
if err := svc . authz . Authorize ( ctx , & fleet . Team { } , fleet . ActionRead ) ; err != nil {
return nil , err
}
2023-02-16 16:53:26 +00:00
var (
tm * fleet . Team
err error
)
if id != nil {
tm , err = svc . ds . Team ( ctx , * id )
if err != nil {
return nil , err
}
} else if name != nil {
tm , err = svc . ds . TeamByName ( ctx , * name )
if err != nil {
return nil , err
2023-02-15 18:01:44 +00:00
}
}
return tm , nil
}
2022-11-01 19:22:45 +00:00
var jsonNull = json . RawMessage ( ` null ` )
2023-04-12 19:11:04 +00:00
// setAuthCheckedOnPreAuthErr can be used to set the authentication as checked
// in case of errors that happened before an auth check can be performed.
// Otherwise the endpoints return a "authentication skipped" error instead of
// the actual returned error.
func setAuthCheckedOnPreAuthErr ( ctx context . Context ) {
if az , ok := authz_ctx . FromContext ( ctx ) ; ok {
az . SetChecked ( )
2022-02-03 19:24:03 +00:00
}
2023-04-12 19:11:04 +00:00
}
2022-02-03 19:24:03 +00:00
2023-04-12 19:11:04 +00:00
func ( svc * Service ) checkAuthorizationForTeams ( ctx context . Context , specs [ ] * fleet . TeamSpec ) error {
2022-02-03 19:24:03 +00:00
for _ , spec := range specs {
team , err := svc . ds . TeamByName ( ctx , spec . Name )
if err != nil {
2023-07-13 14:55:05 +00:00
if fleet . IsNotFound ( err ) {
2023-04-12 19:11:04 +00:00
// Can the user create a new team?
2022-02-03 19:24:03 +00:00
if err := svc . authz . Authorize ( ctx , & fleet . Team { } , fleet . ActionWrite ) ; err != nil {
return err
}
continue
}
2023-04-12 19:11:04 +00:00
// Set authorization as checked to return a proper error.
setAuthCheckedOnPreAuthErr ( ctx )
2022-02-03 19:24:03 +00:00
return err
}
// can the user modify each team it's trying to modify
if err := svc . authz . Authorize ( ctx , team , fleet . ActionWrite ) ; err != nil {
return err
}
}
2023-04-12 19:11:04 +00:00
return nil
}
2023-06-07 17:29:36 +00:00
func ( svc * Service ) ApplyTeamSpecs ( ctx context . Context , specs [ ] * fleet . TeamSpec , applyOpts fleet . ApplySpecOptions ) ( map [ string ] uint , error ) {
2023-04-12 19:11:04 +00:00
if len ( specs ) == 0 {
setAuthCheckedOnPreAuthErr ( ctx )
// Nothing to do.
2023-06-07 17:29:36 +00:00
return map [ string ] uint { } , nil
2023-04-12 19:11:04 +00:00
}
if err := svc . checkAuthorizationForTeams ( ctx , specs ) ; err != nil {
2023-06-07 17:29:36 +00:00
return nil , err
2023-04-12 19:11:04 +00:00
}
2022-02-03 19:24:03 +00:00
2023-04-12 19:11:04 +00:00
appConfig , err := svc . ds . AppConfig ( ctx )
2022-02-03 19:24:03 +00:00
if err != nil {
2023-06-07 17:29:36 +00:00
return nil , err
2022-02-03 19:24:03 +00:00
}
2023-04-12 19:11:04 +00:00
appConfig . Obfuscate ( )
2022-02-03 19:24:03 +00:00
2022-12-23 16:05:16 +00:00
var details [ ] fleet . TeamActivityDetail
2022-08-24 12:32:45 +00:00
2022-02-03 19:24:03 +00:00
for _ , spec := range specs {
var secrets [ ] * fleet . EnrollSecret
for _ , secret := range spec . Secrets {
secrets = append ( secrets , & fleet . EnrollSecret {
Secret : secret . Secret ,
} )
}
2022-09-19 17:53:44 +00:00
var create bool
2022-02-03 19:24:03 +00:00
team , err := svc . ds . TeamByName ( ctx , spec . Name )
2022-08-31 20:30:46 +00:00
switch {
case err == nil :
// OK
2023-07-13 14:55:05 +00:00
case fleet . IsNotFound ( err ) :
2022-09-19 17:53:44 +00:00
if spec . Name == "" {
2023-06-07 17:29:36 +00:00
return nil , fleet . NewInvalidArgumentError ( "name" , "name may not be empty" )
2022-09-19 17:53:44 +00:00
}
create = true
default :
2023-06-07 17:29:36 +00:00
return nil , err
2022-09-19 17:53:44 +00:00
}
2022-11-01 19:22:45 +00:00
if len ( spec . AgentOptions ) > 0 && ! bytes . Equal ( spec . AgentOptions , jsonNull ) {
if err := fleet . ValidateJSONAgentOptions ( spec . AgentOptions ) ; err != nil {
2022-10-12 21:10:50 +00:00
err = fleet . NewUserMessageError ( err , http . StatusBadRequest )
2022-09-19 17:53:44 +00:00
if applyOpts . Force && ! applyOpts . DryRun {
level . Info ( svc . logger ) . Log ( "err" , err , "msg" , "force-apply team agent options with validation errors" )
}
if ! applyOpts . Force {
2023-06-07 17:29:36 +00:00
return nil , ctxerr . Wrap ( ctx , err , "validate agent options" )
2022-09-19 17:53:44 +00:00
}
}
}
2022-10-05 12:35:36 +00:00
if len ( spec . Secrets ) > fleet . MaxEnrollSecretsCount {
2023-06-07 17:29:36 +00:00
return nil , ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "secrets" , "too many secrets" ) , "validate secrets" )
2022-10-05 12:35:36 +00:00
}
2023-01-24 16:20:02 +00:00
if err := spec . MDM . MacOSUpdates . Validate ( ) ; err != nil {
2023-06-07 17:29:36 +00:00
return nil , ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_updates" , err . Error ( ) ) )
2023-01-24 16:20:02 +00:00
}
2022-09-19 17:53:44 +00:00
if create {
2023-02-15 18:01:44 +00:00
team , err := svc . createTeamFromSpec ( ctx , spec , appConfig , secrets , applyOpts . DryRun )
2022-08-31 20:30:46 +00:00
if err != nil {
2023-06-07 17:29:36 +00:00
return nil , ctxerr . Wrap ( ctx , err , "creating team from spec" )
2022-02-03 19:24:03 +00:00
}
2022-12-23 16:05:16 +00:00
details = append ( details , fleet . TeamActivityDetail {
2022-08-31 20:30:46 +00:00
ID : team . ID ,
Name : team . Name ,
} )
continue
2022-02-03 19:24:03 +00:00
}
2023-04-25 13:36:01 +00:00
if err := svc . editTeamFromSpec ( ctx , team , spec , appConfig , secrets , applyOpts . DryRun ) ; err != nil {
2023-06-07 17:29:36 +00:00
return nil , ctxerr . Wrap ( ctx , err , "editing team from spec" )
2022-02-03 19:24:03 +00:00
}
2022-08-24 12:32:45 +00:00
2022-12-23 16:05:16 +00:00
details = append ( details , fleet . TeamActivityDetail {
2022-08-24 12:32:45 +00:00
ID : team . ID ,
Name : team . Name ,
} )
2022-02-03 19:24:03 +00:00
}
2023-02-15 18:01:44 +00:00
if applyOpts . DryRun {
2023-06-07 17:29:36 +00:00
return nil , nil
2023-02-15 18:01:44 +00:00
}
2023-06-07 17:29:36 +00:00
idsByName := make ( map [ string ] uint , len ( details ) )
2022-09-19 17:53:44 +00:00
if len ( details ) > 0 {
2023-06-07 17:29:36 +00:00
for _ , tm := range details {
idsByName [ tm . Name ] = tm . ID
}
2022-09-19 17:53:44 +00:00
if err := svc . ds . NewActivity (
ctx ,
authz . UserFromContext ( ctx ) ,
2022-12-23 16:05:16 +00:00
fleet . ActivityTypeAppliedSpecTeam {
Teams : details ,
} ,
2022-09-19 17:53:44 +00:00
) ; err != nil {
2023-06-07 17:29:36 +00:00
return nil , ctxerr . Wrap ( ctx , err , "create activity for team spec" )
2022-09-19 17:53:44 +00:00
}
2022-08-24 12:32:45 +00:00
}
2023-06-07 17:29:36 +00:00
return idsByName , nil
2022-02-03 19:24:03 +00:00
}
2022-08-30 11:13:09 +00:00
2023-02-28 20:34:46 +00:00
func ( svc * Service ) createTeamFromSpec (
ctx context . Context ,
spec * fleet . TeamSpec ,
defaults * fleet . AppConfig ,
secrets [ ] * fleet . EnrollSecret ,
dryRun bool ,
) ( * fleet . Team , error ) {
2022-11-01 19:22:45 +00:00
agentOptions := & spec . AgentOptions
if len ( spec . AgentOptions ) == 0 {
2022-08-30 11:13:09 +00:00
agentOptions = defaults . AgentOptions
}
2022-08-31 20:30:46 +00:00
// if a team spec is not provided, use the global features, otherwise
// build a new config from the spec with default values applied.
var err error
features := defaults . Features
if spec . Features != nil {
features , err = unmarshalWithGlobalDefaults ( spec . Features )
if err != nil {
return nil , err
}
2022-08-30 11:13:09 +00:00
}
2023-02-15 18:01:44 +00:00
var macOSSettings fleet . MacOSSettings
2023-02-28 20:34:46 +00:00
if err := svc . applyTeamMacOSSettings ( ctx , spec , & macOSSettings ) ; err != nil {
return nil , err
2023-02-15 18:01:44 +00:00
}
2023-04-25 13:36:01 +00:00
macOSSetup := spec . MDM . MacOSSetup
2023-04-26 21:09:21 +00:00
if macOSSetup . MacOSSetupAssistant . Set || macOSSetup . BootstrapPackage . Set {
2023-04-25 13:36:01 +00:00
if ! defaults . MDM . EnabledAndConfigured {
2023-04-26 21:09:21 +00:00
return nil , ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_setup" ,
` Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features. ` ) )
2023-04-25 13:36:01 +00:00
}
}
2023-02-15 18:01:44 +00:00
if dryRun {
return & fleet . Team { Name : spec . Name } , nil
}
2023-03-08 13:31:53 +00:00
tm , err := svc . ds . NewTeam ( ctx , & fleet . Team {
2022-08-30 11:13:09 +00:00
Name : spec . Name ,
Config : fleet . TeamConfig {
2023-02-28 20:34:46 +00:00
AgentOptions : agentOptions ,
Features : features ,
MDM : fleet . TeamMDM {
MacOSUpdates : spec . MDM . MacOSUpdates ,
MacOSSettings : macOSSettings ,
2023-04-25 13:36:01 +00:00
MacOSSetup : macOSSetup ,
2023-02-28 20:34:46 +00:00
} ,
2022-08-30 11:13:09 +00:00
} ,
Secrets : secrets ,
} )
2023-03-08 13:31:53 +00:00
if err != nil {
return nil , err
}
if macOSSettings . EnableDiskEncryption {
if err := svc . MDMAppleEnableFileVaultAndEscrow ( ctx , & tm . ID ) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "enable team filevault and escrow" )
}
if err := svc . ds . NewActivity (
ctx ,
authz . UserFromContext ( ctx ) ,
fleet . ActivityTypeEnabledMacosDiskEncryption { TeamID : & tm . ID , TeamName : & tm . Name } ,
) ; err != nil {
return nil , ctxerr . Wrap ( ctx , err , "create activity for team macos disk encryption" )
}
}
return tm , nil
2022-08-30 11:13:09 +00:00
}
2023-02-28 20:34:46 +00:00
func ( svc * Service ) editTeamFromSpec (
ctx context . Context ,
team * fleet . Team ,
spec * fleet . TeamSpec ,
2023-04-25 13:36:01 +00:00
appCfg * fleet . AppConfig ,
2023-02-28 20:34:46 +00:00
secrets [ ] * fleet . EnrollSecret ,
dryRun bool ,
) error {
2022-08-30 11:13:09 +00:00
team . Name = spec . Name
2022-11-01 19:22:45 +00:00
// if agent options are not provided, do not change them
if len ( spec . AgentOptions ) > 0 {
if bytes . Equal ( spec . AgentOptions , jsonNull ) {
// agent options provided but null, clear existing agent option
team . Config . AgentOptions = nil
} else {
team . Config . AgentOptions = & spec . AgentOptions
}
}
2022-08-31 20:30:46 +00:00
// replace (don't merge) the features with the new ones, using a config
// that has the global defaults applied.
features , err := unmarshalWithGlobalDefaults ( spec . Features )
if err != nil {
return err
2022-08-30 11:13:09 +00:00
}
2022-08-31 20:30:46 +00:00
team . Config . Features = features
2023-06-06 18:31:33 +00:00
if spec . MDM . MacOSUpdates . Deadline . Set || spec . MDM . MacOSUpdates . MinimumVersion . Set {
team . Config . MDM . MacOSUpdates = spec . MDM . MacOSUpdates
}
2023-02-28 20:34:46 +00:00
2023-03-08 13:31:53 +00:00
oldMacOSDiskEncryption := team . Config . MDM . MacOSSettings . EnableDiskEncryption
2023-02-28 20:34:46 +00:00
if err := svc . applyTeamMacOSSettings ( ctx , spec , & team . Config . MDM . MacOSSettings ) ; err != nil {
return err
2023-02-15 18:01:44 +00:00
}
2023-03-08 13:31:53 +00:00
newMacOSDiskEncryption := team . Config . MDM . MacOSSettings . EnableDiskEncryption
2022-08-31 20:30:46 +00:00
2023-04-25 13:36:01 +00:00
oldMacOSSetup := team . Config . MDM . MacOSSetup
2023-04-26 21:09:21 +00:00
if spec . MDM . MacOSSetup . MacOSSetupAssistant . Set || spec . MDM . MacOSSetup . BootstrapPackage . Set {
2023-04-25 13:36:01 +00:00
if ! appCfg . MDM . EnabledAndConfigured {
2023-04-26 21:09:21 +00:00
return ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_setup" ,
` Couldn't update macos_setup because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features. ` ) )
}
if spec . MDM . MacOSSetup . MacOSSetupAssistant . Set {
team . Config . MDM . MacOSSetup . MacOSSetupAssistant = spec . MDM . MacOSSetup . MacOSSetupAssistant
}
if spec . MDM . MacOSSetup . BootstrapPackage . Set {
team . Config . MDM . MacOSSetup . BootstrapPackage = spec . MDM . MacOSSetup . BootstrapPackage
2023-04-25 13:36:01 +00:00
}
}
2023-05-10 20:22:08 +00:00
var didUpdateMacOSEndUserAuth bool
if spec . MDM . MacOSSetup . EnableEndUserAuthentication != oldMacOSSetup . EnableEndUserAuthentication {
if ! appCfg . MDM . EnabledAndConfigured {
return ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_setup.enable_end_user_authentication" ,
` Couldn't update macos_setup.enable_end_user_authentication because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features. ` ) )
}
if spec . MDM . MacOSSetup . EnableEndUserAuthentication && appCfg . MDM . EndUserAuthentication . IsEmpty ( ) {
// TODO: update this error message to include steps to resolve the issue once docs for IdP
// config are available
return ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( "macos_setup.enable_end_user_authentication" ,
` Couldn't enable macos_setup.enable_end_user_authentication because no IdP is configured for MDM features. ` ) )
}
didUpdateMacOSEndUserAuth = true
}
team . Config . MDM . MacOSSetup . EnableEndUserAuthentication = spec . MDM . MacOSSetup . EnableEndUserAuthentication
2022-08-30 11:13:09 +00:00
if len ( secrets ) > 0 {
team . Secrets = secrets
}
2023-02-15 18:01:44 +00:00
if dryRun {
return nil
}
2022-08-30 11:13:09 +00:00
if _ , err := svc . ds . SaveTeam ( ctx , team ) ; err != nil {
return err
}
// only replace enroll secrets if at least one is provided (#6774)
if len ( secrets ) > 0 {
if err := svc . ds . ApplyEnrollSecrets ( ctx , ptr . Uint ( team . ID ) , secrets ) ; err != nil {
return err
}
}
2023-03-08 13:31:53 +00:00
if oldMacOSDiskEncryption != newMacOSDiskEncryption {
var act fleet . ActivityDetails
if team . Config . MDM . MacOSSettings . EnableDiskEncryption {
act = fleet . ActivityTypeEnabledMacosDiskEncryption { TeamID : & team . ID , TeamName : & team . Name }
if err := svc . MDMAppleEnableFileVaultAndEscrow ( ctx , & team . ID ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "enable team filevault and escrow" )
}
} else {
act = fleet . ActivityTypeDisabledMacosDiskEncryption { TeamID : & team . ID , TeamName : & team . Name }
if err := svc . MDMAppleDisableFileVaultAndEscrow ( ctx , & team . ID ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "disable team filevault and escrow" )
}
}
if err := svc . ds . NewActivity ( ctx , authz . UserFromContext ( ctx ) , act ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "create activity for team macos disk encryption" )
}
}
2023-04-25 13:36:01 +00:00
// if the macos setup assistant was cleared, remove it for that team
if spec . MDM . MacOSSetup . MacOSSetupAssistant . Set &&
spec . MDM . MacOSSetup . MacOSSetupAssistant . Value == "" &&
oldMacOSSetup . MacOSSetupAssistant . Value != "" {
if err := svc . DeleteMDMAppleSetupAssistant ( ctx , & team . ID ) ; err != nil {
return ctxerr . Wrapf ( ctx , err , "clear macos setup assistant for team %d" , team . ID )
}
}
2023-04-26 21:09:21 +00:00
// if the bootstrap package was cleared, remove it for that team
if spec . MDM . MacOSSetup . BootstrapPackage . Set &&
spec . MDM . MacOSSetup . BootstrapPackage . Value == "" &&
oldMacOSSetup . BootstrapPackage . Value != "" {
if err := svc . DeleteMDMAppleBootstrapPackage ( ctx , & team . ID ) ; err != nil {
return ctxerr . Wrapf ( ctx , err , "clear bootstrap package for team %d" , team . ID )
}
}
2023-05-10 20:22:08 +00:00
if didUpdateMacOSEndUserAuth {
if err := svc . updateMacOSSetupEnableEndUserAuth ( ctx , spec . MDM . MacOSSetup . EnableEndUserAuthentication , & team . ID , & team . Name ) ; err != nil {
return err
}
}
2022-08-30 11:13:09 +00:00
return nil
}
2022-08-31 20:30:46 +00:00
2023-02-28 20:34:46 +00:00
func ( svc * Service ) applyTeamMacOSSettings ( ctx context . Context , spec * fleet . TeamSpec , applyUpon * fleet . MacOSSettings ) error {
setFields , err := applyUpon . FromMap ( spec . MDM . MacOSSettings )
if err != nil {
return fleet . NewUserMessageError ( err , http . StatusBadRequest )
}
2023-03-27 19:30:29 +00:00
appCfg , err := svc . ds . AppConfig ( ctx )
if err != nil {
return ctxerr . Wrap ( ctx , err , "apply team macos settings" )
}
2023-02-28 20:34:46 +00:00
if ( setFields [ "custom_settings" ] && len ( applyUpon . CustomSettings ) > 0 ) ||
( setFields [ "enable_disk_encryption" ] && applyUpon . EnableDiskEncryption ) {
field := "custom_settings"
if ! setFields [ "custom_settings" ] {
field = "enable_disk_encryption"
}
2023-03-27 19:30:29 +00:00
if ! appCfg . MDM . EnabledAndConfigured {
2023-02-28 20:34:46 +00:00
return ctxerr . Wrap ( ctx , fleet . NewInvalidArgumentError ( fmt . Sprintf ( "macos_settings.%s" , field ) ,
` Couldn't update macos_settings because MDM features aren't turned on in Fleet. Use fleetctl generate mdm-apple and then fleet serve with mdm configuration to turn on MDM features. ` ) )
}
}
return nil
}
2022-08-31 20:30:46 +00:00
// unmarshalWithGlobalDefaults unmarshals features from a team spec, and
// assigns default values based on the global defaults for missing fields
func unmarshalWithGlobalDefaults ( b * json . RawMessage ) ( fleet . Features , error ) {
// build a default config with default values applied
defaults := & fleet . Features { }
defaults . ApplyDefaultsForNewInstalls ( )
// unmarshal the features from the spec into the defaults
if b != nil {
if err := json . Unmarshal ( * b , defaults ) ; err != nil {
return fleet . Features { } , err
}
}
return * defaults , nil
}
2023-03-06 14:54:51 +00:00
func ( svc * Service ) updateTeamMDMAppleSettings ( ctx context . Context , tm * fleet . Team , payload fleet . MDMAppleSettingsPayload ) error {
2023-03-08 13:31:53 +00:00
var didUpdate , didUpdateMacOSDiskEncryption bool
2023-03-06 14:54:51 +00:00
if payload . EnableDiskEncryption != nil {
if tm . Config . MDM . MacOSSettings . EnableDiskEncryption != * payload . EnableDiskEncryption {
tm . Config . MDM . MacOSSettings . EnableDiskEncryption = * payload . EnableDiskEncryption
didUpdate = true
2023-03-08 13:31:53 +00:00
didUpdateMacOSDiskEncryption = true
2023-03-06 14:54:51 +00:00
}
}
if didUpdate {
if _ , err := svc . ds . SaveTeam ( ctx , tm ) ; err != nil {
return err
}
2023-03-08 13:31:53 +00:00
if didUpdateMacOSDiskEncryption {
var act fleet . ActivityDetails
if tm . Config . MDM . MacOSSettings . EnableDiskEncryption {
act = fleet . ActivityTypeEnabledMacosDiskEncryption { TeamID : & tm . ID , TeamName : & tm . Name }
if err := svc . MDMAppleEnableFileVaultAndEscrow ( ctx , & tm . ID ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "enable team filevault and escrow" )
}
} else {
act = fleet . ActivityTypeDisabledMacosDiskEncryption { TeamID : & tm . ID , TeamName : & tm . Name }
if err := svc . MDMAppleDisableFileVaultAndEscrow ( ctx , & tm . ID ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "disable team filevault and escrow" )
}
}
if err := svc . ds . NewActivity ( ctx , authz . UserFromContext ( ctx ) , act ) ; err != nil {
return ctxerr . Wrap ( ctx , err , "create activity for team macos disk encryption" )
}
}
2023-03-06 14:54:51 +00:00
}
return nil
}
2023-05-10 20:22:08 +00:00
func ( svc * Service ) updateTeamMDMAppleSetup ( ctx context . Context , tm * fleet . Team , payload fleet . MDMAppleSetupPayload ) error {
var didUpdate , didUpdateMacOSEndUserAuth bool
if payload . EnableEndUserAuthentication != nil {
if tm . Config . MDM . MacOSSetup . EnableEndUserAuthentication != * payload . EnableEndUserAuthentication {
tm . Config . MDM . MacOSSetup . EnableEndUserAuthentication = * payload . EnableEndUserAuthentication
didUpdate = true
didUpdateMacOSEndUserAuth = true
}
}
if didUpdate {
if _ , err := svc . ds . SaveTeam ( ctx , tm ) ; err != nil {
return err
}
if didUpdateMacOSEndUserAuth {
if err := svc . updateMacOSSetupEnableEndUserAuth ( ctx , tm . Config . MDM . MacOSSetup . EnableEndUserAuthentication , & tm . ID , & tm . Name ) ; err != nil {
return err
}
}
}
return nil
}