Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
808 changes: 801 additions & 7 deletions data/test/tabletserver/exec_cases.txt

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions data/test/tabletserver/stream_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{
"PlanID": "SELECT_STREAM",
"TableName": "a",
"Permissions":[{"TableName":"a","Role":0}],
"FullQuery": "select * from a"
}

Expand All @@ -11,6 +12,7 @@
{
"PlanID": "SELECT_STREAM",
"TableName": "",
"Permissions":[{"TableName":"a","Role":0},{"TableName":"b","Role":0}],
"FullQuery": "select * from a join b"
}

Expand All @@ -23,6 +25,7 @@
{
"PlanID": "SELECT_STREAM",
"TableName": "",
"Permissions":[{"TableName":"a","Role":0},{"TableName":"b","Role":0}],
"FullQuery": "select * from a union select * from b"
}

Expand Down
18 changes: 15 additions & 3 deletions go/vt/vttablet/endtoend/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ import (
"testing"

"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
"github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
"github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"

querypb "github.com/youtube/vitess/go/vt/proto/query"
)

func TestTableACLNoAccess(t *testing.T) {
func TestTableACL(t *testing.T) {
client := framework.NewClient()

aclErr := "table acl error"
Expand Down Expand Up @@ -79,6 +80,17 @@ func TestTableACLNoAccess(t *testing.T) {
}, {
query: "alter table vitess_acl_all_user_read_only comment 'comment'",
err: aclErr,
}, {
query: "select * from vitess_acl_read_only, vitess_acl_no_access",
err: aclErr,
}, {
query: "delete from vitess_acl_read_write where key1=(select key1 from vitess_acl_no_access)",
err: aclErr,
}, {
query: "delete from vitess_acl_read_write where key1=(select key1 from vitess_acl_read_only)",
}, {
query: "update vitess_acl_read_write join vitess_acl_read_only on 1!=1 set key1=1",
err: aclErr,
}}

for _, tcase := range execCases {
Expand All @@ -90,7 +102,7 @@ func TestTableACLNoAccess(t *testing.T) {
continue
}
if err == nil || !strings.HasPrefix(err.Error(), tcase.err) {
t.Errorf("Error: %v, must start with %s", err, tcase.err)
t.Errorf("Execute(%s): Error: %v, must start with %s", tcase.query, err, tcase.err)
}
}

Expand Down
114 changes: 114 additions & 0 deletions go/vt/vttablet/tabletserver/planbuilder/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
Copyright 2018 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package planbuilder

import (
"fmt"

"github.com/youtube/vitess/go/vt/sqlparser"
"github.com/youtube/vitess/go/vt/tableacl"
)

// Permission associates the required access permission
// for each table.
type Permission struct {
TableName string
Role tableacl.Role
}

// BuildPermissions builds the list of required permissions for all the
// tables referenced in a query.
func BuildPermissions(stmt sqlparser.Statement) []Permission {
var permissions []Permission
// All Statement types myst be covered here.
switch node := stmt.(type) {
case *sqlparser.Union, *sqlparser.Select:
permissions = buildSubqueryPermissions(node, tableacl.READER, permissions)
case *sqlparser.Insert:
permissions = buildTableNamePermissions(node.Table, tableacl.WRITER, permissions)
permissions = buildSubqueryPermissions(node, tableacl.READER, permissions)
case *sqlparser.Update:
permissions = buildTableExprsPermissions(node.TableExprs, tableacl.WRITER, permissions)
permissions = buildSubqueryPermissions(node, tableacl.READER, permissions)
case *sqlparser.Delete:
permissions = buildTableExprsPermissions(node.TableExprs, tableacl.WRITER, permissions)
permissions = buildSubqueryPermissions(node, tableacl.READER, permissions)
case *sqlparser.Set, *sqlparser.Show, *sqlparser.OtherRead:
// no-op
case *sqlparser.DDL:
if !node.Table.IsEmpty() {
permissions = buildTableNamePermissions(node.Table, tableacl.ADMIN, permissions)
}
if !node.NewName.IsEmpty() {
permissions = buildTableNamePermissions(node.NewName, tableacl.ADMIN, permissions)
}
case *sqlparser.OtherAdmin:
// no op
default:
panic(fmt.Errorf("BUG: unexpected statement type: %T", node))
}
return permissions
}

func buildSubqueryPermissions(stmt sqlparser.Statement, role tableacl.Role, permissions []Permission) []Permission {
_ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
switch node := node.(type) {
case *sqlparser.Select:
permissions = buildTableExprsPermissions(node.From, role, permissions)
case sqlparser.TableExprs:
return false, nil
}
return true, nil
}, stmt)
return permissions
}

func buildTableExprsPermissions(node sqlparser.TableExprs, role tableacl.Role, permissions []Permission) []Permission {
for _, node := range node {
permissions = buildTableExprPermissions(node, role, permissions)
}
return permissions
}

func buildTableExprPermissions(node sqlparser.TableExpr, role tableacl.Role, permissions []Permission) []Permission {
switch node := node.(type) {
case *sqlparser.AliasedTableExpr:
// An AliasedTableExpr can also be a subquery, but we should skip them here
// because the buildSubQueryPermissions walker will catch them and extract
// the corresponding table names.
switch node := node.Expr.(type) {
case sqlparser.TableName:
permissions = buildTableNamePermissions(node, role, permissions)
case *sqlparser.Subquery:
permissions = buildSubqueryPermissions(node.Select, role, permissions)
}
case *sqlparser.ParenTableExpr:
permissions = buildTableExprsPermissions(node.Exprs, role, permissions)
case *sqlparser.JoinTableExpr:
permissions = buildTableExprPermissions(node.LeftExpr, role, permissions)
permissions = buildTableExprPermissions(node.RightExpr, role, permissions)
}
return permissions
}

func buildTableNamePermissions(node sqlparser.TableName, role tableacl.Role, permissions []Permission) []Permission {
permissions = append(permissions, Permission{
TableName: node.Name.String(),
Role: role,
})
return permissions
}
181 changes: 181 additions & 0 deletions go/vt/vttablet/tabletserver/planbuilder/permission_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
Copyright 2018 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package planbuilder

import (
"reflect"
"testing"

"github.com/youtube/vitess/go/vt/sqlparser"
"github.com/youtube/vitess/go/vt/tableacl"
)

func TestBuildPermissions(t *testing.T) {
tcases := []struct {
input string
output []Permission
}{{
input: "select * from t",
output: []Permission{{
TableName: "t",
Role: tableacl.READER,
}},
}, {
input: "select * from t1 union select * from t2",
output: []Permission{{
TableName: "t1",
Role: tableacl.READER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "insert into t values()",
output: []Permission{{
TableName: "t",
Role: tableacl.WRITER,
}},
}, {
input: "update t set a=1",
output: []Permission{{
TableName: "t",
Role: tableacl.WRITER,
}},
}, {
input: "delete from t",
output: []Permission{{
TableName: "t",
Role: tableacl.WRITER,
}},
}, {
input: "set a=1",
output: nil,
}, {
input: "show variable like 'a%'",
output: nil,
}, {
input: "describe t",
output: nil,
}, {
input: "create table t",
output: []Permission{{
TableName: "t",
Role: tableacl.ADMIN,
}},
}, {
input: "rename table t1 to t2",
output: []Permission{{
TableName: "t1",
Role: tableacl.ADMIN,
}, {
TableName: "t2",
Role: tableacl.ADMIN,
}},
}, {
input: "drop table t",
output: []Permission{{
TableName: "t",
Role: tableacl.ADMIN,
}},
}, {
input: "repair t",
output: nil,
}, {
input: "select (select a from t2) from t1",
output: []Permission{{
TableName: "t1",
Role: tableacl.READER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "insert into t1 values((select a from t2), 1)",
output: []Permission{{
TableName: "t1",
Role: tableacl.WRITER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "update t1 set a = (select b from t2)",
output: []Permission{{
TableName: "t1",
Role: tableacl.WRITER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "delete from t1 where a = (select b from t2)",
output: []Permission{{
TableName: "t1",
Role: tableacl.WRITER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "select * from t1, t2",
output: []Permission{{
TableName: "t1",
Role: tableacl.READER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "select * from (t1, t2)",
output: []Permission{{
TableName: "t1",
Role: tableacl.READER,
}, {
TableName: "t2",
Role: tableacl.READER,
}},
}, {
input: "update t1 join t2 on a=b set c=d",
output: []Permission{{
TableName: "t1",
Role: tableacl.WRITER,
}, {
TableName: "t2",
Role: tableacl.WRITER,
}},
}, {
input: "update (select * from t1) as a join t2 on a=b set c=d",
output: []Permission{{
TableName: "t1",
Role: tableacl.WRITER,
}, {
TableName: "t2",
Role: tableacl.WRITER,
}},
}}

for _, tcase := range tcases {
stmt, err := sqlparser.Parse(tcase.input)
if err != nil {
t.Fatal(err)
}
got := BuildPermissions(stmt)
if !reflect.DeepEqual(got, tcase.output) {
t.Errorf("BuildPermissions(%s): %v, want %v", tcase.input, got, tcase.output)
}
}
}
Loading