Skip to content

Commit 1e25693

Browse files
authored
feat: KeyPair Persist (#104)
1 parent cdf0d49 commit 1e25693

File tree

12 files changed

+633
-48
lines changed

12 files changed

+633
-48
lines changed

src/channel/channel.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ module.exports = class ZitiChannel {
900900
let contentType = contentTypeView[0];
901901

902902
let sequenceView = new Int32Array(buffer, 8, 1);
903-
this._ctx.logger.debug("recv <- contentType[%o] seq[%o]", contentType, sequenceView[0]);
903+
this._ctx.logger.trace("recv <- contentType[%o] seq[%o]", contentType, sequenceView[0]);
904904

905905
let responseSequence = sequenceView[0];
906906

@@ -1002,16 +1002,23 @@ module.exports = class ZitiChannel {
10021002
len = 2000;
10031003
}
10041004
this._ctx.logger.trace("recv <- unencrypted_data (first 2000): %s", m1.substring(0, len));
1005-
} catch (e) { /* nop */ }
1005+
1006+
//
1007+
let dbgStr = m1.substring(0, len);
1008+
this._ctx.logger.trace("recv <- data (first 2000): %s", dbgStr);
1009+
1010+
} catch (e) { }
10061011

10071012
bodyView = unencrypted_data.message;
10081013
} else {
1014+
/* debug...
10091015
let len = bodyView.length;
10101016
if (len > 2000) {
10111017
len = 2000;
10121018
}
10131019
let dbgStr = String.fromCharCode.apply(null, bodyView).substring(0, len);
1014-
this._ctx.logger.trace("recv <- data (first 2000): %s", dbgStr);
1020+
this._ctx.logger.debug("recv <- data (first 2000): %s", dbgStr);
1021+
*/
10151022

10161023
//temp debugging
10171024
// if (dbgStr.includes("var openMe = (window.parent")) {

src/client.js

Lines changed: 114 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,25 @@ class ZitiClient {
7777

7878
CookieInterceptor.write.use( function ( cookie ) {
7979

80+
let name = cookie.substring(0, cookie.indexOf("="));
81+
let value = cookie.substring(cookie.indexOf("=") + 1);
82+
let cookie_value = value.substring(0, value.indexOf(";"));
83+
84+
sendMessageToServiceworker( { command: 'setCookie', name: name, value: cookie_value} );
85+
8086
(async function() { // we use an IIFE because we need to run some await calls, and we cannot make
8187
// our write.use() an async func because it will then return a Promise,
8288
// which would cause Cookie storage in the browser to get corrupted.
8389

8490
console.log('=====> CookieInterceptor sees write of Cookie: ', cookie);
8591

8692
const release = await ziti._cookiemutex.acquire();
87-
8893
let zitiCookies = await ls.getWithExpiry(zitiConstants.get().ZITI_COOKIES);
8994
if (isNull(zitiCookies)) {
9095
zitiCookies = {}
9196
}
9297
// console.log('=====> CookieInterceptor ZITI_COOKIES (before): ', zitiCookies);
9398

94-
let name = cookie.substring(0, cookie.indexOf("="));
95-
let value = cookie.substring(cookie.indexOf("=") + 1);
96-
let cookie_value = value.substring(0, value.indexOf(";"));
9799
if (cookie_value !== ''){
98100
let parts = value.split(";");
99101
let cookiePath;
@@ -113,10 +115,10 @@ class ZitiClient {
113115
}
114116

115117
zitiCookies[name] = cookie_value;
118+
119+
await ls.setWithExpiry(zitiConstants.get().ZITI_COOKIES, zitiCookies, new Date(8640000000000000));
116120

117121
// console.log('=====> CookieInterceptor ZITI_COOKIES (after): ', zitiCookies);
118-
119-
await ls.setWithExpiry(zitiConstants.get().ZITI_COOKIES, zitiCookies, new Date(8640000000000000));
120122
}
121123

122124
release();
@@ -348,8 +350,11 @@ class ZitiClient {
348350

349351
return new Promise( async (resolve, reject) => {
350352

351-
console.log('js-sdk fetchFromServiceWorker() entered');
352-
353+
// console.log('js-sdk fetchFromServiceWorker() entered', url);
354+
// for (var pair of opts.headers.entries()) {
355+
// console.log('js-sdk fetchFromServiceWorker() header ', pair[0]+ ': '+ pair[1]);
356+
// }
357+
353358
await ziti._serviceWorkerMutexWithTimeout.runExclusive(async () => {
354359

355360
console.log('js-sdk fetchFromServiceWorker() acquired ziti._mutex');
@@ -688,6 +693,7 @@ zitiFetch = async ( url, opts ) => {
688693
// We only want to intercept fetch requests that target the Ziti HTTP Agent
689694
var regex = new RegExp( zitiConfig.httpAgent.self.host, 'g' );
690695
var regexSlash = new RegExp( /^\//, 'g' );
696+
var regexDotSlash = new RegExp( /^\.\//, 'g' );
691697

692698
if (url.match( regex )) { // the request is targeting the Ziti HTTP Agent
693699

@@ -758,10 +764,49 @@ zitiFetch = async ( url, opts ) => {
758764
});
759765

760766
let newUrl;
761-
if (document.baseURI === zitiConfig.httpAgent.self.host) {
767+
let baseURIUrl = new URL( document.baseURI );
768+
if (baseURIUrl.hostname === zitiConfig.httpAgent.self.host) {
762769
newUrl = new URL( 'https://' + zitiConfig.httpAgent.target.host + ':' + zitiConfig.httpAgent.target.port + url );
763770
} else {
764-
newUrl = new URL( document.baseURI + url );
771+
let baseURI = document.baseURI.replace(/\.\/$/, '');
772+
newUrl = new URL( baseURI + url );
773+
}
774+
ziti._ctx.logger.debug( 'zitiFetch: transformed URL: ', newUrl.toString());
775+
776+
serviceName = await ziti._ctx.shouldRouteOverZiti( newUrl );
777+
778+
if (isUndefined(serviceName)) { // If we have no serviceConfig associated with the hostname:port, do not intercept
779+
ziti._ctx.logger.warn('zitiFetch(): no associated serviceConfig, bypassing intercept of [%s]', url);
780+
return window.realFetch(url, opts);
781+
}
782+
783+
url = newUrl.toString();
784+
785+
} else if (url.match( regexDotSlash )) { // the request starts with a slash
786+
787+
await ziti._clientMutexNoTimeout.runExclusive(async () => {
788+
789+
let isExpired = await ziti._ctx.isIdentityCertExpired();
790+
791+
if (isExpired) {
792+
let updb = new ZitiUPDB(ZitiUPDB.prototype);
793+
await updb.init( { ctx: ziti._ctx, logger: ziti._ctx.logger } );
794+
await updb.awaitCredentialsAndAPISession();
795+
}
796+
})
797+
.catch(( err ) => {
798+
ziti._ctx.logger.error(err);
799+
throw err;
800+
});
801+
802+
let newUrl;
803+
let baseURIUrl = new URL( document.baseURI );
804+
if (baseURIUrl.hostname === zitiConfig.httpAgent.self.host) {
805+
let slashUrl = url.replace('./', '/');
806+
newUrl = new URL( 'https://' + zitiConfig.httpAgent.target.host + ':' + zitiConfig.httpAgent.target.port + slashUrl );
807+
} else {
808+
let baseURI = document.baseURI.replace(/\/$/, '');
809+
newUrl = new URL( baseURI + url );
765810
}
766811
ziti._ctx.logger.debug( 'zitiFetch: transformed URL: ', newUrl.toString());
767812

@@ -1113,7 +1158,7 @@ _onMessage_setControllerApi = async ( event ) => {
11131158
pki.generateKeyPair(); // initiate keypair calculation
11141159
}
11151160
_onMessage_generateKeyPair = async ( event ) => {
1116-
_internal_generateKeyPair();
1161+
// _internal_generateKeyPair();
11171162
_sendResponse( event, 'OK' );
11181163
}
11191164

@@ -1245,6 +1290,36 @@ _onMessage_isIdentityPresent = async ( event ) => {
12451290

12461291
await updb.init( { ctx: ziti._ctx, logger: ziti._ctx.logger } );
12471292

1293+
let haveKeypair = await updb._haveKeypair();
1294+
1295+
if (!haveKeypair) {
1296+
1297+
let keypairPresent = await updb.awaitKeypair( zitiConstants.get().ZITI_IDENTITY_KEYPAIR_OBTAIN_FROM_FS );
1298+
ziti._ctx.logger.debug('_onMessage_promptForZitiCredsNoWait: awaitKeypair returned [%o]', keypairPresent);
1299+
1300+
if (keypairPresent == zitiConstants.get().ZITI_IDENTITY_PUBLIC_KEY_FILE_NOT_FOUND || keypairPresent == zitiConstants.get().ZITI_IDENTITY_PRIVATE_KEY_FILE_NOT_FOUND) {
1301+
1302+
// If keypair not in FS or IDB, then initiate keypair generation
1303+
_internal_generateKeyPair();
1304+
1305+
// Do not proceed until we have a keypair (this will render a dialog to the user informing them of status)
1306+
let pki = new ZitiPKI(ZitiPKI.prototype);
1307+
await pki.init( { ctx: ziti._ctx, logger: ziti._ctx.logger } );
1308+
await pki.awaitKeyPairGenerationComplete(); // await completion of keypair calculation
1309+
1310+
keypairPresent = await updb.awaitKeypair( zitiConstants.get().ZITI_IDENTITY_KEYPAIR_OBTAIN_FROM_IDB );
1311+
ziti._ctx.logger.debug('_onMessage_promptForZitiCredsNoWait: awaitKeypair returned [%o]', keypairPresent);
1312+
1313+
if (keypairPresent !== zitiConstants.get().ZITI_IDENTITY_KEYPAIR_FOUND) {
1314+
throw new Error('should not happen');
1315+
} else {
1316+
1317+
ziti._ctx.logger.debug('_onMessage_promptForZitiCredsNoWait: now have the keypair we need, so proceeding to obtain creds');
1318+
1319+
}
1320+
}
1321+
}
1322+
12481323
let haveCreds = await updb._haveCreds();
12491324

12501325
if (!haveCreds) {
@@ -1317,6 +1392,21 @@ _onMessage_purgeCert = async ( event ) => {
13171392
}
13181393

13191394

1395+
/**
1396+
*
1397+
*/
1398+
_onMessage_getCookies = async ( event ) => {
1399+
console.log('JS SDK _onMessage_getCookies entered');
1400+
// if (ziti._cookiemutex.isLocked()) {
1401+
// ziti._cookiemutex.waitForUnlock();
1402+
// }
1403+
// let zitiCookies = await ls.getWithExpiry(zitiConstants.get().ZITI_COOKIES);
1404+
// console.log('JS SDK _onMessage_getCookies returning: ', zitiCookies);
1405+
// _sendResponse( event, zitiCookies );
1406+
_sendResponse( event, 'nop OK' );
1407+
}
1408+
1409+
13201410
/**
13211411
*
13221412
*/
@@ -1372,6 +1462,7 @@ if (!zitiConfig.serviceWorker.active) {
13721462
else if (event.data.command === 'promptForZitiCreds') { _onMessage_promptForZitiCreds( event ); }
13731463
else if (event.data.command === 'promptForZitiCredsNoWait') { _onMessage_promptForZitiCredsNoWait( event ); }
13741464
else if (event.data.command === 'purgeCert') { _onMessage_purgeCert( event ); }
1465+
else if (event.data.command === 'getCookies') { _onMessage_getCookies( event ); }
13751466

13761467
else if (event.data.command === 'nop') { _onMessage_nop( event ); }
13771468

@@ -1384,6 +1475,18 @@ if (!zitiConfig.serviceWorker.active) {
13841475
*/
13851476
navigator.serviceWorker.startMessages();
13861477

1478+
// Kiosk mode?
1479+
if (window.opener) {
1480+
// wait for messages from opener
1481+
window.addEventListener( 'message', function( e ) {
1482+
this.window.kioskZitiUserName = e.data.username;
1483+
this.window.kioskZitiPassword = e.data.password;
1484+
});
1485+
1486+
// tell the opener we are waiting
1487+
window.opener.postMessage( 'inited', '*' );
1488+
}
1489+
13871490

13881491
} else {
13891492
console.error("The current browser doesn't support service workers");

src/constants.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ const ZITI_CONSTANTS =
4747
*/
4848
'ZITI_IDENTITY_CERT': 'ZITI_IDENTITY_CERT',
4949

50+
/**
51+
* The Identity public key (generated locally during enrollment)
52+
*/
53+
'ZITI_IDENTITY_PUBLIC_KEY_FILENAME': 'ZITI_BROWZER_PUBLIC_KEY.pem',
54+
'ZITI_IDENTITY_PRIVATE_KEY_FILENAME': 'ZITI_BROWZER_PRIVATE_KEY.pem',
55+
'ZITI_IDENTITY_PUBLIC_KEY_FILE_NOT_FOUND': 'ZITI_IDENTITY_PUBLIC_KEY_FILE_NOT_FOUND',
56+
'ZITI_IDENTITY_PRIVATE_KEY_FILE_NOT_FOUND': 'ZITI_IDENTITY_PRIVATE_KEY_FILE_NOT_FOUND',
57+
'ZITI_IDENTITY_KEYPAIR_FOUND': 'ZITI_IDENTITY_KEYPAIR_FOUND',
58+
'ZITI_IDENTITY_KEYPAIR_OBTAIN_FROM_FS': 'ZITI_IDENTITY_KEYPAIR_OBTAIN_FROM_FS',
59+
'ZITI_IDENTITY_KEYPAIR_OBTAIN_FROM_IDB': 'ZITI_IDENTITY_KEYPAIR_OBTAIN_FROM_IDB',
60+
61+
5062
/**
5163
* The Identity public key (generated locally during enrollment)
5264
*/

src/http/request.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ limitations under the License.
257257
}
258258

259259
// Obtain all Cookie KV pairs from the Ziti Cookie cache
260+
const release = await ziti._cookiemutex.acquire();
260261
let zitiCookies = await ls.getWithExpiry(zitiConstants.get().ZITI_COOKIES);
262+
release();
261263
if (!isNull(zitiCookies)) {
262264
for (const cookie in zitiCookies) {
263265
if (zitiCookies.hasOwnProperty(cookie)) {
@@ -354,4 +356,4 @@ limitations under the License.
354356
redirect: { enumerable: true },
355357
clone: { enumerable: true },
356358
});
357-
359+

src/http/ziti-websocket-wrapper.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -973,14 +973,14 @@ function sendAfterClose(websocket, data, cb) {
973973
* @private
974974
*/
975975
function receiverOnMessage(data) {
976-
if (typeof data === 'string') {
977-
ziti._ctx.logger.info('ZitiWebSocketWrapper.receiverOnMessage() entered, emitting STRING: %o', data);
976+
// if (typeof data === 'string') {
977+
// ziti._ctx.logger.info('ZitiWebSocketWrapper.receiverOnMessage() entered, emitting STRING: %o', data);
978978
this[kWebSocket].emit('message', data);
979-
} else {
980-
let blob = new Blob(data);
981-
ziti._ctx.logger.info('ZitiWebSocketWrapper.receiverOnMessage() entered, emitting BLOB: %o', blob);
982-
this[kWebSocket].emit('message', blob);
983-
}
979+
// } else {
980+
// let blob = new Blob([new Uint8Array(data, 0, data.byteLength)]);
981+
// ziti._ctx.logger.info('ZitiWebSocketWrapper.receiverOnMessage() entered, emitting BLOB: %o', blob);
982+
// this[kWebSocket].emit('message', blob);
983+
// }
984984
}
985985

986986
/**

src/pki/pki.js

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ ZitiPKI.prototype.generateKeyPair = async function() {
259259
* @params {nothing}
260260
* @returns {nothing}
261261
*/
262-
ZitiPKI.prototype.awaitKeyPairGenerationComplete = async function() {
262+
ZitiPKI.prototype.awaitKeyPairGenerationComplete = async function( bypassRenderingOfUI ) {
263263

264264
let self = this;
265265

@@ -274,34 +274,39 @@ ZitiPKI.prototype.generateKeyPair = async function() {
274274

275275
if (typeof window !== 'undefined') {
276276

277-
identityModalCSS.inject();
278-
keypairModalHTML.inject();
279-
MicroModal.init({
280-
onShow: modal => console.info(`${modal.id} is shown`), // [1]
281-
onClose: modal => console.info(`${modal.id} is hidden`), // [2]
282-
openTrigger: 'ziti-data-micromodal-trigger', // [3]
283-
closeTrigger: 'data-custom-close', // [4]
284-
openClass: 'is-open', // [5]
285-
disableScroll: true, // [6]
286-
disableFocus: false, // [7]
287-
awaitOpenAnimation: false, // [8]
288-
awaitCloseAnimation: true, // [9]
289-
debugMode: false // [10]
290-
});
291-
292-
MicroModal.show('ziti-keypair-modal');
277+
if (bypassRenderingOfUI) {
278+
self.logger.info('bypassRenderingOfUI');
279+
} else {
280+
281+
identityModalCSS.inject();
282+
keypairModalHTML.inject();
283+
MicroModal.init({
284+
onShow: modal => console.info(`${modal.id} is shown`), // [1]
285+
onClose: modal => console.info(`${modal.id} is hidden`), // [2]
286+
openTrigger: 'ziti-data-micromodal-trigger', // [3]
287+
closeTrigger: 'data-custom-close', // [4]
288+
openClass: 'is-open', // [5]
289+
disableScroll: true, // [6]
290+
disableFocus: false, // [7]
291+
awaitOpenAnimation: false, // [8]
292+
awaitCloseAnimation: true, // [9]
293+
debugMode: false // [10]
294+
});
295+
296+
MicroModal.show('ziti-keypair-modal');
293297

294-
modalMsg.setMessage('Please do not close this browser window.');
298+
modalMsg.setMessage('Please do not close this browser window.');
295299

296-
modalMsg.setProgress('Zero-Trust KeyPair creation in progress.');
300+
modalMsg.setProgress('Zero-Trust KeyPair creation in progress.');
301+
}
297302
}
298303

299304

300305
(async function waitForKeyPairGenerationComplete() {
301306
let haveKeys = await self._haveKeypair();
302307
if (haveKeys) {
303308
MicroModal.close('ziti-keypair-modal');
304-
return resolve();
309+
return resolve(true);
305310
}
306311
setTimeout(waitForKeyPairGenerationComplete, 200);
307312
})();

src/ui/identity_modal/keypair_generation_html.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ exports.inject = () => {
3535
<h2 class="modal__title" id="modal-1-title">
3636
<img src="https://ziti-logo.s3.amazonaws.com/ziti-logo_avatar.png" width=25 >
3737
<span>
38-
Zero-Trust KeyPair Creation
38+
Zero-Trust KeyPair Generation
3939
</span>
4040
</h2>
4141
</header>
42+
<h2 class="form-signin-heading">No KeyPair found in selected folder.<br/><br/>New KeyPair will be generated now.</h2>
43+
<span style="padding-top: 5px;">&nbsp;</span>
4244
<div style="text-align: center; padding-top: 15px;">
4345
<span id="ziti-keypair-error" style="color: #e80853; font-weight: 800; font-size: 0.9em;"></span>
4446
</div>

src/ui/identity_modal/updb_prompt_html.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ exports.inject = () => {
4545
</header>
4646
4747
<h2 class="form-signin-heading">Please Login</h2>
48-
<input id="ziti-login-username" type="text" class="form-control" name="username" placeholder="Username" required="do it 1" autofocus="" />
48+
<input id="ziti-login-username" type="text" class="form-control" name="username" placeholder="Ziti Username" required="do it 1" autofocus="" />
4949
<span style="padding-top: 5px;">&nbsp;</span>
50-
<input id="ziti-login-password" type="password" class="form-control" style="padding-top: 5px;" name="password" placeholder="Password" required="do it 2"/>
50+
<input id="ziti-login-password" type="password" class="form-control" style="padding-top: 5px;" name="password" placeholder="Ziti Password" required="do it 2"/>
5151
<button id="ziti-login-button" class="btn btn-lg btn-primary btn-block form-signin-button" type="submit">Authenticate</button>
5252
</form>
5353
</div>

0 commit comments

Comments
 (0)