Skip to content

Commit

Permalink
fix(redshift-alpha): extract tableName from custom resource functions
Browse files Browse the repository at this point in the history
  • Loading branch information
5d committed Dec 11, 2024
1 parent 06cdaac commit ec4892d
Show file tree
Hide file tree
Showing 31 changed files with 1,268 additions and 704 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ export async function handler(props: UserTablePrivilegesHandlerProps & ClusterPr
const clusterProps = props;

if (event.RequestType === 'Create') {
await grantPrivileges(username, tablePrivileges, clusterProps);
await grantPrivileges(username, tablePrivileges, clusterProps, event.StackId);
return { PhysicalResourceId: makePhysicalId(username, clusterProps, event.RequestId) };
} else if (event.RequestType === 'Delete') {
await revokePrivileges(username, tablePrivileges, clusterProps);
await revokePrivileges(username, tablePrivileges, clusterProps, event.StackId);
return;
} else if (event.RequestType === 'Update') {
const { replace } = await updatePrivileges(
username,
tablePrivileges,
clusterProps,
event.OldResourceProperties as unknown as UserTablePrivilegesHandlerProps & ClusterProps,
event.StackId,
);
const physicalId = replace ? makePhysicalId(username, clusterProps, event.RequestId) : event.PhysicalResourceId;
return { PhysicalResourceId: physicalId };
Expand All @@ -31,19 +32,35 @@ export async function handler(props: UserTablePrivilegesHandlerProps & ClusterPr
}
}

async function revokePrivileges(username: string, tablePrivileges: TablePrivilege[], clusterProps: ClusterProps) {
async function revokePrivileges(
username: string,
tablePrivileges: TablePrivilege[],
clusterProps: ClusterProps,
stackId: string,
) {
// Limited by human input
// eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism
await Promise.all(tablePrivileges.map(({ tableName, actions }) => {
return executeStatement(`REVOKE ${actions.join(', ')} ON ${tableName} FROM ${username}`, clusterProps);
return executeStatement(
`REVOKE ${actions.join(', ')} ON ${normalizedTableName(tableName, stackId)} FROM ${username}`,
clusterProps,
);
}));
}

async function grantPrivileges(username: string, tablePrivileges: TablePrivilege[], clusterProps: ClusterProps) {
async function grantPrivileges(
username: string,
tablePrivileges: TablePrivilege[],
clusterProps: ClusterProps,
stackId: string,
) {
// Limited by human input
// eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism
await Promise.all(tablePrivileges.map(({ tableName, actions }) => {
return executeStatement(`GRANT ${actions.join(', ')} ON ${tableName} TO ${username}`, clusterProps);
return executeStatement(
`GRANT ${actions.join(', ')} ON ${normalizedTableName(tableName, stackId)} TO ${username}`,
clusterProps,
);
}));
}

Expand All @@ -52,16 +69,17 @@ async function updatePrivileges(
tablePrivileges: TablePrivilege[],
clusterProps: ClusterProps,
oldResourceProperties: UserTablePrivilegesHandlerProps & ClusterProps,
stackId: string,
): Promise<{ replace: boolean }> {
const oldClusterProps = oldResourceProperties;
if (clusterProps.clusterName !== oldClusterProps.clusterName || clusterProps.databaseName !== oldClusterProps.databaseName) {
await grantPrivileges(username, tablePrivileges, clusterProps);
await grantPrivileges(username, tablePrivileges, clusterProps, stackId);
return { replace: true };
}

const oldUsername = oldResourceProperties.username;
if (oldUsername !== username) {
await grantPrivileges(username, tablePrivileges, clusterProps);
await grantPrivileges(username, tablePrivileges, clusterProps, stackId);
return { replace: true };
}

Expand All @@ -72,7 +90,7 @@ async function updatePrivileges(
))
));
if (tablesToRevoke.length > 0) {
await revokePrivileges(username, tablesToRevoke, clusterProps);
await revokePrivileges(username, tablesToRevoke, clusterProps, stackId);
}

const tablesToGrant = tablePrivileges.filter(({ tableId, tableName, actions }) => {
Expand All @@ -85,8 +103,21 @@ async function updatePrivileges(
return tableAdded || actionsAdded;
});
if (tablesToGrant.length > 0) {
await grantPrivileges(username, tablesToGrant, clusterProps);
await grantPrivileges(username, tablesToGrant, clusterProps, stackId);
}

return { replace: false };
}

/**
* We need this normalization logic because some of the `TableName` values
* are physical IDs generated in the `./util.ts` module.
* */
const normalizedTableName = (tableName: string, stackId: string): string => {
const segments = tableName.split(':');
const suffix = segments.slice(-1);
if (suffix != null && stackId.endsWith(suffix[0])) {
return segments.slice(-2)[0] ?? tableName;
}
return tableName;
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function handler(props: TableAndClusterProps, event: AWSLambda.Clou

if (event.RequestType === 'Create') {
tableName = await createTable(tableNamePrefix, getTableNameSuffix(props.tableName.generateSuffix), tableColumns, tableAndClusterProps);
return { PhysicalResourceId: makePhysicalId(tableNamePrefix, tableAndClusterProps, event.StackId.substring(event.StackId.length - 12)) };
return { PhysicalResourceId: makePhysicalId(tableName, tableAndClusterProps, event.StackId.substring(event.StackId.length - 12)) };
} else if (event.RequestType === 'Delete') {
await dropTable(
event.PhysicalResourceId.includes(event.StackId.substring(event.StackId.length - 12)) ? tableName : event.PhysicalResourceId,
Expand Down
54 changes: 30 additions & 24 deletions packages/@aws-cdk/aws-redshift-alpha/lib/private/privileges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ITable, TableAction } from '../table';
import { IUser } from '../user';
import { DatabaseQuery } from './database-query';
import { HandlerName } from './database-query-provider/handler-name';
import { TablePrivilege as SerializedTablePrivilege, UserTablePrivilegesHandlerProps } from './handler-props';
import { UserTablePrivilegesHandlerProps } from './handler-props';

/**
* The Redshift table and action that make up a privilege that can be granted to a Redshift user.
Expand Down Expand Up @@ -62,33 +62,25 @@ export class UserTablePrivileges extends Construct {
username: props.user.username,
tablePrivileges: cdk.Lazy.any({
produce: () => {
const reducedPrivileges = this.privileges.reduce((privileges, { table, actions }) => {
const tableId = table.node.id;
if (!(tableId in privileges)) {
privileges[tableId] = {
const groupedPrivileges = this.privileges.reduce(
(privileges, { table, actions }) => ({
...privileges,
[table.node.id]: {
actions: [
...(privileges[table.node.id]?.actions ?? []),
...actions,
],
tableName: table.tableName,
actions: [],
};
}
actions = actions.concat(privileges[tableId].actions);
if (actions.includes(TableAction.ALL)) {
actions = [TableAction.ALL];
}
if (actions.includes(TableAction.UPDATE) || actions.includes(TableAction.DELETE)) {
actions.push(TableAction.SELECT);
}
privileges[tableId] = {
tableName: table.tableName,
actions: Array.from(new Set(actions)),
};
return privileges;
}, {} as { [key: string]: { tableName: string; actions: TableAction[] } });
const serializedPrivileges: SerializedTablePrivilege[] = Object.entries(reducedPrivileges).map(([tableId, config]) => ({
},
}),
{} as Record<string, { tableName: string; actions: TableAction[] }>,
);

return Object.entries(groupedPrivileges).map(([tableId, config]) => ({
tableId,
tableName: config.tableName,
actions: config.actions.map(action => TableAction[action]),
actions: unifyTableActions(config.actions).map(action => TableAction[action]),
}));
return serializedPrivileges;
},
}) as any,
},
Expand All @@ -102,3 +94,17 @@ export class UserTablePrivileges extends Construct {
this.privileges.push({ table, actions });
}
}

const unifyTableActions = (tableActions: TableAction[]): TableAction[] => {
const set = new Set<TableAction>(tableActions);

if (set.has(TableAction.ALL)) {
return [TableAction.ALL];
}

if (set.has(TableAction.UPDATE) || set.has(TableAction.DELETE)) {
set.add(TableAction.SELECT);
}

return [...set];
};
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-redshift-alpha/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class Table extends TableBase {
properties: {
tableName: {
prefix: props.tableName ?? cdk.Names.uniqueId(this),
generateSuffix: !props.tableName ? 'true' : 'false',
generateSuffix: (props.tableName == null).toString(),
},
tableColumns: this.tableColumns,
distStyle: props.distStyle,
Expand All @@ -282,7 +282,7 @@ export class Table extends TableBase {
},
});

this.tableName = this.resource.ref;
this.tableName = props.tableName ?? this.resource.ref;
}

/**
Expand Down
Loading

0 comments on commit ec4892d

Please sign in to comment.