import React, { useEffect, useMemo, useState } from 'react'; import { OrgRole } from '@grafana/data'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; import { Button, ConfirmModal, Icon, Tooltip, CellProps, Tag, InteractiveTable, Column, FetchDataFunc, Pagination, Avatar, } from '@grafana/ui'; import { Flex, Stack, Box } from '@grafana/ui/src/unstable'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { fetchRoleOptions } from 'app/core/components/RolePicker/api'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; import config from 'app/core/config'; import { contextSrv } from 'app/core/core'; import { AccessControlAction, OrgUser, Role } from 'app/types'; import { OrgRolePicker } from '../OrgRolePicker'; import { TableWrapper } from './TableWrapper'; type Cell = CellProps; const disabledRoleMessage = `This user's role is not editable because it is synchronized from your auth provider. Refer to the Grafana authentication docs for details.`; const getBasicRoleDisabled = (user: OrgUser) => { let basicRoleDisabled = !contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersWrite, user); let authLabel = Array.isArray(user.authLabels) && user.authLabels.length > 0 ? user.authLabels[0] : ''; // A GCom specific feature toggle for role locking has been introduced, as the previous implementation had a bug with locking down external users synced through GCom (https://github.com/grafana/grafana/pull/72044) // Remove this conditional once FlagGcomOnlyExternalOrgRoleSync feature toggle has been removed if (authLabel !== 'grafana.com' || config.featureToggles.gcomOnlyExternalOrgRoleSync) { const isUserSynced = user?.isExternallySynced; basicRoleDisabled = isUserSynced || basicRoleDisabled; } return basicRoleDisabled; }; const selectors = e2eSelectors.pages.UserListPage.UsersListPage; export interface Props { users: OrgUser[]; orgId?: number; onRoleChange: (role: OrgRole, user: OrgUser) => void; onRemoveUser: (user: OrgUser) => void; fetchData?: FetchDataFunc; changePage: (page: number) => void; page: number; totalPages: number; } export const OrgUsersTable = ({ users, orgId, onRoleChange, onRemoveUser, fetchData, changePage, page, totalPages, }: Props) => { const [userToRemove, setUserToRemove] = useState(null); const [roleOptions, setRoleOptions] = useState([]); useEffect(() => { async function fetchOptions() { try { if (contextSrv.hasPermission(AccessControlAction.ActionRolesList)) { let options = await fetchRoleOptions(orgId); setRoleOptions(options); } } catch (e) { console.error('Error loading options'); } } if (contextSrv.licensedAccessControlEnabled()) { fetchOptions(); } }, [orgId]); const columns: Array> = useMemo( () => [ { id: 'avatarUrl', header: '', cell: ({ cell: { value } }: Cell<'avatarUrl'>) => value && , }, { id: 'login', header: 'Login', cell: ({ cell: { value } }: Cell<'login'>) =>
{value}
, sortType: 'string', }, { id: 'email', header: 'Email', cell: ({ cell: { value } }: Cell<'email'>) => value, sortType: 'string', }, { id: 'name', header: 'Name', cell: ({ cell: { value } }: Cell<'name'>) => value, sortType: 'string', }, { id: 'lastSeenAtAge', header: 'Last active', cell: ({ cell: { value } }: Cell<'lastSeenAtAge'>) => value, sortType: (a, b) => new Date(a.original.lastSeenAt).getTime() - new Date(b.original.lastSeenAt).getTime(), }, { id: 'role', header: 'Role', cell: ({ cell: { value }, row: { original } }: Cell<'role'>) => { const basicRoleDisabled = getBasicRoleDisabled(original); return contextSrv.licensedAccessControlEnabled() ? ( onRoleChange(newRole, original)} basicRoleDisabled={basicRoleDisabled} basicRoleDisabledMessage={disabledRoleMessage} width={40} /> ) : ( onRoleChange(newRole, original)} /> ); }, }, { id: 'info', header: '', cell: ({ row: { original } }: Cell) => { const basicRoleDisabled = getBasicRoleDisabled(original); return ( basicRoleDisabled && ( This user's role is not editable because it is synchronized from your auth provider. Refer to the  Grafana authentication docs  for details. } > ) ); }, }, { id: 'authLabels', header: 'Origin', cell: ({ cell: { value } }: Cell<'authLabels'>) => ( <>{Array.isArray(value) && value.length > 0 && } ), }, { id: 'isDisabled', header: '', cell: ({ cell: { value } }: Cell<'isDisabled'>) => <>{value && }, }, { id: 'delete', header: '', cell: ({ row: { original } }: Cell) => { return ( contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersRemove, original) && (