diff options
author | Emiliano Ciavatta | 2020-10-16 17:06:05 +0000 |
---|---|---|
committer | Emiliano Ciavatta | 2020-10-16 17:06:05 +0000 |
commit | 56f70a72196c777f248038bb2e2e4099e6e1367d (patch) | |
tree | 714ad5aed8698dfffbb472b3fa74909acb8cdead /frontend/src/components/objects | |
parent | 6204c99e69d1707a79c5e56685b47310106c60b0 (diff) | |
parent | 79b8b2fa3e8563c986da8baa3a761f2d4f0c6f47 (diff) |
Merge branch 'develop'
Diffstat (limited to 'frontend/src/components/objects')
-rw-r--r-- | frontend/src/components/objects/Connection.js | 114 | ||||
-rw-r--r-- | frontend/src/components/objects/Connection.scss | 72 | ||||
-rw-r--r-- | frontend/src/components/objects/ConnectionMatchedRules.js | 51 | ||||
-rw-r--r-- | frontend/src/components/objects/ConnectionMatchedRules.scss | 23 | ||||
-rw-r--r-- | frontend/src/components/objects/CopyLinkPopover.js | 54 | ||||
-rw-r--r-- | frontend/src/components/objects/LinkPopover.js | 32 | ||||
-rw-r--r-- | frontend/src/components/objects/LinkPopover.scss | 5 | ||||
-rw-r--r-- | frontend/src/components/objects/MessageAction.js | 69 | ||||
-rw-r--r-- | frontend/src/components/objects/MessageAction.scss | 8 |
9 files changed, 421 insertions, 7 deletions
diff --git a/frontend/src/components/objects/Connection.js b/frontend/src/components/objects/Connection.js new file mode 100644 index 0000000..b70b7f7 --- /dev/null +++ b/frontend/src/components/objects/Connection.js @@ -0,0 +1,114 @@ +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from "react"; +import {Form} from "react-bootstrap"; +import backend from "../../backend"; +import dispatcher from "../../dispatcher"; +import {dateTimeToTime, durationBetween, formatSize} from "../../utils"; +import ButtonField from "../fields/ButtonField"; +import "./Connection.scss"; +import CopyLinkPopover from "./CopyLinkPopover"; +import LinkPopover from "./LinkPopover"; + +const classNames = require("classnames"); + +class Connection extends Component { + + state = { + update: false + }; + + handleAction = (name) => { + if (name === "hide") { + const enabled = !this.props.data.hidden; + backend.post(`/api/connections/${this.props.data.id}/${enabled ? "hide" : "show"}`) + .then((_) => { + this.props.onEnabled(!enabled); + this.setState({update: true}); + }); + } + if (name === "mark") { + const marked = this.props.data.marked; + backend.post(`/api/connections/${this.props.data.id}/${marked ? "unmark" : "mark"}`) + .then((_) => { + this.props.onMarked(!marked); + this.setState({update: true}); + }); + } + }; + + render() { + let conn = this.props.data; + let serviceName = "/dev/null"; + let serviceColor = "#0f192e"; + if (this.props.services[conn["port_dst"]]) { + const service = this.props.services[conn["port_dst"]]; + serviceName = service.name; + serviceColor = service.color; + } + let startedAt = new Date(conn["started_at"]); + let closedAt = new Date(conn["closed_at"]); + let processedAt = new Date(conn["processed_at"]); + let timeInfo = <div> + <span>Started at {startedAt.toLocaleDateString() + " " + startedAt.toLocaleTimeString()}</span><br/> + <span>Processed at {processedAt.toLocaleDateString() + " " + processedAt.toLocaleTimeString()}</span><br/> + <span>Closed at {closedAt.toLocaleDateString() + " " + closedAt.toLocaleTimeString()}</span> + </div>; + + const commentPopoverContent = <div> + <span>Click to <strong>{conn.comment.length > 0 ? "edit" : "add"}</strong> comment</span> + {conn.comment && <Form.Control as="textarea" readOnly={true} rows={2} defaultValue={conn.comment}/>} + </div>; + + return ( + <tr className={classNames("connection", {"connection-selected": this.props.selected}, + {"has-matched-rules": conn.matched_rules.length > 0})}> + <td> + <span className="connection-service"> + <ButtonField small fullSpan color={serviceColor} name={serviceName} + onClick={() => dispatcher.dispatch("connections_filters", + {"service_port": conn["port_dst"].toString()})}/> + </span> + </td> + <td className="clickable" onClick={this.props.onSelected}>{conn["ip_src"]}</td> + <td className="clickable" onClick={this.props.onSelected}>{conn["port_src"]}</td> + <td className="clickable" onClick={this.props.onSelected}>{conn["ip_dst"]}</td> + <td className="clickable" onClick={this.props.onSelected}>{conn["port_dst"]}</td> + <td className="clickable" onClick={this.props.onSelected}> + <LinkPopover text={dateTimeToTime(conn["started_at"])} content={timeInfo} placement="right"/> + </td> + <td className="clickable" onClick={this.props.onSelected}>{durationBetween(startedAt, closedAt)}</td> + <td className="clickable" onClick={this.props.onSelected}>{formatSize(conn["client_bytes"])}</td> + <td className="clickable" onClick={this.props.onSelected}>{formatSize(conn["server_bytes"])}</td> + <td className="connection-actions"> + <LinkPopover text={<span className={classNames("connection-icon", {"icon-enabled": conn.marked})} + onClick={() => this.handleAction("mark")}>!!</span>} + content={<span>Mark this connection</span>} placement="right"/> + <LinkPopover text={<span className={classNames("connection-icon", {"icon-enabled": conn.comment})} + onClick={() => this.handleAction("comment")}>@</span>} + content={commentPopoverContent} placement="right"/> + <CopyLinkPopover text="#" value={conn.id} + textClassName={classNames("connection-icon", {"icon-enabled": conn.hidden})}/> + </td> + </tr> + ); + } + +} + +export default Connection; diff --git a/frontend/src/components/objects/Connection.scss b/frontend/src/components/objects/Connection.scss new file mode 100644 index 0000000..bf66272 --- /dev/null +++ b/frontend/src/components/objects/Connection.scss @@ -0,0 +1,72 @@ +@import "../../colors"; + +.connection { + border-top: 3px solid $color-primary-3; + border-bottom: 3px solid $color-primary-3; + background-color: $color-primary-0; + + td { + vertical-align: middle; + } + + .connection-service { + .btn { + width: 100%; + } + + .btn:hover { + color: $color-primary-4; + background-color: $color-primary-1 !important; + } + } + + .connection-icon { + font-size: 18px; + font-weight: 600; + margin-right: 6px; + cursor: pointer; + } + + .connection-icon.icon-enabled { + color: $color-secondary-2; + } + + &:hover { + background-color: $color-primary-2; + } + + &.connection-selected { + background-color: $color-primary-2; + } + + &.has-matched-rules { + border-bottom: 0; + } + + .link-popover { + font-weight: 400; + } + + .connection-actions .link-popover { + text-decoration: none; + } +} + +.connection-popover { + border: none; + background-color: $color-primary-4; + + .popover-body { + color: $color-primary-1; + + textarea { + font-size: 12px; + width: 200px; + background-color: $color-primary-3; + } + } + + .arrow::after { + border-right-color: $color-primary-4; + } +} diff --git a/frontend/src/components/objects/ConnectionMatchedRules.js b/frontend/src/components/objects/ConnectionMatchedRules.js new file mode 100644 index 0000000..a69cad8 --- /dev/null +++ b/frontend/src/components/objects/ConnectionMatchedRules.js @@ -0,0 +1,51 @@ +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from "react"; +import {withRouter} from "react-router-dom"; +import dispatcher from "../../dispatcher"; +import ButtonField from "../fields/ButtonField"; +import "./ConnectionMatchedRules.scss"; + +class ConnectionMatchedRules extends Component { + + onMatchedRulesSelected = (id) => { + const params = new URLSearchParams(this.props.location.search); + const rules = params.getAll("matched_rules"); + if (!rules.includes(id)) { + rules.push(id); + dispatcher.dispatch("connections_filters", {"matched_rules": rules}); + } + }; + + render() { + const matchedRules = this.props.matchedRules.map((mr) => { + const rule = this.props.rules.find((r) => r.id === mr); + return <ButtonField key={mr} onClick={() => this.onMatchedRulesSelected(rule.id)} name={rule.name} + color={rule.color} small/>; + }); + + return ( + <tr className="connection-matches"> + <td className="row-label">matched_rules:</td> + <td className="rule-buttons" colSpan={9}>{matchedRules}</td> + </tr> + ); + } +} + +export default withRouter(ConnectionMatchedRules); diff --git a/frontend/src/components/objects/ConnectionMatchedRules.scss b/frontend/src/components/objects/ConnectionMatchedRules.scss new file mode 100644 index 0000000..f46a914 --- /dev/null +++ b/frontend/src/components/objects/ConnectionMatchedRules.scss @@ -0,0 +1,23 @@ +@import "../../colors"; + +.connection-matches { + background-color: $color-primary-0; + + .rule-buttons { + padding: 0; + } + + .button-field { + display: inline-block; + margin-right: 5px; + + button { + font-size: 0.8em; + padding: 2px 10px; + } + } + + .row-label { + font-size: 0.8em; + } +} diff --git a/frontend/src/components/objects/CopyLinkPopover.js b/frontend/src/components/objects/CopyLinkPopover.js new file mode 100644 index 0000000..b951603 --- /dev/null +++ b/frontend/src/components/objects/CopyLinkPopover.js @@ -0,0 +1,54 @@ +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from "react"; +import TextField from "../fields/TextField"; +import LinkPopover from "./LinkPopover"; + +class CopyLinkPopover extends Component { + + state = {}; + + constructor(props) { + super(props); + + this.copyTextarea = React.createRef(); + } + + handleClick = () => { + this.copyTextarea.current.select(); + document.execCommand("copy"); + this.setState({copiedMessage: true}); + setTimeout(() => this.setState({copiedMessage: false}), 3000); + }; + + render() { + const copyPopoverContent = <div style={{"width": "250px"}}> + {this.state.copiedMessage ? <span><strong>Copied!</strong></span> : + <span>Click to <strong>copy</strong></span>} + <TextField readonly rows={2} value={this.props.value} textRef={this.copyTextarea}/> + </div>; + + return ( + <LinkPopover text={<span className={this.props.textClassName} + onClick={this.handleClick}>{this.props.text}</span>} + content={copyPopoverContent} placement="right"/> + ); + } +} + +export default CopyLinkPopover; diff --git a/frontend/src/components/objects/LinkPopover.js b/frontend/src/components/objects/LinkPopover.js index 8768caa..551a819 100644 --- a/frontend/src/components/objects/LinkPopover.js +++ b/frontend/src/components/objects/LinkPopover.js @@ -1,7 +1,24 @@ -import React, {Component} from 'react'; -import {randomClassName} from "../../utils"; +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from "react"; import {OverlayTrigger, Popover} from "react-bootstrap"; -import './LinkPopover.scss'; +import {randomClassName} from "../../utils"; +import "./LinkPopover.scss"; class LinkPopover extends Component { @@ -22,10 +39,11 @@ class LinkPopover extends Component { ); return (this.props.content ? - <OverlayTrigger trigger={["hover", "focus"]} placement={this.props.placement || "top"} overlay={popover}> - <span className="link-popover">{this.props.text}</span> - </OverlayTrigger> : - <span className="link-popover-empty">{this.props.text}</span> + <OverlayTrigger trigger={["hover", "focus"]} placement={this.props.placement || "top"} + overlay={popover}> + <span className="link-popover">{this.props.text}</span> + </OverlayTrigger> : + <span className="link-popover-empty">{this.props.text}</span> ); } } diff --git a/frontend/src/components/objects/LinkPopover.scss b/frontend/src/components/objects/LinkPopover.scss index 725224c..c81f8bb 100644 --- a/frontend/src/components/objects/LinkPopover.scss +++ b/frontend/src/components/objects/LinkPopover.scss @@ -5,3 +5,8 @@ cursor: pointer; text-decoration: underline; } + +.popover { + font-family: "Fira Code", monospace; + font-size: 0.75em; +} diff --git a/frontend/src/components/objects/MessageAction.js b/frontend/src/components/objects/MessageAction.js new file mode 100644 index 0000000..e0c96e8 --- /dev/null +++ b/frontend/src/components/objects/MessageAction.js @@ -0,0 +1,69 @@ +/* + * This file is part of caronte (https://github.com/eciavatta/caronte). + * Copyright (c) 2020 Emiliano Ciavatta. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import React, {Component} from "react"; +import {Modal} from "react-bootstrap"; +import ButtonField from "../fields/ButtonField"; +import TextField from "../fields/TextField"; +import "./MessageAction.scss"; + +class MessageAction extends Component { + + constructor(props) { + super(props); + this.state = { + copyButtonText: "copy" + }; + this.actionValue = React.createRef(); + this.copyActionValue = this.copyActionValue.bind(this); + } + + copyActionValue() { + this.actionValue.current.select(); + document.execCommand("copy"); + this.setState({copyButtonText: "copied!"}); + setTimeout(() => this.setState({copyButtonText: "copy"}), 3000); + } + + render() { + return ( + <Modal + {...this.props} + show={true} + size="lg" + aria-labelledby="message-action-dialog" + centered + > + <Modal.Header> + <Modal.Title id="message-action-dialog"> + {this.props.actionName} + </Modal.Title> + </Modal.Header> + <Modal.Body> + <TextField readonly value={this.props.actionValue} textRef={this.actionValue} rows={15}/> + </Modal.Body> + <Modal.Footer className="dialog-footer"> + <ButtonField variant="green" bordered onClick={this.copyActionValue} + name={this.state.copyButtonText}/> + <ButtonField variant="red" bordered onClick={this.props.onHide} name="close"/> + </Modal.Footer> + </Modal> + ); + } +} + +export default MessageAction; diff --git a/frontend/src/components/objects/MessageAction.scss b/frontend/src/components/objects/MessageAction.scss new file mode 100644 index 0000000..996007b --- /dev/null +++ b/frontend/src/components/objects/MessageAction.scss @@ -0,0 +1,8 @@ +@import "../../colors"; + +.message-action-value { + font-size: 13px; + padding: 15px; + color: $color-primary-4; + background-color: $color-primary-2; +} |