@@ -293,7 +293,7 @@ func TestSchemaScopeOps(t *testing.T) {
293293func TestTableScopeOps (t * testing.T ) {
294294 q := preparedPolicy (t , twoOrgFixture ())
295295
296- for _ , op := range []string {"SelectFromColumns" , "FilterTables" , "ShowColumns" , "FilterColumns" } {
296+ for _ , op := range []string {"SelectFromColumns" , "ShowCreateTable" , " FilterTables" , "ShowColumns" , "FilterColumns" } {
297297 if ! evalAllow (t , q , buildInput ("42" , op , tableResource ("org_42" , "public" , "events" ))) {
298298 t .Errorf ("%s on own catalog must allow" , op )
299299 }
@@ -306,6 +306,30 @@ func TestTableScopeOps(t *testing.T) {
306306 }
307307}
308308
309+ func TestShowCreateTableReadAuthority (t * testing.T ) {
310+ q := preparedPolicy (t , twoOrgFixture ())
311+ for _ , tc := range []struct {
312+ name string
313+ user string
314+ catalog string
315+ allowed bool
316+ }{
317+ {"admin bundle read grant" , AdminPrincipal , "org_42" , true },
318+ {"admin orphan catalog" , AdminPrincipal , "org_99" , false },
319+ {"observer" , ObserverPrincipal , "org_42" , false },
320+ {"tenant system catalog" , "42" , "system" , false },
321+ {"admin system catalog" , AdminPrincipal , "system" , false },
322+ {"observer system catalog" , ObserverPrincipal , "system" , false },
323+ } {
324+ t .Run (tc .name , func (t * testing.T ) {
325+ got := evalAllow (t , q , buildInput (tc .user , "ShowCreateTable" , tableResource (tc .catalog , "metadata" , "table_comments" )))
326+ if got != tc .allowed {
327+ t .Errorf ("ShowCreateTable = %v, want %v" , got , tc .allowed )
328+ }
329+ })
330+ }
331+ }
332+
309333// --------------------------------------------------------------------------
310334// Tenant writes: a customer has full DuckLake DDL/DML authority inside the
311335// catalog projected for its org, and no write authority anywhere else.
@@ -853,6 +877,7 @@ func TestIsolationMatrix(t *testing.T) {
853877 {"FilterSchemas" , func (c string ) map [string ]interface {} { return schemaResource (c , "s" ) }},
854878 {"ShowTables" , func (c string ) map [string ]interface {} { return schemaResource (c , "s" ) }},
855879 {"SelectFromColumns" , func (c string ) map [string ]interface {} { return tableResource (c , "s" , "t" ) }},
880+ {"ShowCreateTable" , func (c string ) map [string ]interface {} { return tableResource (c , "s" , "t" ) }},
856881 {"FilterTables" , func (c string ) map [string ]interface {} { return tableResource (c , "s" , "t" ) }},
857882 {"ShowColumns" , func (c string ) map [string ]interface {} { return tableResource (c , "s" , "t" ) }},
858883 {"FilterColumns" , func (c string ) map [string ]interface {} { return tableResource (c , "s" , "t" ) }},
@@ -1959,7 +1984,7 @@ func TestScopedGroupReadsOnlyItsOwnSchemas(t *testing.T) {
19591984 {"a shared schema it holds no grant in" , "public" , "anything" , false },
19601985 } {
19611986 t .Run (tc .name , func (t * testing.T ) {
1962- for _ , op := range []string {"SelectFromColumns" , "FilterTables" , "ShowColumns" } {
1987+ for _ , op := range []string {"SelectFromColumns" , "ShowCreateTable" , " FilterTables" , "ShowColumns" } {
19631988 got := evalAllow (t , q , tableInput (op , "org_acme" , tc .schema , tc .table , scopedIdentity ()))
19641989 if got != tc .want {
19651990 t .Errorf ("%s on %s.%s = %v, want %v" , op , tc .schema , tc .table , got , tc .want )
@@ -1978,11 +2003,13 @@ func TestScopedGroupRelationGrantDoesNotLeakItsSchema(t *testing.T) {
19782003 gc , gs := scopedFixture ()
19792004 q := preparedScopedPolicy (t , gc , gs )
19802005
1981- if ! evalAllow (t , q , tableInput ("SelectFromColumns" , "org_acme" , "posthog" , "events" , scopedIdentity ())) {
1982- t .Error ("granted relation posthog.events must be readable" )
1983- }
1984- if evalAllow (t , q , tableInput ("SelectFromColumns" , "org_acme" , "posthog" , "persons" , scopedIdentity ())) {
1985- t .Error ("posthog.persons was NOT granted and must not be readable" )
2006+ for _ , op := range []string {"SelectFromColumns" , "ShowCreateTable" } {
2007+ if ! evalAllow (t , q , tableInput (op , "org_acme" , "posthog" , "events" , scopedIdentity ())) {
2008+ t .Errorf ("%s must allow the granted relation posthog.events" , op )
2009+ }
2010+ if evalAllow (t , q , tableInput (op , "org_acme" , "posthog" , "persons" , scopedIdentity ())) {
2011+ t .Errorf ("%s must deny the ungranted relation posthog.persons" , op )
2012+ }
19862013 }
19872014}
19882015
@@ -2013,7 +2040,7 @@ func TestScopedGroupStillCannotCrossTenants(t *testing.T) {
20132040 gc , gs := scopedFixture ()
20142041 q := preparedScopedPolicy (t , gc , gs )
20152042
2016- for _ , op := range []string {"SelectFromColumns" , "FilterTables" , "ShowColumns" } {
2043+ for _ , op := range []string {"SelectFromColumns" , "ShowCreateTable" , " FilterTables" , "ShowColumns" } {
20172044 if evalAllow (t , q , tableInput (op , "org_other" , "posthog_7" , "events" , scopedIdentity ())) {
20182045 t .Errorf ("%s reached another tenant's catalog" , op )
20192046 }
0 commit comments