UNPKG

@azure/msal-node-extensions

Version:

![npm (scoped)](https://img.shields.io/npm/v/@azure/msal-node-extensions) ![npm](https://img.shields.io/npm/dw/@azure/msal-node-extensions)

201 lines (198 loc) 7.08 kB
/*! @azure/msal-node-extensions v5.3.2 2026-07-07 */ 'use strict'; import { promises } from 'fs'; import { dirname } from 'path'; import { Constants, ErrorCodes } from '../utils/Constants.mjs'; import { PersistenceError } from '../error/PersistenceError.mjs'; import { BasePersistence } from './BasePersistence.mjs'; import { isNodeError } from '../utils/TypeGuards.mjs'; import { Logger, LogLevel } from '../lib/msal-common/dist/logger/Logger.mjs'; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Reads and writes data to file specified by file location. File contents are not * encrypted. * * If file or directory has not been created, it FilePersistence.create() will create * file and any directories in the path recursively. */ /** Owner read/write only (0o600) */ const FILE_MODE = 0o600; /** Owner read/write/execute only (0o700) */ const DIR_MODE = 0o700; class FilePersistence extends BasePersistence { constructor(fileLocation, loggerOptions) { super(); this.logger = new Logger(loggerOptions || FilePersistence.createDefaultLoggerOptions()); this.filePath = fileLocation; } static async create(fileLocation, loggerOptions) { const filePersistence = new FilePersistence(fileLocation, loggerOptions); await filePersistence.createCacheFile(); return filePersistence; } async save(contents) { try { // Tighten permissions before writing to close TOCTOU window on existing files await promises.chmod(this.getFilePath(), FILE_MODE).catch((err) => { if (!isNodeError(err) || err.code !== Constants.ENOENT_ERROR) { throw err; } }); await promises.writeFile(this.getFilePath(), contents, { encoding: "utf-8", mode: FILE_MODE, }); await promises.chmod(this.getFilePath(), FILE_MODE); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async saveBuffer(contents) { try { // Tighten permissions before writing to close TOCTOU window on existing files await promises.chmod(this.getFilePath(), FILE_MODE).catch((err) => { if (!isNodeError(err) || err.code !== Constants.ENOENT_ERROR) { throw err; } }); await promises.writeFile(this.getFilePath(), contents, { mode: FILE_MODE, }); await promises.chmod(this.getFilePath(), FILE_MODE); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async load() { try { return await promises.readFile(this.getFilePath(), "utf-8"); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async loadBuffer() { try { return await promises.readFile(this.getFilePath()); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async delete() { try { await promises.unlink(this.getFilePath()); return true; } catch (err) { if (isNodeError(err)) { if (err.code === Constants.ENOENT_ERROR) { // file does not exist, so it was not deleted this.logger.warning("Cache file does not exist, so it could not be deleted", ""); return false; } throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } getFilePath() { return this.filePath; } async reloadNecessary(lastSync) { return lastSync < (await this.timeLastModified()); } getLogger() { return this.logger; } createForPersistenceValidation() { const testCacheFileLocation = `${dirname(this.filePath)}/test.cache`; return FilePersistence.create(testCacheFileLocation); } static createDefaultLoggerOptions() { return { loggerCallback: () => { // allow users to not set loggerCallback }, piiLoggingEnabled: false, logLevel: LogLevel.Info, }; } async timeLastModified() { try { const stats = await promises.stat(this.filePath); return stats.mtime.getTime(); } catch (err) { if (isNodeError(err)) { if (err.code === Constants.ENOENT_ERROR) { // file does not exist, so it's never been modified this.logger.verbose("Cache file does not exist", ""); return 0; } throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async createCacheFile() { await this.createFileDirectory(); // File is created only if it does not exist const fileHandle = await promises.open(this.filePath, "a", FILE_MODE); await fileHandle.close(); await promises.chmod(this.filePath, FILE_MODE); this.logger.info(`File created at ${this.filePath}`, ""); } async createFileDirectory() { try { await promises.mkdir(dirname(this.filePath), { recursive: true, mode: DIR_MODE, }); } catch (err) { if (isNodeError(err)) { if (err.code === Constants.EEXIST_ERROR) { this.logger.info(`Directory ${dirname(this.filePath)} already exists`, ""); } else { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } } else { throw err; } } } } export { FilePersistence }; //# sourceMappingURL=FilePersistence.mjs.map