Skip to content

Commit

Permalink
Generate tokens just-in-time
Browse files Browse the repository at this point in the history
Auth tokens are now retrieved just-in-time by the permanent file
system (rather than being passed during the creation of the permanent
file system).  This is a critical fix because (1) it prevents certain
paths that would lead to stale tokens but also (2) it means that
creating a permanent file system becomes a synchronous operation.  This
also resolves a bug where the failure to generate a token could result
in a hanging sftp connection.

Issue #288 Permanent file system errors can result in hung connections
  • Loading branch information
slifty committed Nov 7, 2023
1 parent 04c7d3c commit 9a83861
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 412 deletions.
34 changes: 18 additions & 16 deletions src/classes/PermanentFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import type {
Attributes,
FileEntry,
} from 'ssh2';
import type { AuthTokenManager } from './AuthTokenManager';

const isRootPath = (requestedPath: string): boolean => (
requestedPath === '/'
Expand Down Expand Up @@ -79,10 +80,10 @@ export class PermanentFileSystem {

private archivesCache?: Archive[];

private readonly authToken;
private readonly authTokenManager;

public constructor(authToken: string) {
this.authToken = authToken;
public constructor(authTokenManager: AuthTokenManager) {
this.authTokenManager = authTokenManager;
}

private static loadRootFileEntries(): FileEntry[] {
Expand Down Expand Up @@ -191,7 +192,7 @@ export class PermanentFileSystem {
const childName = path.basename(requestedPath);
const parentFolder = await this.loadFolder(parentPath);
return createFolder(
this.getClientConfiguration(),
await this.getClientConfiguration(),
{
name: childName,
},
Expand All @@ -201,7 +202,7 @@ export class PermanentFileSystem {

public async deleteDirectory(requestedPath: string): Promise<void> {
const account = await getAuthenticatedAccount(
this.getClientConfiguration(),
await this.getClientConfiguration(),
);
if (!account.isSftpDeletionEnabled) {
throw new OperationNotAllowedError('You must enable SFTP deletion directly in your account settings.');
Expand All @@ -220,7 +221,7 @@ export class PermanentFileSystem {
const folder = await this.loadFolder(requestedPath);

await deleteFolder(
this.getClientConfiguration(),
await this.getClientConfiguration(),
folder.id,
);
}
Expand All @@ -242,14 +243,14 @@ export class PermanentFileSystem {
fileSystemCompatibleName: archiveRecordName,
};
const s3Url = await uploadFile(
this.getClientConfiguration(),
await this.getClientConfiguration(),
dataStream,
fileFragment,
archiveRecordfragment,
parentFolder,
);
await createArchiveRecord(
this.getClientConfiguration(),
await this.getClientConfiguration(),
s3Url,
fileFragment,
archiveRecordfragment,
Expand All @@ -259,7 +260,7 @@ export class PermanentFileSystem {

public async deleteFile(requestedPath: string): Promise<void> {
const account = await getAuthenticatedAccount(
this.getClientConfiguration(),
await this.getClientConfiguration(),
);
if (!account.isSftpDeletionEnabled) {
throw new OperationNotAllowedError('You must enable SFTP deletion directly in your account settings.');
Expand All @@ -274,7 +275,7 @@ export class PermanentFileSystem {
);

await deleteArchiveRecord(
this.getClientConfiguration(),
await this.getClientConfiguration(),
archiveRecord.id,
);
}
Expand Down Expand Up @@ -338,7 +339,7 @@ export class PermanentFileSystem {
childName,
);
const populatedArchiveRecord = await getArchiveRecord(
this.getClientConfiguration(),
await this.getClientConfiguration(),
archiveRecord.id,
archiveId,
);
Expand Down Expand Up @@ -380,17 +381,18 @@ export class PermanentFileSystem {
return this.loadArchiveRecords(archiveRecordPaths);
}

private getClientConfiguration(): ClientConfiguration {
private async getClientConfiguration(): Promise<ClientConfiguration> {
const authToken = await this.authTokenManager.getAuthToken();
return {
bearerToken: this.authToken,
bearerToken: authToken,
baseUrl: process.env.PERMANENT_API_BASE_PATH,
};
}

private async loadArchives(): Promise<Archive[]> {
if (!this.archivesCache) {
this.archivesCache = await getArchives(
this.getClientConfiguration(),
await this.getClientConfiguration(),
);
}
return this.archivesCache;
Expand All @@ -417,7 +419,7 @@ export class PermanentFileSystem {
return cachedArchiveFolders;
}
const archiveFolders = await getArchiveFolders(
this.getClientConfiguration(),
await this.getClientConfiguration(),
archiveId,
);
this.archiveFoldersCache.set(archiveId, archiveFolders);
Expand Down Expand Up @@ -506,7 +508,7 @@ export class PermanentFileSystem {
childName,
);
const populatedTargetFolder = await getFolder(
this.getClientConfiguration(),
await this.getClientConfiguration(),
targetFolder.id,
archiveId,
);
Expand Down
5 changes: 3 additions & 2 deletions src/classes/PermanentFileSystemManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { logger } from '../logger';
import { PermanentFileSystem } from './PermanentFileSystem';
import type { AuthTokenManager } from './AuthTokenManager';

export class PermanentFileSystemManager {
private readonly permanentFileSystems = new Map<string, PermanentFileSystem>();
Expand All @@ -8,15 +9,15 @@ export class PermanentFileSystemManager {

public getCurrentPermanentFileSystemForUser(
user: string,
authToken: string,
authTokenManager: AuthTokenManager,
): PermanentFileSystem {
logger.silly('Get permanent file system for user', { user });
this.resetDeletionTimeout(user);
const existingFileSystem = this.permanentFileSystems.get(user);
if (existingFileSystem !== undefined) {
return existingFileSystem;
}
const permanentFileSystem = new PermanentFileSystem(authToken);
const permanentFileSystem = new PermanentFileSystem(authTokenManager);
this.permanentFileSystems.set(
user,
permanentFileSystem,
Expand Down
Loading

0 comments on commit 9a83861

Please sign in to comment.