Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle advanced filters conditions from dashboard #1520

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Set;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
Expand Down Expand Up @@ -349,6 +350,47 @@ public String updateUrlSettings() {

}

private boolean updateFiltersFlag;
private String permissionValue;

Map<String,Boolean> advancedFilterPermission;



public String getAdvancedFilterFlagsForAccount(){
AccountSettings accountSettings = AccountSettingsDao.instance.findOne(AccountSettingsDao.generateFilter());
advancedFilterPermission = new HashMap<>();
advancedFilterPermission.put(AccountSettings.ALLOW_FILTER_LOGS, accountSettings.getAllowFilterLogs());
advancedFilterPermission.put(AccountSettings.ALLOW_DELETION_OF_REDUNDANT_URLS, accountSettings.getAllowDeletionOfUrls());

return SUCCESS.toUpperCase();
}

public String updatePermissionsForAdvancedFilters(){
if(this.permissionValue.equals(AccountSettings.ALLOW_DELETION_OF_REDUNDANT_URLS) || this.permissionValue.equals(AccountSettings.ALLOW_FILTER_LOGS)){
AccountSettingsDao.instance.updateOne(
AccountSettingsDao.generateFilter(),
Updates.set(this.permissionValue, this.updateFiltersFlag)
);
return SUCCESS.toUpperCase();
}else{
addActionError("invalid permission");
return ERROR.toUpperCase();
}
}

public void setUpdateFiltersFlag(boolean updateFiltersFlag) {
this.updateFiltersFlag = updateFiltersFlag;
}

public void setPermissionValue(String permissionValue) {
this.permissionValue = permissionValue;
}

public Map<String, Boolean> getAdvancedFilterPermission() {
return advancedFilterPermission;
}

public AccountSettings getAccountSettings() {
return this.accountSettings;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package com.akto.action.settings;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.bson.conversions.Bson;

import com.akto.action.UserAction;
import com.akto.dao.ApiCollectionsDao;
import com.akto.dao.context.Context;
import com.akto.dao.monitoring.FilterConfigYamlParser;
import com.akto.dao.runtime_filters.AdvancedTrafficFiltersDao;
import com.akto.dto.ApiCollection;
import com.akto.dto.monitoring.FilterConfig;
import com.akto.dto.test_editor.YamlTemplate;
import com.akto.util.Constants;
import com.akto.utils.TrafficFilterUtil;
import com.akto.utils.jobs.CleanInventory;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Sorts;
import com.mongodb.client.model.Updates;
import com.opensymphony.xwork2.Action;

public class AdvancedTrafficFiltersAction extends UserAction {

Expand Down Expand Up @@ -82,6 +89,30 @@ public String deleteAdvancedFilter(){
return SUCCESS.toUpperCase();
}

public String syncTrafficFromFilters(){
FilterConfig filterConfig = new FilterConfig();
try {
filterConfig = FilterConfigYamlParser.parseTemplate(yamlContent, true);
if (filterConfig.getId() == null) {
throw new Exception("id field cannot be empty");
}
if (filterConfig.getFilter() == null) {
throw new Exception("filter field cannot be empty");
}

List<ApiCollection> apiCollections = ApiCollectionsDao.instance.findAll(
Filters.empty(), Projections.include(ApiCollection.HOST_NAME));
YamlTemplate yamlTemplate = new YamlTemplate(filterConfig.getId(), Context.now(), getSUser().getLogin(), Context.now(), this.yamlContent, null);

CleanInventory.cleanFilteredSampleDataFromAdvancedFilters(apiCollections,Arrays.asList(yamlTemplate),new ArrayList<>() , "", false, true);
return Action.SUCCESS.toUpperCase();
} catch (Exception e) {
e.printStackTrace();
addActionError(e.getMessage());
return ERROR.toUpperCase();
}
}

public List<YamlTemplate> getTemplatesList() {
return templatesList;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ public void accept(Account account) {
);
List<String> redundantUrlList = accountSettings.getAllowRedundantEndpointsList();
try {
CleanInventory.cleanFilteredSampleDataFromAdvancedFilters(apiCollections , yamlTemplates, redundantUrlList,filePath, shouldDeleteApis);
CleanInventory.cleanFilteredSampleDataFromAdvancedFilters(apiCollections , yamlTemplates, redundantUrlList,filePath, shouldDeleteApis, false);
} catch (Exception e) {
e.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ private static void cleanInventoryJob() {

}

public static void cleanFilteredSampleDataFromAdvancedFilters(List<ApiCollection> apiCollections, List<YamlTemplate> yamlTemplates, List<String> redundantUrlList, String filePath, boolean shouldDeleteRequest) throws IOException{
public static void cleanFilteredSampleDataFromAdvancedFilters(List<ApiCollection> apiCollections, List<YamlTemplate> yamlTemplates, List<String> redundantUrlList, String filePath, boolean shouldDeleteRequest, boolean saveLogsToDB) throws IOException{

Map<Integer, ApiCollection> apiCollectionMap = apiCollections.stream().collect(Collectors.toMap(ApiCollection::getId, Function.identity()));
// BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filePath)));
Expand Down Expand Up @@ -185,15 +185,32 @@ public static void cleanFilteredSampleDataFromAdvancedFilters(List<ApiCollection

if(movingApi){
toMove.add(sampleData.getId());
logger.info("[BadApisUpdater] Updating bad from template API: " + sampleData.getId(), LogDb.DASHBOARD);
if(saveLogsToDB){
loggerMaker.infoAndAddToDb("Filter passed, modify sample data of API: " + sampleData.getId(), LogDb.DASHBOARD);
}else{
logger.info("[BadApisUpdater] Updating bad from template API: " + sampleData.getId(), LogDb.DASHBOARD);
}
}

else if (isRedundant || !isAllowedFromTemplate) {
// writer.write(sampleData.toString());
toBeDeleted.add(sampleData.getId());
logger.info("[BadApisRemover] " + isNetsparkerPresent + " Deleting bad API from template: " + sampleData.getId(), LogDb.DASHBOARD);
toBeDeleted.add(sampleData.getId());
if(saveLogsToDB){
loggerMaker.infoAndAddToDb(
"Filter passed, deleting bad api found from filter: " + sampleData.getId(), LogDb.DASHBOARD
);
}else{
logger.info("[BadApisRemover] " + isNetsparkerPresent + " Deleting bad API from template: " + sampleData.getId(), LogDb.DASHBOARD);
}
} else {
logger.info("[BadApisRemover] " + isNetsparkerPresent + " Keeping API from template: " + sampleData.getId(), LogDb.DASHBOARD);
if(saveLogsToDB){
loggerMaker.infoAndAddToDb(
"Filter did not pass, keeping api found from filter: " + sampleData.getId(), LogDb.DASHBOARD
);
}else{
logger.info("[BadApisRemover] " + isNetsparkerPresent + " Keeping API from template: " + sampleData.getId(), LogDb.DASHBOARD);
}

}
} catch (Exception e) {
loggerMaker.errorAndAddToDb("[BadApisRemover] Couldn't delete an api for default payload: " + sampleData.getId() + e.getMessage(), LogDb.DASHBOARD);
Expand Down
64 changes: 64 additions & 0 deletions apps/dashboard/src/main/resources/struts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6657,6 +6657,70 @@
</result>
</action>

<action name="api/dryRunAdvancedFilters" class="com.akto.action.settings.AdvancedTrafficFiltersAction" method="syncTrafficFromFilters">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="roleAccessInterceptor">
<param name="featureLabel">ADMIN_ACTIONS</param>
<param name="accessType">READ_WRITE</param>
</interceptor-ref>
<result name="FORBIDDEN" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<result name="SUCCESS" type="json">
</result>
<result name="ERROR" type="json">
<param name="statusCode">422</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
</action>

<action name="api/updateRetrospectiveFilterSettings" class="com.akto.action.AdminSettingsAction" method="updatePermissionsForAdvancedFilters">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="roleAccessInterceptor">
<param name="featureLabel">ADMIN_ACTIONS</param>
<param name="accessType">READ_WRITE</param>
</interceptor-ref>
<result name="FORBIDDEN" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<result name="SUCCESS" type="json">
</result>
<result name="ERROR" type="json">
<param name="statusCode">422</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
</action>

<action name="api/getAccountSettingsForAdvancedFilters" class="com.akto.action.AdminSettingsAction" method="getAdvancedFilterFlagsForAccount">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="roleAccessInterceptor">
<param name="featureLabel">USER_ACTIONS</param>
<param name="accessType">READ</param>
</interceptor-ref>
<result name="FORBIDDEN" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<result name="SUCCESS" type="json">
<param name="root">advancedFilterPermission</param>
</result>
<result name="ERROR" type="json">
<param name="statusCode">422</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
</action>

</package>

</struts>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Logs = () => {
const [ logs, setLogs ] = useState({
startTime: null,
endTime: null,
logGroup: '',
logGroup: 'DASHBOARD',
logData: []
})
const [ loading, setLoading ] = useState(false)
Expand Down Expand Up @@ -103,7 +103,7 @@ const Logs = () => {
<div style={{ display: "grid", gridTemplateColumns: "auto max-content", gap: "10px"}}>
<Dropdown
menuItems={logGroupOptions}
initial="Select log group"
initial="Dashboard"
selected={handleSelectLogGroup}
/>
<ButtonGroup segmented>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'
import PageWithMultipleCards from '../../../components/layouts/PageWithMultipleCards'
import { Box, Button, HorizontalStack, Icon, LegacyCard, Text, Tooltip, VerticalStack } from '@shopify/polaris'
import { Box, Button, Checkbox, HorizontalStack, Icon, LegacyCard, Modal, Popover, Text, Tooltip, VerticalStack } from '@shopify/polaris'
import trafficFiltersRequest from './api'
import func from "@/util/func"
import TitleWithInfo from "@/apps/dashboard/components/shared/TitleWithInfo"
Expand All @@ -15,6 +15,9 @@ function AdvancedTrafficFilters() {
const [templateList, setTemplateList] = useState([])
const [currentId, setCurrentId] = useState("")
const [currentState, setCurrentState] = useState(false)
const [modalActive, setModalActive] = useState(false);
const [popoverActive, setPopoverActive] = useState(false);
const [permissionsMap,setPermissionsMap] = useState({})

const fetchData = async () => {
await trafficFiltersRequest.getAdvancedFiltersForTraffic().then((resp) => {
Expand All @@ -29,6 +32,9 @@ function AdvancedTrafficFilters() {
}

})
await trafficFiltersRequest.getAdvancedFiltersPermissions().then((resp) => {
setPermissionsMap(resp)
})
}

useEffect(() => {
Expand All @@ -47,6 +53,14 @@ function AdvancedTrafficFilters() {

}

const handleDryRun = async(content) => {
if(window.IS_SAAS === "false"){
await trafficFiltersRequest.dryRunAdvancedFilters(content).then((res)=> {
window.open("/dashboard/settings/logs", "_blank")
})
}
}

const resetFunc = () => {
setCurrentTemplate({message: ''});
setOgData({message: ''});
Expand Down Expand Up @@ -81,13 +95,50 @@ function AdvancedTrafficFilters() {
setCurrentState(state)
}

const handleCheckboxClicked = async(permission, value) => {
await trafficFiltersRequest.updateAdvancedFiltersPermissions(value, permission).then((res) => {
setPermissionsMap((prev) => {
prev[permission] = value
return {...prev}
})
})
}

const tooltipText= currentState ? "Mark as Active" : "Mark as Deactive"

const titleComp = (
<HorizontalStack align="space-between">
<Text variant="headingSm">Add or modify filters</Text>
<Popover
activator={<Button disclosure size="slim" onClick={() => setPopoverActive(!popoverActive)}>Actions</Button>}
active={popoverActive}
onClose={() => setPopoverActive(false)}
autofocusTarget="container"
>
<Popover.Section>
<VerticalStack gap={"2"}>
<Checkbox
checked={permissionsMap['allowFilterLogs']}
label="Allow filtered urls in logs"
onChange={() => handleCheckboxClicked('allowFilterLogs', !permissionsMap['allowFilterLogs'])}
/>
<Checkbox
label="Allow retrospective deletion"
checked={permissionsMap['allowDeletionOfUrls']}
onChange={() => handleCheckboxClicked('allowDeletionOfUrls', !permissionsMap['allowDeletionOfUrls'])}
/>
</VerticalStack>
</Popover.Section>
</Popover>
</HorizontalStack>
)

return(
<>
<LegacyCard
title={(<Text variant="headingSm">Add or modify filters</Text>)}
title={titleComp}
footerActionAlignment="right"
primaryFooterAction={{content: 'Save', onAction: () => handleSave(currentTemplate),
primaryFooterAction={{content: 'Save', onAction: () => setModalActive(true),
disabled: (currentTemplate?.message !== undefined && currentTemplate.message.length === 0) || (typeof (currentTemplate) === 'string' && currentTemplate.length === 0)
}}
secondaryFooterActions={[{content: 'Add new', onAction: () => resetFunc()}]}
Expand Down Expand Up @@ -129,6 +180,21 @@ function AdvancedTrafficFilters() {
</Box>
</LegacyCard.Section>
</LegacyCard>
<Modal
open={modalActive}
onClose={() => setModalActive(false)}
primaryAction={{content: 'Save', onAction: () => handleSave(currentTemplate)}}
secondaryActions={window.IS_SAAS !== "true" ? [{content: 'Dry run', onAction: () => handleDryRun(currentTemplate)}]: []}
title={"Add advanced filters"}
>
<Modal.Section>
<Text variant="bodyMd" color="subdued">
Adding an filter will stop/modify traffic ingestion in the dashboard.
Are you sure you want to add the filter?
</Text>
</Modal.Section>
</Modal>
</>
)
}

Expand Down
Loading
Loading