diff --git a/src/connectors/cme.html b/src/connectors/cme.html new file mode 100644 index 00000000..b0e3f138 --- /dev/null +++ b/src/connectors/cme.html @@ -0,0 +1,10 @@ + + + + + + Bitwarden CME Connector + + + + diff --git a/src/connectors/cme.ts b/src/connectors/cme.ts new file mode 100644 index 00000000..168cb32f --- /dev/null +++ b/src/connectors/cme.ts @@ -0,0 +1,118 @@ +import { KeyConnectorUserKeyResponse } from 'jslib-common/models/response/keyConnectorUserKeyResponse'; +import { b64Decode, getQsParam } from './common'; + +document.addEventListener('DOMContentLoaded', () => { + init(); +}); + +let parentUrl: string = null; +let parentOrigin: string = null; +let sentSuccess = false; + +async function init() { + await start(); + onMessage(); +} + +async function start() { + sentSuccess = false; + + const data = getQsParam('data'); + if (!data) { + error('No data.'); + return; + } + + parentUrl = getQsParam('parent'); + if (!parentUrl) { + error('No parent.'); + return; + } else { + parentUrl = decodeURIComponent(parentUrl); + parentOrigin = new URL(parentUrl).origin; + } + + let decodedData: any; + try { + decodedData = JSON.parse(b64Decode(data)); + } + catch (e) { + error('Cannot parse data.'); + return; + } + + const keyConnectorUrl = decodedData.url; + const bearerAccessToken = decodedData.token; + const operation = decodedData.operation; + const key = decodedData.key; + + if (operation === 'get') { + const getRequest = new Request(keyConnectorUrl + '/user-keys', { + cache: 'no-store', + method: 'GET', + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + bearerAccessToken, + }), + }); + getRequest.headers.set('Cache-Control', 'no-store'); + getRequest.headers.set('Pragma', 'no-cache'); + + const response = await fetch(getRequest); + if (response.status !== 200) { + error('Error getting key'); + return; + } + success(new KeyConnectorUserKeyResponse(await response.json())); + } else if (operation === 'post') { + const postRequest = new Request(keyConnectorUrl + '/user-keys', { + cache: 'no-store', + method: 'POST', + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + bearerAccessToken, + 'Content-Type': 'application/json; charset=utf-8', + }), + body: JSON.stringify({key: key}), + }); + + const response = await fetch(postRequest); + if (response.status !== 200) { + error('Error posting key'); + return + } + success(null); + } else { + // TODO: put operation + error('Unsupported operation.'); + } +} + +function onMessage() { + window.addEventListener('message', event => { + if (!event.origin || event.origin === '' || event.origin !== parentOrigin) { + return; + } + + if (event.data === 'start') { + start(); + } + }, false); +} + +function error(message: string) { + parent.postMessage('error|' + message, parentUrl); +} + +function success(response: KeyConnectorUserKeyResponse) { + if (sentSuccess) { + return; + } + const lol = 'success|' + (response != null && response.key != null ? response.key : ''); + parent.postMessage(lol, parentUrl); + sentSuccess = true; +} + +function info(message: string | object) { + parent.postMessage('info|' + JSON.stringify(message), parentUrl); +} diff --git a/webpack.config.js b/webpack.config.js index e9146981..df028968 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -124,6 +124,11 @@ const plugins = [ filename: 'captcha-mobile-connector.html', chunks: ['connectors/captcha'], }), + new HtmlWebpackPlugin({ + template: './src/connectors/cme.html', + filename: 'cme-connector.html', + chunks: ['connectors/cme'], + }), new CopyWebpackPlugin({ patterns:[ { from: './src/.nojekyll' }, @@ -218,6 +223,7 @@ const webpackConfig = { 'connectors/duo': './src/connectors/duo.ts', 'connectors/sso': './src/connectors/sso.ts', 'connectors/captcha': './src/connectors/captcha.ts', + 'connectors/cme': './src/connectors/cme.ts', 'theme_head': './src/theme.js', }, externals: {