Skip to content

Commit

Permalink
Adds PCAP/Attachment download capability (#120)
Browse files Browse the repository at this point in the history
* adding file/pcap download capability

* updating packages

* updating dev version of shared package

* updating shared package dev version

* update typescript, fix import

* fixing broken ts build

* sorting file names

* update nm-web-shared version
  • Loading branch information
sperry94 committed Jan 29, 2020
1 parent 323dec2 commit 189f0fa
Show file tree
Hide file tree
Showing 19 changed files with 1,112 additions and 76 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"@kbn/test-subj-selector": "0.2.1",
"@kbn/ui-framework": "1.0.0",
"@logrhythm/icons": "^1.19.0",
"@logrhythm/nm-web-shared": "1.10.1",
"@logrhythm/nm-web-shared": "1.11.0",
"@logrhythm/webui": "^5.9.15",
"@types/json-stable-stringify": "^1.0.32",
"@types/lodash.clonedeep": "^4.5.4",
Expand All @@ -138,6 +138,7 @@
"abortcontroller-polyfill": "^1.3.0",
"angular": "^1.7.9",
"angular-aria": "^1.7.8",
"angular-bind-html-compile": "^1.4.1",
"angular-elastic": "^2.5.1",
"angular-recursion": "^1.0.5",
"angular-route": "^1.7.8",
Expand Down Expand Up @@ -225,7 +226,7 @@
"react-grid-layout": "^0.16.2",
"react-hooks-testing-library": "^0.5.0",
"react-input-range": "^1.3.0",
"react-jss": "^8.6.1",
"react-jss": "^10.0.0",
"react-markdown": "^3.4.1",
"react-redux": "^5.1.1",
"react-router-dom": "^4.3.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
on-change-sort-order="onChangeSortOrder"
on-move-column="onMoveColumn"
on-remove-column="onRemoveColumn"
on-select-all="onSelectAll"
on-select-current-page="onSelectCurrentPage"
></thead>
<tbody>
<tr ng-repeat="row in pageOfItems|limitTo:limit track by row._index+row._type+row._id+row._score+row._version+row._routing"
Expand Down Expand Up @@ -84,6 +86,7 @@
on-change-sort-order="onChangeSortOrder"
on-move-column="onMoveColumn"
on-remove-column="onRemoveColumn"
on-select-all="onSelectAll"
></thead>
<tbody>
<tr ng-repeat="row in hits|limitTo:limit track by row._index+row._type+row._id+row._score+row._version+row._routing"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import './components/pager';
import './lib/pager';

import { getLimitedSearchResultsMessage } from './doc_table_strings';
import SelectedCaptureSessions from '@logrhythm/nm-web-shared/services/selected_capture_sessions';

uiModules
.get('app/discover')
Expand Down Expand Up @@ -119,8 +120,23 @@ uiModules
calculateItemsOnPage();
};

$scope.shouldShowLimitedResultsWarning = () =>
!$scope.pager.hasNextPage && $scope.pager.totalItems < $scope.totalHitCount;
},
$scope.shouldShowLimitedResultsWarning = () => (
!$scope.pager.hasNextPage && $scope.pager.totalItems < $scope.totalHitCount
);

$scope.onSelectAll = () => {
const allSessions = $scope.hits
.map(h => !!h && !!h._source && h._source.Session)
.filter(h => !!h);
SelectedCaptureSessions.set(allSessions);
};

$scope.onSelectCurrentPage = () => {
const allSessions = $scope.pageOfItems
.map(h => !!h && !!h._source && h._source.Session)
.filter(h => !!h);
SelectedCaptureSessions.set(allSessions);
};
}
};
});
1 change: 1 addition & 0 deletions src/legacy/core_plugins/kibana/public/kibana.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import './management';
import './doc';
import './dev_tools';
import './context';
import '../../../../netmon/directives';
import 'ui/vislib';
import 'ui/agg_response';
import 'ui/agg_types';
Expand Down
104 changes: 104 additions & 0 deletions src/netmon/components/attach_download.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { useState, useEffect } from 'react';
import { createUseStyles } from 'react-jss';
import { EuiButtonIcon } from '@elastic/eui';
import { toastNotifications } from 'ui/notify';
import { startAttachmentDownload } from '@logrhythm/nm-web-shared/services/session_files';
import FileDownloadModal from './file_download/file_download_modal';

const useStyles = createUseStyles({
buttonWrapper: {
cursor: 'pointer',
},
});

export interface AttachDownloadProps {
session: string;
fileName: string;
}

const AttachDownload = (props: AttachDownloadProps) => {
const { session, fileName } = props;

const classes = useStyles();

const [downloadId, setDownloadId] = useState('');
const [fileNames, setFileNames] = useState<string[]>([]);

useEffect(
() => {
const newFileNames = new Set<string>();

fileName
.split(',')
.map(f => f.trim())
.forEach(file => {
let fileNameToAdd: string = file;

const prefix: number = 1;
while (newFileNames.has(fileNameToAdd)) {
fileNameToAdd = `${prefix}_${file}`;
}

newFileNames.add(fileNameToAdd);
});

setFileNames(Array.from(newFileNames));
},
[fileName]
);

const handleStartDownload = async () => {
try {
const startDownloadRes = await startAttachmentDownload(session, fileNames);

setDownloadId(startDownloadRes.data.downloadID);
} catch (err) {
console.error( // eslint-disable-line
`An error occurred creating an attachment download for session ${session}`,
err
);
toastNotifications.addDanger('An error initiating a download for the attachment(s).');
return;
}
};

return (
<>
<div className={classes.buttonWrapper}>
<EuiButtonIcon
disabled={fileNames.length === 0}
iconType="importAction"
color="primary"
onClick={handleStartDownload}
aria-label="Download Attachment"
/>
</div>
<FileDownloadModal
downloadId={downloadId}
fileType="reconstruction"
onClose={() => setDownloadId('')}
/>
</>
);
};

export default AttachDownload; // eslint-disable-line
105 changes: 105 additions & 0 deletions src/netmon/components/capture_download.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { useEffect, useState } from 'react';
import { createUseStyles } from 'react-jss';
import { EuiButtonIcon, EuiCheckbox } from '@elastic/eui';
import SelectedCaptureSessions from '@logrhythm/nm-web-shared/services/selected_capture_sessions';
import { startPcapDownload } from '@logrhythm/nm-web-shared/services/session_files';
import { toastNotifications } from 'ui/notify';
import FileDownloadModal from './file_download/file_download_modal';

const useStyles = createUseStyles({
contentWrapper: {
display: 'flex',
alignItems: 'center',
},
buttonWrapper: {
cursor: 'pointer',
},
});

export interface AttachDownloadProps {
session: string;
}

const CaptureDownload = (props: AttachDownloadProps) => {
const { session } = props;

const classes = useStyles();

const [downloadId, setDownloadId] = useState('');
const [isSelected, setIsSelected] = useState(false);

useEffect(
() => {
return SelectedCaptureSessions.subscribe(session, setIsSelected);
},
[session]
);

const handleStartDownload = async () => {
try {
const sessions = !SelectedCaptureSessions.isEmpty()
? SelectedCaptureSessions.getAll()
: [session];

const startDownloadRes = await startPcapDownload(sessions);

setDownloadId(startDownloadRes.data.downloadID);
SelectedCaptureSessions.reset();
} catch (err) {
console.error(`An error occurred creating a PCAP download for session ${session}`, err); // eslint-disable-line
toastNotifications.addDanger('An error initiating a download for the PCAP(s).');
return;
}
};

return (
<>
<div className={classes.contentWrapper}>
<div className={classes.buttonWrapper}>
<EuiButtonIcon
iconType="importAction"
color="primary"
onClick={handleStartDownload}
aria-label="Download PCAPs"
/>
</div>
<EuiCheckbox
id={`download_session_${session}`}
checked={isSelected}
onChange={e => {
const action = e.target.checked
? SelectedCaptureSessions.add
: SelectedCaptureSessions.remove;
action(session);
}}
/>
</div>
<FileDownloadModal
downloadId={downloadId}
fileType="pcap"
onClose={() => setDownloadId('')}
/>
</>
);
};

export default CaptureDownload; // eslint-disable-line
Loading

0 comments on commit 189f0fa

Please sign in to comment.