vitess.io/vitess@v0.16.2/go/vt/mysqlctl/tmutils/permissions.go (about) 1 /* 2 Copyright 2019 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 tmutils contains helper methods to deal with the tabletmanagerdata 18 // proto3 structures. 19 package tmutils 20 21 import ( 22 "fmt" 23 "hash/crc64" 24 "sort" 25 "strings" 26 27 "vitess.io/vitess/go/sqltypes" 28 "vitess.io/vitess/go/vt/concurrency" 29 querypb "vitess.io/vitess/go/vt/proto/query" 30 31 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 32 ) 33 34 // This file contains helper methods to deal with Permissions. 35 36 var ( 37 hashTable = crc64.MakeTable(crc64.ISO) 38 ) 39 40 // permissionList is an internal type to facilitate common code between the 3 permission types 41 type permissionList interface { 42 Get(int) (primayKey string, value string) 43 Len() int 44 } 45 46 func printPrivileges(priv map[string]string) string { 47 si := make([]string, 0, len(priv)) 48 for k := range priv { 49 si = append(si, k) 50 } 51 sort.Strings(si) 52 result := "" 53 for _, k := range si { 54 result += " " + k + "(" + priv[k] + ")" 55 } 56 return result 57 } 58 59 // NewUserPermission is a helper method to create a tabletmanagerdatapb.UserPermission 60 func NewUserPermission(fields []*querypb.Field, values []sqltypes.Value) *tabletmanagerdatapb.UserPermission { 61 up := &tabletmanagerdatapb.UserPermission{ 62 Privileges: make(map[string]string), 63 } 64 for i, field := range fields { 65 switch strings.ToLower(field.Name) { 66 case "host": 67 up.Host = values[i].ToString() 68 case "user": 69 up.User = values[i].ToString() 70 case "password": 71 vBytes, _ := values[i].ToBytes() 72 up.PasswordChecksum = crc64.Checksum(vBytes, hashTable) 73 case "password_last_changed": 74 // we skip this one, as the value may be 75 // different on primary and replicas. 76 default: 77 up.Privileges[field.Name] = values[i].ToString() 78 } 79 } 80 return up 81 } 82 83 // UserPermissionPrimaryKey returns the sorting key for a UserPermission 84 func UserPermissionPrimaryKey(up *tabletmanagerdatapb.UserPermission) string { 85 return up.Host + ":" + up.User 86 } 87 88 // UserPermissionString pretty-prints a UserPermission 89 func UserPermissionString(up *tabletmanagerdatapb.UserPermission) string { 90 var passwd string 91 if up.PasswordChecksum == 0 { 92 passwd = "NoPassword" 93 } else { 94 passwd = fmt.Sprintf("PasswordChecksum(%v)", up.PasswordChecksum) 95 } 96 return "UserPermission " + passwd + printPrivileges(up.Privileges) 97 } 98 99 type userPermissionList []*tabletmanagerdatapb.UserPermission 100 101 func (upl userPermissionList) Get(i int) (string, string) { 102 return UserPermissionPrimaryKey(upl[i]), UserPermissionString(upl[i]) 103 } 104 105 func (upl userPermissionList) Len() int { 106 return len(upl) 107 } 108 109 // NewDbPermission is a helper method to create a tabletmanagerdatapb.DbPermission 110 func NewDbPermission(fields []*querypb.Field, values []sqltypes.Value) *tabletmanagerdatapb.DbPermission { 111 up := &tabletmanagerdatapb.DbPermission{ 112 Privileges: make(map[string]string), 113 } 114 for i, field := range fields { 115 switch field.Name { 116 case "Host": 117 up.Host = values[i].ToString() 118 case "Db": 119 up.Db = values[i].ToString() 120 case "User": 121 up.User = values[i].ToString() 122 default: 123 up.Privileges[field.Name] = values[i].ToString() 124 } 125 } 126 return up 127 } 128 129 // DbPermissionPrimaryKey returns the sorting key for a DbPermission 130 func DbPermissionPrimaryKey(dp *tabletmanagerdatapb.DbPermission) string { 131 return dp.Host + ":" + dp.Db + ":" + dp.User 132 } 133 134 // DbPermissionString pretty-prints a DbPermission 135 func DbPermissionString(dp *tabletmanagerdatapb.DbPermission) string { 136 return "DbPermission" + printPrivileges(dp.Privileges) 137 } 138 139 type dbPermissionList []*tabletmanagerdatapb.DbPermission 140 141 func (upl dbPermissionList) Get(i int) (string, string) { 142 return DbPermissionPrimaryKey(upl[i]), DbPermissionString(upl[i]) 143 } 144 145 func (upl dbPermissionList) Len() int { 146 return len(upl) 147 } 148 149 func printPermissions(name string, permissions permissionList) string { 150 result := name + " Permissions:\n" 151 for i := 0; i < permissions.Len(); i++ { 152 pk, val := permissions.Get(i) 153 result += " " + pk + ": " + val + "\n" 154 } 155 return result 156 } 157 158 // PermissionsString pretty-prints Permissions 159 func PermissionsString(permissions *tabletmanagerdatapb.Permissions) string { 160 return printPermissions("User", userPermissionList(permissions.UserPermissions)) + 161 printPermissions("Db", dbPermissionList(permissions.DbPermissions)) 162 } 163 164 func diffPermissions(name, leftName string, left permissionList, rightName string, right permissionList, er concurrency.ErrorRecorder) { 165 166 leftIndex := 0 167 rightIndex := 0 168 for leftIndex < left.Len() && rightIndex < right.Len() { 169 lpk, lval := left.Get(leftIndex) 170 rpk, rval := right.Get(rightIndex) 171 172 // extra value on the left side 173 if lpk < rpk { 174 er.RecordError(fmt.Errorf("%v has an extra %v %v", leftName, name, lpk)) 175 leftIndex++ 176 continue 177 } 178 179 // extra value on the right side 180 if lpk > rpk { 181 er.RecordError(fmt.Errorf("%v has an extra %v %v", rightName, name, rpk)) 182 rightIndex++ 183 continue 184 } 185 186 // same name, let's see content 187 if lval != rval { 188 er.RecordError(fmt.Errorf("permissions differ on %v %v:\n%s: %v\n differs from:\n%s: %v", name, lpk, leftName, lval, rightName, rval)) 189 } 190 leftIndex++ 191 rightIndex++ 192 } 193 for leftIndex < left.Len() { 194 lpk, _ := left.Get(leftIndex) 195 er.RecordError(fmt.Errorf("%v has an extra %v %v", leftName, name, lpk)) 196 leftIndex++ 197 } 198 for rightIndex < right.Len() { 199 rpk, _ := right.Get(rightIndex) 200 er.RecordError(fmt.Errorf("%v has an extra %v %v", rightName, name, rpk)) 201 rightIndex++ 202 } 203 } 204 205 // DiffPermissions records the errors between two permission sets 206 func DiffPermissions(leftName string, left *tabletmanagerdatapb.Permissions, rightName string, right *tabletmanagerdatapb.Permissions, er concurrency.ErrorRecorder) { 207 diffPermissions("user", leftName, userPermissionList(left.UserPermissions), rightName, userPermissionList(right.UserPermissions), er) 208 diffPermissions("db", leftName, dbPermissionList(left.DbPermissions), rightName, dbPermissionList(right.DbPermissions), er) 209 } 210 211 // DiffPermissionsToArray difs two sets of permissions, and returns the difference 212 func DiffPermissionsToArray(leftName string, left *tabletmanagerdatapb.Permissions, rightName string, right *tabletmanagerdatapb.Permissions) (result []string) { 213 er := concurrency.AllErrorRecorder{} 214 DiffPermissions(leftName, left, rightName, right, &er) 215 if er.HasErrors() { 216 return er.ErrorStrings() 217 } 218 return nil 219 }