diff --git a/Master/azcollect.js b/Master/azcollect.js index 2e5ce6f..35a2bed 100644 --- a/Master/azcollect.js +++ b/Master/azcollect.js @@ -1,13 +1,13 @@ /* ----------------------------------------------------------------------------- * @copyright (C) 2017, Alert Logic, Inc * @doc - * + * * The module for communicating with Alertlogic Azcollect service. - * + * * @end * ----------------------------------------------------------------------------- */ - + const m_alServiceC = require('../lib/al_servicec'); const m_version = require('./version'); @@ -27,7 +27,7 @@ class Azcollect extends m_alServiceC.AlServiceC { super(apiEndpoint, 'azcollect', 'v1', aimsCreds, process.env.TMP); } - + _o365RegisterBody() { let o365AuditStreams = JSON.parse(process.env.O365_CONTENT_STREAMS); let registerParams = {}; @@ -47,15 +47,16 @@ class Azcollect extends m_alServiceC.AlServiceC { }}; return Object.assign({}, commonParams , configParams); } - + register_o365() { let regBody = this._o365RegisterBody(); return this.post(`/register/o365`, {body: regBody}); } - + checkin(collectorType, collectorId, statusVal, descriptionVal) { let statusBody = { - collector_version : m_version.getVersion(), + type : collectorType, + version : m_version.getVersion(), status : statusVal, description : descriptionVal }; diff --git a/Master/endpoints.js b/Master/endpoints.js index 1514c57..2a0208e 100644 --- a/Master/endpoints.js +++ b/Master/endpoints.js @@ -16,12 +16,12 @@ const m_appSettings = require('./appsettings'); exports.checkUpdate = function (context, AlertlogicMasterTimer, callback) { if (process.env.APP_INGEST_ENDPOINT && process.env.APP_AZCOLLECT_ENDPOINT) { - context.log('DEBUG: Reuse Ingest endpoint', process.env.APP_INGEST_ENDPOINT); - context.log('DEBUG: Reuse Azcollect endpoint', process.env.APP_AZCOLLECT_ENDPOINT); + context.log.verbose('Reuse Ingest endpoint', process.env.APP_INGEST_ENDPOINT); + context.log.verbose('Reuse Azcollect endpoint', process.env.APP_AZCOLLECT_ENDPOINT); return callback(null); } else { // Endpoint settings do not exist. Update them. - let alApiEndpoint = process.env.CUSTOMCONNSTR_APP_AL_API_ENDPOINT; + let alApiEndpoint = process.env.CUSTOMCONNSTR_APP_AL_API_ENDPOINT; let alResidency = process.env.CUSTOMCONNSTR_APP_AL_RESIDENCY; let aimsCreds = { access_key_id : process.env.CUSTOMCONNSTR_APP_CI_ACCESS_KEY_ID, diff --git a/Master/index.js b/Master/index.js index 601a3e5..05b95be 100644 --- a/Master/index.js +++ b/Master/index.js @@ -28,7 +28,7 @@ module.exports = function (context, AlertlogicMasterTimer) { if (endpointsError) { return asyncCallback(endpointsError); } - context.log('INFO: Alertlogic endpoints updated.'); + context.log.info('Alertlogic endpoints updated.'); return asyncCallback(null); }); }, @@ -41,7 +41,7 @@ module.exports = function (context, AlertlogicMasterTimer) { if (azcollectError) { return asyncCallback(azcollectError); } - context.log('INFO: O365 source registered', collectorId); + context.log.info('O365 source registered', collectorId); return asyncCallback(null, azcollectSvc); }); }, @@ -52,15 +52,15 @@ module.exports = function (context, AlertlogicMasterTimer) { if (azcollectError) { return asyncCallback(`Checkin failed ${azcollectError}`); } - context.log('INFO: O365 source checkin OK', checkinResp); + context.log.info('O365 source checkin OK', checkinResp); return asyncCallback(null); }); } ], function(error, results) { if (error) { - context.log('ERROR: Master error ', error); + context.log.error('Master error ', error); } - context.done(); + context.done(error); }); }; diff --git a/Master/o365collector.js b/Master/o365collector.js index 4b3777f..1101e78 100644 --- a/Master/o365collector.js +++ b/Master/o365collector.js @@ -18,7 +18,7 @@ const m_o365mgmnt = require('../lib/o365_mgmnt'); exports.checkRegister = function (context, AlertlogicMasterTimer, azcollectSvc, callback) { if (process.env.O365_COLLECTOR_ID && process.env.O365_HOST_ID) { - context.log('DEBUG: Reuse collector id', process.env.O365_COLLECTOR_ID); + context.log.verbose('Reuse collector id', process.env.O365_COLLECTOR_ID); return callback(null, process.env.O365_COLLECTOR_ID); } else { // Collector is not registered. @@ -27,7 +27,6 @@ exports.checkRegister = function (context, AlertlogicMasterTimer, azcollectSvc, O365_COLLECTOR_ID: resp.source.id, O365_HOST_ID: resp.source.host.id }; - context.log(newSettings); m_appSettings.updateAppsettings(newSettings, function(settingsError) { if (settingsError) { @@ -44,7 +43,7 @@ exports.checkRegister = function (context, AlertlogicMasterTimer, azcollectSvc, exports.checkin = function (context, AlertlogicMasterTimer, azcollectSvc, callback) { return m_o365mgmnt.subscriptionsList( - function(listErr, subscrptions, httpRequest, response) { + function(listErr, subscriptions, httpRequest, response) { if (listErr) { azcollectSvc.checkin('o365', process.env.O365_COLLECTOR_ID, 'error', `${listErr}`) @@ -54,15 +53,14 @@ exports.checkin = function (context, AlertlogicMasterTimer, azcollectSvc, callba .catch(function(exception) { return callback(`Unable to checkin ${exception}`); }); - return callback(listErr); } else { - return _checkEnableAuditStreams(context, subscrptions, + return _checkEnableAuditStreams(context, subscriptions, function(enableErr, checkResults) { if (enableErr) { azcollectSvc.checkin('o365', process.env.O365_COLLECTOR_ID, 'error', `${enableErr}`) .then(resp => { - callback(null, resp); + return callback(null, resp); }) .catch(function(exception) { return callback(`Unable to checkin ${exception}`); @@ -71,7 +69,7 @@ exports.checkin = function (context, AlertlogicMasterTimer, azcollectSvc, callba azcollectSvc.checkin('o365', process.env.O365_COLLECTOR_ID, 'ok', `${checkResults}`) .then(resp => { - callback(null, resp); + return callback(null, resp); }) .catch(function(exception) { return callback(`Unable to checkin ${exception}`); @@ -96,7 +94,7 @@ var _checkEnableAuditStreams = function(context, listedStreams, callback) { currentStream.webhook && currentStream.webhook.status === 'enabled' && currentStream.webhook.address === webhookURL) { - context.log('DEBUG: Stream already enabled', stream); + context.log.verbose('Stream already enabled', stream); return asyncCallback(null, stream); } else { let webhook = { webhook : { diff --git a/O365WebHook/index.js b/O365WebHook/index.js index 4545426..bcc85d4 100644 --- a/O365WebHook/index.js +++ b/O365WebHook/index.js @@ -25,12 +25,12 @@ module.exports = function (context, event) { return m_o365content.processNotifications(context, eventBody, function(err) { if (err) { - context.log(`ERROR: ${err}`); + context.log.error(`${err}`); context.res.headers = {}; context.res.status = 500; - context.done(); + context.done(err); } else { - context.log('Debug: Success!'); + context.log.info('OK!'); context.done(); } }); diff --git a/O365WebHook/ingest_proto.js b/O365WebHook/ingest_proto.js index c85d5b5..7b0bd23 100644 --- a/O365WebHook/ingest_proto.js +++ b/O365WebHook/ingest_proto.js @@ -21,7 +21,7 @@ const crypto = require('crypto'); module.exports.load = function(context, callback) { protobuf.load(getCommonProtoPath(), function(err, root) { if (err) - context.log('Error: Unable to load proto files.', err); + context.log.error('Unable to load proto files.', err); callback(err, root); }); @@ -34,7 +34,7 @@ module.exports.setMessage = function(context, root, content, callback) { }, function(err, result) { if (err) - context.log('Error: Unable to build messages.'); + context.log.error('Unable to build messages.'); callback(err, result); } @@ -63,7 +63,7 @@ module.exports.setHostMetadata = function(context, root, content, callback) { build(hostmetaType, metadataPayload, function(err, buf) { if (err) - context.log('Error: Unable to build host_metadata.'); + context.log.error('Unable to build host_metadata.'); return callback(err, buf); }); @@ -81,7 +81,7 @@ module.exports.setBatch = function(context, root, metadata, messages, callback) build(batchType, batchPayload, function(err, buf) { if (err) - context.log('Error: Unable to build collected_batch.'); + context.log.error('Unable to build collected_batch.'); return callback(err, buf); }); @@ -97,7 +97,7 @@ module.exports.setBatchList = function(context, root, batches, callback) { build(batchListType, batchListPayload, function(err, buf) { if (err) - context.log('Error: Unable to build collected_batch_list.'); + context.log.error('Unable to build collected_batch_list.'); return callback(err, buf); }); @@ -148,7 +148,7 @@ function parseMessage(context, root, memo, content, callback) { build(messageType, messagePayload, function(err, buf) { if (err) - context.log('Error: Unable to build collected_message.'); + context.log.error('Unable to build collected_message.'); memo.push(buf); return callback(err, memo); diff --git a/O365WebHook/o365content.js b/O365WebHook/o365content.js index 94581c3..13ec585 100644 --- a/O365WebHook/o365content.js +++ b/O365WebHook/o365content.js @@ -83,7 +83,7 @@ function parseContent(context, parsedContent, callback) { var creationTime; if (item.CreationTime == undefined) { - context.log('WARNING: Unable to parse CreationTime from content.'); + context.log.warn('Unable to parse CreationTime from content.'); creationTime = Math.floor(Date.now() / 1000); } else { @@ -105,7 +105,7 @@ function parseContent(context, parsedContent, callback) { if (err) { return callback(`Content parsing failure. ${err}`); } else { - context.log('DEBUG: parsedData: ', result); + context.log.verbose('parsedData: ', result); return callback(null, result); } } @@ -152,6 +152,9 @@ function sendToIngest(context, content, callback) { if (err) { return callback(`Unable to compress. ${err}`); } else { + if (compressed.byteLength > 700000) + context.log.warn(`Compressed log batch length`, + `(${compressed.byteLength}) exceeds maximum allowed value.`); return g_ingestc.sendO365Data(compressed) .then(resp => { return callback(null, resp); diff --git a/PostDeploymentActions/updateMasterTimer.ps1 b/PostDeploymentActions/updateMasterTimer.ps1 new file mode 100755 index 0000000..456aa2c --- /dev/null +++ b/PostDeploymentActions/updateMasterTimer.ps1 @@ -0,0 +1,8 @@ +$date = Get-Date +$min = ($date.Minute + 1) % 15 +$sec = $date.Second +$new_schedule = "$sec $min-59/15 * * * *" +Write-Output "Updating Master timer trigger with ($new_schedule)." +$master_function = Get-Content '..\\wwwroot\\Master\\function.json' -raw | ConvertFrom-Json +$master_function.bindings | % {if($_.name -eq 'AlertlogicMasterTimer'){$_.schedule=$new_schedule}} +$master_function | ConvertTo-Json | set-content '..\\wwwroot\\Master\\function.json' \ No newline at end of file diff --git a/PostDeploymentActions/updateUpdaterTimer.ps1 b/PostDeploymentActions/updateUpdaterTimer.ps1 new file mode 100755 index 0000000..af874c4 --- /dev/null +++ b/PostDeploymentActions/updateUpdaterTimer.ps1 @@ -0,0 +1,9 @@ +$randH = Get-Random -minimum 0 -maximum 11 +$randM = Get-Random -minimum 0 -maximum 59 +$randS = Get-Random -minimum 0 -maximum 59 +$randH12 = $randH + 12 +$new_schedule = "$randS $randM $randH,$randH12 * * *" +Write-Output "Updating Updater timer trigger with ($new_schedule)". +$master_function = Get-Content '..\\wwwroot\\Updater\\function.json' -raw | ConvertFrom-Json +$master_function.bindings | % {if($_.name -eq 'AlertlogicUpdaterTimer'){$_.schedule=$new_schedule}} +$master_function | ConvertTo-Json | set-content '..\\wwwroot\\Updater\\function.json' \ No newline at end of file diff --git a/README.md b/README.md index fce4f29..75435cd 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ Alert Logic Office 365 Log Collector # Overview -This repo contains Azure Web application Node.js source code and an ARM template for setting up a data collector in Azure which will collect and forward Office 365 log data to the Alert Logic Cloud Defender Log Manager (LM) feature. +This repository contains Azure Web application Node.js source code and an ARM template for setting up a data collector in Azure which will collect and forward Office 365 log data to the Alert Logic Cloud Defender Log Manager (LM) feature. # Installation Installation requires the following steps: 1. Register a new O365 web application in O365 portal for collecting O365 logs. -1. Set up the required Active Directory security permissions for the application to authorize it to read threat intelligence data and activity reports for your orgaization. +1. Set up the required Active Directory security permissions for the application to authorize it to read threat intelligence data and activity reports for your organization. 1. Create an Access Key that will allow the application to connect to the Alert Logic Cloud Defender and Cloud Insight backend. 1. Download and deploy a custom ARM template to Microsoft Azure to create functions for collecting and managing O365 log data 1. Verify that installation was successful using Alertlogic CloudDefender UI. @@ -37,18 +37,18 @@ In order to install O365 Log collector: 1. On the `Settings` panel and select `Required permissions` and click `+Add` 1. Hit `Select an API` and chose `Office 365 Management APIs`, click `Select` 1. In `Application permissions` select `Read service health information for your organization`, `Read activity data for your organization`, `Read threat intelligence data for your organization` and `Read activity reports for your organization`. Click `Select` and `Done` buttons. -1. On `Required permissions` panel click `Required permissions` button and confirm the selection. **Note**, only AD tenant admin can grant permisions to an Azure AD application. +1. On `Required permissions` panel click `Required permissions` button and confirm the selection. **Note**, only AD tenant admin can grant permissions to an Azure AD application. 1. On the `Settings` panel of the application and select `Keys`. 1. Enter key `Description` and `Duration` and click `Save`. **Note**, please save the key value, it is needed later during template deployment. 1. Save the `Application ID` and `Service Principal Id` for use below. To get the `Service Principal Id`, navigate to the `Registered App` blade, click on the link under `Managed application in local directory`. Then click `Properties`. The `Service Principal Id` -is labled `Object ID` on the properties page. **Caution** This is not the same `Object ID` listed in the `Properties` blade reached +is labeled `Object ID` on the properties page. **Caution** This is not the same `Object ID` listed in the `Properties` blade reached by clicking `Settings` or `All Settings` from the `Registered app`. It is also not the `Object ID` shown on the `Registered app` blade itself. ## Create an Alert Logic Access Key -Login and get an authentication token from the Alert Logic Cloud Insight product [AIMS API](https://console.product.dev.alertlogic.com/api/aims/). For example, from the command line use [curl](https://en.wikipedia.org/wiki/CURL) as follows (where `` is your CloudInsight user and `` is your CloudInsight password): +Login and get an authentication token from the Alert Logic Cloud Insight product [AIMS API](https://console.product.dev.alertlogic.com/api/aims/). From the command line use [curl](https://en.wikipedia.org/wiki/CURL) as follows (where `` is your CloudInsight user and `` is your CloudInsight password): ``` curl -X POST -v -u ':' https://api.global-services.global.alertlogic.com/aims/v1/authenticate @@ -60,7 +60,7 @@ Make a note of the following fields returned in the response: * ACCOUNT ID * TOKEN -Use the authentication token returned in the response to create access keys for the Azure application deployed in the next section. For example, issue the following curl command (where `` is the auth token, `` is the account id, and `` is the user id returned above): +Use the authentication token returned in the response to create access keys for the Azure application deployed in the next section. Issue the following curl command (where `` is the auth token, `` is the account id, and `` is the user id returned above): ``` curl -X POST -H "x-aims-auth-token: " https://api.global-services.global.alertlogic.com/aims/v1//users//access_keys @@ -83,11 +83,12 @@ curl -X POST -H "x-aims-auth-token: " https://api.global-services.global. ## Function deployment +Log into [Azure portal](https://portal.azure.com). **Note**, In order to perform steps below you should have an active Azure subscription, to find out visit [Azure subscriptions blade](https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade) + ### Deploy via the Custom ARM Template in an Azure Subscription -1. **TODO: it is possible to use URI deployment without downloading a file.** Download an ARM [template](https://github.com/alertlogic/azure-collector/raw/master/template.json) -1. Log into [Azure portal](https://portal.azure.com). **Note**, In order to perform steps below you should have an acive Azure subscription, to find out visit [Azure subscriptions blade](https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade) -1. Go to [Customer Deployment](https://portal.azure.com/#create/Microsoft.Template) page. Type in `deploy` in a seach query located on top of Azure Web UI and select `Deploy a custom template`. +1. Download an ARM [template](https://github.com/alertlogic/azure-collector/raw/master/template.json) +1. Go to [Customer Deployment](https://portal.azure.com/#create/Microsoft.Template) page. Type in `deploy` in a search query located on top of Azure Web UI and select `Deploy a custom template`. 1. Click `Build your own template in the editor` and load the file previously downloaded on step 1 above. 1. Click `Save` button. 2. Fill in required template parameters and click the `Purchase` button to start a deployment. I.e.: @@ -133,13 +134,13 @@ Wait until it is deployed successfully. ## Verify the Installation 1. Go to `Azure Apps` and choose your function. The last log under `Functions-> Master-> Monitor` should have OK status and should not contain any error messages. -1. Log into Alertlogic CloudDefender and navigate into `Log Manager -> Sources` page. Check new O365 log source (with a name provided on step 15) has been created and source status is `ok`. +1. Log into Alertlogic CloudDefender and navigate into `Log Manager -> Sources` page. Check new O365 log source (with a name provided during `az group deployment create` above) has been created and source status is `ok`. # How It Works ## Master Function -The `Master` function is a timer trigger function which is responcible for: +The `Master` function is a timer trigger function which is responsible for: - registering the Azure web app In Alertlogic backend; - reporting health-checks to the backed; - performing log source configuration updates, which happen via Alertlogic UI. @@ -189,13 +190,13 @@ A notification contains a link to the actual data which is retrieved by the `Col 1. Run Master function locally: `npm run local-master` 1. Run Updater function locally: `npm run local-updater` 1. Run O365WebHook function locally: `npm run local-o365webhook` -1. Run `npm test` in order to perform code analisys. +1. Run `npm test` in order to perform code analysis and unit tests. Please use the following [code style](https://github.com/airbnb/javascript) as much as possible. ## Setting environment in dev_config.js -- `process.env.APP_TENANT_ID` - The GUID of the tenant ie. 'alazurealertlogic.onmicrosoft.com' +- `process.env.APP_TENANT_ID` - The GUID of the tenant i.e. 'alazurealertlogic.onmicrosoft.com' - `process.env.CUSTOMCONNSTR_APP_CLIENT_ID` - The GUID of your application that created the subscription. You can obtain it from _Azure_ -> _AD_ -> _App registrations_ -> _Your app name_ - `process.env.CUSTOMCONNSTR_APP_CLIENT_SECRET` - A secret key of your application from _App Registrations_. diff --git a/Updater/index.js b/Updater/index.js index f60c85f..c31c556 100644 --- a/Updater/index.js +++ b/Updater/index.js @@ -23,16 +23,16 @@ module.exports = function (context, AlertlogicUpdaterTimer) { requestNewToken(context, creds, function(tokenError, adToken) { if (tokenError) { - context.log('Error getting AD token: ', + context.log.error('Error getting AD token: ', tokenError.statusCode, tokenError.statusMessage); - context.done(); + context.done(tokenError); } else { siteSync(context, adToken, function(syncError) { if (syncError) { - context.log('Site sync failed: ', syncError); - context.done(); + context.log.error('Site sync failed: ', syncError); + context.done(syncError); } else { - context.log('Site sync OK'); + context.log.info('Site sync OK'); context.done(); } }); diff --git a/azure_doc.md b/azure_doc.md deleted file mode 100644 index 940327a..0000000 --- a/azure_doc.md +++ /dev/null @@ -1,32 +0,0 @@ -# Azure unofficial doc -This document should contain clarification of official documentation and behavior of Azure in practice. - -## Office 365 Management Activity API reference -The documentation about [Office 365 API](https://msdn.microsoft.com/en-us/office-365/office-365-management-activity-api-reference) -contains several either mistakes or behavior we could not observer. - -### Notification failure and retry -The notification shall retry delivery in case of failure. If it encounter excessive failures it shall increase the time between retries. -That may potentially lead to disabling the webhook. However we could not observer any message redelivery neither disabling the webhook. -The webhook was invoked 8 times and contained 19 messages (contents) in total. - -### Endpoint with startTime and endTime -Either both must be present or omitted and the difference between each of them can not be larger than 24h. It is incorrect and it doesn't -work with both parameters. It worked with `startTime` only and the difference between `startTime` and now can be more than 24h. - - -## Azure Function - -### Return status -There should be at least three ways how to specify return status (code) according this -(doc)[https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node]. However the only way which works is: -``` -context.res.headers = {}; -context.res.status = 500; -``` - -### Status in Invocation log -The status (either red cross or green tick) most likely indicates whether the function crashes or finishes with `context.done()`. It doesn't -reflect return status code. - - diff --git a/local_dev/master_local_dev.js b/local_dev/master_local_dev.js index eee3bca..6abd5b4 100644 --- a/local_dev/master_local_dev.js +++ b/local_dev/master_local_dev.js @@ -1,3 +1,5 @@ +const util = require('util'); + var devConfig = require('./dev_config'); var azureFunction = require('../Master/index'); @@ -39,10 +41,19 @@ var debugContext = { bindings: { req }, - log: function () { - var util = require('util'); - var val = util.format.apply(null, arguments); - console.log(val); + log: { + error : function() { + return console.log('ERROR:', util.format.apply(null, arguments)); + }, + warn : function() { + return console.log('WARNING:', util.format.apply(null, arguments)); + }, + info : function() { + return console.log('INFO:', util.format.apply(null, arguments)); + }, + verbose : function() { + return console.log('VERBOSE:', util.format.apply(null, arguments)); + } }, done: function () { console.log('Response:', this.res); diff --git a/local_dev/o365webhook_local_dev.js b/local_dev/o365webhook_local_dev.js index 44eb5ad..30478bf 100644 --- a/local_dev/o365webhook_local_dev.js +++ b/local_dev/o365webhook_local_dev.js @@ -1,3 +1,5 @@ +const util = require('util'); + var devConfig = require('./dev_config'); var azureFunction = require('../O365WebHook/index'); @@ -31,10 +33,19 @@ var debugContext = { bindings: { req }, - log: function () { - var util = require('util'); - var val = util.format.apply(null, arguments); - console.log(val); + log: { + error : function() { + return console.log('ERROR:', util.format.apply(null, arguments)); + }, + warn : function() { + return console.log('WARNING:', util.format.apply(null, arguments)); + }, + info : function() { + return console.log('INFO:', util.format.apply(null, arguments)); + }, + verbose : function() { + return console.log('VERBOSE:', util.format.apply(null, arguments)); + } }, done: function () { console.log('Response:', this.res); @@ -58,15 +69,6 @@ var debugEvent = { // "contentCreated": "2017-08-13T23:03:56.050Z", // "notificationSent": "2017-08-13T23:03:56.050Z", // "contentExpiration": "2017-08-20T16:44:49.279Z" - // }, - // { - // "contentType": "Audit.AzureActiveDirectory", - // "contentId": "20170815224757748004716$20170815224757748004716$audit_azureactivedirectory$Audit_AzureActiveDirectory$IsFromNotification", - // "contentUri": "https://manage.office.com/api/v1.0/bf8d32d3-1c13-4487-af02-80dba2236485/activity/feed/audit/20170815224757748004716$20170815224757748004716$audit_azureactivedirectory$Audit_AzureActiveDirectory$IsFromNotification", - // "notificationStatus": "Failed", - // "contentCreated": "2017-08-17T12:31:25.653Z", - // "notificationSent": "2017-08-17T12:31:25.653Z", - // "contentExpiration": "2017-08-22T22:47:57.748Z" // } ] }; diff --git a/local_dev/updater_local_dev.js b/local_dev/updater_local_dev.js index 3581890..c31d407 100644 --- a/local_dev/updater_local_dev.js +++ b/local_dev/updater_local_dev.js @@ -1,3 +1,5 @@ +const util = require('util'); + var devConfig = require('./dev_config'); var azureFunction = require('../Updater/index'); @@ -39,10 +41,19 @@ var debugContext = { bindings: { req }, - log: function () { - var util = require('util'); - var val = util.format.apply(null, arguments); - console.log(val); + log: { + error : function() { + return console.log('ERROR:', util.format.apply(null, arguments)); + }, + warn : function() { + return console.log('WARNING:', util.format.apply(null, arguments)); + }, + info : function() { + return console.log('INFO:', util.format.apply(null, arguments)); + }, + verbose : function() { + return console.log('VERBOSE:', util.format.apply(null, arguments)); + } }, done: function () { console.log('Response:', this.res); diff --git a/package.json b/package.json index 763d48d..6d5dc43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azure_collector", - "version": "0.0.1", + "version": "1.0.3", "dependencies": { "async": "*", "azure": "^2.0.0-preview", diff --git a/template.json b/template.json index 54d8382..ac94579 100644 --- a/template.json +++ b/template.json @@ -91,6 +91,10 @@ "name": "SCM_USE_FUNCPACK", "value": "1" }, + { + "name": "SCM_POST_DEPLOYMENT_ACTIONS_PATH", + "value": "PostDeploymentActions" + }, { "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('Storage Name'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('Storage Name')), '2015-06-15').key1)]" diff --git a/test/master_appsettings_test.js b/test/master_appsettings_test.js new file mode 100644 index 0000000..cc80bf4 --- /dev/null +++ b/test/master_appsettings_test.js @@ -0,0 +1,116 @@ +/* ----------------------------------------------------------------------------- + * @copyright (C) 2017, Alert Logic, Inc + * @doc + * + * Unit tests for Master function + * + * @end + * ----------------------------------------------------------------------------- + */ + +var testMock = require('./mock'); + +var assert = require('assert'); +var rewire = require('rewire'); +var sinon = require('sinon'); + +var m_appsettings = rewire('../Master/appsettings'); + +describe('Master Function appsettings.js Units', function() { + var private_appAdCreds; + var private_websiteClient; + var msListApplicationSettingsStub; + var msUpdateApplicationSettingsStub; + + before(function() { + private_appAdCreds = m_appsettings.__get__('g_appAdCreds'); + private_websiteClient = m_appsettings.__get__('g_websiteClient'); + + retrieveTokenFromCacheStub = sinon.stub(private_appAdCreds, '_retrieveTokenFromCache').callsFake( + function fakeFn(callback) { + var mockToken = { + 'tokenType' : 'Bearer', + 'expiresIn' : 3599, + 'expiresOn': '2017-09-26T11:34:40.703Z', + 'resource' : 'https://management.azure.com', + 'accessToken' : 'some-token', + 'isMRRT' : true, + '_clientId' : process.env.CUSTOMCONNSTR_APP_CLIENT_ID, + '_authority' :' https://login.microsoftonline.com/' + process.env.APP_TENANT_ID + }; + return callback(null, mockToken); + }); + msListApplicationSettingsStub = sinon.stub(private_websiteClient.webApps, 'listApplicationSettings').callsFake( + function fakeFn(rgName, name, options, callback) { + var mockSettings = { + properties : { + MOCK_SETTING : 'mock-value' + } + }; + return callback(null, mockSettings, null, null); + }); + msUpdateApplicationSettingsStub = sinon.stub(private_websiteClient.webApps, 'updateApplicationSettings').callsFake( + function fakeFn(rgName, name, settings, options, callback) { + return callback(null, null, null, null); + }); + }); + after(function() { + msListApplicationSettingsStub.restore(); + msUpdateApplicationSettingsStub.restore(); + retrieveTokenFromCacheStub.restore(); + }); + beforeEach(function() { + msListApplicationSettingsStub.resetHistory(); + msUpdateApplicationSettingsStub.resetHistory(); + }); + + describe('Azure web application setting tests', function() { + it('checks getAppsettings()', function(done) { + m_appsettings.getAppsettings(function(err, settings){ + if (err) + return done(err); + + sinon.assert.callCount(msListApplicationSettingsStub, 1); + done(); + }); + }); + + it('checks setAppsettings()', function(done) { + var testSettings = { + test : 'test' + }; + m_appsettings.setAppsettings(testSettings, function(err, settings) { + if (err) + return done(err); + + sinon.assert.callCount(msUpdateApplicationSettingsStub, 1); + done(); + }); + }); + + it('checks updateAppsettings()', function(done) { + var testSettings = { + test : 'test' + }; + m_appsettings.updateAppsettings(testSettings, function(err, settings) { + if (err) + return done(err); + + var expectedSettings = { + properties : { + MOCK_SETTING : 'mock-value', + test : 'test' + } + }; + sinon.assert.callCount(msListApplicationSettingsStub, 1); + sinon.assert.callCount(msUpdateApplicationSettingsStub, 1); + sinon.assert.calledWith(msUpdateApplicationSettingsStub, + process.env.APP_RESOURCE_GROUP, + process.env.WEBSITE_SITE_NAME, + expectedSettings, + null); + done(); + }); + }); + }); +}); diff --git a/test/master_endpoints_test.js b/test/master_endpoints_test.js new file mode 100644 index 0000000..de0894c --- /dev/null +++ b/test/master_endpoints_test.js @@ -0,0 +1,87 @@ +/* ----------------------------------------------------------------------------- + * @copyright (C) 2017, Alert Logic, Inc + * @doc + * + * Unit tests for Master function + * + * @end + * ----------------------------------------------------------------------------- + */ + +var assert = require('assert'); +var sinon = require('sinon'); + +var testMock = require('./mock'); + +var m_endpoints = require('../Master/endpoints'); +var m_appsettings = require('../Master/appsettings'); + +describe('Master Function endpoints.js Units', function() { + var updateSettingsStub = null; + var endpointsStub = null; + + before(function() { + var residency = process.env.CUSTOMCONNSTR_APP_AL_RESIDENCY; + + updateSettingsStub = sinon.stub(m_appsettings, 'updateAppsettings').callsFake( + function fakeFn(settings, callback) { + return callback(null, settings); + }); + endpointsStub = sinon.stub(m_endpoints.Endpoints.prototype, 'getEndpoint'); + endpointsStub.withArgs('ingest', residency).resolves({ + ingest : 'new-ingest-endpoint' + }); + endpointsStub.withArgs('azcollect', residency).resolves({ + azcollect : 'new-azcollect-endpoint' + }); + }); + after(function() { + if (updateSettingsStub) updateSettingsStub.restore(); + if (endpointsStub) endpointsStub.restore(); + }); + beforeEach(function() { + if (updateSettingsStub) updateSettingsStub.resetHistory(); + if (endpointsStub) endpointsStub.resetHistory(); + }); + + describe('AL endpoints retrieval tests', function() { + it('checks endpoints values are reused if already fetched', function(done) { + process.env.APP_INGEST_ENDPOINT = 'existing-ingest-endpoint'; + process.env.APP_AZCOLLECT_ENDPOINT = 'existing-azcollect-endpoint'; + m_endpoints.checkUpdate(testMock.context, testMock.timer, + function(err){ + if (err) { + return done(err); + } else { + sinon.assert.callCount(endpointsStub, 0); + sinon.assert.callCount(updateSettingsStub, 0); + return done(); + } + }); + }); + + it('checks endpoints values are saved as app settings', function(done) { + process.env.APP_INGEST_ENDPOINT = null; + process.env.APP_AZCOLLECT_ENDPOINT = null; + var residency = process.env.CUSTOMCONNSTR_APP_AL_RESIDENCY; + + m_endpoints.checkUpdate(testMock.context, testMock.timer, + function(err){ + if (err) { + return done(err); + } else { + var expectedSettings = { + APP_INGEST_ENDPOINT : 'new-ingest-endpoint', + APP_AZCOLLECT_ENDPOINT : 'new-azcollect-endpoint' + }; + sinon.assert.callCount(endpointsStub, 2); + sinon.assert.calledWith(endpointsStub, 'ingest', residency); + sinon.assert.calledWith(endpointsStub, 'azcollect', residency); + sinon.assert.callCount(updateSettingsStub, 1); + sinon.assert.calledWith(updateSettingsStub, expectedSettings); + return done(); + } + }); + }); + }); +}); diff --git a/test/master_o365collector_test.js b/test/master_o365collector_test.js new file mode 100644 index 0000000..a9f0e3e --- /dev/null +++ b/test/master_o365collector_test.js @@ -0,0 +1,414 @@ +/* ----------------------------------------------------------------------------- + * @copyright (C) 2017, Alert Logic, Inc + * @doc + * + * Unit tests for Master function + * + * @end + * ----------------------------------------------------------------------------- + */ + +var assert = require('assert'); +var rewire = require('rewire'); +var sinon = require('sinon'); + +var testMock = require('./mock'); +var m_o365mgmnt = require('../lib/o365_mgmnt'); + +var m_azcollect = require('../Master/azcollect'); +var m_o365collector = rewire('../Master/o365collector'); +var m_appsettings = require('../Master/appsettings'); + +describe('Master Function o365collector.js Units', function() { + var private_checkEnableAuditStreams; + var msSubscriptionsStartStub; + var updateSettingsStub = null; + + before(function() { + private_checkEnableAuditStreams = m_o365collector.__get__('_checkEnableAuditStreams'); + msSubscriptionsStartStub = sinon.stub(m_o365mgmnt, 'subscriptionsStart').callsFake( + function fakeFn(contentType, webhook, callback) { + return callback(null); + }); + }); + after(function() { + msSubscriptionsStartStub.restore(); + }); + beforeEach(function() { + msSubscriptionsStartStub.resetHistory(); + }); + afterEach(function() { + if (updateSettingsStub) updateSettingsStub.restore(); + }); + + describe('_checkEnableAuditStreams()', function() { + it('enables configured streams', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.General"]'; + private_checkEnableAuditStreams(testMock.context, [], function(err, streams){ + if (err) + return done(err); + + sinon.assert.callCount(msSubscriptionsStartStub, 3); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.AzureActiveDirectory"); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.Exchange"); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.General"); + return done(); + }); + }); + + it('checks already enabled streams with proper webhook configs', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.SharePoint", "Audit.General"]'; + private_checkEnableAuditStreams(testMock.context, testMock.allEnabledStreams, function(err, streams){ + if (err) + return done(err); + + assert.equal(4, streams.length); + sinon.assert.callCount(msSubscriptionsStartStub, 0); + return done(); + }); + }); + + it('checks subscriptionStart is called for already enabled webhooks if web app is being reinstalled', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.SharePoint", "Audit.General"]'; + var twoOldEnabledStreams = [ + { + "contentType": "Audit.AzureActiveDirectory", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://old-app.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.Exchange", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://old-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.General", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.SharePoint", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + } + ]; + private_checkEnableAuditStreams(testMock.context, twoOldEnabledStreams, function(err, streams){ + if (err) + return done(err); + + sinon.assert.callCount(msSubscriptionsStartStub, 2); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.AzureActiveDirectory"); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.Exchange"); + return done(); + }); + }); + + it('checks subscriptionStart is called for disabled streams', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.General"]'; + var disabledStreams = [ + { + "contentType": "Audit.AzureActiveDirectory", + "status": "disabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.General", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + } + ]; + private_checkEnableAuditStreams(testMock.context, disabledStreams, function(err, streams){ + if (err) + return done(err); + + sinon.assert.callCount(msSubscriptionsStartStub, 1); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.AzureActiveDirectory"); + return done(); + }); + }); + + it('checks subscriptionStart is called for disabled webhooks', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.General"]'; + var disabledStreams = [ + { + "contentType": "Audit.AzureActiveDirectory", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.General", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "disabled" + } + } + ]; + private_checkEnableAuditStreams(testMock.context, disabledStreams, function(err, streams){ + if (err) + return done(err); + + sinon.assert.callCount(msSubscriptionsStartStub, 1); + sinon.assert.calledWith(msSubscriptionsStartStub, "Audit.General"); + return done(); + }); + }); + }); + + describe('O365 collector checkin tests', function() { + it('checks successfull OK checkin', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.General"]'; + var enabledStreams = [ + { + "contentType": "Audit.AzureActiveDirectory", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.General", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "disabled" + } + } + ]; + var msSubscriptionsListStub = sinon.stub(m_o365mgmnt, 'subscriptionsList').callsFake( + function fakeFn(callback) { + return callback(null, enabledStreams, null, null); + }); + var azcollectSvc = new m_azcollect.Azcollect('api-endpoint', 'creds'); + sinon.stub(azcollectSvc, 'checkin').resolves([{}]); + + m_o365collector.checkin(testMock.context, testMock.timer, azcollectSvc, + function(err, resp){ + if (err) { + msSubscriptionsListStub.restore(); + return done(err); + } else { + sinon.assert.callCount(azcollectSvc.checkin, 1); + sinon.assert.calledWith(azcollectSvc.checkin, + 'o365', process.env.O365_COLLECTOR_ID, 'ok'); + msSubscriptionsListStub.restore(); + return done(); + } + }); + }); + + it('checks successfull Error checkin during Office subscriptionList error', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.General"]'; + var enabledStreams = [ + { + "contentType": "Audit.AzureActiveDirectory", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.General", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "disabled" + } + } + ]; + var listError = 'Office subscriptionList error'; + var msSubscriptionsListStub = sinon.stub(m_o365mgmnt, 'subscriptionsList').callsFake( + function fakeFn(callback) { + return callback(listError); + }); + var azcollectSvc = new m_azcollect.Azcollect('api-endpoint', 'creds'); + sinon.stub(azcollectSvc, 'checkin').resolves([{}]); + + m_o365collector.checkin(testMock.context, testMock.timer, azcollectSvc, + function(err, resp){ + if (err) { + msSubscriptionsListStub.restore(); + return done(err); + } else { + sinon.assert.callCount(azcollectSvc.checkin, 1); + sinon.assert.calledWith(azcollectSvc.checkin, + 'o365', process.env.O365_COLLECTOR_ID, 'error', listError); + msSubscriptionsListStub.restore(); + return done(null); + } + }); + }); + + it('checks successfull Error checkin during Office subscriptionStart error', function(done) { + process.env.O365_CONTENT_STREAMS = + '["Audit.AzureActiveDirectory", "Audit.General"]'; + var enabledStreams = [ + { + "contentType": "Audit.AzureActiveDirectory", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "enabled" + } + }, + { + "contentType": "Audit.General", + "status": "enabled", + "webhook": { + "authId": null, + "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "expiration": "", + "status": "disabled" + } + } + ]; + var startError = 'Office subscriptionStart error'; + var msSubscriptionsListStub = sinon.stub(m_o365mgmnt, 'subscriptionsList').callsFake( + function fakeFn(callback) { + return callback(null, enabledStreams, null, null); + }); + msSubscriptionsStartStub.restore(); + var msSubscriptionsStartErrorStub = sinon.stub(m_o365mgmnt, 'subscriptionsStart').callsFake( + function fakeFn(contentType, webhook, callback) { + return callback(startError); + }); + var azcollectSvc = new m_azcollect.Azcollect('api-endpoint', 'creds'); + sinon.stub(azcollectSvc, 'checkin').resolves([{}]); + + m_o365collector.checkin(testMock.context, testMock.timer, azcollectSvc, + function(err, resp){ + if (err) { + msSubscriptionsListStub.restore(); + msSubscriptionsStartErrorStub.restore(); + return done(err); + } else { + sinon.assert.callCount(azcollectSvc.checkin, 1); + sinon.assert.calledWith(azcollectSvc.checkin, + 'o365', process.env.O365_COLLECTOR_ID, 'error', startError); + msSubscriptionsListStub.restore(); + msSubscriptionsStartErrorStub.restore(); + return done(null); + } + }); + }); + }); + + describe('O365 collector register tests', function() { + it('checks collector and host id are reused if already registered', function(done) { + process.env.O365_COLLECTOR_ID = 'existing-collector-id'; + process.env.O365_HOST_ID = 'existing-collector-id'; + updateSettingsStub = sinon.stub(m_appsettings, 'updateAppsettings').callsFake( + function fakeFn(settings, callback) { + return callback(null, settings); + }); + var azcollectSvc = new m_azcollect.Azcollect('api-endpoint', 'creds'); + sinon.stub(azcollectSvc, 'register_o365').resolves({ + source : { + id : 'new-source-id', + host : { + id : 'new-host-id' + } + } + }); + + m_o365collector.checkRegister(testMock.context, testMock.timer, azcollectSvc, + function(err, resp){ + if (err) { + return done(err); + } else { + sinon.assert.callCount(updateSettingsStub, 0); + sinon.assert.callCount(azcollectSvc.register_o365, 0); + return done(); + } + }); + }); + + it('checks updateSettings is called during registration', function(done) { + process.env.O365_COLLECTOR_ID = null; + updateSettingsStub = sinon.stub(m_appsettings, 'updateAppsettings').callsFake( + function fakeFn(settings, callback) { + return callback(null, settings); + }); + var azcollectSvc = new m_azcollect.Azcollect('api-endpoint', 'creds'); + sinon.stub(azcollectSvc, 'register_o365').resolves({ + source : { + id : 'new-source-id', + host : { + id : 'new-host-id' + } + } + }); + + m_o365collector.checkRegister(testMock.context, testMock.timer, azcollectSvc, + function(err, resp){ + if (err) { + return done(err); + } else { + var expectedSettings = { + O365_COLLECTOR_ID: 'new-source-id', + O365_HOST_ID: 'new-host-id' + }; + sinon.assert.callCount(updateSettingsStub, 1); + sinon.assert.calledWith(updateSettingsStub, expectedSettings); + return done(); + } + }); + }); + }); +}); diff --git a/test/master_test.js b/test/master_test.js deleted file mode 100644 index 9839161..0000000 --- a/test/master_test.js +++ /dev/null @@ -1,61 +0,0 @@ -/* ----------------------------------------------------------------------------- - * @copyright (C) 2017, Alert Logic, Inc - * @doc - * - * Unit tests for Master function - * - * @end - * ----------------------------------------------------------------------------- - */ - -var assert = require('assert'); -var rewire = require('rewire'); -var sinon = require('sinon'); - -var testMock = require('./mock'); -var m_o365mgmnt = require('../lib/o365_mgmnt'); - -var o365collector = rewire('../Master/o365collector'); - -describe('Master Function Units', function() { - var private_checkEnableAuditStreams; - var subscriptionsStartStub; - - before(function() { - private_checkEnableAuditStreams = o365collector.__get__('_checkEnableAuditStreams'); - subscriptionsStartStub = sinon.stub(m_o365mgmnt, 'subscriptionsStart').callsFake( - function fakeFn(contentType, webhook, callback) { - return callback(null); - }); - }); - after(function() { - subscriptionsStartStub.restore(); - }); - - describe('_checkEnableAuditStreams()', function() { - it('checks already enabled streams with proper webhook configs', function(done) { - process.env.O365_CONTENT_STREAMS = - '["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.SharePoint", "Audit.General"]'; - private_checkEnableAuditStreams(testMock.context, testMock.allEnabledStreams, function(err, streams){ - if (err) - return done(err); - - assert.equal(4, streams.length); - assert.equal(0, subscriptionsStartStub.callCount); - done(); - }); - }); - - it('checks subscriptionStart is called for already enabled webhooks if web app is being reinstalled', function(done) { - process.env.O365_CONTENT_STREAMS = - '["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.SharePoint", "Audit.General"]'; - private_checkEnableAuditStreams(testMock.context, testMock.oneOldEnabledStream, function(err, streams){ - if (err) - return done(err); - - assert.equal(1, subscriptionsStartStub.callCount); - done(); - }); - }); - }); -}); diff --git a/test/mock.js b/test/mock.js index 37aa181..3430aad 100644 --- a/test/mock.js +++ b/test/mock.js @@ -7,23 +7,45 @@ * @end * ----------------------------------------------------------------------------- */ + +const util = require('util'); +const fs = require('fs'); +const m_alUtil = require('../lib/al_util'); + process.env.WEBSITE_HOSTNAME = 'kkuzmin-app-o365.azurewebsites.net'; +process.env.WEBSITE_SITE_NAME = 'kkuzmin-app-o365.azurewebsites.net'; process.env.O365_CONTENT_STREAMS = '["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.SharePoint", "Audit.General"]'; process.env.TMP = '/tmp/'; process.env.APP_SUBSCRIPTION_ID = 'subscription-id'; process.env.CUSTOMCONNSTR_APP_CLIENT_ID = 'client-id'; process.env.CUSTOMCONNSTR_APP_CLIENT_SECRET = 'client-secret'; +process.env.CUSTOMCONNSTR_APP_CI_ACCESS_KEY_ID = 'ci-access-key-id'; +process.env.CUSTOMCONNSTR_APP_CI_SECRET_KEY = 'ci-secret-key'; process.env.APP_TENANT_ID = 'test.onmicrosoft.com'; process.env.O365_TENANT_ID = 'test.onmicrosoft.com'; +process.env.APP_RESOURCE_GROUP = 'resource-group'; +process.env.O365_COLLECTOR_ID = 'o365-collector-id'; +process.env.O365_HOST_ID = 'o365-host-id'; +process.env.CUSTOMCONNSTR_APP_AL_RESIDENCY = 'default'; +process.env.CUSTOMCONNSTR_APP_AL_API_ENDPOINT = 'al-api-endpoint'; var context = { invocationId: 'ID', bindings: { }, - log: function () { - var util = require('util'); - var val = util.format.apply(null, arguments); - console.log(val); + log: { + error : function() { + return console.log('ERROR:', util.format.apply(null, arguments)); + }, + warn : function() { + return console.log('WARNING:', util.format.apply(null, arguments)); + }, + info : function() { + return console.log('INFO:', util.format.apply(null, arguments)); + }, + verbose : function() { + return console.log('VERBOSE:', util.format.apply(null, arguments)); + } }, done: function () { console.log('Test response:'); @@ -74,7 +96,7 @@ var allEnabledStreams = [ } ]; -var oneOldEnabledStream = [ +var twoOldEnabledStreams = [ { "contentType": "Audit.AzureActiveDirectory", "status": "enabled", @@ -90,7 +112,7 @@ var oneOldEnabledStream = [ "status": "enabled", "webhook": { "authId": null, - "address": "https://kkuzmin-app-o365.azurewebsites.net/api/o365/webhook", + "address": "https://old-app-o365.azurewebsites.net/api/o365/webhook", "expiration": "", "status": "enabled" } @@ -117,6 +139,14 @@ var oneOldEnabledStream = [ } ]; -exports.allEnabledStreams = allEnabledStreams; -exports.oneOldEnabledStream = oneOldEnabledStream; -exports.context = context; +var timer = { + isPastDue: false, + last: '2017-08-03T13:30:00', + next: '2017-08-03T13:45:00' +}; + +module.exports = { + allEnabledStreams : allEnabledStreams, + context : context, + timer : timer +};