Skip to content

Commit

Permalink
chore: Upgrading CDK version & moving to SecretValue with sensitive f…
Browse files Browse the repository at this point in the history
…ields (#45)

This PR:
- adds dependency on the recent AWS CDK library 
- [BREAKING] moves to using SecretValue for sensitive fields 

Fixes #35
  • Loading branch information
rpawlaszek authored Nov 27, 2023
1 parent 19a915e commit 97cf86b
Show file tree
Hide file tree
Showing 59 changed files with 617 additions and 1,573 deletions.
6 changes: 3 additions & 3 deletions .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion .projenrc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { CdklabsConstructLibrary } from 'cdklabs-projen-project-types';
import { Stability } from 'projen/lib/cdk';

const cdkVersion = '2.84.0';
const cdkVersion = '2.110.1';
const project = new CdklabsConstructLibrary({
name: '@cdklabs/cdk-appflow',
author: 'Amazon Web Services',
Expand Down
232 changes: 109 additions & 123 deletions API.md

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ For more information, see the [Amazon AppFlow User Guide](https://docs.aws.amazo
## Example

```ts
import { SecretValue } from 'aws-cdk-lib';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { ISecret } from 'aws-cdk-lib/aws-secretsmanager';
import {
Expand All @@ -30,8 +31,8 @@ import {
} from '@cdklabs/cdk-appflow';

declare const clientSecret: ISecret;
declare const accessToken: string;
declare const refreshToken: string;
declare const accessToken: SecretValue;
declare const refreshToken: SecretValue;
declare const instanceUrl: string;

const profile = new SalesforceConnectorProfile(this, 'MyConnectorProfile', {
Expand Down Expand Up @@ -188,7 +189,7 @@ It is *recommended* to follow [data protection mechanisms for Amazon AppFlow](ht

## Confidential information

Amazon AppFlow application integration is done using `ConnectionProfiles`. A `ConnectionProfile` requires providing sensitive information in the form of e.g. access and refresh tokens. It is *recommended* that such information is stored securely and passed to AWS CDK securely. All sensitive fields are effectively `IResolvable` and this means they can be resolved at deploy time. With that one should follow the [best practices for credentials with CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/security-best-practices.html#creds).
Amazon AppFlow application integration is done using `ConnectionProfiles`. A `ConnectionProfile` requires providing sensitive information in the form of e.g. access and refresh tokens. It is *recommended* that such information is stored securely and passed to AWS CDK securely. All sensitive fields are effectively `IResolvable` and this means they can be resolved at deploy time. With that one should follow the [best practices for credentials with CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/security-best-practices.html#creds). In this library, the sensitive fields are typed as `SecretValue` to emphasize these should not be plain strings.

An example of using a predefined AWS Secrets Manager secret for storing sensitive information can be found below:

Expand All @@ -204,9 +205,9 @@ const profile = new GoogleAnalytics4ConnectorProfile(stack, 'GA4Connector', {
oAuth: {
flow: {
refreshTokenGrant: {
refreshToken: secret.secretValueFromJson('refreshToken').toString(),
clientId: secret.secretValueFromJson('clientId').toString(),
clientSecret: secret.secretValueFromJson('clientSecret').toString(),
refreshToken: secret.secretValueFromJson('refreshToken'),
clientId: secret.secretValueFromJson('clientId'),
clientSecret: secret.secretValueFromJson('clientSecret'),
},
},
},
Expand Down
12 changes: 6 additions & 6 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions src/googleanalytics4/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { SecretValue } from 'aws-cdk-lib';
import { CfnConnectorProfile } from 'aws-cdk-lib/aws-appflow';
import { Construct } from 'constructs';
import { GoogleAnalytics4ConnectorType } from './type';
Expand Down Expand Up @@ -35,17 +36,17 @@ export interface GoogleAnalytics4RefreshTokenGrantFlow {
/**
* A non-expired refresh token.
*/
readonly refreshToken?: string;
readonly refreshToken?: SecretValue;

/**
* The secret of the client app.
*/
readonly clientSecret?: string;
readonly clientSecret?: SecretValue;

/**
* The id of the client app.
*/
readonly clientId?: string;
readonly clientId?: SecretValue;
}

/**
Expand All @@ -67,7 +68,7 @@ export interface GoogleAnalytics4OAuthSettings {
*
* @default Retrieves a fresh accessToken with the information in the [flow property]{@link GoogleAnalytics4OAuthSettings#flow}
*/
readonly accessToken?: string;
readonly accessToken?: SecretValue;

/**
* The OAuth flow used for obtaining a new accessToken when the old is not present or expired.
Expand Down Expand Up @@ -124,10 +125,10 @@ export class GoogleAnalytics4ConnectorProfile extends ConnectorProfileBase {
customConnector: {
oauth2: {
// INFO: when using Refresh Token Grant Flow - access token property is required
accessToken: properties.oAuth.accessToken ?? 'dummyAccessToken',
refreshToken: properties.oAuth.flow?.refreshTokenGrant.refreshToken,
clientId: properties.oAuth.flow?.refreshTokenGrant.clientId,
clientSecret: properties.oAuth.flow?.refreshTokenGrant.clientSecret,
accessToken: properties.oAuth.accessToken?.unsafeUnwrap() ?? 'dummyAccessToken',
refreshToken: properties.oAuth.flow?.refreshTokenGrant.refreshToken?.unsafeUnwrap(),
clientId: properties.oAuth.flow?.refreshTokenGrant.clientId?.unsafeUnwrap(),
clientSecret: properties.oAuth.flow?.refreshTokenGrant.clientSecret?.unsafeUnwrap(),
},
authenticationType: ConnectorAuthenticationType.OAUTH2,
},
Expand Down
13 changes: 7 additions & 6 deletions src/marketo/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { SecretValue } from 'aws-cdk-lib';
import { CfnConnectorProfile } from 'aws-cdk-lib/aws-appflow';
import { Construct } from 'constructs';
import { MarketoConnectorType } from './type';
Expand All @@ -13,16 +14,16 @@ export interface MarketoConnectorProfileProps extends ConnectorProfileProps {
}

export interface MarketoOAuthClientCredentialsFlow {
readonly clientId: string;
readonly clientSecret: string;
readonly clientId: SecretValue;
readonly clientSecret: SecretValue;
}

export interface MarketoOAuthFlow {
readonly clientCredentials: MarketoOAuthClientCredentialsFlow;
}

export interface MarketoOAuthSettings {
readonly accessToken?: string;
readonly accessToken?: SecretValue;
readonly flow: MarketoOAuthFlow;
}

Expand Down Expand Up @@ -53,9 +54,9 @@ export class MarketoConnectorProfile extends ConnectorProfileBase {
const properties = (props as MarketoConnectorProfileProps);
return {
marketo: {
accessToken: properties.oAuth.accessToken,
clientId: properties.oAuth.flow.clientCredentials.clientId,
clientSecret: properties.oAuth.flow.clientCredentials.clientSecret,
accessToken: properties.oAuth.accessToken?.unsafeUnwrap(),
clientId: properties.oAuth.flow.clientCredentials.clientId.unsafeUnwrap(),
clientSecret: properties.oAuth.flow.clientCredentials.clientSecret.unsafeUnwrap(),
},
};
}
Expand Down
17 changes: 9 additions & 8 deletions src/microsoftdynamics365/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { SecretValue } from 'aws-cdk-lib';
import { CfnConnectorProfile } from 'aws-cdk-lib/aws-appflow';
import { Construct } from 'constructs';
import { MicrosoftDynamics365ConnectorType } from './type';
Expand All @@ -20,9 +21,9 @@ export interface MicrosoftDynamics365OAuthEndpointsSettings {
}

export interface MicrosoftDynamics365RefreshTokenGrantFlow {
readonly refreshToken?: string;
readonly clientSecret?: string;
readonly clientId?: string;
readonly refreshToken?: SecretValue;
readonly clientSecret?: SecretValue;
readonly clientId?: SecretValue;
}

export interface MicrosoftDynamics365OAuthFlow {
Expand All @@ -36,7 +37,7 @@ export interface MicrosoftDynamics365OAuthSettings {
*
* Note that if only the access token is provided AppFlow is not able to retrieve a fresh access token when the current one is expired
*/
readonly accessToken?: string;
readonly accessToken?: SecretValue;

readonly flow?: MicrosoftDynamics365OAuthFlow;

Expand Down Expand Up @@ -93,10 +94,10 @@ export class MicrosoftDynamics365ConnectorProfile extends ConnectorProfileBase {
customConnector: {
oauth2: {
// INFO: when using Refresh Token Grant Flow - access token property is required
accessToken: properties.oAuth.accessToken ?? 'dummyAccessToken',
refreshToken: properties.oAuth.flow?.refreshTokenGrant.refreshToken ?? 'dummyRefreshToken',
clientId: properties.oAuth.flow?.refreshTokenGrant.clientId,
clientSecret: properties.oAuth.flow?.refreshTokenGrant.clientSecret,
accessToken: properties.oAuth.accessToken?.unsafeUnwrap() ?? 'dummyAccessToken',
refreshToken: properties.oAuth.flow?.refreshTokenGrant.refreshToken?.unsafeUnwrap() ?? 'dummyRefreshToken',
clientId: properties.oAuth.flow?.refreshTokenGrant.clientId?.unsafeUnwrap(),
clientSecret: properties.oAuth.flow?.refreshTokenGrant.clientSecret?.unsafeUnwrap(),
},
authenticationType: ConnectorAuthenticationType.OAUTH2,
},
Expand Down
17 changes: 9 additions & 8 deletions src/microsoftsharepointonline/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { SecretValue } from 'aws-cdk-lib';
import { CfnConnectorProfile } from 'aws-cdk-lib/aws-appflow';
import { Construct } from 'constructs';
import { MicrosoftSharepointOnlineConnectorType } from './type';
Expand All @@ -19,9 +20,9 @@ export interface MicrosoftSharepointOnlineOAuthEndpointsSettings {
}

export interface MicrosoftSharepointOnlineRefreshTokenGrantFlow {
readonly refreshToken?: string;
readonly clientSecret?: string;
readonly clientId?: string;
readonly refreshToken?: SecretValue;
readonly clientSecret?: SecretValue;
readonly clientId?: SecretValue;
}

export interface MicrosoftSharepointOnlineOAuthFlow {
Expand All @@ -35,7 +36,7 @@ export interface MicrosoftSharepointOnlineOAuthSettings {
*
* Note that if only the access token is provided AppFlow is not able to retrieve a fresh access token when the current one is expired
*/
readonly accessToken?: string;
readonly accessToken?: SecretValue;

readonly flow?: MicrosoftSharepointOnlineOAuthFlow;

Expand Down Expand Up @@ -89,10 +90,10 @@ export class MicrosoftSharepointOnlineConnectorProfile extends ConnectorProfileB
customConnector: {
oauth2: {
// INFO: when using Refresh Token Grant Flow - access token property is required
accessToken: properties.oAuth.accessToken ?? 'dummyAccessToken',
refreshToken: properties.oAuth.flow?.refreshTokenGrant.refreshToken,
clientId: properties.oAuth.flow?.refreshTokenGrant.clientId,
clientSecret: properties.oAuth.flow?.refreshTokenGrant.clientSecret,
accessToken: properties.oAuth.accessToken?.unsafeUnwrap() ?? 'dummyAccessToken',
refreshToken: properties.oAuth.flow?.refreshTokenGrant.refreshToken?.unsafeUnwrap(),
clientId: properties.oAuth.flow?.refreshTokenGrant.clientId?.unsafeUnwrap(),
clientSecret: properties.oAuth.flow?.refreshTokenGrant.clientSecret?.unsafeUnwrap(),
},
authenticationType: ConnectorAuthenticationType.OAUTH2,
},
Expand Down
6 changes: 3 additions & 3 deletions src/redshift/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { ICluster } from '@aws-cdk/aws-redshift-alpha';
import { Aws } from 'aws-cdk-lib';
import { Aws, SecretValue } from 'aws-cdk-lib';
import { CfnConnectorProfile } from 'aws-cdk-lib/aws-appflow';
import { Effect, IRole, Policy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from 'aws-cdk-lib/custom-resources';
Expand All @@ -15,7 +15,7 @@ import { ConnectorProfileBase, ConnectorProfileProps } from '../core/connectors/

export interface RedshiftConnectorBasicCredentials {
readonly username?: string;
readonly password?: string;
readonly password?: SecretValue;
}

export interface RedshiftConnectorProfileProps extends ConnectorProfileProps {
Expand Down Expand Up @@ -105,7 +105,7 @@ export class RedshiftConnectorProfile extends ConnectorProfileBase {
return {
redshift: properties && {
username: properties.basicAuth.username,
password: properties.basicAuth.password,
password: properties.basicAuth.password?.unsafeUnwrap(),
},
};
}
Expand Down
13 changes: 7 additions & 6 deletions src/salesforce-marketing-cloud/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { SecretValue } from 'aws-cdk-lib';
import { CfnConnectorProfile } from 'aws-cdk-lib/aws-appflow';
import { Construct } from 'constructs';
import { SalesforceMarketingCloudConnectorType } from './type';
Expand All @@ -19,16 +20,16 @@ export interface SalesforceMarketingCloudOAuthEndpoints {
}

export interface SalesforceMarketingCloudOAuthClientSettings {
readonly clientId: string;
readonly clientSecret: string;
readonly clientId: SecretValue;
readonly clientSecret: SecretValue;
}

export interface SalesforceMarketingCloudFlowSettings {
readonly clientCredentials: SalesforceMarketingCloudOAuthClientSettings;
}

export interface SalesforceMarketingCloudOAuthSettings {
readonly accessToken?: string;
readonly accessToken?: SecretValue;
readonly flow?: SalesforceMarketingCloudFlowSettings;
readonly endpoints: SalesforceMarketingCloudOAuthEndpoints;
}
Expand Down Expand Up @@ -71,9 +72,9 @@ export class SalesforceMarketingCloudConnectorProfile extends ConnectorProfileBa
return {
customConnector: {
oauth2: {
accessToken: properties.oAuth.accessToken,
clientId: properties.oAuth.flow?.clientCredentials.clientId,
clientSecret: properties.oAuth.flow?.clientCredentials.clientSecret,
accessToken: properties.oAuth.accessToken?.unsafeUnwrap(),
clientId: properties.oAuth.flow?.clientCredentials.clientId?.unsafeUnwrap(),
clientSecret: properties.oAuth.flow?.clientCredentials.clientSecret?.unsafeUnwrap(),
},
authenticationType: ConnectorAuthenticationType.OAUTH2,
},
Expand Down
Loading

0 comments on commit 97cf86b

Please sign in to comment.