k8s.io/kubernetes@v1.29.3/pkg/kubeapiserver/options/authorization_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes 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 options 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/google/go-cmp/cmp" 27 "github.com/spf13/pflag" 28 29 utilerrors "k8s.io/apimachinery/pkg/util/errors" 30 "k8s.io/apimachinery/pkg/util/wait" 31 "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" 32 ) 33 34 func TestAuthzValidate(t *testing.T) { 35 examplePolicyFile := "../../auth/authorizer/abac/example_policy_file.jsonl" 36 37 testCases := []struct { 38 name string 39 modes []string 40 policyFile string 41 webhookConfigFile string 42 webhookRetryBackoff *wait.Backoff 43 expectErr bool 44 expectErrorSubString string 45 }{ 46 { 47 name: "Unknown modes should return errors", 48 modes: []string{"DoesNotExist"}, 49 expectErr: true, 50 expectErrorSubString: "is not a valid mode", 51 }, 52 { 53 name: "At least one authorizationMode is necessary", 54 modes: []string{}, 55 expectErr: true, 56 expectErrorSubString: "at least one authorization-mode must be passed", 57 }, 58 { 59 name: "ModeAlwaysAllow specified more than once", 60 modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysAllow}, 61 expectErr: true, 62 expectErrorSubString: "has mode specified more than once", 63 }, 64 { 65 name: "ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile", 66 modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny}, 67 expectErr: false, 68 }, 69 { 70 name: "ModeABAC requires a policy file", 71 modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC}, 72 expectErr: true, 73 expectErrorSubString: "authorization-mode ABAC's authorization policy file not passed", 74 }, 75 { 76 name: "Authorization Policy file cannot be used without ModeABAC", 77 modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny}, 78 policyFile: examplePolicyFile, 79 webhookConfigFile: "", 80 expectErr: true, 81 expectErrorSubString: "cannot specify --authorization-policy-file without mode ABAC", 82 }, 83 { 84 name: "ModeABAC should not error if a valid policy path is provided", 85 modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC}, 86 policyFile: examplePolicyFile, 87 webhookConfigFile: "", 88 expectErr: false, 89 }, 90 { 91 name: "ModeWebhook requires a config file", 92 modes: []string{modes.ModeWebhook}, 93 expectErr: true, 94 expectErrorSubString: "authorization-mode Webhook's authorization config file not passed", 95 }, 96 { 97 name: "Cannot provide webhook config file without ModeWebhook", 98 modes: []string{modes.ModeAlwaysAllow}, 99 webhookConfigFile: "authz_webhook_config.yaml", 100 expectErr: true, 101 expectErrorSubString: "cannot specify --authorization-webhook-config-file without mode Webhook", 102 }, 103 { 104 name: "ModeWebhook should not error if a valid config file is provided", 105 modes: []string{modes.ModeWebhook}, 106 webhookConfigFile: "authz_webhook_config.yaml", 107 expectErr: false, 108 }, 109 { 110 name: "ModeWebhook should error if an invalid number of webhook retry attempts is provided", 111 modes: []string{modes.ModeWebhook}, 112 webhookConfigFile: "authz_webhook_config.yaml", 113 webhookRetryBackoff: &wait.Backoff{Steps: 0}, 114 expectErr: true, 115 expectErrorSubString: "number of webhook retry attempts must be greater than 0", 116 }, 117 } 118 119 for _, testcase := range testCases { 120 t.Run(testcase.name, func(t *testing.T) { 121 options := NewBuiltInAuthorizationOptions() 122 options.Modes = testcase.modes 123 options.WebhookConfigFile = testcase.webhookConfigFile 124 options.WebhookRetryBackoff = testcase.webhookRetryBackoff 125 options.PolicyFile = testcase.policyFile 126 127 errs := options.Validate() 128 if len(errs) > 0 && !testcase.expectErr { 129 t.Errorf("got unexpected err %v", errs) 130 } 131 if testcase.expectErr && len(errs) == 0 { 132 t.Errorf("should return an error") 133 } 134 if len(errs) > 0 && testcase.expectErr { 135 if !strings.Contains(utilerrors.NewAggregate(errs).Error(), testcase.expectErrorSubString) { 136 t.Errorf("exepected to found error: %s, but no error found", testcase.expectErrorSubString) 137 } 138 } 139 }) 140 } 141 } 142 143 func TestBuiltInAuthorizationOptionsAddFlags(t *testing.T) { 144 var args = []string{ 145 fmt.Sprintf("--authorization-mode=%s,%s,%s,%s", modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC, modes.ModeWebhook), 146 "--authorization-policy-file=policy_file.json", 147 "--authorization-webhook-config-file=webhook_config_file.yaml", 148 "--authorization-webhook-version=v1", 149 "--authorization-webhook-cache-authorized-ttl=60s", 150 "--authorization-webhook-cache-unauthorized-ttl=30s", 151 } 152 153 expected := &BuiltInAuthorizationOptions{ 154 Modes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC, modes.ModeWebhook}, 155 PolicyFile: "policy_file.json", 156 WebhookConfigFile: "webhook_config_file.yaml", 157 WebhookVersion: "v1", 158 WebhookCacheAuthorizedTTL: 60 * time.Second, 159 WebhookCacheUnauthorizedTTL: 30 * time.Second, 160 WebhookRetryBackoff: &wait.Backoff{ 161 Duration: 500 * time.Millisecond, 162 Factor: 1.5, 163 Jitter: 0.2, 164 Steps: 5, 165 }, 166 } 167 168 opts := NewBuiltInAuthorizationOptions() 169 pf := pflag.NewFlagSet("test-builtin-authorization-opts", pflag.ContinueOnError) 170 opts.AddFlags(pf) 171 172 if err := pf.Parse(args); err != nil { 173 t.Fatal(err) 174 } 175 176 if !opts.AreLegacyFlagsSet() { 177 t.Fatal("legacy flags should have been configured") 178 } 179 180 // setting the method to nil since methods can't be compared with reflect.DeepEqual 181 opts.AreLegacyFlagsSet = nil 182 183 if !reflect.DeepEqual(opts, expected) { 184 t.Error(cmp.Diff(opts, expected)) 185 } 186 }