export function RoleView()

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}
    </>
  );
}