Sharepoint Audit List Permissions batch

On 17/10/2025 0

In SharepointOnLine


const siteUrl = _spPageContextInfo.webAbsoluteUrl;
const listUrl = "Shared%20Documents";


function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}


async function generateGuid() {
    await sleep(10);// * trynum
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        const r = Math.random() * 16 | 0;
        const v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}
async function getRequestDigest() {

    const fetchOptions = {
        method: "POST",
        headers: { Accept: "application/json;odata=verbose" },
        credentials: "include",
    };
    const data = (await (await ExecuteQuery(`${siteUrl}/_api/contextinfo`, fetchOptions)).json());
    return data.d.GetContextWebInformation.FormDigestValue;
}

async function ExecuteQuery(req, fetchOptions, maxRetry = 3, wait = 10000, trynum = 1) {

    console.log("ExecuteQuery", req, fetchOptions, maxRetry, wait, trynum);
    if (trynum >= maxRetry) {
        console.log("ExecuteQuery Error", req);
        console.log("ExecuteQuery Error", fetchOptions);
        throw new Error(`ExecuteQuery error! maxRetry >= trynum`);
    }
    try {
        let startDate = new Date();
        let diffMinutes = 0;
        respList1 = await fetch(req, fetchOptions);
        endDate = new Date();
        const diffMs = endDate - startDate;
        diffMinutes = Math.floor(diffMs / (1000));
        console.log(`seconds ${diffMinutes} queryNumber  ${trynum}`)

        //avoid 429 too much queries
        if (!respList1.ok && respList1.status == 429) {
            const errorDetails = await respList1.text(); // Get error details from the response
            let err = JSON.parse(errorDetails);
            console.error(`HTTP error! Status: ${respList1.status}, Details: ${err.error.message.value}`);
            await sleep(wait * 2);// * trynum
            if (fetchOptions.method === "POST") {
                //regnerate the digest
                fetchOptions["X-RequestDigest"] = await getRequestDigest();
            }
            return await ExecuteQuery(req, fetchOptions, maxRetry, (wait * 5), ++trynum);
        }

        //avoid 503 server unavailable / connections error
        if (!respList1.ok && respList1.status == 503) {
            const errorDetails = await respList1.text(); // Get error details from the response
            let err = JSON.parse(errorDetails);
            console.error(`HTTP error! Status: ${respList1.status}, Details: ${err.error.message.value}`);
            await sleep(1000 * wait);// wait 10 seconds
            if (fetchOptions.method === "POST") {
                //regnerate the digest
                fetchOptions["X-RequestDigest"] = await getRequestDigest();
            }
            return await ExecuteQuery(req, fetchOptions, maxRetry, (wait * 5), ++trynum);
        }

        //avoid 403 reload page in another tab
        if (!respList1.ok && respList1.status == 403) {
            //debugger;
            window.open(siteUrl, '_blank')
            const errorDetails = await respList1.text(); // Get error details from the response
            let err = JSON.parse(errorDetails);
            console.error(`HTTP error! Status: ${respList1.status}, Details: ${err.error.message.value}`);
            console.log("waiting due to error 403");
            await sleep(10000);// wait 10 seconds
            window.open(siteUrl, '_blank')
            await sleep(1000 * wait);// wait 10 seconds
            if (fetchOptions.method === "POST") {
                //regnerate the digest
                fetchOptions["X-RequestDigest"] = await getRequestDigest();
            }
            return await ExecuteQuery(req, fetchOptions, maxRetry, (wait * 5), ++trynum);
        }

        if (!respList1.ok) {
            console.log("ExecuteQuery Error", respList1);
            const errorDetails = await respList1.text(); // Get error details from the response
            console.log("ExecuteQuery Error", respList1);
            console.error(`HTTP error! Status: ${respList1.status}, Details: ${errorDetails}`);
            let err = JSON.parse(errorDetails);
            console.error(`HTTP error! Status: ${respList1.status}, Details: ${err.error.message.value}`);
            console.log(err.error.message.value);
            throw new Error(`HTTP error! Status: ${respList1.status}`);
        }

        return respList1

    } catch (error) {
        console.log(error);
        throw new Error(`HTTP error! Status: ${error}`);
    }
}

async function GetFolderSize(siteUrl, listUrl1, query) {
    // Fetch options with headers for authentication and response format
    const fetchOptions = {
        method: 'GET',
        headers: {
            'Accept': 'application/json;odata=verbose'
        }
    };

    //get web relativeUrl
    var req = `${siteUrl}/_api/web?$select=ServerRelativeUrl`;
    const webServerRelativUrl = (await (await ExecuteQuery(req, fetchOptions)).json()).d.ServerRelativeUrl;
    // get total items count 
    req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/?$select=ItemCount`;

    const ItemsCount = (await (await ExecuteQuery(req, fetchOptions)).json()).d.ItemCount;
    let query1 = "";
    if (`${query}`.trim() !== "") {
        query1 = `&$filter=${query}`;
        query = ` and ${query}`;
    }
    //get firstId
    req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items?$select=Id&$top=1&$orderby=Id asc${query1}`;
    console.log("req", req);
    const firstId = (await (await ExecuteQuery(req, fetchOptions)).json()).d.results[0].Id;
    console.log("firstId", firstId);
    //get lastId
    req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items?$select=Id&$top=1&$orderby=Id desc${query1}`;
    console.log("last", req);
    const lastId = (await (await ExecuteQuery(req, fetchOptions)).json()).d.results[0].Id;
    console.log("lastId", lastId);


    let startId = firstId;
    let endId = firstId + 5000;
    var allItems = [];

    console.log(`startId ${startId} endId ${endId} lastId ${lastId}`);//FileSizeDisplay
    console.log("query", query);//_UIVersionString File_x0020_Size
    const startDate = new Date();
    let endDate = new Date();
    let diffMinutes = 0;
    let queryNumber = 1;

    do {
        //to avoid 429 error
        if (queryNumber % 10 == 0) {
            console.log(`sleep 1 minute queryNumber ${queryNumber}`);
            await sleep(60000); // 60 000 ms = 1 minute
        }
        var select = "?$select=File/Length,File/UIVersionLabel,File_x0020_Type,Id,HasUniqueRoleAssignments,FileRef,FileLeafRef,FileDirRef,Created,Modified,Author/Title,Author/EMail,Editor/Title,Editor/EMail,ContentTypeId";
        req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items${select}&$filter=Id ge ${startId} and Id lt ${endId} and Id le ${lastId}${query}&$orderby=Id asc&$top=5000&$expand=File,Author,Editor`;
        console.log("req", req);
        // Send the asynchronous GET request to the REST API endpoint /_api/site/usage
        let respList1 = null;
        try {
            respList1 = await ExecuteQuery(req, fetchOptions);

            endDate = new Date();
            queryNumber++;
        } catch (error) {
            console.log("error", error);
            return allItems;
        }
        const items = (await respList1.json()).d.results;
        allItems.push(...items);
        startId += 5000;
        endId += 5000;
        const diffMs = endDate - startDate;
        diffMinutes = Math.floor(diffMs / (1000 * 60));
        console.log(`startId ${startId} endId ${endId} lastId ${lastId} diffMinutes ${diffMinutes} queryNumber  ${queryNumber}, ItemsCount : ${ItemsCount}`)
        await sleep(1500); //  1,5 second
        //debugger;
    }
    while (startId <= lastId);

    return allItems;
}

async function batchFetchPermissions(queriess) {
    const queries = [...queriess];
    console.log("batchFetchPermissions", queries);
    const batchBoundary = "batch_" + await generateGuid();
    const batchBody = queries.map((query) => {
        return `
--${batchBoundary}
Content-Type: application/http
Content-Transfer-Encoding: binary

GET ${query.req} HTTP/1.1
Accept: application/json;odata=verbose
`;
    }).join("\n") + `\n--${batchBoundary}--`;
    const digest = await getRequestDigest();//
    console.log("batchBody", batchBody.length);
    const headers = {
        Accept: "application/json;odata=verbose",
        "X-RequestDigest": digest,
        "Content-Type": `multipart/mixed; boundary="${batchBoundary}"`,
    };

    const response = await fetch(`${siteUrl}/_api/$batch`, {
        method: "POST",
        headers,
        body: batchBody,
    });

    const text = await response.text(); // Parse response manually (multipart) 
    const responses = text.split("HTTP/1.1 200 OK");
    const results1 = [];
    for (let k = 1; k < responses.length; k++) {
        const parts = responses[k].split("\r\n\r\n")[1];
        // The JSON is the last part after the headers
        let jsonString = parts[parts.length - 1];

        // Parse the JSON
        jsonString = parts.split("\r\n")[0];
        const d = {
            Id: `${queries[k - 1].Id}`,
            Index: k,
            result: JSON.parse(jsonString.trim()).d
        }
        results1.push(d);
    }
    return [...results1];
}
const maxBatch = 20;
async function LoadUniquePermissions(items) {
    let batchCount = 0;
    let requests = [];
    var req = `${siteUrl}/_api/web?$select=ServerRelativeUrl`;
    const fetchOptions = {
        method: 'GET',
        headers: {
            'Accept': 'application/json;odata=verbose'
        }
    };
    const perms = [];
    const webServerRelativUrl = (await (await ExecuteQuery(req, fetchOptions)).json()).d.ServerRelativeUrl;

    for (let i = 0; i < items.length; i++) {
        items[i].permId = await generateGuid();
        if (items[i].HasUniqueRoleAssignments) {
            requests.push({
                Id: items[i].permId,
                req: `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl}')/items(${items[i].Id})/RoleAssignments?$expand=Member,RoleDefinitionBindings&$select=Member/Id,Member/LoginName,Member/Title,RoleDefinitionBindings/Name`
            });
            batchCount++;
        }

        if (requests.length === maxBatch) {
            perms.push(await batchFetchPermissions(requests));
            requests = [];
        }
    }

    if (requests.length > 0) {
        perms.push(await batchFetchPermissions(requests));
        requests = [];
    }

    for (let i = 0; i < items.length; i++) {
        const perm = perms.flat().filter(f => `${f.Id}` == `${items[i].permId}`);
        if (items[i].HasUniqueRoleAssignments && perm.length === 1) {
            items[i].permissions = perm[0].result.results;
        }
    }


    console.log("with perm", items);
    return items;
}
const disgest = await getRequestDigest();
console.log("disgest", disgest);
const items = await GetFolderSize(siteUrl, listUrl, "Id le 145000");//(Id eq 225 or Id eq 226) Id gt 270
const rest = await LoadUniquePermissions(items);


 

Sharepoint REST javascript

No ratings yet - be the first to rate this.

Add a comment

Anti-spam