Website: use shared Google API auth client in android proxy endpoints. (#47810)

Closes: https://github.com/fleetdm/fleet/issues/46496

Changes:
- Updated the website's custom hook to create a Google API auth client
and make it available at `sails.googleAuthClient`
- Updated Android proxy endpoints to use the shared Google API auth
client.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Optimized Google API authentication handling for Android management
features to improve system performance and reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Eric
2026-07-20 18:38:55 -05:00
committed by GitHub
parent 5e19bfc4a2
commit 632b4d924b
16 changed files with 156 additions and 202 deletions
@@ -51,21 +51,15 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
let newEnrollmentToken = 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});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Enrollmenttokens.html#create
let enrollmentTokenCreateResponse = await androidmanagement.enterprises.enrollmentTokens.create({
let enrollmentTokenCreateResponse = await androidManagementConnection.enterprises.enrollmentTokens.create({
parent: `enterprises/${androidEnterpriseId}`,
// Note: Typically, we use defined inputs instead of accessing req.body directly. This behavior should not be repeated in future Android proxy endpoints.
requestBody: this.req.body,
@@ -62,24 +62,17 @@ module.exports = {
enterprise.pubsubTopic = fullPubSubTopicName;
let newSubscriptionName = `projects/${sails.config.custom.androidEnterpriseProjectId}/subscriptions/${newPubSubTopicName}`;
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Complete the setup of the new Android enterprise.
// Note: We're using sails.helpers.flow.build here to handle any errors that occurr using google's node library.
let newEnterprise = 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',
'https://www.googleapis.com/auth/pubsub'
],
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();
let pubsub = google.pubsub({version: 'v1', auth: authClient});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
let pubsub = google.pubsub({version: 'v1', auth: androidManagementAuthClient});
// Create a new pubsub topic for this enterprise.
// [?]: https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics/create
@@ -88,7 +81,6 @@ module.exports = {
requestBody: {
messageRetentionDuration: '86400s'// 24 hours
},
auth: authClient,
});
// Debugging attempt - Give it a second before calling the getIamPolicy (plus excessive back-off retry delays.)
@@ -100,7 +92,6 @@ module.exports = {
const newPubSubTopicIamPolicy = await sails.helpers.flow.build(async () => {
const policy = await pubsub.projects.topics.getIamPolicy({
resource: fullPubSubTopicName,
auth: authClient,
});
return policy.data;
@@ -124,7 +115,6 @@ module.exports = {
requestBody: {
policy: newPubSubTopicIamPolicy
},
auth: authClient,
});
}).retry(undefined, [1000, 1500, 2000]);
@@ -141,18 +131,16 @@ module.exports = {
pushEndpoint: pubsubPushUrl// Use the pubsubPushUrl provided by the Fleet server.
}
},
auth: authClient,
});
// Now create the new enterprise for this Fleet server.
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises.html#create
let createEnterpriseResponse = await androidmanagement.enterprises.create({
let createEnterpriseResponse = await androidManagementConnection.enterprises.create({
agreementAccepted: true,
enterpriseToken: enterpriseToken,
projectId: sails.config.custom.androidEnterpriseProjectId,
signupUrlName: signupUrlName,
requestBody: enterprise,
auth: authClient,
});
return createEnterpriseResponse.data;
}).intercept({status: 400}, (err)=>{
@@ -54,24 +54,16 @@ module.exports = {
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Get a signup url for this Android enterprise.
// Note: We're using sails.helpers.flow.build here to handle any errors that occurr using google's node library.
let signupUrl = 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});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?] https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Signupurls.html#create
let createSignupUrlResponse = await androidmanagement.signupUrls.create({
let createSignupUrlResponse = await androidManagementConnection.signupUrls.create({
// The callback URL that the admin will be redirected to after successfully creating an enterprise. Before redirecting there the system will add a query parameter to this URL named enterpriseToken which will contain an opaque token to be used for the create enterprise request. The URL will be parsed then reformatted in order to add the enterpriseToken parameter, so there may be some minor formatting changes.
callbackUrl: callbackUrl,
// The ID of the Google Cloud Platform project which will own the enterprise.
@@ -67,23 +67,17 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Create the webApp.
// Note: We're using sails.helpers.flow.build here to handle any errors that occur using google's node library.
let createWebAppResponse = 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Webapps.html#create
let createWebAppResponse = await androidmanagement.enterprises.webApps.create({
let createWebAppResponse = await androidManagementConnection.enterprises.webApps.create({
parent: `enterprises/${androidEnterpriseId}`,
requestBody: {
title,
@@ -56,23 +56,17 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Delete 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.
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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#delete
await androidmanagement.enterprises.devices.delete({
await androidManagementConnection.enterprises.devices.delete({
name: `enterprises/${androidEnterpriseId}/devices/${deviceId}`,
});
}).intercept({status: 429}, (err)=>{
@@ -50,30 +50,21 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Delete the Android enterprise from Google (if it still exists)
// Note: If the enterprise is already deleted in Google, we still want to clean up proxy database
try {
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',
'https://www.googleapis.com/auth/pubsub'
],
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});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// Delete the android enterprise.
await androidmanagement.enterprises.delete({
await androidManagementConnection.enterprises.delete({
name: `enterprises/${androidEnterpriseId}`,
});
let pubsub = google.pubsub('v1');
let pubsub = google.pubsub({version: 'v1', auth: androidManagementAuthClient});
// Delete the enterprise's pubsub topic
await pubsub.projects.topics.delete({
topic: thisAndroidEnterprise.pubsubTopicName,
+7 -12
View File
@@ -55,23 +55,18 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Get 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 getDeviceResponse = 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#get
let getDeviceResult = await androidmanagement.enterprises.devices.get({
let getDeviceResult = await androidManagementConnection.enterprises.devices.get({
name: `enterprises/${androidEnterpriseId}/devices/${deviceId}`,
});
return getDeviceResult.data;
@@ -64,28 +64,17 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// List android devices from an enterprises using the passed parameters
return 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
let authClient = await googleAuth.getClient();
google.options({auth: authClient});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// Get the Android devices list from Google
let devicesResponse = await androidmanagement.enterprises.devices.list({
let devicesResponse = await androidManagementConnection.enterprises.devices.list({
parent: `enterprises/${thisAndroidEnterprise.androidEnterpriseId}`,
pageSize: pageSize,
pageToken: pageToken,
@@ -50,31 +50,21 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Get the Android enterprises list from Google
try {
let enterprisesList = 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});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// List all enterprises accessible to this service account
let allEnterprises = [];
let tokenForNextPageOfEnterprises;
await sails.helpers.flow.until(async ()=>{
let listEnterprisesResponse = await androidmanagement.enterprises.list({
let listEnterprisesResponse = await androidManagementConnection.enterprises.list({
projectId: sails.config.custom.androidEnterpriseProjectId,
pageSize: 100,
pageToken: tokenForNextPageOfEnterprises,
@@ -55,23 +55,17 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Get 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 getApplicationsResponse = 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Applications.html#get
let getApplicationsResult = await androidmanagement.enterprises.applications.get({
let getApplicationsResult = await androidManagementConnection.enterprises.applications.get({
name: `enterprises/${androidEnterpriseId}/applications/${applicationId}`,
});
return getApplicationsResult.data;
@@ -153,23 +153,17 @@ module.exports = {
commandBody.requestDeviceInfoParams = requestDeviceInfoParams;
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Issue the command to 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 issueCommandResponse = 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#issueCommand
let response = await androidmanagement.enterprises.devices.issueCommand({
let response = await androidManagementConnection.enterprises.devices.issueCommand({
name: `enterprises/${androidEnterpriseId}/devices/${deviceId}`,
requestBody: commandBody,
});
@@ -57,23 +57,18 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Devices.html#patch
let patchDeviceResponse = await androidmanagement.enterprises.devices.patch({
let patchDeviceResponse = await androidManagementConnection.enterprises.devices.patch({
name: `enterprises/${androidEnterpriseId}/devices/${deviceId}`,
// 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.
@@ -57,23 +57,17 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Update the policy for this Android enterprise.
// Note: We're using sails.helpers.flow.build here to handle any errors that occurr using google's node library.
let modifyPoliciesResponse = 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// [?]: https://googleapis.dev/nodejs/googleapis/latest/androidmanagement/classes/Resource$Enterprises$Policies.html#patch
let patchPoliciesResponse = await androidmanagement.enterprises.policies.patch({
let patchPoliciesResponse = await androidManagementConnection.enterprises.policies.patch({
name: `enterprises/${androidEnterpriseId}/policies/${policyId}`,
// 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.
@@ -68,25 +68,20 @@ module.exports = {
throw 'unauthorized';
}
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper.
// Note: we are doing this outside of the sails.helpers.flow.build() so any errors related to the website's credentials returned by the helper are not intercepted.
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
// Update the policy applications for this Android enterprise.
// Note: We're using sails.helpers.flow.build here to handle any errors that occurr using google's node library.
let modifyApplicationPolicyResponse = 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 });
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
switch (googleAction) {
case 'removePolicyApplications': {
let response = await androidmanagement.enterprises.policies.removePolicyApplications({
let response = await androidManagementConnection.enterprises.policies.removePolicyApplications({
name: `enterprises/${androidEnterpriseId}/policies/${policyId}`,
requestBody: { packageNames },
});
@@ -94,7 +89,7 @@ module.exports = {
}
default: {
let response = await androidmanagement.enterprises.policies.modifyPolicyApplications({
let response = await androidManagementConnection.enterprises.policies.modifyPolicyApplications({
name: `enterprises/${androidEnterpriseId}/policies/${policyId}`,
requestBody: { changes },
});
@@ -0,0 +1,61 @@
module.exports = {
friendlyName: 'Get Android Management authorization client',
description: 'Returns a shared Google API auth client for the Android Management API proxy, creating it if one has not been created yet.',
moreInfoUrl: 'https://github.com/fleetdm/fleet/issues/46496',
exits: {
success: {
outputFriendlyName: 'Android Management authorization client',
outputDescription: 'The shared Google API auth client stored on `sails.androidManagementAuthorization`.',
outputType: 'ref',
},
},
fn: async function () {
require('assert')(sails.config.custom.androidEnterpriseServiceAccountEmailAddress);
require('assert')(sails.config.custom.androidEnterpriseServiceAccountPrivateKey);
// Initialize a Google API auth client for the Android Management API proxy, but only if one has not
// already been created for this server process. The googleapis library caches the OAuth2 access_token
// on a reused client and refreshes it automatically when it expires, so we build a single shared client
// per process (each web dyno is its own process) and reuse it across all Android proxy requests.
if (!sails.androidManagementAuthorization) {
let { google } = require('googleapis');
let googleAuth = new google.auth.GoogleAuth({
// The pubsub scope is included because creating/deleting an Android enterprise also provisions/removes a Pub/Sub topic and subscription.
scopes: [
'https://www.googleapis.com/auth/androidmanagement',
'https://www.googleapis.com/auth/pubsub',
],
credentials: {
client_email: sails.config.custom.androidEnterpriseServiceAccountEmailAddress,// eslint-disable-line camelcase
private_key: sails.config.custom.androidEnterpriseServiceAccountPrivateKey,// eslint-disable-line camelcase
},
});
let androidManagementAuthClient = await googleAuth.getClient();
// Mint an access token now so invalid credentials surface here instead of failing silently on the
// first Android Management API call. This only hits Google's OAuth2 token endpoint, not the Android
// Management API, so it does not count against AMAPI rate limits.
await androidManagementAuthClient.getAccessToken();
// Assign the global last, so that if either step above throws, the global is left unset and the next
// request retries instead of caching a client whose credentials never validated.
sails.androidManagementAuthorization = androidManagementAuthClient;
}
return sails.androidManagementAuthorization;
}
};
@@ -33,24 +33,18 @@ module.exports = {
let isEnterpriseManagedByFleet = false;
// Log into google.
// Get the shared Google API auth client with the getAndroidManagementAuthorizationClient helper
let androidManagementAuthClient = await sails.helpers.androidProxy.getAndroidManagementAuthorizationClient();
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
},
});
let authClient = await googleAuth.getClient();
google.options({auth: authClient});
let androidManagementConnection = google.androidmanagement({version: 'v1', auth: androidManagementAuthClient});
// Use Google's LIST call to check if enterprise exists.
let enterprises = [];
let tokenForNextPageOfEnterprises;
await sails.helpers.flow.until(async ()=>{
let listEnterprisesResponse = await androidmanagement.enterprises.list({
let listEnterprisesResponse = await androidManagementConnection.enterprises.list({
projectId: sails.config.custom.androidEnterpriseProjectId,
pageSize: 100,
pageToken: tokenForNextPageOfEnterprises,