-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(storage): enables location credentials provider #13605
Changes from 9 commits
99d95a9
9e9d5e6
7b95231
977801b
bb6305c
ac123a5
960b357
04841d4
bf18044
b2fdbb4
da383b3
eb6395d
fdb22ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
export const INVALID_STORAGE_INPUT = 'InvalidStorageInput'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { StorageValidationErrorCode } from '../../../errors/types/validation'; | ||
import { assertValidationError } from '../../../errors/utils/assertValidationError'; | ||
|
||
export const resolveIdentityId = (identityId?: string): string => { | ||
assertValidationError(!!identityId, StorageValidationErrorCode.NoIdentityId); | ||
|
||
return identityId; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,14 +6,27 @@ import { AmplifyClassV6, StorageAccessLevel } from '@aws-amplify/core'; | |
import { assertValidationError } from '../../../errors/utils/assertValidationError'; | ||
import { StorageValidationErrorCode } from '../../../errors/types/validation'; | ||
import { resolvePrefix as defaultPrefixResolver } from '../../../utils/resolvePrefix'; | ||
import { ResolvedS3Config } from '../types/options'; | ||
import { | ||
LocationCredentialsProvider, | ||
ResolvedS3Config, | ||
} from '../types/options'; | ||
import { | ||
StorageCopyInputWithPath, | ||
StorageOperationInputWithKey, | ||
StorageOperationInputWithPath, | ||
StorageOperationInputWithPrefix, | ||
} from '../../../types/inputs'; | ||
import { StorageError } from '../../../errors/StorageError'; | ||
import { CopyInput } from '../types'; | ||
import { INVALID_STORAGE_INPUT } from '../../../errors/constants'; | ||
|
||
import { DEFAULT_ACCESS_LEVEL, LOCAL_TESTING_S3_ENDPOINT } from './constants'; | ||
|
||
interface S3ApiOptions { | ||
accessLevel?: StorageAccessLevel; | ||
targetIdentityId?: string; | ||
useAccelerateEndpoint?: boolean; | ||
locationCredentialsProvider?: LocationCredentialsProvider; | ||
} | ||
|
||
interface ResolvedS3ConfigAndInput { | ||
|
@@ -23,6 +36,14 @@ interface ResolvedS3ConfigAndInput { | |
isObjectLockEnabled?: boolean; | ||
identityId?: string; | ||
} | ||
export type DeprecatedStorageInput = | ||
| StorageOperationInputWithKey | ||
| StorageOperationInputWithPrefix | ||
| CopyInput; | ||
|
||
export type CallbackPathStorageInput = | ||
| StorageOperationInputWithPath | ||
| StorageCopyInputWithPath; | ||
ashika112 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* resolve the common input options for S3 API handlers from Amplify configuration and library options. | ||
|
@@ -38,13 +59,13 @@ interface ResolvedS3ConfigAndInput { | |
export const resolveS3ConfigAndInput = async ( | ||
amplify: AmplifyClassV6, | ||
apiOptions?: S3ApiOptions, | ||
input?: DeprecatedStorageInput | CallbackPathStorageInput, | ||
AllanZhengYP marked this conversation as resolved.
Show resolved
Hide resolved
|
||
): Promise<ResolvedS3ConfigAndInput> => { | ||
/** | ||
* IdentityId is always cached in memory so we can safely make calls here. It | ||
* should be stable even for unauthenticated users, regardless of credentials. | ||
*/ | ||
const { identityId } = await amplify.Auth.fetchAuthSession(); | ||
assertValidationError(!!identityId, StorageValidationErrorCode.NoIdentityId); | ||
ashika112 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* A credentials provider function instead of a static credentials object is | ||
|
@@ -53,7 +74,13 @@ export const resolveS3ConfigAndInput = async ( | |
* credentials if they are expired. | ||
*/ | ||
const credentialsProvider = async () => { | ||
const { credentials } = await amplify.Auth.fetchAuthSession(); | ||
if (isLocationCredentialsProvider(apiOptions)) { | ||
assertStorageInput(input); | ||
} | ||
|
||
const { credentials } = isLocationCredentialsProvider(apiOptions) | ||
? await apiOptions.locationCredentialsProvider() | ||
: await amplify.Auth.fetchAuthSession(); | ||
assertValidationError( | ||
!!credentials, | ||
StorageValidationErrorCode.NoCredentials, | ||
|
@@ -101,3 +128,72 @@ export const resolveS3ConfigAndInput = async ( | |
isObjectLockEnabled, | ||
}; | ||
}; | ||
|
||
const isLocationCredentialsProvider = ( | ||
options?: S3ApiOptions, | ||
): options is S3ApiOptions & { | ||
locationCredentialsProvider: LocationCredentialsProvider; | ||
} => { | ||
return !!options?.locationCredentialsProvider; | ||
}; | ||
|
||
const isInputWithCallbackPath = (input?: CallbackPathStorageInput) => { | ||
return ( | ||
((input as StorageOperationInputWithPath)?.path && | ||
typeof (input as StorageOperationInputWithPath).path === 'function') || | ||
((input as StorageCopyInputWithPath)?.destination?.path && | ||
typeof (input as StorageCopyInputWithPath).destination?.path === | ||
'function') || | ||
israx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
((input as StorageCopyInputWithPath)?.source?.path && | ||
typeof (input as StorageCopyInputWithPath).source?.path === 'function') | ||
); | ||
}; | ||
|
||
const isDeprecatedInput = ( | ||
input?: DeprecatedStorageInput | CallbackPathStorageInput, | ||
): input is DeprecatedStorageInput => { | ||
return ( | ||
isInputWithKey(input) || | ||
isInputWithPrefix(input) || | ||
isInputWithCopySourceOrDestination(input) | ||
); | ||
}; | ||
const assertStorageInput = ( | ||
input?: DeprecatedStorageInput | CallbackPathStorageInput, | ||
) => { | ||
if (isDeprecatedInput(input) || isInputWithCallbackPath(input)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not use assertValidationError here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but any validation we already perform it through the assertValidation why change the pattern here and now? |
||
throw new StorageError({ | ||
name: INVALID_STORAGE_INPUT, | ||
message: 'The input needs to have a path as a string value.', | ||
recoverySuggestion: | ||
'Please provide a valid path as a string value for the input.', | ||
}); | ||
} | ||
}; | ||
|
||
const isInputWithKey = ( | ||
ashika112 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
input?: DeprecatedStorageInput | CallbackPathStorageInput, | ||
): input is StorageOperationInputWithKey => { | ||
return !!( | ||
(input as StorageOperationInputWithKey)?.key && | ||
typeof (input as StorageOperationInputWithKey).key === 'string' | ||
); | ||
}; | ||
const isInputWithPrefix = ( | ||
input?: DeprecatedStorageInput | CallbackPathStorageInput, | ||
): input is StorageOperationInputWithPrefix => { | ||
return !!( | ||
(input as StorageOperationInputWithPrefix)?.prefix && | ||
typeof (input as StorageOperationInputWithPrefix).prefix === 'string' | ||
); | ||
}; | ||
const isInputWithCopySourceOrDestination = ( | ||
input?: CopyInput | CallbackPathStorageInput, | ||
): input is CopyInput => { | ||
return !!( | ||
((input as CopyInput)?.source?.key && | ||
typeof (input as CopyInput).source?.key === 'string') || | ||
((input as CopyInput)?.destination?.key && | ||
typeof (input as CopyInput).destination?.key === 'string') | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
resolveS3ConfingAndInput
helper is not asserting for the identityId anymoreThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while this might not be needed, if customers are already relyign on this message for something, would removing it cause a breaking change for them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ashika112 I don't think removing the validation here itself is a breaking change, unless they are using Storage APIs intentionally to check for identity ID. However, it's not the intended usage of the Storage APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh i mean , if customer lets say is specifically finding this error to avoid making a call right now, wouldn't it break? this is propogated all the way to customer. Again coming from path callback perspective, but if this check is moved to path validation i think it is fine there. but wondering if there is any other use case like that