Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/aws-cdk-lib/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ baseConfig.rules['import/no-extraneous-dependencies'] = [
baseConfig.rules['@cdklabs/no-throw-default-error'] = ['error'];
// not yet supported
const noThrowDefaultErrorNotYetSupported = [
'aws-secretsmanager',
'core',
'custom-resources',
'region-info',
Expand Down
16 changes: 8 additions & 8 deletions packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Schedule } from '../../aws-events';
import * as iam from '../../aws-iam';
import * as kms from '../../aws-kms';
import * as lambda from '../../aws-lambda';
import { Duration, Resource, Stack } from '../../core';
import { Duration, Resource, Stack, UnscopedValidationError, ValidationError } from '../../core';
import { addConstructMetadata } from '../../core/lib/metadata-resource';
import { propertyInjectable } from '../../core/lib/prop-injectable';

Expand Down Expand Up @@ -99,7 +99,7 @@ export class RotationSchedule extends Resource {
addConstructMetadata(this, props);

if ((!props.rotationLambda && !props.hostedRotation) || (props.rotationLambda && props.hostedRotation)) {
throw new Error('One of `rotationLambda` or `hostedRotation` must be specified.');
throw new ValidationError('One of `rotationLambda` or `hostedRotation` must be specified.', this);
}

if (props.rotationLambda?.permissionsNode.defaultChild) {
Expand Down Expand Up @@ -141,10 +141,10 @@ export class RotationSchedule extends Resource {
const automaticallyAfterMillis = props.automaticallyAfter.toMilliseconds();
if (automaticallyAfterMillis > 0) {
if (automaticallyAfterMillis < Duration.hours(4).toMilliseconds()) {
throw new Error(`automaticallyAfter must not be smaller than 4 hours, got ${props.automaticallyAfter.toHours()} hours`);
throw new ValidationError(`automaticallyAfter must not be smaller than 4 hours, got ${props.automaticallyAfter.toHours()} hours`, this);
}
if (automaticallyAfterMillis > Duration.days(1000).toMilliseconds()) {
throw new Error(`automaticallyAfter must not be greater than 1000 days, got ${props.automaticallyAfter.toDays()} days`);
throw new ValidationError(`automaticallyAfter must not be greater than 1000 days, got ${props.automaticallyAfter.toDays()} days`, this);
}
scheduleExpression = Schedule.rate(props.automaticallyAfter).expressionString;
}
Expand Down Expand Up @@ -305,7 +305,7 @@ export class HostedRotation implements ec2.IConnectable {
private readonly masterSecret?: ISecret,
) {
if (type.isMultiUser && !masterSecret) {
throw new Error('The `masterSecret` must be specified when using the multi user scheme.');
throw new UnscopedValidationError('The `masterSecret` must be specified when using the multi user scheme.');
}
}

Expand All @@ -317,7 +317,7 @@ export class HostedRotation implements ec2.IConnectable {
Stack.of(scope).addTransform('AWS::SecretsManager-2020-07-23');

if (!this.props.vpc && this.props.securityGroups) {
throw new Error('`vpc` must be specified when specifying `securityGroups`.');
throw new ValidationError('`vpc` must be specified when specifying `securityGroups`.', secret);
}

if (this.props.vpc) {
Expand Down Expand Up @@ -361,12 +361,12 @@ export class HostedRotation implements ec2.IConnectable {
*/
public get connections() {
if (!this.props.vpc) {
throw new Error('Cannot use connections for a hosted rotation that is not deployed in a VPC');
throw new UnscopedValidationError('Cannot use connections for a hosted rotation that is not deployed in a VPC');
}

// If we are in a vpc and bind() has been called _connections should be defined
if (!this._connections) {
throw new Error('Cannot use connections for a hosted rotation that has not been bound to a secret');
throw new UnscopedValidationError('Cannot use connections for a hosted rotation that has not been bound to a secret');
}

return this._connections;
Expand Down
10 changes: 5 additions & 5 deletions packages/aws-cdk-lib/aws-secretsmanager/lib/secret-rotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ISecret } from './secret';
import * as ec2 from '../../aws-ec2';
import * as lambda from '../../aws-lambda';
import * as serverless from '../../aws-sam';
import { Duration, Names, Stack, Token, CfnMapping, Aws, RemovalPolicy } from '../../core';
import { Duration, Names, Stack, Token, CfnMapping, Aws, RemovalPolicy, ValidationError, UnscopedValidationError } from '../../core';

/**
* Options for a SecretRotationApplication
Expand Down Expand Up @@ -150,7 +150,7 @@ export class SecretRotationApplication {
} else if (partition === 'aws-us-gov') {
return `arn:aws-us-gov:serverlessrepo:us-gov-west-1:023102451235:applications/${this.applicationName}`;
} else {
throw new Error(`unsupported partition: ${partition}`);
throw new UnscopedValidationError(`unsupported partition: ${partition}`);
}
}

Expand All @@ -166,7 +166,7 @@ export class SecretRotationApplication {
} else if (partition === 'aws-us-gov') {
return '1.1.213';
} else {
throw new Error(`unsupported partition: ${partition}`);
throw new UnscopedValidationError(`unsupported partition: ${partition}`);
}
}
}
Expand Down Expand Up @@ -277,11 +277,11 @@ export class SecretRotation extends Construct {
super(scope, id);

if (!props.target.connections.defaultPort) {
throw new Error('The `target` connections must have a default port range.');
throw new ValidationError('The `target` connections must have a default port range.', this);
}

if (props.application.isMultiUser && !props.masterSecret) {
throw new Error('The `masterSecret` must be specified for application using the multi user scheme.');
throw new ValidationError('The `masterSecret` must be specified for application using the multi user scheme.', this);
}

// Max length of 64 chars, get the last 64 chars
Expand Down
26 changes: 13 additions & 13 deletions packages/aws-cdk-lib/aws-secretsmanager/lib/secret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RotationSchedule, RotationScheduleOptions } from './rotation-schedule';
import * as secretsmanager from './secretsmanager.generated';
import * as iam from '../../aws-iam';
import * as kms from '../../aws-kms';
import { ArnFormat, FeatureFlags, Fn, IResolveContext, IResource, Lazy, RemovalPolicy, Resource, ResourceProps, SecretValue, Stack, Token, TokenComparison } from '../../core';
import { ArnFormat, FeatureFlags, Fn, IResolveContext, IResource, Lazy, RemovalPolicy, Resource, ResourceProps, SecretValue, Stack, Token, TokenComparison, UnscopedValidationError, ValidationError } from '../../core';
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
import { propertyInjectable } from '../../core/lib/prop-injectable';
import * as cxapi from '../../cx-api';
Expand Down Expand Up @@ -289,7 +289,7 @@ export class SecretStringValueBeta1 {
*/
public static fromToken(secretValueFromToken: string) {
if (!Token.isUnresolved(secretValueFromToken)) {
throw new Error('SecretStringValueBeta1 appears to be plaintext (unsafe) string (or resolved Token); use fromUnsafePlaintext if this is intentional');
throw new UnscopedValidationError('SecretStringValueBeta1 appears to be plaintext (unsafe) string (or resolved Token); use fromUnsafePlaintext if this is intentional');
}
return new SecretStringValueBeta1(secretValueFromToken);
}
Expand Down Expand Up @@ -391,7 +391,7 @@ abstract class SecretBase extends Resource implements ISecret {

// Throw if secret is not imported and it's shared cross account and no KMS key is provided
if (this instanceof Secret && result.resourceStatement && (!this.encryptionKey && crossAccount === TokenComparison.DIFFERENT)) {
throw new Error('KMS Key must be provided for cross account access to Secret');
throw new ValidationError('KMS Key must be provided for cross account access to Secret', this);
}

return result;
Expand All @@ -415,7 +415,7 @@ abstract class SecretBase extends Resource implements ISecret {

// Throw if secret is not imported and it's shared cross account and no KMS key is provided
if (this instanceof Secret && result.resourceStatement && !this.encryptionKey) {
throw new Error('KMS Key must be provided for cross account access to Secret');
throw new ValidationError('KMS Key must be provided for cross account access to Secret', this);
}

return result;
Expand Down Expand Up @@ -478,7 +478,7 @@ abstract class SecretBase extends Resource implements ISecret {
const existing = this.node.tryFindChild(id);

if (existing) {
throw new Error('Secret is already attached to a target.');
throw new ValidationError('Secret is already attached to a target.', this);
}

return new SecretTargetAttachment(this, id, {
Expand Down Expand Up @@ -587,17 +587,17 @@ export class Secret extends SecretBase {

if (attrs.secretArn) {
if (attrs.secretCompleteArn || attrs.secretPartialArn) {
throw new Error('cannot use `secretArn` with `secretCompleteArn` or `secretPartialArn`');
throw new ValidationError('cannot use `secretArn` with `secretCompleteArn` or `secretPartialArn`', scope);
}
secretArn = attrs.secretArn;
secretArnIsPartial = false;
} else {
if ((attrs.secretCompleteArn && attrs.secretPartialArn) ||
(!attrs.secretCompleteArn && !attrs.secretPartialArn)) {
throw new Error('must use only one of `secretCompleteArn` or `secretPartialArn`');
throw new ValidationError('must use only one of `secretCompleteArn` or `secretPartialArn`', scope);
}
if (attrs.secretCompleteArn && !arnIsComplete(attrs.secretCompleteArn)) {
throw new Error('`secretCompleteArn` does not appear to be complete; missing 6-character suffix');
throw new ValidationError('`secretCompleteArn` does not appear to be complete; missing 6-character suffix', scope);
}
[secretArn, secretArnIsPartial] = attrs.secretCompleteArn ? [attrs.secretCompleteArn, false] : [attrs.secretPartialArn!, true];
}
Expand Down Expand Up @@ -636,15 +636,15 @@ export class Secret extends SecretBase {
if (props.generateSecretString &&
(props.generateSecretString.secretStringTemplate || props.generateSecretString.generateStringKey) &&
!(props.generateSecretString.secretStringTemplate && props.generateSecretString.generateStringKey)) {
throw new Error('`secretStringTemplate` and `generateStringKey` must be specified together.');
throw new ValidationError('`secretStringTemplate` and `generateStringKey` must be specified together.', this);
}

if ((props.generateSecretString ? 1 : 0)
+ (props.secretStringBeta1 ? 1 : 0)
+ (props.secretStringValue ? 1 : 0)
+ (props.secretObjectValue ? 1 : 0)
> 1) {
throw new Error('Cannot specify more than one of `generateSecretString`, `secretStringValue`, `secretObjectValue`, and `secretStringBeta1`.');
throw new ValidationError('Cannot specify more than one of `generateSecretString`, `secretStringValue`, `secretObjectValue`, and `secretStringBeta1`.', this);
}

const secretString = props.secretObjectValue
Expand Down Expand Up @@ -723,7 +723,7 @@ export class Secret extends SecretBase {
public addReplicaRegion(region: string, encryptionKey?: kms.IKey): void {
const stack = Stack.of(this);
if (!Token.isUnresolved(stack.region) && !Token.isUnresolved(region) && region === stack.region) {
throw new Error('Cannot add the region where this stack is deployed as a replica region.');
throw new ValidationError('Cannot add the region where this stack is deployed as a replica region.', this);
}

this.replicaRegions.push({
Expand Down Expand Up @@ -996,7 +996,7 @@ function parseSecretName(construct: IConstruct, secretArn: string) {
const hasSecretsSuffix = lastHyphenIndex !== -1 && resourceName.slice(lastHyphenIndex + 1).length === 6;
return hasSecretsSuffix ? resourceName.slice(0, lastHyphenIndex) : resourceName;
}
throw new Error('invalid ARN format; no secret name provided');
throw new ValidationError('invalid ARN format; no secret name provided', construct);
}

/**
Expand All @@ -1011,7 +1011,7 @@ function parseSecretName(construct: IConstruct, secretArn: string) {
function parseSecretNameForOwnedSecret(construct: Construct, secretArn: string, secretName?: string) {
const resourceName = Stack.of(construct).splitArn(secretArn, ArnFormat.COLON_RESOURCE_NAME).resourceName;
if (!resourceName) {
throw new Error('invalid ARN format; no secret name provided');
throw new ValidationError('invalid ARN format; no secret name provided', construct);
}

// Secret name was explicitly provided, but is unresolved; best option is to use it directly.
Expand Down
Loading