Skip to content

Commit a6cb25c

Browse files
authored
Merge pull request #498 from oskarhane/3.0-store-credentials-config
Read and act on server config retain_connection_credentials
2 parents 3d16c36 + ce64e7e commit a6cb25c

File tree

9 files changed

+120
-11
lines changed

9 files changed

+120
-11
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
"redux-devtools": "^3.2.0",
9999
"redux-devtools-dock-monitor": "^1.1.1",
100100
"redux-devtools-log-monitor": "^1.0.11",
101-
"redux-mock-store": "^1.2.2",
101+
"redux-mock-store": "^1.2.3",
102102
"standard": "^8.6.0",
103103
"style-loader": "^0.16.1",
104104
"url-loader": "^0.5.8",

src/browser/modules/Stream/Auth/ConnectedView.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020

2121
import { StyledConnectionBody, StyledCode, StyledConnectionFooter } from './styled'
2222

23-
const ConnectedView = ({host, username}) => {
23+
const ConnectedView = ({host, username, storeCredentials}) => {
2424
return (
2525
<StyledConnectionBody>
2626
You are connected as user <StyledCode>{username}</StyledCode><br />
2727
to the server <StyledCode>{host}</StyledCode><br />
2828
<StyledConnectionFooter>
29-
Connection credentials are stored in your web browser.
29+
Connection credentials are {(storeCredentials ? '' : 'not ')}stored in your web browser.
3030
</StyledConnectionFooter>
3131
</StyledConnectionBody>
3232
)

src/browser/modules/Stream/Auth/ConnectionForm.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { withBus } from 'preact-suber'
2424
import { getActiveConnectionData, getActiveConnection, setActiveConnection, updateConnection, CONNECT } from 'shared/modules/connections/connectionsDuck'
2525
import { getInitCmd, updateBoltRouting } from 'shared/modules/settings/settingsDuck'
2626
import { executeSystemCommand } from 'shared/modules/commands/commandsDuck'
27+
import { shouldRetainConnectionCredentials } from 'shared/modules/dbMeta/dbMetaDuck'
2728
import { FORCE_CHANGE_PASSWORD } from 'shared/modules/cypher/cypherDuck'
2829
import { changeCurrentUsersPasswordQueryObj } from 'shared/modules/cypher/procedureFactory'
2930
import { toBoltHost, isRoutingHost } from 'services/utils'
@@ -151,6 +152,7 @@ export class ConnectionForm extends Component {
151152
view = <ConnectedView
152153
host={this.props.activeConnectionData.host}
153154
username={this.props.activeConnectionData.username}
155+
storeCredentials={this.props.storeCredentials}
154156
/>
155157
} else if (!this.state.isConnected && !this.state.passwordChangeNeeded) {
156158
view = (<ConnectForm
@@ -172,7 +174,8 @@ const mapStateToProps = (state) => {
172174
return {
173175
initCmd: getInitCmd(state),
174176
activeConnection: getActiveConnection(state),
175-
activeConnectionData: getActiveConnectionData(state)
177+
activeConnectionData: getActiveConnectionData(state),
178+
storeCredentials: shouldRetainConnectionCredentials(state)
176179
}
177180
}
178181

@@ -191,6 +194,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
191194
return {
192195
activeConnection: stateProps.activeConnection,
193196
activeConnectionData: stateProps.activeConnectionData,
197+
storeCredentials: stateProps.storeCredentials,
194198
...ownProps,
195199
...dispatchProps,
196200
executeInitCmd: () => {

src/shared/modules/connections/connectionsDuck.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const CONNECTION_SUCCESS = 'connections/CONNECTION_SUCCESS'
4141
export const DISCONNECTION_SUCCESS = 'connections/DISCONNECTION_SUCCESS'
4242
export const LOST_CONNECTION = 'connections/LOST_CONNECTION'
4343
export const UPDATE_CONNECTION_STATE = 'connections/UPDATE_CONNECTION_STATE'
44+
export const UPDATE_RETAIN_CREDENTIALS = NAME + '/UPDATE_RETAIN_CREDENTIALS'
4445

4546
export const DISCONNECTED_STATE = 0
4647
export const CONNECTED_STATE = 1
@@ -78,7 +79,13 @@ export function getActiveConnection (state) {
7879
}
7980

8081
export function getActiveConnectionData (state) {
81-
return state[NAME].activeConnection ? state[NAME].connectionsById[state[NAME].activeConnection] : null
82+
if (!state[NAME].activeConnection) return null
83+
let data = state[NAME].connectionsById[state[NAME].activeConnection]
84+
if (data.username && data.password) return data
85+
if (!(data.username && data.password) && (memoryUsername && memoryPassword)) { // No retain state
86+
return {...data, username: memoryUsername, password: memoryPassword}
87+
}
88+
return data
8289
}
8390

8491
const addConnectionHelper = (state, obj) => {
@@ -134,6 +141,11 @@ const mergeConnectionHelper = (state, connection) => {
134141
)
135142
}
136143

144+
// Local vars
145+
let memoryUsername = ''
146+
let memoryPassword = ''
147+
148+
// Reducer
137149
export default function (state = initialState, action) {
138150
state = hydrate(initialState, state)
139151

@@ -210,11 +222,20 @@ export const connectionLossFilter = (action) => {
210222
return notLostCodes.indexOf(action.error.code) < 0
211223
}
212224

225+
export const setRetainCredentials = (shouldRetain) => {
226+
return {
227+
type: UPDATE_RETAIN_CREDENTIALS,
228+
shouldRetain
229+
}
230+
}
231+
213232
// Epics
214233
export const connectEpic = (action$, store) => {
215234
return action$.ofType(CONNECT)
216235
.mergeMap((action) => {
217236
if (!action.$$responseChannel) return Rx.Observable.of(null)
237+
memoryUsername = ''
238+
memoryPassword = ''
218239
return bolt.openConnection(action, { encrypted: getEncryptionMode() }, onLostConnection(store.dispatch))
219240
.then((res) => ({ type: action.$$responseChannel, success: true }))
220241
.catch(([e]) => ({ type: action.$$responseChannel, success: false, error: e }))
@@ -324,3 +345,26 @@ export const checkSettingsForRoutingDriver = (action$, store) => {
324345
return { type: 'NOOP' }
325346
})
326347
}
348+
349+
export const retainCredentialsSettingsEpic = (action$, store) => {
350+
return action$.ofType(UPDATE_RETAIN_CREDENTIALS)
351+
.do((action) => {
352+
const connection = getActiveConnectionData(store.getState())
353+
if (!action.shouldRetain && (connection.username || connection.password)) {
354+
memoryUsername = connection.username
355+
memoryPassword = connection.password
356+
connection.username = ''
357+
connection.password = ''
358+
return store.dispatch(updateConnection(connection))
359+
}
360+
if (action.shouldRetain && memoryUsername && memoryPassword) {
361+
connection.username = memoryUsername
362+
connection.password = memoryPassword
363+
memoryUsername = ''
364+
memoryPassword = ''
365+
return store.dispatch(updateConnection(connection))
366+
}
367+
return
368+
})
369+
.mapTo({ type: 'NOOP' })
370+
}

src/shared/modules/connections/connectionsDuck.test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,46 @@ describe('connectionsDucks Epics', () => {
181181
store.dispatch(action)
182182
})
183183
})
184+
185+
describe('retainCredentialsSettingsEpic', () => {
186+
// Given
187+
const epicMiddleware = createEpicMiddleware(connections.retainCredentialsSettingsEpic)
188+
const myMockStore = configureMockStore([epicMiddleware, createReduxMiddleware(bus)])
189+
let store
190+
beforeAll(() => {
191+
bus.reset()
192+
store = myMockStore({
193+
connections: {
194+
activeConnection: 'xxx',
195+
connectionsById: {
196+
xxx: {id: 'xxx', username: 'usr', password: 'pw'}
197+
},
198+
allConnectionIds: ['xxx']
199+
}
200+
})
201+
})
202+
afterEach(() => {
203+
store.clearActions()
204+
bus.reset()
205+
})
206+
test('Dispatches an action to remove credentials from localstorage', (done) => {
207+
// Given
208+
const action = connections.setRetainCredentials(false)
209+
bus.take('NOOP', (currentAction) => {
210+
// Then
211+
expect(store.getActions()).toEqual([
212+
action,
213+
connections.updateConnection({
214+
id: 'xxx',
215+
username: '',
216+
password: ''
217+
}),
218+
currentAction
219+
])
220+
done()
221+
})
222+
223+
// When
224+
store.dispatch(action)
225+
})
226+
})

src/shared/modules/dbMeta/dbMetaDuck.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
import Rx from 'rxjs/Rx'
2222
import bolt from 'services/bolt/bolt'
2323
import { hydrate } from 'services/duckUtils'
24-
import { getJmxValues, getServerConfig } from 'services/bolt/boltHelpers'
24+
import { getJmxValues, getServerConfig, isConfigValFalsy } from 'services/bolt/boltHelpers'
2525
import {
2626
CONNECTED_STATE,
2727
CONNECTION_SUCCESS,
2828
connectionLossFilter,
2929
DISCONNECTION_SUCCESS,
3030
LOST_CONNECTION,
31-
UPDATE_CONNECTION_STATE
31+
UPDATE_CONNECTION_STATE,
32+
setRetainCredentials
3233
} from 'shared/modules/connections/connectionsDuck'
3334

3435
export const NAME = 'meta'
@@ -84,6 +85,11 @@ export const getStoreId = (state) => state[NAME].server.storeId
8485
export const getAvailableSettings = (state) => (state[NAME] || initialState).settings
8586
export const allowOutgoingConnections = (state) => getAvailableSettings(state)['browser.allow_outgoing_connections']
8687
export const credentialsTimeout = (state) => getAvailableSettings(state)['browser.credential_timeout'] || 0
88+
export const shouldRetainConnectionCredentials = (state) => {
89+
const conf = getAvailableSettings(state)['browser.retain_connection_credentials']
90+
if (conf === null || typeof conf === 'undefined') return true
91+
return !isConfigValFalsy(conf)
92+
}
8793

8894
/**
8995
* Helpers
@@ -240,8 +246,16 @@ export const dbMetaEpic = (some$, store) =>
240246
.mergeMap(() => {
241247
return getServerConfig(['browser.'])
242248
.then((settings) => {
243-
if (settings) store.dispatch(updateSettings(settings))
244-
return null
249+
if (!settings) return
250+
let retainCredentials = true
251+
if ( // Check if we should wipe user creds from localstorage
252+
typeof settings['browser.retain_connection_credentials'] !== 'undefined' &&
253+
isConfigValFalsy(settings['browser.retain_connection_credentials'])
254+
) {
255+
retainCredentials = false
256+
}
257+
store.dispatch(setRetainCredentials(retainCredentials))
258+
store.dispatch(updateSettings(settings))
245259
})
246260
})
247261
.takeUntil(some$.ofType(LOST_CONNECTION).filter(connectionLossFilter))

src/shared/rootEpic.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import { combineEpics } from 'redux-observable'
2222
import { handleCommandsEpic, postConnectCmdEpic } from './modules/commands/commandsDuck'
23-
import { checkSettingsForRoutingDriver, connectEpic, disconnectEpic, startupConnectEpic, disconnectSuccessEpic, startupConnectionSuccessEpic, startupConnectionFailEpic, detectActiveConnectionChangeEpic, connectionLostEpic } from './modules/connections/connectionsDuck'
23+
import { retainCredentialsSettingsEpic, checkSettingsForRoutingDriver, connectEpic, disconnectEpic, startupConnectEpic, disconnectSuccessEpic, startupConnectionSuccessEpic, startupConnectionFailEpic, detectActiveConnectionChangeEpic, connectionLostEpic } from './modules/connections/connectionsDuck'
2424
import { dbMetaEpic, clearMetaOnDisconnectEpic } from './modules/dbMeta/dbMetaDuck'
2525
import { cancelRequestEpic } from './modules/requests/requestsDuck'
2626
import { discoveryOnStartupEpic } from './modules/discovery/discoveryDuck'
@@ -36,6 +36,7 @@ export default combineEpics(
3636
handleCommandsEpic,
3737
postConnectCmdEpic,
3838
connectionLostEpic,
39+
retainCredentialsSettingsEpic,
3940
checkSettingsForRoutingDriver,
4041
connectEpic,
4142
disconnectEpic,

src/shared/services/bolt/boltHelpers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,6 @@ export const getJmxValues = (nameAttributePairs = []) => {
6868
return null
6969
})
7070
}
71+
72+
export const isConfigValTruthy = (val) => [true, 'true', 'yes', 1, '1'].indexOf(val) > -1
73+
export const isConfigValFalsy = (val) => [false, 'false', 'no', 0, '0'].indexOf(val) > -1

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5646,7 +5646,7 @@ redux-devtools@^3.2.0:
56465646
prop-types "^15.5.7"
56475647
redux-devtools-instrument "^1.0.1"
56485648

5649-
redux-mock-store@^1.2.2:
5649+
redux-mock-store@^1.2.3:
56505650
version "1.2.3"
56515651
resolved "https://neo.jfrog.io/neo/api/npm/npm/redux-mock-store/-/redux-mock-store-1.2.3.tgz#1b3ad299da91cb41ba30d68e3b6f024475fb9e1b"
56525652

0 commit comments

Comments
 (0)