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 }