vitess.io/vitess@v0.16.2/go/vt/vtadmin/testutil/authztestgen/main.go (about)

     1  /*
     2  Copyright 2022 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 main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"text/template"
    25  
    26  	"github.com/spf13/pflag"
    27  
    28  	"vitess.io/vitess/go/vt/vtadmin/rbac"
    29  
    30  	vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
    31  )
    32  
    33  type Config struct {
    34  	Package  string           `json:"package"`
    35  	Clusters []*ClusterConfig `json:"clusters"`
    36  	Tests    []*Test          `json:"tests"`
    37  }
    38  
    39  type ClusterConfig struct {
    40  	ID                      string                    `json:"id"`
    41  	Name                    string                    `json:"name"`
    42  	FakeVtctldClientResults []*FakeVtctldClientResult `json:"vtctldclient_mock_data"`
    43  	DBTablets               []*vtadminpb.Tablet       `json:"db_tablet_list"`
    44  }
    45  
    46  type Test struct {
    47  	Method         string        `json:"method"`
    48  	Rules          []*AuthzRules `json:"rules"`
    49  	Request        string        `json:"request"`
    50  	SerializeCases bool          `json:"serialize_cases"`
    51  	Cases          []*TestCase   `json:"cases"`
    52  }
    53  
    54  type TestCase struct {
    55  	Name            string      `json:"name"`
    56  	Actor           *rbac.Actor `json:"actor"`
    57  	IsPermitted     bool        `json:"is_permitted"`
    58  	IncludeErrorVar bool        `json:"include_error_var"`
    59  	Assertions      []string    `json:"assertions"`
    60  }
    61  
    62  type AuthzRules struct {
    63  	Resource string   `json:"resource"`
    64  	Actions  []string `json:"actions"`
    65  	Subjects []string `json:"subjects"`
    66  	Clusters []string `json:"clusters"`
    67  }
    68  
    69  type FakeVtctldClientResult struct {
    70  	FieldName string `json:"field"`
    71  	Type      string `json:"type"`
    72  	Value     string `json:"value"`
    73  }
    74  
    75  type DocConfig struct {
    76  	Methods []*DocMethod
    77  }
    78  
    79  type DocMethod struct {
    80  	Name  string
    81  	Rules []*struct {
    82  		Resource rbac.Resource
    83  		Action   rbac.Action
    84  	}
    85  }
    86  
    87  func transformConfigForDocs(in Config) DocConfig {
    88  	cfg := DocConfig{}
    89  
    90  	for _, t := range in.Tests {
    91  		m := &DocMethod{
    92  			Name: t.Method,
    93  		}
    94  
    95  		resourceActions := map[string]struct{}{}
    96  		for _, r := range t.Rules {
    97  			for _, a := range r.Actions {
    98  				k := fmt.Sprintf("%s.%s", r.Resource, a)
    99  				if _, ok := resourceActions[k]; ok {
   100  					continue
   101  				}
   102  
   103  				resourceActions[k] = struct{}{}
   104  
   105  				m.Rules = append(m.Rules, &struct {
   106  					Resource rbac.Resource
   107  					Action   rbac.Action
   108  				}{
   109  					Resource: rbac.Resource(r.Resource),
   110  					Action:   rbac.Action(a),
   111  				})
   112  			}
   113  		}
   114  
   115  		cfg.Methods = append(cfg.Methods, m)
   116  	}
   117  
   118  	return cfg
   119  }
   120  
   121  func panicIf(err error) {
   122  	if err != nil {
   123  		panic(err)
   124  	}
   125  }
   126  
   127  func open(path string) (output io.Writer, closer func()) {
   128  	output = os.Stdout
   129  	closer = func() {}
   130  
   131  	if path != "" {
   132  		f, err := os.Create(path)
   133  		panicIf(err)
   134  
   135  		closer = func() { f.Close() }
   136  		output = f
   137  	}
   138  
   139  	return output, closer
   140  }
   141  
   142  func main() {
   143  	path := pflag.StringP("config", "c", "config.json", "authztest configuration (see the Config type in this package for the spec)")
   144  	pflag.StringVarP(path, "config-path", "p", "config.json", "alias for --config")
   145  	outputPath := pflag.StringP("output", "o", "", "destination to write generated code. if empty, defaults to os.Stdout")
   146  	docgen := pflag.Bool("docgen", false, "generate docs table from authztest config instead of authz tests themselves")
   147  
   148  	pflag.Parse()
   149  
   150  	data, err := os.ReadFile(*path)
   151  	panicIf(err)
   152  
   153  	var cfg Config
   154  	err = json.Unmarshal(data, &cfg)
   155  	panicIf(err)
   156  
   157  	if *docgen {
   158  		cfg := transformConfigForDocs(cfg)
   159  		tmpl, err := template.New("docs").Funcs(map[string]any{
   160  			"formatDocRow": formatDocRow,
   161  		}).Parse(_doct)
   162  		panicIf(err)
   163  
   164  		output, closer := open(*outputPath)
   165  		defer closer()
   166  
   167  		err = tmpl.Execute(output, &cfg)
   168  		panicIf(err)
   169  
   170  		return
   171  	}
   172  
   173  	tmpl, err := template.New("tests").Funcs(map[string]any{
   174  		"getActor":       getActor,
   175  		"writeAssertion": writeAssertion,
   176  	}).Parse(_t)
   177  	panicIf(err)
   178  
   179  	output, closer := open(*outputPath)
   180  	defer closer()
   181  
   182  	err = tmpl.Execute(output, &cfg)
   183  	panicIf(err)
   184  }