in public/apps/configuration/panels/role-view/role-view.tsx [93:413]
export function RoleView(props: RoleViewProps) {
const duplicateRoleLink = buildHashUrl(ResourceType.roles, Action.duplicate, props.roleName);
const [mappedUsers, setMappedUsers] = React.useState<MappedUsersListing[]>([]);
const [errorFlag, setErrorFlag] = React.useState(false);
const [selection, setSelection] = useState<MappedUsersListing[]>([]);
const [hosts, setHosts] = React.useState<string[]>([]);
const [actionGroupDict, setActionGroupDict] = React.useState<DataObject<ActionGroupItem>>({});
const [roleClusterPermission, setRoleClusterPermission] = useState<string[]>([]);
const [roleIndexPermission, setRoleIndexPermission] = React.useState<RoleIndexPermissionView[]>(
[]
);
const [roleTenantPermission, setRoleTenantPermission] = React.useState<
RoleTenantPermissionView[]
>([]);
const [toasts, addToast, removeToast] = useToastState();
const [isReserved, setIsReserved] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const PERMISSIONS_TAB_INDEX = 0;
const MAP_USER_TAB_INDEX = 1;
React.useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const originalRoleMapData = await getRoleMappingData(props.coreStart.http, props.roleName);
if (originalRoleMapData) {
setMappedUsers(transformRoleMappingData(originalRoleMapData));
setHosts(originalRoleMapData.hosts);
}
const actionGroups = await fetchActionGroups(props.coreStart.http);
setActionGroupDict(actionGroups);
const roleData = await getRoleDetail(props.coreStart.http, props.roleName);
setIsReserved(roleData.reserved);
setRoleClusterPermission(roleData.cluster_permissions);
setRoleIndexPermission(transformRoleIndexPermissions(roleData.index_permissions));
setRoleTenantPermission(transformRoleTenantPermissions(roleData.tenant_permissions));
} catch (e) {
addToast(createUnknownErrorToast('fetchRoleMappingData', 'load data'));
console.log(e);
setErrorFlag(true);
} finally {
setLoading(false);
}
};
fetchData();
}, [addToast, props.coreStart.http, props.roleName, props.prevAction]);
const handleRoleMappingDelete = async () => {
try {
const usersToDelete: string[] = selection.map((r) => r.userName);
const internalUsers: string[] = mappedUsers
.filter((r) => r.userType === UserType.internal)
.map((r) => r.userName);
const externalIdentities: string[] = mappedUsers
.filter((r) => r.userType === UserType.external)
.map((r) => r.userName);
const updateObject: RoleMappingDetail = {
users: difference(internalUsers, usersToDelete),
backend_roles: difference(externalIdentities, usersToDelete),
hosts,
};
await updateRoleMapping(props.coreStart.http, props.roleName, updateObject);
setMappedUsers(difference(mappedUsers, selection));
setSelection([]);
} catch (e) {
console.log(e);
}
};
const [showDeleteConfirmModal, deleteConfirmModal] = useDeleteConfirmState(
handleRoleMappingDelete,
'mapping(s)'
);
const emptyListMessage = (
<EuiEmptyPrompt
title={<h2>No user has been mapped to this role</h2>}
titleSize="s"
body={
<EuiText size="s" color="subdued" grow={false}>
<p>You can map users or backend roles to this role</p>
</EuiText>
}
actions={
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem>
<ExternalLinkButton
text="Create internal user"
href={buildHashUrl(ResourceType.users, Action.create)}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton
data-test-subj="map-users"
fill
onClick={() => {
window.location.href = buildHashUrl(
ResourceType.roles,
Action.edit,
props.roleName,
SubAction.mapuser
);
}}
>
Map users
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
}
/>
);
const tabs = [
{
id: 'permissions',
name: 'Permissions',
disabled: false,
content: (
<>
<EuiSpacer size="m" />
{isReserved && (
<EuiCallOut
title="This role is reserved for the Security plugin environment. Reserved roles are restricted for any permission customizations."
iconType="lock"
size="s"
>
<p>
Make use of this role by mapping users. You can also{' '}
<EuiLink href={buildHashUrl(ResourceType.roles, Action.create)}>
create your own role
</EuiLink>{' '}
or <EuiLink href={duplicateRoleLink}>duplicate</EuiLink> this one for further
customization.
</p>
</EuiCallOut>
)}
<EuiSpacer size="m" />
<ClusterPermissionPanel
roleName={props.roleName}
clusterPermissions={roleClusterPermission}
actionGroups={actionGroupDict}
loading={loading}
isReserved={isReserved}
/>
<EuiSpacer size="m" />
<IndexPermissionPanel
roleName={props.roleName}
indexPermissions={roleIndexPermission}
actionGroups={actionGroupDict}
errorFlag={errorFlag}
loading={loading}
isReserved={isReserved}
/>
<EuiSpacer size="m" />
<TenantsPanel
roleName={props.roleName}
tenantPermissions={roleTenantPermission}
errorFlag={errorFlag}
coreStart={props.coreStart}
loading={loading}
isReserved={isReserved}
/>
</>
),
},
{
id: 'users',
name: 'Mapped users',
disabled: false,
content: (
<>
<EuiSpacer />
<EuiPageContent>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle size="s">
<h3>
Mapped users
<span className="panel-header-count"> ({mappedUsers.length})</span>
</h3>
</EuiTitle>
<EuiText size="xs" color="subdued" className="panel-header-subtext">
You can map two types of users: users and backend roles. A user can have its own
backend role and host for an external authentication and authorization. A backend
role directly maps to roles through an external authentication system.{' '}
<ExternalLink href={DocLinks.MapUsersToRolesDoc} />
</EuiText>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiFlexGroup>
<EuiFlexItem>
<EuiButton onClick={showDeleteConfirmModal} disabled={selection.length === 0}>
Delete mapping
</EuiButton>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton
data-test-subj="manage-mapping"
onClick={() => {
window.location.href = buildHashUrl(
ResourceType.roles,
Action.edit,
props.roleName,
SubAction.mapuser
);
}}
>
Manage mapping
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiHorizontalRule margin="s" />
<EuiPageBody>
<EuiInMemoryTable
data-test-subj="role-mapping-list"
tableLayout={'auto'}
loading={mappedUsers === [] && !errorFlag}
columns={mappedUserColumns}
items={mappedUsers}
itemId={'userName'}
pagination={true}
message={showTableStatusMessage(loading, mappedUsers, emptyListMessage)}
selection={{ onSelectionChange: setSelection }}
sorting={true}
error={
errorFlag ? 'Load data failed, please check console log for more detail.' : ''
}
/>
</EuiPageBody>
</EuiPageContent>
</>
),
},
];
let pageActions;
const actionsMenuItems: React.ReactElement[] = [
<EuiButtonEmpty key="duplicate" href={duplicateRoleLink}>
duplicate
</EuiButtonEmpty>,
<EuiButtonEmpty
data-test-subj="delete"
key="delete"
color="danger"
onClick={async () => {
try {
await requestDeleteRoles(props.coreStart.http, [props.roleName]);
setCrossPageToast(buildUrl(ResourceType.roles), {
id: 'deleteRole',
color: 'success',
title: props.roleName + ' deleted.',
});
window.location.href = buildHashUrl(ResourceType.roles);
} catch (e) {
addToast(createUnknownErrorToast('deleteRole', 'delete role'));
}
}}
>
delete
</EuiButtonEmpty>,
];
const [actionsMenu] = useContextMenuState('Actions', {}, actionsMenuItems);
if (isReserved) {
pageActions = <EuiButton href={duplicateRoleLink}>Duplicate role</EuiButton>;
} else {
pageActions = (
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>{actionsMenu}</EuiFlexItem>
<EuiFlexItem>
<EuiButton href={buildHashUrl(ResourceType.roles, Action.edit, props.roleName)}>
Edit role
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
);
}
return (
<>
{props.buildBreadcrumbs(props.roleName)}
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle size="l">
<h1>{props.roleName}</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>{pageActions}</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiTabbedContent
tabs={tabs}
initialSelectedTab={
props.prevAction === SubAction.mapuser
? tabs[MAP_USER_TAB_INDEX]
: tabs[PERMISSIONS_TAB_INDEX]
}
/>
<EuiSpacer />
<EuiGlobalToastList toasts={toasts} toastLifeTimeMs={10000} dismissToast={removeToast} />
{deleteConfirmModal}
</>
);
}