support select CA file extension

This commit is contained in:
guox191 2019-03-25 22:47:49 +08:00
parent d9548ae7cf
commit 67151c071b
4 changed files with 89 additions and 28 deletions

View File

@ -1,7 +1,5 @@
'use strict'; 'use strict';
const DEFAULT_WEB_PORT = 8002; // port for web interface
const express = require('express'), const express = require('express'),
url = require('url'), url = require('url'),
bodyParser = require('body-parser'), bodyParser = require('body-parser'),
@ -16,9 +14,13 @@ const express = require('express'),
ip = require('ip'), ip = require('ip'),
compress = require('compression'); compress = require('compression');
const DEFAULT_WEB_PORT = 8002; // port for web interface
const packageJson = require('../package.json'); const packageJson = require('../package.json');
const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb
const certFileTypes = ['crt', 'cer', 'pem', 'der'];
/** /**
* *
* *
@ -204,8 +206,9 @@ class webInterface extends events.EventEmitter {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
const _crtFilePath = certMgr.getRootCAFilePath(); const _crtFilePath = certMgr.getRootCAFilePath();
if (_crtFilePath) { if (_crtFilePath) {
const fileType = certFileTypes.indexOf(req.query.type) !== -1 ? req.query.type : 'crt';
res.setHeader('Content-Type', 'application/x-x509-ca-cert'); res.setHeader('Content-Type', 'application/x-x509-ca-cert');
res.setHeader('Content-Disposition', 'attachment; filename="rootCA.crt"'); res.setHeader('Content-Disposition', `attachment; filename="rootCA.${fileType}"`);
res.end(fs.readFileSync(_crtFilePath, { encoding: null })); res.end(fs.readFileSync(_crtFilePath, { encoding: null }));
} else { } else {
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
@ -213,25 +216,12 @@ class webInterface extends events.EventEmitter {
} }
}); });
//make qr code
app.get('/qr', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/html');
const qr = qrCode.qrcode(4, 'M');
const targetUrl = req.protocol + '://' + req.get('host');
qr.addData(targetUrl);
qr.make();
const qrImageTag = qr.createImgTag(4);
const resDom = '<a href="__url"> __img <br> click or scan qr code to start client </a>'.replace(/__url/, targetUrl).replace(/__img/, qrImageTag);
res.end(resDom);
});
app.get('/api/getQrCode', (req, res) => { app.get('/api/getQrCode', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
const fileType = certFileTypes.indexOf(req.query.type) !== -1 ? req.query.type : 'crt';
const qr = qrCode.qrcode(4, 'M'); const qr = qrCode.qrcode(4, 'M');
const targetUrl = req.protocol + '://' + req.get('host') + '/fetchCrtFile'; const targetUrl = req.protocol + '://' + req.get('host') + '/fetchCrtFile?type=' + fileType;
const isRootCAFileExists = certMgr.isRootCAFileExists(); const isRootCAFileExists = certMgr.isRootCAFileExists();
qr.addData(targetUrl); qr.addData(targetUrl);

View File

@ -0,0 +1,44 @@
const WebInterface = require('../../lib/webInterface.js');
const Recorder = require('../../lib/recorder');
const { directGet } = require('../util/HttpUtil.js');
describe('WebInterface server', () => {
let webServer = null;
let webHost = 'http://127.0.0.1:8002';
beforeAll(() => {
const recorder = new Recorder();
webServer = new WebInterface({
webPort: 8002,
}, recorder);
});
afterAll(() => {
webServer.close();
});
it('should support change CA extensions in /getQrCode', done => {
const certFileTypes = ['crt', 'cer', 'pem', 'der'];
const tasks = certFileTypes.map((type) => {
return directGet(`${webHost}/api/getQrCode`, { type })
.then(res => {
const body = JSON.parse(res.body);
expect(body.qrImgDom).toMatch('<img src="data:image/');
expect(body.url).toBe(`${webHost}/fetchCrtFile?type=${type}`);
});
});
Promise.all(tasks)
.then(done)
.catch(done);
});
it('should fallback to .crt file in /getQrCode', done => {
directGet(`${webHost}/api/getQrCode`, { type: 'unkonw' })
.then(res => {
expect(JSON.parse(res.body).url).toBe(`${webHost}/fetchCrtFile?type=crt`);
done();
})
.catch(done);
});
});

View File

@ -5,26 +5,29 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import ClassBind from 'classnames/bind';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { message, Button, Spin } from 'antd'; import { message, Button, Spin, Select } from 'antd';
import ResizablePanel from 'component/resizable-panel'; import ResizablePanel from 'component/resizable-panel';
import { hideRootCA, updateIsRootCAExists } from 'action/globalStatusAction'; import { hideRootCA, updateIsRootCAExists } from 'action/globalStatusAction';
import { MenuKeyMap } from 'common/Constant'; import { MenuKeyMap } from 'common/Constant';
import { getJSON, ajaxGet, postJSON } from 'common/ApiUtil'; import { getJSON, postJSON } from 'common/ApiUtil';
import Style from './download-root-ca.less'; import Style from './download-root-ca.less';
import CommonStyle from '../style/common.less'; import CommonStyle from '../style/common.less';
const certFileTypes = ['crt', 'cer', 'pem', 'der'];
class DownloadRootCA extends React.Component { class DownloadRootCA extends React.Component {
constructor () { constructor () {
super(); super();
this.state = { this.state = {
loadingCAQr: false, loadingCAQr: false,
generatingCA: false generatingCA: false,
fileType: certFileTypes[0]
}; };
this.onClose = this.onClose.bind(this); this.onClose = this.onClose.bind(this);
this.onFileTypeChange = this.onFileTypeChange.bind(this);
this.getQrCodeContent = this.getQrCodeContent.bind(this); this.getQrCodeContent = this.getQrCodeContent.bind(this);
} }
@ -38,7 +41,7 @@ class DownloadRootCA extends React.Component {
loadingCAQr: true loadingCAQr: true
}); });
getJSON('/api/getQrCode') getJSON('/api/getQrCode', { type: this.state.fileType })
.then((response) => { .then((response) => {
this.setState({ this.setState({
loadingCAQr: false, loadingCAQr: false,
@ -57,12 +60,33 @@ class DownloadRootCA extends React.Component {
this.props.dispatch(hideRootCA()); this.props.dispatch(hideRootCA());
} }
onFileTypeChange (value) {
this.setState({
fileType: value
}, () => {
this.fetchData();
});
}
getQrCodeContent () { getQrCodeContent () {
const imgDomContent = { __html: this.state.CAQrCodeImageDom }; const imgDomContent = { __html: this.state.CAQrCodeImageDom };
const content = ( const content = (
<div className={Style.qrCodeWrapper} > <div className={Style.qrCodeWrapper} >
<div dangerouslySetInnerHTML={imgDomContent} /> <div dangerouslySetInnerHTML={imgDomContent} />
<span>Scan to download rootCA.crt to your Phone</span> <div>Scan to download rootCA.{this.state.fileType} to your Phone</div>
<div>You can change the CA's file extension:
<Select
defaultValue={this.state.fileType}
className={Style.fileSelect}
onChange={this.onFileTypeChange}
>
{
certFileTypes.map(key => (
<Option key={key} value={key}>{key}</Option>
))
}
</Select>
</div>
</div> </div>
); );
@ -71,7 +95,6 @@ class DownloadRootCA extends React.Component {
} }
getGenerateRootCADiv () { getGenerateRootCADiv () {
const doToggleRemoteIntercept = () => { const doToggleRemoteIntercept = () => {
postJSON('/api/generateRootCA') postJSON('/api/generateRootCA')
.then((result) => { .then((result) => {
@ -127,8 +150,8 @@ class DownloadRootCA extends React.Component {
</div> </div>
<div className={Style.buttons} > <div className={Style.buttons} >
<a href="/fetchCrtFile" target="_blank"> <a href={`/fetchCrtFile?type=${this.state.fileType}`} target="_blank">
<Button type="primary" size="large" > Download </Button> <Button type="primary" size="large" >Download</Button>
</a> </a>
<span className={Style.tipSpan} >Or click the button to download.</span> <span className={Style.tipSpan} >Or click the button to download.</span>
</div> </div>
@ -146,7 +169,6 @@ class DownloadRootCA extends React.Component {
return ( return (
<ResizablePanel onClose={this.onClose} visible={panelVisible} > <ResizablePanel onClose={this.onClose} visible={panelVisible} >
{this.props.globalStatus.isRootCAFileExists ? this.getDownloadDiv() : this.getGenerateRootCADiv()} {this.props.globalStatus.isRootCAFileExists ? this.getDownloadDiv() : this.getGenerateRootCADiv()}
</ResizablePanel> </ResizablePanel>
); );
} }

View File

@ -55,4 +55,9 @@
margin-top: 18px; margin-top: 18px;
display: block; display: block;
} }
}
.fileSelect {
width: 60px;
margin-left: 8px;
} }