Skip to content

Commit a9fabdc

Browse files
bhabalancoderabbitai[bot]rsarika
authored
feat(plugin-encryption): Create new plugin-encryption (#4099)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: rsarika <[email protected]>
1 parent f027038 commit a9fabdc

File tree

32 files changed

+1595
-15
lines changed

32 files changed

+1595
-15
lines changed

LICENSE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The contents of this repository are licensed pursuant to the terms of the MIT Li
44

55
### MIT License
66

7-
Copyright (c) 2024 Cisco Systems
7+
Copyright (c) 2025 Cisco Systems
88

99
Permission is hereby granted, free of charge, to any person obtaining a copy
1010
of this software and associated documentation files (the "Software"), to deal
@@ -22,4 +22,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2222
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2323
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2424
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25-
SOFTWARE.
25+
SOFTWARE.

docs/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ <h2 class="page-header">Samples</h2>
8484
<a href="./samples/browser-read-status/" class="list-group-item">Read Status</a>
8585

8686
<a href="./samples/browser-socket/" class="list-group-item">Socket</a>
87+
88+
<a href="./samples/plugin-encryption/" class="list-group-item">Encryption Plugin</a>
8789
</div>
8890
</div>
8991
<div class="col-md-4">

docs/samples/browser-plugin-meetings/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ function generateWebexConfig({credentials}) {
108108
services: {
109109
discovery: {
110110
u2c: 'https://u2c-intb.ciscospark.com/u2c/api/v1',
111-
hydra: 'https://apialpha.ciscospark.com/v1/'
111+
hydra: 'https://hydra-intb.ciscospark.com/v1/'
112112
}
113113
}
114114
}),

docs/samples/calling/app.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ async function generateGuestToken() {
194194
if (error.code === 401) {
195195
// TODO: Refresh the access token and try again with the new token
196196
}
197-
}
197+
}
198198
}
199199

200200
async function handleServiceSelect(e) {
@@ -246,7 +246,7 @@ async function initCalling(e) {
246246
webexConfig.config.services = {
247247
discovery: {
248248
u2c: 'https://u2c-intb.ciscospark.com/u2c/api/v1',
249-
hydra: 'https://apialpha.ciscospark.com/v1/',
249+
hydra: 'https://hydra-intb.ciscospark.com/v1/',
250250
},
251251
};
252252
}
@@ -653,7 +653,7 @@ function createCall(e) {
653653

654654
localAudioStream.on('user-mute-state-change', (userMuted) => {
655655
muteElm.value = userMuted && muteElm.value === 'Mute' ? 'Unmute' : 'Mute';
656-
});
656+
});
657657

658658
call.dial(localAudioStream);
659659
}
@@ -746,7 +746,7 @@ async function getMediaStreams() {
746746
}
747747

748748
async function toggleNoiseReductionEffect() {
749-
const options = {authToken: tokenElm.value, env: enableProd ? 'prod': 'int'}
749+
const options = {authToken: tokenElm.value, env: enableProd ? 'prod': 'int'}
750750
effect = await localAudioStream.getEffectByKind('noise-reduction-effect');
751751

752752
if (!effect) {

docs/samples/plugin-encryption/app.js

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/* eslint-env browser */
2+
3+
/* global Webex */
4+
5+
/* eslint-disable no-console */
6+
/* eslint-disable require-jsdoc */
7+
8+
// Declare some globals that we'll need throughout.
9+
let webex;
10+
let enableProd = true;
11+
let subscribedUserIds = [];
12+
13+
const credentialsFormElm = document.querySelector('#credentials');
14+
const tokenElm = document.querySelector('#access-token');
15+
const saveElm = document.querySelector('#access-token-save');
16+
const registerBtn = document.querySelector('#register-btn');
17+
const deregisterBtn = document.querySelector('#deregister-btn');
18+
const authStatusElm = document.querySelector('#access-token-status');
19+
const encryptedFileUrlInput = document.querySelector('#encrypted-file-url');
20+
const useFileServiceCheckbox = document.querySelector('#use-file-service');
21+
const encryptedFileJweInput = document.querySelector('#encrypted-file-jwe');
22+
const encryptedFileKeyURIInput = document.querySelector('#encrypted-file-keyURI');
23+
const decryptedFileNameInput = document.querySelector('#decrypted-file-name');
24+
const decryptFileBtn = document.querySelector('#decrypt-my-file-btn');
25+
const decryptFileResult = document.querySelector('#decrypt-file-result');
26+
const mimeTypeDropdown = document.querySelector('#mime-types');
27+
28+
// Store and Grab `access-token` from localstorage
29+
if (localStorage.getItem('date') > new Date().getTime()) {
30+
tokenElm.value = localStorage.getItem('access-token');
31+
} else {
32+
localStorage.removeItem('access-token');
33+
}
34+
35+
tokenElm.addEventListener('change', (event) => {
36+
const token = event.target.value;
37+
if (!token) {
38+
localStorage.removeItem('access-token');
39+
localStorage.removeItem('date');
40+
return;
41+
}
42+
localStorage.setItem('access-token', event.target.value);
43+
localStorage.setItem('date', new Date().getTime() + 12 * 60 * 60 * 1000);
44+
});
45+
46+
function changeEnv() {
47+
enableProd = !enableProd;
48+
enableProduction.innerHTML = enableProd ? 'In Production' : 'In Integration';
49+
}
50+
51+
function updateStatus(enabled) {
52+
decryptFileResult.innerText = '';
53+
decryptFileBtn.disabled = !enabled;
54+
}
55+
56+
async function initWebex(e) {
57+
e.preventDefault();
58+
console.log('Authentication#initWebex()');
59+
60+
tokenElm.disabled = true;
61+
saveElm.disabled = true;
62+
63+
decryptFileBtn.disabled = true;
64+
authStatusElm.innerText = 'initializing...';
65+
66+
const webexConfig = {
67+
config: {
68+
logger: {
69+
level: 'debug', // set the desired log level
70+
},
71+
},
72+
credentials: {
73+
access_token: tokenElm.value
74+
}
75+
};
76+
77+
if (!enableProd) {
78+
webexConfig.config.services = {
79+
discovery: {
80+
u2c: 'https://u2c-intb.ciscospark.com/u2c/api/v1',
81+
hydra: 'https://hydra-intb.ciscospark.com/v1/',
82+
},
83+
};
84+
}
85+
86+
webex = window.webex = Webex.init(webexConfig);
87+
88+
webex.once('ready', () => {
89+
console.log('Authentication#initWebex() :: Webex Ready');
90+
authStatusElm.innerText = 'Webex is ready. Saved access token!';
91+
registerBtn.disabled = false;
92+
});
93+
e.stopPropagation();
94+
}
95+
96+
credentialsFormElm.addEventListener('submit', initWebex);
97+
98+
encryptedFileUrlInput.addEventListener('input', () => {
99+
decryptFileResult.innerText = '';
100+
});
101+
102+
103+
async function register(){
104+
webex.cypher.register().then(() => {
105+
console.log('Authentication#initWebex() :: Webex Registered');
106+
authStatusElm.innerText = 'Webex is ready and registered!';
107+
updateStatus(true);
108+
registerBtn.disabled = true;
109+
deregisterBtn.disabled = false;
110+
}).catch((err) => {
111+
console.error(`error registering webex: ${err}`);
112+
authStatusElm.innerText = 'Error registering Webex. Check access token!';
113+
});
114+
}
115+
116+
async function deregister(){
117+
webex.cypher.deregister().then(() => {
118+
console.log('Authentication#initWebex() :: Webex Deregistered');
119+
authStatusElm.innerText = 'Webex is ready, but not registered!';
120+
updateStatus(false);
121+
registerBtn.disabled = false;
122+
deregisterBtn.disabled = true;
123+
}).catch((err) => {
124+
console.error(`error deregistering webex: ${err}`);
125+
authStatusElm.innerText = 'Error deregistering Webex. Check access token!';
126+
});
127+
}
128+
129+
async function decryptFile() {
130+
decryptFileResult.innerText = '';
131+
const fileUrl = encryptedFileUrlInput.value;
132+
const encryptedFileName = decryptedFileNameInput.value;
133+
const mimeType = mimeTypeDropdown.value;
134+
135+
if (!fileUrl) {
136+
decryptFileResult.innerText = ': error - Invalid file URL';
137+
return;
138+
}
139+
140+
if (!mimeType) {
141+
decryptFileResult.innerText = ': error - Invalid MIME type';
142+
return;
143+
}
144+
145+
let objectUrl;
146+
try {
147+
let decryptedBuf;
148+
const options = {
149+
useFileService: useFileServiceCheckbox.checked,
150+
jwe: encryptedFileJweInput.value,
151+
keyUri: encryptedFileKeyURIInput.value,
152+
};
153+
154+
decryptedBuf = await webex.cypher.downloadAndDecryptFile(fileUrl, options);
155+
const file = new File([decryptedBuf], encryptedFileName, {type: mimeType});
156+
objectUrl = URL.createObjectURL(file);
157+
const a = document.createElement("a");
158+
a.href = objectUrl;
159+
a.download = file.name || "download";
160+
document.body.appendChild(a);
161+
a.click();
162+
document.body.removeChild(a);
163+
decryptFileResult.innerText = ': success';
164+
}
165+
catch (error) {
166+
console.error('error decrypting file', error);
167+
decryptFileResult.innerText = ': error';
168+
} finally {
169+
if (objectUrl) {
170+
URL.revokeObjectURL(objectUrl);
171+
}
172+
}
173+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>Encryption Kitchen Sink</title>
7+
<link rel="stylesheet" href="./style.css" />
8+
</head>
9+
10+
<body>
11+
<main class="container">
12+
<div style="display: flex; justify-content: center;">
13+
<article id="docs-content" class="docs">
14+
15+
<!-- ############################## ############################## ############################## -->
16+
<section>
17+
<h2>Webex - encryption</h2>
18+
<p>Use this kitchen sink to interact with the Webex encryption service</p>
19+
</section>
20+
21+
<!-- ############################## ############################## ############################## -->
22+
<!-- authentication -->
23+
<section id="authentication">
24+
<h2 class="collapsible">Authentication</h2>
25+
<div class="section-content">
26+
<p class="note"><strong>Note:</strong> Get an access token from our developer portal: <a
27+
href="https://developer.webex.com/docs/api/getting-started"
28+
target="_blank">https://developer.webex.com/docs/api/getting-started</a>.</p>
29+
<p class="note"><strong>Note:</strong> Webex JS SDK must be initialized using a valid token.</p>
30+
31+
<!-- authentication-credentials -->
32+
<form id="credentials">
33+
<fieldset>
34+
<legend>Credentials</legend>
35+
<div class="u-mv">
36+
<p class="note">Initializes webex object and registers Webex JS SDK as a device.</p>
37+
<button id="enableProduction" type="button" class="btn-code" onclick=changeEnv()>In Production</button><span class="text-color"> ( Click to change the environment )</span>
38+
<input id="access-token" name="accessToken" placeholder="Access Token" value="" type="text" style="margin: 1rem 0 0.5rem 0;" required>
39+
</div>
40+
41+
<button id="access-token-save" class="btn-code" type="submit">Initialize Webex</button>
42+
<pre id="access-token-status">Not initialized</pre>
43+
<button id="register-btn" class="btn-code" disabled onclick="register()">Register</button>
44+
<button id="deregister-btn" class="btn-code" disabled onclick="deregister()">Deregister</button>
45+
</fieldset>
46+
</form>
47+
</div>
48+
</section>
49+
50+
<section id="encryptionActions">
51+
<h2 class="collapsible">Encryption Actions</h2>
52+
<div class="section-content">
53+
<fieldset>
54+
<legend>Decrypt files</legend>
55+
<div class="u-mv">
56+
<div>
57+
File URL: <input id="encrypted-file-url" required value="" type="string" style="margin-bottom: 0.5rem;">
58+
</div>
59+
<div>
60+
Use File Service?(leave unchecked if you are unsure): <input id="use-file-service" type="checkbox" style="margin-bottom: 0.5rem;">
61+
</div>
62+
<div>
63+
JWE: <input id="encrypted-file-jwe" value="" style="margin-bottom: 0.5rem;" placeholder="Optional">
64+
</div>
65+
<div>
66+
keyURI: <input id="encrypted-file-keyURI" value="" style="margin-bottom: 0.5rem;" placeholder="Optional">
67+
</div>
68+
<div>
69+
File Name: <input id="decrypted-file-name" value="my-decrypted-file.jpeg" type="string" style="display: inline-block; margin-bottom: 0.5rem;">
70+
<!-- A dropdown with common MIME types -->
71+
File Type: <select name="mime-types" id="mime-types" style="display: inline-block;">
72+
<option value="image/jpeg">image/jpeg</option>
73+
<option value="image/png">image/png</option>
74+
<option value="application/json">application/json</option>
75+
<option value="application/javascript">application/javascript</option>
76+
<option value="application/pdf">application/pdf</option>
77+
<option value="application/xml">application/xml</option>
78+
<option value="application/zip">application/zip</option>
79+
<option value="application/x-www-form-urlencoded">application/x-www-form-urlencoded</option>
80+
<option value="application/vnd.ms-excel">application/vnd.ms-excel</option>
81+
<option value="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</option>
82+
<option value="application/vnd.ms-powerpoint">application/vnd.ms-powerpoint</option>
83+
<option value="application/vnd.openxmlformats-officedocument.presentationml.presentation">application/vnd.openxmlformats-officedocument.presentationml.presentation</option>
84+
<option value="application/msword">application/msword</option>
85+
<option value="application/vnd.openxmlformats-officedocument.wordprocessingml.document">application/vnd.openxmlformats-officedocument.wordprocessingml.document</option>
86+
<option value="audio/mpeg">audio/mpeg</option>
87+
<option value="audio/ogg">audio/ogg</option>
88+
<option value="audio/wav">audio/wav</option>
89+
<option value="audio/webm">audio/webm</option>
90+
<option value="image/bmp">image/bmp</option>
91+
<option value="image/gif">image/gif</option>
92+
<option value="image/svg+xml">image/svg+xml</option>
93+
<option value="image/tiff">image/tiff</option>
94+
<option value="image/webp">image/webp</option>
95+
<option value="text/css">text/css</option>
96+
<option value="text/csv">text/csv</option>
97+
<option value="text/html">text/html</option>
98+
<option value="text/plain">text/plain</option>
99+
<option value="text/javascript">text/javascript</option>
100+
<option value="video/mp4">video/mp4</option>
101+
<option value="video/mpeg">video/mpeg</option>
102+
<option value="video/ogg">video/ogg</option>
103+
<option value="video/webm">video/webm</option>
104+
</select>
105+
</div>
106+
<button type="button" id="decrypt-my-file-btn" onclick="decryptFile()" class="btn" style="display: block;">
107+
<div>Download and decrypt my file<span id="decrypt-file-result"></span> </div>
108+
</button>
109+
</fieldset>
110+
</div>
111+
</section>
112+
113+
</article>
114+
</div>
115+
</main>
116+
<script src="../encryption.min.js"></script>
117+
<script src="app.js"></script>
118+
</body>
119+
120+
</html>

0 commit comments

Comments
 (0)