/*
* Copyright OpenSearch Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file 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 {
EuiAccordion,
EuiButton,
EuiComboBox,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiSuperSelect,
EuiTextArea,
} from '@elastic/eui';
import React, { Dispatch, Fragment, SetStateAction } from 'react';
import { isEmpty } from 'lodash';
import { RoleIndexPermission, ResourceType } from '../../types';
import {
appendElementToArray,
removeElementFromArray,
updateElementInArrayHandler,
} from '../../utils/array-state-utils';
import {
appendOptionToComboBoxHandler,
comboBoxOptionToString,
stringToComboBoxOption,
} from '../../utils/combo-box-utils';
import { FormRow } from '../../utils/form-row';
import { PanelWithHeader } from '../../utils/panel-with-header';
import { RoleIndexPermissionStateClass } from './types';
import { FieldLevelSecurityMethod, ComboBoxOptions } from '../../types';
import { getFieldLevelSecurityMethod } from '../../utils/index-permission-utils';
import { LIMIT_WIDTH_INPUT_CLASS } from '../../constants';
import { buildHashUrl } from '../../utils/url-builder';
import { ExternalLinkButton } from '../../utils/display-utils';
import { DocLinks } from '../../constants';
export function getEmptyIndexPermission(): RoleIndexPermissionStateClass {
return {
indexPatterns: [],
allowedActions: [],
docLevelSecurity: '',
fieldLevelSecurityMethod: 'exclude',
fieldLevelSecurityFields: [],
maskedFields: [],
};
}
/**
* Remove the leading ~ which indicates exclude and convert to combo box option.
* @param fieldLevelSecurityRawFields fields fetched from backend
* ["~field1", "~field2"] => ["field1", "field2"]
* ["field1", "field2"] => ["field1", "field2"]
*/
function getFieldLevelSecurityFields(fieldLevelSecurityRawFields: string[]): ComboBoxOptions {
return fieldLevelSecurityRawFields
.map((s: string) => s.replace(/^~/, ''))
.map(stringToComboBoxOption);
}
function packFieldLevelSecurity(method: FieldLevelSecurityMethod, fieldOptions: ComboBoxOptions) {
const fields = fieldOptions.map(comboBoxOptionToString);
if (method === 'include') {
return fields;
}
return fields.map((field) => '~' + field);
}
export function buildIndexPermissionState(
indexPerm: RoleIndexPermission[]
): RoleIndexPermissionStateClass[] {
return indexPerm.map((perm) => ({
indexPatterns: perm.index_patterns.map(stringToComboBoxOption),
allowedActions: perm.allowed_actions.map(stringToComboBoxOption),
docLevelSecurity: perm.dls,
fieldLevelSecurityMethod: getFieldLevelSecurityMethod(perm.fls),
fieldLevelSecurityFields: getFieldLevelSecurityFields(perm.fls),
maskedFields: perm.masked_fields.map(stringToComboBoxOption),
}));
}
export function unbuildIndexPermissionState(
indexPerm: RoleIndexPermissionStateClass[]
): RoleIndexPermission[] {
return indexPerm.map((perm) => ({
index_patterns: perm.indexPatterns.map(comboBoxOptionToString),
dls: perm.docLevelSecurity,
fls: packFieldLevelSecurity(perm.fieldLevelSecurityMethod, perm.fieldLevelSecurityFields),
masked_fields: perm.maskedFields.map(comboBoxOptionToString),
allowed_actions: perm.allowedActions.map(comboBoxOptionToString),
}));
}
const FIELD_LEVEL_SECURITY_PLACEHOLDER = `{
"bool": {
"must": {
"match": {
"genres": "Comedy"
}
}
}
}`;
export function IndexPatternRow(props: {
value: ComboBoxOptions;
onChangeHandler: (s: ComboBoxOptions) => void;
onCreateHandler: (s: string) => void;
}) {
return (
);
}
export function IndexPermissionRow(props: {
value: ComboBoxOptions;
permisionOptionsSet: ComboBoxOptions;
onChangeHandler: (s: ComboBoxOptions) => void;
}) {
return (
{/* TODO: 'Browse and select' button with a pop-up modal for selection */}
);
}
export function DocLevelSecurityRow(props: {
value: string;
onChangeHandler: (e: React.ChangeEvent) => void;
}) {
return (
);
}
export function FieldLevelSecurityRow(props: {
method: FieldLevelSecurityMethod;
fields: ComboBoxOptions;
onMethodChangeHandler: (s: string) => void;
onFieldChangeHandler: (s: ComboBoxOptions) => void;
onFieldCreateHandler: (s: string) => void;
}) {
return (
);
}
export function AnonymizationRow(props: {
value: ComboBoxOptions;
onChangeHandler: (s: ComboBoxOptions) => void;
onCreateHandler: (s: string) => void;
}) {
return (
);
}
export function generateIndexPermissionPanels(
indexPermissions: RoleIndexPermissionStateClass[],
permisionOptionsSet: ComboBoxOptions,
setRoleIndexPermission: Dispatch>
) {
const panels = indexPermissions.map((permission, arrayIndex) => {
const onValueChangeHandler = (attributeToUpdate: string) =>
updateElementInArrayHandler(setRoleIndexPermission, [arrayIndex, attributeToUpdate]);
const onCreateOptionHandler = (attributeToUpdate: string) =>
appendOptionToComboBoxHandler(setRoleIndexPermission, [arrayIndex, attributeToUpdate]);
return (
removeElementFromArray(setRoleIndexPermission, [], arrayIndex)}
>
Remove
}
>
onValueChangeHandler('docLevelSecurity')(e.target.value)}
/>
);
});
return <>{panels}>;
}
export function IndexPermissionPanel(props: {
state: RoleIndexPermissionStateClass[];
optionUniverse: ComboBoxOptions;
setState: Dispatch>;
}) {
const { state, optionUniverse, setState } = props;
// Show one empty row if there is no data.
if (isEmpty(state)) {
setState([getEmptyIndexPermission()]);
}
return (
{generateIndexPermissionPanels(state, optionUniverse, setState)}
{
appendElementToArray(setState, [], getEmptyIndexPermission());
}}
>
Add another index permission
);
}