vitess.io/vitess@v0.16.2/go/vt/tableacl/tableacl.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 tableacl
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"sort"
    25  	"strings"
    26  	"sync"
    27  
    28  	"github.com/tchap/go-patricia/patricia"
    29  	"google.golang.org/protobuf/proto"
    30  
    31  	"vitess.io/vitess/go/json2"
    32  	"vitess.io/vitess/go/vt/log"
    33  	"vitess.io/vitess/go/vt/tableacl/acl"
    34  
    35  	tableaclpb "vitess.io/vitess/go/vt/proto/tableacl"
    36  )
    37  
    38  // ACLResult embeds an acl.ACL and also tell which table group it belongs to.
    39  type ACLResult struct {
    40  	acl.ACL
    41  	GroupName string
    42  }
    43  
    44  type aclEntry struct {
    45  	tableNameOrPrefix string
    46  	groupName         string
    47  	acl               map[Role]acl.ACL
    48  }
    49  
    50  type aclEntries []aclEntry
    51  
    52  func (aes aclEntries) Len() int {
    53  	return len(aes)
    54  }
    55  
    56  func (aes aclEntries) Less(i, j int) bool {
    57  	return aes[i].tableNameOrPrefix < aes[j].tableNameOrPrefix
    58  }
    59  
    60  func (aes aclEntries) Swap(i, j int) {
    61  	aes[i], aes[j] = aes[j], aes[i]
    62  }
    63  
    64  // mu protects acls and defaultACL.
    65  var mu sync.Mutex
    66  
    67  var acls = make(map[string]acl.Factory)
    68  
    69  // defaultACL tells the default ACL implementation to use.
    70  var defaultACL string
    71  
    72  type tableACL struct {
    73  	// mutex protects entries, config, and callback
    74  	sync.RWMutex
    75  	entries aclEntries
    76  	config  *tableaclpb.Config
    77  	// callback is executed on successful reload.
    78  	callback func()
    79  	// ACL Factory override for testing
    80  	factory acl.Factory
    81  }
    82  
    83  // currentTableACL stores current effective ACL information.
    84  var currentTableACL tableACL
    85  
    86  // Init initiates table ACLs.
    87  //
    88  // The config file can be binary-proto-encoded, or json-encoded.
    89  // In the json case, it looks like this:
    90  //
    91  //	{
    92  //	  "table_groups": [
    93  //	    {
    94  //	      "table_names_or_prefixes": ["name1"],
    95  //	      "readers": ["client1"],
    96  //	      "writers": ["client1"],
    97  //	      "admins": ["client1"]
    98  //	    }
    99  //	  ]
   100  //	}
   101  func Init(configFile string, aclCB func()) error {
   102  	return currentTableACL.init(configFile, aclCB)
   103  }
   104  
   105  func (tacl *tableACL) init(configFile string, aclCB func()) error {
   106  	tacl.SetCallback(aclCB)
   107  	if configFile == "" {
   108  		return nil
   109  	}
   110  	data, err := os.ReadFile(configFile)
   111  	if err != nil {
   112  		log.Infof("unable to read tableACL config file: %v  Error: %v", configFile, err)
   113  		return err
   114  	}
   115  	config := &tableaclpb.Config{}
   116  	if err := proto.Unmarshal(data, config); err != nil {
   117  		// try to parse tableacl as json file
   118  		if jsonErr := json2.Unmarshal(data, config); jsonErr != nil {
   119  			log.Infof("unable to parse tableACL config file as a protobuf or json file.  protobuf err: %v  json err: %v", err, jsonErr)
   120  			return fmt.Errorf("unable to unmarshal Table ACL data: %s", data)
   121  		}
   122  	}
   123  	return tacl.Set(config)
   124  }
   125  
   126  func (tacl *tableACL) SetCallback(callback func()) {
   127  	tacl.Lock()
   128  	defer tacl.Unlock()
   129  	tacl.callback = callback
   130  }
   131  
   132  // InitFromProto inits table ACLs from a proto.
   133  func InitFromProto(config *tableaclpb.Config) error {
   134  	return currentTableACL.Set(config)
   135  }
   136  
   137  // load loads configurations from a proto-defined Config
   138  // If err is nil, then entries is guaranteed to be non-nil (though possibly empty).
   139  func load(config *tableaclpb.Config, newACL func([]string) (acl.ACL, error)) (entries aclEntries, err error) {
   140  	if err := ValidateProto(config); err != nil {
   141  		return nil, err
   142  	}
   143  	entries = aclEntries{}
   144  	for _, group := range config.TableGroups {
   145  		readers, err := newACL(group.Readers)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		writers, err := newACL(group.Writers)
   150  		if err != nil {
   151  			return nil, err
   152  		}
   153  		admins, err := newACL(group.Admins)
   154  		if err != nil {
   155  			return nil, err
   156  		}
   157  		for _, tableNameOrPrefix := range group.TableNamesOrPrefixes {
   158  			entries = append(entries, aclEntry{
   159  				tableNameOrPrefix: tableNameOrPrefix,
   160  				groupName:         group.Name,
   161  				acl: map[Role]acl.ACL{
   162  					READER: readers,
   163  					WRITER: writers,
   164  					ADMIN:  admins,
   165  				},
   166  			})
   167  		}
   168  	}
   169  	sort.Sort(entries)
   170  	return entries, nil
   171  }
   172  
   173  func (tacl *tableACL) aclFactory() (acl.Factory, error) {
   174  	if tacl.factory == nil {
   175  		return GetCurrentACLFactory()
   176  	}
   177  	return tacl.factory, nil
   178  }
   179  
   180  func (tacl *tableACL) Set(config *tableaclpb.Config) error {
   181  	factory, err := tacl.aclFactory()
   182  	if err != nil {
   183  		return err
   184  	}
   185  	entries, err := load(config, factory.New)
   186  	if err != nil {
   187  		return err
   188  	}
   189  	tacl.Lock()
   190  	tacl.entries = entries
   191  	tacl.config = proto.Clone(config).(*tableaclpb.Config)
   192  	callback := tacl.callback
   193  	tacl.Unlock()
   194  	if callback != nil {
   195  		callback()
   196  	}
   197  	return nil
   198  }
   199  
   200  // Valid returns whether the tableACL is valid.
   201  // Currently it only checks that it has been initialized.
   202  func (tacl *tableACL) Valid() bool {
   203  	tacl.RLock()
   204  	defer tacl.RUnlock()
   205  	return tacl.entries != nil
   206  }
   207  
   208  // ValidateProto returns an error if the given proto has problems
   209  // that would cause InitFromProto to fail.
   210  func ValidateProto(config *tableaclpb.Config) (err error) {
   211  	t := patricia.NewTrie()
   212  	for _, group := range config.TableGroups {
   213  		for _, name := range group.TableNamesOrPrefixes {
   214  			var prefix patricia.Prefix
   215  			if strings.HasSuffix(name, "%") {
   216  				prefix = []byte(strings.TrimSuffix(name, "%"))
   217  			} else {
   218  				prefix = []byte(name + "\000")
   219  			}
   220  			if bytes.Contains(prefix, []byte("%")) {
   221  				return fmt.Errorf("got: %s, '%%' means this entry is a prefix and should not appear in the middle of name or prefix", name)
   222  			}
   223  			overlapVisitor := func(_ patricia.Prefix, item patricia.Item) error {
   224  				return fmt.Errorf("conflicting entries: %q overlaps with %q", name, item)
   225  			}
   226  			if err := t.VisitSubtree(prefix, overlapVisitor); err != nil {
   227  				return err
   228  			}
   229  			if err := t.VisitPrefixes(prefix, overlapVisitor); err != nil {
   230  				return err
   231  			}
   232  			t.Insert(prefix, name)
   233  		}
   234  	}
   235  	return nil
   236  }
   237  
   238  // Authorized returns the list of entities who have the specified role on a tablel.
   239  func Authorized(table string, role Role) *ACLResult {
   240  	return currentTableACL.Authorized(table, role)
   241  }
   242  
   243  func (tacl *tableACL) Authorized(table string, role Role) *ACLResult {
   244  	tacl.RLock()
   245  	defer tacl.RUnlock()
   246  	start := 0
   247  	end := len(tacl.entries)
   248  	for start < end {
   249  		mid := start + (end-start)/2
   250  		val := tacl.entries[mid].tableNameOrPrefix
   251  		if table == val || (strings.HasSuffix(val, "%") && strings.HasPrefix(table, val[:len(val)-1])) {
   252  			acl, ok := tacl.entries[mid].acl[role]
   253  			if ok {
   254  				return &ACLResult{
   255  					ACL:       acl,
   256  					GroupName: tacl.entries[mid].groupName,
   257  				}
   258  			}
   259  			break
   260  		} else if table < val {
   261  			end = mid
   262  		} else {
   263  			start = mid + 1
   264  		}
   265  	}
   266  	return &ACLResult{
   267  		ACL:       acl.DenyAllACL{},
   268  		GroupName: "",
   269  	}
   270  }
   271  
   272  // GetCurrentConfig returns a copy of current tableacl configuration.
   273  func GetCurrentConfig() *tableaclpb.Config {
   274  	return currentTableACL.Config()
   275  }
   276  
   277  func (tacl *tableACL) Config() *tableaclpb.Config {
   278  	tacl.RLock()
   279  	defer tacl.RUnlock()
   280  	return proto.Clone(tacl.config).(*tableaclpb.Config)
   281  }
   282  
   283  // Register registers an AclFactory.
   284  func Register(name string, factory acl.Factory) {
   285  	mu.Lock()
   286  	defer mu.Unlock()
   287  	if _, ok := acls[name]; ok {
   288  		panic(fmt.Sprintf("register a registered key: %s", name))
   289  	}
   290  	acls[name] = factory
   291  }
   292  
   293  // SetDefaultACL sets the default ACL implementation.
   294  func SetDefaultACL(name string) {
   295  	mu.Lock()
   296  	defer mu.Unlock()
   297  	defaultACL = name
   298  }
   299  
   300  // GetCurrentACLFactory returns current table acl implementation.
   301  func GetCurrentACLFactory() (acl.Factory, error) {
   302  	mu.Lock()
   303  	defer mu.Unlock()
   304  	if len(acls) == 0 {
   305  		return nil, fmt.Errorf("no AclFactories registered")
   306  	}
   307  	if defaultACL == "" {
   308  		if len(acls) == 1 {
   309  			for _, aclFactory := range acls {
   310  				return aclFactory, nil
   311  			}
   312  		}
   313  		return nil, errors.New("there are more than one AclFactory registered but no default has been given")
   314  	}
   315  	if aclFactory, ok := acls[defaultACL]; ok {
   316  		return aclFactory, nil
   317  	}
   318  	return nil, fmt.Errorf("aclFactory for given default: %s is not found", defaultACL)
   319  }