Skip to content

Commit

Permalink
feat(internal-plugin-calendar): support unified scheduler in beta bra…
Browse files Browse the repository at this point in the history
…nch (merge from master) (#3076)

Co-authored-by: Vic Wang <[email protected]>
  • Loading branch information
vskygk and Vic Wang authored Sep 15, 2023
1 parent f5e9bba commit d119728
Show file tree
Hide file tree
Showing 9 changed files with 761 additions and 15 deletions.
2 changes: 0 additions & 2 deletions packages/@webex/internal-plugin-calendar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@
"sinon": "^9.2.4"
},
"dependencies": {
"@webex/internal-plugin-calendar": "workspace:^",
"@webex/internal-plugin-conversation": "workspace:^",
"@webex/internal-plugin-device": "workspace:^",
"@webex/internal-plugin-encryption": "workspace:^",
"@webex/webex-core": "workspace:^",
"btoa": "^1.2.1",
"lodash": "^4.17.21",
"uuid": "^3.3.2"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const _decryptTextProp = (ctx, name, key, object) => {
if (!object[name]) {
return Promise.resolve();
}

return ctx.webex.internal.encryption
.decryptText(key.uri || key, object[name])
.then((plaintext) => {
object[name] = plaintext;
});
};

const DecryptHelper = {
/**
* Decrypt scheduler data response
* @param {object} [ctx] context
* @param {object} [data] scheduler data response
* @returns {Promise} Resolves with decrypted response
* */
decryptSchedulerDataResponse: (ctx, data) => {
if (!data) {
return Promise.resolve();
}

if (!data.encryptionKeyUrl) {
return Promise.resolve();
}

// Decrypt participant properties if meeting object contains participants
const decryptedParticipants = data.encryptedParticipants
? data.encryptedParticipants.map((participant) =>
Promise.all([
_decryptTextProp(ctx, 'encryptedEmailAddress', data.encryptionKeyUrl, participant),
_decryptTextProp(ctx, 'encryptedName', data.encryptionKeyUrl, participant),
])
)
: [];

// Decrypt encryptedScheduleFor properties if meeting object contains SOB
const decryptedScheduleFor = Promise.all(
Object.values(data.encryptedScheduleFor || {}).flatMap((item) => [
_decryptTextProp(ctx, 'encryptedEmail', data.encryptionKeyUrl, item),
_decryptTextProp(ctx, 'encryptedDisplayName', data.encryptionKeyUrl, item),
])
);

// Decrypt meetingJoinInfo properties if meeting object contains meetingJoinInfo
const decryptedMeetingJoinInfo = data.meetingJoinInfo
? Promise.all([
_decryptTextProp(ctx, 'meetingJoinURI', data.encryptionKeyUrl, data.meetingJoinInfo),
_decryptTextProp(ctx, 'meetingJoinURL', data.encryptionKeyUrl, data.meetingJoinInfo),
])
: [];

const decryptedOrganizer = data.encryptedOrganizer
? Promise.all([
_decryptTextProp(
ctx,
'encryptedEmailAddress',
data.encryptionKeyUrl,
data.encryptedOrganizer
),
_decryptTextProp(ctx, 'encryptedName', data.encryptionKeyUrl, data.encryptedOrganizer),
])
: [];

return Promise.all(
[
_decryptTextProp(ctx, 'encryptedSubject', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'encryptedLocation', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'encryptedNotes', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'webexURI', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'webexURL', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'spaceMeetURL', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'spaceURI', data.encryptionKeyUrl, data),
_decryptTextProp(ctx, 'spaceURL', data.encryptionKeyUrl, data),
].concat(
decryptedOrganizer,
decryptedParticipants,
decryptedScheduleFor,
decryptedMeetingJoinInfo
)
);
},
/**
* Decrypt free-busy response
* @param {object} [ctx] context
* @param {object} [data] free-busy response
* @returns {Promise} Resolves with decrypted response
* */
decryptFreeBusyResponse: (ctx, data) => {
if (!data) {
return Promise.resolve();
}

if (!data.calendarFreeBusyScheduleResponse) {
return Promise.resolve();
}

if (!data.calendarFreeBusyScheduleResponse.encryptionKeyUrl) {
return Promise.resolve();
}

const calendarFreeBusyItems = data.calendarFreeBusyScheduleResponse.calendarFreeBusyItems
? data.calendarFreeBusyScheduleResponse.calendarFreeBusyItems.map((calendarFreeBusyItem) =>
Promise.all([
_decryptTextProp(
ctx,
'email',
data.calendarFreeBusyScheduleResponse.encryptionKeyUrl,
calendarFreeBusyItem
),
])
)
: [];

return Promise.all([].concat(calendarFreeBusyItems));
},
};

export default DecryptHelper;
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {isArray} from 'lodash';

const _encryptTextProp = (ctx, name, key, object) => {
if (!object[name]) {
return Promise.resolve();
}

return ctx.webex.internal.encryption
.encryptText(key.uri || key, object[name])
.then((ciphertext) => {
object[name] = ciphertext;
});
};

const _encryptCalendarEventPayload = (data, ctx) => {
Object.assign(data, {encryptionKeyUrl: ctx.encryptionKeyUrl});

const encryptedAttendees = data.attendees
? data.attendees.map((attendee) =>
Promise.all([
_encryptTextProp(ctx, 'displayName', data.encryptionKeyUrl, attendee),
_encryptTextProp(ctx, 'email', data.encryptionKeyUrl, attendee),
])
)
: [];

return Promise.all(
[
_encryptTextProp(ctx, 'subject', data.encryptionKeyUrl, data),
_encryptTextProp(ctx, 'notes', data.encryptionKeyUrl, data),
_encryptTextProp(ctx, 'webexOptions', data.encryptionKeyUrl, data),
].concat([encryptedAttendees])
);
};

const _encryptFreeBusyPayload = (data, ctx) => {
Object.assign(data, {encryptionKeyUrl: ctx.encryptionKeyUrl});

const promises = [];
if (data.emails && Array.isArray(data.emails)) {
data.emails.map((item, index) =>
promises.push(
ctx.webex.internal.encryption
.encryptText(data.encryptionKeyUrl, item)
.then((encryptText) => {
data.emails[index] = encryptText;
})
)
);
}

return Promise.all(promises);
};

const EncryptHelper = {
/**
* Encrypt create / update calendar event request payload
* @param {object} [ctx] context
* @param {object} [data] meeting payload data
* @returns {Promise} Resolves with encrypted request payload
* */
encryptCalendarEventRequest: (ctx, data) => {
if (ctx.encryptionKeyUrl) {
return _encryptCalendarEventPayload(data, ctx);
}

return ctx.webex.internal.encryption.kms.createUnboundKeys({count: 1}).then((keys) => {
const key = isArray(keys) ? keys[0] : keys;
ctx.encryptionKeyUrl = key.uri;

return _encryptCalendarEventPayload(data, ctx);
});
},
/**
* Encrypt free-busy request payload, if request payload only includes the sensitive data, like email, need to encrypt these reqeust parameters, and playload includes encrypt url.
* Otherwise, don't encrypt playload and without encrypt url,Due to calendar serivce will vaild both encrypt url and sensitive that are both present. if not, will return 400 bad reqeust to caller.
* @param {object} [ctx] context
* @param {object} [data] free busy payload data
* @returns {Promise} Resolves with encrypted request payload
* */
encryptFreeBusyRequest: (ctx, data) => {
if (!data.emails || !Array.isArray(data.emails)) {
return Promise.resolve();
}
if (ctx.encryptionKeyUrl) {
return _encryptFreeBusyPayload(data, ctx);
}

return ctx.webex.internal.encryption.kms.createUnboundKeys({count: 1}).then((keys) => {
const key = isArray(keys) ? keys[0] : keys;
ctx.encryptionKeyUrl = key.uri;

return _encryptFreeBusyPayload(data, ctx);
});
},
};

export default EncryptHelper;
Loading

0 comments on commit d119728

Please sign in to comment.