2025-10-09 12:17:48 +00:00
module . exports = {
friendlyName : 'Modify android device' ,
description : 'Modifies a device of an Android enterprise' ,
inputs : {
androidEnterpriseId : {
type : 'string' ,
required : true ,
} ,
deviceId : {
type : 'string' ,
required : true ,
} ,
} ,
exits : {
2025-10-10 22:11:42 +00:00
success : { description : 'The device of an Android enterprise was successfully updated.' } ,
missingAuthHeader : { description : 'This request was missing an authorization header.' , responseType : 'unauthorized' } ,
unauthorized : { description : 'Invalid authentication token.' , responseType : 'unauthorized' } ,
Do not return MDM=off android hosts from reconciler (#34304)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34299
Unreleased bug in Android Config profiles 4.75.0 feature. No changes
file as such. I'm not entirely sure how to cause this as I was unable to
repro it locally, there may be a timing issue or something, so I haven't
fully QA'd manually. QA was limited to verifying basic reconciler
functionality
Also updated Website endpoint to not throw a 5XX since we expect this to
occasionally happen
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [ ] QA'd all new/changed functionality manually
For unreleased bug fixes in a release candidate, one of:
- [x] Confirmed that the fix is not expected to adversely impact load
test results
2025-10-16 13:59:17 +00:00
notFound : { description : 'No Android enterprise found for this Fleet server.' , responseType : 'notFound' } ,
deviceNoLongerManaged : { description : 'The device is no longer managed by the Android enterprise.' , responseType : 'notFound' } ,
2026-02-19 23:46:41 +00:00
invalidPolicyName : { description : 'The specified policy_name is invalid' , responseType : 'badRequest' }
2025-10-09 12:17:48 +00:00
} ,
fn : async function ( { androidEnterpriseId , deviceId } ) {
// Extract fleetServerSecret from the Authorization header
let authHeader = this . req . get ( 'authorization' ) ;
let fleetServerSecret ;
if ( authHeader && authHeader . startsWith ( 'Bearer' ) ) {
fleetServerSecret = authHeader . replace ( 'Bearer' , '' ) . trim ( ) ;
} else {
2025-10-10 22:11:42 +00:00
throw 'missingAuthHeader' ;
2025-10-09 12:17:48 +00:00
}
// Authenticate this request
let thisAndroidEnterprise = await AndroidEnterprise . findOne ( {
androidEnterpriseId : androidEnterpriseId
} ) ;
// Return a 404 response if no records are found.
if ( ! thisAndroidEnterprise ) {
2025-10-10 22:11:42 +00:00
throw 'notFound' ;
2025-10-09 12:17:48 +00:00
}
// Return an unauthorized response if the provided secret does not match.
if ( thisAndroidEnterprise . fleetServerSecret !== fleetServerSecret ) {
2025-10-10 22:11:42 +00:00
throw 'unauthorized' ;
2025-10-09 12:17:48 +00:00
}
// Check the list of Android Enterprises managed by Fleet to see if this Android Enterprise is still managed.
let isEnterpriseManagedByFleet = await sails . helpers . androidProxy . getIsEnterpriseManagedByFleet ( androidEnterpriseId ) ;
// Return a 404 response if this Android enterprise is no longer managed by Fleet.
if ( ! isEnterpriseManagedByFleet ) {
2025-10-10 22:11:42 +00:00
throw 'notFound' ;
2025-10-09 12:17:48 +00:00
}
// Update the device for this Android enterprise.
// Note: We're using sails.helpers.flow.build here to handle any errors that occur using google's node library.
let modifyDeviceResponse = await sails . helpers . flow . build ( async ( ) => {
let { google } = require ( 'googleapis' ) ;
let androidmanagement = google . androidmanagement ( 'v1' ) ;
let googleAuth = new google . auth . GoogleAuth ( {
scopes : [ 'https://www.googleapis.com/auth/androidmanagement' ] ,
credentials : {
client _email : sails . config . custom . androidEnterpriseServiceAccountEmailAddress , // eslint-disable-line camelcase
private _key : sails . config . custom . androidEnterpriseServiceAccountPrivateKey , // eslint-disable-line camelcase
} ,
} ) ;
// Acquire the google auth client, and bind it to all future calls
let authClient = await googleAuth . getClient ( ) ;
google . options ( { auth : authClient } ) ;
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#patch
let patchDeviceResponse = await androidmanagement . enterprises . devices . patch ( {
name : ` enterprises/ ${ androidEnterpriseId } /devices/ ${ deviceId } ` ,
2026-03-07 00:06:25 +00:00
// Note: Typically, we use defined inputs instead of accessing req.body directly. We forward req.body here to prevent previously set values from being overwritten by undefined values.
// This behavior should not be repeated in future Android proxy endpoints.
2025-10-09 12:17:48 +00:00
requestBody : this . req . body ,
} ) ;
return patchDeviceResponse . data ;
2025-11-12 21:37:21 +00:00
} ) . intercept ( { status : 429 } , ( err ) => {
// If the Android management API returns a 429 response, log an additional warning that will trigger a help-p1 alert.
sails . log . warn ( ` p1: Android management API rate limit exceeded! ` ) ;
return new Error ( ` When attempting to update a device for an Android enterprise ( ${ androidEnterpriseId } ), an error occurred. Error: ${ err } ` ) ;
} ) . intercept ( ( err ) => {
Do not return MDM=off android hosts from reconciler (#34304)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34299
Unreleased bug in Android Config profiles 4.75.0 feature. No changes
file as such. I'm not entirely sure how to cause this as I was unable to
repro it locally, there may be a timing issue or something, so I haven't
fully QA'd manually. QA was limited to verifying basic reconciler
functionality
Also updated Website endpoint to not throw a 5XX since we expect this to
occasionally happen
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
## Testing
- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [ ] QA'd all new/changed functionality manually
For unreleased bug fixes in a release candidate, one of:
- [x] Confirmed that the fix is not expected to adversely impact load
test results
2025-10-16 13:59:17 +00:00
let errorString = err . toString ( ) ;
if ( errorString . includes ( 'Device is no longer being managed' ) ) {
return { 'deviceNoLongerManaged' : 'The device is no longer managed by the Android enterprise.' } ;
}
2026-02-19 23:46:41 +00:00
if ( errorString . includes ( 'policy_name is expected to start' ) ) {
return { 'invalidPolicyName' : 'The request could not be completed because of an invalid policy name.' } ;
}
return new Error ( ` When attempting to update a device for an Android enterprise ( ${ androidEnterpriseId } ), an error occurred. Error: ${ require ( 'util' ) . inspect ( err ) } ` ) ;
2025-10-09 12:17:48 +00:00
} ) ;
// Return the modified device back to the Fleet server.
return modifyDeviceResponse ;
}
} ;