Harmonization with IVIS
This commit is contained in:
parent
428fb9db7b
commit
397f85dac4
41 changed files with 8587 additions and 10940 deletions
2377
client/package-lock.json
generated
2377
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -22,17 +22,15 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coreui/coreui": "^2.1.4",
|
"@coreui/coreui": "^2.1.4",
|
||||||
"@fortawesome/fontawesome-free": "^5.6.3",
|
"@fortawesome/fontawesome-free": "^5.6.3",
|
||||||
"@material-ui/core": "^3.7.1",
|
|
||||||
"@material-ui/icons": "^3.0.1",
|
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"datatables.net": "^1.10.19",
|
"datatables.net": "^1.10.19",
|
||||||
"datatables.net-bs4": "^1.10.19",
|
"datatables.net-bs4": "^1.10.19",
|
||||||
"grapesjs": "^0.14.49",
|
"grapesjs": "^0.14.50",
|
||||||
"grapesjs-mjml": "0.0.27",
|
"grapesjs-mjml": "0.0.27",
|
||||||
"grapesjs-preset-newsletter": "^0.2.20",
|
"grapesjs-preset-newsletter": "^0.2.20",
|
||||||
"i18next": "^13.0.1",
|
"i18next": "^13.1.0",
|
||||||
"i18next-browser-languagedetector": "^2.2.4",
|
"i18next-browser-languagedetector": "^2.2.4",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^4.0.0-rc.12",
|
||||||
"juice": "^5.1.0",
|
"juice": "^5.1.0",
|
||||||
"mjml4-in-browser": "^1.0.1",
|
"mjml4-in-browser": "^1.0.1",
|
||||||
"moment": "^2.23.0",
|
"moment": "^2.23.0",
|
||||||
|
@ -42,6 +40,7 @@
|
||||||
"react": "^16.7.0",
|
"react": "^16.7.0",
|
||||||
"react-ace": "^6.3.2",
|
"react-ace": "^6.3.2",
|
||||||
"react-ckeditor-component": "^1.1.0",
|
"react-ckeditor-component": "^1.1.0",
|
||||||
|
"react-color": "^2.17.0",
|
||||||
"react-day-picker": "^7.2.4",
|
"react-day-picker": "^7.2.4",
|
||||||
"react-dnd": "^7.0.2",
|
"react-dnd": "^7.0.2",
|
||||||
"react-dnd-html5-backend": "^7.0.2",
|
"react-dnd-html5-backend": "^7.0.2",
|
||||||
|
@ -49,33 +48,32 @@
|
||||||
"react-dom": "^16.7.0",
|
"react-dom": "^16.7.0",
|
||||||
"react-dropzone": "^8.0.3",
|
"react-dropzone": "^8.0.3",
|
||||||
"react-google-charts": "^3.0.10",
|
"react-google-charts": "^3.0.10",
|
||||||
"react-i18next": "^8.4.0",
|
"react-i18next": "^9.0.1",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^4.3.1",
|
||||||
"react-sortable-tree": "^2.6.0",
|
"react-sortable-tree": "^2.6.0",
|
||||||
"slugify": "^1.3.4",
|
"slugify": "^1.3.4",
|
||||||
"url-parse": "^1.4.4"
|
"url-parse": "^1.4.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.1.5",
|
"@babel/cli": "^7.2.3",
|
||||||
"@babel/core": "^7.1.6",
|
"@babel/core": "^7.2.2",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
"@babel/plugin-proposal-decorators": "^7.2.3",
|
||||||
"@babel/plugin-proposal-function-bind": "^7.0.0",
|
"@babel/plugin-proposal-function-bind": "^7.2.0",
|
||||||
"@babel/preset-env": "^7.0.0",
|
"@babel/preset-env": "^7.2.3",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"babel-loader": "^8.0.4",
|
"babel-loader": "^8.0.4",
|
||||||
"clean-css-cli": "^4.2.1",
|
"clean-css-cli": "^4.2.1",
|
||||||
"css-loader": "^1.0.1",
|
"css-loader": "^2.1.0",
|
||||||
"file-loader": "^2.0.0",
|
"file-loader": "^3.0.1",
|
||||||
"node-sass": "^4.11.0",
|
"node-sass": "^4.11.0",
|
||||||
"nodemon": "^1.18.9",
|
"nodemon": "^1.18.9",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"raw-loader": "^0.5.1",
|
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"url-loader": "^1.1.2",
|
"url-loader": "^1.1.2",
|
||||||
"webpack": "^4.25.1",
|
"webpack": "^4.28.3",
|
||||||
"webpack-cli": "^3.1.2"
|
"webpack-cli": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import csfrToken from 'csfrToken';
|
import csrfToken from 'csrfToken';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import interoperableErrors from '../../../shared/interoperable-errors';
|
import interoperableErrors from '../../../shared/interoperable-errors';
|
||||||
|
|
||||||
const axiosInst = axios.create({
|
const axiosInst = axios.create({
|
||||||
headers: {'X-CSRF-TOKEN': csfrToken}
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const axiosWrapper = {
|
const axiosWrapper = {
|
||||||
|
|
4
client/src/lib/bootstrap-components.js
vendored
4
client/src/lib/bootstrap-components.js
vendored
|
@ -14,7 +14,6 @@ import {withComponentMixins} from "./decorator-helpers";
|
||||||
withTranslation,
|
withTranslation,
|
||||||
withErrorHandling
|
withErrorHandling
|
||||||
])
|
])
|
||||||
|
|
||||||
class DismissibleAlert extends Component {
|
class DismissibleAlert extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
severity: PropTypes.string.isRequired,
|
severity: PropTypes.string.isRequired,
|
||||||
|
@ -74,6 +73,7 @@ class Button extends Component {
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
iconTitle: PropTypes.string,
|
iconTitle: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
type: PropTypes.string
|
type: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ class Button extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button type={type} className={className} onClick={::this.onClick}>{icon}{iconSpacer}{props.label}</button>
|
<button type={type} className={className} onClick={::this.onClick} title={this.props.title}>{icon}{iconSpacer}{props.label}</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {SectionContentContext} from "./page-common";
|
|
||||||
import {createComponentMixin} from "./decorator-helpers";
|
import {createComponentMixin} from "./decorator-helpers";
|
||||||
|
|
||||||
function handleError(that, error) {
|
function handleError(that, error) {
|
||||||
|
@ -66,3 +65,12 @@ export function withAsyncErrorHandler(target, name, descriptor) {
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function wrapWithAsyncErrorHandler(self, fn) {
|
||||||
|
return async function () {
|
||||||
|
try {
|
||||||
|
await fn.apply(this, arguments)
|
||||||
|
} catch (error) {
|
||||||
|
handleError(self, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Icon
|
Icon
|
||||||
} from "./bootstrap-components";
|
} from "./bootstrap-components";
|
||||||
|
import { SketchPicker } from 'react-color';
|
||||||
|
|
||||||
import ACEEditorRaw
|
import ACEEditorRaw
|
||||||
from 'react-ace';
|
from 'react-ace';
|
||||||
import 'brace/theme/github';
|
import 'brace/theme/github';
|
||||||
|
@ -494,6 +496,66 @@ class TextArea extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@withComponentMixins([
|
||||||
|
withFormStateOwner
|
||||||
|
])
|
||||||
|
class ColorPicker extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
opened: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.setState({
|
||||||
|
opened: !this.state.opened
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
selected(value) {
|
||||||
|
const owner = this.getFormStateOwner();
|
||||||
|
const id = this.props.id;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
opened: false
|
||||||
|
});
|
||||||
|
|
||||||
|
owner.updateFormValue(id, value.rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const props = this.props;
|
||||||
|
const owner = this.getFormStateOwner();
|
||||||
|
const id = this.props.id;
|
||||||
|
const htmlId = 'form_' + id;
|
||||||
|
const t = props.t;
|
||||||
|
const color = owner.getFormValue(id);
|
||||||
|
|
||||||
|
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
||||||
|
<div>
|
||||||
|
<div className="input-group">
|
||||||
|
<div className={styles.colorPickerSwatchWrapper} onClick={::this.toggle}>
|
||||||
|
<div className={styles.colorPickerSwatchColor} style={{background: `rgba(${ color.r }, ${ color.g }, ${ color.b }, ${ color.a })`}}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{this.state.opened &&
|
||||||
|
<div className={styles.colorPickerWrapper}>
|
||||||
|
<SketchPicker color={color} onChange={::this.selected} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@withComponentMixins([
|
@withComponentMixins([
|
||||||
withTranslation,
|
withTranslation,
|
||||||
withFormStateOwner
|
withFormStateOwner
|
||||||
|
@ -897,37 +959,6 @@ class ACEEditor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Excluded. It's not very useful and just eats a lot of space in the resulting JS.
|
|
||||||
|
|
||||||
@withComponentMixins([
|
|
||||||
withFormStateOwner
|
|
||||||
])
|
|
||||||
class CKEditor extends Component {
|
|
||||||
static propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
label: PropTypes.string,
|
|
||||||
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
||||||
height: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const props = this.props;
|
|
||||||
const owner = this.getFormStateOwner();
|
|
||||||
const id = this.props.id;
|
|
||||||
const htmlId = 'form_' + id;
|
|
||||||
|
|
||||||
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
|
||||||
<CKEditorRaw
|
|
||||||
onChange={(event, editor) => owner.updateFormValue(id, editor.getData())}
|
|
||||||
onInit={ editor => {
|
|
||||||
editor.ui.view.editable.editableElement.style.height = props.height;
|
|
||||||
} }
|
|
||||||
data={owner.getFormValue(id)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
const proto = InnerClass.prototype;
|
const proto = InnerClass.prototype;
|
||||||
|
@ -1378,6 +1409,7 @@ export {
|
||||||
CheckBoxGroup,
|
CheckBoxGroup,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
TextArea,
|
TextArea,
|
||||||
|
ColorPicker,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
AlignedRow,
|
AlignedRow,
|
||||||
|
@ -1387,6 +1419,5 @@ export {
|
||||||
TableSelect,
|
TableSelect,
|
||||||
TableSelectMode,
|
TableSelectMode,
|
||||||
ACEEditor,
|
ACEEditor,
|
||||||
CKEditor,
|
|
||||||
FormSendMethod
|
FormSendMethod
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {convertToFake, getLang} from '../../../shared/langs';
|
||||||
import {createComponentMixin} from "./decorator-helpers";
|
import {createComponentMixin} from "./decorator-helpers";
|
||||||
|
|
||||||
import lang_en_US_common from "../../../locales/en-US/common";
|
import lang_en_US_common from "../../../locales/en-US/common";
|
||||||
import {withPageHelpers} from "./page-common";
|
|
||||||
|
|
||||||
const resourcesCommon = {
|
const resourcesCommon = {
|
||||||
'en-US': lang_en_US_common,
|
'en-US': lang_en_US_common,
|
||||||
|
|
|
@ -315,12 +315,12 @@ export class SectionContent extends Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
}
|
};
|
||||||
|
|
||||||
this.historyUnlisten = props.history.listen((location, action) => {
|
this.historyUnlisten = props.history.listen((location, action) => {
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.closeFlashMessage();
|
this.closeFlashMessage();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
.form { // This is here to give the styles below higher priority than Bootstrap has
|
.form { // This is here to give the styles below higher priority than Bootstrap has
|
||||||
:global .DayPicker {
|
:global .DayPicker {
|
||||||
border-left: 1px solid lightgray;
|
border: $input-border-width solid $input-border-color;
|
||||||
border-right: 1px solid lightgray;
|
border-radius: $input-border-radius;
|
||||||
border-bottom: 1px solid lightgray;
|
padding: $input-padding-y $input-padding-x;
|
||||||
border-radius: 4px;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:global .form-horizontal .control-label {
|
:global .form-horizontal .control-label {
|
||||||
|
@ -114,11 +112,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropZone {
|
.colorPickerWrapper {
|
||||||
padding-top: 30px;
|
text-align: right;
|
||||||
padding-bottom: 30px;
|
}
|
||||||
margin-bottom: 15px;
|
|
||||||
margin-top: 8px;
|
.dropZone{
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
margin-top: 3px;
|
||||||
border: 2px solid #E6E9ED;
|
border: 2px solid #E6E9ED;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: #FAFAD2;
|
background-color: #FAFAD2;
|
||||||
|
|
|
@ -79,6 +79,7 @@ export class UntrustedContentHost extends Component {
|
||||||
resolve(msg.data.ret);
|
resolve(msg.data.ret);
|
||||||
} else if (msg.type === 'rpcRequest') {
|
} else if (msg.type === 'rpcRequest') {
|
||||||
const ret = await this.props.onMethodAsync(msg.data.method, msg.data.params);
|
const ret = await this.props.onMethodAsync(msg.data.method, msg.data.params);
|
||||||
|
this.sendMessage('rpcResponse', {msgId: msg.data.msgId, ret});
|
||||||
} else if (msg.type === 'clientHeight') {
|
} else if (msg.type === 'clientHeight') {
|
||||||
const newHeight = msg.data;
|
const newHeight = msg.data;
|
||||||
this.contentNode.height = newHeight;
|
this.contentNode.height = newHeight;
|
||||||
|
@ -137,7 +138,7 @@ export class UntrustedContentHost extends Component {
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.refreshAccessToken();
|
this.refreshAccessToken();
|
||||||
this.scheduleRefreshAccessToken();
|
this.scheduleRefreshAccessToken();
|
||||||
}, 60 * 1000);
|
}, 30 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdate() {
|
handleUpdate() {
|
||||||
|
@ -232,7 +233,7 @@ export class UntrustedContentRoot extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(type, data) {
|
sendMessage(type, data) {
|
||||||
window.parent.postMessage({type, data}, getTrustedUrl());
|
window.parent.postMessage({type, data}, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -334,7 +335,7 @@ class ParentRPC {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(type, data) {
|
sendMessage(type, data) {
|
||||||
window.parent.postMessage({type, data}, getTrustedUrl());
|
window.parent.postMessage({type, data}, '*');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,9 @@ function getTrustedUrl(path) {
|
||||||
return mailtrainConfig.trustedUrlBase + (path || '');
|
return mailtrainConfig.trustedUrlBase + (path || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSandboxUrl(path) {
|
function getSandboxUrl(path, customRestrictedAccessToken) {
|
||||||
return mailtrainConfig.sandboxUrlBase + restrictedAccessToken + '/' + (path || '');
|
const localRestrictedAccessToken = customRestrictedAccessToken || restrictedAccessToken;
|
||||||
|
return mailtrainConfig.sandboxUrlBase + localRestrictedAccessToken + '/' + (path || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPublicUrl(path, opts) {
|
function getPublicUrl(path, opts) {
|
||||||
|
@ -43,7 +44,7 @@ function getBaseDir() {
|
||||||
if (mailtrainConfig.appType === AppType.TRUSTED) {
|
if (mailtrainConfig.appType === AppType.TRUSTED) {
|
||||||
return mailtrainConfig.trustedUrlBaseDir;
|
return mailtrainConfig.trustedUrlBaseDir;
|
||||||
} else if (mailtrainConfig.appType === AppType.SANDBOXED) {
|
} else if (mailtrainConfig.appType === AppType.SANDBOXED) {
|
||||||
return mailtrainConfig.sandboxUrlBaseDir + anonymousRestrictedAccessToken;
|
return mailtrainConfig.sandboxUrlBaseDir + restrictedAccessToken;
|
||||||
} else if (mailtrainConfig.appType === AppType.PUBLIC) {
|
} else if (mailtrainConfig.appType === AppType.PUBLIC) {
|
||||||
return mailtrainConfig.publicUrlBaseDir;
|
return mailtrainConfig.publicUrlBaseDir;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default class List extends Component {
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
}
|
}
|
||||||
|
|
||||||
<Title>{t('segment')}</Title>
|
<Title>{t('Segments')}</Title>
|
||||||
|
|
||||||
<Table ref={node => this.table = node} withHeader dataUrl={`rest/segments-table/${this.props.list.id}`} columns={columns} />
|
<Table ref={node => this.table = node} withHeader dataUrl={`rest/segments-table/${this.props.list.id}`} columns={columns} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -93,7 +93,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
externals: {
|
externals: {
|
||||||
jquery: 'jQuery',
|
jquery: 'jQuery',
|
||||||
csfrToken: 'csfrToken',
|
csrfToken: 'csrfToken',
|
||||||
mailtrainConfig: 'mailtrainConfig'
|
mailtrainConfig: 'mailtrainConfig'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
6433
mvis/client/package-lock.json
generated
6433
mvis/client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,23 +10,22 @@
|
||||||
"author": "Tomas Bures",
|
"author": "Tomas Bures",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "^6.24.1",
|
"@babel/cli": "^7.2.3",
|
||||||
"babel-core": "^6",
|
"@babel/core": "^7.2.2",
|
||||||
"babel-loader": "^7.1.1",
|
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"@babel/plugin-proposal-decorators": "^7.2.3",
|
||||||
"babel-plugin-transform-function-bind": "^6.22.0",
|
"@babel/plugin-proposal-function-bind": "^7.2.0",
|
||||||
"babel-preset-env": "^1.6.0",
|
"@babel/preset-env": "^7.2.3",
|
||||||
"babel-preset-react": "^6.24.1",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"babel-preset-stage-1": "^6.24.1",
|
"babel-loader": "^8.0.4",
|
||||||
"css-loader": "^0.28.5",
|
"css-loader": "^2.1.0",
|
||||||
"file-loader": "^0.11.2",
|
"file-loader": "^3.0.1",
|
||||||
"i18next-conv": "^3.0.3",
|
"node-sass": "^4.11.0",
|
||||||
"imports-loader": "^0.7.1",
|
"sass-loader": "^7.1.0",
|
||||||
"node-sass": "^4.5.3",
|
"style-loader": "^0.23.1",
|
||||||
"sass-loader": "^6.0.6",
|
|
||||||
"style-loader": "^0.18.2",
|
|
||||||
"url-loader": "^1.1.2",
|
"url-loader": "^1.1.2",
|
||||||
"webpack": "^3.5.5"
|
"webpack": "^4.28.3",
|
||||||
|
"webpack-cli": "^3.2.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {},
|
"optionalDependencies": {},
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|
|
@ -3,7 +3,7 @@ const path = require('path');
|
||||||
|
|
||||||
const webpackConf = require('../ivis-core/client/webpack.config');
|
const webpackConf = require('../ivis-core/client/webpack.config');
|
||||||
|
|
||||||
webpackConf.resolve.modules = ['node_modules', '../ivis-core/client/node_modules', './src/bimlib'];
|
webpackConf.resolve.modules = ['node_modules', '../ivis-core/client/node_modules'];
|
||||||
webpackConf.entry = {
|
webpackConf.entry = {
|
||||||
'index-trusted': ['babel-polyfill', './src/root-trusted.js'],
|
'index-trusted': ['babel-polyfill', './src/root-trusted.js'],
|
||||||
'index-sandbox': ['babel-polyfill', '../ivis-core/client/src/root-sandbox.js']
|
'index-sandbox': ['babel-polyfill', '../ivis-core/client/src/root-sandbox.js']
|
||||||
|
|
1466
mvis/server/package-lock.json
generated
1466
mvis/server/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -7,14 +7,14 @@
|
||||||
"author": "Tomas Bures",
|
"author": "Tomas Bures",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=7.1.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"optionalDependencies": {},
|
"optionalDependencies": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.16.2",
|
"axios": "^0.18.0",
|
||||||
"js-yaml": "^3.12.0",
|
"js-yaml": "^3.12.0",
|
||||||
"knex": "^0.13.0",
|
"knex": "^0.16.3",
|
||||||
"moment": "^2.18.1",
|
"moment": "^2.18.1",
|
||||||
"moment-timezone": "^0.5.21",
|
"moment-timezone": "^0.5.21",
|
||||||
"mysql2": "^1.3.5",
|
"mysql2": "^1.3.5",
|
||||||
|
|
14
mvis/test-embed/package-lock.json
generated
14
mvis/test-embed/package-lock.json
generated
|
@ -42,11 +42,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"axios": {
|
"axios": {
|
||||||
"version": "0.16.2",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||||
"integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=",
|
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"follow-redirects": "^1.2.3",
|
"follow-redirects": "^1.3.0",
|
||||||
"is-buffer": "^1.1.5"
|
"is-buffer": "^1.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -259,9 +259,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.5.10",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz",
|
||||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
"integrity": "sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "=3.1.0"
|
"debug": "=3.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
"author": "Tomas Bures",
|
"author": "Tomas Bures",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=7.1.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"optionalDependencies": {},
|
"optionalDependencies": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.16.2",
|
"axios": "^0.18.0",
|
||||||
"express": "^4.15.2",
|
"express": "^4.15.2",
|
||||||
"handlebars": "^4.0.11",
|
"handlebars": "^4.0.11",
|
||||||
"hbs": "^4.0.1"
|
"hbs": "^4.0.1"
|
||||||
|
|
|
@ -234,7 +234,6 @@ function createApp(appType) {
|
||||||
|
|
||||||
useWith404Fallback('/static', express.static(path.join(__dirname, '..', 'client', 'static')));
|
useWith404Fallback('/static', express.static(path.join(__dirname, '..', 'client', 'static')));
|
||||||
useWith404Fallback('/mailtrain', express.static(path.join(__dirname, '..', 'client', 'dist')));
|
useWith404Fallback('/mailtrain', express.static(path.join(__dirname, '..', 'client', 'dist')));
|
||||||
useWith404Fallback('/locales', express.static(path.join(__dirname, '..', 'client', 'locales')));
|
|
||||||
|
|
||||||
useWith404Fallback('/static-npm/fontawesome', express.static(path.join(__dirname, '..', 'client', 'node_modules', '@fortawesome', 'fontawesome-free', 'webfonts')));
|
useWith404Fallback('/static-npm/fontawesome', express.static(path.join(__dirname, '..', 'client', 'node_modules', '@fortawesome', 'fontawesome-free', 'webfonts')));
|
||||||
useWith404Fallback('/static-npm/popper.min.js', express.static(path.join(__dirname, '..', 'client', 'node_modules', 'popper.js', 'dist', 'umd', 'popper.min.js')));
|
useWith404Fallback('/static-npm/popper.min.js', express.static(path.join(__dirname, '..', 'client', 'node_modules', 'popper.js', 'dist', 'umd', 'popper.min.js')));
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
const knex = require('./knex');
|
const knex = require('./knex');
|
||||||
const entitySettings = require('./entity-settings');
|
const entitySettings = require('./entity-settings');
|
||||||
|
const { enforce } = require('./helpers');
|
||||||
|
const shares = require('../models/shares');
|
||||||
|
|
||||||
async function ajaxListTx(tx, params, queryFun, columns, options) {
|
async function ajaxListTx(tx, params, queryFun, columns, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
@ -109,7 +111,8 @@ async function ajaxListTx(tx, params, queryFun, columns, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ajaxListWithPermissionsTx(tx, context, fetchSpecs, params, queryFun, columns, options) {
|
async function ajaxListWithPermissionsTx(tx, context, fetchSpecs, params, queryFun, columns, options) {
|
||||||
// Note that this function is not intended to be used with the synthetic admin context obtained by contextHelpers.getAdminContext()
|
enforce(!context.user.admin, 'ajaxListWithPermissionsTx is not supposed to be called by assumed admin');
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
const permCols = [];
|
const permCols = [];
|
||||||
|
@ -138,13 +141,18 @@ async function ajaxListWithPermissionsTx(tx, context, fetchSpecs, params, queryF
|
||||||
const entityType = entitySettings.getEntityType(fetchSpec.entityTypeId);
|
const entityType = entitySettings.getEntityType(fetchSpec.entityTypeId);
|
||||||
|
|
||||||
if (fetchSpec.requiredOperations) {
|
if (fetchSpec.requiredOperations) {
|
||||||
|
const requiredOperations = shares.filterPermissionsByRestrictedAccessHandler(context, fetchSpec.entityTypeId, null, fetchSpec.requiredOperations, 'ajaxListWithPermissionsTx');
|
||||||
const entityIdColumn = fetchSpec.column ? fetchSpec.column : entityType.entitiesTable + '.id';
|
const entityIdColumn = fetchSpec.column ? fetchSpec.column : entityType.entitiesTable + '.id';
|
||||||
|
|
||||||
query = query.innerJoin(
|
if (requiredOperations.length > 0) {
|
||||||
function () {
|
query = query.innerJoin(
|
||||||
return this.from(entityType.permissionsTable).distinct('entity').where('user', context.user.id).whereIn('operation', fetchSpec.requiredOperations).as(`permitted__${fetchSpec.entityTypeId}`);
|
function () {
|
||||||
},
|
return this.from(entityType.permissionsTable).distinct('entity').where('user', context.user.id).whereIn('operation', requiredOperations).as(`permitted__${fetchSpec.entityTypeId}`);
|
||||||
`permitted__${fetchSpec.entityTypeId}.entity`, entityIdColumn)
|
},
|
||||||
|
`permitted__${fetchSpec.entityTypeId}.entity`, entityIdColumn)
|
||||||
|
} else {
|
||||||
|
query = query.whereRaw('FALSE');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,9 +194,7 @@ async function ajaxListWithPermissions(context, fetchSpecs, params, queryFun, co
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports.ajaxListTx = ajaxListTx;
|
||||||
ajaxListTx,
|
module.exports.ajaxList = ajaxList;
|
||||||
ajaxList,
|
module.exports.ajaxListWithPermissionsTx = ajaxListWithPermissionsTx;
|
||||||
ajaxListWithPermissionsTx,
|
module.exports.ajaxListWithPermissions = ajaxListWithPermissions;
|
||||||
ajaxListWithPermissions
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
const moment = require('moment');
|
const path = require('path');
|
||||||
|
|
||||||
|
const knexConstructor = require('knex');
|
||||||
|
|
||||||
const knex = require('knex')({
|
const knex = require('knex')({
|
||||||
client: 'mysql',
|
client: 'mysql',
|
||||||
|
@ -16,9 +18,9 @@ const knex = require('knex')({
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
migrations: {
|
migrations: {
|
||||||
directory: __dirname + '/../setup/knex/migrations'
|
directory: path.join(__dirname, '..', 'setup', 'knex', 'migrations')
|
||||||
}
|
}
|
||||||
//, debug: true
|
//, debug: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { enforce } = require('./helpers');
|
const { enforce } = require('./helpers');
|
||||||
const shares = require('../models/shares');
|
|
||||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||||
|
const shares = require('../models/shares');
|
||||||
|
|
||||||
async function validateEntity(tx, entity) {
|
async function validateEntity(tx, entity) {
|
||||||
enforce(entity.namespace, 'Entity namespace not set');
|
enforce(entity.namespace, 'Entity namespace not set');
|
||||||
|
|
|
@ -222,7 +222,7 @@ if (LdapStrategy) {
|
||||||
module.exports.isAuthMethodLocal = true;
|
module.exports.isAuthMethodLocal = true;
|
||||||
|
|
||||||
passport.use(new LocalStrategy(nodeifyFunction(async (username, password) => {
|
passport.use(new LocalStrategy(nodeifyFunction(async (username, password) => {
|
||||||
return await users.getByUsernameIfPasswordMatch(username, password);
|
return await users.getByUsernameIfPasswordMatch(contextHelpers.getAdminContext(), username, password);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
passport.serializeUser((user, done) => done(null, user.id));
|
passport.serializeUser((user, done) => done(null, user.id));
|
||||||
|
|
|
@ -13,7 +13,7 @@ const dependencyHelpers = require('../lib/dependency-helpers');
|
||||||
const allowedKeys = new Set(['name', 'description', 'namespace']);
|
const allowedKeys = new Set(['name', 'description', 'namespace']);
|
||||||
|
|
||||||
async function listTree(context) {
|
async function listTree(context) {
|
||||||
// FIXME - process permissions
|
enforce(!context.user.admin, 'listTree is not supposed to be called by assumed admin');
|
||||||
|
|
||||||
const entityType = entitySettings.getEntityType('namespace');
|
const entityType = entitySettings.getEntityType('namespace');
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ async function listTree(context) {
|
||||||
entry.title = row.name;
|
entry.title = row.name;
|
||||||
entry.description = row.description;
|
entry.description = row.description;
|
||||||
entry.permissions = row.permissions ? row.permissions.split(';') : [];
|
entry.permissions = row.permissions ? row.permissions.split(';') : [];
|
||||||
|
entry.permissions = shares.filterPermissionsByRestrictedAccessHandler(context, 'namespace', row.id, entry.permissions, 'namespaces.listTree')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune out the inaccessible namespaces
|
// Prune out the inaccessible namespaces
|
||||||
|
@ -117,19 +118,66 @@ async function getById(context, id) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function create(context, entity) {
|
async function getChildrenTx(tx, context, id) {
|
||||||
|
await shares.enforceEntityPermissionTx(tx, context, 'namespace', id, 'view');
|
||||||
|
|
||||||
|
const entityType = entitySettings.getEntityType('namespace');
|
||||||
|
|
||||||
|
const extraKeys = em.get('models.namespaces.extraKeys', []);
|
||||||
|
|
||||||
|
let children;
|
||||||
|
if (context.user.admin) {
|
||||||
|
children = await knex('namespaces')
|
||||||
|
.where('namespaces.namespace', id)
|
||||||
|
.select([
|
||||||
|
'namespaces.id', 'namespaces.name', 'namespaces.description', 'namespaces.namespace',
|
||||||
|
...extraKeys.map(key => 'namespaces.' + key)
|
||||||
|
]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const rows = await knex('namespaces')
|
||||||
|
.leftJoin(entityType.permissionsTable, {
|
||||||
|
[entityType.permissionsTable + '.entity']: 'namespaces.id',
|
||||||
|
[entityType.permissionsTable + '.user']: context.user.id
|
||||||
|
})
|
||||||
|
.where('namespaces.namespace', id)
|
||||||
|
.groupBy('namespaces.id')
|
||||||
|
.select([
|
||||||
|
'namespaces.id', 'namespaces.name', 'namespaces.description', 'namespaces.namespace',
|
||||||
|
...extraKeys.map(key => 'namespaces.' + key),
|
||||||
|
knex.raw(`GROUP_CONCAT(${entityType.permissionsTable + '.operation'} SEPARATOR \';\') as permissions`)
|
||||||
|
]);
|
||||||
|
|
||||||
|
children = [];
|
||||||
|
for (const row of rows) {
|
||||||
|
row.permissions = row.permissions ? row.permissions.split(';') : [];
|
||||||
|
row.permissions = shares.filterPermissionsByRestrictedAccessHandler(context, 'namespace', row.id, row.permissions, 'namespaces.getChildrenTx');
|
||||||
|
if (row.permissions.includes('view')) {
|
||||||
|
children.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTx(tx, context, entity) {
|
||||||
enforce(entity.namespace, 'Parent namespace must be set');
|
enforce(entity.namespace, 'Parent namespace must be set');
|
||||||
|
|
||||||
|
await shares.enforceEntityPermissionTx(tx, context, 'namespace', entity.namespace, 'createNamespace');
|
||||||
|
|
||||||
|
const ids = await tx('namespaces').insert(filterObject(entity, allowedKeys));
|
||||||
|
const id = ids[0];
|
||||||
|
|
||||||
|
// We don't have to rebuild all entity types, because no entity can be a child of the namespace at this moment.
|
||||||
|
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'namespace', entityId: id });
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create(context, entity) {
|
||||||
return await knex.transaction(async tx => {
|
return await knex.transaction(async tx => {
|
||||||
await shares.enforceEntityPermissionTx(tx, context, 'namespace', entity.namespace, 'createNamespace');
|
return await createTx(tx, context, entity);
|
||||||
|
|
||||||
const ids = await tx('namespaces').insert(filterObject(entity, allowedKeys));
|
|
||||||
const id = ids[0];
|
|
||||||
|
|
||||||
// We don't have to rebuild all entity types, because no entity can be a child of the namespace at this moment.
|
|
||||||
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'namespace', entityId: id });
|
|
||||||
|
|
||||||
return id;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +235,8 @@ async function remove(context, id) {
|
||||||
module.exports.hash = hash;
|
module.exports.hash = hash;
|
||||||
module.exports.listTree = listTree;
|
module.exports.listTree = listTree;
|
||||||
module.exports.getById = getById;
|
module.exports.getById = getById;
|
||||||
|
module.exports.getChildrenTx = getChildrenTx;
|
||||||
module.exports.create = create;
|
module.exports.create = create;
|
||||||
|
module.exports.createTx = createTx;
|
||||||
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
|
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
|
||||||
module.exports.remove = remove;
|
module.exports.remove = remove;
|
||||||
|
|
|
@ -332,8 +332,6 @@ async function rebuildPermissionsTx(tx, restriction) {
|
||||||
}
|
}
|
||||||
const entities = await entitiesQuery;
|
const entities = await entitiesQuery;
|
||||||
|
|
||||||
// TODO - process restriction.parentId
|
|
||||||
|
|
||||||
const parentEntities = new Map();
|
const parentEntities = new Map();
|
||||||
let nonChildEntities;
|
let nonChildEntities;
|
||||||
if (entityType.dependentPermissions) {
|
if (entityType.dependentPermissions) {
|
||||||
|
@ -541,30 +539,7 @@ async function _checkPermissionTx(tx, context, entityTypeId, entityId, requiredO
|
||||||
requiredOperations = [ requiredOperations ];
|
requiredOperations = [ requiredOperations ];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.user.restrictedAccessHandler) {
|
requiredOperations = filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, requiredOperations, 'checkPermissions');
|
||||||
const originalRequiredOperations = requiredOperations;
|
|
||||||
if (context.user.restrictedAccessHandler.permissions) {
|
|
||||||
const entityPerms = context.user.restrictedAccessHandler.permissions[entityTypeId];
|
|
||||||
|
|
||||||
if (!entityPerms) {
|
|
||||||
requiredOperations = [];
|
|
||||||
} else if (entityPerms === true) {
|
|
||||||
// no change to require operations
|
|
||||||
} else if (entityPerms instanceof Set) {
|
|
||||||
requiredOperations = requiredOperations.filter(perm => entityPerms.has(perm));
|
|
||||||
} else {
|
|
||||||
const allowedPerms = entityPerms[entityId];
|
|
||||||
if (allowedPerms) {
|
|
||||||
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
|
||||||
} else {
|
|
||||||
requiredOperations = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
requiredOperations = [];
|
|
||||||
}
|
|
||||||
log.verbose('check permissions with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' requiredOperations: [' + originalRequiredOperations + '] -> [' + requiredOperations + ']');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requiredOperations.length === 0) {
|
if (requiredOperations.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -686,9 +661,59 @@ async function getPermissionsTx(tx, context, entityTypeId, entityId) {
|
||||||
.where('entity', entityId)
|
.where('entity', entityId)
|
||||||
.where('user', context.user.id);
|
.where('user', context.user.id);
|
||||||
|
|
||||||
return rows.map(x => x.operation);
|
const operations = rows.map(x => x.operation);
|
||||||
|
return filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, operations, 'getPermissions');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If entityId is null, it means that we require that restrictedAccessHandler does not differentiate based on entityId. This is used in ajaxListWithPermissionsTx.
|
||||||
|
function filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, permissions, operationMsg) {
|
||||||
|
if (context.user.restrictedAccessHandler) {
|
||||||
|
const originalOperations = permissions;
|
||||||
|
if (context.user.restrictedAccessHandler.permissions) {
|
||||||
|
const entityPerms = context.user.restrictedAccessHandler.permissions[entityTypeId];
|
||||||
|
|
||||||
|
if (!entityPerms) {
|
||||||
|
permissions = [];
|
||||||
|
} else if (entityPerms === true) {
|
||||||
|
// no change to operations
|
||||||
|
} else if (entityPerms instanceof Set) {
|
||||||
|
permissions = permissions.filter(perm => entityPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
if (entityId) {
|
||||||
|
const allowedPerms = entityPerms[entityId];
|
||||||
|
if (allowedPerms) {
|
||||||
|
permissions = permissions.filter(perm => allowedPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
const allowedPerms = entityPerms['default'];
|
||||||
|
if (allowedPerms) {
|
||||||
|
permissions = permissions.filter(perm => allowedPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
permissions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const allowedPerms = entityPerms['default'];
|
||||||
|
if (allowedPerms) {
|
||||||
|
permissions = permissions.filter(perm => allowedPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
permissions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
permissions = [];
|
||||||
|
}
|
||||||
|
log.verbose(operationMsg + ' with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' operations: [' + originalOperations + '] -> [' + permissions + ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAccessibleByRestrictedAccessHandler(context, entityTypeId, entityId, permissions, operationMsg) {
|
||||||
|
return filterPermissionsByRestrictedAccessHandler(context, entityTypeId, entityId, permissions, operationMsg).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports.listByEntityDTAjax = listByEntityDTAjax;
|
module.exports.listByEntityDTAjax = listByEntityDTAjax;
|
||||||
module.exports.listByUserDTAjax = listByUserDTAjax;
|
module.exports.listByUserDTAjax = listByUserDTAjax;
|
||||||
module.exports.listUnassignedUsersDTAjax = listUnassignedUsersDTAjax;
|
module.exports.listUnassignedUsersDTAjax = listUnassignedUsersDTAjax;
|
||||||
|
@ -710,3 +735,5 @@ module.exports.throwPermissionDenied = throwPermissionDenied;
|
||||||
module.exports.regenerateRoleNamesTable = regenerateRoleNamesTable;
|
module.exports.regenerateRoleNamesTable = regenerateRoleNamesTable;
|
||||||
module.exports.getGlobalPermissions = getGlobalPermissions;
|
module.exports.getGlobalPermissions = getGlobalPermissions;
|
||||||
module.exports.getPermissionsTx = getPermissionsTx;
|
module.exports.getPermissionsTx = getPermissionsTx;
|
||||||
|
module.exports.filterPermissionsByRestrictedAccessHandler = filterPermissionsByRestrictedAccessHandler;
|
||||||
|
module.exports.isAccessibleByRestrictedAccessHandler = isAccessibleByRestrictedAccessHandler;
|
|
@ -36,20 +36,28 @@ function hash(entity) {
|
||||||
return hasher.hash(filterObject(entity, hashKeys));
|
return hasher.hash(filterObject(entity, hashKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _getBy(context, key, value, extraColumns = []) {
|
async function _getByTx(tx, context, key, value, extraColumns = []) {
|
||||||
const columns = ['id', 'username', 'name', 'email', 'namespace', 'role', ...extraColumns];
|
const columns = ['id', 'username', 'name', 'email', 'namespace', 'role', ...extraColumns];
|
||||||
|
|
||||||
const user = await knex('users').select(columns).where(key, value).first();
|
const user = await tx('users').select(columns).where(key, value).first();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
shares.throwPermissionDenied();
|
shares.throwPermissionDenied();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that getRestrictedAccessToken relies to this check to see whether a user may impersonate another. If "manageUsers" here were to be changed to something like "viewUsers", then
|
||||||
|
// a corresponding check has to be added to getRestrictedAccessToken
|
||||||
await shares.enforceEntityPermission(context, 'namespace', user.namespace, 'manageUsers');
|
await shares.enforceEntityPermission(context, 'namespace', user.namespace, 'manageUsers');
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function _getBy(context, key, value, extraColumns = []) {
|
||||||
|
return await knex.transaction(async tx => {
|
||||||
|
return await _getByTx(tx, context, key, value, extraColumns);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function getById(context, id) {
|
async function getById(context, id) {
|
||||||
return await _getBy(context, 'id', id);
|
return await _getBy(context, 'id', id);
|
||||||
}
|
}
|
||||||
|
@ -131,7 +139,7 @@ async function _validateAndPreprocess(tx, entity, isCreate, isOwnAccount) {
|
||||||
|
|
||||||
if (!isOwnAccount) {
|
if (!isOwnAccount) {
|
||||||
const otherUserWithSameUsernameQuery = tx('users').where('username', entity.username);
|
const otherUserWithSameUsernameQuery = tx('users').where('username', entity.username);
|
||||||
if (entity.id) {
|
if (!isCreate) {
|
||||||
otherUserWithSameUsernameQuery.andWhereNot('id', entity.id);
|
otherUserWithSameUsernameQuery.andWhereNot('id', entity.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,9 +262,9 @@ async function getByUsername(username) {
|
||||||
return await _getBy(contextHelpers.getAdminContext(), 'username', username);
|
return await _getBy(contextHelpers.getAdminContext(), 'username', username);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getByUsernameIfPasswordMatch(username, password) {
|
async function getByUsernameIfPasswordMatch(context, username, password) {
|
||||||
try {
|
try {
|
||||||
const user = await _getBy(contextHelpers.getAdminContext(), 'username', username, ['password']);
|
const user = await _getBy('username', username, ['password']);
|
||||||
|
|
||||||
if (!await bcryptCompare(password, user.password)) {
|
if (!await bcryptCompare(password, user.password)) {
|
||||||
throw new interoperableErrors.IncorrectPasswordError();
|
throw new interoperableErrors.IncorrectPasswordError();
|
||||||
|
@ -405,8 +413,10 @@ async function getByRestrictedAccessToken(token) {
|
||||||
|
|
||||||
if (tokenEntry) {
|
if (tokenEntry) {
|
||||||
const user = await getById(contextHelpers.getAdminContext(), tokenEntry.userId);
|
const user = await getById(contextHelpers.getAdminContext(), tokenEntry.userId);
|
||||||
|
user.restrictedAccessMethod = tokenEntry.method;
|
||||||
user.restrictedAccessHandler = tokenEntry.handler;
|
user.restrictedAccessHandler = tokenEntry.handler;
|
||||||
user.restrictedAccessToken = tokenEntry.token;
|
user.restrictedAccessToken = tokenEntry.token;
|
||||||
|
user.restrictedAccessParams = tokenEntry.params;
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
|
||||||
|
|
5179
server/package-lock.json
generated
5179
server/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -35,28 +35,28 @@
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"phantomjs-prebuilt": "^2.1.16",
|
"phantomjs-prebuilt": "^2.1.16",
|
||||||
"selenium-webdriver": "^3.5.0",
|
"selenium-webdriver": "^3.6.0",
|
||||||
"url-pattern": "^1.0.3"
|
"url-pattern": "^1.0.3"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"posix": "^4.1.2"
|
"posix": "^4.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aws-sdk": "^2.358.0",
|
"aws-sdk": "^2.383.0",
|
||||||
"bcrypt-nodejs": "0.0.3",
|
"bcrypt-nodejs": "0.0.3",
|
||||||
"bluebird": "^3.5.3",
|
"bluebird": "^3.5.3",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"bounce-handler": "7.3.2-fork.3",
|
"bounce-handler": "7.3.2-fork.3",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"config": "^2.0.1",
|
"config": "^3.0.1",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"connect-redis": "^3.4.0",
|
"connect-redis": "^3.4.0",
|
||||||
"cookie-parser": "^1.4.3",
|
"cookie-parser": "^1.4.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"csurf": "^1.9.0",
|
"csurf": "^1.9.0",
|
||||||
"csv-parse": "^3.2.0",
|
"csv-parse": "^4.3.0",
|
||||||
"csv-stringify": "^4.3.1",
|
"csv-stringify": "^5.1.2",
|
||||||
"device": "^0.3.9",
|
"device": "^0.3.9",
|
||||||
"dompurify": "^1.0.8",
|
"dompurify": "^1.0.8",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
|
@ -75,16 +75,16 @@
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"html-to-text": "^4.0.0",
|
"html-to-text": "^4.0.0",
|
||||||
"humanize": "0.0.9",
|
"humanize": "0.0.9",
|
||||||
"i18next": "^12.0.0",
|
"i18next": "^13.1.0",
|
||||||
"isemail": "^3.2.0",
|
"isemail": "^3.2.0",
|
||||||
"jsdom": "^13.0.0",
|
"jsdom": "^13.1.0",
|
||||||
"juice": "^5.0.1",
|
"juice": "^5.1.0",
|
||||||
"knex": "^0.15.2",
|
"knex": "^0.16.3",
|
||||||
"libmime": "^4.0.1",
|
"libmime": "^4.0.1",
|
||||||
"mailparser": "^2.4.3",
|
"mailparser": "^2.4.3",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"mjml": "^4.2.0",
|
"mjml": "^4.3.0",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.23.0",
|
||||||
"moment-timezone": "^0.5.23",
|
"moment-timezone": "^0.5.23",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"multer": "^1.4.1",
|
"multer": "^1.4.1",
|
||||||
|
@ -93,12 +93,10 @@
|
||||||
"node-mocks-http": "^1.7.3",
|
"node-mocks-http": "^1.7.3",
|
||||||
"node-object-hash": "^1.4.1",
|
"node-object-hash": "^1.4.1",
|
||||||
"nodeify": "^1.0.1",
|
"nodeify": "^1.0.1",
|
||||||
"nodemailer": "^4.6.8",
|
"nodemailer": "^5.0.0",
|
||||||
"nodemailer-openpgp": "^1.2.0",
|
"nodemailer-openpgp": "^1.2.0",
|
||||||
"npm": "^6.4.1",
|
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"nyc": "^13.1.0",
|
"openpgp": "^4.4.3",
|
||||||
"openpgp": "^4.2.1",
|
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"premailer-api": "^1.0.4",
|
"premailer-api": "^1.0.4",
|
||||||
|
@ -106,7 +104,7 @@
|
||||||
"request-promise": "^4.2.2",
|
"request-promise": "^4.2.2",
|
||||||
"serve-favicon": "^2.5.0",
|
"serve-favicon": "^2.5.0",
|
||||||
"shortid": "^2.2.14",
|
"shortid": "^2.2.14",
|
||||||
"slugify": "^1.3.3",
|
"slugify": "^1.3.4",
|
||||||
"smtp-server": "^3.4.7",
|
"smtp-server": "^3.4.7",
|
||||||
"toml": "^2.3.3",
|
"toml": "^2.3.3",
|
||||||
"try-require": "^1.2.1",
|
"try-require": "^1.2.1",
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('config');
|
|
||||||
const passport = require('../../lib/passport');
|
const passport = require('../../lib/passport');
|
||||||
const users = require('../../models/users');
|
const users = require('../../models/users');
|
||||||
const shares = require('../../models/shares');
|
|
||||||
|
|
||||||
const router = require('../../lib/router-async').create();
|
const router = require('../../lib/router-async').create();
|
||||||
const {castToInteger} = require('../../lib/helpers');
|
const {castToInteger} = require('../../lib/helpers');
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
client: 'mysql2',
|
client: 'mysql',
|
||||||
connection: config.mysql
|
connection: config.mysql
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
{{#if mailtrainConfig}}
|
{{#if mailtrainConfig}}
|
||||||
<script>
|
<script>
|
||||||
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
{{#if reactCsrfToken}}window.csrfToken = '{{reactCsrfToken}}';{{/if}}
|
||||||
window.mailtrainConfig = {{{mailtrainConfig}}};
|
window.mailtrainConfig = {{{mailtrainConfig}}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
{{#if mailtrainConfig}}
|
{{#if mailtrainConfig}}
|
||||||
<script>
|
<script>
|
||||||
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
{{#if reactCsrfToken}}window.csrfToken = '{{reactCsrfToken}}';{{/if}}
|
||||||
window.mailtrainConfig = {{{mailtrainConfig}}};
|
window.mailtrainConfig = {{{mailtrainConfig}}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
{{#if mailtrainConfig}}
|
{{#if mailtrainConfig}}
|
||||||
<script>
|
<script>
|
||||||
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
{{#if reactCsrfToken}}window.csrfToken = '{{reactCsrfToken}}';{{/if}}
|
||||||
window.mailtrainConfig = {{{mailtrainConfig}}};
|
window.mailtrainConfig = {{{mailtrainConfig}}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
{{#if mailtrainConfig}}
|
{{#if mailtrainConfig}}
|
||||||
<script>
|
<script>
|
||||||
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
{{#if reactCsrfToken}}window.csrfToken = '{{reactCsrfToken}}';{{/if}}
|
||||||
window.mailtrainConfig = {{{mailtrainConfig}}};
|
window.mailtrainConfig = {{{mailtrainConfig}}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.csfrToken = '{{reactCsrfToken}}';
|
window.csrfToken = '{{reactCsrfToken}}';
|
||||||
window.mailtrainConfig = {{{mailtrainConfig}}};
|
window.mailtrainConfig = {{{mailtrainConfig}}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -205,12 +205,9 @@ mysql:
|
||||||
user: mailtrain_ro
|
user: mailtrain_ro
|
||||||
password: "$mysqlRoPassword"
|
password: "$mysqlRoPassword"
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
# Install required node packages
|
reinstallModules
|
||||||
for idx in client shared server zone-mta; do
|
|
||||||
(cd $idx && npm install)
|
|
||||||
done
|
|
||||||
|
|
||||||
(cd client && npm run build)
|
(cd client && npm run build)
|
||||||
|
|
||||||
chown -R mailtrain:mailtrain .
|
chown -R mailtrain:mailtrain .
|
||||||
|
@ -218,6 +215,15 @@ EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function reinstallModules {
|
||||||
|
# Install required node packages
|
||||||
|
for idx in client shared server zone-mta mvis/client mvis/server mvis/test-embed mvis/ivis-core/client mvis/ivis-core/server mvis/ivis-core/shared mvis/ivis-core/embedding; do
|
||||||
|
echo Reinstalling modules in $idx
|
||||||
|
(cd $idx && rm -rf node_modules && npm install)
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function installHttpd {
|
function installHttpd {
|
||||||
local portTrusted="$1"
|
local portTrusted="$1"
|
||||||
local portSandbox="$2"
|
local portSandbox="$2"
|
||||||
|
|
9
setup/reinstall-modules.sh
Normal file
9
setup/reinstall-modules.sh
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_PATH=$(dirname $(realpath -s $0))
|
||||||
|
. $SCRIPT_PATH/functions
|
||||||
|
cd $SCRIPT_PATH/..
|
||||||
|
|
||||||
|
reinstallModules
|
3509
shared/package-lock.json
generated
3509
shared/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -12,30 +12,27 @@
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.23.0",
|
||||||
"moment-timezone": "^0.5.23",
|
"moment-timezone": "^0.5.23",
|
||||||
"owasp-password-strength-test": "github:bures/owasp-password-strength-test"
|
"owasp-password-strength-test": "github:bures/owasp-password-strength-test"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.1.5",
|
"@babel/cli": "^7.2.3",
|
||||||
"@babel/core": "^7.1.6",
|
"@babel/core": "^7.2.2",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
"@babel/plugin-proposal-decorators": "^7.2.3",
|
||||||
"@babel/plugin-proposal-function-bind": "^7.0.0",
|
"@babel/plugin-proposal-function-bind": "^7.2.0",
|
||||||
"@babel/preset-env": "^7.0.0",
|
"@babel/preset-env": "^7.2.3",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@ckeditor/ckeditor5-dev-utils": "^11.0.1",
|
|
||||||
"@ckeditor/ckeditor5-dev-webpack-plugin": "^7.0.1",
|
|
||||||
"babel-loader": "^8.0.4",
|
"babel-loader": "^8.0.4",
|
||||||
"css-loader": "^1.0.1",
|
"css-loader": "^2.1.0",
|
||||||
"file-loader": "^2.0.0",
|
"file-loader": "^3.0.1",
|
||||||
"node-sass": "^4.10.0",
|
"node-sass": "^4.11.0",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"raw-loader": "^0.5.1",
|
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"url-loader": "^1.1.2",
|
"url-loader": "^1.1.2",
|
||||||
"webpack": "^4.25.1",
|
"webpack": "^4.28.3",
|
||||||
"webpack-cli": "^3.1.2"
|
"webpack-cli": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue