diff --git a/src/browser/modules/Stream/Auth/ConnectForm.tsx b/src/browser/modules/Stream/Auth/ConnectForm.tsx index 9de13413c42..4e69f932186 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 schemeRestriction = props.allowedSchemes.length > 0 + return ( Connect URL - {props.allowedSchemes && props.allowedSchemes.length ? ( + {schemeRestriction ? ( <> - 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..11b63002352 100644 --- a/src/browser/modules/Stream/Auth/ConnectionForm.jsx +++ b/src/browser/modules/Stream/Auth/ConnectionForm.jsx @@ -46,7 +46,11 @@ 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 { + getAllowedAuthSchemes, + getAllowedBoltSchemes, + inCloudEnv +} from 'shared/modules/app/appDuck' import { FOCUS } from 'shared/modules/editor/editorDuck' import { generateBoltUrl, @@ -293,7 +297,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,10 +319,11 @@ export class ConnectionForm extends Component { username={this.state.username} password={this.state.password} database={this.state.requestedUseDb} - 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} /> ) } @@ -334,7 +339,8 @@ const mapStateToProps = state => { playImplicitInitCommands: getPlayImplicitInitCommands(state), storeCredentials: shouldRetainConnectionCredentials(state), isConnected: isConnected(state), - allowedSchemes: getAllowedBoltSchemes(state) + allowedSchemes: getAllowedBoltSchemes(state), + allowedAuthMethods: getAllowedAuthSchemes(state) } } @@ -356,6 +362,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { storeCredentials: stateProps.storeCredentials, isConnected: stateProps.isConnected, allowedSchemes: stateProps.allowedSchemes, + allowedAuthMethods: stateProps.allowedAuthMethods, ...ownProps, ...dispatchProps, executeInitCmd: () => { 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]} /> ) diff --git a/src/browser/modules/Stream/Auth/ConnectionFrame.jsx b/src/browser/modules/Stream/Auth/ConnectionFrame.jsx index 5edc717ffcb..36fc71ac2a5 100644 --- a/src/browser/modules/Stream/Auth/ConnectionFrame.jsx +++ b/src/browser/modules/Stream/Auth/ConnectionFrame.jsx @@ -28,6 +28,9 @@ import { Lead } from 'browser-components/Text' import Render from 'browser-components/Render' import { StyledConnectionAside, StyledConnectionBodyContainer } from './styled' +import { connect } from 'react-redux' +import { getAllowedAuthSchemes } from 'shared/modules/app/appDuck' +import { NO_AUTH } from 'services/bolt/boltHelpers' export class ConnectionFrame extends Component { constructor(props) { @@ -58,20 +61,23 @@ 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. + Database access + {this.props.mightRequireAuth + ? ' might require ' + : ' requires '} + an authenticated connection -
-
- - -

Connected to Neo4j

- Nice to meet you. -
-
+ + )}
{ + return { + mightRequireAuth: getAllowedAuthSchemes(state).includes(NO_AUTH) + } +} + +export default connect(mapStateToProps)(ConnectionFrame) diff --git a/src/shared/modules/app/appDuck.js b/src/shared/modules/app/appDuck.js index e7f9db9f369..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` @@ -39,8 +41,16 @@ 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 getAllowedAuthSchemes = state => + inCloudEnv(state) ? [] : [NATIVE, NO_AUTH] + 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 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('/')