From 2e8ebaee0cb42caa4154cf15a163ab4518cb248b Mon Sep 17 00:00:00 2001 From: Oskar Damkjaer Date: Wed, 28 Oct 2020 12:33:14 +0100 Subject: [PATCH 1/5] Update connection form to better reflect aura usage --- .../modules/Stream/Auth/ConnectForm.tsx | 54 +++++++++++-------- .../modules/Stream/Auth/ConnectionForm.jsx | 11 ++-- .../modules/Stream/Auth/ConnectionFrame.jsx | 33 +++++++----- src/shared/modules/app/appDuck.js | 5 ++ 4 files changed, 67 insertions(+), 36 deletions(-) diff --git a/src/browser/modules/Stream/Auth/ConnectForm.tsx b/src/browser/modules/Stream/Auth/ConnectForm.tsx index 9de13413c42..23a623f7497 100644 --- a/src/browser/modules/Stream/Auth/ConnectForm.tsx +++ b/src/browser/modules/Stream/Auth/ConnectForm.tsx @@ -35,7 +35,6 @@ import { toKeyString } from 'services/utils' import { stripScheme, getScheme } from 'services/boltscheme.utils' type AuthenticationMethod = typeof NATIVE | typeof NO_AUTH -const authMethods: AuthenticationMethod[] = [NATIVE, NO_AUTH] const readableauthenticationMethods: Record = { [NATIVE]: 'Username / Password', [NO_AUTH]: 'No authentication' @@ -43,6 +42,7 @@ const readableauthenticationMethods: Record = { interface ConnectFormProps { allowedSchemes: string[] + allowedAuthMethods: AuthenticationMethod[] authenticationMethod: string host: string onAuthenticationMethodChange: () => void @@ -102,16 +102,27 @@ export default function ConnectForm(props: ConnectFormProps): JSX.Element { props.onConnectClick(() => setConnecting(false)) } + const hasSecureSchemes = ['neo4j+s', 'bolt+s'].every(scheme => + props.allowedSchemes.includes(scheme) + ) + const hoverText = `Pick neo4j${ + hasSecureSchemes ? '+s' : '' + }:// for a routed connection, bolt${ + hasSecureSchemes ? '+s' : '' + }:// for a direct connection to a DBMS instance.` + + const multipleSchemesAllowed = props.allowedSchemes.length > 1 + return ( Connect URL - {props.allowedSchemes && props.allowedSchemes.length ? ( + {multipleSchemesAllowed ? ( <> - Pick neo4j:// for a routed connection, bolt:// for a direct - connection to a DBMS. + {hoverText} ) : ( @@ -162,22 +172,24 @@ export default function ConnectForm(props: ConnectFormProps): JSX.Element { )} - - - Authentication type - - {authMethods.map(auth => ( - - ))} - - - + {props.allowedAuthMethods.length > 1 && ( + + + Authentication type + + {props.allowedAuthMethods.map(auth => ( + + ))} + + + + )} {props.authenticationMethod === NATIVE && ( diff --git a/src/browser/modules/Stream/Auth/ConnectionForm.jsx b/src/browser/modules/Stream/Auth/ConnectionForm.jsx index dcc3e986680..d28aeff69b9 100644 --- a/src/browser/modules/Stream/Auth/ConnectionForm.jsx +++ b/src/browser/modules/Stream/Auth/ConnectionForm.jsx @@ -46,7 +46,7 @@ import { NATIVE, NO_AUTH } from 'services/bolt/boltHelpers' import ConnectForm from './ConnectForm' import ConnectedView from './ConnectedView' import ChangePasswordForm from './ChangePasswordForm' -import { getAllowedBoltSchemes } from 'shared/modules/app/appDuck' +import { getAllowedBoltSchemes, inCloudEnv } from 'shared/modules/app/appDuck' import { FOCUS } from 'shared/modules/editor/editorDuck' import { generateBoltUrl, @@ -293,7 +293,7 @@ export class ConnectionForm extends Component { } else if ( this.props.isConnected && this.props.activeConnectionData && - this.props.activeConnectionData.authEnabled === false // excplicit false = auth disabled for sure + this.props.activeConnectionData.authEnabled === false // explicit false = auth disabled for sure ) { view = ( @@ -315,6 +315,9 @@ export class ConnectionForm extends Component { username={this.state.username} password={this.state.password} database={this.state.requestedUseDb} + allowedAuthMethods={ + this.props.inCloudEnv ? [NATIVE] : [NATIVE, NO_AUTH] + } authenticationMethod={this.state.authenticationMethod} used={this.state.used} allowedSchemes={this.props.allowedSchemes} @@ -334,7 +337,8 @@ const mapStateToProps = state => { playImplicitInitCommands: getPlayImplicitInitCommands(state), storeCredentials: shouldRetainConnectionCredentials(state), isConnected: isConnected(state), - allowedSchemes: getAllowedBoltSchemes(state) + allowedSchemes: getAllowedBoltSchemes(state), + inCloudEnv: inCloudEnv(state) } } @@ -356,6 +360,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { storeCredentials: stateProps.storeCredentials, isConnected: stateProps.isConnected, allowedSchemes: stateProps.allowedSchemes, + inCloudEnv: stateProps.onAura, ...ownProps, ...dispatchProps, executeInitCmd: () => { diff --git a/src/browser/modules/Stream/Auth/ConnectionFrame.jsx b/src/browser/modules/Stream/Auth/ConnectionFrame.jsx index 5edc717ffcb..23d6b4bdf7d 100644 --- a/src/browser/modules/Stream/Auth/ConnectionFrame.jsx +++ b/src/browser/modules/Stream/Auth/ConnectionFrame.jsx @@ -28,6 +28,8 @@ import { Lead } from 'browser-components/Text' import Render from 'browser-components/Render' import { StyledConnectionAside, StyledConnectionBodyContainer } from './styled' +import { connect } from 'react-redux' +import { inCloudEnv } from 'shared/modules/app/appDuck' export class ConnectionFrame extends Component { constructor(props) { @@ -58,20 +60,21 @@ export class ConnectionFrame extends Component { contents={ <> - - + {this.state.success ? ( + <> +

Connected to Neo4j

+ Nice to meet you. + + ) : ( + <>

Connect to Neo4j

- Database access might require an authenticated connection. + {this.props.inCloudEnv + ? 'Database access on Neo4j Aura requires an authenticated connection' + : 'Database access might require an authenticated connection.'} -
-
- - -

Connected to Neo4j

- Nice to meet you. -
-
+ + )}
{ + return { + inCloudEnv: inCloudEnv(state) + } +} + +export default connect(mapStateToProps)(ConnectionFrame) diff --git a/src/shared/modules/app/appDuck.js b/src/shared/modules/app/appDuck.js index e7f9db9f369..9b7f5acdf46 100644 --- a/src/shared/modules/app/appDuck.js +++ b/src/shared/modules/app/appDuck.js @@ -39,8 +39,13 @@ export const getEnv = state => (state[NAME] || {}).env || WEB export const hasDiscoveryEndpoint = state => [WEB, CLOUD].includes(getEnv(state)) export const inWebEnv = state => getEnv(state) === WEB +export const inCloudEnv = state => getEnv(state) === CLOUD export const inWebBrowser = state => [WEB, CLOUD].includes(getEnv(state)) export const getAllowedBoltSchemes = (state, encryptionFlag) => { + if (inCloudEnv(state) /* Aura only allows neo4j+s */) { + return ['neo4j+s'] + } + const isHosted = inWebBrowser(state) const hostedUrl = getHostedUrl(state) return !isHosted From a4dda5706a9430bc354a5dc918e77d5db32e1485 Mon Sep 17 00:00:00 2001 From: Oskar Damkjaer Date: Wed, 28 Oct 2020 15:05:58 +0100 Subject: [PATCH 2/5] Update unit test --- src/shared/modules/discovery/discoveryDuck.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/modules/discovery/discoveryDuck.test.js b/src/shared/modules/discovery/discoveryDuck.test.js index f057e9c0d63..d2fe52ff2ec 100644 --- a/src/shared/modules/discovery/discoveryDuck.test.js +++ b/src/shared/modules/discovery/discoveryDuck.test.js @@ -323,7 +323,7 @@ describe('discoveryOnStartupEpic cloud env', () => { }) test('listens on APP_START and finds a bolt host and dispatches an action with the found host in cloud env', done => { // Given - const expectedHost = 'bolt://myhost:7777' + const expectedHost = 'neo4j+s://myhost:7777' const action = { type: APP_START, env: CLOUD } nock(getDiscoveryEndpoint()) .get('/') From 61d4ddc4adc4fea8f7e710ab3ac692a1e85d55b9 Mon Sep 17 00:00:00 2001 From: Oskar Damkjaer Date: Fri, 30 Oct 2020 09:27:03 +0100 Subject: [PATCH 3/5] Move auth methods for consistency --- src/browser/modules/Stream/Auth/ConnectionForm.jsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/browser/modules/Stream/Auth/ConnectionForm.jsx b/src/browser/modules/Stream/Auth/ConnectionForm.jsx index d28aeff69b9..dcc6199aeac 100644 --- a/src/browser/modules/Stream/Auth/ConnectionForm.jsx +++ b/src/browser/modules/Stream/Auth/ConnectionForm.jsx @@ -315,13 +315,11 @@ export class ConnectionForm extends Component { username={this.state.username} password={this.state.password} database={this.state.requestedUseDb} - allowedAuthMethods={ - this.props.inCloudEnv ? [NATIVE] : [NATIVE, NO_AUTH] - } - authenticationMethod={this.state.authenticationMethod} + supportsMultiDb={this.state.supportsMultiDb} used={this.state.used} allowedSchemes={this.props.allowedSchemes} - supportsMultiDb={this.state.supportsMultiDb} + allowedAuthMethods={this.props.allowedAuthMethods} + authenticationMethod={this.state.authenticationMethod} /> ) } @@ -338,7 +336,7 @@ const mapStateToProps = state => { storeCredentials: shouldRetainConnectionCredentials(state), isConnected: isConnected(state), allowedSchemes: getAllowedBoltSchemes(state), - inCloudEnv: inCloudEnv(state) + allowedAuthMethods: inCloudEnv(state) ? [NATIVE] : [NATIVE, NO_AUTH] } } @@ -360,7 +358,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { storeCredentials: stateProps.storeCredentials, isConnected: stateProps.isConnected, allowedSchemes: stateProps.allowedSchemes, - inCloudEnv: stateProps.onAura, + allowedAuthMethods: stateProps.allowedAuthMethods, ...ownProps, ...dispatchProps, executeInitCmd: () => { From ee8e02ec980b21509394ec5c9f503d924c2857a2 Mon Sep 17 00:00:00 2001 From: Oskar Damkjaer Date: Tue, 3 Nov 2020 11:38:40 +0100 Subject: [PATCH 4/5] Update unit test --- src/browser/modules/Stream/Auth/ConnectionForm.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/browser/modules/Stream/Auth/ConnectionForm.test.js b/src/browser/modules/Stream/Auth/ConnectionForm.test.js index 2f3e00be183..cd9be4f31ca 100644 --- a/src/browser/modules/Stream/Auth/ConnectionForm.test.js +++ b/src/browser/modules/Stream/Auth/ConnectionForm.test.js @@ -21,6 +21,7 @@ import React from 'react' import { render, fireEvent } from '@testing-library/react' import { ConnectionForm } from './ConnectionForm' +import { NATIVE, NO_AUTH } from 'services/bolt/boltHelpers' test('should print correct state for retaining credentials', async () => { const bus = { @@ -53,6 +54,7 @@ test('should print correct state for retaining credentials', async () => { executeInitCmd={executeInitCmd} isConnected={false} allowedSchemes={['neo4j']} + allowedAuthMethods={[NATIVE, NO_AUTH]} /> ) @@ -84,6 +86,7 @@ test('should print correct state for retaining credentials', async () => { executeInitCmd={executeInitCmd} isConnected={true} allowedSchemes={['neo4j']} + allowedAuthMethods={[NATIVE, NO_AUTH]} /> ) @@ -108,6 +111,7 @@ test('should print correct state for retaining credentials', async () => { executeInitCmd={executeInitCmd} isConnected={true} allowedSchemes={['neo4j']} + allowedAuthMethods={[NATIVE, NO_AUTH]} /> ) From e311806a179080ecf19f8571826e9c883d071706 Mon Sep 17 00:00:00 2001 From: Oskar Damkjaer Date: Wed, 4 Nov 2020 10:01:40 +0100 Subject: [PATCH 5/5] Update messages on migth require auth --- src/browser/modules/Stream/Auth/ConnectForm.tsx | 6 +++--- src/browser/modules/Stream/Auth/ConnectionForm.jsx | 8 ++++++-- src/browser/modules/Stream/Auth/ConnectionFrame.jsx | 13 ++++++++----- src/shared/modules/app/appDuck.js | 5 +++++ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/browser/modules/Stream/Auth/ConnectForm.tsx b/src/browser/modules/Stream/Auth/ConnectForm.tsx index 23a623f7497..4e69f932186 100644 --- a/src/browser/modules/Stream/Auth/ConnectForm.tsx +++ b/src/browser/modules/Stream/Auth/ConnectForm.tsx @@ -111,18 +111,18 @@ export default function ConnectForm(props: ConnectFormProps): JSX.Element { hasSecureSchemes ? '+s' : '' }:// for a direct connection to a DBMS instance.` - const multipleSchemesAllowed = props.allowedSchemes.length > 1 + const schemeRestriction = props.allowedSchemes.length > 0 return ( Connect URL - {multipleSchemesAllowed ? ( + {schemeRestriction ? ( <> { storeCredentials: shouldRetainConnectionCredentials(state), isConnected: isConnected(state), allowedSchemes: getAllowedBoltSchemes(state), - allowedAuthMethods: inCloudEnv(state) ? [NATIVE] : [NATIVE, NO_AUTH] + allowedAuthMethods: getAllowedAuthSchemes(state) } } diff --git a/src/browser/modules/Stream/Auth/ConnectionFrame.jsx b/src/browser/modules/Stream/Auth/ConnectionFrame.jsx index 23d6b4bdf7d..36fc71ac2a5 100644 --- a/src/browser/modules/Stream/Auth/ConnectionFrame.jsx +++ b/src/browser/modules/Stream/Auth/ConnectionFrame.jsx @@ -29,7 +29,8 @@ import { Lead } from 'browser-components/Text' import Render from 'browser-components/Render' import { StyledConnectionAside, StyledConnectionBodyContainer } from './styled' import { connect } from 'react-redux' -import { inCloudEnv } from 'shared/modules/app/appDuck' +import { getAllowedAuthSchemes } from 'shared/modules/app/appDuck' +import { NO_AUTH } from 'services/bolt/boltHelpers' export class ConnectionFrame extends Component { constructor(props) { @@ -69,9 +70,11 @@ export class ConnectionFrame extends Component { <>

Connect to Neo4j

- {this.props.inCloudEnv - ? 'Database access on Neo4j Aura requires an authenticated connection' - : 'Database access might require an authenticated connection.'} + Database access + {this.props.mightRequireAuth + ? ' might require ' + : ' requires '} + an authenticated connection )} @@ -92,7 +95,7 @@ export class ConnectionFrame extends Component { const mapStateToProps = state => { return { - inCloudEnv: inCloudEnv(state) + mightRequireAuth: getAllowedAuthSchemes(state).includes(NO_AUTH) } } diff --git a/src/shared/modules/app/appDuck.js b/src/shared/modules/app/appDuck.js index 9b7f5acdf46..8a10fbeb98c 100644 --- a/src/shared/modules/app/appDuck.js +++ b/src/shared/modules/app/appDuck.js @@ -18,6 +18,8 @@ * along with this program. If not, see . */ +import { NATIVE, NO_AUTH } from 'services/bolt/boltHelpers' + // Action type constants export const NAME = 'app' export const APP_START = `${NAME}/APP_START` @@ -41,6 +43,9 @@ export const hasDiscoveryEndpoint = state => export const inWebEnv = state => getEnv(state) === WEB export const inCloudEnv = state => getEnv(state) === CLOUD export const inWebBrowser = state => [WEB, CLOUD].includes(getEnv(state)) +export const getAllowedAuthSchemes = state => + inCloudEnv(state) ? [] : [NATIVE, NO_AUTH] + export const getAllowedBoltSchemes = (state, encryptionFlag) => { if (inCloudEnv(state) /* Aura only allows neo4j+s */) { return ['neo4j+s']