mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 00:49:03 +00:00
Changes: - Created a new database model: `MicrosoftComplianceTenant`. A model that stores information about complaince tenants - Added `/policies/is-cloud-customer`: a policy that blocks requests to microsoft proxy endpoints if a `MS API KEY` header is missing or does not match a new config variable (`sails.custom.config.cloudCustomerCompliancePartnerSharedSecret`) - Added `microsoft-proxy/create-compliance-partner-tenant`: an action that creates a database record for a new compliance tenant and generates an API key that is used to authenticate future requests to microsoft proxy endpoints for an entra tenant. - Added `microsoft-proxy/get-compliance-partner-settings`: an action that returns information about Fleet's complaince partner entra application and the entra tenant's admin consent status (whether or not a tenant's entra admin has granted permissions to Fleet's compliance partner application) - Added `microsoft-proxy/get-tenants-admin-consent-status`: an action that updates the admin consent status of a compliance tenant record. - Added `microsoft-proxy/setup-compliance-partner-tenant`: an action that provisions a compliance tenant, creates a complaince policy for macOS devices assigns the created policy to the built-in "All users" user group on the tenants entra instance. - Added `microsoft-proxy/update-one-devices-compliance-status`: an action that receives information about a device on a compliance tenant's Fleet instance, sends that information to their Entra instance, and returns the messsage ID returned by the asynchronus Entra API. - Added `microsoft-proxy/get-one-compliance-status-result`: an action that returns the result of a compliance status update from the Entra API. - Added `sails.helpers.microsoft-proxy.get-access-token-and-api-urls` A helper that gets an access token for a tenant's entra instance and the URLs of the API endpoints the microsoft proxy actions use for a tenant. - Added `scripts/send-entra-heartbeat-requests` A script that will run daily to keep all microsoft compliance integrations provisioned. - --------- Co-authored-by: Lucas Rodriguez <[email protected]>
92 lines
4.1 KiB
JavaScript
Vendored
92 lines
4.1 KiB
JavaScript
Vendored
module.exports = {
|
|
|
|
|
|
friendlyName: 'Send entra heartbeat requests',
|
|
|
|
|
|
description: 'Sends heartbeat requests to Microsoft compliance tenants to keep the integration active.',
|
|
|
|
|
|
fn: async function () {
|
|
|
|
// Find all MicrosoftComplianceTenant records with setupComplete: true
|
|
let allActiveEntraTenants = await MicrosoftComplianceTenant.find({setupComplete: true});
|
|
|
|
sails.log('Syncing hearbeat requests for '+allActiveEntraTenants.length+(allActiveEntraTenants.length > 1 ? ' tenants.' : ' tenant.'));
|
|
|
|
// Create an empty object to store caught errors. We don't want this script to stop running if there is an error with a single entra tenant, so instead, we'll store any errors that occur and bail early for that tenant if any occur, and we'll log them individually before the script is done.
|
|
let errorReportById = {};
|
|
|
|
await sails.helpers.flow.simultaneouslyForEach(allActiveEntraTenants, async (entraTenant)=>{
|
|
let connectionIdAsString = String(entraTenant.id);
|
|
|
|
let tokenAndApiUrls = await sails.helpers.microsoftProxy.getAccessTokenAndApiUrls.with({
|
|
complianceTenantRecordId: entraTenant.id
|
|
}).tolerate((err)=>{
|
|
errorReportById[connectionIdAsString] = new Error(`Could not get an access token and API urls for a MicrosoftComplianceTenant (id: ${connectionIdAsString}). Full error: ${err}`);
|
|
});
|
|
|
|
if(errorReportById[connectionIdAsString]){// If there was an error with the previous request, bail early for this Entra tenant.
|
|
return;
|
|
}
|
|
|
|
let accessToken = tokenAndApiUrls.manageApiAccessToken;
|
|
let tenantDataSyncUrl = tokenAndApiUrls.tenantDataSyncUrl;
|
|
|
|
|
|
// Send a heartbeat request.
|
|
let tenantHeartbeatResponse = await sails.helpers.http.sendHtttpRequest.with({
|
|
method: 'PUT',
|
|
url: `${tenantDataSyncUrl}/PartnerTenantHeartbeat(guid'${encodeURIComponent(entraTenant.entraTenantId)}')?api-version=1.6`,
|
|
headers: {
|
|
'Authorization': `Bearer ${accessToken}`
|
|
},
|
|
body: {
|
|
Timestamp: new Date().toISOString(),
|
|
}
|
|
}).tolerate((err)=>{
|
|
errorReportById[connectionIdAsString] = new Error(`Could not send a heartbeat request for a MicrosoftComplianceTenant (id: ${connectionIdAsString}). Full error: ${err}`);
|
|
});
|
|
if(errorReportById[connectionIdAsString]){// If there was an error with the previous request, bail early for this Entra tenant.
|
|
return;
|
|
}
|
|
|
|
let parsedtenantHeartbeatResponse;
|
|
try {
|
|
parsedtenantHeartbeatResponse = JSON.parse(tenantHeartbeatResponse);
|
|
} catch(err){
|
|
errorReportById[connectionIdAsString] = new Error(`Could not parse the JSON response of a heartbeat request for a Microsoft compliance tenant (id: ${connectionIdAsString}). Full error: ${err}`);
|
|
}
|
|
|
|
if(errorReportById[connectionIdAsString]){// If there was an error with the previous request, bail early for this Entra tenant.
|
|
return;
|
|
}
|
|
|
|
if(parsedtenantHeartbeatResponse.ResyncTimestamp){
|
|
// TODO: do we want to do anything about the resync timestamp if it is set?
|
|
}
|
|
|
|
await MicrosoftComplianceTenant.updateOne({id: entraTenant.id}).with({
|
|
lastHeartbeatAt: Date.now(),
|
|
});
|
|
});
|
|
|
|
let numberOfLoggedErrors = 0;
|
|
|
|
// After we've sent requests for all active Entra tenants, log any errors that occured.
|
|
for (let connectionIdAsString of Object.keys(errorReportById)) {
|
|
if (false === errorReportById[connectionIdAsString]) {
|
|
// If a heartbeat was sent successfully, do nothing.
|
|
} else {
|
|
// If an error was logged for a entra tenant, log the error, and increment the numberOfLoggedErrors
|
|
numberOfLoggedErrors++;
|
|
sails.log.warn('p1: An error occurred while sending a heartbeat request for a Microsfot entra compliance tenant with the id'+connectionIdAsString+'. Logged error:\n'+errorReportById[connectionIdAsString]);
|
|
}
|
|
}//∞
|
|
|
|
sails.log('Heartbeat requests have been sent for '+(allActiveEntraTenants.length - numberOfLoggedErrors)+(allActiveEntraTenants.length - numberOfLoggedErrors > 1 || numberOfLoggedErrors === allActiveEntraTenants.length ? ' tenants.' : ' tenant.'));
|
|
}
|
|
|
|
|
|
};
|
|
|