diff --git a/embark-ui/package-lock.json b/embark-ui/package-lock.json
index 564e6f8e..02b570c0 100644
--- a/embark-ui/package-lock.json
+++ b/embark-ui/package-lock.json
@@ -155,6 +155,14 @@
"color-convert": "^1.9.0"
}
},
+ "ansi-to-html": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.6.tgz",
+ "integrity": "sha512-90M/2sZna3OsoOEbSyXK46poFnlClBC53Rx6etNKQK7iShsX5fI5E/M9Ld6FurtLaxAWLuAPi0Jp8p3y5oAkxg==",
+ "requires": {
+ "entities": "^1.1.1"
+ }
+ },
"anymatch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@@ -434,6 +442,11 @@
"postcss-value-parser": "^3.2.3"
}
},
+ "autoscroll-react": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/autoscroll-react/-/autoscroll-react-3.2.0.tgz",
+ "integrity": "sha512-HOiwy9GGTSk9WZwEPd+FwNLLZ17o5wkjAtAb+RtQUr/2J1PT2KRG+OI6LoGtfY5EwWvQKAbSsjkgLvLhItscSg=="
+ },
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -7106,6 +7119,22 @@
"prepend-http": "^1.0.0",
"query-string": "^4.1.0",
"sort-keys": "^1.0.0"
+ },
+ "dependencies": {
+ "query-string": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+ "requires": {
+ "object-assign": "^4.1.0",
+ "strict-uri-encode": "^1.0.0"
+ }
+ },
+ "strict-uri-encode": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
+ }
}
},
"npm-run-path": {
@@ -8826,12 +8855,12 @@
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"query-string": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
- "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.1.0.tgz",
+ "integrity": "sha512-pNB/Gr8SA8ff8KpUFM36o/WFAlthgaThka5bV19AD9PNTH20Pwq5Zxodif2YyHwrctp6SkL4GqlOot0qR/wGaw==",
"requires": {
- "object-assign": "^4.1.0",
- "strict-uri-encode": "^1.0.0"
+ "decode-uri-component": "^0.2.0",
+ "strict-uri-encode": "^2.0.0"
}
},
"querystring": {
@@ -10235,9 +10264,9 @@
}
},
"strict-uri-encode": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
},
"string-length": {
"version": "1.0.1",
diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js
index 967c1026..ced87b63 100644
--- a/embark-ui/src/actions/index.js
+++ b/embark-ui/src/actions/index.js
@@ -13,11 +13,32 @@ function action(type, payload = {}) {
return {type, ...payload};
}
-export const AUTHORIZE = createRequestTypes('AUTHORIZE');
-export const authorize = {
- request: (token, callback) => action(AUTHORIZE[REQUEST], {token, callback}),
- success: () => action(AUTHORIZE[SUCCESS]),
- failure: (error) => action(AUTHORIZE[FAILURE], {error})
+export const AUTHENTICATE = createRequestTypes('AUTHENTICATE');
+export const authenticate = {
+ request: (token) => action(AUTHENTICATE[REQUEST], {token}),
+ success: (_result, payload) => action(AUTHENTICATE[SUCCESS], {token: payload.token}),
+ failure: (error) => action(AUTHENTICATE[FAILURE], {error})
+};
+
+export const FETCH_TOKEN = createRequestTypes('FETCH_TOKEN');
+export const fetchToken = {
+ request: () => action(FETCH_TOKEN[REQUEST]),
+ success: (token) => action(FETCH_TOKEN[SUCCESS], {token}),
+ failure: () => action(FETCH_TOKEN[FAILURE])
+};
+
+export const POST_TOKEN = createRequestTypes('POST_TOKEN');
+export const postToken = {
+ request: (token) => action(POST_TOKEN[REQUEST], {token}),
+ success: (token) => action(POST_TOKEN[SUCCESS], {token}),
+ failure: () => action(POST_TOKEN[FAILURE])
+};
+
+export const LOGOUT = createRequestTypes('LOGOUT');
+export const logout = {
+ request: () => action(LOGOUT[REQUEST]),
+ success: () => action(LOGOUT[SUCCESS]),
+ failure: () => action(LOGOUT[FAILURE])
};
export const ACCOUNTS = createRequestTypes('ACCOUNTS');
@@ -226,20 +247,6 @@ export const saveCurrentFile = {
failure: () => action(SAVE_CURRENT_FILE[FAILURE])
};
-export const GET_TOKEN = createRequestTypes('TOKEN');
-export const getToken = {
- request: (callback) => action(GET_TOKEN[REQUEST], {callback}),
- success: (token) => action(GET_TOKEN[SUCCESS], {token}),
- failure: () => action(GET_TOKEN[FAILURE])
-};
-
-export const POST_TOKEN = createRequestTypes('POST_TOKEN');
-export const postToken = {
- request: (token) => action(POST_TOKEN[REQUEST], {token}),
- success: (token) => action(POST_TOKEN[SUCCESS], {token}),
- failure: () => action(POST_TOKEN[FAILURE])
-};
-
export const GAS_ORACLE = createRequestTypes('GAS_ORACLE');
export const gasOracle = {
request: () => action(GAS_ORACLE[REQUEST]),
diff --git a/embark-ui/src/components/AuthError.js b/embark-ui/src/components/AuthError.js
deleted file mode 100644
index 38eaccce..00000000
--- a/embark-ui/src/components/AuthError.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import PropTypes from "prop-types";
-import React from 'react';
-import {Page, Alert, Form, Button} from "tabler-react";
-
-const AuthError = ({error}) => {
- return
-
- {error}
-
-
-
-
- ;
-};
-
-AuthError.propTypes = {
- error: PropTypes.string.isRequired
-};
-
-export default AuthError;
-
diff --git a/embark-ui/src/components/Layout.js b/embark-ui/src/components/Layout.js
index 212da010..b76788fd 100644
--- a/embark-ui/src/components/Layout.js
+++ b/embark-ui/src/components/Layout.js
@@ -13,37 +13,49 @@ const navBarItems = [
{value: "Documentation", to: "/embark/documentation", icon: "file-text", LinkComponent: NavLink}
];
-const Layout = (props) => (
+const Layout = ({children, logout}) => (
-
-
+
+
+
+
+
+
+
+
)
}}
navProps={{itemsObjects: navBarItems}}
>
- {props.children}
+ {children}
);
Layout.propTypes = {
- children: PropTypes.element
+ children: PropTypes.element,
+ logout: PropTypes.func
};
export default Layout;
diff --git a/embark-ui/src/components/Unauthenticated.js b/embark-ui/src/components/Unauthenticated.js
new file mode 100644
index 00000000..c7b5b94d
--- /dev/null
+++ b/embark-ui/src/components/Unauthenticated.js
@@ -0,0 +1,47 @@
+import PropTypes from "prop-types";
+import React from 'react';
+import {Page, Alert, Form, Button} from "tabler-react";
+
+class Unauthenticated extends React.Component {
+ constructor(props){
+ super(props);
+ this.state = { token: '' };
+ }
+
+ handleTokenChange(event){
+ this.setState({token: event.target.value});
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+ this.props.authenticate(this.state.token);
+ }
+
+ render() {
+ return (
+
+ {this.props.error &&
+ {this.props.error}
+ }
+ this.handleTokenChange(e)}
+ placeholder="Enter Token"/>
+
+
+
+ );
+ }
+}
+
+Unauthenticated.propTypes = {
+ authenticate: PropTypes.func,
+ error: PropTypes.string
+};
+
+export default Unauthenticated;
+
diff --git a/embark-ui/src/containers/AppContainer.js b/embark-ui/src/containers/AppContainer.js
index 3480d532..f66c3c41 100644
--- a/embark-ui/src/containers/AppContainer.js
+++ b/embark-ui/src/containers/AppContainer.js
@@ -4,64 +4,72 @@ import React, {Component} from 'react';
import {withRouter} from "react-router-dom";
import routes from '../routes';
-import AuthError from '../components/AuthError';
+import Unauthenticated from '../components/Unauthenticated';
+import Layout from "../components/Layout";
import queryString from 'query-string';
import {
initBlockHeader,
- authorize, getToken, postToken,
+ authenticate, fetchToken, logout,
processes as processesAction,
versions as versionsAction,
plugins as pluginsAction
} from '../actions';
+import { getToken, getAuthenticationError } from '../reducers/selectors';
+
class AppContainer extends Component {
constructor (props) {
super(props);
- this.state = {
- authenticateError: null
- };
- this.checkToken();
+ this.queryStringAuthenticate();
}
- checkToken() {
- if (this.props.location.search) {
- const token = queryString.parse(this.props.location.search).token;
- this.props.postToken(token);
- return this.props.authorize(token, this.authCallback.bind(this));
+ queryStringAuthenticate() {
+ if (!this.props.location.search) {
+ return;
}
- this.props.getToken((err, token) => {
- this.props.authorize(token, this.authCallback.bind(this));
- });
- }
-
- authCallback(err) {
- if (err) {
- return this.setState({authenticateError: err});
+ const token = queryString.parse(this.props.location.search).token;
+ if (token === this.props.token) {
+ return;
}
- this.setState({authenticateError: null});
+ this.props.authenticate(token);
}
componentDidMount() {
- this.props.initBlockHeader();
- this.props.fetchProcesses();
- this.props.fetchVersions();
- this.props.fetchPlugins();
+ this.props.fetchToken();
+ }
+
+ componentDidUpdate(){
+ if (this.props.token) {
+ this.props.authenticate(this.props.token);
+ this.props.initBlockHeader();
+ this.props.fetchProcesses();
+ this.props.fetchVersions();
+ this.props.fetchPlugins();
+ }
+ }
+
+ shouldRenderUnauthenticated() {
+ return this.props.authenticationError || !this.props.token;
}
render() {
- if (this.state.authenticateError) {
- return ;
- }
- return ({routes});
+ return (
+
+ {this.shouldRenderUnauthenticated() ? : {routes}}
+
+ );
}
}
AppContainer.propTypes = {
- authorize: PropTypes.func,
- getToken: PropTypes.func,
- postToken: PropTypes.func,
+ token: PropTypes.string,
+ authenticationError: PropTypes.string,
+ authenticate: PropTypes.func,
+ logout: PropTypes.func,
+ fetchToken: PropTypes.func,
initBlockHeader: PropTypes.func,
fetchProcesses: PropTypes.func,
fetchPlugins: PropTypes.func,
@@ -69,13 +77,20 @@ AppContainer.propTypes = {
location: PropTypes.object
};
+function mapStateToProps(state) {
+ return {
+ token: getToken(state),
+ authenticationError: getAuthenticationError(state)
+ };
+}
+
export default withRouter(connect(
- null,
+ mapStateToProps,
{
initBlockHeader,
- authorize: authorize.request,
- getToken: getToken.request,
- postToken: postToken.request,
+ authenticate: authenticate.request,
+ logout: logout.request,
+ fetchToken: fetchToken.request,
fetchProcesses: processesAction.request,
fetchVersions: versionsAction.request,
fetchPlugins: pluginsAction.request
diff --git a/embark-ui/src/index.js b/embark-ui/src/index.js
index 7f69eded..3b41ef57 100644
--- a/embark-ui/src/index.js
+++ b/embark-ui/src/index.js
@@ -6,7 +6,6 @@ import {Provider} from 'react-redux';
import "tabler-react/dist/Tabler.css";
import "./general.css";
import "./slider.css";
-import Layout from "./components/Layout";
import AppContainer from './containers/AppContainer';
import history from "./history";
@@ -18,9 +17,7 @@ const store = configureStore();
ReactDOM.render(
-
-
-
+
,
document.getElementById('root')
diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js
index af2ab2b1..1ad5c799 100644
--- a/embark-ui/src/reducers/index.js
+++ b/embark-ui/src/reducers/index.js
@@ -1,5 +1,5 @@
import {combineReducers} from 'redux';
-import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES} from "../actions";
+import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES, LOGOUT, AUTHENTICATE} from "../actions";
const BN_FACTOR = 10000;
const voidAddress = '0x0000000000000000000000000000000000000000';
@@ -83,6 +83,9 @@ const filtrer = {
},
gasOracleStats: function(stat, index, _self) {
return index === 0; // Only keep last one
+ },
+ versions: function(version, index, self) {
+ return index === self.findIndex((v) => v.value === version.value && v.name === version.name);
}
};
@@ -115,8 +118,8 @@ function entities(state = entitiesDefaultState, action) {
return state;
}
-function errorMessage(state = null, action) {
- return action.error || state;
+function errorMessage(_state = null, action) {
+ return action.error || null;
}
function errorEntities(state = {}, action) {
@@ -146,8 +149,16 @@ function compilingContract(state = false, action) {
return state;
}
-function token(state = null, action) {
- return (action.token) ? action.token : state;
+function authentication(state = {}, action) {
+ if (action.type === LOGOUT[SUCCESS]) {
+ return {};
+ }
+
+ if (action.type === AUTHENTICATE[FAILURE]) {
+ return {error: action.error};
+ }
+
+ return (action.token) ? {token: action.token} : state;
}
const rootReducer = combineReducers({
@@ -156,7 +167,7 @@ const rootReducer = combineReducers({
compilingContract,
errorMessage,
errorEntities,
- token
+ authentication
});
export default rootReducer;
diff --git a/embark-ui/src/reducers/selectors.js b/embark-ui/src/reducers/selectors.js
index 95ca30d6..cefcae15 100644
--- a/embark-ui/src/reducers/selectors.js
+++ b/embark-ui/src/reducers/selectors.js
@@ -1,5 +1,13 @@
import {last} from '../utils/utils';
+export function getToken(state) {
+ return state.authentication.token;
+}
+
+export function getAuthenticationError(state) {
+ return state.authentication.error;
+}
+
export function getAccounts(state) {
return state.entities.accounts;
}
diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js
index f311a0e1..d49fb81f 100644
--- a/embark-ui/src/sagas/index.js
+++ b/embark-ui/src/sagas/index.js
@@ -3,11 +3,10 @@ import * as api from '../services/api';
import * as storage from '../services/storage';
import {eventChannel} from 'redux-saga';
import {all, call, fork, put, takeEvery, take, select} from 'redux-saga/effects';
+import {getToken} from '../reducers/selectors';
function *doRequest(entity, serviceFn, payload) {
- payload.token = yield select(function (state) {
- return state.token;
- });
+ payload.token = yield select(getToken);
const {response, error} = yield call(serviceFn, payload);
if(response) {
yield put(entity.success(response.data, payload));
@@ -42,13 +41,14 @@ export const fetchFile = doRequest.bind(null, actions.file, api.fetchFile);
export const postFile = doRequest.bind(null, actions.saveFile, api.postFile);
export const deleteFile = doRequest.bind(null, actions.removeFile, api.deleteFile);
export const fetchEthGas = doRequest.bind(null, actions.gasOracle, api.getEthGasAPI);
-export const authorize = doRequest.bind(null, actions.authorize, api.authorize);
+export const authenticate = doRequest.bind(null, actions.authenticate, api.authenticate);
export const fetchCurrentFile = doRequest.bind(null, actions.currentFile, storage.fetchCurrentFile);
export const postCurrentFile = doRequest.bind(null, actions.saveCurrentFile, storage.postCurrentFile);
export const deleteCurrentFile = doRequest.bind(null, null, storage.deleteCurrentFile);
-export const fetchToken = doRequest.bind(null, actions.getToken, storage.fetchToken);
+export const fetchToken = doRequest.bind(null, actions.fetchToken, storage.fetchToken);
export const postToken = doRequest.bind(null, actions.postToken, storage.postToken);
+export const logout = doRequest.bind(null, actions.logout, storage.logout);
export function *watchFetchTransaction() {
@@ -172,20 +172,24 @@ export function *watchPostCurrentFile() {
yield takeEvery(actions.SAVE_CURRENT_FILE[actions.REQUEST], postCurrentFile);
}
-export function *watchFetchToken() {
- yield takeEvery(actions.GET_TOKEN[actions.REQUEST], fetchToken);
-}
-
-export function *watchPostToken() {
- yield takeEvery(actions.POST_TOKEN[actions.REQUEST], postToken);
-}
-
export function *watchFetchEthGas() {
yield takeEvery(actions.GAS_ORACLE[actions.REQUEST], fetchEthGas);
}
export function *watchAuthenticate() {
- yield takeEvery(actions.AUTHORIZE[actions.REQUEST], authorize);
+ yield takeEvery(actions.AUTHENTICATE[actions.REQUEST], authenticate);
+}
+
+export function *watchAuthenticateSuccess() {
+ yield takeEvery(actions.AUTHENTICATE[actions.SUCCESS], postToken);
+}
+
+export function *watchFetchToken() {
+ yield takeEvery(actions.FETCH_TOKEN[actions.REQUEST], fetchToken);
+}
+
+export function *watchLogout() {
+ yield takeEvery(actions.LOGOUT[actions.REQUEST], logout);
}
function createChannel(socket) {
@@ -297,9 +301,10 @@ export default function *root() {
fork(watchFetchCurrentFile),
fork(watchPostCurrentFile),
fork(watchFetchToken),
- fork(watchPostToken),
fork(watchFetchEthGas),
fork(watchAuthenticate),
+ fork(watchAuthenticateSuccess),
+ fork(watchLogout),
fork(watchListenGasOracle)
]);
}
diff --git a/embark-ui/src/services/api.js b/embark-ui/src/services/api.js
index 90ec8681..48f73a7b 100644
--- a/embark-ui/src/services/api.js
+++ b/embark-ui/src/services/api.js
@@ -137,8 +137,8 @@ export function deleteFile(payload) {
return destroy('/file', {params: payload, token: payload.token});
}
-export function authorize() {
- return post('/authorize', ...arguments);
+export function authenticate() {
+ return post('/authenticate', ...arguments);
}
// TODO token for WS?
diff --git a/embark-ui/src/services/storage.js b/embark-ui/src/services/storage.js
index b9080681..a1d9ebdc 100644
--- a/embark-ui/src/services/storage.js
+++ b/embark-ui/src/services/storage.js
@@ -18,18 +18,23 @@ export function deleteCurrentFile() {
});
}
-export function postToken(data) {
+export function postToken({token}) {
return new Promise(function(resolve) {
- localStorage.setItem('token', data.token);
- resolve({response: {data: data.token}});
- });
-}
-
-export function fetchToken({callback}) {
- callback = callback || function(){};
- return new Promise(function(resolve) {
- const token = localStorage.getItem('token');
- callback(null, token);
+ localStorage.setItem('token', token);
resolve({response: {data: token}});
});
}
+
+export function fetchToken() {
+ return new Promise(function(resolve) {
+ const token = localStorage.getItem('token');
+ resolve({response: {data: token}});
+ });
+}
+
+export function logout() {
+ return new Promise(function(resolve) {
+ localStorage.clear();
+ resolve({response: true});
+ });
+}
diff --git a/lib/modules/authenticator/index.js b/lib/modules/authenticator/index.js
index 8a6e6c8f..ac4a6af5 100644
--- a/lib/modules/authenticator/index.js
+++ b/lib/modules/authenticator/index.js
@@ -16,10 +16,10 @@ class Authenticator {
registerCalls() {
this.embark.registerAPICall(
'post',
- '/embark-api/authorize',
+ '/embark-api/authenticate',
(req, res) => {
if (req.body.token !== this.authToken) {
- this.logger.warn(__('Someone tried and failed to authorize to the backend'));
+ this.logger.warn(__('Someone tried and failed to authenticate to the backend'));
this.logger.warn(__('- User-Agent: %s', req.headers['user-agent']));
this.logger.warn(__('- Referer: %s', req.headers.referer));
return res.send(ERROR_OBJ);
@@ -32,7 +32,7 @@ class Authenticator {
return {
match: () => cmd === "token",
process: (callback) => {
- callback(null, __('Your authorisation token: %s', this.authToken));
+ callback(null, __('Your authentication token: %s', this.authToken));
}
};
});