Skip to content

Commit

Permalink
feat(fs): support providing credentials for both read and read-write
Browse files Browse the repository at this point in the history
  • Loading branch information
blacha committed Mar 4, 2024
1 parent 0386b61 commit 09bf376
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 15 deletions.
22 changes: 15 additions & 7 deletions packages/fs-aws/src/credentials.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { S3Client } from '@aws-sdk/client-s3';
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
import { FileSystem, FileSystemProvider } from '@chunkd/fs';
import { FileSystem, FileSystemProvider, Flag } from '@chunkd/fs';

import { FsAwsS3 } from './fs.s3.js';
import { AwsCredentialConfig, AwsCredentialProvider } from './types.js';
Expand All @@ -18,6 +18,12 @@ export function validateConfig(cfg: AwsCredentialProvider, loc: URL): AwsCredent
return cfg;
}

function credentialsMatch(cfg: AwsCredentialConfig, href: string, flag: Flag): boolean {
if (!href.startsWith(cfg.prefix)) return false;
if (flag === 'rw' && cfg.flags === 'r') return false;
return true;
}

export class FsConfigFetcher {
loc: URL;
fs: FileSystem;
Expand All @@ -39,11 +45,11 @@ export class FsConfigFetcher {
return this._config;
}

async findCredentials(loc: URL): Promise<AwsCredentialConfig | null> {
async findCredentials(loc: URL, flag: Flag): Promise<AwsCredentialConfig | null> {
const href = loc.href;
const cfg = await this.config;
for (const credentials of cfg.prefixes) {
if (href.startsWith(credentials.prefix)) return credentials;
if (credentialsMatch(credentials, href, flag)) return credentials;
}
return null;
}
Expand Down Expand Up @@ -147,21 +153,23 @@ export class AwsS3CredentialProvider implements FileSystemProvider<FsAwsS3> {
}

/** Look up the credentials for a path */
async findCredentials(loc: URL): Promise<AwsCredentialConfig | null> {
async findCredentials(loc: URL, flag: Flag): Promise<AwsCredentialConfig | null> {
const href = loc.href;
for (const cfg of this.configs) {
if ('findCredentials' in cfg) {
const credentials = await cfg.findCredentials(loc);
const credentials = await cfg.findCredentials(loc, flag);
if (credentials) return credentials;
} else if (href.startsWith(cfg.prefix)) {
// If we need write credentials but these credentials only provide read skip it
if (flag === 'rw' && cfg.flags === 'r') continue;
return cfg;
}
}
return null;
}

async find(path: URL): Promise<FsAwsS3 | null> {
const cs = await this.findCredentials(path);
async find(loc: URL, flag: Flag): Promise<FsAwsS3 | null> {
const cs = await this.findCredentials(loc, flag);
if (cs == null) return null;

const cacheKey = `${cs.roleArn}__${cs.externalId}__${cs.roleSessionDuration}`;
Expand Down
12 changes: 6 additions & 6 deletions packages/fs-aws/src/fs.s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export class FsAwsS3 implements FileSystem {
const ce = toFsError(e, `Failed to list: "${loc}"`, loc, 'list', this);

if (this.credentials != null && ce.code === 403) {
const newFs = await this.credentials.find(loc);
const newFs = await this.credentials.find(loc, 'r');
if (newFs) {
yield* newFs.details(loc, opts);
return;
Expand All @@ -154,7 +154,7 @@ export class FsAwsS3 implements FileSystem {
} catch (e) {
const ce = toFsError(e, `Failed to read: "${loc}"`, loc, 'read', this);
if (this.credentials != null && ce.code === 403) {
const newFs = await this.credentials.find(loc);
const newFs = await this.credentials.find(loc, 'r');
if (newFs) return newFs.read(loc);
}
throw ce;
Expand Down Expand Up @@ -185,7 +185,7 @@ export class FsAwsS3 implements FileSystem {
} catch (e) {
const ce = toFsError(e, `Failed to write to "${loc}"`, loc, 'write', this);
if (ce.code === 403) {
const newFs = await this.credentials.find(testPath);
const newFs = await this.credentials.find(testPath, 'rw');
if (newFs) return newFs;
}
throw ce;
Expand Down Expand Up @@ -222,7 +222,7 @@ export class FsAwsS3 implements FileSystem {
} catch (e) {
const ce = toFsError(e, `Failed to write: "${loc}"`, loc, 'write', this);
if (this.credentials != null && ce.code === 403) {
const newFs = await this.credentials.find(loc);
const newFs = await this.credentials.find(loc, 'rw');
if (newFs) return newFs.write(loc, buf, ctx);
}
throw ce;
Expand All @@ -243,7 +243,7 @@ export class FsAwsS3 implements FileSystem {
const ce = toFsError(e, `Failed to delete: "${loc}"`, loc, 'delete', this);
if (ce.code === 404) return;
if (this.credentials != null && ce.code === 403) {
const newFs = await this.credentials.find(loc);
const newFs = await this.credentials.find(loc, 'rw');
if (newFs) return newFs.delete(loc);
}
throw ce;
Expand Down Expand Up @@ -297,7 +297,7 @@ export class FsAwsS3 implements FileSystem {
if (ce.code === 404) return null;

if (this.credentials != null && ce.code === 403) {
const newFs = await this.credentials.find(loc);
const newFs = await this.credentials.find(loc, 'r');
if (newFs) return newFs.head(loc);
}
throw ce;
Expand Down
2 changes: 2 additions & 0 deletions packages/fs/src/flags.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export type Flag = FlagRead | FlagReadWrite;
/** Ability to read from a location */
export type FlagRead = 'r';
/** Ability to read and write to a location */
export type FlagReadWrite = 'rw';
12 changes: 10 additions & 2 deletions packages/fs/src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { FileSystem } from './file.system.js';
import { Flag } from './flags.js';

export interface FileSystemProvider<T extends FileSystem = FileSystem> {
/** find a file system for a given prefix */
find(prefix: URL): Promise<T | null>;
/**
* find a file system for a given prefix and permissions
*
* @param prefix location to search for
* @param flag Permissions required (Read or ReadWrite)
*
* @returns File System with the required permission, null otherwise
*/
find(prefix: URL, flag: Flag): Promise<T | null>;
}

0 comments on commit 09bf376

Please sign in to comment.