From 7041853556506ed133e4d3c78d74b6a786d8b811 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Mon, 29 Jan 2018 14:43:58 +0800 Subject: [PATCH] AWS Multi-Factor Authentication (#730) * Support AWS Multifactor Auth (#719) * Back end support * Update to latest aws4 dep (#719) * Mockup AWS MFA UI (#719) * Add enabled to most auth panes * Some more work --- packages/insomnia-app/app/models/request.js | 3 +- .../app/network/__tests__/network.test.js | 23 ++- packages/insomnia-app/app/network/network.js | 13 +- .../app/ui/components/base/button.js | 7 +- .../ui/components/editors/auth/asap-auth.js | 128 +++++++++----- .../components/editors/auth/auth-wrapper.js | 5 +- .../ui/components/editors/auth/aws-auth.js | 158 +++++++++++++----- .../ui/components/editors/auth/basic-auth.js | 131 ++++++++++----- .../ui/components/editors/auth/bearer-auth.js | 80 ++++++--- .../ui/components/editors/auth/digest-auth.js | 131 ++++++++++----- .../ui/components/editors/auth/hawk-auth.js | 65 ++++--- .../ui/components/editors/auth/ntlm-auth.js | 131 ++++++++++----- .../app/ui/components/key-value-editor/row.js | 9 +- .../app/ui/css/components/forms.less | 11 ++ .../ui/css/components/key-value-editor.less | 9 - 15 files changed, 629 insertions(+), 275 deletions(-) diff --git a/packages/insomnia-app/app/models/request.js b/packages/insomnia-app/app/models/request.js index bf4289ad20..8a81d76a18 100644 --- a/packages/insomnia-app/app/models/request.js +++ b/packages/insomnia-app/app/models/request.js @@ -130,7 +130,8 @@ export function newAuth (type: string, oldAuth: RequestAuthentication = {}): Req type, disabled: oldAuth.disabled || false, accessKeyId: oldAuth.accessKeyId || '', - secretAccessKey: oldAuth.secretAccessKey || '' + secretAccessKey: oldAuth.secretAccessKey || '', + sessionToken: oldAuth.sessionToken || '' }; // Hawk diff --git a/packages/insomnia-app/app/network/__tests__/network.test.js b/packages/insomnia-app/app/network/__tests__/network.test.js index cd6df95040..788b68a4ee 100644 --- a/packages/insomnia-app/app/network/__tests__/network.test.js +++ b/packages/insomnia-app/app/network/__tests__/network.test.js @@ -558,16 +558,21 @@ describe('_getAwsAuthHeaders', () => { authentication: { type: AUTH_AWS_IAM, accessKeyId: 'AKIA99999999', - secretAccessKey: 'SAK9999999999999' + secretAccessKey: 'SAK9999999999999', + sessionToken: 'ST9999999999999999' }, headers: [{name: 'content-type', value: 'application/json'}], body: {text: '{}'}, method: 'POST', url: 'https://example.com/path?query=q1' }; + const credentials = { + accessKeyId: req.authentication.accessKeyId || '', + secretAccessKey: req.authentication.secretAccessKey || '', + sessionToken: req.authentication.sessionToken || '' + }; const headers = networkUtils._getAwsAuthHeaders( - req.authentication.accessKeyId, - req.authentication.secretAccessKey, + credentials, req.headers, req.body.text, req.url, @@ -587,7 +592,8 @@ describe('_getAwsAuthHeaders', () => { authentication: { type: AUTH_AWS_IAM, accessKeyId: 'AKIA99999999', - secretAccessKey: 'SAK9999999999999' + secretAccessKey: 'SAK9999999999999', + sessionToken: 'ST99999999999999' }, headers: [ 'Accept: */*', @@ -596,10 +602,13 @@ describe('_getAwsAuthHeaders', () => { url: 'https://example.com', method: 'GET' }; - + const credentials = { + accessKeyId: req.authentication.accessKeyId || '', + secretAccessKey: req.authentication.secretAccessKey || '', + sessionToken: req.authentication.sessionToken || '' + }; const headers = networkUtils._getAwsAuthHeaders( - req.authentication.accessKeyId, - req.authentication.secretAccessKey, + credentials, req.headers, null, req.url, diff --git a/packages/insomnia-app/app/network/network.js b/packages/insomnia-app/app/network/network.js index 23f9499320..9563be161d 100644 --- a/packages/insomnia-app/app/network/network.js +++ b/packages/insomnia-app/app/network/network.js @@ -493,9 +493,13 @@ export async function _actuallySend ( return handleError( new Error('AWS authentication not supported for provided body type')); } + const credentials = { + accessKeyId: renderedRequest.authentication.accessKeyId || '', + secretAccessKey: renderedRequest.authentication.secretAccessKey || '', + sessionToken: renderedRequest.authentication.sessionToken || '' + }; const extraHeaders = _getAwsAuthHeaders( - renderedRequest.authentication.accessKeyId || '', - renderedRequest.authentication.secretAccessKey || '', + credentials, headers, requestBody || '', finalUrl, @@ -853,15 +857,12 @@ export function _parseHeaders ( // exported for unit tests only export function _getAwsAuthHeaders ( - accessKeyId: string, - secretAccessKey: string, + credentials: Object, headers: Array, body: string, url: string, method: string ) { - const credentials = {accessKeyId, secretAccessKey}; - const parsedUrl = urlParse(url); const contentTypeHeader = getContentTypeHeader(headers); diff --git a/packages/insomnia-app/app/ui/components/base/button.js b/packages/insomnia-app/app/ui/components/base/button.js index 85cd54c799..edd517c21d 100644 --- a/packages/insomnia-app/app/ui/components/base/button.js +++ b/packages/insomnia-app/app/ui/components/base/button.js @@ -21,11 +21,13 @@ class Button extends PureComponent { disabled, tabIndex, className, - type + type, + id } = this.props; return ( @@ -141,11 +167,35 @@ class AsapAuth extends React.PureComponent { render () { const fields = this.renderAsapAuthenticationFields(); + const {authentication} = this.props; return (
- {fields} + + {fields} + + + + +
+ + +
+ +
+
); diff --git a/packages/insomnia-app/app/ui/components/editors/auth/auth-wrapper.js b/packages/insomnia-app/app/ui/components/editors/auth/auth-wrapper.js index 9bdebd19fe..17e741c406 100644 --- a/packages/insomnia-app/app/ui/components/editors/auth/auth-wrapper.js +++ b/packages/insomnia-app/app/ui/components/editors/auth/auth-wrapper.js @@ -57,7 +57,7 @@ class AuthWrapper extends PureComponent { } else if (authentication.type === AUTH_HAWK) { return ( +
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+
); } } diff --git a/packages/insomnia-app/app/ui/components/editors/auth/basic-auth.js b/packages/insomnia-app/app/ui/components/editors/auth/basic-auth.js index fbcf2cae42..fe39c759ac 100644 --- a/packages/insomnia-app/app/ui/components/editors/auth/basic-auth.js +++ b/packages/insomnia-app/app/ui/components/editors/auth/basic-auth.js @@ -1,34 +1,28 @@ import React, {PureComponent} from 'react'; +import classnames from 'classnames'; import PropTypes from 'prop-types'; import autobind from 'autobind-decorator'; -import KeyValueEditor from '../../key-value-editor/editor'; -import {trackEvent} from '../../../../common/analytics'; -import {AUTH_BASIC} from '../../../../common/constants'; +import OneLineEditor from '../../codemirror/one-line-editor'; +import Button from '../../base/button'; @autobind class BasicAuth extends PureComponent { - _handleOnCreate () { - trackEvent('Basic Auth Editor', 'Create'); + _handleDisable () { + const {authentication} = this.props; + authentication.disabled = !authentication.disabled; + this.props.onChange(authentication); } - _handleOnDelete () { - trackEvent('Basic Auth Editor', 'Delete'); + _handleChangeUsername (value) { + const {authentication} = this.props; + authentication.username = value; + this.props.onChange(authentication); } - _handleToggleDisable (pair) { - const label = pair.disabled ? 'Disable' : 'Enable'; - trackEvent('Basic Auth Editor', 'Toggle', label); - } - - _handleChange (pairs) { - const pair = { - type: AUTH_BASIC, - username: pairs.length ? pairs[0].name : '', - password: pairs.length ? pairs[0].value : '', - disabled: pairs.length ? pairs[0].disabled : false - }; - - this.props.onChange(pair); + _handleChangePassword (value) { + const {authentication} = this.props; + authentication.password = value; + this.props.onChange(authentication); } render () { @@ -40,28 +34,81 @@ class BasicAuth extends PureComponent { nunjucksPowerUserMode } = this.props; - const pairs = [{ - name: authentication.username || '', - value: authentication.password || '', - disabled: authentication.disabled || false - }]; - return ( - +
+ + + + + + + + + + + + + + + +
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+
); } } diff --git a/packages/insomnia-app/app/ui/components/editors/auth/bearer-auth.js b/packages/insomnia-app/app/ui/components/editors/auth/bearer-auth.js index e364f60a20..1c8d396175 100644 --- a/packages/insomnia-app/app/ui/components/editors/auth/bearer-auth.js +++ b/packages/insomnia-app/app/ui/components/editors/auth/bearer-auth.js @@ -1,38 +1,78 @@ import React, {PureComponent} from 'react'; +import classnames from 'classnames'; import PropTypes from 'prop-types'; import autobind from 'autobind-decorator'; import OneLineEditor from '../../codemirror/one-line-editor'; -import * as misc from '../../../../common/misc'; +import Button from '../../base/button'; @autobind class BearerAuth extends PureComponent { - constructor (props) { - super(props); - - this._handleChangeProperty = misc.debounce(this._handleChangeProperty, 500); + _handleDisable () { + const {authentication} = this.props; + authentication.disabled = !authentication.disabled; + this.props.onChange(authentication); } _handleChange (token) { - const {request} = this.props; - const authentication = Object.assign({}, request.authentication, {token}); + const {authentication} = this.props; + authentication.token = token; this.props.onChange(authentication); } render () { - const {request, handleRender, handleGetRenderContext, nunjucksPowerUserMode} = this.props; - const {token} = request.authentication; + const {authentication, handleRender, handleGetRenderContext, nunjucksPowerUserMode} = this.props; return ( -
- +
+ + + + + + + + + + + +
+ + +
+ +
+
+ + +
+ +
+
); } @@ -42,7 +82,7 @@ BearerAuth.propTypes = { handleRender: PropTypes.func.isRequired, handleGetRenderContext: PropTypes.func.isRequired, nunjucksPowerUserMode: PropTypes.bool.isRequired, - request: PropTypes.object.isRequired, + authentication: PropTypes.object.isRequired, onChange: PropTypes.func.isRequired }; diff --git a/packages/insomnia-app/app/ui/components/editors/auth/digest-auth.js b/packages/insomnia-app/app/ui/components/editors/auth/digest-auth.js index d1d23f72c5..a28843f150 100644 --- a/packages/insomnia-app/app/ui/components/editors/auth/digest-auth.js +++ b/packages/insomnia-app/app/ui/components/editors/auth/digest-auth.js @@ -1,34 +1,28 @@ import React, {PureComponent} from 'react'; +import classnames from 'classnames'; import PropTypes from 'prop-types'; import autobind from 'autobind-decorator'; -import KeyValueEditor from '../../key-value-editor/editor'; -import {trackEvent} from '../../../../common/analytics'; -import {AUTH_DIGEST} from '../../../../common/constants'; +import OneLineEditor from '../../codemirror/one-line-editor'; +import Button from '../../base/button'; @autobind class DigestAuth extends PureComponent { - _handleOnCreate () { - trackEvent('Digest Auth Editor', 'Create'); + _handleDisable () { + const {authentication} = this.props; + authentication.disabled = !authentication.disabled; + this.props.onChange(authentication); } - _handleOnDelete () { - trackEvent('Digest Auth Editor', 'Delete'); + _handleChangeUsername (value) { + const {authentication} = this.props; + authentication.username = value; + this.props.onChange(authentication); } - _handleToggleDisable (pair) { - const label = pair.disabled ? 'Disable' : 'Enable'; - trackEvent('Digest Auth Editor', 'Toggle', label); - } - - _handleChange (pairs) { - const pair = { - type: AUTH_DIGEST, - username: pairs.length ? pairs[0].name : '', - password: pairs.length ? pairs[0].value : '', - disabled: pairs.length ? pairs[0].disabled : false - }; - - this.props.onChange(pair); + _handleChangePassword (value) { + const {authentication} = this.props; + authentication.password = value; + this.props.onChange(authentication); } render () { @@ -40,28 +34,81 @@ class DigestAuth extends PureComponent { handleGetRenderContext } = this.props; - const pairs = [{ - name: authentication.username || '', - value: authentication.password || '', - disabled: authentication.disabled || false - }]; - return ( - +
+ + + + + + + + + + + + + + + +
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+
); } } diff --git a/packages/insomnia-app/app/ui/components/editors/auth/hawk-auth.js b/packages/insomnia-app/app/ui/components/editors/auth/hawk-auth.js index 659d23ae79..8ce63fc224 100644 --- a/packages/insomnia-app/app/ui/components/editors/auth/hawk-auth.js +++ b/packages/insomnia-app/app/ui/components/editors/auth/hawk-auth.js @@ -1,15 +1,16 @@ // @flow -import type {Request} from '../../../../models/request'; +import type {RequestAuthentication} from '../../../../models/request'; import * as React from 'react'; +import classnames from 'classnames'; import autobind from 'autobind-decorator'; import OneLineEditor from '../../codemirror/one-line-editor'; -import * as misc from '../../../../common/misc'; import {HAWK_ALGORITHM_SHA1, HAWK_ALGORITHM_SHA256} from '../../../../common/constants'; import HelpTooltip from '../../help-tooltip'; +import Button from '../../base/button'; type Props = { - request: Request, + authentication: RequestAuthentication, handleRender: Function, handleGetRenderContext: Function, nunjucksPowerUserMode: boolean, @@ -18,17 +19,15 @@ type Props = { @autobind class HawkAuth extends React.PureComponent { - _handleChangeProperty: Function; - - constructor (props: any) { - super(props); - - this._handleChangeProperty = misc.debounce(this._handleChangeProperty, 500); + _handleDisable () { + const {authentication} = this.props; + authentication.disabled = !authentication.disabled; + this.props.onChange(authentication); } _handleChangeProperty (property: string, value: string | boolean): void { - const {request} = this.props; - const authentication = Object.assign({}, request.authentication, {[property]: value}); + const {authentication} = this.props; + authentication[property] = value; this.props.onChange(authentication); } @@ -77,10 +76,10 @@ class HawkAuth extends React.PureComponent { onChange: Function, help: string | null = null ): React.Element<*> { - const {request} = this.props; + const {authentication} = this.props; const id = label.replace(/ /g, '-'); - const value = request.authentication.hasOwnProperty(property) - ? request.authentication[property] + const value = authentication.hasOwnProperty(property) + ? authentication[property] : options[0]; return ( @@ -92,7 +91,9 @@ class HawkAuth extends React.PureComponent { -
+