Allow end user authentication during automatic MDM enrollment to be enabled on a per-team basis (#11566)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
- Added `PATCH /mdm/apple/setup` endpoint.
|
||||
- Added `enable_end_user_authentication` to `mdm.macos_setup` in global app config and team config
|
||||
objects.
|
||||
@@ -1258,6 +1258,14 @@ kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_setup:
|
||||
`
|
||||
appConfigSpecEnableEndUserAuth = `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_setup:
|
||||
enable_end_user_authentication: %s
|
||||
`
|
||||
team1Spec = `
|
||||
apiVersion: v1
|
||||
@@ -1299,6 +1307,16 @@ spec:
|
||||
macos_setup:
|
||||
bootstrap_package: %s
|
||||
macos_setup_assistant: %s
|
||||
`
|
||||
team1SpecEnableEndUserAuth = `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: tm1
|
||||
mdm:
|
||||
macos_setup:
|
||||
enable_end_user_authentication: %s
|
||||
`
|
||||
)
|
||||
|
||||
@@ -1338,6 +1356,23 @@ spec:
|
||||
assert.False(t, ds.InsertMDMAppleBootstrapPackageFuncInvoked)
|
||||
assert.False(t, ds.DeleteMDMAppleBootstrapPackageFuncInvoked)
|
||||
assert.False(t, ds.SaveTeamFuncInvoked)
|
||||
|
||||
// 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)
|
||||
})
|
||||
|
||||
t.Run("setup assistant invalid file, not json, invalid json", func(t *testing.T) {
|
||||
@@ -1711,6 +1746,80 @@ spec:
|
||||
assert.Equal(t, "", mockStore.appConfig.MDM.MacOSSetup.BootstrapPackage.Value)
|
||||
mockStore.Unlock()
|
||||
})
|
||||
|
||||
// // 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
|
||||
// })
|
||||
}
|
||||
|
||||
func TestApplySpecs(t *testing.T) {
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null
|
||||
},
|
||||
"end_user_authentication": {
|
||||
|
||||
@@ -26,6 +26,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant:
|
||||
end_user_authentication:
|
||||
idp_name: ""
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null
|
||||
},
|
||||
"end_user_authentication": {
|
||||
|
||||
@@ -26,6 +26,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant:
|
||||
end_user_authentication:
|
||||
idp_name: ""
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null
|
||||
}
|
||||
},
|
||||
@@ -93,6 +94,7 @@
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": null,
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant:
|
||||
name: team1
|
||||
---
|
||||
@@ -43,5 +44,6 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package:
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant:
|
||||
name: team2
|
||||
|
||||
@@ -23,6 +23,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package: null
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: null
|
||||
macos_updates:
|
||||
deadline: ""
|
||||
|
||||
@@ -23,6 +23,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package: %s
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: %s
|
||||
macos_updates:
|
||||
deadline: ""
|
||||
|
||||
@@ -12,6 +12,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package: null
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: null
|
||||
macos_updates:
|
||||
deadline: ""
|
||||
|
||||
@@ -12,6 +12,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package: %s
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: %s
|
||||
macos_updates:
|
||||
deadline: ""
|
||||
|
||||
@@ -12,6 +12,7 @@ spec:
|
||||
enable_disk_encryption: false
|
||||
macos_setup:
|
||||
bootstrap_package: null
|
||||
enable_end_user_authentication: false
|
||||
macos_setup_assistant: null
|
||||
macos_updates:
|
||||
deadline: ""
|
||||
|
||||
@@ -769,6 +769,40 @@ This activity contains the following fields:
|
||||
}
|
||||
```
|
||||
|
||||
### Type `enabled_macos_setup_end_user_auth`
|
||||
|
||||
Generated when a user turns on end user authentication for macOS hosts that automatically enroll to a team (or no team).
|
||||
|
||||
This activity contains the following fields:
|
||||
- "team_id": The ID of the team that end user authentication applies to, null if it applies to devices that are not in a team.
|
||||
- "team_name": The name of the team that end user authentication applies to, null if it applies to devices that are not in a team.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"team_id": 123,
|
||||
"team_name": "Workstations"
|
||||
}
|
||||
```
|
||||
|
||||
### Type `disabled_macos_setup_end_user_auth`
|
||||
|
||||
Generated when a user turns off end user authentication for macOS hosts that automatically enroll to a team (or no team).
|
||||
|
||||
This activity contains the following fields:
|
||||
- "team_id": The ID of the team that end user authentication applies to, null if it applies to devices that are not in a team.
|
||||
- "team_name": The name of the team that end user authentication applies to, null if it applies to devices that are not in a team.
|
||||
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"team_id": 123,
|
||||
"team_name": "Workstations"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<meta name="pageOrderInSection" value="1400">
|
||||
@@ -81,6 +81,7 @@ GitOps is an API-only and write-only role that can be used on CI/CD pipelines.
|
||||
| Upload an EULA file for MDM automatic enrollment\* | | | | ✅ | |
|
||||
| View/download MDM macOS setup assistant\* | | | ✅ | ✅ | |
|
||||
| Edit/upload MDM macOS setup assistant\* | | | ✅ | ✅ | |
|
||||
| Enable/disable MDM macOS setup end user authentication\* | | | ✅ | ✅ | |
|
||||
|
||||
\* Applies only to Fleet Premium
|
||||
|
||||
@@ -138,6 +139,7 @@ Users that are members of multiple teams can be assigned different roles for eac
|
||||
| Edit [team MDM settings](https://fleetdm.com/docs/using-fleet/mdm-macos-settings) | | | | ✅ | ✅ |
|
||||
| View/download MDM macOS setup assistant | | | ✅ | ✅ | |
|
||||
| Edit/upload MDM macOS setup assistant | | | ✅ | ✅ | |
|
||||
| Enable/disable MDM macOS setup end user authentication | | | ✅ | ✅ | |
|
||||
|
||||
\* Applies only to [Fleet REST API](https://fleetdm.com/docs/using-fleet/rest-api)
|
||||
|
||||
|
||||
@@ -859,6 +859,7 @@ None.
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": "",
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": "path/to/config.json"
|
||||
}
|
||||
},
|
||||
@@ -935,16 +936,6 @@ None.
|
||||
"integrations": {
|
||||
"jira": null
|
||||
},
|
||||
"mdm": {
|
||||
"apple_bm_terms_expired": false,
|
||||
"apple_bm_enabled_and_configured": false,
|
||||
"enabled_and_configured": false,
|
||||
"apple_bm_default_team": "",
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"debug": false,
|
||||
"json": false,
|
||||
@@ -1047,6 +1038,7 @@ Modifies the Fleet's configuration with the supplied information.
|
||||
| deadline | string | body | _mdm.macos_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past. **Requires Fleet Premium license** |
|
||||
| custom_settings | list | body | _mdm.macos_settings settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have those custom profiles applied. |
|
||||
| enable_disk_encryption | boolean | body | _mdm.macos_settings settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have disk encryption enabled if set to true. **Requires Fleet Premium license** |
|
||||
| enable_end_user_authentication | boolean | body | _mdm.macos_setup settings_. If set to true, end user authentication will be required during automatic MDM enrollment of new macOS devices. Settings for your IdP provider must also be [configured](https://fleetdm.com/docs/using-fleet/mdm-macos-setup#end-user-authentication). **Requires Fleet Premium license** |
|
||||
| additional_queries | boolean | body | Whether or not additional queries are enabled on hosts. |
|
||||
| force | bool | query | Force apply the agent options even if there are validation errors. |
|
||||
| dry_run | bool | query | Validate the configuration and return any validation errors, but do not apply the changes. |
|
||||
@@ -1142,6 +1134,7 @@ Modifies the Fleet's configuration with the supplied information.
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": "",
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": "path/to/config.json"
|
||||
}
|
||||
},
|
||||
@@ -4293,6 +4286,38 @@ The summary can optionally be filtered by team id.
|
||||
}
|
||||
```
|
||||
|
||||
### Turn on end user authentication for macOS setup
|
||||
|
||||
_Available in Fleet Premium_
|
||||
|
||||
`PATCH /api/v1/fleet/mdm/apple/setup`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | In | Description |
|
||||
| ------------- | ------ | ---- | -------------------------------------------------------------------------------------- |
|
||||
| team_id | integer | body | The team ID to apply the settings to. Settings applied to hosts in no team if absent. |
|
||||
| enable_end_user_authentication | boolean | body | Whether end user authentication should be enabled for new macOS devices that automatically enroll to the team (or no team). |
|
||||
|
||||
#### Example
|
||||
|
||||
`PATCH /api/v1/fleet/mdm/apple/setup`
|
||||
|
||||
##### Request body
|
||||
|
||||
```json
|
||||
{
|
||||
"team_id": 1,
|
||||
"enabled_end_user_authentication": true
|
||||
}
|
||||
```
|
||||
|
||||
##### Default response
|
||||
|
||||
`Status: 204`
|
||||
|
||||
|
||||
|
||||
### Upload an EULA file
|
||||
|
||||
_Available in Fleet Premium_
|
||||
@@ -6370,6 +6395,7 @@ _Available in Fleet Premium_
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": "",
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": "path/to/config.json"
|
||||
}
|
||||
}
|
||||
@@ -6482,6 +6508,8 @@ _Available in Fleet Premium_
|
||||
| deadline | string | body | Hosts that belong to this team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past. |
|
||||
| macos_settings | object | body | MacOS-specific settings. |
|
||||
| enable_disk_encryption | boolean | body | Hosts that belong to this team and are enrolled into Fleet's MDM will have disk encryption enabled if set to true. |
|
||||
| macos_setup | object | body | Setup for automatic MDM enrollment of macOS devices. |
|
||||
| enable_end_user_authentication | boolean | body | If set to true, end user authentication will be required during automatic MDM enrollment of new macOS devices. Settings for your IdP provider must also be [configured](https://fleetdm.com/docs/using-fleet/mdm-macos-setup#end-user-authentication). |
|
||||
|
||||
|
||||
#### Example (add users to a team)
|
||||
@@ -6547,6 +6575,7 @@ _Available in Fleet Premium_
|
||||
},
|
||||
"macos_setup": {
|
||||
"bootstrap_package": "",
|
||||
"enable_end_user_authentication": false,
|
||||
"macos_setup_assistant": "path/to/config.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,92 @@ func (svc *Service) MDMAppleDisableFileVaultAndEscrow(ctx context.Context, teamI
|
||||
return ctxerr.Wrap(ctx, err, "disabling FileVault")
|
||||
}
|
||||
|
||||
func (svc *Service) UpdateMDMAppleSetup(ctx context.Context, payload fleet.MDMAppleSetupPayload) error {
|
||||
if err := svc.authz.Authorize(ctx, payload, fleet.ActionWrite); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := svc.validateMDMAppleSetupPayload(ctx, payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if payload.TeamID != nil && *payload.TeamID != 0 {
|
||||
tm, err := svc.teamByIDOrName(ctx, payload.TeamID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return svc.updateTeamMDMAppleSetup(ctx, tm, payload)
|
||||
}
|
||||
return svc.updateAppConfigMDMAppleSetup(ctx, payload)
|
||||
}
|
||||
|
||||
func (svc *Service) updateAppConfigMDMAppleSetup(ctx context.Context, payload fleet.MDMAppleSetupPayload) error {
|
||||
ac, err := svc.AppConfigObfuscated(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var didUpdate, didUpdateMacOSEndUserAuth bool
|
||||
if payload.EnableEndUserAuthentication != nil {
|
||||
if ac.MDM.MacOSSetup.EnableEndUserAuthentication != *payload.EnableEndUserAuthentication {
|
||||
ac.MDM.MacOSSetup.EnableEndUserAuthentication = *payload.EnableEndUserAuthentication
|
||||
didUpdate = true
|
||||
didUpdateMacOSEndUserAuth = true
|
||||
}
|
||||
}
|
||||
|
||||
if didUpdate {
|
||||
if err := svc.ds.SaveAppConfig(ctx, ac); err != nil {
|
||||
return err
|
||||
}
|
||||
if didUpdateMacOSEndUserAuth {
|
||||
if err := svc.updateMacOSSetupEnableEndUserAuth(ctx, ac.MDM.MacOSSetup.EnableEndUserAuthentication, nil, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) updateMacOSSetupEnableEndUserAuth(ctx context.Context, enable bool, teamID *uint, teamName *string) error {
|
||||
// // TODO: Call Apple Business Manager API to define new enrollment profile (depends on
|
||||
// // https://github.com/fleetdm/fleet/issues/10995)
|
||||
// //
|
||||
// // modify the method signature of the syncer to include a team config pointer
|
||||
// // and check enable_end_user_authenthication in the team config if not nil
|
||||
// // otherwise check enable_end_user_authenthication the app config.
|
||||
// if err := svc.mdmAppleSyncDEPProfiles(ctx); err != nil {
|
||||
// return err
|
||||
// }
|
||||
var act fleet.ActivityDetails
|
||||
if enable {
|
||||
act = fleet.ActivityTypeEnabledMacosSetupEndUserAuth{TeamID: teamID, TeamName: teamName}
|
||||
} else {
|
||||
act = fleet.ActivityTypeDisabledMacosSetupEndUserAuth{TeamID: teamID, TeamName: teamName}
|
||||
}
|
||||
if err := svc.ds.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "create activity for macos enable end user auth change")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) validateMDMAppleSetupPayload(ctx context.Context, payload fleet.MDMAppleSetupPayload) error {
|
||||
ac, err := svc.AppConfigObfuscated(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ac.MDM.EnabledAndConfigured {
|
||||
return &fleet.MDMNotConfiguredError{}
|
||||
}
|
||||
if payload.EnableEndUserAuthentication != nil && *payload.EnableEndUserAuthentication == true && ac.MDM.EndUserAuthentication.IsEmpty() {
|
||||
// TODO: update this error message to include steps to resolve the issue once docs for IdP
|
||||
// config are available
|
||||
return fleet.NewInvalidArgumentError("enable_end_user_authentication",
|
||||
`Couldn't enable macos_setup.enable_end_user_authentication because no IdP is configured for MDM features.`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) MDMAppleUploadBootstrapPackage(ctx context.Context, name string, pkg io.Reader, teamID uint) error {
|
||||
if err := svc.authz.Authorize(ctx, &fleet.MDMAppleBootstrapPackage{TeamID: teamID}, fleet.ActionWrite); err != nil {
|
||||
return err
|
||||
@@ -505,7 +591,6 @@ func (svc *Service) InitiateMDMAppleSSO(ctx context.Context) (string, error) {
|
||||
}
|
||||
|
||||
return idpURL, nil
|
||||
|
||||
}
|
||||
|
||||
func (svc *Service) InitiateMDMAppleSSOCallback(ctx context.Context, auth fleet.Auth) (string, error) {
|
||||
|
||||
@@ -110,7 +110,7 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var macOSMinVersionUpdated, macOSDiskEncryptionUpdated bool
|
||||
var macOSMinVersionUpdated, macOSDiskEncryptionUpdated, macOSEnableEndUserAuthUpdated bool
|
||||
if payload.MDM != nil {
|
||||
if payload.MDM.MacOSUpdates != nil {
|
||||
if err := payload.MDM.MacOSUpdates.Validate(); err != nil {
|
||||
@@ -128,6 +128,21 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
|
||||
macOSDiskEncryptionUpdated = team.Config.MDM.MacOSSettings.EnableDiskEncryption != payload.MDM.MacOSSettings.EnableDiskEncryption
|
||||
team.Config.MDM.MacOSSettings.EnableDiskEncryption = payload.MDM.MacOSSettings.EnableDiskEncryption
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if payload.Integrations != nil {
|
||||
@@ -195,6 +210,11 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
|
||||
return nil, ctxerr.Wrap(ctx, err, "create activity for team macos disk encryption")
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
return team, err
|
||||
}
|
||||
|
||||
@@ -755,6 +775,22 @@ func (svc *Service) editTeamFromSpec(
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if len(secrets) > 0 {
|
||||
team.Secrets = secrets
|
||||
}
|
||||
@@ -809,6 +845,12 @@ func (svc *Service) editTeamFromSpec(
|
||||
}
|
||||
}
|
||||
|
||||
if didUpdateMacOSEndUserAuth {
|
||||
if err := svc.updateMacOSSetupEnableEndUserAuth(ctx, spec.MDM.MacOSSetup.EnableEndUserAuthentication, &team.ID, &team.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -889,3 +931,26 @@ func (svc *Service) updateTeamMDMAppleSettings(ctx context.Context, tm *fleet.Te
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ CREATE TABLE `app_config_json` (
|
||||
UNIQUE KEY `id` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null}, \"macos_updates\": {\"deadline\": \"\", \"minimum_version\": \"\"}, \"macos_settings\": {\"custom_settings\": null, \"enable_disk_encryption\": false}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"apple_bm_enabled_and_configured\": false}, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"org_logo_url\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false}, \"macos_updates\": {\"deadline\": \"\", \"minimum_version\": \"\"}, \"macos_settings\": {\"custom_settings\": null, \"enable_disk_encryption\": false}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"apple_bm_enabled_and_configured\": false}, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"org_logo_url\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `carve_blocks` (
|
||||
|
||||
@@ -63,6 +63,9 @@ var ActivityDetailsList = []ActivityDetails{
|
||||
|
||||
ActivityTypeAddedBootstrapPackage{},
|
||||
ActivityTypeDeletedBootstrapPackage{},
|
||||
|
||||
ActivityTypeEnabledMacosSetupEndUserAuth{},
|
||||
ActivityTypeDisabledMacosSetupEndUserAuth{},
|
||||
}
|
||||
|
||||
type ActivityDetails interface {
|
||||
@@ -939,6 +942,44 @@ func (a ActivityTypeDeletedBootstrapPackage) Documentation() (activity, details,
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityTypeEnabledMacosSetupEndUserAuth struct {
|
||||
TeamID *uint `json:"team_id"`
|
||||
TeamName *string `json:"team_name"`
|
||||
}
|
||||
|
||||
func (a ActivityTypeEnabledMacosSetupEndUserAuth) ActivityName() string {
|
||||
return "enabled_macos_setup_end_user_auth"
|
||||
}
|
||||
|
||||
func (a ActivityTypeEnabledMacosSetupEndUserAuth) Documentation() (activity, details, detailsExample string) {
|
||||
return `Generated when a user turns on end user authentication for macOS hosts that automatically enroll to a team (or no team).`,
|
||||
`This activity contains the following fields:
|
||||
- "team_id": The ID of the team that end user authentication applies to, null if it applies to devices that are not in a team.
|
||||
- "team_name": The name of the team that end user authentication applies to, null if it applies to devices that are not in a team.`, `{
|
||||
"team_id": 123,
|
||||
"team_name": "Workstations"
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityTypeDisabledMacosSetupEndUserAuth struct {
|
||||
TeamID *uint `json:"team_id"`
|
||||
TeamName *string `json:"team_name"`
|
||||
}
|
||||
|
||||
func (a ActivityTypeDisabledMacosSetupEndUserAuth) ActivityName() string {
|
||||
return "disabled_macos_setup_end_user_auth"
|
||||
}
|
||||
|
||||
func (a ActivityTypeDisabledMacosSetupEndUserAuth) Documentation() (activity, details, detailsExample string) {
|
||||
return `Generated when a user turns off end user authentication for macOS hosts that automatically enroll to a team (or no team).`,
|
||||
`This activity contains the following fields:
|
||||
- "team_id": The ID of the team that end user authentication applies to, null if it applies to devices that are not in a team.
|
||||
- "team_name": The name of the team that end user authentication applies to, null if it applies to devices that are not in a team.`, `{
|
||||
"team_id": 123,
|
||||
"team_name": "Workstations"
|
||||
}`
|
||||
}
|
||||
|
||||
// LogRoleChangeActivities logs activities for each role change, globally and one for each change in teams.
|
||||
func LogRoleChangeActivities(ctx context.Context, ds Datastore, adminUser *User, oldGlobalRole *string, oldTeamRoles []UserTeam, user *User) error {
|
||||
if user.GlobalRole != nil && (oldGlobalRole == nil || *oldGlobalRole != *user.GlobalRole) {
|
||||
|
||||
+3
-2
@@ -258,8 +258,9 @@ func (s *MacOSSettings) FromMap(m map[string]interface{}) (map[string]bool, erro
|
||||
|
||||
// MacOSSetup contains settings related to the setup of DEP enrolled devices.
|
||||
type MacOSSetup struct {
|
||||
BootstrapPackage optjson.String `json:"bootstrap_package"`
|
||||
MacOSSetupAssistant optjson.String `json:"macos_setup_assistant"`
|
||||
BootstrapPackage optjson.String `json:"bootstrap_package"`
|
||||
EnableEndUserAuthentication bool `json:"enable_end_user_authentication"`
|
||||
MacOSSetupAssistant optjson.String `json:"macos_setup_assistant"`
|
||||
}
|
||||
|
||||
// MDMEndUserAuthentication contains settings related to end user authentication
|
||||
|
||||
@@ -432,6 +432,18 @@ func (p MDMAppleSettingsPayload) AuthzType() string {
|
||||
return "mdm_apple_settings"
|
||||
}
|
||||
|
||||
// MDMAppleSetupPayload describes the payload accepted by the endpoint to
|
||||
// update specific MDM macos setup values for a team (or no team).
|
||||
type MDMAppleSetupPayload struct {
|
||||
TeamID *uint `json:"team_id"`
|
||||
EnableEndUserAuthentication *bool `json:"enable_end_user_authentication"`
|
||||
}
|
||||
|
||||
// AuthzType implements authz.AuthzTyper.
|
||||
func (p MDMAppleSetupPayload) AuthzType() string {
|
||||
return "mdm_apple_settings" // TODO: add mdm_apple_setup to rego?
|
||||
}
|
||||
|
||||
// NanoEnrollment represents a row in the nano_enrollments table managed by
|
||||
// nanomdm. It is meant to be used internally by the server, not to be returned
|
||||
// as part of endpoints, and as a precaution its json-encoding is explicitly
|
||||
|
||||
@@ -713,6 +713,10 @@ type Service interface {
|
||||
// Delete the MDM Apple Setup Assistant for the provided team or no team.
|
||||
DeleteMDMAppleSetupAssistant(ctx context.Context, teamID *uint) error
|
||||
|
||||
// UpdateMDMAppleSetup updates the specified MDM Apple setup values for a
|
||||
// specified team or for hosts with no team.
|
||||
UpdateMDMAppleSetup(ctx context.Context, payload MDMAppleSetupPayload) error
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CronSchedulesService
|
||||
|
||||
|
||||
@@ -206,9 +206,12 @@ func (d *DEPService) RegisterProfileWithAppleDEPServer(ctx context.Context, setu
|
||||
// always still set configuration_web_url, otherwise the request method
|
||||
// coming from Apple changes from GET to POST, and we want to preserve
|
||||
// backwards compatibility.
|
||||
if appCfg.MDM.EndUserAuthentication.SSOProviderSettings.IsEmpty() {
|
||||
jsonProf.ConfigurationWebURL = enrollURL
|
||||
} else {
|
||||
jsonProf.ConfigurationWebURL = enrollURL
|
||||
if !appCfg.MDM.EndUserAuthentication.SSOProviderSettings.IsEmpty() {
|
||||
// TODO: modify method signatures for this (and callers as applicable)
|
||||
// to include a team config pointer and check enable_end_user_authenthication
|
||||
// in the team config if not nil otherwise check enable_end_user_authenthication
|
||||
// in the app config.
|
||||
jsonProf.ConfigurationWebURL = appCfg.ServerSettings.ServerURL + "/mdm/sso"
|
||||
}
|
||||
|
||||
|
||||
@@ -403,15 +403,6 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
||||
}
|
||||
}
|
||||
|
||||
mdmSSOSettingsChanged := oldAppConfig.MDM.EndUserAuthentication.SSOProviderSettings !=
|
||||
appConfig.MDM.EndUserAuthentication.SSOProviderSettings
|
||||
serverURLChanged := oldAppConfig.ServerSettings.ServerURL != appConfig.ServerSettings.ServerURL
|
||||
if (mdmSSOSettingsChanged || serverURLChanged) && license.Tier == "premium" {
|
||||
if err := svc.EnterpriseOverrides.MDMAppleSyncDEPProfiles(ctx); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "sync DEP profile")
|
||||
}
|
||||
}
|
||||
|
||||
if oldAppConfig.MDM.MacOSSetup.BootstrapPackage.Value != appConfig.MDM.MacOSSetup.BootstrapPackage.Value &&
|
||||
appConfig.MDM.MacOSSetup.BootstrapPackage.Value == "" {
|
||||
// clear bootstrap package for no team - note that we cannot call
|
||||
@@ -480,6 +471,28 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
||||
}
|
||||
}
|
||||
|
||||
mdmEnableEndUserAuthChanged := oldAppConfig.MDM.MacOSSetup.EnableEndUserAuthentication != appConfig.MDM.MacOSSetup.EnableEndUserAuthentication
|
||||
if mdmEnableEndUserAuthChanged {
|
||||
var act fleet.ActivityDetails
|
||||
if appConfig.MDM.MacOSSetup.EnableEndUserAuthentication {
|
||||
act = fleet.ActivityTypeEnabledMacosSetupEndUserAuth{}
|
||||
} else {
|
||||
act = fleet.ActivityTypeDisabledMacosSetupEndUserAuth{}
|
||||
}
|
||||
if err := svc.ds.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "create activity for macos enable end user auth change")
|
||||
}
|
||||
}
|
||||
|
||||
mdmSSOSettingsChanged := oldAppConfig.MDM.EndUserAuthentication.SSOProviderSettings !=
|
||||
appConfig.MDM.EndUserAuthentication.SSOProviderSettings
|
||||
serverURLChanged := oldAppConfig.ServerSettings.ServerURL != appConfig.ServerSettings.ServerURL
|
||||
if (mdmEnableEndUserAuthChanged || mdmSSOSettingsChanged || serverURLChanged) && license.Tier == "premium" {
|
||||
if err := svc.EnterpriseOverrides.MDMAppleSyncDEPProfiles(ctx); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "sync DEP profile")
|
||||
}
|
||||
}
|
||||
|
||||
return obfuscatedAppConfig, nil
|
||||
}
|
||||
|
||||
@@ -499,6 +512,9 @@ func (svc *Service) validateMDM(
|
||||
if oldMdm.MacOSSetup.BootstrapPackage.Value != mdm.MacOSSetup.BootstrapPackage.Value && !license.IsPremium() {
|
||||
invalid.Append("macos_setup.bootstrap_package", ErrMissingLicense.Error())
|
||||
}
|
||||
if oldMdm.MacOSSetup.EnableEndUserAuthentication != mdm.MacOSSetup.EnableEndUserAuthentication && !license.IsPremium() {
|
||||
invalid.Append("macos_setup.enable_end_user_authentication", ErrMissingLicense.Error())
|
||||
}
|
||||
|
||||
// we want to use `oldMdm` here as this boolean is set by the fleet
|
||||
// server at startup and can't be modified by the user
|
||||
@@ -522,6 +538,10 @@ func (svc *Service) validateMDM(
|
||||
invalid.Append("macos_setup.bootstrap_package",
|
||||
`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 oldMdm.MacOSSetup.EnableEndUserAuthentication != mdm.MacOSSetup.EnableEndUserAuthentication {
|
||||
invalid.Append("macos_setup.enable_end_user_authentication",
|
||||
`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 name := mdm.AppleBMDefaultTeam; name != "" && name != oldMdm.AppleBMDefaultTeam {
|
||||
@@ -560,6 +580,16 @@ func (svc *Service) validateMDM(
|
||||
|
||||
validateSSOProviderSettings(mdm.EndUserAuthentication.SSOProviderSettings, oldMdm.EndUserAuthentication.SSOProviderSettings, invalid)
|
||||
}
|
||||
|
||||
// MacOSSetup validation
|
||||
if mdm.MacOSSetup.EnableEndUserAuthentication {
|
||||
if mdm.EndUserAuthentication.IsEmpty() {
|
||||
// TODO: update this error message to include steps to resolve the issue once docs for IdP
|
||||
// config are available
|
||||
invalid.Append("macos_setup.enable_end_user_authentication",
|
||||
`Couldn't enable macos_setup.enable_end_user_authentication because no IdP is configured for MDM features.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateSSOProviderSettings(incoming, existing fleet.SSOProviderSettings, invalid *fleet.InvalidArgumentError) {
|
||||
|
||||
@@ -1944,6 +1944,41 @@ func (svc *Service) DeleteMDMAppleSetupAssistant(ctx context.Context, teamID *ui
|
||||
return fleet.ErrMissingLicense
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Update MDM Apple Setup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type updateMDMAppleSetupRequest struct {
|
||||
fleet.MDMAppleSetupPayload
|
||||
}
|
||||
|
||||
type updateMDMAppleSetupResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r updateMDMAppleSetupResponse) error() error { return r.Err }
|
||||
|
||||
func (r updateMDMAppleSetupResponse) Status() int { return http.StatusNoContent }
|
||||
|
||||
// This endpoint is required because the UI must allow maintainers (in addition
|
||||
// to admins) to update some MDM Apple settings, while the update config/update
|
||||
// team endpoints only allow write access to admins.
|
||||
func updateMDMAppleSetupEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
||||
req := request.(*updateMDMAppleSetupRequest)
|
||||
if err := svc.UpdateMDMAppleSetup(ctx, req.MDMAppleSetupPayload); err != nil {
|
||||
return updateMDMAppleSetupResponse{Err: err}, nil
|
||||
}
|
||||
return updateMDMAppleSetupResponse{}, nil
|
||||
}
|
||||
|
||||
func (svc *Service) UpdateMDMAppleSetup(ctx context.Context, payload fleet.MDMAppleSetupPayload) error {
|
||||
// skipauth: No authorization check needed due to implementation returning
|
||||
// only license error.
|
||||
svc.authz.SkipAuthorization(ctx)
|
||||
|
||||
return fleet.ErrMissingLicense
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// POST /mdm/sso
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupAppleMDMService(t *testing.T) (fleet.Service, context.Context, *mock.Store) {
|
||||
func setupAppleMDMService(t *testing.T, license *fleet.LicenseInfo) (fleet.Service, context.Context, *mock.Store) {
|
||||
ds := new(mock.Store)
|
||||
cfg := config.TestConfig()
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -66,7 +66,7 @@ func setupAppleMDMService(t *testing.T) (fleet.Service, context.Context, *mock.S
|
||||
MDMStorage: mdmStorage,
|
||||
DEPStorage: depStorage,
|
||||
MDMPusher: pusher,
|
||||
License: &fleet.LicenseInfo{Tier: fleet.TierPremium},
|
||||
License: license,
|
||||
}
|
||||
svc, ctx := newTestServiceWithConfig(t, ds, cfg, nil, nil, opts)
|
||||
|
||||
@@ -175,7 +175,7 @@ func setupAppleMDMService(t *testing.T) (fleet.Service, context.Context, *mock.S
|
||||
}
|
||||
|
||||
func TestAppleMDMAuthorization(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
|
||||
checkAuthErr := func(t *testing.T, err error, shouldFailWithAuth bool) {
|
||||
t.Helper()
|
||||
@@ -441,7 +441,7 @@ func TestAppleMDMAuthorization(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMDMAppleConfigProfileAuthz(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -615,7 +615,7 @@ func TestMDMAppleConfigProfileAuthz(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewMDMAppleConfigProfile(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
ctx = viewer.NewContext(ctx, viewer.Viewer{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||||
|
||||
mcBytes := mcBytesForTest("Foo", "Bar", "UUID")
|
||||
@@ -665,7 +665,7 @@ func mcBytesForTest(name, identifier, uuid string) []byte {
|
||||
}
|
||||
|
||||
func TestHostDetailsMDMProfiles(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
ctx = viewer.NewContext(ctx, viewer.Viewer{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
|
||||
|
||||
expected := []fleet.HostMDMAppleProfile{
|
||||
@@ -808,7 +808,7 @@ func TestHostDetailsMDMProfiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMDMCommandAuthz(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
|
||||
ds.HostLiteFunc = func(ctx context.Context, hostID uint) (*fleet.Host, error) {
|
||||
switch hostID {
|
||||
@@ -1221,7 +1221,7 @@ func TestMDMCommandAndReportResultsProfileHandling(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMDMBatchSetAppleProfiles(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
|
||||
ds.TeamByNameFunc = func(ctx context.Context, name string) (*fleet.Team, error) {
|
||||
return &fleet.Team{ID: 1, Name: name}, nil
|
||||
@@ -1508,7 +1508,7 @@ func TestMDMBatchSetAppleProfiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateMDMAppleSettings(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
|
||||
ds.TeamFunc = func(ctx context.Context, id uint) (*fleet.Team, error) {
|
||||
return &fleet.Team{ID: id, Name: "team"}, nil
|
||||
@@ -1661,6 +1661,163 @@ func TestUpdateMDMAppleSettings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateMDMAppleSetup(t *testing.T) {
|
||||
setupTest := func(tier string) (fleet.Service, context.Context, *mock.Store) {
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: tier})
|
||||
ds.TeamFunc = func(ctx context.Context, id uint) (*fleet.Team, error) {
|
||||
return &fleet.Team{ID: id, Name: "team"}, nil
|
||||
}
|
||||
ds.SaveTeamFunc = func(ctx context.Context, team *fleet.Team) (*fleet.Team, error) {
|
||||
return team, nil
|
||||
}
|
||||
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
return nil
|
||||
}
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return &fleet.AppConfig{MDM: fleet.MDM{EnabledAndConfigured: true}}, nil
|
||||
}
|
||||
ds.SaveAppConfigFunc = func(ctx context.Context, appConfig *fleet.AppConfig) error {
|
||||
return nil
|
||||
}
|
||||
return svc, ctx, ds
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
user *fleet.User
|
||||
teamID *uint
|
||||
wantErr string
|
||||
}
|
||||
// TODO: Add tests for gitops and observer plus roles? (Settings endpoint test above may also need to be updated)
|
||||
|
||||
t.Run("FreeTier", func(t *testing.T) {
|
||||
freeTestCases := []testCase{
|
||||
{
|
||||
"global admin",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
|
||||
nil,
|
||||
"Requires Fleet Premium license",
|
||||
},
|
||||
{
|
||||
"global maintainer",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleMaintainer)},
|
||||
nil,
|
||||
"Requires Fleet Premium license",
|
||||
},
|
||||
{
|
||||
"team id with free license",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
|
||||
ptr.Uint(1),
|
||||
"Requires Fleet Premium license",
|
||||
},
|
||||
}
|
||||
svc, ctx, _ := setupTest(fleet.TierFree)
|
||||
for _, tt := range freeTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// prepare the context with the user and license
|
||||
ctx := viewer.NewContext(ctx, viewer.Viewer{User: tt.user})
|
||||
err := svc.UpdateMDMAppleSetup(ctx, fleet.MDMAppleSetupPayload{TeamID: tt.teamID})
|
||||
if tt.wantErr == "" {
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
})
|
||||
t.Run("PremiumTier", func(t *testing.T) {
|
||||
premiumTestCases := []testCase{
|
||||
{
|
||||
"global admin premium",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"global admin, team",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
|
||||
ptr.Uint(1),
|
||||
"",
|
||||
},
|
||||
{
|
||||
"global maintainer premium",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleMaintainer)},
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"global maintainer, team",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleMaintainer)},
|
||||
ptr.Uint(1),
|
||||
"",
|
||||
},
|
||||
{
|
||||
"global observer",
|
||||
&fleet.User{GlobalRole: ptr.String(fleet.RoleObserver)},
|
||||
nil,
|
||||
authz.ForbiddenErrorMessage,
|
||||
},
|
||||
{
|
||||
"team admin, DOES belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleAdmin}}},
|
||||
ptr.Uint(1),
|
||||
"",
|
||||
},
|
||||
{
|
||||
"team admin, DOES NOT belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 2}, Role: fleet.RoleAdmin}}},
|
||||
ptr.Uint(1),
|
||||
authz.ForbiddenErrorMessage,
|
||||
},
|
||||
{
|
||||
"team maintainer, DOES belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleMaintainer}}},
|
||||
ptr.Uint(1),
|
||||
"",
|
||||
},
|
||||
{
|
||||
"team maintainer, DOES NOT belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 2}, Role: fleet.RoleMaintainer}}},
|
||||
ptr.Uint(1),
|
||||
authz.ForbiddenErrorMessage,
|
||||
},
|
||||
{
|
||||
"team observer, DOES belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserver}}},
|
||||
ptr.Uint(1),
|
||||
authz.ForbiddenErrorMessage,
|
||||
},
|
||||
{
|
||||
"team observer, DOES NOT belong to team",
|
||||
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 2}, Role: fleet.RoleObserver}}},
|
||||
ptr.Uint(1),
|
||||
authz.ForbiddenErrorMessage,
|
||||
},
|
||||
{
|
||||
"user no roles",
|
||||
&fleet.User{ID: 1337},
|
||||
nil,
|
||||
authz.ForbiddenErrorMessage,
|
||||
},
|
||||
}
|
||||
svc, ctx, _ := setupTest(fleet.TierPremium)
|
||||
for _, tt := range premiumTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// prepare the context with the user and license
|
||||
ctx := viewer.NewContext(ctx, viewer.Viewer{User: tt.user})
|
||||
err := svc.UpdateMDMAppleSetup(ctx, fleet.MDMAppleSetupPayload{TeamID: tt.teamID})
|
||||
if tt.wantErr == "" {
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMDMAppleCommander(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
mdmStorage := &nanomdm_mock.Storage{}
|
||||
@@ -2244,7 +2401,7 @@ func TestEnsureFleetdConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMDMAppleSetupAssistant(t *testing.T) {
|
||||
svc, ctx, ds := setupAppleMDMService(t)
|
||||
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
|
||||
|
||||
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
|
||||
return nil
|
||||
|
||||
@@ -479,6 +479,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
mdm.POST("/api/_version_/fleet/mdm/hosts/{id:[0-9]+}/wipe", deviceWipeEndpoint, deviceWipeRequest{})
|
||||
|
||||
mdm.PATCH("/api/_version_/fleet/mdm/apple/settings", updateMDMAppleSettingsEndpoint, updateMDMAppleSettingsRequest{})
|
||||
mdm.PATCH("/api/_version_/fleet/mdm/apple/setup", updateMDMAppleSetupEndpoint, updateMDMAppleSetupRequest{})
|
||||
mdm.GET("/api/_version_/fleet/mdm/apple", getAppleMDMEndpoint, nil)
|
||||
|
||||
mdm.POST("/api/_version_/fleet/mdm/apple/setup/eula", createMDMAppleEULAEndpoint, createMDMAppleEULARequest{})
|
||||
|
||||
@@ -3011,6 +3011,267 @@ func (s *integrationMDMTestSuite) TestEULA() {
|
||||
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/apple/setup/eula/%s", eulaToken), nil, http.StatusNotFound, &deleteResp)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestMDMMacOSSetup() {
|
||||
t := s.T()
|
||||
|
||||
s.mockDEPResponse(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
encoder := json.NewEncoder(w)
|
||||
switch r.URL.Path {
|
||||
case "/session":
|
||||
err := encoder.Encode(map[string]string{"auth_session_token": "xyz"})
|
||||
require.NoError(t, err)
|
||||
case "/profile":
|
||||
err := encoder.Encode(godep.ProfileResponse{ProfileUUID: "abc"})
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
_, _ = w.Write([]byte(`{}`))
|
||||
}
|
||||
}))
|
||||
|
||||
// setup test data
|
||||
var acResp appConfigResponse
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"end_user_authentication": {
|
||||
"entity_id": "https://localhost:8080",
|
||||
"issuer_uri": "http://localhost:8080/simplesaml/saml2/idp/SSOService.php",
|
||||
"idp_name": "SimpleSAML",
|
||||
"metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php"
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.NotEmpty(t, acResp.MDM.EndUserAuthentication)
|
||||
|
||||
tm, err := s.ds.NewTeam(context.Background(), &fleet.Team{Name: "team1"})
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
raw string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
raw: `"mdm": {}`,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
raw: `"mdm": {
|
||||
"macos_setup": {}
|
||||
}`,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
raw: `"mdm": {
|
||||
"macos_setup": {
|
||||
"enable_end_user_authentication": true
|
||||
}
|
||||
}`,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
raw: `"mdm": {
|
||||
"macos_setup": {
|
||||
"enable_end_user_authentication": false
|
||||
}
|
||||
}`,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("UpdateAppConfig", func(t *testing.T) {
|
||||
acResp := appConfigResponse{}
|
||||
path := "/api/latest/fleet/config"
|
||||
fmtJSON := func(s string) json.RawMessage {
|
||||
return json.RawMessage(fmt.Sprintf(`{
|
||||
%s
|
||||
}`, s))
|
||||
}
|
||||
|
||||
// get the initial appconfig; enable end user authentication default is false
|
||||
s.DoJSON("GET", path, nil, http.StatusOK, &acResp)
|
||||
require.False(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
|
||||
for i, c := range cases {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("PATCH", path, fmtJSON(c.raw), http.StatusOK, &acResp)
|
||||
require.Equal(t, c.expected, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", path, nil, http.StatusOK, &acResp)
|
||||
require.Equal(t, c.expected, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateTeamConfig", func(t *testing.T) {
|
||||
path := fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID)
|
||||
fmtJSON := `{
|
||||
"name": %q,
|
||||
%s
|
||||
}`
|
||||
|
||||
// get the initial team config; enable end user authentication default is false
|
||||
teamResp := teamResponse{}
|
||||
s.DoJSON("GET", path, nil, http.StatusOK, &teamResp)
|
||||
require.False(t, teamResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
|
||||
for i, c := range cases {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
teamResp = teamResponse{}
|
||||
s.DoJSON("PATCH", path, json.RawMessage(fmt.Sprintf(fmtJSON, tm.Name, c.raw)), http.StatusOK, &teamResp)
|
||||
require.Equal(t, c.expected, teamResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
|
||||
teamResp = teamResponse{}
|
||||
s.DoJSON("GET", path, nil, http.StatusOK, &teamResp)
|
||||
require.Equal(t, c.expected, teamResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestMDMAppleSetupEndpoint", func(t *testing.T) {
|
||||
t.Run("TestNoTeam", func(t *testing.T) {
|
||||
var acResp appConfigResponse
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: ptr.Uint(0), EnableEndUserAuthentication: ptr.Bool(true)}, http.StatusNoContent)
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.True(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
lastActivityID := s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledMacosSetupEndUserAuth{}.ActivityName(),
|
||||
`{"team_id": null, "team_name": null}`, 0)
|
||||
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: ptr.Uint(0), EnableEndUserAuthentication: ptr.Bool(true)}, http.StatusNoContent)
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.True(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledMacosSetupEndUserAuth{}.ActivityName(),
|
||||
``, lastActivityID) // no new activity
|
||||
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: ptr.Uint(0), EnableEndUserAuthentication: ptr.Bool(false)}, http.StatusNoContent)
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.False(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
require.Greater(t, s.lastActivityOfTypeMatches(fleet.ActivityTypeDisabledMacosSetupEndUserAuth{}.ActivityName(),
|
||||
`{"team_id": null, "team_name": null}`, 0), lastActivityID)
|
||||
})
|
||||
|
||||
t.Run("TestTeam", func(t *testing.T) {
|
||||
tmConfigPath := fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID)
|
||||
expectedActivityDetail := fmt.Sprintf(`{"team_id": %d, "team_name": %q}`, tm.ID, tm.Name)
|
||||
var tmResp teamResponse
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: &tm.ID, EnableEndUserAuthentication: ptr.Bool(true)}, http.StatusNoContent)
|
||||
tmResp = teamResponse{}
|
||||
s.DoJSON("GET", tmConfigPath, nil, http.StatusOK, &tmResp)
|
||||
require.True(t, tmResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
lastActivityID := s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledMacosSetupEndUserAuth{}.ActivityName(),
|
||||
expectedActivityDetail, 0)
|
||||
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: &tm.ID, EnableEndUserAuthentication: ptr.Bool(true)}, http.StatusNoContent)
|
||||
tmResp = teamResponse{}
|
||||
s.DoJSON("GET", tmConfigPath, nil, http.StatusOK, &tmResp)
|
||||
require.True(t, tmResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEnabledMacosSetupEndUserAuth{}.ActivityName(),
|
||||
``, lastActivityID) // no new activity
|
||||
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: &tm.ID, EnableEndUserAuthentication: ptr.Bool(false)}, http.StatusNoContent)
|
||||
tmResp = teamResponse{}
|
||||
s.DoJSON("GET", tmConfigPath, nil, http.StatusOK, &tmResp)
|
||||
require.False(t, tmResp.Team.Config.MDM.MacOSSetup.EnableEndUserAuthentication)
|
||||
require.Greater(t, s.lastActivityOfTypeMatches(fleet.ActivityTypeDisabledMacosSetupEndUserAuth{}.ActivityName(),
|
||||
expectedActivityDetail, 0), lastActivityID)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("ValidateEnableEndUserAuthentication", func(t *testing.T) {
|
||||
// ensure the test is setup correctly
|
||||
var acResp appConfigResponse
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"end_user_authentication": {
|
||||
"entity_id": "https://localhost:8080",
|
||||
"issuer_uri": "http://localhost:8080/simplesaml/saml2/idp/SSOService.php",
|
||||
"idp_name": "SimpleSAML",
|
||||
"metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php"
|
||||
},
|
||||
"macos_setup": {
|
||||
"enable_end_user_authentication": true
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.NotEmpty(t, acResp.MDM.EndUserAuthentication)
|
||||
|
||||
// ok to disable end user authentication without a configured IdP
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"end_user_authentication": {
|
||||
"entity_id": "",
|
||||
"issuer_uri": "",
|
||||
"idp_name": "",
|
||||
"metadata_url": ""
|
||||
},
|
||||
"macos_setup": {
|
||||
"enable_end_user_authentication": false
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Equal(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication, false)
|
||||
require.True(t, acResp.MDM.EndUserAuthentication.IsEmpty())
|
||||
|
||||
// can't enable end user authentication without a configured IdP
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"end_user_authentication": {
|
||||
"entity_id": "",
|
||||
"issuer_uri": "",
|
||||
"idp_name": "",
|
||||
"metadata_url": ""
|
||||
},
|
||||
"macos_setup": {
|
||||
"enable_end_user_authentication": true
|
||||
}
|
||||
}
|
||||
}`), http.StatusUnprocessableEntity, &acResp)
|
||||
|
||||
// can't use setup endpoint to enable end user authentication on no team without a configured IdP
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: ptr.Uint(0), EnableEndUserAuthentication: ptr.Bool(true)}, http.StatusUnprocessableEntity)
|
||||
|
||||
// can't enable end user authentication on team config without a configured IdP already on app config
|
||||
var teamResp teamResponse
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID), json.RawMessage(fmt.Sprintf(`{
|
||||
"name": %q,
|
||||
"mdm": {
|
||||
"macos_setup": {
|
||||
"enable_end_user_authentication": true
|
||||
}
|
||||
}
|
||||
}`, tm.Name)), http.StatusUnprocessableEntity, &teamResp)
|
||||
|
||||
// can't use setup endpoint to enable end user authentication on team without a configured IdP
|
||||
s.Do("PATCH", "/api/latest/fleet/mdm/apple/setup",
|
||||
fleet.MDMAppleSetupPayload{TeamID: &tm.ID, EnableEndUserAuthentication: ptr.Bool(true)}, http.StatusUnprocessableEntity)
|
||||
|
||||
// ensure IdP is empty for the rest of the tests
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"end_user_authentication": {
|
||||
"entity_id": "",
|
||||
"issuer_uri": "",
|
||||
"idp_name": "",
|
||||
"metadata_url": ""
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Empty(t, acResp.MDM.EndUserAuthentication)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestMacosSetupAssistant() {
|
||||
ctx := context.Background()
|
||||
t := s.T()
|
||||
|
||||
Reference in New Issue
Block a user