Skip to content

Commit

Permalink
feat: save connection passwords and ssl certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
grailian committed Jul 4, 2017
1 parent 0243e55 commit 24cd7c9
Show file tree
Hide file tree
Showing 9 changed files with 464 additions and 99 deletions.
15 changes: 1 addition & 14 deletions app/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import co from 'co';
import keytar from 'keytar';
import RethinkDbService from './services/rethinkdb.service';
import ReQLEval from './services/reql-eval.service';
import { convertStringsToDates } from './services/date-type.service'
Expand Down Expand Up @@ -163,7 +162,7 @@ export function saveInlineEdit(originalRow, row) {
export function saveRow(conn, selectedTable, row) {
return dispatch => {
return new Promise((resolve, reject) => {
ReQLEval(row).then(async(rowObj) => {
ReQLEval(row).then(async (rowObj) => {
row = convertStringsToDates(selectedTable.editingRecord, rowObj);
selectedTable.codeBodyError = null;

Expand Down Expand Up @@ -286,15 +285,3 @@ export function writeConfigFile() {
});
}
}

export function addKey(service, account, password) {
return (dispatch, getState) => {
keytar.addPassword(service, account, password);
// const state = getState();
// return configService.writeConfigFile({
// email: state.main.email,
// created: state.main.created,
// connections: state.connections
// });
}
}
14 changes: 10 additions & 4 deletions app/components/Sidebar/Connections/connections.actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { writeConfigFile, addKey } from '../../../actions';
import { writeConfigFile } from '../../../actions';
import { updateKeysForConnection } from '../../../services/keychain.service';
import * as types from '../../../action-types';
import { selectConnection } from './selectedConnection.actions';

Expand All @@ -9,8 +10,10 @@ export function addConnection(connection) {
connection: connection
});
dispatch(writeConfigFile());
dispatch(addKey('ReQLPro', connection.user, connection.password));
dispatch(addKey('ReQLPro', connection.host, connection.ca));

// Add password and cert to system keychain
updateKeysForConnection(connection);

// Grab the connection from the updated array of connections, which will have the index property
const conns = getState().connections;
dispatch(selectConnection(conns[conns.length - 1]));
Expand All @@ -25,6 +28,9 @@ export function updateConnection(connection) {
});
dispatch(writeConfigFile());

// Add password and cert to system keychain
updateKeysForConnection(connection);

dispatch(selectConnection(connection));
}
}
Expand All @@ -43,7 +49,7 @@ export function deleteConnection(connection) {
if (shouldSelectNew && getState().connections[0]) {
// Grab the updated array of connections, and select the first one
dispatch(selectConnection(getState().connections[0]));
}else{
} else {
//if there are no connections to select, clear connection error
dispatch({
type: 'SET_DB_CONNECTION_ERROR',
Expand Down
4 changes: 4 additions & 0 deletions app/components/Sidebar/Connections/connections.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as reducer from './connections.reducer';
import * as types from '../../../action-types';

let RethinkDbService;
let KeychainService;
let dispatch;
let getState;

Expand Down Expand Up @@ -42,6 +43,9 @@ describe('connections', () => {
const actions = sinon.stub();
actions.writeConfigFile = sinon.stub().returns('testingWriteConfigFileCall');
mockery.registerMock('../../../actions', actions);

const KeychainService = sinon.stub();
mockery.registerMock('../../../services/keychain.service', KeychainService);
});

describe('deleteConnection', () => {
Expand Down
87 changes: 50 additions & 37 deletions app/main.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Require our sass files
require("../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss");
require("../node_modules/font-awesome/scss/font-awesome.scss");
require("./styles/index.scss");

// // Module needed to access global values from main process to any renderer process
// Module needed to access global values from main process to any renderer process
import { remote, ipcRenderer } from 'electron';
// Segment
import Segment from './services/segment.service';
import ConfigService from './services/config.service';
// React Specific libs/components
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App/App';
import { Provider } from 'react-redux';

import Segment from './services/segment.service';
import ConfigService from './services/config.service';
import { getKeysForConnection } from './services/keychain.service';
import store from './store';
import { getDbConnection } from './components/Sidebar/Connections/selectedConnection.actions';

// Require our sass files
require("../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss");
require("../node_modules/font-awesome/scss/font-awesome.scss");
require("./styles/index.scss");

export function initApp() {

// Set Up Renderer (Browser-side) Events Listeners
Expand Down Expand Up @@ -58,39 +58,52 @@ export function initApp() {
});
};

const initialState = createInitialState(userConfig);
// const initialState = createInitialState(userConfig);
createInitialState(userConfig)
.then((initialState) => {

// Set Initial State
store.dispatch({
type: 'SET_STATE',
state: initialState
});
// Set Initial State
store.dispatch({
type: 'SET_STATE',
state: initialState
});

// If a connection exists, connect to it
if (initialState.connection.selected) {
store.dispatch(getDbConnection(initialState.connection.selected));
}
// If a connection exists, connect to it
if (initialState.connection.selected) {
store.dispatch(getDbConnection(initialState.connection.selected));
}

// Render App Component
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
// Render App Component
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
});
});
}

export function createInitialState(config) {
let state = {
main: { email: config.email || null },
connections: config.connections || [],
connection: {}
};
if (config.connections && config.connections[0]) {
state.connection.selected = config.connections[0];
}
return state;
};
return new Promise((resolve, reject) => {
if (config.connections && config.connections.length > 0) {
config.connections.forEach(async (conn) => {
const keys = await getKeysForConnection(conn);
conn.pass = keys.pass;
conn.ca = keys.ca;
});
}

let state = {
main: { email: config.email || null },
connections: config.connections || [],
connection: {}
};
if (config.connections && config.connections[0]) {
state.connection.selected = config.connections[0];
}
resolve(state);
});
}

initApp();
95 changes: 52 additions & 43 deletions app/main.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as core from './core';
import { main } from './main.reducer';
import store from './store';


let ConfigService, Segment, electron, HS, _store;
// let remote;
require.context = function(){};
require.context = function() {
};
describe('main', () => {

beforeEach(() => {
Expand All @@ -18,7 +18,8 @@ describe('main', () => {
// Mock dependencies
ConfigService = sinon.stub();
mockery.registerMock('./services/config.service', ConfigService);
ConfigService.readConfigFile = sinon.stub().returns(new Promise(function(){}));
ConfigService.readConfigFile = sinon.stub().returns(new Promise(function() {
}));

Segment = {
identify: sinon.spy()
Expand All @@ -35,13 +36,14 @@ describe('main', () => {
mockery.registerMock("../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss", sinon.stub());
mockery.registerMock("../node_modules/font-awesome/scss/font-awesome.scss", sinon.stub());
mockery.registerMock("./styles/index.scss", sinon.stub());
mockery.registerMock("./services/keychain.service", sinon.stub());

electron = sinon.stub();
electron.ipcRenderer = {
on: function(message, callback){
on: function(message, callback) {
this.callback = callback;
},
send: function(message){
send: function(message) {
this.callback();
}
};
Expand All @@ -52,7 +54,7 @@ describe('main', () => {

describe('initApp', () => {

beforeEach(function(){
beforeEach(function() {
_store = sinon.stub();
_store.dispatch = sinon.stub();
mockery.registerMock('./store', _store);
Expand All @@ -70,36 +72,53 @@ describe('main', () => {
});

describe('createInitialState', () => {
beforeEach(function(){
beforeEach(function() {
_store = sinon.stub();
_store.dispatch = sinon.stub();
mockery.registerMock('./store', _store);
});
it('should take the user config file and return initial app state object', () => {

it('should take empty user config file and return initial app state object', (done) => {
const { createInitialState } = require('./main');
let fakeConfigFile, fakeState, actual;

////// test 1 no config file
fakeConfigFile ={};
fakeState = {
const fakeConfigFile = {};
const fakeState = {
connection: {},
connections: [],
main: {
email: null
}
};
actual = createInitialState(fakeConfigFile);
expect(actual).to.eql(fakeState);

////// test 2 config with connections
fakeConfigFile = {
createInitialState(fakeConfigFile)
.then((actual) => {
expect(actual).to.eql(fakeState);
done();
});
});
it('should take user config file with email and no connections and return initial app state object', (done) => {
const { createInitialState } = require('./main');
const fakeConfigFile = {
email: 'cassie@codehangar.io',
};
const fakeState = {
main: { email: 'cassie@codehangar.io' },
connections: [],
connection: {}
};
createInitialState(fakeConfigFile)
.then((actual) => {
expect(actual).to.eql(fakeState);
done();
});
});
it('should take user config file with email and connections and return initial app state object', (done) => {
const { createInitialState } = require('./main');
const fakeConfigFile = {
email: 'cassie@codehangar.io',
connections: [
'connection1', 'connection2'
]
};
fakeState = {
const fakeState = {
main: { email: 'cassie@codehangar.io' },
connections: [
'connection1', 'connection2'
Expand All @@ -108,35 +127,25 @@ describe('main', () => {
selected: 'connection1'
}
};
actual = createInitialState(fakeConfigFile);
expect(actual).to.eql(fakeState);

////// test 3 config without connections
fakeConfigFile = {
email: 'cassie@codehangar.io',
};
fakeState = {
main: { email: 'cassie@codehangar.io' },
connections: [],
connection: {}
};
actual = createInitialState(fakeConfigFile);
expect(actual).to.eql(fakeState);

createInitialState(fakeConfigFile)
.then((actual) => {
expect(actual).to.eql(fakeState);
done();
});
});
});
//
describe('main.reducer', () => {
//
//
it('should call core.setEmail for dispatch type SET_EMAIL', () => {
// core.setEmail = sinon.spy();
// console.log(store.getState());
// store.dispatch({
// type: 'SET_EMAIL',
// email: 'cassie@codehangar.io',
// created: '1/1/17'
// });
// expect(core.setEmail.callCount).to.equal(1);
// core.setEmail = sinon.spy();
// console.log(store.getState());
// store.dispatch({
// type: 'SET_EMAIL',
// email: 'cassie@codehangar.io',
// created: '1/1/17'
// });
// expect(core.setEmail.callCount).to.equal(1);
});
});

Expand Down
Loading

0 comments on commit 24cd7c9

Please sign in to comment.