2017-02-28 21:32:23 +00:00
|
|
|
import React, {PureComponent, PropTypes} from 'react';
|
2017-03-03 01:44:07 +00:00
|
|
|
import autobind from 'autobind-decorator';
|
2016-11-29 01:47:04 +00:00
|
|
|
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
|
|
|
|
import DebouncedInput from '../base/DebouncedInput';
|
|
|
|
import FileInputButton from '../base/FileInputButton';
|
2016-11-28 20:16:07 +00:00
|
|
|
import Modal from '../base/Modal';
|
|
|
|
import ModalBody from '../base/ModalBody';
|
|
|
|
import ModalHeader from '../base/ModalHeader';
|
2016-11-28 20:58:41 +00:00
|
|
|
import PromptButton from '../base/PromptButton';
|
2016-11-29 01:47:04 +00:00
|
|
|
import * as models from '../../../models/index';
|
|
|
|
import * as fs from 'fs';
|
2016-12-05 22:42:40 +00:00
|
|
|
import {trackEvent} from '../../../analytics/index';
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
@autobind
|
2017-02-28 21:32:23 +00:00
|
|
|
class WorkspaceSettingsModal extends PureComponent {
|
2017-03-03 01:44:07 +00:00
|
|
|
constructor (props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
showAddCertificateForm: false,
|
|
|
|
crtPath: '',
|
|
|
|
keyPath: '',
|
|
|
|
pfxPath: '',
|
|
|
|
host: '',
|
2017-03-03 20:09:08 +00:00
|
|
|
passphrase: ''
|
2017-03-03 01:44:07 +00:00
|
|
|
};
|
|
|
|
}
|
2016-11-28 20:16:07 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_workspaceUpdate (patch) {
|
|
|
|
models.workspace.update(this.props.workspace, patch);
|
|
|
|
}
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleSetModalRef (n) {
|
|
|
|
this.modal = n;
|
|
|
|
}
|
|
|
|
_handleRemoveWorkspace () {
|
2016-11-29 01:47:04 +00:00
|
|
|
this.props.handleRemoveWorkspace();
|
|
|
|
this.hide();
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 20:09:08 +00:00
|
|
|
_handleToggleCertificateForm () {
|
|
|
|
this.setState({showAddCertificateForm: !this.state.showAddCertificateForm});
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleRename (name) {
|
|
|
|
this._workspaceUpdate({name});
|
|
|
|
}
|
2017-03-03 20:09:08 +00:00
|
|
|
_handleDescriptionChange (description) {
|
2017-03-03 01:44:07 +00:00
|
|
|
this._workspaceUpdate({description});
|
|
|
|
}
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleCreateHostChange (e) {
|
|
|
|
this.setState({host: e.target.value});
|
|
|
|
}
|
|
|
|
_handleCreatePfxChange (pfxPath) {
|
|
|
|
this.setState({pfxPath});
|
|
|
|
}
|
2017-03-03 20:09:08 +00:00
|
|
|
_handleCreateCrtChange (crtPath) {
|
2017-03-03 01:44:07 +00:00
|
|
|
this.setState({crtPath});
|
|
|
|
}
|
|
|
|
_handleCreateKeyChange (keyPath) {
|
|
|
|
this.setState({keyPath});
|
|
|
|
}
|
|
|
|
_handleCreatePassphraseChange (e) {
|
|
|
|
this.setState({passphrase: e.target.value});
|
|
|
|
}
|
2017-03-03 20:09:08 +00:00
|
|
|
async _handleSubmitCertificate (e) {
|
2016-11-29 01:47:04 +00:00
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const {workspace} = this.props;
|
2016-11-29 20:19:21 +00:00
|
|
|
const {pfxPath, crtPath, keyPath, host, passphrase} = this.state;
|
|
|
|
const cert = crtPath ? fs.readFileSync(crtPath, 'base64') : null;
|
|
|
|
const key = keyPath ? fs.readFileSync(keyPath, 'base64') : null;
|
|
|
|
const pfx = pfxPath ? fs.readFileSync(pfxPath, 'base64') : null;
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2016-11-29 20:19:21 +00:00
|
|
|
const certificate = {host, passphrase, cert, key, pfx, disabled: false};
|
2016-11-29 01:47:04 +00:00
|
|
|
const certificates = [
|
|
|
|
...workspace.certificates.filter(c => c.host !== certificate.host),
|
2017-03-03 20:09:08 +00:00
|
|
|
certificate
|
2016-11-29 01:47:04 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
await models.workspace.update(workspace, {certificates});
|
|
|
|
this._handleToggleCertificateForm();
|
2016-12-05 22:42:40 +00:00
|
|
|
trackEvent('Certificates', 'Create');
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleDeleteCertificate (certificate) {
|
2016-11-29 01:47:04 +00:00
|
|
|
const {workspace} = this.props;
|
|
|
|
const certificates = workspace.certificates.filter(c => c.host !== certificate.host);
|
|
|
|
models.workspace.update(workspace, {certificates});
|
2016-12-05 22:42:40 +00:00
|
|
|
trackEvent('Certificates', 'Delete');
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-29 01:47:04 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleToggleCertificate (certificate) {
|
2016-11-29 01:47:04 +00:00
|
|
|
const {workspace} = this.props;
|
|
|
|
const certificates = workspace.certificates.map(
|
|
|
|
c => c === certificate ? Object.assign({}, c, {disabled: !c.disabled}) : c
|
|
|
|
);
|
|
|
|
models.workspace.update(workspace, {certificates});
|
2016-12-05 22:42:40 +00:00
|
|
|
trackEvent('Certificates', 'Toggle');
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-28 20:16:07 +00:00
|
|
|
|
2016-11-28 20:58:41 +00:00
|
|
|
toggle (workspace) {
|
|
|
|
this.modal.toggle();
|
2016-11-29 20:19:21 +00:00
|
|
|
this.setState({
|
|
|
|
workspace,
|
|
|
|
showAddCertificateForm: false,
|
|
|
|
crtPath: '',
|
|
|
|
keyPath: '',
|
|
|
|
pfxPath: '',
|
|
|
|
host: '',
|
2017-03-03 20:09:08 +00:00
|
|
|
passphrase: ''
|
2016-11-29 20:19:21 +00:00
|
|
|
});
|
2016-11-28 20:58:41 +00:00
|
|
|
}
|
|
|
|
|
2016-11-29 01:47:04 +00:00
|
|
|
show () {
|
2016-11-28 20:16:07 +00:00
|
|
|
this.modal.show();
|
2016-11-29 20:19:21 +00:00
|
|
|
this.setState({
|
|
|
|
showAddCertificateForm: false,
|
|
|
|
crtPath: '',
|
|
|
|
keyPath: '',
|
|
|
|
pfxPath: '',
|
|
|
|
host: '',
|
2017-03-03 20:09:08 +00:00
|
|
|
passphrase: ''
|
2016-11-29 20:19:21 +00:00
|
|
|
});
|
2016-11-28 20:16:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hide () {
|
|
|
|
this.modal.hide();
|
|
|
|
}
|
|
|
|
|
2016-11-29 01:47:04 +00:00
|
|
|
renderModalHeader () {
|
|
|
|
const {workspace} = this.props;
|
2016-11-28 20:58:41 +00:00
|
|
|
return (
|
2016-11-29 01:47:04 +00:00
|
|
|
<ModalHeader key={`header::${workspace._id}`}>
|
2016-11-30 23:11:36 +00:00
|
|
|
Workspace Settings
|
2016-11-29 01:47:04 +00:00
|
|
|
</ModalHeader>
|
2017-03-03 20:09:08 +00:00
|
|
|
);
|
2016-11-28 20:58:41 +00:00
|
|
|
}
|
2016-11-28 20:16:07 +00:00
|
|
|
|
2016-11-29 01:47:04 +00:00
|
|
|
renderModalBody () {
|
|
|
|
const {workspace} = this.props;
|
2016-11-29 20:19:21 +00:00
|
|
|
const {pfxPath, crtPath, keyPath, showAddCertificateForm} = this.state;
|
2016-11-28 20:16:07 +00:00
|
|
|
return (
|
2017-03-01 21:15:56 +00:00
|
|
|
<ModalBody key={`body::${workspace._id}`} noScroll>
|
|
|
|
<Tabs forceRenderTabPanel>
|
2016-11-29 01:47:04 +00:00
|
|
|
<TabList>
|
|
|
|
<Tab>
|
|
|
|
<button>Overview</button>
|
|
|
|
</Tab>
|
|
|
|
<Tab>
|
2016-11-30 23:11:36 +00:00
|
|
|
<button>Client Certificates</button>
|
2016-11-29 01:47:04 +00:00
|
|
|
</Tab>
|
|
|
|
</TabList>
|
|
|
|
<TabPanel className="pad no-pad-top scrollable">
|
|
|
|
<div className="row-fill">
|
|
|
|
<div className="form-control form-control--outlined">
|
|
|
|
<label>Name
|
|
|
|
<DebouncedInput
|
|
|
|
type="text"
|
2017-03-01 21:15:56 +00:00
|
|
|
delay={500}
|
2016-11-29 01:47:04 +00:00
|
|
|
placeholder="Awesome API"
|
|
|
|
defaultValue={workspace.name}
|
|
|
|
onChange={this._handleRename}
|
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="form-control form-control--outlined">
|
|
|
|
<label>Description
|
|
|
|
<DebouncedInput
|
2017-03-01 21:15:56 +00:00
|
|
|
textarea
|
|
|
|
delay={500}
|
2016-11-29 01:47:04 +00:00
|
|
|
rows="4"
|
|
|
|
placeholder="This workspace is for testing the Awesome API!"
|
|
|
|
defaultValue={workspace.description}
|
|
|
|
onChange={this._handleDescriptionChange}
|
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div className="form-control form-control--padded">
|
|
|
|
<label htmlFor="nothing">Danger Zone
|
|
|
|
<PromptButton onClick={this._handleRemoveWorkspace}
|
2017-03-01 21:15:56 +00:00
|
|
|
addIcon
|
2016-11-29 01:47:04 +00:00
|
|
|
className="width-auto btn btn--clicky">
|
|
|
|
<i className="fa fa-trash-o"/> Delete Workspace
|
|
|
|
</PromptButton>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel className="pad scrollable">
|
|
|
|
{!showAddCertificateForm ? (
|
|
|
|
<div>
|
|
|
|
{workspace.certificates.length === 0 ? (
|
|
|
|
<p className="notice info margin-top-sm">
|
2016-11-30 23:11:36 +00:00
|
|
|
You have not yet added any certificates
|
2016-11-29 01:47:04 +00:00
|
|
|
</p>
|
|
|
|
) : workspace.certificates.map(certificate => (
|
|
|
|
<div key={certificate.host} className="row-spaced">
|
|
|
|
<div>
|
|
|
|
<span className="pad-right no-wrap">
|
2016-11-29 20:19:21 +00:00
|
|
|
<strong>PFX:</strong>
|
2017-03-03 20:09:08 +00:00
|
|
|
{' '}
|
|
|
|
{certificate.pfx
|
|
|
|
? <i className="fa fa-check"/>
|
|
|
|
: <i className="fa fa-remove"/>
|
2016-11-29 20:19:21 +00:00
|
|
|
}
|
|
|
|
</span>
|
|
|
|
<span className="pad-right no-wrap">
|
|
|
|
<strong>CRT:</strong>
|
2017-03-03 20:09:08 +00:00
|
|
|
{' '}
|
|
|
|
{certificate.cert
|
|
|
|
? <i className="fa fa-check"/>
|
|
|
|
: <i className="fa fa-remove"/>
|
2016-11-29 20:19:21 +00:00
|
|
|
}
|
|
|
|
</span>
|
|
|
|
<span className="pad-right no-wrap">
|
|
|
|
<strong>Key:</strong>
|
2017-03-03 20:09:08 +00:00
|
|
|
{' '}
|
|
|
|
{certificate.key
|
|
|
|
? <i className="fa fa-check"/>
|
|
|
|
: <i className="fa fa-remove"/>
|
2016-11-29 01:47:04 +00:00
|
|
|
}
|
2016-11-29 20:19:21 +00:00
|
|
|
</span>
|
2016-11-29 21:28:22 +00:00
|
|
|
<span className="pad-right no-wrap" title={certificate.passphrase || null}>
|
2016-11-29 20:19:21 +00:00
|
|
|
<strong>Passphrase:</strong>
|
2017-03-03 20:09:08 +00:00
|
|
|
{' '}
|
|
|
|
{certificate.passphrase
|
|
|
|
? <i className="fa fa-check"/>
|
|
|
|
: <i className="fa fa-remove"/>
|
2016-11-29 01:47:04 +00:00
|
|
|
}
|
2016-11-29 20:19:21 +00:00
|
|
|
</span>
|
2016-11-29 01:47:04 +00:00
|
|
|
<span className="pad-right">
|
2016-11-29 21:28:22 +00:00
|
|
|
<strong>Host:</strong>
|
2017-03-03 20:09:08 +00:00
|
|
|
{' '}
|
2016-11-29 21:28:22 +00:00
|
|
|
<span className="monospace selectable">{certificate.host}</span>
|
2016-11-29 20:19:21 +00:00
|
|
|
</span>
|
2016-11-29 01:47:04 +00:00
|
|
|
</div>
|
|
|
|
<div className="no-wrap">
|
|
|
|
<button className="btn btn--super-compact width-auto"
|
|
|
|
title="Enable or disable certificate"
|
|
|
|
onClick={() => this._handleToggleCertificate(certificate)}>
|
2017-03-03 20:09:08 +00:00
|
|
|
{certificate.disabled
|
|
|
|
? <i className="fa fa-square-o"/>
|
|
|
|
: <i className="fa fa-check-square-o"/>
|
2016-11-29 01:47:04 +00:00
|
|
|
}
|
|
|
|
</button>
|
|
|
|
<PromptButton className="btn btn--super-compact width-auto"
|
|
|
|
confirmMessage=" "
|
2017-03-01 21:15:56 +00:00
|
|
|
addIcon
|
2016-11-29 01:47:04 +00:00
|
|
|
onClick={() => this._handleDeleteCertificate(certificate)}>
|
|
|
|
<i className="fa fa-trash-o"></i>
|
|
|
|
</PromptButton>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
<hr className="hr--spaced"/>
|
|
|
|
<div className="text-center">
|
|
|
|
<button className="btn btn--clicky auto"
|
|
|
|
onClick={this._handleToggleCertificateForm}>
|
|
|
|
Add Certificate
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<form onSubmit={this._handleSubmitCertificate}>
|
|
|
|
<div className="form-control form-control--outlined no-pad-top">
|
2016-11-29 20:19:21 +00:00
|
|
|
<label>Host <span className="faint">(port optional)</span>
|
2016-11-29 01:47:04 +00:00
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
required="required"
|
|
|
|
placeholder="my-api.com"
|
|
|
|
autoFocus="autoFocus"
|
|
|
|
onChange={this._handleCreateHostChange}
|
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
</div>
|
2016-12-21 23:37:48 +00:00
|
|
|
<div className="form-row">
|
|
|
|
<div className="form-control width-auto">
|
2016-11-29 20:19:21 +00:00
|
|
|
<label>PFX <span className="faint">(or PKCS12)</span>
|
2016-11-29 01:47:04 +00:00
|
|
|
<FileInputButton
|
|
|
|
className="btn btn--clicky"
|
2016-11-29 20:19:21 +00:00
|
|
|
onChange={this._handleCreatePfxChange}
|
|
|
|
path={pfxPath}
|
2017-03-01 21:15:56 +00:00
|
|
|
showFileName
|
2016-11-29 01:47:04 +00:00
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
</div>
|
2016-12-21 23:37:48 +00:00
|
|
|
<div className="text-center">
|
2016-11-29 20:19:21 +00:00
|
|
|
<br/><br/>
|
|
|
|
Or
|
2016-11-29 01:47:04 +00:00
|
|
|
</div>
|
2016-12-21 23:37:48 +00:00
|
|
|
<div className="row-fill">
|
2016-11-29 20:19:21 +00:00
|
|
|
<div className="form-control">
|
|
|
|
<label>CRT File
|
|
|
|
<FileInputButton
|
|
|
|
className="btn btn--clicky"
|
|
|
|
name="Cert"
|
|
|
|
onChange={this._handleCreateCrtChange}
|
|
|
|
path={crtPath}
|
2017-03-01 21:15:56 +00:00
|
|
|
showFileName
|
2016-11-29 20:19:21 +00:00
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div className="form-control">
|
|
|
|
<label>Key File
|
|
|
|
<FileInputButton
|
|
|
|
className="btn btn--clicky"
|
|
|
|
name="Key"
|
|
|
|
onChange={this._handleCreateKeyChange}
|
|
|
|
path={keyPath}
|
2017-03-01 21:15:56 +00:00
|
|
|
showFileName/>
|
2016-11-29 20:19:21 +00:00
|
|
|
</label>
|
|
|
|
</div>
|
2016-11-29 01:47:04 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2016-11-29 20:19:21 +00:00
|
|
|
<div className="form-control form-control--outlined">
|
|
|
|
<label>Passphrase
|
|
|
|
<input
|
|
|
|
type="password"
|
|
|
|
placeholder="•••••••••••"
|
|
|
|
onChange={this._handleCreatePassphraseChange}
|
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
</div>
|
2016-11-29 01:47:04 +00:00
|
|
|
<br/>
|
|
|
|
<div className="pad-top text-right">
|
|
|
|
<button type="button"
|
|
|
|
className="btn btn--super-compact"
|
|
|
|
onClick={this._handleToggleCertificateForm}>
|
|
|
|
Cancel
|
|
|
|
</button>
|
2017-03-03 20:09:08 +00:00
|
|
|
{' '}
|
2016-11-29 01:47:04 +00:00
|
|
|
<button className="btn btn--clicky" type="submit">
|
2016-12-05 22:42:40 +00:00
|
|
|
Add Certificate
|
2016-11-29 01:47:04 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
)}
|
|
|
|
</TabPanel>
|
|
|
|
</Tabs>
|
|
|
|
</ModalBody>
|
2017-03-03 20:09:08 +00:00
|
|
|
);
|
2016-11-28 20:16:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
2016-11-29 01:47:04 +00:00
|
|
|
const {workspace} = this.props;
|
2016-11-28 20:16:07 +00:00
|
|
|
return (
|
2017-03-01 21:15:56 +00:00
|
|
|
<Modal ref={this._handleSetModalRef} tall freshState>
|
2016-11-29 01:47:04 +00:00
|
|
|
{workspace ? this.renderModalHeader() : null}
|
|
|
|
{workspace ? this.renderModalBody() : null}
|
2016-11-28 20:16:07 +00:00
|
|
|
</Modal>
|
2017-03-03 20:09:08 +00:00
|
|
|
);
|
2016-11-28 20:16:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-29 01:47:04 +00:00
|
|
|
WorkspaceSettingsModal.propTypes = {
|
|
|
|
handleRemoveWorkspace: PropTypes.func.isRequired,
|
2017-03-03 20:09:08 +00:00
|
|
|
workspace: PropTypes.object.isRequired
|
2016-11-29 01:47:04 +00:00
|
|
|
};
|
2016-11-28 20:16:07 +00:00
|
|
|
|
|
|
|
export default WorkspaceSettingsModal;
|