From 463fae6dfca262668e5b07fac34f2ad7b59a8a13 Mon Sep 17 00:00:00 2001 From: elianddb Date: Tue, 17 Mar 2026 13:26:27 -0700 Subject: [PATCH 1/4] add revision db test for user privileges when using dolt table functions --- .../sqle/enginetest/privilege_test.go | 500 +++++++++++++++--- 1 file changed, 440 insertions(+), 60 deletions(-) diff --git a/go/libraries/doltcore/sqle/enginetest/privilege_test.go b/go/libraries/doltcore/sqle/enginetest/privilege_test.go index 27f8470dd98..70d78e59fc4 100755 --- a/go/libraries/doltcore/sqle/enginetest/privilege_test.go +++ b/go/libraries/doltcore/sqle/enginetest/privilege_test.go @@ -70,9 +70,30 @@ func TestRevisionDatabasePrivileges(t *testing.T) { "Error in test setup: one or more expected tests not found. "+ "Did the name of a test change?") + mutated := make([]queries.UserPrivilegeTest, len(scripts)) + for i, script := range scripts { + script.SetUpScript = append(script.SetUpScript, revisionDatabasePrivsSetupPostfix...) + mutated[i] = script + } + runRevisionDBPrivilegeTests(t, mutated) +} + +func TestDoltOnlyRevisionTableFunctionPrivileges(t *testing.T) { + runRevisionDBPrivilegeTests(t, DoltOnlyRevisionTableFunctionPrivilegeTests) +} + +func TestDoltOnlyRevisionDatabasePrivileges(t *testing.T) { + runRevisionDBPrivilegeTests(t, DoltOnlyRevisionDbPrivilegeTests) +} + +// runRevisionDBPrivilegeTests runs each script in |scripts| against a fresh engine with the +// current database set to `mydb/b1` for every assertion. The setup script runs as root before +// assertions execute, allowing tests to establish schema, data, users, and grants. The branch +// b1 and the revision database mydb/b1 are not created by this function; each script is +// responsible for creating them in its SetUpScript. +func runRevisionDBPrivilegeTests(t *testing.T, scripts []queries.UserPrivilegeTest) { for _, script := range scripts { harness := newDoltHarness(t) - harness.configureStats = true harness.Setup(setup.MydbData, setup.MytableData) t.Run(script.Name, func(t *testing.T) { engine := mustNewEngine(t, harness) @@ -86,18 +107,11 @@ func TestRevisionDatabasePrivileges(t *testing.T) { engine.EngineAnalyzer().Catalog.MySQLDb.AddRootAccount() engine.EngineAnalyzer().Catalog.MySQLDb.SetPersister(&mysql_db.NoopPersister{}) - for _, statement := range append(script.SetUpScript, revisionDatabasePrivsSetupPostfix...) { - if harness.SkipQueryTest(statement) { - t.Skip() - } + for _, statement := range script.SetUpScript { enginetest.RunQueryWithContext(t, engine, harness, ctx, statement) } for _, assertion := range script.Assertions { - if harness.SkipQueryTest(assertion.Query) { - t.Skipf("Skipping query %s", assertion.Query) - } - user := assertion.User host := assertion.Host if user == "" { @@ -971,55 +985,421 @@ var DoltOnlyRevisionDbPrivilegeTests = []queries.UserPrivilegeTest{ }, } -func TestDoltOnlyRevisionDatabasePrivileges(t *testing.T) { - for _, script := range DoltOnlyRevisionDbPrivilegeTests { - harness := newDoltHarness(t) - harness.Setup(setup.MydbData, setup.MytableData) - t.Run(script.Name, func(t *testing.T) { - engine := mustNewEngine(t, harness) - defer engine.Close() - - ctx := enginetest.NewContext(harness) - ctx.WithClient(sql.Client{ - User: "root", - Address: "localhost", - }) - engine.EngineAnalyzer().Catalog.MySQLDb.AddRootAccount() - engine.EngineAnalyzer().Catalog.MySQLDb.SetPersister(&mysql_db.NoopPersister{}) - - for _, statement := range script.SetUpScript { - enginetest.RunQueryWithContext(t, engine, harness, ctx, statement) - } - - for _, assertion := range script.Assertions { - user := assertion.User - host := assertion.Host - if user == "" { - user = "root" - } - if host == "" { - host = "localhost" - } - ctx := enginetest.NewContextWithClient(harness, sql.Client{ - User: user, - Address: host, - }) - ctx.SetCurrentDatabase("mydb/b1") - - if assertion.ExpectedErr != nil { - t.Run(assertion.Query, func(t *testing.T) { - enginetest.AssertErrWithCtx(t, engine, harness, ctx, assertion.Query, nil, assertion.ExpectedErr) - }) - } else if assertion.ExpectedErrStr != "" { - t.Run(assertion.Query, func(t *testing.T) { - enginetest.AssertErrWithCtx(t, engine, harness, ctx, assertion.Query, nil, nil, assertion.ExpectedErrStr) - }) - } else { - t.Run(assertion.Query, func(t *testing.T) { - enginetest.TestQueryWithContext(t, ctx, engine, harness, assertion.Query, assertion.Expected, nil, nil, nil) - }) - } - } - }) - } +var DoltOnlyRevisionTableFunctionPrivilegeTests = []queries.UserPrivilegeTest{ + { + Name: "dolt_schema_diff privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "CREATE TABLE test2 (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + "ALTER TABLE test CHANGE COLUMN col1 word varchar(20);", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_schema_diff('HEAD', 'WORKING', 'test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_schema_diff('HEAD', 'WORKING');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.test TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_schema_diff('HEAD', 'WORKING', 'test');", + Expected: []sql.Row{ + {"test", "test", + "CREATE TABLE `test` (\n `pk` bigint NOT NULL,\n `col1` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;", + "CREATE TABLE `test` (\n `pk` bigint NOT NULL,\n `word` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;", + }, + }, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_schema_diff('HEAD', 'WORKING');", + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_schema_diff('HEAD', 'WORKING');", + Expected: []sql.Row{ + {"test", "test", + "CREATE TABLE `test` (\n `pk` bigint NOT NULL,\n `col1` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;", + "CREATE TABLE `test` (\n `pk` bigint NOT NULL,\n `word` varchar(20),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;", + }, + }, + }, + }, + }, + { + Name: "dolt_diff privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff('HEAD', 'WORKING', 'test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff('HEAD', 'WORKING', 'test');", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "dolt_diff_stat privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING', 'test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING', 'test');", + Expected: []sql.Row{}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING');", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "dolt_diff_summary privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING', 'test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING', 'test');", + Expected: []sql.Row{}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING');", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "dolt_log privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "CREATE TABLE test2 (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT count(*) FROM dolt_log();", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.test TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT count(*) FROM dolt_log();", + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT count(*) FROM dolt_log('main', '--tables', 'test');", + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT count(*) FROM dolt_log();", + Expected: []sql.Row{{int64(3)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT count(*) FROM dolt_log('main', '--tables', 'test');", + Expected: []sql.Row{{int64(1)}}, + }, + }, + }, + { + Name: "dolt_patch privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_patch('HEAD', 'WORKING', 'test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_patch('HEAD', 'WORKING');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_patch('HEAD', 'WORKING', 'test');", + Expected: []sql.Row{}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_patch('HEAD', 'WORKING');", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "dolt_preview_merge_conflicts privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "CREATE TABLE test2 (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_preview_merge_conflicts('main', 'b1', 'test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.test TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_preview_merge_conflicts('main', 'b1', 'test');", + Expected: []sql.Row{}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_preview_merge_conflicts('main', 'b1', 'test2');", + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + }, + }, + { + Name: "dolt_preview_merge_conflicts_summary privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "CREATE TABLE test2 (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'b1');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.test TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'b1');", + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_preview_merge_conflicts_summary('main', 'b1');", + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "dolt_query_diff privilege checking with revision database", + SetUpScript: []string{ + "CREATE TABLE test (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test VALUES (1, 'first row'), (2, 'second row');", + "CREATE TABLE test2 (pk BIGINT PRIMARY KEY, col1 varchar(20));", + "INSERT INTO test2 VALUES (1, 'a'), (2, 'b');", + "call dolt_commit('-Am', 'first commit');", + "CREATE USER tester@localhost;", + "call dolt_branch('b1');", + "use mydb/b1;", + }, + Assertions: []queries.UserPrivilegeTestAssertion{ + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM test', 'SELECT pk FROM test');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk', 'SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.test TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM test', 'SELECT pk FROM test');", + Expected: []sql.Row{}, + }, + { + User: "root", + Host: "localhost", + Query: "GRANT SELECT ON mydb.* TO tester@localhost;", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk', 'SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk');", + Expected: []sql.Row{}, + }, + }, + }, } From aa1c03215a5d34a8312d2233408efacc1e2d36bb Mon Sep 17 00:00:00 2001 From: elianddb Date: Tue, 17 Mar 2026 13:28:08 -0700 Subject: [PATCH 2/4] fix table functions to resolve base database name --- go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go | 3 ++- .../doltcore/sqle/dtablefunctions/dolt_diff_stat.go | 5 +++-- .../doltcore/sqle/dtablefunctions/dolt_diff_summary.go | 5 +++-- go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go | 3 ++- go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go | 5 +++-- .../sqle/dtablefunctions/dolt_preview_merge_conflicts.go | 3 ++- .../dtablefunctions/dolt_preview_merge_conflicts_summary.go | 3 ++- .../doltcore/sqle/dtablefunctions/dolt_query_diff.go | 4 +++- .../doltcore/sqle/dtablefunctions/dolt_schema_diff.go | 6 ++++-- go/libraries/doltcore/sqle/dtablefunctions/dolt_test_run.go | 3 ++- 10 files changed, 26 insertions(+), 14 deletions(-) diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go index 27938faf670..0c6484aebb1 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go @@ -396,7 +396,8 @@ func (dtf *DiffTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Privileg return ExpressionIsDeferred(dtf.tableNameExpr) } - subject := sql.PrivilegeCheckSubject{Database: dtf.database.Name(), Table: tableName} + baseDB, _ := doltdb.SplitRevisionDbName(dtf.database.Name()) + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tableName} // TODO: Add tests for privilege checking return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat.go index 3f4584a87b9..3cfd0c33da1 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_stat.go @@ -162,6 +162,7 @@ func (ds *DiffStatTableFunction) WithChildren(children ...sql.Node) (sql.Node, e // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (ds *DiffStatTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + baseDB, _ := doltdb.SplitRevisionDbName(ds.database.Name()) if ds.tableNameExpr != nil { if !types.IsText(ds.tableNameExpr.Type()) { return ExpressionIsDeferred(ds.tableNameExpr) @@ -176,7 +177,7 @@ func (ds *DiffStatTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Privi return false } - subject := sql.PrivilegeCheckSubject{Database: ds.database.Name(), Table: tableName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tableName} return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } @@ -187,7 +188,7 @@ func (ds *DiffStatTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Privi operations := make([]sql.PrivilegedOperation, 0, len(tblNames)) for _, tblName := range tblNames { - subject := sql.PrivilegeCheckSubject{Database: ds.database.Name(), Table: tblName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tblName} operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary.go index 2c81efda5e9..794d579130d 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff_summary.go @@ -153,6 +153,7 @@ func (ds *DiffSummaryTableFunction) WithChildren(children ...sql.Node) (sql.Node // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (ds *DiffSummaryTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + baseDB, _ := doltdb.SplitRevisionDbName(ds.database.Name()) if ds.tableNameExpr != nil { if !types.IsText(ds.tableNameExpr.Type()) { return ExpressionIsDeferred(ds.tableNameExpr) @@ -170,7 +171,7 @@ func (ds *DiffSummaryTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Pr return false } - subject := sql.PrivilegeCheckSubject{Database: ds.database.Name(), Table: tableName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tableName} // TODO: Add tests for privilege checking return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } @@ -182,7 +183,7 @@ func (ds *DiffSummaryTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Pr var operations []sql.PrivilegedOperation for _, tblName := range tblNames { - subject := sql.PrivilegeCheckSubject{Database: ds.database.Name(), Table: tblName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tblName} operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go index c9988309839..a848a04a2eb 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go @@ -312,6 +312,7 @@ func (ltf *LogTableFunction) WithChildren(children ...sql.Node) (sql.Node, error // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (ltf *LogTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + baseDB, _ := doltdb.SplitRevisionDbName(ltf.database.Name()) tblNames, err := ltf.database.GetTableNames(ctx) if err != nil { return false @@ -319,7 +320,7 @@ func (ltf *LogTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Privilege var operations []sql.PrivilegedOperation for _, tblName := range tblNames { - subject := sql.PrivilegeCheckSubject{Database: ltf.database.Name(), Table: tblName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tblName} operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go index 2cdeef430c1..0c6713c47fa 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_patch.go @@ -308,6 +308,7 @@ func (p *PatchTableFunction) WithChildren(children ...sql.Node) (sql.Node, error // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (p *PatchTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + baseDB, _ := doltdb.SplitRevisionDbName(p.database.Name()) if p.tableNameExpr != nil { if !sqltypes.IsText(p.tableNameExpr.Type()) { return ExpressionIsDeferred(p.tableNameExpr) @@ -322,7 +323,7 @@ func (p *PatchTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Privilege return false } - subject := sql.PrivilegeCheckSubject{Database: p.database.Name(), Table: tableName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tableName} return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } @@ -333,7 +334,7 @@ func (p *PatchTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Privilege operations := make([]sql.PrivilegedOperation, 0, len(tblNames)) for _, tblName := range tblNames { - subject := sql.PrivilegeCheckSubject{Database: p.database.Name(), Table: tblName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tblName} operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts.go index 1c346c332d1..3bc050d0c01 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts.go @@ -171,7 +171,8 @@ func (pm *PreviewMergeConflictsTableFunction) CheckAuth(ctx *sql.Context, opChec return false } - subject := sql.PrivilegeCheckSubject{Database: pm.database.Name(), Table: tableName} + baseDB, _ := doltdb.SplitRevisionDbName(pm.database.Name()) + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tableName} // TODO: Add tests for privilege checking return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go index e8c5666dea3..8ee1b423455 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go @@ -128,6 +128,7 @@ func (pm *PreviewMergeConflictsSummaryTableFunction) WithChildren(children ...sq // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (pm *PreviewMergeConflictsSummaryTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + baseDB, _ := doltdb.SplitRevisionDbName(pm.database.Name()) tblNames, err := pm.database.GetTableNames(ctx) if err != nil { return false @@ -135,7 +136,7 @@ func (pm *PreviewMergeConflictsSummaryTableFunction) CheckAuth(ctx *sql.Context, var operations []sql.PrivilegedOperation for _, tblName := range tblNames { - subject := sql.PrivilegeCheckSubject{Database: pm.database.Name(), Table: tblName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tblName} operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff.go index f1539662ab7..ee2c17bc8fc 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_query_diff.go @@ -19,6 +19,7 @@ import ( "io" "strings" + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/schema" gms "github.com/dolthub/go-mysql-server" @@ -320,7 +321,8 @@ func (tf *QueryDiffTableFunction) WithChildren(node ...sql.Node) (sql.Node, erro // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (tf *QueryDiffTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { - subject := sql.PrivilegeCheckSubject{Database: tf.database.Name()} + baseDB, _ := doltdb.SplitRevisionDbName(tf.database.Name()) + subject := sql.PrivilegeCheckSubject{Database: baseDB} return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff.go index 2d6019d9c6d..d254e2b4315 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_schema_diff.go @@ -25,6 +25,7 @@ import ( "github.com/dolthub/go-mysql-server/sql/types" "github.com/dolthub/dolt/go/libraries/doltcore/diff" + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/schema" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlfmt" @@ -171,13 +172,14 @@ func (ds *SchemaDiffTableFunction) WithChildren(children ...sql.Node) (sql.Node, // CheckAuth implements the interface sql.AuthorizationCheckerNode. func (ds *SchemaDiffTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { + baseDB, _ := doltdb.SplitRevisionDbName(ds.Database().Name()) if ds.tableNameExpr != nil { _, _, _, tableName, err := ds.evaluateArguments() if err != nil { return ExpressionIsDeferred(ds.tableNameExpr) } - subject := sql.PrivilegeCheckSubject{Database: ds.database.Name(), Table: tableName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tableName} return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } @@ -187,7 +189,7 @@ func (ds *SchemaDiffTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.Pri } operations := make([]sql.PrivilegedOperation, 0, len(tblNames)) for _, tblName := range tblNames { - subject := sql.PrivilegeCheckSubject{Database: ds.database.Name(), Table: tblName} + subject := sql.PrivilegeCheckSubject{Database: baseDB, Table: tblName} operations = append(operations, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_test_run.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_test_run.go index e76874f7c95..c9d1d87eaac 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_test_run.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_test_run.go @@ -143,7 +143,8 @@ func (trtf *TestsRunTableFunction) WithChildren(node ...sql.Node) (sql.Node, err // CheckAuth implements the interface sql.AuthorizationCheckerNode func (trtf *TestsRunTableFunction) CheckAuth(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { - subject := sql.PrivilegeCheckSubject{Database: trtf.database.Name()} + baseDB, _ := doltdb.SplitRevisionDbName(trtf.database.Name()) + subject := sql.PrivilegeCheckSubject{Database: baseDB} return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Select)) } From 1c9eb1d4b26ecca4ff2f685cda0407ee253b2a24 Mon Sep 17 00:00:00 2001 From: elianddb Date: Tue, 17 Mar 2026 13:35:33 -0700 Subject: [PATCH 3/4] amend privileges_test name to follow tests naming convention --- .../sqle/enginetest/{privilege_test.go => dolt_privilege_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename go/libraries/doltcore/sqle/enginetest/{privilege_test.go => dolt_privilege_test.go} (100%) mode change 100755 => 100644 diff --git a/go/libraries/doltcore/sqle/enginetest/privilege_test.go b/go/libraries/doltcore/sqle/enginetest/dolt_privilege_test.go old mode 100755 new mode 100644 similarity index 100% rename from go/libraries/doltcore/sqle/enginetest/privilege_test.go rename to go/libraries/doltcore/sqle/enginetest/dolt_privilege_test.go From 54d3a94c317fd68318526330c643f9cd265b2316 Mon Sep 17 00:00:00 2001 From: elianddb Date: Tue, 17 Mar 2026 14:32:53 -0700 Subject: [PATCH 4/4] amend `dolt_query_diff` script test to expect database-level privilege check --- .../sqle/enginetest/dolt_privilege_test.go | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_privilege_test.go b/go/libraries/doltcore/sqle/enginetest/dolt_privilege_test.go index 70d78e59fc4..09e27fa4019 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_privilege_test.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_privilege_test.go @@ -1376,6 +1376,12 @@ var DoltOnlyRevisionTableFunctionPrivilegeTests = []queries.UserPrivilegeTest{ Query: "SELECT * FROM dolt_query_diff('SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk', 'SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk');", ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT 1 AS pk', 'SELECT 2 AS pk');", + ExpectedErr: sql.ErrDatabaseAccessDeniedForUser, + }, { User: "root", Host: "localhost", @@ -1383,10 +1389,29 @@ var DoltOnlyRevisionTableFunctionPrivilegeTests = []queries.UserPrivilegeTest{ Expected: []sql.Row{{types.NewOkResult(0)}}, }, { - User: "tester", - Host: "localhost", - Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM test', 'SELECT pk FROM test');", - Expected: []sql.Row{}, + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM test', 'SELECT pk FROM test');", + // TODO(elianddb): Add privilege checks scoped to tables touched in query args. + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT 1 AS pk', 'SELECT 2 AS pk');", + ExpectedErr: sql.ErrPrivilegeCheckFailed, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk', 'SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk');", + ExpectedErr: sql.ErrTableAccessDeniedForUser, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM (SELECT pk FROM test2) s', 'SELECT pk FROM (SELECT pk FROM test) s');", + ExpectedErr: sql.ErrTableAccessDeniedForUser, }, { User: "root", @@ -1397,7 +1422,26 @@ var DoltOnlyRevisionTableFunctionPrivilegeTests = []queries.UserPrivilegeTest{ { User: "tester", Host: "localhost", - Query: "SELECT * FROM dolt_query_diff('SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk', 'SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk');", + Query: "SELECT * FROM dolt_query_diff('SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = t2.pk', 'SELECT t.pk FROM test t JOIN test2 t2 ON t.pk = 2');", + Expected: []sql.Row{{1, 2, "modified"}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT 1 AS pk', 'SELECT 2 AS pk');", + Expected: []sql.Row{{1, 2, "modified"}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM test limit 1', 'SELECT pk FROM test2 limit 1');", + // TODO(elianddb): Add privilege checks scoped to tables touched in query args. + Expected: []sql.Row{{1, nil, "deleted"}, {nil, 1, "added"}}, + }, + { + User: "tester", + Host: "localhost", + Query: "SELECT * FROM dolt_query_diff('SELECT pk FROM (SELECT pk FROM test2) s', 'SELECT pk FROM (SELECT pk FROM test) s');", Expected: []sql.Row{}, }, },