vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/grpc_server_acls/acls_test.go (about) 1 /* 2 Copyright 2023 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package grpc_server_acls 18 19 import ( 20 "context" 21 "flag" 22 "fmt" 23 "os" 24 "path" 25 "testing" 26 27 "vitess.io/vitess/go/vt/callerid" 28 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 "google.golang.org/grpc" 32 33 "vitess.io/vitess/go/test/endtoend/cluster" 34 "vitess.io/vitess/go/vt/grpcclient" 35 "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" 36 "vitess.io/vitess/go/vt/vtgate/vtgateconn" 37 ) 38 39 var ( 40 clusterInstance *cluster.LocalProcessCluster 41 vtgateGrpcAddress string 42 hostname = "localhost" 43 keyspaceName = "ks" 44 cell = "zone1" 45 sqlSchema = ` 46 create table test_table ( 47 id bigint, 48 val varchar(128), 49 primary key(id) 50 ) Engine=InnoDB; 51 ` 52 grpcServerAuthStaticJSON = ` 53 [ 54 { 55 "Username": "some_other_user", 56 "Password": "test_password" 57 }, 58 { 59 "Username": "another_unrelated_user", 60 "Password": "test_password" 61 } 62 ] 63 ` 64 tableACLJSON = ` 65 { 66 "table_groups": [ 67 { 68 "name": "default", 69 "table_names_or_prefixes": ["%"], 70 "readers": ["user_with_access"], 71 "writers": ["user_with_access"], 72 "admins": ["user_with_access"] 73 } 74 ] 75 } 76 ` 77 ) 78 79 func TestMain(m *testing.M) { 80 81 defer cluster.PanicHandler(nil) 82 flag.Parse() 83 84 exitcode := func() int { 85 clusterInstance = cluster.NewCluster(cell, hostname) 86 defer clusterInstance.Teardown() 87 88 // Start topo server 89 if err := clusterInstance.StartTopo(); err != nil { 90 return 1 91 } 92 93 // Directory for authn / authz config files 94 authDirectory := path.Join(clusterInstance.TmpDirectory, "auth") 95 if err := os.Mkdir(authDirectory, 0700); err != nil { 96 return 1 97 } 98 99 // Create grpc_server_auth_static.json file 100 grpcServerAuthStaticPath := path.Join(authDirectory, "grpc_server_auth_static.json") 101 if err := createFile(grpcServerAuthStaticPath, grpcServerAuthStaticJSON); err != nil { 102 return 1 103 } 104 105 // Create table_acl.json file 106 tableACLPath := path.Join(authDirectory, "table_acl.json") 107 if err := createFile(tableACLPath, tableACLJSON); err != nil { 108 return 1 109 } 110 111 // Configure vtgate to use static auth 112 clusterInstance.VtGateExtraArgs = []string{ 113 "--grpc_auth_mode", "static", 114 "--grpc_auth_static_password_file", grpcServerAuthStaticPath, 115 "--grpc_use_effective_callerid", 116 "--grpc-use-static-authentication-callerid", 117 } 118 119 // Configure vttablet to use table ACL 120 clusterInstance.VtTabletExtraArgs = []string{ 121 "--enforce-tableacl-config", 122 "--queryserver-config-strict-table-acl", 123 "--table-acl-config", tableACLPath, 124 } 125 126 // Start keyspace 127 keyspace := &cluster.Keyspace{ 128 Name: keyspaceName, 129 SchemaSQL: sqlSchema, 130 } 131 if err := clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil { 132 return 1 133 } 134 135 // Start vtgate 136 if err := clusterInstance.StartVtgate(); err != nil { 137 clusterInstance.VtgateProcess = cluster.VtgateProcess{} 138 return 1 139 } 140 vtgateGrpcAddress = fmt.Sprintf("%s:%d", clusterInstance.Hostname, clusterInstance.VtgateGrpcPort) 141 142 return m.Run() 143 }() 144 os.Exit(exitcode) 145 } 146 147 // TestEffectiveCallerIDWithAccess verifies that an authenticated gRPC static user with an effectiveCallerID that has ACL access can execute queries 148 func TestEffectiveCallerIDWithAccess(t *testing.T) { 149 ctx, cancel := context.WithCancel(context.Background()) 150 defer cancel() 151 152 vtgateConn, err := dialVTGate(ctx, t, "some_other_user", "test_password") 153 if err != nil { 154 t.Fatal(err) 155 } 156 defer vtgateConn.Close() 157 158 session := vtgateConn.Session(keyspaceName+"@primary", nil) 159 query := "SELECT id FROM test_table" 160 ctx = callerid.NewContext(ctx, callerid.NewEffectiveCallerID("user_with_access", "", ""), nil) 161 _, err = session.Execute(ctx, query, nil) 162 assert.NoError(t, err) 163 } 164 165 // TestEffectiveCallerIDWithNoAccess verifies that an authenticated gRPC static user without an effectiveCallerID that has ACL access cannot execute queries 166 func TestEffectiveCallerIDWithNoAccess(t *testing.T) { 167 ctx, cancel := context.WithCancel(context.Background()) 168 defer cancel() 169 170 vtgateConn, err := dialVTGate(ctx, t, "another_unrelated_user", "test_password") 171 if err != nil { 172 t.Fatal(err) 173 } 174 defer vtgateConn.Close() 175 176 session := vtgateConn.Session(keyspaceName+"@primary", nil) 177 query := "SELECT id FROM test_table" 178 ctx = callerid.NewContext(ctx, callerid.NewEffectiveCallerID("user_no_access", "", ""), nil) 179 _, err = session.Execute(ctx, query, nil) 180 require.Error(t, err) 181 assert.Contains(t, err.Error(), "Select command denied to user") 182 assert.Contains(t, err.Error(), "for table 'test_table' (ACL check error)") 183 } 184 185 func dialVTGate(ctx context.Context, t *testing.T, username string, password string) (*vtgateconn.VTGateConn, error) { 186 clientCreds := &grpcclient.StaticAuthClientCreds{Username: username, Password: password} 187 creds := grpc.WithPerRPCCredentials(clientCreds) 188 dialerFunc := grpcvtgateconn.DialWithOpts(ctx, creds) 189 dialerName := t.Name() 190 vtgateconn.RegisterDialer(dialerName, dialerFunc) 191 return vtgateconn.DialProtocol(ctx, dialerName, vtgateGrpcAddress) 192 } 193 194 func createFile(path string, contents string) error { 195 f, err := os.Create(path) 196 if err != nil { 197 return err 198 } 199 _, err = f.WriteString(contents) 200 if err != nil { 201 return err 202 } 203 return f.Close() 204 }