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  }