Skip to content

Commit 11f255c

Browse files
authored
Merge pull request #3306 from dolthub/elian/10083
dolthub/dolt#10083: Honor *definer privileges* when rebinding views
2 parents 49b8a44 + 00f026c commit 11f255c

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

enginetest/queries/priv_auth_queries.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,29 @@ type ServerAuthenticationTestAssertion struct {
108108
// UserPrivTests test the user and privilege systems. These tests always have the root account available, and the root
109109
// account is used with any queries in the SetUpScript.
110110
var UserPrivTests = []UserPrivilegeTest{
111+
// https://github.com/dolthub/dolt/issues/10083
112+
{
113+
Name: "Selecting a view only needs SELECT grants",
114+
SetUpScript: []string{
115+
"CREATE TABLE perms_t (id INT PRIMARY KEY, val VARCHAR(10));",
116+
"INSERT INTO perms_t VALUES (1, 'a'), (2, 'b');",
117+
"CREATE VIEW perms_v AS SELECT id, val FROM perms_t ORDER BY id;",
118+
"CREATE USER 'view_reader'@'localhost' IDENTIFIED BY 'pw';",
119+
"GRANT SELECT ON perms_t TO 'view_reader'@'localhost';",
120+
"GRANT SELECT ON perms_v TO 'view_reader'@'localhost';",
121+
},
122+
Assertions: []UserPrivilegeTestAssertion{
123+
{
124+
User: "view_reader",
125+
Host: "localhost",
126+
Query: "SELECT * FROM perms_v",
127+
Expected: []sql.Row{
128+
{int64(1), "a"},
129+
{int64(2), "b"},
130+
},
131+
},
132+
},
133+
},
111134
{
112135
Name: "Create user limits",
113136
Assertions: []UserPrivilegeTestAssertion{

sql/base_session.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ func (s *BaseSession) Client() Client { return s.client }
9696
// SetClient implements the Session interface.
9797
func (s *BaseSession) SetClient(c Client) {
9898
s.client = c
99-
return
10099
}
101100

102101
// GetAllSessionVariables implements the Session interface.

sql/planbuilder/definer.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package planbuilder
16+
17+
import (
18+
"github.com/dolthub/go-mysql-server/sql"
19+
"github.com/dolthub/go-mysql-server/sql/mysql_db"
20+
)
21+
22+
// mockDefiner temporarily impersonates the definer during binding. It clones the current authorization state
23+
// (when available), adds the requested global privileges (e.g. CREATE VIEW), and updates both the session privilege
24+
// cache and the cached AuthorizationQueryState. Callers must defer the returned restore function.
25+
func (b *Builder) mockDefiner(privileges ...sql.PrivilegeType) func() {
26+
if b == nil || b.ctx == nil || b.ctx.Session == nil {
27+
return func() {}
28+
}
29+
30+
var privilegeSet mysql_db.PrivilegeSet
31+
if state, ok := b.authQueryState.(defaultAuthorizationQueryState); ok && state.enabled {
32+
privilegeSet = state.privSet.Copy()
33+
} else {
34+
privilegeSet = mysql_db.NewPrivilegeSet()
35+
}
36+
privilegeSet.AddGlobalStatic(privileges...)
37+
38+
initialAuthQueryState := b.authQueryState
39+
if state, ok := b.authQueryState.(defaultAuthorizationQueryState); ok {
40+
state.privSet = privilegeSet
41+
b.authQueryState = state
42+
}
43+
44+
initialPrivilegeSet, initialCounter := b.ctx.Session.GetPrivilegeSet()
45+
b.ctx.SetPrivilegeSet(privilegeSet, initialCounter)
46+
47+
return func() {
48+
b.authQueryState = initialAuthQueryState
49+
b.ctx.SetPrivilegeSet(initialPrivilegeSet, initialCounter)
50+
}
51+
}

sql/planbuilder/from.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,9 @@ func (b *Builder) resolveView(name string, database sql.Database, asOf interface
845845
if err != nil {
846846
b.handleErr(err)
847847
}
848+
// TODO: Once view definers are persisted, load the real definer client
849+
restoreInvoker := b.mockDefiner(sql.PrivilegeType_CreateView)
850+
defer restoreInvoker()
848851
node, _, err := b.bindOnlyWithDatabase(database, stmt, viewDef.CreateViewStatement)
849852
if err != nil {
850853
// TODO: Need to account for non-existing functions or

0 commit comments

Comments
 (0)