Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
29 changes: 28 additions & 1 deletion packages/@aws-cdk/aws-s3tables-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,36 @@ const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', {
});
```

### Controlling Table Permissions

```ts
// Grant the principal read permissions to the table
const accountId = '123456789012'
table.grantRead(new iam.AccountPrincipal(accountId));

// Grant the role write permissions to the table
const role = new iam.Role(stack, 'MyRole', { assumedBy: new iam.ServicePrincipal('sample') });
table.grantWrite(role);

// Grant the user read and write permissions to the table
table.grantReadWrite(new iam.User(stack, 'MyUser'));

// Grant an account permissions to the table
table.grantReadWrite(new iam.AccountPrincipal(accountId));

// Add custom resource policy statements
const permissions = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3tables:*'],
principals: [ new iam.ServicePrincipal('example.aws.internal') ],
resources: ['*']
});

table.addToResourcePolicy(permissions);
```

## Coming Soon

L2 Construct support for:

- Table Policy
- KMS encryption support for Tables
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-s3tables-alpha/awslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"duration-prop-name:@aws-cdk/aws-s3tables-alpha.UnreferencedFileRemoval.noncurrentDays",
"duration-prop-type:@aws-cdk/aws-s3tables-alpha.UnreferencedFileRemoval.unreferencedDays",
"duration-prop-name:@aws-cdk/aws-s3tables-alpha.UnreferencedFileRemoval.unreferencedDays",
"attribute-tag:@aws-cdk/aws-s3tables-alpha.TableBucket.tableBucketPolicy"
"attribute-tag:@aws-cdk/aws-s3tables-alpha.TableBucket.tableBucketPolicy",
"attribute-tag:@aws-cdk/aws-s3tables-alpha.Table.tablePolicy",
"props-physical-name:@aws-cdk/aws-s3tables-alpha.TablePolicyProps"
]
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-s3tables-alpha/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './table-bucket';
export * from './table-bucket-policy';
export * from './namespace';
export * from './table';
export * from './table-policy';
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-s3tables-alpha/lib/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Table Bucket
// Read priveleges
export const TABLE_BUCKET_READ_ACCESS = [
's3tables:Get*',
Expand All @@ -21,6 +22,22 @@ export const TABLE_BUCKET_READ_WRITE_ACCESS = [...new Set([
...TABLE_BUCKET_WRITE_ACCESS,
])];

// Table
// Read priveleges
export const TABLE_READ_ACCESS = [
's3tables:Get*',
];
// Write priveleges
export const TABLE_WRITE_ACCESS = [
's3tables:PutTableData',
's3tables:UpdateTableMetadataLocation',
's3tables:RenameTable',
];
export const TABLE_READ_WRITE_ACCESS = [...new Set([
...TABLE_READ_ACCESS,
...TABLE_WRITE_ACCESS,
])];

// Permissions for user defined KMS Keys
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html
export const KEY_READ_ACCESS = [
Expand Down
66 changes: 66 additions & 0 deletions packages/@aws-cdk/aws-s3tables-alpha/lib/table-policy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Construct } from 'constructs';
import { CfnTablePolicy } from 'aws-cdk-lib/aws-s3tables';
import * as iam from 'aws-cdk-lib/aws-iam';
import { RemovalPolicy, Resource } from 'aws-cdk-lib/core';
import { ITable } from './table';
import { addConstructMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable';

/**
* Parameters for constructing a TablePolicy
*/
export interface TablePolicyProps {
/**
* The associated table
*/
readonly table: ITable;
/**
* The policy document for the table's resource policy
* @default undefined An empty iam.PolicyDocument will be initialized
*/
readonly resourcePolicy?: iam.PolicyDocument;
/**
* Policy to apply when the policy is removed from this stack.
*
* @default - RemovalPolicy.DESTROY.
*/
readonly removalPolicy?: RemovalPolicy;
}

/**
* A Policy for S3 Tables.
*
* You will almost never need to use this construct directly.
* Instead, Table.addToResourcePolicy can be used to add more policies to your table directly
*/
@propertyInjectable
export class TablePolicy extends Resource {
/** Uniquely identifies this class. */
public static readonly PROPERTY_INJECTION_ID: string = '@aws-cdk.aws-s3tables-alpha.TablePolicy';
/**
* The IAM PolicyDocument containing permissions represented by this policy.
*/
public readonly document: iam.PolicyDocument;
/**
* @internal The underlying policy resource.
*/
private readonly _resource: CfnTablePolicy;

constructor(scope: Construct, id: string, props: TablePolicyProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);

// Use default policy if not provided with props
this.document = props.resourcePolicy || new iam.PolicyDocument({});

this._resource = new CfnTablePolicy(this, id, {
tableArn: props.table.tableArn,
resourcePolicy: this.document,
});

if (props.removalPolicy) {
this._resource.applyRemovalPolicy(props.removalPolicy);
}
}
}
141 changes: 140 additions & 1 deletion packages/@aws-cdk/aws-s3tables-alpha/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
Token,
} from 'aws-cdk-lib/core';
import { INamespace } from './namespace';
import { CfnTable } from 'aws-cdk-lib/aws-s3tables';
import { CfnTable, CfnTablePolicy } from 'aws-cdk-lib/aws-s3tables';
import { addConstructMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable';
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as perms from './permissions';
import { EOL } from 'os';

/**
Expand Down Expand Up @@ -39,6 +41,58 @@ export interface ITable extends IResource {
* @attribute
*/
readonly region?: string;

/**
* Adds a statement to the resource policy for a principal (i.e.
* account/role/service) to perform actions on this table.
*
* Note that the policy statement may or may not be added to the policy.
* For example, when an `ITable` is created from an existing table,
* it's not possible to tell whether the table already has a policy
* attached, let alone to re-use that policy to add more statements to it.
* So it's safest to do nothing in these cases.
*
* @param statement the policy statement to be added to the table's
* policy.
* @returns metadata about the execution of this method. If the policy
* was not added, the value of `statementAdded` will be `false`. You
* should always check this value to make sure that the operation was
* actually carried out. Otherwise, synthesis and deploy will terminate
* silently, which may be confusing.
*/
addToResourcePolicy(statement: iam.PolicyStatement): iam.AddToResourcePolicyResult;

/**
* Grant read permissions for this table to an IAM principal (Role/Group/User).
*
* If the parent TableBucket of this table has encryption,
* you should grant kms:Decrypt permission to use this key to the same principal.
*
* @param identity The principal to allow read permissions to
*/
grantRead(identity: iam.IGrantable): iam.Grant;

/**
* Grant write permissions for this table to an IAM principal (Role/Group/User).
*
* If the parent TableBucket of this table has encryption,
* you should grant kms:GenerateDataKey and kms:Decrypt permission
* to use this key to the same principal.
*
* @param identity The principal to allow write permissions to
*/
grantWrite(identity: iam.IGrantable): iam.Grant;

/**
* Grant read and write permissions for this table to an IAM principal (Role/Group/User).
*
* If the parent TableBucket of this table has encryption,
* you should grant kms:GenerateDataKey and kms:Decrypt permission
* to use this key to the same principal.
*
* @param identity The principal to allow read and write permissions to
*/
grantReadWrite(identity: iam.IGrantable): iam.Grant;
}

/**
Expand All @@ -47,6 +101,83 @@ export interface ITable extends IResource {
abstract class TableBase extends Resource implements ITable {
public abstract readonly tableName: string;
public abstract readonly tableArn: string;

/**
* The resource policy associated with this table.
*
* If `autoCreatePolicy` is true, a `TablePolicy` will be created upon the
* first call to addToResourcePolicy(s).
*/
public abstract tablePolicy?: CfnTablePolicy;

/**
* Indicates if a table resource policy should automatically created upon
* the first call to `addToResourcePolicy`.
*/
protected abstract autoCreatePolicy: boolean;

public addToResourcePolicy(
statement: iam.PolicyStatement,
): iam.AddToResourcePolicyResult {
if (!this.tablePolicy && this.autoCreatePolicy) {
this.tablePolicy = new CfnTablePolicy(this, 'DefaultPolicy', {
tableArn: this.tableArn,
resourcePolicy: new iam.PolicyDocument({}),
});
}

if (this.tablePolicy) {
this.tablePolicy.resourcePolicy.addStatements(statement);
return { statementAdded: true, policyDependable: this.tablePolicy };
}

return { statementAdded: false };
}

public grantRead(identity: iam.IGrantable) {
return this.grant(
identity,
perms.TABLE_READ_ACCESS,
this.tableArn,
);
}

public grantWrite(identity: iam.IGrantable) {
return this.grant(
identity,
perms.TABLE_WRITE_ACCESS,
this.tableArn,
);
}

public grantReadWrite(identity: iam.IGrantable) {
return this.grant(
identity,
perms.TABLE_READ_WRITE_ACCESS,
this.tableArn,
);
}

/**
* Grants the given s3tables permissions to the provided principal
* @returns Grant object
*/
private grant(
grantee: iam.IGrantable,
tableActions: string[],
resourceArn: string,
...otherResourceArns: (string | undefined)[]) {
const resources = [resourceArn, ...otherResourceArns].filter(arn => arn != undefined);

const grant = iam.Grant.addToPrincipalOrResource({
grantee,
actions: tableActions,
resourceArns: resources,
resource: this,
});

return grant;
}
}

/**
Expand Down Expand Up @@ -260,6 +391,7 @@ export class Table extends TableBase {
class Import extends TableBase {
public readonly tableName = attrs.tableName;
public readonly tableArn = tableArn;
public readonly tablePolicy?: CfnTablePolicy;
protected autoCreatePolicy: boolean = false;

/**
Expand Down Expand Up @@ -350,6 +482,13 @@ export class Table extends TableBase {
*/
public readonly namespace: INamespace;

/**
* The resource policy for this table.
*/
public readonly tablePolicy?: CfnTablePolicy;

protected autoCreatePolicy: boolean = true;

constructor(scope: Construct, id: string, props: TableProps) {
super(scope, id, {});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ class Fixture extends Stack {
});
const namespace = new Namespace(scope, 'ExampleNamespace', {
tableBucket: tableBucket,
namespaceName: 'example-namespace-1',
namespaceName: 'example_namespace_1',
});
const table = new Table(scope, 'ExampleTable', {
tableName: 'example_table',
namespace,
openTableFormat: OpenTableFormat.ICEBERG,
});

const stack = this;
/// here
}
Expand Down

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

Loading
Loading