Rename AwsCredentialsProvider.getCredentials -> AwsCredentialsManager.getCredentialProvider
Signed-off-by: Clare Liguori <liguori@amazon.com>
This commit is contained in:
@@ -11,7 +11,7 @@ Backstage app config.
|
||||
Users can configure IAM user credentials, IAM roles, and profile names
|
||||
for their AWS accounts in their Backstage config.
|
||||
|
||||
If the AWS integration configuration is missing, the credentials provider
|
||||
If the AWS integration configuration is missing, the credentials manager
|
||||
from this package will fall back to the AWS SDK default credentials chain for
|
||||
resources in the main AWS account.
|
||||
The default credentials chain for Node resolves credentials in the
|
||||
@@ -79,32 +79,35 @@ aws:
|
||||
## Integrate new plugins
|
||||
|
||||
Backend plugins can provide an AWS ARN or account ID to this library in order to
|
||||
retrieve a credentials provider for the relevant account that can be fed directly
|
||||
retrieve a credential provider for the relevant account that can be fed directly
|
||||
to an AWS SDK client.
|
||||
The AWS SDK for Javascript V3 must be used.
|
||||
|
||||
```typescript
|
||||
const awsCredentialsProvider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
|
||||
// provide the account ID explicitly
|
||||
const creds = await awsCredentialsProvider.getCredentials({ accountId });
|
||||
const credProvider = await awsCredentialsManager.getCredentialProvider({
|
||||
accountId,
|
||||
});
|
||||
// OR extract the account ID from the ARN
|
||||
const creds = await awsCredentialsProvider.getCredentials({ arn });
|
||||
const credProvider = await awsCredentialsManager.getCredentialProvider({ arn });
|
||||
// OR provide neither to get main account's credentials
|
||||
const creds = await awsCredentialsProvider.getCredentials({});
|
||||
const credProvider = await awsCredentialsManager.getCredentialProvider({});
|
||||
|
||||
// Example constructing an AWS Proton client with the returned credentials provider
|
||||
// Example constructing an AWS Proton client with the returned credential provider
|
||||
const client = new ProtonClient({
|
||||
region,
|
||||
credentialDefaultProvider: () => creds.provider,
|
||||
credentialDefaultProvider: () => credProvider.sdkCredentialProvider,
|
||||
});
|
||||
```
|
||||
|
||||
Depending on the nature of your plguin, you may either have the user specify the
|
||||
Depending on the nature of your plugin, you may either have the user specify the
|
||||
relevant ARN or account ID in a catalog entity annotation or in the static Backstage
|
||||
app configuration for your plugin.
|
||||
|
||||
For example, you can create a new catalog entity annotation for your plugin:
|
||||
For example, you can create a new catalog entity annotation for your plugin containing
|
||||
either an AWS account ID or ARN:
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
@@ -117,7 +120,7 @@ metadata:
|
||||
my-other-plugin.io/aws-dynamodb-table: 'arn:aws:dynamodb:us-east-2:123456789012:table/example-table'
|
||||
```
|
||||
|
||||
In your plugin, read the annotation value so that you can retrieve the credentials provider:
|
||||
In your plugin, read the annotation value so that you can retrieve the credential provider:
|
||||
|
||||
```typescript
|
||||
const MY_AWS_ACCOUNT_ID_ANNOTATION = 'my-plugin.io/aws-account-id';
|
||||
@@ -126,7 +129,7 @@ const getAwsAccountId = (entity: Entity) =>
|
||||
entity.metadata.annotations?.[MY_AWS_ACCOUNT_ID_ANNOTATION]);
|
||||
```
|
||||
|
||||
Alternatively, you can create a new configuration field for your plugin:
|
||||
Alternatively, you can create a new Backstage app configuration field for your plugin:
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
@@ -138,18 +141,20 @@ my-other-plugin:
|
||||
awsDynamoDbTable: 'arn:aws:dynamodb:us-east-2:123456789012:table/example-table'
|
||||
```
|
||||
|
||||
In your plugin, read the configuration value so that you can retrieve the credentials provider:
|
||||
In your plugin, read the configuration value so that you can retrieve the credential provider:
|
||||
|
||||
```typescript
|
||||
// Read an account ID from your plugin's configuration
|
||||
const awsCredentialsProvider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const accountId = config.getString('my-plugin.awsAccountId');
|
||||
const creds = await awsCredentialsProvider.getCredentials({ accountId });
|
||||
const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const accountId = config.getOptionalString('my-plugin.awsAccountId');
|
||||
const credProvider = await awsCredentialsManager.getCredentialProvider({
|
||||
accountId,
|
||||
});
|
||||
|
||||
// Or, read an AWS ARN from your plugin's configuration
|
||||
const awsCredentialsProvider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const arn = config.getString('my-other-plugin.awsDynamoDbTable');
|
||||
const creds = await awsCredentialsProvider.getCredentials({ arn });
|
||||
const credProvider = await awsCredentialsManager.getCredentialProvider({ arn });
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
@@ -7,23 +7,25 @@ import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
// @public
|
||||
export type AwsCredentials = {
|
||||
export type AwsCredentialProvider = {
|
||||
accountId?: string;
|
||||
stsRegion?: string;
|
||||
provider: AwsCredentialIdentityProvider;
|
||||
sdkCredentialProvider: AwsCredentialIdentityProvider;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface AwsCredentialsProvider {
|
||||
getCredentials(opts?: AwsCredentialsProviderOptions): Promise<AwsCredentials>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type AwsCredentialsProviderOptions = {
|
||||
export type AwsCredentialProviderOptions = {
|
||||
accountId?: string;
|
||||
arn?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface AwsCredentialsManager {
|
||||
getCredentialProvider(
|
||||
opts?: AwsCredentialProviderOptions,
|
||||
): Promise<AwsCredentialProvider>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type AwsIntegrationAccountConfig = {
|
||||
accountId: string;
|
||||
@@ -60,10 +62,12 @@ export type AwsIntegrationMainAccountConfig = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export class DefaultAwsCredentialsProvider implements AwsCredentialsProvider {
|
||||
export class DefaultAwsCredentialsManager implements AwsCredentialsManager {
|
||||
// (undocumented)
|
||||
static fromConfig(config: Config): DefaultAwsCredentialsProvider;
|
||||
getCredentials(opts?: AwsCredentialsProviderOptions): Promise<AwsCredentials>;
|
||||
static fromConfig(config: Config): DefaultAwsCredentialsManager;
|
||||
getCredentialProvider(
|
||||
opts?: AwsCredentialProviderOptions,
|
||||
): Promise<AwsCredentialProvider>;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
||||
+69
-69
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DefaultAwsCredentialsProvider } from './DefaultAwsCredentialsProvider';
|
||||
import { DefaultAwsCredentialsManager } from './DefaultAwsCredentialsManager';
|
||||
import { mockClient, AwsClientStub } from 'aws-sdk-client-mock';
|
||||
import 'aws-sdk-client-mock-jest';
|
||||
import {
|
||||
@@ -31,7 +31,7 @@ let config: Config;
|
||||
|
||||
jest.mock('fs', () => ({ promises: { readFile: jest.fn() } }));
|
||||
|
||||
describe('DefaultAwsCredentialsProvider', () => {
|
||||
describe('DefaultAwsCredentialsManager', () => {
|
||||
beforeEach(() => {
|
||||
process.env = { ...env };
|
||||
jest.resetAllMocks();
|
||||
@@ -145,16 +145,16 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
process.env = env;
|
||||
});
|
||||
|
||||
describe('#getCredentials', () => {
|
||||
describe('#getCredentialProvider', () => {
|
||||
it('retrieves assume-role creds for the given account ID and caches the provider', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '111111111111',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('111111111111');
|
||||
expect(awsCredentialProvider.accountId).toEqual('111111111111');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_1',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_1',
|
||||
@@ -162,23 +162,23 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
expiration: new Date('2022-01-01'),
|
||||
});
|
||||
|
||||
const awsCredentials2 = await provider.getCredentials({
|
||||
const awsCredentialProvider2 = await provider.getCredentialProvider({
|
||||
accountId: '111111111111',
|
||||
});
|
||||
|
||||
expect(awsCredentials).toBe(awsCredentials2);
|
||||
expect(awsCredentialProvider).toBe(awsCredentialProvider2);
|
||||
expect(stsMock).toHaveReceivedCommandTimes(AssumeRoleCommand, 1);
|
||||
});
|
||||
|
||||
it('retrieves assume-role creds in another partition for the given account ID', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '222222222222',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('222222222222');
|
||||
expect(awsCredentialProvider.accountId).toEqual('222222222222');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_2',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_2',
|
||||
@@ -188,14 +188,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('retrieves assume-role creds for an account using the account defaults', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '999999999999',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('999999999999');
|
||||
expect(awsCredentialProvider.accountId).toEqual('999999999999');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_9',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_9',
|
||||
@@ -205,14 +205,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('retrieves static creds for the given account ID', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '333333333333',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('333333333333');
|
||||
expect(awsCredentialProvider.accountId).toEqual('333333333333');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'my-access-key',
|
||||
secretAccessKey: 'my-secret-access-key',
|
||||
@@ -228,14 +228,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(minConfig);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(minConfig);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '123456789012',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'GHI',
|
||||
secretAccessKey: 'JKL',
|
||||
@@ -251,23 +251,23 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(minConfig);
|
||||
const awsCredentials1 = await provider.getCredentials({});
|
||||
const awsCredentials2 = await provider.getCredentials({});
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(minConfig);
|
||||
const awsCredentialProvider1 = await provider.getCredentialProvider({});
|
||||
const awsCredentialProvider2 = await provider.getCredentialProvider({});
|
||||
|
||||
expect(awsCredentials1).toBe(awsCredentials2);
|
||||
expect(awsCredentialProvider1).toBe(awsCredentialProvider2);
|
||||
expect(stsMock).toHaveReceivedCommandTimes(GetCallerIdentityCommand, 1);
|
||||
});
|
||||
|
||||
it('retrieves the ini provider chain for the given account ID', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '555555555555',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('555555555555');
|
||||
expect(awsCredentialProvider.accountId).toEqual('555555555555');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_9',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_9',
|
||||
@@ -275,14 +275,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('retrieves the default cred provider chain for the given account ID', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '444444444444',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('444444444444');
|
||||
expect(awsCredentialProvider.accountId).toEqual('444444444444');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_10',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_10',
|
||||
@@ -299,14 +299,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(minConfig);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(minConfig);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '123456789012',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_9',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_9',
|
||||
@@ -317,14 +317,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
const minConfig = new ConfigReader({
|
||||
aws: {},
|
||||
});
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(minConfig);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(minConfig);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '123456789012',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_10',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_10',
|
||||
@@ -335,14 +335,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
|
||||
it('retrieves default cred provider chain from the main account when there is no AWS integration config', async () => {
|
||||
const minConfig = new ConfigReader({});
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(minConfig);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(minConfig);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
accountId: '123456789012',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_10',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_10',
|
||||
@@ -352,14 +352,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('extracts the account ID from an ARN', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
arn: 'arn:aws:ecs:region:111111111111:service/cluster-name/service-name',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('111111111111');
|
||||
expect(awsCredentialProvider.accountId).toEqual('111111111111');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'ACCESS_KEY_ID_1',
|
||||
secretAccessKey: 'SECRET_ACCESS_KEY_1',
|
||||
@@ -369,14 +369,14 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('falls back to main account credentials when account ID cannot be extracted from the ARN', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({
|
||||
arn: 'arn:aws:s3:::bucket_name',
|
||||
});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'GHI',
|
||||
secretAccessKey: 'JKL',
|
||||
@@ -384,12 +384,12 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('falls back to main account credentials when neither account ID nor ARN are provided', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials({});
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider({});
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'GHI',
|
||||
secretAccessKey: 'JKL',
|
||||
@@ -397,12 +397,12 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
});
|
||||
|
||||
it('falls back to main account credentials when no options are provided', async () => {
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
const awsCredentials = await provider.getCredentials();
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
const awsCredentialProvider = await provider.getCredentialProvider();
|
||||
|
||||
expect(awsCredentials.accountId).toEqual('123456789012');
|
||||
expect(awsCredentialProvider.accountId).toEqual('123456789012');
|
||||
|
||||
const creds = await awsCredentials.provider();
|
||||
const creds = await awsCredentialProvider.sdkCredentialProvider();
|
||||
expect(creds).toEqual({
|
||||
accessKeyId: 'GHI',
|
||||
secretAccessKey: 'JKL',
|
||||
@@ -413,16 +413,16 @@ describe('DefaultAwsCredentialsProvider', () => {
|
||||
const minConfig = new ConfigReader({
|
||||
aws: {},
|
||||
});
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(minConfig);
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(minConfig);
|
||||
await expect(
|
||||
provider.getCredentials({ accountId: '111222333444' }),
|
||||
provider.getCredentialProvider({ accountId: '111222333444' }),
|
||||
).rejects.toThrow(/no AWS integration that matches 111222333444/);
|
||||
});
|
||||
|
||||
it('rejects main account that has invalid credentials', async () => {
|
||||
stsMock.on(GetCallerIdentityCommand).rejects('No credentials found');
|
||||
const provider = DefaultAwsCredentialsProvider.fromConfig(config);
|
||||
await expect(provider.getCredentials({})).rejects.toThrow(
|
||||
const provider = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
await expect(provider.getCredentialProvider({})).rejects.toThrow(
|
||||
/No credentials found/,
|
||||
);
|
||||
});
|
||||
+58
-52
@@ -21,9 +21,9 @@ import {
|
||||
AwsIntegrationMainAccountConfig,
|
||||
} from './config';
|
||||
import {
|
||||
AwsCredentials,
|
||||
AwsCredentialsProvider,
|
||||
AwsCredentialsProviderOptions,
|
||||
AwsCredentialsManager,
|
||||
AwsCredentialProvider,
|
||||
AwsCredentialProviderOptions,
|
||||
} from './types';
|
||||
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
|
||||
import {
|
||||
@@ -36,20 +36,20 @@ import { parse } from '@aws-sdk/util-arn-parser';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
/**
|
||||
* Retrieves the account ID for the given credentials provider from STS.
|
||||
* Retrieves the account ID for the given credential provider from STS.
|
||||
*/
|
||||
async function fillInAccountId(creds: AwsCredentials) {
|
||||
if (creds.accountId) {
|
||||
async function fillInAccountId(credProvider: AwsCredentialProvider) {
|
||||
if (credProvider.accountId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = new STSClient({
|
||||
region: creds.stsRegion,
|
||||
customUserAgent: 'backstage-aws-credentials-provider',
|
||||
credentialDefaultProvider: () => creds.provider,
|
||||
region: credProvider.stsRegion,
|
||||
customUserAgent: 'backstage-aws-credentials-manager',
|
||||
credentialDefaultProvider: () => credProvider.sdkCredentialProvider,
|
||||
});
|
||||
const resp = await client.send(new GetCallerIdentityCommand({}));
|
||||
creds.accountId = resp.Account!;
|
||||
credProvider.accountId = resp.Account!;
|
||||
}
|
||||
|
||||
function getStaticCredentials(
|
||||
@@ -72,7 +72,7 @@ function getProfileCredentials(
|
||||
profile,
|
||||
clientConfig: {
|
||||
region,
|
||||
customUserAgent: 'backstage-aws-credentials-provider',
|
||||
customUserAgent: 'backstage-aws-credentials-manager',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -91,9 +91,9 @@ function getDefaultCredentialsChain(): AwsCredentialIdentityProvider {
|
||||
* 4. Profile creds
|
||||
* 5. Default AWS SDK creds chain
|
||||
*/
|
||||
function getAccountCredentialsProvider(
|
||||
function getSdkCredentialProvider(
|
||||
config: AwsIntegrationAccountConfig,
|
||||
mainAccountCreds: AwsCredentialIdentityProvider,
|
||||
mainAccountCredProvider: AwsCredentialIdentityProvider,
|
||||
): AwsCredentialIdentityProvider {
|
||||
if (config.roleName) {
|
||||
const region = config.region ?? 'us-east-1';
|
||||
@@ -102,7 +102,7 @@ function getAccountCredentialsProvider(
|
||||
return fromTemporaryCredentials({
|
||||
masterCredentials: config.accessKeyId
|
||||
? getStaticCredentials(config.accessKeyId!, config.secretAccessKey!)
|
||||
: mainAccountCreds,
|
||||
: mainAccountCredProvider,
|
||||
params: {
|
||||
RoleArn: `arn:${partition}:iam::${config.accountId}:role/${config.roleName}`,
|
||||
RoleSessionName: 'backstage',
|
||||
@@ -110,7 +110,7 @@ function getAccountCredentialsProvider(
|
||||
},
|
||||
clientConfig: {
|
||||
region,
|
||||
customUserAgent: 'backstage-aws-credentials-provider',
|
||||
customUserAgent: 'backstage-aws-credentials-manager',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -134,7 +134,7 @@ function getAccountCredentialsProvider(
|
||||
* 2. Profile creds
|
||||
* 3. Default AWS SDK creds chain
|
||||
*/
|
||||
function getMainAccountCredentialsProvider(
|
||||
function getMainAccountSdkCredentialProvider(
|
||||
config: AwsIntegrationMainAccountConfig,
|
||||
): AwsCredentialIdentityProvider {
|
||||
if (config.accessKeyId) {
|
||||
@@ -153,8 +153,8 @@ function getMainAccountCredentialsProvider(
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class DefaultAwsCredentialsProvider implements AwsCredentialsProvider {
|
||||
static fromConfig(config: Config): DefaultAwsCredentialsProvider {
|
||||
export class DefaultAwsCredentialsManager implements AwsCredentialsManager {
|
||||
static fromConfig(config: Config): DefaultAwsCredentialsManager {
|
||||
const awsConfig = config.has('aws')
|
||||
? readAwsIntegrationConfig(config.getConfig('aws'))
|
||||
: {
|
||||
@@ -163,63 +163,66 @@ export class DefaultAwsCredentialsProvider implements AwsCredentialsProvider {
|
||||
accountDefaults: {},
|
||||
};
|
||||
|
||||
const mainAccountProvider = getMainAccountCredentialsProvider(
|
||||
const mainAccountSdkCredProvider = getMainAccountSdkCredentialProvider(
|
||||
awsConfig.mainAccount,
|
||||
);
|
||||
const mainAccountCreds: AwsCredentials = {
|
||||
provider: mainAccountProvider,
|
||||
const mainAccountCredProvider: AwsCredentialProvider = {
|
||||
sdkCredentialProvider: mainAccountSdkCredProvider,
|
||||
};
|
||||
|
||||
const accountCreds = new Map<string, AwsCredentials>();
|
||||
const accountCredProviders = new Map<string, AwsCredentialProvider>();
|
||||
for (const accountConfig of awsConfig.accounts) {
|
||||
const provider = getAccountCredentialsProvider(
|
||||
const sdkCredentialProvider = getSdkCredentialProvider(
|
||||
accountConfig,
|
||||
mainAccountCreds.provider,
|
||||
mainAccountSdkCredProvider,
|
||||
);
|
||||
accountCreds.set(accountConfig.accountId, {
|
||||
accountCredProviders.set(accountConfig.accountId, {
|
||||
accountId: accountConfig.accountId,
|
||||
stsRegion: accountConfig.region,
|
||||
provider,
|
||||
sdkCredentialProvider,
|
||||
});
|
||||
}
|
||||
|
||||
return new DefaultAwsCredentialsProvider(
|
||||
accountCreds,
|
||||
return new DefaultAwsCredentialsManager(
|
||||
accountCredProviders,
|
||||
awsConfig.accountDefaults,
|
||||
mainAccountCreds,
|
||||
mainAccountCredProvider,
|
||||
);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly accountCredentials: Map<string, AwsCredentials>,
|
||||
private readonly accountCredentialProviders: Map<
|
||||
string,
|
||||
AwsCredentialProvider
|
||||
>,
|
||||
private readonly accountDefaults: AwsIntegrationDefaultAccountConfig,
|
||||
private readonly mainAccountCredentials: AwsCredentials,
|
||||
private readonly mainAccountCredentialProvider: AwsCredentialProvider,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns {@link AwsCredentials} for a given AWS account.
|
||||
* Returns an {@link AwsCredentialProvider} for a given AWS account.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { provider } = await getCredentials({
|
||||
* const { provider } = await getCredentialProvider({
|
||||
* accountId: '0123456789012',
|
||||
* })
|
||||
*
|
||||
* const { provider } = await getCredentials({
|
||||
* const { provider } = await getCredentialProvider({
|
||||
* arn: 'arn:aws:ecs:us-west-2:123456789012:service/my-http-service'
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @param opts - the AWS account ID or AWS resource ARN
|
||||
* @returns A promise of {@link AwsCredentials}.
|
||||
* @returns A promise of {@link AwsCredentialProvider}.
|
||||
*/
|
||||
async getCredentials(
|
||||
opts?: AwsCredentialsProviderOptions,
|
||||
): Promise<AwsCredentials> {
|
||||
async getCredentialProvider(
|
||||
opts?: AwsCredentialProviderOptions,
|
||||
): Promise<AwsCredentialProvider> {
|
||||
// If no options provided, fall back to the main account
|
||||
if (!opts) {
|
||||
await fillInAccountId(this.mainAccountCredentials);
|
||||
return this.mainAccountCredentials;
|
||||
await fillInAccountId(this.mainAccountCredentialProvider);
|
||||
return this.mainAccountCredentialProvider;
|
||||
}
|
||||
|
||||
// Determine the account ID: either explicitly provided or extracted from the provided ARN
|
||||
@@ -232,13 +235,13 @@ export class DefaultAwsCredentialsProvider implements AwsCredentialsProvider {
|
||||
// If the account ID was not provided (explicitly or in the ARN),
|
||||
// fall back to the main account
|
||||
if (!accountId) {
|
||||
await fillInAccountId(this.mainAccountCredentials);
|
||||
return this.mainAccountCredentials;
|
||||
await fillInAccountId(this.mainAccountCredentialProvider);
|
||||
return this.mainAccountCredentialProvider;
|
||||
}
|
||||
|
||||
// Return a cached provider if available
|
||||
if (this.accountCredentials.has(accountId)) {
|
||||
return this.accountCredentials.get(accountId)!;
|
||||
if (this.accountCredentialProviders.has(accountId)) {
|
||||
return this.accountCredentialProviders.get(accountId)!;
|
||||
}
|
||||
|
||||
// First, fall back to using the account defaults
|
||||
@@ -250,20 +253,23 @@ export class DefaultAwsCredentialsProvider implements AwsCredentialsProvider {
|
||||
region: this.accountDefaults.region,
|
||||
externalId: this.accountDefaults.externalId,
|
||||
};
|
||||
const provider = getAccountCredentialsProvider(
|
||||
const sdkCredentialProvider = getSdkCredentialProvider(
|
||||
config,
|
||||
this.mainAccountCredentials.provider,
|
||||
this.mainAccountCredentialProvider.sdkCredentialProvider,
|
||||
);
|
||||
const creds: AwsCredentials = { accountId, provider };
|
||||
this.accountCredentials.set(accountId, creds);
|
||||
return creds;
|
||||
const credProvider: AwsCredentialProvider = {
|
||||
accountId,
|
||||
sdkCredentialProvider,
|
||||
};
|
||||
this.accountCredentialProviders.set(accountId, credProvider);
|
||||
return credProvider;
|
||||
}
|
||||
|
||||
// Then, fall back to using the main account, but only
|
||||
// if the account requested matches the main account ID
|
||||
await fillInAccountId(this.mainAccountCredentials);
|
||||
if (accountId === this.mainAccountCredentials.accountId) {
|
||||
return this.mainAccountCredentials;
|
||||
await fillInAccountId(this.mainAccountCredentialProvider);
|
||||
if (accountId === this.mainAccountCredentialProvider.accountId) {
|
||||
return this.mainAccountCredentialProvider;
|
||||
}
|
||||
|
||||
// Otherwise, the account needs to be explicitly configured in Backstage
|
||||
@@ -21,9 +21,9 @@ export type {
|
||||
AwsIntegrationDefaultAccountConfig,
|
||||
AwsIntegrationMainAccountConfig,
|
||||
} from './config';
|
||||
export { DefaultAwsCredentialsProvider } from './DefaultAwsCredentialsProvider';
|
||||
export { DefaultAwsCredentialsManager } from './DefaultAwsCredentialsManager';
|
||||
export type {
|
||||
AwsCredentials,
|
||||
AwsCredentialsProvider,
|
||||
AwsCredentialsProviderOptions,
|
||||
AwsCredentialsManager,
|
||||
AwsCredentialProvider,
|
||||
AwsCredentialProviderOptions,
|
||||
} from './types';
|
||||
|
||||
@@ -20,10 +20,19 @@ import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AwsCredentials = {
|
||||
export type AwsCredentialProvider = {
|
||||
/**
|
||||
* The AWS account ID of these credentials
|
||||
*/
|
||||
accountId?: string;
|
||||
/**
|
||||
* The STS region used with these credentials
|
||||
*/
|
||||
stsRegion?: string;
|
||||
provider: AwsCredentialIdentityProvider;
|
||||
/**
|
||||
* The credential identity provider to use when creating AWS SDK for Javascript V3 clients
|
||||
*/
|
||||
sdkCredentialProvider: AwsCredentialIdentityProvider;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -31,7 +40,7 @@ export type AwsCredentials = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AwsCredentialsProviderOptions = {
|
||||
export type AwsCredentialProviderOptions = {
|
||||
/**
|
||||
* The AWS account ID, e.g. '0123456789012'
|
||||
*/
|
||||
@@ -49,9 +58,11 @@ export type AwsCredentialsProviderOptions = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AwsCredentialsProvider {
|
||||
export interface AwsCredentialsManager {
|
||||
/**
|
||||
* Get credentials for an AWS account.
|
||||
*/
|
||||
getCredentials(opts?: AwsCredentialsProviderOptions): Promise<AwsCredentials>;
|
||||
getCredentialProvider(
|
||||
opts?: AwsCredentialProviderOptions,
|
||||
): Promise<AwsCredentialProvider>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user