forked from p34709852/monkey
Formatting + Removed export events button from main page
This commit is contained in:
parent
8136c31476
commit
85401e5d48
|
@ -100,7 +100,8 @@ TESTS_MAP = {
|
|||
TEST_SCHEDULED_EXECUTION: {
|
||||
TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.",
|
||||
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||
STATUS_INCONCLUSIVE: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software."
|
||||
STATUS_INCONCLUSIVE: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.",
|
||||
STATUS_PASSED: "Monkey failed to execute in a scheduled manner."
|
||||
},
|
||||
RECOMMENDATION_KEY: RECOMMENDATION_USER_BEHAVIOUR,
|
||||
PILLARS_KEY: [PEOPLE, NETWORKS],
|
||||
|
|
|
@ -77,7 +77,9 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
|||
return (
|
||||
<Fragment>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<PrintReportButton onClick={() => {print();}} />
|
||||
<PrintReportButton onClick={() => {
|
||||
print();
|
||||
}}/>
|
||||
</div>
|
||||
<div className="report-page">
|
||||
<ReportHeader report_type={ReportTypes.zeroTrust}/>
|
||||
|
@ -85,43 +87,14 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
|||
{content}
|
||||
</div>
|
||||
<div style={{marginTop: '20px'}}>
|
||||
<PrintReportButton onClick={() => {print();}} />
|
||||
<PrintReportButton onClick={() => {
|
||||
print();
|
||||
}}/>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
generateFindingsSection() {
|
||||
return (<div id="findings-overview">
|
||||
<h2>Findings</h2>
|
||||
<p>
|
||||
Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things
|
||||
happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper
|
||||
insight as to what exactly happened during this test.
|
||||
</p>
|
||||
<FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
|
||||
</div>);
|
||||
}
|
||||
|
||||
generateRecommendationsSection() {
|
||||
return (<div id="recommendations-overview">
|
||||
<h2>Recommendations</h2>
|
||||
<p>
|
||||
Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results
|
||||
to understand how the monkey tested your adherence to that recommendation.
|
||||
</p>
|
||||
{
|
||||
Object.keys(this.state.recommendations).map((pillar) =>
|
||||
<SinglePillarRecommendationsStatus
|
||||
key={pillar}
|
||||
pillar={pillar}
|
||||
recommendationsStatus={this.state.recommendations[pillar]}
|
||||
pillarsToStatuses={this.state.pillars.pillarsToStatuses}/>
|
||||
)
|
||||
}
|
||||
</div>);
|
||||
}
|
||||
|
||||
generateOverviewSection() {
|
||||
return (<div id="overview-section">
|
||||
<h2>Summary</h2>
|
||||
|
@ -145,13 +118,44 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
|||
</Row>
|
||||
<Row>
|
||||
<Col xs={12} sm={12} md={12} lg={12}>
|
||||
<ZeroTrustReportLegend />
|
||||
<ZeroTrustReportLegend/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Grid>
|
||||
</div>);
|
||||
}
|
||||
|
||||
generateRecommendationsSection() {
|
||||
return (<div id="recommendations-overview">
|
||||
<h2>Recommendations</h2>
|
||||
<p>
|
||||
Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results
|
||||
to understand how the monkey tested your adherence to that recommendation.
|
||||
</p>
|
||||
{
|
||||
Object.keys(this.state.recommendations).map((pillar) =>
|
||||
<SinglePillarRecommendationsStatus
|
||||
key={pillar}
|
||||
pillar={pillar}
|
||||
recommendationsStatus={this.state.recommendations[pillar]}
|
||||
pillarsToStatuses={this.state.pillars.pillarsToStatuses}/>
|
||||
)
|
||||
}
|
||||
</div>);
|
||||
}
|
||||
|
||||
generateFindingsSection() {
|
||||
return (<div id="findings-overview">
|
||||
<h2>Findings</h2>
|
||||
<p>
|
||||
Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things
|
||||
happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper
|
||||
insight as to what exactly happened during this test.
|
||||
</p>
|
||||
<FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
|
||||
</div>);
|
||||
}
|
||||
|
||||
stillLoadingDataFromServer() {
|
||||
return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined";
|
||||
}
|
||||
|
|
|
@ -26,14 +26,9 @@ export default class EventsButtonsComponent extends Component {
|
|||
<div>
|
||||
<EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide} exportFilename={this.props.exportFilename} />
|
||||
<div className="text-center" style={{"display": "grid"}}>
|
||||
<Button className="btn btn-info btn-lg" onClick={this.show}>
|
||||
<Button className="btn btn-primary btn-lg" onClick={this.show}>
|
||||
Show Events
|
||||
</Button>
|
||||
<ExportEventsButton onClick={() => {
|
||||
const content = JSON.stringify(this.props.events, null, 2);
|
||||
const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
||||
FileSaver.saveAs(blob, this.props.exportFilename + ".json");
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types';
|
||||
import { Popover, OverlayTrigger } from 'react-bootstrap';
|
||||
import {Popover, OverlayTrigger} from 'react-bootstrap';
|
||||
import * as d3 from 'd3'
|
||||
|
||||
class ArcNode extends React.Component {
|
||||
|
||||
handleClick(e_) { this.props.disableHover(this.refs.overlay); }
|
||||
handleOver(e_) { if(this.props.hover) { this.refs.overlay.show(); } }
|
||||
handleOut(e_) { if(this.props.hover){ this.refs.overlay.hide(); } }
|
||||
|
||||
render() {
|
||||
let {prefix, index, data} = this.props;
|
||||
|
||||
|
@ -16,31 +11,51 @@ class ArcNode extends React.Component {
|
|||
let id = prefix + 'Node_' + index;
|
||||
|
||||
return (
|
||||
<g transform={'rotate(180)'} id={data.node.pillar} key={prefix + 'arcGroup' + index}>
|
||||
<OverlayTrigger ref={'overlay'} key={prefix + 'arcOverlayTrigger' + index} trigger={null} placement={data.popover} overlay={<Popover id={prefix + 'ArcPopover' + index} style={{backgroundColor: data.hex}} title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
|
||||
<path
|
||||
<g transform={'rotate(180)'} id={data.node.pillar} key={prefix + 'arcGroup' + index}>
|
||||
<OverlayTrigger ref={'overlay'} key={prefix + 'arcOverlayTrigger' + index} trigger={null}
|
||||
placement={data.popover}
|
||||
overlay={<Popover id={prefix + 'ArcPopover' + index} style={{backgroundColor: data.hex}}
|
||||
title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
|
||||
<path
|
||||
|
||||
id={prefix + 'Node_' + index}
|
||||
className={'arcNode'}
|
||||
data-tooltip={data.tooltip}
|
||||
d={arc()}
|
||||
fill={data.hex}
|
||||
onClick={this.handleClick.bind(this)}
|
||||
onMouseEnter={this.handleOver.bind(this)}
|
||||
onMouseLeave={this.handleOut.bind(this)}
|
||||
id={prefix + 'Node_' + index}
|
||||
className={'arcNode'}
|
||||
data-tooltip={data.tooltip}
|
||||
d={arc()}
|
||||
fill={data.hex}
|
||||
onClick={this.handleClick.bind(this)}
|
||||
onMouseEnter={this.handleOver.bind(this)}
|
||||
onMouseLeave={this.handleOut.bind(this)}
|
||||
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
<text x={0} dy={data.fontStyle.size * 1.2} fontSize={data.fontStyle.size} fill={'white'} textAnchor='middle'
|
||||
pointerEvents={'none'}>
|
||||
<textPath href={'#' + id} startOffset={'26.4%'}>
|
||||
<tspan fontFamily={'FontAwesome'}>{data.icon + '\u2000'}</tspan>
|
||||
<tspan>{data.label}</tspan>
|
||||
</textPath>
|
||||
</text>
|
||||
</g>
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
<text x={0} dy={data.fontStyle.size * 1.2} fontSize={data.fontStyle.size} fill={'white'} textAnchor='middle'
|
||||
pointerEvents={'none'}>
|
||||
<textPath href={'#' + id} startOffset={'26.4%'}>
|
||||
<tspan fontFamily={'FontAwesome'}>{data.icon + '\u2000'}</tspan>
|
||||
<tspan>{data.label}</tspan>
|
||||
</textPath>
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
handleClick(e_) {
|
||||
this.props.disableHover(this.refs.overlay);
|
||||
}
|
||||
|
||||
handleOver(e_) {
|
||||
if (this.props.hover) {
|
||||
this.refs.overlay.show();
|
||||
}
|
||||
}
|
||||
|
||||
handleOut(e_) {
|
||||
if (this.props.hover) {
|
||||
this.refs.overlay.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArcNode.propTypes = {
|
||||
|
|
|
@ -1,40 +1,59 @@
|
|||
import React from 'react'
|
||||
import PillarLabel from "../PillarLabel";
|
||||
import { Popover, OverlayTrigger } from 'react-bootstrap';
|
||||
import {Popover, OverlayTrigger} from 'react-bootstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class CircularNode extends React.Component {
|
||||
|
||||
handleClick(e_) { this.props.disableHover(this.refs.overlay); }
|
||||
handleOver(e_) { if(this.props.hover) { this.refs.overlay.show(); } }
|
||||
handleOut(e_) { if(this.props.hover){ this.refs.overlay.hide(); } }
|
||||
|
||||
render() {
|
||||
let {prefix, index, data} = this.props;
|
||||
|
||||
let translate = 'translate(' + data.cx + ',' + data.cy + ')';
|
||||
return (
|
||||
<g transform={translate} id={data.node.pillar} key={prefix + 'circularGroup' + index}>
|
||||
<OverlayTrigger ref={'overlay'} key={prefix + 'CircularOverlay' + index} trigger={null} placement={data.popover} overlay={<Popover id={prefix + 'CircularClickPopover' + index} style={{backgroundColor : data.hex}} title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
|
||||
<circle
|
||||
id={prefix + 'Node_' + index}
|
||||
className={'circularNode'}
|
||||
data-tooltip={data.tooltip}
|
||||
r={data.r}
|
||||
opacity={0.8}
|
||||
fill={data.hex}
|
||||
onClick={this.handleClick.bind(this)}
|
||||
onMouseEnter={this.handleOver.bind(this)}
|
||||
onMouseLeave={this.handleOut.bind(this)}
|
||||
<g transform={translate} id={data.node.pillar} key={prefix + 'circularGroup' + index}>
|
||||
<OverlayTrigger ref={'overlay'} key={prefix + 'CircularOverlay' + index} trigger={null} placement={data.popover}
|
||||
overlay={<Popover id={prefix + 'CircularClickPopover' + index}
|
||||
style={{backgroundColor: data.hex}}
|
||||
title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
|
||||
<circle
|
||||
id={prefix + 'Node_' + index}
|
||||
className={'circularNode'}
|
||||
data-tooltip={data.tooltip}
|
||||
r={data.r}
|
||||
opacity={0.8}
|
||||
fill={data.hex}
|
||||
onClick={this.handleClick.bind(this)}
|
||||
onMouseEnter={this.handleOver.bind(this)}
|
||||
onMouseLeave={this.handleOut.bind(this)}
|
||||
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
<foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}} key={prefix + 'PillarLabelOject' + index} x={data.offset.x - data.fontStyle.size * 6} y={data.offset.y - data.fontStyle.size} width={ data.fontStyle.size * 12} height={data.fontStyle.size * 6}>
|
||||
<PillarLabel key={prefix + 'PillarLabel' + index} pillar={data.node.pillar} status={data.status} />
|
||||
</foreignObject>
|
||||
</g>
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
<foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}}
|
||||
key={prefix + 'PillarLabelOject' + index} x={data.offset.x - data.fontStyle.size * 6}
|
||||
y={data.offset.y - data.fontStyle.size} width={data.fontStyle.size * 12}
|
||||
height={data.fontStyle.size * 6}>
|
||||
<PillarLabel key={prefix + 'PillarLabel' + index} pillar={data.node.pillar} status={data.status}/>
|
||||
</foreignObject>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
handleClick(e_) {
|
||||
this.props.disableHover(this.refs.overlay);
|
||||
}
|
||||
|
||||
handleOver(e_) {
|
||||
if (this.props.hover) {
|
||||
this.refs.overlay.show();
|
||||
}
|
||||
}
|
||||
|
||||
handleOut(e_) {
|
||||
if (this.props.hover) {
|
||||
this.refs.overlay.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CircularNode.propTypes = {
|
||||
|
|
|
@ -12,11 +12,14 @@ class VennDiagram extends React.Component {
|
|||
|
||||
this.state = {hover: true, currentPopover: undefined};
|
||||
this._disableHover = this._disableHover.bind(this);
|
||||
|
||||
|
||||
this.width = this.height = 512;
|
||||
|
||||
this.prefix = 'vennDiagram';
|
||||
this.fontStyles = [{size: Math.max(9, this.width / 28), color: 'white'}, { size: Math.max(6, this.width / 38), color: 'white'}, { size: Math.max(6, this.width / 48), color: 'white'} ];
|
||||
this.fontStyles = [{size: Math.max(9, this.width / 28), color: 'white'}, {
|
||||
size: Math.max(6, this.width / 38),
|
||||
color: 'white'
|
||||
}, {size: Math.max(6, this.width / 48), color: 'white'}];
|
||||
this.offset = this.width / 16;
|
||||
|
||||
this.thirdWidth = this.width / 3;
|
||||
|
@ -25,14 +28,43 @@ class VennDiagram extends React.Component {
|
|||
this.width1By11 = this.width / 11;
|
||||
this.width1By28 = this.width / 28;
|
||||
this.arcNodesGap = 4;
|
||||
|
||||
|
||||
this.layout = {
|
||||
Data: {cx: 0, cy: 0, r: this.width11By2, offset: {x: 0, y: 0}, popover: 'top'},
|
||||
People: {cx: -this.width2By7, cy: 0, r: this.width11By2, offset: {x: this.width1By11 + this.fontStyles[1].size / 5 * 3, y: 0}, popover: 'right'},
|
||||
Networks: {cx: this.width2By7, cy: 0, r: this.width11By2, offset: {x: -this.width1By11 - this.fontStyles[1].size / 5 * 3, y: 0}, popover: 'left'},
|
||||
Devices: {cx: 0, cy: this.width2By7, r: this.width11By2, offset: {x: 0, y: -this.width1By11 + this.fontStyles[1].size / 6 * 3}, popover: 'top'},
|
||||
Workloads: {cx: 0, cy: -this.width2By7, r: this.width11By2, offset: {x: 0, y: this.width1By11}, popover: 'bottom' },
|
||||
VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, icon: '\uf070', popover: 'left'},
|
||||
People: {
|
||||
cx: -this.width2By7,
|
||||
cy: 0,
|
||||
r: this.width11By2,
|
||||
offset: {x: this.width1By11 + this.fontStyles[1].size / 5 * 3, y: 0},
|
||||
popover: 'right'
|
||||
},
|
||||
Networks: {
|
||||
cx: this.width2By7,
|
||||
cy: 0,
|
||||
r: this.width11By2,
|
||||
offset: {x: -this.width1By11 - this.fontStyles[1].size / 5 * 3, y: 0},
|
||||
popover: 'left'
|
||||
},
|
||||
Devices: {
|
||||
cx: 0,
|
||||
cy: this.width2By7,
|
||||
r: this.width11By2,
|
||||
offset: {x: 0, y: -this.width1By11 + this.fontStyles[1].size / 6 * 3},
|
||||
popover: 'top'
|
||||
},
|
||||
Workloads: {
|
||||
cx: 0,
|
||||
cy: -this.width2By7,
|
||||
r: this.width11By2,
|
||||
offset: {x: 0, y: this.width1By11},
|
||||
popover: 'bottom'
|
||||
},
|
||||
VisibilityAndAnalytics: {
|
||||
inner: this.thirdWidth - this.width1By28,
|
||||
outer: this.thirdWidth,
|
||||
icon: '\uf070',
|
||||
popover: 'right'
|
||||
},
|
||||
AutomationAndOrchestration: {
|
||||
inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap,
|
||||
outer: this.thirdWidth - this.width1By28 - this.arcNodesGap,
|
||||
|
@ -85,51 +117,61 @@ class VennDiagram extends React.Component {
|
|||
|
||||
}
|
||||
|
||||
componentDidMount() { this.parseData(); if(this.state.currentPopover !== undefined) { this.state.currentPopover.show(); } }
|
||||
|
||||
_disableHover(ref_) { this.setState({hover: false, currentPopover: ref_, data: this.state.data }); }
|
||||
|
||||
componentDidMount() {
|
||||
this.parseData();
|
||||
if (this.state.currentPopover !== undefined) {
|
||||
this.state.currentPopover.show();
|
||||
}
|
||||
}
|
||||
|
||||
_disableHover(ref_) {
|
||||
this.setState({hover: false, currentPopover: ref_, data: this.state.data});
|
||||
}
|
||||
|
||||
_onMouseMove(e) {
|
||||
|
||||
|
||||
let self = this;
|
||||
|
||||
|
||||
let hidden = 'none';
|
||||
let html = '';
|
||||
let bcolor = '#DEDEDE';
|
||||
|
||||
if(this.state.currentPopover !== undefined) { this.state.currentPopover.show(); }
|
||||
|
||||
if (this.state.currentPopover !== undefined) {
|
||||
this.state.currentPopover.show();
|
||||
}
|
||||
|
||||
document.querySelectorAll('circle, path').forEach((d_, i_) => {
|
||||
d_.setAttribute('opacity', "0.8");
|
||||
d_.setAttribute('opacity', "0.8");
|
||||
});
|
||||
|
||||
if (e.target.id.includes('Node')) {
|
||||
|
||||
e.target.setAttribute('opacity', 0.95);
|
||||
e.target.setAttribute('opacity', 0.95);
|
||||
|
||||
// Set highest z-index
|
||||
e.target.parentNode.parentNode.appendChild(e.target.parentNode);
|
||||
|
||||
// Set highest z-index
|
||||
e.target.parentNode.parentNode.appendChild(e.target.parentNode);
|
||||
|
||||
} else {
|
||||
|
||||
// Return z indices to default
|
||||
Object.keys(this.layout).forEach(function (d_, i_) {
|
||||
document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); })
|
||||
// Return z indices to default
|
||||
Object.keys(this.layout).forEach(function (d_, i_) {
|
||||
document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode);
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
_onClick(e) {
|
||||
|
||||
if (!e.target.id.includes('Node')) {
|
||||
|
||||
this.state.currentPopover.hide();
|
||||
this.setState({hover: true, currentPopover: undefined, data: this.state.data });
|
||||
}
|
||||
|
||||
if (!e.target.id.includes('Node')) {
|
||||
|
||||
this.state.currentPopover.hide();
|
||||
this.setState({hover: true, currentPopover: undefined, data: this.state.data});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
parseData() {
|
||||
|
||||
|
||||
let self = this;
|
||||
let data = [];
|
||||
const omit = (prop, {[prop]: _, ...rest}) => rest;
|
||||
|
@ -157,23 +199,25 @@ class VennDiagram extends React.Component {
|
|||
this.setState({hover: true, activePopover: undefined, data: data});
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
||||
buildTooltipHtmlContent(object_) {
|
||||
|
||||
return Object.keys(object_).map((key_, i_) => { return ( <p key={this.prefix + key_ + i_}>{key_}: {object_[key_]}</p> ) })
|
||||
return Object.keys(object_).map((key_, i_) => {
|
||||
return (<p key={this.prefix + key_ + i_}>{key_}: {object_[key_]}</p>)
|
||||
})
|
||||
}
|
||||
|
||||
setLayoutElement(rule_, key_, html_, d_) {
|
||||
|
||||
if(rule_ === null) { console.log(Error('The node scores are invalid, please check the data or the rules set.')); }
|
||||
|
||||
if (rule_ === null) {
|
||||
console.log(Error('The node scores are invalid, please check the data or the rules set.'));
|
||||
}
|
||||
|
||||
if (key_ === 'Data') {
|
||||
this.layout[key_].fontStyle = this.fontStyles[0];
|
||||
}
|
||||
else if(this.layout[key_].hasOwnProperty('cx')){
|
||||
} else if (this.layout[key_].hasOwnProperty('cx')) {
|
||||
this.layout[key_].fontStyle = this.fontStyles[1];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.layout[key_].fontStyle = this.fontStyles[2];
|
||||
}
|
||||
|
||||
|
@ -218,11 +262,12 @@ class VennDiagram extends React.Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<div ref={(divElement) => this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)}>
|
||||
<svg id={this.prefix} viewBox={viewPortParameters} width={'100%'} height={'100%'}
|
||||
<div ref={(divElement) => this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)}
|
||||
onClick={this._onClick.bind(this)}>
|
||||
<svg id={this.prefix} viewBox={viewPortParameters} width={'100%'} height={'100%'}
|
||||
xmlns='http://www.w3.org/2000/svg' xmlnsXlink='http://www.w3.org/1999/xlink'>
|
||||
{nodes}
|
||||
</svg>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue