Skip to content

Commit 0e1b3bd

Browse files
author
EC2 Default User
committed
Adding ColumnWithTables permission grants.
1 parent fc6325a commit 0e1b3bd

File tree

3 files changed

+204
-46
lines changed

3 files changed

+204
-46
lines changed

bin/aws.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,44 @@ openTargetsStack.grantIamRead(analyticsStack.NotebookRole);
4646

4747

4848

49-
const exampleUser = iam.User.fromUserName(coreDataLake, 'exampleGrantee', 'paul0' );
49+
const exampleUser = iam.User.fromUserName(coreDataLake, 'exampleGrantee', 'paul1' );
5050

51-
var exampleGrant: DataLakeEnrollment.LakeFormationPermissionGrant = {
51+
var exampleGrant: DataLakeEnrollment.TablePermissionGrant = {
5252
tables: ["association_data", "evidence_data","target_list","disease_list"],
5353
DatabasePermissions: [DataLakeEnrollment.DatabasePermission.Alter, DataLakeEnrollment.DatabasePermission.CreateTable, DataLakeEnrollment.DatabasePermission.Drop],
5454
GrantableDatabasePermissions: [DataLakeEnrollment.DatabasePermission.Alter, DataLakeEnrollment.DatabasePermission.CreateTable, DataLakeEnrollment.DatabasePermission.Drop],
5555
TablePermissions: [DataLakeEnrollment.TablePermission.Select, DataLakeEnrollment.TablePermission.Insert, DataLakeEnrollment.TablePermission.Delete],
5656
GrantableTablePermissions: [DataLakeEnrollment.TablePermission.Select]
5757
};
5858

59-
openTargetsStack.grantLakeFormationPermissions(exampleUser, exampleGrant);
59+
openTargetsStack.grantTablePermissions(exampleUser, exampleGrant);
60+
61+
62+
63+
64+
// In the example below, we are using the compound_structures table from ChEMBL. It has the following table definition:
65+
// ['molregno', 'molfile', 'standard_inchi', 'standard_inchi_key', 'canonical_smiles']
66+
// Lets say we want to give a principal ONLY select permissions to everything in the compound_structures table BUT the 'canonical_smiles' column.
67+
68+
var exampleTableWithColumnsGrant: DataLakeEnrollment.TableWithColumnPermissionGrant = {
69+
table: "chembl_25_public_compound_structures",
70+
// Note that we are NOT including 'canonical_smiles'. That effectivley prevents this user from querying that column.
71+
columns: ['molregno', 'molfile', 'standard_inchi', 'standard_inchi_key'],
72+
DatabasePermissions: [],
73+
GrantableDatabasePermissions: [],
74+
TableColumnPermissions: [DataLakeEnrollment.TablePermission.Select],
75+
GrantableTableColumnPermissions: []
76+
};
77+
78+
var exampleTableWithColumnsGrant_WithWildCard: DataLakeEnrollment.TableWithColumnPermissionGrant = {
79+
table: "chembl_25_public_compound_structures",
80+
wildCardFilter: DataLakeEnrollment.TableWithColumnFilter.Exclude,
81+
columns: ['canonical_smiles'],
82+
DatabasePermissions: [],
83+
GrantableDatabasePermissions: [],
84+
TableColumnPermissions: [DataLakeEnrollment.TablePermission.Select],
85+
GrantableTableColumnPermissions: []
86+
};
87+
88+
// Note that exampleTableWithColumnsGrant exampleTableWithColumnsGrant_WithWildCard grants the same effecitve permissions. One just uses a the wildcard.
89+
chemblStack.grantTableWithColumnPermissions(exampleUser, exampleTableWithColumnsGrant);

lib/constructs/data-lake-enrollment.ts

Lines changed: 150 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ export class DataLakeEnrollment extends cdk.Construct {
1717
public DataSetName: string;
1818
private CoarseAthenaAccessPolicy: iam.ManagedPolicy;
1919
private CoarseResourceAccessPolicy: iam.ManagedPolicy;
20+
private CoarseIamPolciesApplied: boolean;
2021

2122
constructor(scope: cdk.Construct, id: string, props: DataLakeEnrollment.DataLakeEnrollmentProps) {
2223
super(scope, id);
2324

2425

2526
this.DataSetName = props.DataSetName;
27+
this.CoarseIamPolciesApplied = false;
2628

2729
}
2830

@@ -224,21 +226,59 @@ export class DataLakeEnrollment extends cdk.Construct {
224226
}
225227

226228

227-
private createLakeFormationPermission(resourceId: string, dataLakePrincipal: lakeformation.CfnPermissions.DataLakePrincipalProperty,
228-
resource: lakeformation.CfnPermissions.ResourceProperty, permissions: string[], grantablePremissions: string[] ){
229-
230-
new lakeformation.CfnPermissions(this, resourceId, {
231-
dataLakePrincipal: dataLakePrincipal,
232-
resource: resource,
233-
permissions: permissions,
234-
permissionsWithGrantOption: grantablePremissions
235-
});
229+
public grantTableWithColumnPermissions(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.TableWithColumnPermissionGrant){
230+
231+
const coreGrant = this.setupIamAndLakeFormationDatabasePermissionForPrincipal(principal, permissionGrant.DatabasePermissions, permissionGrant.GrantableDatabasePermissions);
232+
233+
const wildcardProperty: lakeformation.CfnPermissions.ColumnWildcardProperty = {
234+
excludedColumnNames: permissionGrant.columns
235+
};
236+
237+
var tableWithColumnsProperty : lakeformation.CfnPermissions.TableWithColumnsResourceProperty = {
238+
columnNames: permissionGrant.columns,
239+
databaseName: this.DataEnrollment.Dataset_Datalake.databaseName,
240+
name: permissionGrant.table
241+
};
242+
243+
if(permissionGrant.wildCardFilter === null){
244+
tableWithColumnsProperty = {
245+
columnNames: permissionGrant.columns,
246+
databaseName: this.DataEnrollment.Dataset_Datalake.databaseName,
247+
name: permissionGrant.table
248+
};
249+
}else{
250+
251+
if(permissionGrant.wildCardFilter == DataLakeEnrollment.TableWithColumnFilter.Include){
252+
tableWithColumnsProperty = {
253+
columnNames: permissionGrant.columns,
254+
databaseName: this.DataEnrollment.Dataset_Datalake.databaseName,
255+
name: permissionGrant.table
256+
};
257+
}
258+
259+
if(permissionGrant.wildCardFilter == DataLakeEnrollment.TableWithColumnFilter.Exclude){
260+
tableWithColumnsProperty = {
261+
databaseName: this.DataEnrollment.Dataset_Datalake.databaseName,
262+
name: permissionGrant.table,
263+
columnWildcard: {
264+
excludedColumnNames: permissionGrant.columns
265+
}
266+
};
267+
}
268+
269+
}
270+
271+
const tableWithColumnResourceProperty : lakeformation.CfnPermissions.ResourceProperty = {
272+
tableWithColumnsResource: tableWithColumnsProperty
273+
};
274+
275+
this.createLakeFormationPermission(`${coreGrant.grantIdPrefix}-${permissionGrant.table}-databaseTableWithColumnGrant`,coreGrant.dataLakePrincipal , tableWithColumnResourceProperty, permissionGrant.TableColumnPermissions, permissionGrant.GrantableTableColumnPermissions)
276+
277+
236278
}
237279

238-
public grantLakeFormationPermissions(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.LakeFormationPermissionGrant){
239-
280+
public grantDatabasePermission(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.DatabasePermissionGrant){
240281

241-
this.grantCoarseIamRead(principal);
242282

243283
var grantIdPrefix = ""
244284
var dataLakePrincipal : lakeformation.CfnPermissions.DataLakePrincipalProperty = {
@@ -260,10 +300,17 @@ export class DataLakeEnrollment extends cdk.Construct {
260300
if(resolvedPrincipalType === iam.User){
261301
const resolvedPrincipal = principal as iam.User;
262302
grantIdPrefix = `${resolvedPrincipal.userName}-${this.DataSetName}`
263-
dataLakePrincipal = { dataLakePrincipalIdentifier: resolvedPrincipal.userName };
303+
dataLakePrincipal = { dataLakePrincipalIdentifier: resolvedPrincipal.userArn };
264304
}
265305

266-
this.createLakeFormationPermission(`${grantIdPrefix}-databaseGrant`,dataLakePrincipal , databaseResourceProperty, permissionGrant.DatabasePermissions, permissionGrant.GrantableDatabasePermissions)
306+
this.createLakeFormationPermission(`${grantIdPrefix}-databaseGrant`,dataLakePrincipal , databaseResourceProperty, permissionGrant.DatabasePermissions, permissionGrant.GrantableDatabasePermissions)
307+
308+
}
309+
310+
311+
public grantTablePermissions(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.TablePermissionGrant){
312+
313+
const coreGrant = this.setupIamAndLakeFormationDatabasePermissionForPrincipal(principal, permissionGrant.DatabasePermissions, permissionGrant.GrantableDatabasePermissions);
267314

268315
permissionGrant.tables.forEach(table => {
269316
var tableResourceProperty : lakeformation.CfnPermissions.ResourceProperty = {
@@ -272,16 +319,53 @@ export class DataLakeEnrollment extends cdk.Construct {
272319
databaseName: this.DataEnrollment.Dataset_Datalake.databaseName
273320
}
274321
};
275-
this.createLakeFormationPermission(`${grantIdPrefix}-${table}-databaseTableGrant`,dataLakePrincipal , tableResourceProperty, permissionGrant.TablePermissions, permissionGrant.TablePermissions)
322+
this.createLakeFormationPermission(`${coreGrant.grantIdPrefix}-${table}-databaseTableGrant`,coreGrant.dataLakePrincipal , tableResourceProperty, permissionGrant.TablePermissions, permissionGrant.GrantableTablePermissions)
276323
});
277324

278325
}
279326

280-
private determinePrincipalType(principal: iam.IPrincipal){
327+
328+
public grantCoarseIamRead(principal: iam.IPrincipal){
329+
330+
331+
const resolvedPrincipalType = this.determinePrincipalType(principal);
332+
333+
if(resolvedPrincipalType === iam.Role){
334+
this.CoarseAthenaAccessPolicy.attachToRole(principal as iam.Role);
335+
this.CoarseResourceAccessPolicy.attachToRole(principal as iam.Role);
336+
this.CoarseIamPolciesApplied = true;
337+
return;
338+
}
339+
340+
if(resolvedPrincipalType === iam.User){
341+
this.CoarseAthenaAccessPolicy.attachToUser(principal as iam.User);
342+
this.CoarseResourceAccessPolicy.attachToUser(principal as iam.User);
343+
this.CoarseIamPolciesApplied = true;
344+
return;
345+
}
346+
347+
348+
349+
350+
351+
}
352+
353+
354+
355+
private createLakeFormationPermission(resourceId: string, dataLakePrincipal: lakeformation.CfnPermissions.DataLakePrincipalProperty, resource: lakeformation.CfnPermissions.ResourceProperty, permissions: string[], grantablePremissions: string[] ){
356+
357+
new lakeformation.CfnPermissions(this, resourceId, {
358+
dataLakePrincipal: dataLakePrincipal,
359+
resource: resource,
360+
permissions: permissions,
361+
permissionsWithGrantOption: grantablePremissions
362+
});
363+
}
364+
private determinePrincipalType(principal: iam.IPrincipal){
281365

282366
if(principal instanceof iam.Role){
283367
//return principal as iam.Role;
284-
return iam.Role;
368+
return iam.Role;
285369
}
286370

287371
if(principal instanceof iam.User){
@@ -290,45 +374,56 @@ export class DataLakeEnrollment extends cdk.Construct {
290374
}
291375

292376
if(principal instanceof cdk.Resource){
293-
377+
294378
try{
295-
const user = principal as iam.User;
379+
const user = principal as iam.User;
296380
return iam.User;
297381
} catch(exception) {
298382
console.log(exception);
299383
}
300384
try{
301385
const role = principal as iam.Role;
302-
return iam.Role;
386+
return iam.Role;
303387
} catch(exception) {
304388
console.log(exception);
305389
}
306390
}
307-
391+
308392
throw("Unable to deterimine principal type...");
309393

310394
}
311-
395+
private setupIamAndLakeFormationDatabasePermissionForPrincipal(principal: iam.IPrincipal, databasePermissions: Array<DataLakeEnrollment.DatabasePermission>, grantableDatabasePermissions: Array<DataLakeEnrollment.DatabasePermission> ){
312396

313-
public grantCoarseIamRead(principal: iam.IPrincipal){
397+
this.grantCoarseIamRead(principal);
398+
399+
var grantIdPrefix = ""
400+
var dataLakePrincipal : lakeformation.CfnPermissions.DataLakePrincipalProperty = {
401+
dataLakePrincipalIdentifier: ""
402+
};
403+
var databaseResourceProperty : lakeformation.CfnPermissions.ResourceProperty = {
404+
//dataLocationResource: {resourceArn: this.DataEnrollment.DataLakeBucketName},
405+
databaseResource: {name: this.DataEnrollment.Dataset_Datalake.databaseName}
406+
};
314407

315408
const resolvedPrincipalType = this.determinePrincipalType(principal);
316409

317-
if(resolvedPrincipalType === iam.Role){
318-
this.CoarseAthenaAccessPolicy.attachToRole(principal as iam.Role);
319-
this.CoarseResourceAccessPolicy.attachToRole(principal as iam.Role);
320-
return;
410+
if(resolvedPrincipalType === iam.Role) {
411+
const resolvedPrincipal = principal as iam.Role;
412+
grantIdPrefix = `${resolvedPrincipal.roleArn}-${this.DataSetName}`
413+
dataLakePrincipal = { dataLakePrincipalIdentifier: resolvedPrincipal.roleArn };
321414
}
322415

323416
if(resolvedPrincipalType === iam.User){
324-
this.CoarseAthenaAccessPolicy.attachToUser(principal as iam.User);
325-
this.CoarseResourceAccessPolicy.attachToUser(principal as iam.User);
326-
return;
417+
const resolvedPrincipal = principal as iam.User;
418+
grantIdPrefix = `${resolvedPrincipal.userName}-${this.DataSetName}`
419+
dataLakePrincipal = { dataLakePrincipalIdentifier: resolvedPrincipal.userArn };
327420
}
328-
329-
throw("Unable to attach policy. Principal is not a user or role.");
330421

331-
}
422+
this.grantDatabasePermission(principal, { DatabasePermissions: databasePermissions, GrantableDatabasePermissions: grantableDatabasePermissions });
423+
424+
return { grantIdPrefix: grantIdPrefix, dataLakePrincipal: dataLakePrincipal };
425+
}
426+
332427
}
333428

334429

@@ -352,20 +447,41 @@ export namespace DataLakeEnrollment
352447
Insert = 'INSERT',
353448
}
354449

450+
export enum TableWithColumnFilter {
451+
Include = "Include",
452+
Exclude = "Exclude"
453+
}
454+
355455
export interface DataLakeEnrollmentProps extends cdk.StackProps {
356456
dataLakeBucket: s3.Bucket;
357457
GlueScriptPath: string;
358458
GlueScriptArguments: any;
359459
DataSetName: string;
360460
}
361461

362-
export interface LakeFormationPermissionGrant {
462+
export interface DatabasePermissionGrant {
463+
DatabasePermissions: Array<DatabasePermission>;
464+
GrantableDatabasePermissions: Array<DatabasePermission>;
465+
}
466+
467+
468+
export interface TablePermissionGrant {
363469
tables: Array<string>;
364470
DatabasePermissions: Array<DatabasePermission>;
365471
GrantableDatabasePermissions: Array<DatabasePermission>;
366472
TablePermissions: Array<TablePermission>;
367473
GrantableTablePermissions: Array<TablePermission>;
368474
}
475+
476+
export interface TableWithColumnPermissionGrant {
477+
table: string;
478+
columns: Array<string>;
479+
wildCardFilter?: TableWithColumnFilter;
480+
DatabasePermissions: Array<DatabasePermission>;
481+
GrantableDatabasePermissions: Array<DatabasePermission>;
482+
TableColumnPermissions: Array<TablePermission>;
483+
GrantableTableColumnPermissions: Array<TablePermission>;
484+
}
369485
}
370486

371487

lib/stacks/dataset-stack.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,40 @@ import s3assets = require('@aws-cdk/aws-s3-assets');
88
import { DataSetEnrollmentProps, DataSetEnrollment } from '../constructs/data-set-enrollment';
99
import { DataLakeEnrollment } from '../constructs/data-lake-enrollment';
1010
import { DataLakeStack } from './datalake-stack';
11-
11+
1212
export interface DataSetStackProps extends cdk.StackProps {
1313
DataLake: DataLakeStack;
14-
}
14+
}
1515

1616

1717
export class DataSetStack extends cdk.Stack {
18-
18+
1919
public Enrollment: DataLakeEnrollment;
2020
public DataLake: DataLakeStack;
21-
21+
2222
constructor(scope: cdk.Construct, id: string, props: DataSetStackProps) {
2323
super(scope, id, props);
2424
this.DataLake = props.DataLake;
2525
}
26-
27-
public grantLakeFormationPermissions(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.LakeFormationPermissionGrant){
28-
26+
27+
28+
public grantDatabasePermissions( principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.DatabasePermissionGrant){
29+
this.Enrollment.grantDatabasePermission(principal, permissionGrant);
30+
}
31+
32+
public grantTablePermissions(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.TablePermissionGrant){
33+
2934
this.DataLake.grantAthenaResultsBucketPermission(principal);
30-
this.Enrollment.grantLakeFormationPermissions(principal, permissionGrant);
35+
this.Enrollment.grantTablePermissions(principal, permissionGrant);
3136
}
32-
37+
38+
public grantTableWithColumnPermissions(principal: iam.IPrincipal, permissionGrant: DataLakeEnrollment.TableWithColumnPermissionGrant){
39+
40+
this.DataLake.grantAthenaResultsBucketPermission(principal);
41+
this.Enrollment.grantTableWithColumnPermissions(principal, permissionGrant);
42+
}
43+
44+
3345
public grantIamRead(principal: iam.IPrincipal){
3446
this.DataLake.grantAthenaResultsBucketPermission(principal);
3547
this.Enrollment.grantCoarseIamRead(principal);

0 commit comments

Comments
 (0)