|
| 1 | +import { Layout as DashboardLayout } from '../../layouts/index.js' |
| 2 | +import { usePermissions } from '../../hooks/use-permissions' |
| 3 | +import { useSettings } from '../../hooks/use-settings' |
| 4 | +import { CippTablePage } from '../../components/CippComponents/CippTablePage.jsx' |
| 5 | +import { NoAccounts } from '@mui/icons-material' |
| 6 | + |
| 7 | +/* |
| 8 | + * Lists SharePoint Online's tenant external users store (live) and classifies every entry: |
| 9 | + * - Entra B2B: backed by a live Entra guest object |
| 10 | + * - Orphaned B2B: the Entra guest was deleted but SharePoint still knows the user |
| 11 | + * - SharePoint-only: legacy email-authenticated guest that never had an Entra object |
| 12 | + */ |
| 13 | +const Page = () => { |
| 14 | + const tenantFilter = useSettings().currentTenant |
| 15 | + const { checkPermissions } = usePermissions() |
| 16 | + const canWriteSite = checkPermissions(['Sharepoint.Site.ReadWrite']) |
| 17 | + |
| 18 | + const actions = [ |
| 19 | + { |
| 20 | + label: 'Remove Guest Access', |
| 21 | + type: 'POST', |
| 22 | + icon: <NoAccounts />, |
| 23 | + url: '/api/ExecRemoveSPOExternalUser', |
| 24 | + customDataformatter: (row) => { |
| 25 | + const r = Array.isArray(row) ? row[0] : row |
| 26 | + return { |
| 27 | + tenantFilter: r.Tenant ?? tenantFilter, |
| 28 | + EntraUserId: r.EntraUserId, |
| 29 | + LoginName: r.LoginName, |
| 30 | + SiteUrls: Array.isArray(r.Sites) ? r.Sites : [], |
| 31 | + DisplayName: r.DisplayName, |
| 32 | + } |
| 33 | + }, |
| 34 | + confirmText: |
| 35 | + 'Fully remove guest access for [DisplayName]? This deletes their Entra guest account (if one exists) AND removes them from every site listed in the Sites column, so nothing is left orphaned. Sharing links they hold can be revoked from the Sharing Report; the inert SharePoint store entry ages out on its own.', |
| 36 | + color: 'error', |
| 37 | + condition: (row) => canWriteSite && (row.InEntra || (row.Sites ?? []).length > 0), |
| 38 | + multiPost: false, |
| 39 | + }, |
| 40 | + ] |
| 41 | + |
| 42 | + const filters = [ |
| 43 | + { |
| 44 | + filterName: 'SharePoint-only', |
| 45 | + value: [{ id: 'GuestType', value: 'SharePoint-only (email authenticated)' }], |
| 46 | + type: 'column', |
| 47 | + }, |
| 48 | + { |
| 49 | + filterName: 'Orphaned B2B', |
| 50 | + value: [{ id: 'GuestType', value: 'Orphaned B2B (not in Entra)' }], |
| 51 | + type: 'column', |
| 52 | + }, |
| 53 | + { |
| 54 | + filterName: 'Entra B2B', |
| 55 | + value: [{ id: 'GuestType', value: 'Entra B2B' }], |
| 56 | + type: 'column', |
| 57 | + }, |
| 58 | + ] |
| 59 | + |
| 60 | + return ( |
| 61 | + <CippTablePage |
| 62 | + title="SharePoint External Users" |
| 63 | + apiUrl="/api/ListSharePointExternalUsers" |
| 64 | + apiDataKey="Results" |
| 65 | + actions={actions} |
| 66 | + filters={filters} |
| 67 | + simpleColumns={[ |
| 68 | + 'DisplayName', |
| 69 | + 'AcceptedAs', |
| 70 | + 'GuestType', |
| 71 | + 'InEntra', |
| 72 | + 'Source', |
| 73 | + 'Sites', |
| 74 | + 'WhenCreated', |
| 75 | + 'InvitedBy', |
| 76 | + ]} |
| 77 | + /> |
| 78 | + ) |
| 79 | +} |
| 80 | + |
| 81 | +Page.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout> |
| 82 | + |
| 83 | +export default Page |
0 commit comments