172 lines
7.6 KiB
JavaScript
172 lines
7.6 KiB
JavaScript
/* Globals */
|
|
var DEZE; // context voor alle globale functies vanuit ASP
|
|
var __Log;
|
|
var __DoLog;
|
|
var _AiAi;
|
|
var safe;
|
|
var Oracle;
|
|
var custabspath;
|
|
|
|
var config;
|
|
var token;
|
|
function initialize(params)
|
|
{
|
|
DEZE = params.DEZE;
|
|
|
|
__Log = DEZE.__Log;
|
|
__DoLog = DEZE.__DoLog;
|
|
_AiAi = DEZE._AiAi;
|
|
safe = DEZE.safe;
|
|
customerId = DEZE.customerId;
|
|
|
|
custabspath = params.custabspath;
|
|
Oracle = DEZE.Oracle;
|
|
}
|
|
|
|
// notification zoals de webhook hem van Graph binnenkreeg
|
|
function process_webhook(res_ruimte_key, zaalemail, notification)
|
|
{
|
|
__Log("Now in process_webhook.wsc for " + zaalemail);
|
|
|
|
/* global */ config = loadconfig(custabspath + "\\Exchange\\exchange.config");
|
|
/* global */ token = requestToken(config);
|
|
|
|
if ("encryptedContent" in notification) {
|
|
var success = process_encrypted_notification(res_ruimte_key, zaalemail, notification);
|
|
if (success === null) {
|
|
// Either;
|
|
// Signature invalid (= malafide request)
|
|
// Empty notification
|
|
// Event not yet ready for processing
|
|
return false;
|
|
} else if (success) {
|
|
return true; // Gelukt; klaar
|
|
} /* else if (success === false) {
|
|
Niet gelukt; ga hieronder door in de unencrypted flow die zelf alle data ophaalt
|
|
} */
|
|
}
|
|
|
|
// Onderstaande route is voor Outlook series, daarvan krijgen we niet direct alle benodigde informatie door
|
|
// Tevens voor backwards compatibility, vlak na de upgrade naar 2024.1 zijn de subscriptions nog niet vernieuwd en ontvangen we nog normale notifications
|
|
|
|
/* Zijn alle event-id's al in Facilitor bekend? Zo niet, vul die dan eerst aan */
|
|
if (getMSGraphSyncLevel() & 4) // Anders kunnen we het event bij de organizer toch niet opvragen
|
|
{ /*
|
|
Als wij vanuit Facilitor een reservering aanmaken doen wij dat op naam van de organisator (res_rsv_ruimte_externnr2)
|
|
Dan weten we het id van het event van de ruimte (res_rsv_ruimte_externnr) nog niet.
|
|
*/
|
|
var sql = "SELECT 'dummy'"
|
|
+ " FROM res_rsv_ruimte"
|
|
+ " WHERE res_rsv_ruimte_externnr IS NOT NULL"
|
|
+ " AND INSTR(res_rsv_ruimte_externnr, " + safe.quoted_sql(notification.resourceData.id) + ") = 1" // =1 want ook bij occurrences is dit het id van de seriesMaster
|
|
+ " AND res_rsv_ruimte_verwijder IS NULL";
|
|
var oRs = Oracle.Execute(sql);
|
|
if (oRs.EoF) { // We moeten het res_rsv_ruimte_externnr (ruimte-event-id) nog invullen, is alleen leeg reserveringen die in Facilitor zijn aangemaakt
|
|
__Log("Reservering niet teruggevonden in Facilitor op basis van externnr, eerst event-gegevens opvragen bij Outlook."
|
|
+ "\nRuimte-resource id: " + notification.resourceData.id);
|
|
if (updateExternnr(zaalemail, notification.resourceData.id) == -1) {
|
|
return null; // Errorcode -1; exit
|
|
}
|
|
}
|
|
oRs.Close();
|
|
}
|
|
return doImport(res_ruimte_key, zaalemail, "EXCHANGE");
|
|
}
|
|
|
|
function process_encrypted_notification(res_ruimte_key, zaalemail, notification)
|
|
{
|
|
var success = 0;
|
|
var base64encodedPayload = notification.encryptedContent.data;
|
|
var base64encodedKey = notification.encryptedContent.dataKey;
|
|
var base64encodedSignature = notification.encryptedContent.dataSignature;
|
|
var encryptionCertificateId = notification.encryptedContent.encryptionCertificateId;
|
|
|
|
if (!unlockChilkat()) {
|
|
__DoLog("Error Chilkat", "#FF0000");
|
|
return false;
|
|
}
|
|
|
|
// 1. Use the encryptionCertificateId property to identify the certificate to use.
|
|
if (encryptionCertificateId !== CERT_ID_PREFIX + customerId.toUpperCase()) {
|
|
__DoLog("Error invalid certificate ID; " + encryptionCertificateId + " (should be: " + CERT_ID_PREFIX + customerId.toUpperCase() + ")", "#FF0000");
|
|
return false;
|
|
} else {
|
|
var pfx = new ActiveXObject("Chilkat_9_5_0.Pfx");
|
|
var success = pfx.LoadPfxFile(custabspath + "\\Exchange\\" + config.certificate, config.certificate_password);
|
|
if (success != 1) {
|
|
__DoLog("LoadPfxFile geen succes", "#FF0000");
|
|
__DoLog(pfx.LastErrorText);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 2. Initialize an RSA cryptographic component (such as the .NET RSACryptoServiceProvider) with the private key.
|
|
var rsa = new ActiveXObject("Chilkat_9_5_0.Rsa");
|
|
rsa.OaepPadding = 1;
|
|
rsa.EncodingMode = "base64";
|
|
rsa.Charset = "utf-8";
|
|
success = rsa.ImportPrivateKeyObj(pfx.GetPrivateKey(0));
|
|
if (success != 1) {
|
|
__DoLog("Importing private key failed", "#FF0000");
|
|
__DoLog(rsa.LastErrorText);
|
|
return false;
|
|
}
|
|
|
|
// 3. Decrypt the symmetric key delivered in the dataKey property of each item in the change notification.
|
|
try {
|
|
var decryptedKeyBytes = rsa.DecryptBytesENC(base64encodedKey, 1);
|
|
} catch (e) {
|
|
__DoLog(e.message);
|
|
return false;
|
|
}
|
|
|
|
// 4. Use the symmetric key to calculate the HMAC-SHA256 signature of the value in data (=base64encodedPayload).
|
|
var crypt = new ActiveXObject("Chilkat_9_5_0.Crypt2");
|
|
crypt.MacAlgorithm = "hmac";
|
|
crypt.HashAlgorithm = "SHA-256";
|
|
crypt.SetMacKeyBytes(decryptedKeyBytes);
|
|
|
|
var decodedPayloadBytes = crypt.Decode(base64encodedPayload, "base64");
|
|
|
|
var base64encodedHMAC = crypt.MacBytesENC(decodedPayloadBytes);
|
|
if (base64encodedHMAC !== base64encodedSignature) {
|
|
__DoLog("Unable to verify signature", "#FF0000");
|
|
__DoLog(notification);
|
|
return null;
|
|
}
|
|
|
|
// 5. Use the symmetric key with an Advanced Encryption Standard (AES) (such as the .NET AesCryptoServiceProvider) to decrypt the content in data.
|
|
crypt.EncodingMode = "base64";
|
|
|
|
// crypt.SetEncodedKey(base64EncodedDecryptedSymetricKey, "base64");
|
|
crypt.SecretKey = decryptedKeyBytes;
|
|
|
|
// Set the "initialization vector" by copying the first 16 bytes of the symmetric key used for decryption.
|
|
// We hebben de eerste 16 bytes (1 byte = 8 bits) nodig, hex is base16, elk karakter is dus 4 bits (2 karakters = 8 bits = 1 byte), dus pak de eerste 32 hex-karakters
|
|
crypt.IV = crypt.Decode(crypt.Encode(decryptedKeyBytes, "hex").slice(0, 32), "hex");
|
|
|
|
// 6. The decrypted value is a JSON string that represents the resource instance in the change notification.
|
|
try {
|
|
var decryptedResourceDataStr = crypt.BytesToString(crypt.DecryptBytes(crypt.Decode(base64encodedPayload, "base64")), "utf-8");
|
|
if (decryptedResourceDataStr === "{}") { // Leeg = deleted (en deleted != isCancelled, deleten gebeurd in de agenda van de deelnemende ruimte, isCancelled gebeurd door de organisator)
|
|
var isDeleted = true;
|
|
}
|
|
var decryptedResourceData = JSON.parse(decryptedResourceDataStr);
|
|
if (isDeleted) {
|
|
decryptedResourceData["@removed"] = {
|
|
"reason": "deleted"
|
|
}
|
|
}
|
|
decryptedResourceData.id = notification.resourceData.id; // Die staat niet in de rich notification, maar bij de resourceData
|
|
} catch (e) {
|
|
__DoLog(e.message);
|
|
return false;
|
|
}
|
|
|
|
if (decryptedResourceData.type === "singleInstance" || decryptedResourceData["@removed"]) { // Voor series(seriesMaster/occurrence/exception) moeten we toch meer gegevens ophalen, dus via de reguliere flow
|
|
if (!eventReadyForProcessing(zaalemail, decryptedResourceData)) {
|
|
return null;
|
|
}
|
|
return doImportSingle(res_ruimte_key, zaalemail, decryptedResourceData);
|
|
}
|
|
} |