提交 425c3423 authored 作者: vipcxj's avatar vipcxj

Merge branch 'tmp'

# Conflicts: # .gitignore # .roadhogrc.js # src/models/domain.js # src/models/login.js # src/models/main/index.js # src/router.js
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules/
/node_modules
# production
/dist/
/dist
# misc
.DS_Store
npm-debug.log*
.idea/
/storybook-static/
/.idea
/storybook-static
const proxy = {
"target": "http://192.168.1.22:8082",
"target": "http://192.168.1.22:8080",
"changeOrigin": true,
"pathRewrite": {
"^/api": "/app/restful-services"
"^/api": "/app/api"
}
};
const resource_proxy = {
"target": "http://192.168.1.22:8082",
"target": "http://192.168.1.22:8080",
"changeOrigin": true,
"pathRewrite": {
"^/": "/app/"
"^/resource": "/app/resource"
}
};
......@@ -32,15 +32,7 @@ module.exports = {
["import", { "libraryName": "antd", "style": true }]
],
"proxy": {
"/api/app": proxy,
"/api/auth": proxy,
"/api/user": proxy,
"/api/domain": proxy,
"/api/datasource": proxy,
"/api/module": proxy,
"/api/configure": proxy,
"/api/interface": proxy,
"/api/template": proxy,
"/api": proxy,
"/resource": resource_proxy,
}
},
......
......@@ -14,14 +14,17 @@
},
"dependencies": {
"antd": "^2.13.0",
"axo": "0.0.2",
"babel-plugin-import": "^1.2.1",
"babel-runtime": "^6.9.2",
"bowser": "^1.8.1",
"bundle-loader": "^0.5.5",
"dva": "^1.2.1",
"dva-loading": "^0.2.1",
"fastjson_ref_resolver": "latest",
"fingerprintjs": "^0.5.3",
"lodash": "^4.17.4",
"lowdb": "^1.0.0",
"moment": "^2.18.1",
"prop-types": "^15.5.10",
"qrcode": "^1.0.0",
......
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Input, Spin } from 'antd';
import { getCert, init, sign } from '../../utils/uca';
class UCA extends Component {
constructor(props, context) {
super(props, context);
this.state = {
ready: false,
};
}
componentDidMount() {
this.task = setInterval(() => {
const res = init();
if (res === 0) {
clearInterval(this.task);
this.task = null;
this.setState({
ready: true,
});
}
}, 300);
}
componentWillUnmount() {
if (this.task) {
clearInterval(this.task);
}
}
onChange = (value) => {
if (this.state.ready && this.props.onChange) {
this.props.onChange(this.valueToObj(value));
}
};
objToValue = (obj) => {
return obj ? obj.input : '';
};
valueToObj = (value) => {
if (!value) {
return value;
}
if (this.props.data) {
return sign(value, this.props.data);
} else {
return getCert(value);
}
};
render() {
const onChange = (e) => {
return this.onChange(e.target.value);
};
const { loading, value, style, ...rest } = this.props;
const { width, height } = style || {};
return (
<div style={{ width, height }}>
<Spin spinning={!this.state.ready || loading} size="small">
<Input {...rest} disabled={!this.state.ready || loading} value={this.objToValue(value)} onChange={onChange} type="password" />
</Spin>
</div>
);
}
}
UCA.propTypes = {
loading: PropTypes.bool,
data: PropTypes.string,
};
UCA.defaultProps = {
loading: false,
};
export default UCA;
import { histories } from '../utils/auth';
const data = {
app: null,
};
export function initApp(app) {
export async function initApp(app) {
data.app = app;
await histories.init();
await histories.createHistory('domain', 10);
await histories.createHistory('module', 50);
return data.app;
}
export function getApp() {
......
......@@ -90,59 +90,3 @@ export const invalidateLayout = (name) => {
export const invalidateLayouts = () => {
data.flags.layout = {};
};
// export const setModules = (modules) => {
// data.init = true;
// data.modules = modules;
// };
//
// export const getModules = () => {
// return data.modules;
// };
//
// export const isInited = () => {
// return data.init;
// };
//
// // noinspection EqualityComparisonWithCoercionJS
// export const getModule = id => data.modules.filter(m => m.id == id).pop(); // eslint-disable-line eqeqeq, max-len
//
// export const getPath = (id) => {
// const module = getModule(id);
// if (module.parent) {
// const parent = getModule(module.parent);
// return [...getPath(parent.id), id];
// } else {
// return [id];
// }
// };
//
// export const getChildren = (id) => {
// const children = [];
// for (const module of data.modules) {
// // noinspection EqualityComparisonWithCoercionJS
// if (module.parent == id) { // eslint-disable-line eqeqeq
// children.push(module);
// }
// }
// return children;
// };
//
// export function foreachModule(callback) {
// for (const module of data.modules) {
// callback(module);
// }
// }
//
// export const setModuleConfigure = (id, configure) => {
// const module = getModule(id);
// if (module) {
// module.configure = configure;
// }
// };
//
// export const getModuleConfigure = (id) => {
// const module = getModule(id);
// return module ? module.configure : null;
// };
......@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dva Demo</title>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="https://cdn.bootcss.com/babel-polyfill/6.26.0/polyfill.min.js"></script>
<!--<link rel="stylesheet" href="/index.css" />-->
</head>
<body>
......
......@@ -19,8 +19,6 @@ const app = dva({
},
});
initApp(app);
app.model(appModel);
// 2. Plugins
......@@ -35,4 +33,11 @@ app.use(createLoading({
app.router(routerConfig);
// 5. Start
app.start('#root');
initApp(app).then(theApp => theApp.start('#root'));
export default app;
export const getStore = () => {
// eslint-disable-next-line no-underscore-dangle
return app._store;
};
import { routerRedux } from 'dva/router';
import { login, userInfo } from '../services/login';
import { fullPath } from '../utils/helper';
import { setToken, setUser } from '../utils/auth';
import { authorize, login, userInfo } from '../services/login';
import { validate as passValidate } from '../services/login/password';
import { requestCode, validate as ucaValidate } from '../services/login/uca';
import { fullPath, encrypt } from '../utils/helper';
import { setToken, setUser, setDomain, histories } from '../utils/auth';
import { switchDomain, currentDomain } from '../services/domain';
import { errors } from '../utils/error';
import config from '../utils/config';
import { getStore } from '../index';
const successAuthed = async (tokenId, userName, remember) => {
await histories.pushHistory('userName', remember ? userName : '');
await setToken(tokenId);
const uInfo = await userInfo();
await setUser(uInfo.id, uInfo.name);
const path = await histories.getLatest('domain');
if (!path) {
getStore().dispatch(routerRedux.push(fullPath('/domain')));
} else {
await switchDomain(path);
const domain = await currentDomain();
if (domain) {
await setDomain(domain.name, path);
const latest = await histories.getLatest('module');
if (latest && config.fastNavigationPage) {
getStore().dispatch(routerRedux.push(fullPath('/fastNav')));
} else {
getStore().dispatch(routerRedux.push(fullPath('/main')));
}
} else {
getStore().dispatch(routerRedux.push(fullPath('/domain')));
}
}
};
const processAuthRequirements = (requirements, supports) => {
let res = [];
for (const requirement of requirements) {
const { authTypes } = requirement;
if (authTypes && authTypes.length > 0) {
const filteredAuthTypes = authTypes.filter(authType => supports.indexOf(authType) !== -1);
if (filteredAuthTypes.length === 0) {
throw errors.unsupportedAuthType(authTypes);
}
res = res.map(require => require.filter(item => authTypes.indexOf(item) === -1)).filter(require => require.length > 0);
res.push(filteredAuthTypes);
}
}
return res;
};
let tkId;
export default {
namespace: 'login',
state: {},
reducers: {},
state: {
status: 'login',
userName: '',
ucaCode: '',
authRequires: [],
},
reducers: {
setStatus(state, { payload }) {
return {
...state,
status: payload,
};
},
setUserName(state, { payload }) {
return {
...state,
userName: payload,
};
},
setUCACode(state, { payload }) {
return {
...state,
ucaCode: payload,
};
},
setAuthRequires(state, { payload }) {
return {
...state,
authRequires: payload,
};
},
},
effects: {
*login({ payload }, { call, put }) {
const result = yield call(login, payload);
const { tokenId, authResponse, remainedAuthRequirements } = result;
if (authResponse.status !== 'authed' && authResponse.status !== 'skipped') {
*init(ignored, { put, call }) {
yield put({ type: 'setStatus', payload: 'login' });
yield put({ type: 'setUCACode', payload: '' });
yield put({ type: 'setAuthRequires', payload: [] });
const userName = yield call(histories.getLatest, 'userName');
yield put({ type: 'setUserName', payload: userName || '' });
},
*login({ payload: userName }, { call, put }) {
const { tokenId, remainedAuthRequirements } = yield call(login, {
type: 'userName',
data: userName,
});
tkId = tokenId;
const { requirements } = remainedAuthRequirements;
const requires = processAuthRequirements(requirements, ['password', 'uca']);
if (requires.length === 0) {
yield call(successAuthed, tokenId);
} else {
if (requires.some(req => req.indexOf('uca') !== -1)) {
yield put({ type: 'requestUCACode' });
}
yield put({ type: 'setAuthRequires', payload: requires });
}
yield put({ type: 'setStatus', payload: 'auth' });
},
*requestUCACode(ignored, { call, put }) {
const { data } = yield call(authorize, yield call(requestCode, encrypt(tkId)));
yield put({ type: 'setUCACode', payload: data });
},
*auth({ payload: { userName, password, uca, remember } }, { call }) {
let response;
if (password) {
response = yield call(authorize, yield call(passValidate, password, encrypt(tkId)));
const { status } = response;
if (status !== 'authed' && status !== 'skipped') {
throw errors.wrongPassword();
}
const { requirements } = remainedAuthRequirements;
if (requirements.length > 0) {
throw errors.unsupportedAuthType(requirements);
}
yield call(setToken, tokenId);
const uInfo = yield call(userInfo);
yield call(setUser, uInfo.id, uInfo.name);
yield put(routerRedux.push(fullPath('/main')));
if (uca) {
if (uca.status === 0) {
response = yield call(authorize, yield call(ucaValidate, uca.signed, encrypt(tkId)));
const { status } = response;
if (status !== 'authed' && status !== 'skipped') {
throw errors.generalError('证书验证失败。');
}
} else {
throw errors.generalError(uca.error);
}
}
if (response) {
const { status, remainedAuthRequirements, data } = response;
if (status !== 'authed' && status !== 'skipped') {
throw errors.authFailed(data);
}
if (remainedAuthRequirements.requirements.length === 0) {
yield call(successAuthed, tkId, userName, remember);
}
}
},
},
subscriptions: {},
......
export default [];
export default [/* {
name: 'test-menu',
showName: '测试',
modules: [{
name: 'test-module',
showName: '测试',
layout: {
route: 'test',
},
}],
}*/];
......@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Router } from 'dva/router';
import _ from 'lodash';
import config from './utils/config';
import { isAuthed } from './utils/auth';
import { isAuthed, histories } from './utils/auth';
import { fullPath, makePromise0 } from './utils/helper';
import { processError } from './utils/error';
import App from './routes/app';
......@@ -29,14 +29,28 @@ const authenticated = async (replace) => {
await maybeLogin(replace);
};
const createRoute = async (app, { name, showName, modules, children }) => {
const combinePath = (base, path) => {
if (!base) {
return path;
}
if (base[base.length - 1] === '/') {
return `${base}${path}`;
} else {
return `${base}/${path}`;
}
};
const createRoute = async (app, group, basePath) => {
const { name, showName, modules, children } = group;
const theFullPath = combinePath(basePath, name);
// noinspection JSUnusedLocalSymbols
return {
path: name,
fullPath: theFullPath,
name: showName,
component: Monk,
getChildRoutes(nextState, cb) {
createRoutes(app, modules, children)
createRoutes(app, modules, children, theFullPath)
.then((result) => {
cb(null, result);
})
......@@ -48,7 +62,7 @@ const createRoute = async (app, { name, showName, modules, children }) => {
};
const createRoutes = async (app, modules, groups) => {
const createRoutes = async (app, modules, groups, basePath) => {
const routes = [];
if (modules) {
for (const module of modules) {
......@@ -61,9 +75,10 @@ const createRoutes = async (app, modules, groups) => {
info = module;
layout = module.layout;
}
const { name, showName } = info;
const { name, showName, icon, description } = info;
const route = {
path: name,
fullPath: combinePath(basePath, name),
name: showName,
};
if (layout.route) {
......@@ -81,12 +96,50 @@ const createRoutes = async (app, modules, groups) => {
} else {
route.component = Monk;
}
if (route.onEnter) {
const onEnter = route.onEnter;
route.onEnter = (nextState, replace, cb) => {
if (nextState && nextState.location && nextState.location.pathname === route.fullPath) {
histories.pushHistory('module', {
name,
showName,
icon,
description,
path: route.fullPath,
}).then(() => new Promise((resolve, reject) => {
onEnter(nextState, replace, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
})).then(() => cb()).catch(err => cb(err));
} else {
return onEnter(nextState, replace, cb);
}
};
} else {
route.onEnter = (nextState, replace, cb) => {
if (nextState && nextState.location && nextState.location.pathname === route.fullPath) {
histories.pushHistory('module', {
name,
showName,
icon,
description,
path: route.fullPath,
}).then(() => cb()).catch(err => cb(err));
} else {
cb();
}
};
}
routes.push(route);
}
}
if (groups) {
for (const group of groups) {
routes.push(await createRoute(app, group));
routes.push(await createRoute(app, group, basePath));
}
}
return routes;
......@@ -98,7 +151,18 @@ function RouterConfig({ history, app }) {
path: `${contextPath}/`,
component: App,
indexRoute: {
onEnter: (nextState, replace) => replace(`${contextPath}/main`),
onEnter: (nextState, replace, cb) => {
histories.getLatest('module').then((latest) => {
if (latest && config.fastNavigationPage) {
replace(`${contextPath}/fastNav`);
} else {
replace(`${contextPath}/main`);
}
cb();
}).catch((err) => {
cb(err);
});
},
},
childRoutes: [
{
......@@ -124,7 +188,7 @@ function RouterConfig({ history, app }) {
getChildRoutes: (nextState, cb) => {
getMenus()
.then((menus) => {
createRoutes(app, [], menus)
createRoutes(app, [], menus, `${contextPath}/main`)
.then((result) => {
cb(null, result);
})
......@@ -140,6 +204,20 @@ function RouterConfig({ history, app }) {
],
},
];
if (config.fastNavigationPage) {
routes[0].childRoutes.push({
path: 'fastNav',
onEnter: (ignored, replace, cb) => {
authenticated(replace).then(() => cb()).catch(err => cb(err));
},
getComponent(ignored, cb) {
require.ensure([], (require) => {
registerModel(app, require('./models/main/modules/' + config.fastNavigationPage)); // eslint-disable-line import/no-dynamic-require, prefer-template
cb(null, require('./routes/main/modules/' + config.fastNavigationPage)); // eslint-disable-line import/no-dynamic-require, prefer-template
}, 'fastNav');
},
});
}
return (
<Router history={history} routes={routes} onError={processError} />
);
......
import React from 'react';
import { Input, Select } from 'antd';
import styles from './authInput.less';
class AuthInputs extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
index: props.data.length > 0 ? 0 : -1,
};
}
onChange = (value) => {
this.setState({
index: this.props.data.findIndex(item => item.key === value),
});
};
render() {
const { data } = this.props;
const select = this.state.index >= 0 ? data[this.state.index].key : undefined;
if (data.length > 0) {
return (
<Input.Group compact className={styles['bl-authInput']}>
<Select value={select} onChange={this.onChange} style={{ width: '30%' }}>
{
data.map(item => (<Select.Option value={item.key} key={item.key}> { item.label } </Select.Option>))
}
</Select>
{
React.cloneElement(data[this.state.index >= 0 ? this.state.index : 0].node(),
{
style: { width: '70%' },
})
}
</Input.Group>
);
}
}
}
export default AuthInputs;
.bl-authInput > * {
vertical-align: middle!important;
}
......@@ -5,34 +5,113 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Form, Icon, Input, Checkbox, Button } from 'antd';
import { connect } from 'dva';
import AuthInputs from './authInput';
import UCA from '../../components/uca';
import config from '../../utils/config';
import styles from './index.less';
const FormItem = Form.Item;
class LoginForm extends React.Component {
constructor(props, context) {
super(props, context);
this.handleSubmit = this::this.handleSubmit;
componentDidMount() {
this.props.dispatch({ type: 'login/init' });
}
handleSubmit(e) {
onUserBlur = (e) => {
const { value } = e.target;
if (value) {
this.props.dispatch({ type: 'login/login', payload: value });
}
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.dispatch({
type: 'login/login',
payload: {
type: 'userName',
authType: 'password',
...values,
},
if (this.props.login.status === 'login') {
this.authFocus = true;
this.props.dispatch({ type: 'login/login', payload: values.userName });
} else if (this.props.login.status === 'auth') {
this.props.dispatch({ type: 'login/auth', payload: values });
}
}
});
};
createPassword = (focus = false) => {
const { getFieldDecorator } = this.props.form;
return getFieldDecorator('password', {
rules: [{
required: true,
message: '请输入密码。',
}],
})(
<Input autoFocus={focus} prefix={<Icon type="lock" />} type="password" placeholder="密码" />,
);
};
createUCA = (focus = false) => {
const { form, login } = this.props;
const { getFieldDecorator } = form;
return getFieldDecorator('uca', {
rules: [{
required: true,
message: '请输入usb-key的密钥。',
}],
})(
<UCA autoFocus={focus} loading={!login.ucaCode} data={login.ucaCode} prefix={<Icon type="hdd" placeholder="密钥" />} />,
);
};
createValidates = () => {
const { login } = this.props;
if (login.status === 'login') {
return [];
} else if (login.status === 'auth') {
const { authRequires } = login;
return authRequires.map((authRequire) => {
const pass = authRequire.indexOf('password') !== -1;
const uca = authRequire.indexOf('uca') !== -1;
let focus = false;
if (pass || uca) {
if (this.authFocus) {
focus = true;
this.authFocus = false;
}
}
if (pass && uca) {
return (
<Form.Item key="passOrUca">
<AuthInputs
data={[
{
key: 'password',
label: '密码',
node: () => this.createPassword(focus),
},
{
key: 'uca',
label: '证书',
node: () => this.createUCA(focus),
},
]}
/>
</Form.Item>
);
} else if (pass) {
return (
<Form.Item key="pass">
{ this.createPassword(focus) }
</Form.Item>
);
} else if (uca) {
return (
<Form.Item key="uca">
{ this.createUCA(focus) }
</Form.Item>
);
} else {
return <Form.Item />;
}
});
}
};
render() {
const { getFieldDecorator } = this.props.form;
......@@ -45,31 +124,31 @@ class LoginForm extends React.Component {
<span>{config.name}</span>
</div>
<Form onSubmit={this.handleSubmit}>
<FormItem>
<Form.Item>
{
getFieldDecorator('userName', {
initialValue: this.props.login.userName,
rules: [{
required: true,
message: '请输入用户名。',
}],
})(
<Input prefix={<Icon type="user" />} placeholder="用户名" />,
<Input
autoFocus={!this.authFocus}
onBlur={this.onUserBlur}
prefix={<Icon type="user" />}
placeholder="用户名"
onChange={() => {
this.props.dispatch({ type: 'login/setStatus', payload: 'login' });
}}
/>,
)
}
</FormItem>
<FormItem>
</Form.Item>
{
getFieldDecorator('password', {
rules: [{
required: true,
message: '请输入密码。',
}],
})(
<Input prefix={<Icon type="lock" />} type="password" placeholder="密码" />,
)
this.createValidates()
}
</FormItem>
<FormItem>
<Form.Item>
{
getFieldDecorator('remember', {
valuePropName: 'checked',
......@@ -83,7 +162,7 @@ class LoginForm extends React.Component {
登录
</Button>
或者<a>现在注册!</a>
</FormItem>
</Form.Item>
</Form>
</div>
</div>
......
......@@ -7,7 +7,6 @@
.container {
width: 320px;
height: 320px;
position: absolute;
background-color: white;
top: 50%;
......
......@@ -2,7 +2,7 @@
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
import { getDomain } from '../utils/auth';
import { getDomain, histories } from '../utils/auth';
export async function fetchDomains(basePath, withRoot = false) {
if (!basePath) {
......@@ -31,7 +31,8 @@ export async function fetchDomains(basePath, withRoot = false) {
}
export async function switchDomain(path) {
return post(`${config.apiContextPath}/api/domain/user/path`, { dmPath: path });
await post(`${config.apiContextPath}/api/domain/user/path`, { dmPath: path });
histories.pushHistory('domain', path);
}
export async function currentDomain() {
......
import request from '../utils/request';
import post from '../utils/post';
import doDelete from '../utils/delete';
import config from '../utils/config';
export const userApi = {
async getAllInterfaceInfoes() {
return request(`${config.apiContextPath}/api/interface/user/info`);
},
async getInterfaceInfo(name) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/info`);
},
async validateState(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/invoke/validateState`, { dmPath });
},
async invokeInterface(name, params, dmPath) {
return post(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/invoke`, { params, dmPath });
},
};
export const adminApi = {
async getAllInterfaceInfoes(dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/info`, { dmPath });
},
async getInterfaceInfo(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/info`, { dmPath });
},
async getInterfaceConfigure(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { dmPath });
},
async getInterfacePlainConfigure(name, dmPath) {
return post(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/plainConfigure`, { dmPath });
},
async setInterfaceConfigure(name, configure, dmPath) {
return post(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { configure, dmPath });
},
async removeInterfaceConfigure(name, dmPath) {
return doDelete(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { dmPath });
},
};
import Fingerprint from 'fingerprintjs';
import post from '../utils/post';
import request from '../utils/request';
import config from '../utils/config';
export async function login(payload) {
const data = {};
data.type = payload.type;
if (data.type === 'userName') {
data.data = payload.userName;
}
if (payload.authType === 'password') {
data.authRequest = {
type: payload.authType,
parameters: {
cipher: payload.password,
},
};
}
data.tokenInfo = {
productId: 'big-machine-web-front',
deviceId: `${new Fingerprint({ ie_activex: true }).get()}`,
};
return post(`${config.apiContextPath}/api/auth/login`, data, {}, {}, false);
}
export async function userInfo() {
return request(`${config.apiContextPath}/api/user/info`);
}
/* eslint-disable no-param-reassign */
/** @module services/login */
import { getDeviceId } from '../../utils/device';
import post from '../../utils/post';
import request from '../../utils/request';
import { encrypt } from '../../utils/helper';
import { getToken } from '../../utils/auth';
import config from '../../utils/config';
/**
* @typedef {Object} TokenInfo
* @property {number} [life]
* @property {boolean} [persist]
* @property {!string} productId
* @property {!string} deviceId
*/
/**
* @typedef {Object} LoginRequest
* @property {!string} type
* @property {string} [data]
* @property {TokenInfo} [tokenInfo]
* @property {AuthRequest} [authRequest]
*/
/**
* @typedef {Object} AuthRequest
* @property {!string} type
* @property {Object} [parameters]
*/
/**
* @typedef {Object} LoginResponse
* @property {!string} tokenId
* @property {?AuthResponse} authResponse
* @property {!AuthRequirements} remainedAuthRequirements
*/
/**
* @typedef {Object} AuthResponse
* @property {!string} type
* @property {!string} status
* @property {?Object} data
*/
/**
* @typedef {Object} AuthRequirements
* @property {Array.<AuthRequirement>} requirements
*/
/**
* @typedef {Object} AuthRequirement
* @property {Array.<string>} authTypes
*/
/**
* 登录
* @param {!LoginRequest} loginRequest 登录请求
* @returns {Promise.<LoginResponse>}
*/
export async function login(loginRequest) {
if (!loginRequest.tokenInfo) {
loginRequest.tokenInfo = {
productId: config.productId,
deviceId: `${getDeviceId()}`,
};
}
return post(`${config.apiContextPath}/api/auth/login`, loginRequest, {}, {}, false);
}
/**
* 认证
* @param {{tkId: ?string, request: AuthRequest}} authRequest 认证请求
* @return {Promise.<AuthResponse>}
*/
export async function authorize(authRequest) {
if (!authRequest.tkId) {
authRequest.tkId = encrypt(await getToken());
}
return post(`${config.apiContextPath}/api/auth/authorize`, authRequest, {}, {}, false);
}
export async function userInfo() {
return request(`${config.apiContextPath}/api/user/info`);
}
import { addToken } from './utils';
export const validate = async (password, token) => {
const request = {
type: 'password',
parameters: {
cipher: password,
},
};
return addToken(request, token);
};
import { addToken } from './utils';
export const requestCode = async (token) => {
const request = {
type: 'uca',
parameters: {
action: 'request',
},
};
return addToken(request, token);
};
export const validate = async (signed, token) => {
const request = {
type: 'uca',
parameters: {
action: 'validate',
response: signed,
},
};
return addToken(request, token);
};
import { getToken } from '../../utils/auth';
export const addToken = async (request, token) => {
if (token) {
return {
tkId: token,
request,
};
} else {
let tkId;
const localToken = await getToken();
if (localToken) {
// eslint-disable-next-line no-param-reassign
tkId = localToken;
}
if (tkId) {
return {
tkId,
request,
};
} else {
return {
request,
};
}
}
};
import { castArray } from 'lodash';
import post from '../utils/post';
import config from '../utils/config';
import request from '../utils/request';
export class Operations {
constructor() {
this.operations = [];
}
addCmd = (cmd, ...args) => {
this.operations.push({ cmd, args });
return this;
};
setName = (name) => {
return this.addCmd('setName', name);
};
setRight = (right) => {
return this.addCmd('setRight', right);
};
addTags = (...tags) => {
return this.addCmd('addTags', ...tags);
};
removeTags = (...tags) => {
return this.addCmd('removeTags', ...tags);
};
clearTags = () => {
return this.addCmd('clearTags');
};
setTags = (...tags) => {
return this.addCmd('setTags', ...tags);
};
addOwners = (...owners) => {
return this.addCmd('addOwners', ...owners);
};
removeOwners = (...owners) => {
return this.addCmd('removeOwners', ...owners);
};
clearOwners = () => {
return this.addCmd('clearOwners');
};
setOwners = (...owners) => {
return this.addCmd('setOwners', ...owners);
};
addOwnerGroups = (...groups) => {
return this.addCmd('addOwnerGroups', ...groups);
};
removeOwnerGroups = (...groups) => {
return this.addCmd('removeOwnerGroups', ...groups);
};
clearOwnerGroups = () => {
return this.addCmd('clearOwnerGroups');
};
setOwnerGroups = (...groups) => {
return this.addCmd('setOwnerGroups', ...groups);
};
use = (usage) => {
return this.addCmd('use', usage);
};
unuse = (usage) => {
return this.addCmd('unuse', usage);
};
}
export const createOperations = () => {
return new Operations();
};
export async function editResource(uri, operations) {
return post(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`, { operations: operations.operations });
}
export async function getResourceInfo(uri) {
return request(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`);
}
export async function queryResourceByUsage(usage, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, pst, psz });
}
export async function queryResourceByUsageStartWith(usage, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, startWith: true, pst, psz });
}
export async function queryResourceByAllTags(tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { tags: castArray(tags), pst, psz });
}
export async function queryResourceByAnyTags(tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { tags: castArray(tags), andOr: 'or', pst, psz });
}
export async function queryResourceByUsageAndAllTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, tags: castArray(tags), pst, psz });
}
export async function queryResourceByUsageAndAnyTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, tags: castArray(tags), andOr: 'or', pst, psz });
}
export async function queryResourceByUsageStartWithAndAllTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, startWith: true, tags: castArray(tags), pst, psz });
}
export async function queryResourceByUsageStartWithAndAnyTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, startWith: true, tags: castArray(tags), andOr: 'or', pst, psz });
}
......@@ -5,21 +5,21 @@ import config from '../utils/config';
const { contextPath } = config;
const makePath = (base, path) => {
const makePath = (base, path, withContext = true) => {
if (path.startsWith('/')) {
return `${contextPath}${path}`;
return withContext ? `${contextPath}${path}` : path;
}
const basePath = base.endsWith('/') ? base : `${base}/`;
return resolvePathname(path, basePath);
};
const processPath = (base, path) => {
const processPath = (base, path, withContext = true) => {
if (isString(path)) {
return makePath(base, path);
return makePath(base, path, withContext);
}
return {
...path,
pathname: makePath(base, path.pathname),
pathname: makePath(base, path.pathname, withContext),
};
};
......@@ -41,12 +41,12 @@ history.listen((loc) => {
location.hash = loc.hash;
});
export const push = (path, state) => {
return history.push(processPath(getHistoryBase(history), path), state);
export const push = (path, state, withContext = true) => {
return history.push(processPath(getHistoryBase(history), path, withContext), state);
};
export const replace = (path, state) => {
return history.replace(processPath(getHistoryBase(history), path), state);
export const replace = (path, state, withContext = true) => {
return history.replace(processPath(getHistoryBase(history), path, withContext), state);
};
export const go = (n) => {
......@@ -81,15 +81,15 @@ const getThisBase = (theThis) => {
}
};
export const thisPush = (theThis, pathOrLoc) => {
export const thisPush = (theThis, pathOrLoc, withContext = true) => {
checkThis(theThis);
const route = processPath(getThisBase(theThis), pathOrLoc);
const route = processPath(getThisBase(theThis), pathOrLoc, withContext);
return theThis.props.router.push(route);
};
export const thisReplace = (theThis, pathOrLoc) => {
export const thisReplace = (theThis, pathOrLoc, withContext = true) => {
checkThis(theThis);
const route = processPath(getThisBase(theThis), pathOrLoc);
const route = processPath(getThisBase(theThis), pathOrLoc, withContext);
return theThis.props.router.replace(route);
};
......
import post from '../utils/post';
import config from '../utils/config';
export const bindCert = async (uid, cert) => {
return post(`${config.apiContextPath}/api/uca/admin/cert`, { uid, cert });
};
/* eslint-disable no-param-reassign */
/**
* Created by yaohx_169 on 2017/6/8.
*/
import { cookie } from './config';
import { getCookie, setCookie, delCookie } from './helper';
import db from './db';
export async function getToken() {
return getCookie(cookie.token);
......@@ -57,3 +59,110 @@ export async function isAuthed() {
export async function hasDomain() {
return getDomain().then(result => !!result);
}
const normHistory = (history, size) => {
if (!history) {
history = {};
}
if (!history.size) {
if (size) {
history.size = size;
} else {
history.size = 10;
}
} else if (size && history.size !== size) {
history.size = size;
}
if (history.size < 1) {
history.size = 1;
}
if (!history.data) {
history.data = [];
history.start = 0;
history.top = 0;
history.empty = true;
}
return history;
};
const next = (i, size) => {
if (i < 0 || i >= size) {
throw new Error(`out of range: ${i} in ${size}`);
}
if (i + 1 >= size) {
return 0;
} else {
return i + 1;
}
};
const prev = (i, size) => {
if (i < 0 || i >= size) {
throw new Error(`out of range: ${i} in ${size}`);
}
if (i - i < 0) {
return size - 1;
} else {
return i - 1;
}
};
export const histories = {
async getLatest(name) {
let history = await db.get(`history.${name}`).value();
history = normHistory(history);
if (history.empty) {
return null;
}
return history.data[prev(history.top, history.size)];
},
async createHistory(name, size) {
let history = db.get(`history.${name}`).value();
history = normHistory(history, size);
return db.set(`history.${name}`, history).write();
},
async destroyHistory(name) {
return db.unset(`history.${name}`).write();
},
async getHistory(name, size) {
let history = db.get(`history.${name}`).value();
history = normHistory(history, size);
if (history.empty) {
return [];
} else if (history.top > history.start) {
return history.data.slice(history.start, history.top);
} else {
return [...history.data.slice(history.start, history.size), ...history.data.slice(0, history.top)];
}
},
async pushHistory(name, value, size) {
let history = await db.get(`history.${name}`).value();
history = normHistory(history, size);
history.data[history.top] = value;
const nextPos = next(history.top, history.size);
if (!history.empty && history.start === history.top) {
history.top = history.start = nextPos;
} else {
history.top = nextPos;
}
if (history.empty) {
history.empty = false;
}
return db.set(`history.${name}`, history).write();
},
async popHistory(name) {
let history = await db.get(`history.${name}`).value();
history = normHistory(history);
if (history.empty) {
return;
}
history.top = prev(history.top, history.size);
if (history.top === history.start) {
history.empty = true;
}
return db.set(`history.${name}`, history).write();
},
async init() {
return db.read();
},
};
......@@ -14,11 +14,25 @@ export const cookie = {
export const errors = {
exception: 0x00010000,
no_such_user: 0x00010001,
wrong_password: 0x00010002,
invalid_token: 0x00010003,
invalid_query_param: 0x00010004,
domain_not_set: 0x00010005,
wrong_sms_code: 0x00010006,
sms_code_time_out: 0x00010007,
invalid_mobile: 0x00010008,
no_operation_right: 0x00010100,
no_module_right: 0x00010101,
no_interface_right: 0x00010102,
no_batch_right: 0x00010103,
no_resource_right: 0x00010104,
no_domain_right: 0x00010105,
no_datasource_right: 0x00010106,
general_error: 0x00011111,
// client error:
token_missing: 0x00000001,
wrong_password: 0x00000002,
unsupported_auth_type: 0x00000003,
auth_failed: 0x00000004,
};
export const api = {
......@@ -45,6 +59,8 @@ const config = {
logo: `${_contextPath}/logo.png`,
contextPath: _contextPath,
apiContextPath: _apiContextPath,
productId: 'big-machine-web-front',
fastNavigationPage: '',
defaultDateFormat,
defaultTimeFormat,
defaultDateTimeFormat,
......
/** @module utils/db */
import low from 'lowdb';
import LocalStorage from 'lowdb/adapters/LocalStorage';
const adapter = new LocalStorage('db');
/**
* @typedef {Object} DB
* @template {T}
*/
/**
* @member {Function} DB~get
* @param {Array.<string>|string} path
* @param {*} [defaultValue]
* @return {DB.<*>}
*/
/**
* @member {Function} DB~set
* @param {Array.<string>|string} path
* @param value
* @return {DB}
*/
/**
* @member {Function} DB~find
* @template {T}
* @param {Function} [predicate=_.identity]
* @param {number} [fromIndex=0]
* @return {DB.<T>}
*/
/**
* @member {Function} DB~unset
* @param {Array.<string>|string} path
* @return {DB.<boolean>}
*/
/**
* @member {Function} DB~read
* @return {Promise.<*>}
*/
/**
* @member {Function} DB~write
* @return {Promise.<*>}
*/
/**
* @member {Function} DB~value
* @template {T}
* @return {Promise.<T>}
*/
/**
* @type {DB.<*>}
*/
const db = low(adapter);
export default db;
import Fingerprint from 'fingerprintjs';
export function getDeviceId() {
// noinspection JSUnresolvedFunction
return new Fingerprint({ ie_activex: true }).get();
}
......@@ -21,6 +21,7 @@ export function processError(err) {
message.error('用户不存在!');
break;
case errorCodes.invalid_token:
case errorCodes.token_missing:
push('/login');
break;
default:
......@@ -84,8 +85,16 @@ export const errors = {
code: errorCodes.wrong_password,
msg: '密码错误!',
}),
authFailed: msg => createError({
code: errorCodes.auth_failed,
msg: `登录验证失败!${msg || ''}`,
}),
unsupportedAuthType: (...types) => createError({
code: errorCodes.unsupported_auth_type,
msg: `不支持的客户端验证方式:${types.join('、')}.`,
}),
generalError: msg => createError({
code: errorCodes.general_error,
msg: `${msg || '未知错误。'}`,
}),
};
......@@ -17,10 +17,14 @@ export function setCookie(name, value, options = {}) {
}
export function getCookie(name) {
const reg = new RegExp(`(^|)${name}=([^;]*)(;|$)`);
const reg = new RegExp(`(^|)${name}=([^;]*)(;|$)`, 'g');
const arr = document.cookie.match(reg); // eslint-disable-line
if (arr) {
return decodeURIComponent(arr[2]);
let value = arr.map(v => v.substring(`${name}=`.length).trim()).filter(v => !!v).pop();
if (value.endsWith(';')) {
value = value.substring(0, value.length - 1);
}
return decodeURIComponent(value);
} else {
return null;
}
......
......@@ -73,13 +73,15 @@ export function parseObject(response, middleware, { num2str = false, bool2str =
const mapValue = _.curry(mapObj)(_, mapArr);
if (contentType.indexOf('json') !== -1) {
return response.json()
.then(json => (middleware ? middleware(json) : json))
.then((data) => {
let out = data;
.then((json) => {
let out = json;
if (_.isObjectLike(out)) {
out = new Resolver(out).resolve();
}
return needMap ? mapValue(out) : out;
return middleware ? middleware(out) : out;
})
.then((data) => {
return needMap ? mapValue(data) : data;
});
} else if (contentType.indexOf('xml') !== -1) {
return response.text()
......@@ -87,17 +89,18 @@ export function parseObject(response, middleware, { num2str = false, bool2str =
return require.ensure([], (require) => {
const { parseString } = require('xml2js');
const options = {};
const json = JSON.parse(parseString(text, options));
return json;
return JSON.parse(parseString(text, options));
});
})
.then((json => (middleware ? middleware(json) : json)))
.then((data) => {
let out = data;
.then((json) => {
let out = json;
if (_.isObjectLike(out)) {
out = new Resolver(out).resolve();
}
return needMap ? mapValue(out) : out;
return middleware ? middleware(out) : out;
})
.then((data) => {
return needMap ? mapValue(data) : data;
});
} else if (contentType.indexOf('text') !== -1) {
return response.text();
......
import _ from 'lodash';
const { isString, get, sortBy } = _;
/**
* @callback KeyExtractor
* @param value
* @return key
*/
/**
* 获取用于对比的key
* @param value
* @param {string|KeyExtractor} [keyMethod]
* @return {*}
*/
const getKey = (value, keyMethod) => {
if (!keyMethod) {
return value;
} else if (isString(keyMethod)) {
return get(value, keyMethod);
} else {
return keyMethod(value);
}
};
/**
* 在数组中查找某个对象,并返回
* @param {Array} array
* @param toFind 查找目标
* @param {string|KeyExtractor} [key] 用于判断2个数据是否相同
* @return result 找到的对象
*/
const findBy = (array, toFind, key) => {
if (array) {
for (const el of array) {
if (getKey(el, key) === toFind) {
return el;
}
}
}
return null;
};
/**
* 计算频率数据
* @param {Array} array 数据
* @param {string|KeyExtractor} [key] 用于判断2个数据是否相同
* @param {string} [order=desc] 怎么排序,可选值为asc和desc
*/
export const toHistogram = (array, key, order = 'desc') => {
if (!array || array.length === 0) {
return [];
}
const res = [];
let newKey;
if (!key) {
newKey = 'data';
} else if (isString(key)) {
newKey = `data.${key}`;
} else {
newKey = value => key(value.data);
}
for (const el of array) {
const find = findBy(res, getKey(el, key), newKey);
if (find) {
++find.num;
} else {
res.push({
data: el,
num: 1,
});
}
}
switch (order) {
case 'desc':
return _(res).sortBy(v => v.num).reverse().value();
case 'asc':
return sortBy(res, v => v.num);
default:
throw new Error(`unsupported order: ${order}`);
}
};
import AXO from 'axo';
import bowser from 'bowser';
export const isBrowserSupport = () => {
return bowser.msie;
};
/**
* 初始化
* @return {number} 0: success; 1: blocked by the browser; 2: driver not installed
*/
export const init = () => {
if (bowser.msie) {
try {
const se = new AXO('SafeEngineCOM.SafeEngineCtl');
if (se) {
return 0;
} else {
return 2;
}
} catch (err) {
return 1;
}
} else {
return 3;
}
};
/**
* 获取证书
* @param password usb-key的密钥
* @return {{[cert]: string, [deadTime]: number, status: number, [error]: string}}
*/
export const getCert = (password) => {
if (bowser.msie) {
const safeEngine = new AXO('SafeEngineCOM.SafeEngineCtl');
if (!safeEngine) {
return {
status: 1,
input: password,
error: '未安装USB证书控件。',
};
}
safeEngine.SEH_InitialSession(27, 'com1', password, 100, 2, '', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: 'USB-KEY初始化失败,请确认USB-KEY是否插入或密码是否正确。',
};
}
try {
const cert = safeEngine.SEH_GetSelfCertificate(27, 'com1', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: `获取个人证书错误,错误代码为:${safeEngine.ErrorCode}。`,
};
}
const res = {
status: 0,
cert,
input: password,
};
const deadTime = safeEngine.SEH_GetCertValidDate(cert);
res.deadTime = parseInt(deadTime, 10);
return res;
} finally {
safeEngine.SEH_ClearSession();
}
} else {
return {
status: 1,
error: '当前浏览器不支持activeX控件,请切换ie或带ie内核的360之类的浏览器。',
};
}
};
/**
* 签名
* @param password usb-key的密钥
* @param data 需要签名的数据,从服务端获取
* @return {{input: string, [cert]: string, [deadTime]: number, [signed]: string, status: number, [error]: string}}
*/
export const sign = (password, data) => {
if (bowser.msie || bowser.msedge) {
const safeEngine = new AXO('SafeEngineCOM.SafeEngineCtl');
if (!safeEngine) {
return {
status: 1,
input: password,
error: '未安装USB证书控件。',
};
}
safeEngine.SEH_InitialSession(27, 'com1', password, 100, 2, '', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: 'USB-KEY初始化失败,请确认USB-KEY是否插入或密码是否正确。',
};
}
try {
const cert = safeEngine.SEH_GetSelfCertificate(27, 'com1', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: `获取个人证书错误,错误代码为:${safeEngine.ErrorCode}。`,
};
}
const res = { cert, status: 0, input: password };
const deadTime = safeEngine.SEH_GetCertValidDate(cert);
res.deadTime = parseInt(deadTime, 10);
const signed = safeEngine.SEH_SignData(data, 3);
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: `数字签名失败,错误代码为:${safeEngine.ErrorCode}。`,
};
}
res.signed = signed;
return res;
} finally {
safeEngine.SEH_ClearSession();
}
} else {
return {
status: 1,
error: '当前浏览器不支持activeX控件,请切换ie或带ie内核的360之类的浏览器。',
};
}
};
......@@ -9,16 +9,16 @@ import dva from 'dva';
import createLoading from 'dva-loading';
import DsTable from '../src/components/table/dstable';
import TableInput from '../src/components/table/input-table';
import UCA from '../src/components/uca';
import { makeCancelable } from '../src/utils/promise';
import { login } from '../src/services/login';
import { validate } from '../src/services/login/password';
import { switchDomain } from '../src/services/domain';
import { setToken } from '../src/utils/auth';
const loginIt = async (userName, password, domainPath) => {
const type = 'userName';
const authType = 'password';
const result = await login({ type, authType, userName, password });
const result = await login({ type: 'userName', data: userName, authRequest: await validate(password) });
const { tokenId } = result;
await setToken(tokenId);
await switchDomain(domainPath);
......@@ -86,6 +86,9 @@ const RangePicker = DatePicker.RangePicker;
storiesOf('antd', module)
.add('RangePicker', () => {
return <RangePicker />;
})
.add('uca', () => {
return <UCA onChange={evt => console.log(evt)} />;
});
storiesOf('table-input', module)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论