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

Show aliases in data source options for detector and correlation rule creation #864

Merged
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
12 changes: 9 additions & 3 deletions cypress/integration/1_detectors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@ const validatePendingFieldMappingsPanel = (mappings) => {
});
};

const fillDetailsForm = (detectorName, dataSource) => {
const fillDetailsForm = (detectorName, dataSource, isCustomDataSource = false) => {
getNameField().type(detectorName);
getDataSourceField().selectComboboxItem(dataSource);
if (isCustomDataSource) {
getDataSourceField()
.focus()
.type(dataSource + '{enter}');
} else {
getDataSourceField().selectComboboxItem(dataSource);
}
getDataSourceField().focus().blur();
getLogTypeField().selectComboboxItem(getLogTypeLabel(cypressLogTypeDns));
getLogTypeField().focus().blur();
Expand All @@ -124,7 +130,7 @@ const fillDetailsForm = (detectorName, dataSource) => {
const createDetector = (detectorName, dataSource, expectFailure) => {
getCreateDetectorButton().click({ force: true });

fillDetailsForm(detectorName, dataSource);
fillDetailsForm(detectorName, dataSource, expectFailure);

cy.getElementByText('.euiAccordion .euiTitle', 'Selected detection rules (14)')
.click({ force: true, timeout: 5000 })
Expand Down
35 changes: 14 additions & 21 deletions public/pages/Correlations/containers/CreateCorrelationRule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { CoreServicesContext } from '../../../components/core_services';
import { RouteComponentProps, useParams } from 'react-router-dom';
import { validateName } from '../../../utils/validation';
import { FieldMappingService, IndexService } from '../../../services';
import { errorNotificationToast, getLogTypeOptions } from '../../../utils/helpers';
import { errorNotificationToast, getDataSources, getLogTypeOptions } from '../../../utils/helpers';

export interface CreateCorrelationRuleProps {
indexService: IndexService;
Expand All @@ -56,9 +56,11 @@ export interface CreateCorrelationRuleProps {
notifications: NotificationsStart | null;
}

export interface CorrelationOptions {
export interface CorrelationOption {
label: string;
value: string;
value?: string;
index?: string;
options?: CorrelationOption[];
}

const parseTime = (time: number) => {
Expand Down Expand Up @@ -87,9 +89,9 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
props: CreateCorrelationRuleProps
) => {
const correlationStore = DataStore.correlations;
const [indices, setIndices] = useState<CorrelationOptions[]>([]);
const [indices, setIndices] = useState<CorrelationOption[]>([]);
const [logFieldsByIndex, setLogFieldsByIndex] = useState<{
[index: string]: CorrelationOptions[];
[index: string]: CorrelationOption[];
}>({});
const params = useParams<{ ruleId: string }>();
const [initialValues, setInitialValues] = useState({
Expand Down Expand Up @@ -229,26 +231,14 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
};

const context = useContext(CoreServicesContext);
const parseOptions = (indices: string[]) => {
return indices.map(
(index: string): CorrelationOptions => ({
label: index,
value: index,
})
);
};

const getIndices = useCallback(async () => {
try {
const indicesResponse = await props.indexService.getIndices();
if (indicesResponse.ok) {
const indicesNames = parseOptions(
indicesResponse.response.indices.map((index) => index.index)
);
setIndices(indicesNames);
const dataSourcesRes = await getDataSources(props.indexService, props.notifications);
if (dataSourcesRes.ok) {
setIndices(dataSourcesRes.dataSources);
}
} catch (error: any) {}
}, [props.indexService.getIndices]);
}, [props.indexService, props.notifications]);

useEffect(() => {
getIndices();
Expand Down Expand Up @@ -423,6 +413,9 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
);
updateLogFieldsForIndex(e[0]?.value || '');
}}
renderOption={(option: CorrelationOption) => {
return option.index ? `${option.label} (${option.index})` : option.label;
}}
onBlur={props.handleBlur(`queries[${queryIdx}].index`)}
selectedOptions={
query.index ? [{ value: query.index, label: query.index }] : []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export const DetectionRules: React.FC<DetectionRulesProps> = ({
</EuiText>
</div>
}
extraAction={
<EuiButton href={`#${ROUTES.RULES}`} target="_blank">
Manage <EuiIcon type={'popout'} />
</EuiButton>
}
id={'detectorRulesAccordion'}
initialIsOpen={false}
isLoading={loading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { IndexOption } from '../../../../../Detectors/models/interfaces';
import { MIN_NUM_DATA_SOURCES } from '../../../../../Detectors/utils/constants';
import IndexService from '../../../../../../services/IndexService';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { errorNotificationToast } from '../../../../../../utils/helpers';
import { getDataSources } from '../../../../../../utils/helpers';
import _ from 'lodash';
import { FieldMappingService } from '../../../../../../services';

Expand Down Expand Up @@ -57,41 +57,28 @@ export default class DetectorDataSource extends Component<
}

componentDidMount = async () => {
this.getIndices();
this.getDataSources();
};

getIndices = async () => {
getDataSources = async () => {
this.setState({ loading: true });
try {
const indicesResponse = await this.props.indexService.getIndices();
if (indicesResponse.ok) {
const indices = indicesResponse.response.indices;
const indicesNames = indices.map((index) => index.index);

this.setState({
loading: false,
indexOptions: this.parseOptions(indicesNames),
});
} else {
errorNotificationToast(
this.props.notifications,
'retrieve',
'indices',
indicesResponse.error
);
this.setState({ errorMessage: indicesResponse.error });
}
} catch (error: any) {
errorNotificationToast(this.props.notifications, 'retrieve', 'indices', error);
const res = await getDataSources(this.props.indexService, this.props.notifications);

if (res.ok) {
this.setState({
loading: false,
indexOptions: res.dataSources,
});
} else {
this.setState({ loading: false, errorMessage: res.error });
}
this.setState({ loading: false });
};

parseOptions = (indices: string[]) => {
return indices.map((index) => ({ label: index }));
parseOptions = (options: string[]) => {
return options.map((option) => ({ label: option }));
};

onCreateOption = (searchValue: string, options: EuiComboBoxOptionOption[]) => {
onCreateOption = (searchValue: string) => {
const parsedOptions = this.parseOptions(this.props.detectorIndices);
parsedOptions.push({ label: searchValue });
this.onSelectionChange(parsedOptions);
Expand Down Expand Up @@ -172,6 +159,9 @@ export default class DetectorDataSource extends Component<
isInvalid={!!errorMessage}
isClearable={true}
data-test-subj={'define-detector-select-data-source'}
renderOption={(option: IndexOption) => {
return option.index ? `${option.label} (${option.index})` : option.label;
}}
/>
</EuiFormRow>
{differentLogTypesDetected ? (
Expand Down
1 change: 1 addition & 0 deletions public/pages/Detectors/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

export interface IndexOption {
label: string;
index?: string;
}
9 changes: 8 additions & 1 deletion public/services/IndexService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { HttpSetup } from 'opensearch-dashboards/public';
import { ServerResponse } from '../../server/models/types';
import { GetIndicesResponse } from '../../server/models/interfaces';
import { GetAliasesResponse, GetIndicesResponse } from '../../server/models/interfaces';
import { API } from '../../server/utils/constants';
import { IIndexService } from '../../types';

Expand All @@ -32,6 +32,13 @@ export default class IndexService implements IIndexService {
return response;
};

getAliases = async (): Promise<ServerResponse<GetAliasesResponse>> => {
const url = `..${API.ALIASES_BASE}`;
const response = (await this.httpClient.get(url)) as ServerResponse<GetAliasesResponse>;

return response;
};

updateAliases = async (actions: any): Promise<ServerResponse<{}>> => {
const url = `..${API.UPDATE_ALIASES}`;
const response = (await this.httpClient.post(url, {
Expand Down
66 changes: 65 additions & 1 deletion public/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { parse, View } from 'vega/build-es5/vega.js';
import { expressionInterpreter as vegaExpressionInterpreter } from 'vega-interpreter/build/vega-interpreter';
import { RuleInfo } from '../../server/models/interfaces';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { OpenSearchService } from '../services';
import { IndexService, OpenSearchService } from '../services';
import { ruleSeverity, ruleTypes } from '../pages/Rules/utils/constants';
import { Handler } from 'vega-tooltip';
import _ from 'lodash';
Expand Down Expand Up @@ -432,3 +432,67 @@ export function addDetectionType(
export function isThreatIntelQuery(queryId: string) {
return queryId?.startsWith('threat_intel_');
}

export async function getDataSources(
indexService: IndexService,
notifications: any
): Promise<
| {
ok: true;
dataSources: { label: string; options: { label: string; value: string; index?: string }[] }[];
}
| { ok: false; error: string }
> {
const dataSourceOptions = [];
try {
const aliasesResponse = await indexService.getAliases();
const indicesResponse = await indexService.getIndices();

if (aliasesResponse.ok) {
const aliases = aliasesResponse.response.aliases.filter(
({ index }) => !index.startsWith('.')
);
const aliasOptions = aliases.map(({ alias, index }) => ({
label: alias,
index: index,
value: alias,
}));

dataSourceOptions.push({
label: 'Aliases',
options: aliasOptions,
});
} else {
errorNotificationToast(notifications, 'retrieve', 'aliases', aliasesResponse.error);
return { ok: false, error: aliasesResponse.error };
}

if (indicesResponse.ok) {
const indices = indicesResponse.response.indices;
const indexOptions = indices
.map(({ index }) => ({ label: index, value: index }))
.filter(({ label }) => !label.startsWith('.'));

dataSourceOptions.push({
label: 'Indices',
options: indexOptions,
});
} else {
errorNotificationToast(notifications, 'retrieve', 'indices', indicesResponse.error);

return { ok: false, error: indicesResponse.error };
}

return {
ok: true,
dataSources: dataSourceOptions,
};
} catch (error: any) {
errorNotificationToast(notifications, 'retrieve', 'indices', error);

return {
ok: false,
error,
};
}
}
10 changes: 10 additions & 0 deletions server/models/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface SecurityAnalyticsApi {
readonly CORRELATION_BASE: string;
readonly SEARCH_DETECTORS: string;
readonly INDICES_BASE: string;
readonly ALIASES_BASE: string;
readonly FINDINGS_BASE: string;
readonly GET_FINDINGS: string;
readonly DOCUMENT_IDS_QUERY: string;
Expand Down Expand Up @@ -54,6 +55,10 @@ export interface GetIndicesResponse {
indices: CatIndex[];
}

export interface GetAliasesResponse {
aliases: CatAlias[];
}

// Default _cat index response
export interface CatIndex {
'docs.count': string;
Expand All @@ -69,6 +74,11 @@ export interface CatIndex {
data_stream: string | null;
}

export interface CatAlias {
alias: string;
index: string;
}

export interface SearchResponse<T> {
hits: {
total: { value: number };
Expand Down
8 changes: 8 additions & 0 deletions server/routes/IndexRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export function setupIndexRoutes(services: NodeServices, router: IRouter) {
indexService.getIndices
);

router.get(
{
path: API.ALIASES_BASE,
validate: {},
},
indexService.getAliases
);

router.post(
{
path: `${API.INDICES_BASE}`,
Expand Down
Loading
Loading