1// Copyright 2020 Istio Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package model 16 17import ( 18 "reflect" 19 "testing" 20 21 rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v2" 22 "github.com/gogo/protobuf/proto" 23 24 "istio.io/istio/pkg/util/protomarshal" 25) 26 27func TestGenerator(t *testing.T) { 28 cases := []struct { 29 name string 30 g generator 31 key string 32 value string 33 forTCP bool 34 want interface{} 35 }{ 36 { 37 name: "destIPGenerator", 38 g: destIPGenerator{}, 39 value: "1.2.3.4", 40 want: yamlPermission(t, ` 41 destinationIp: 42 addressPrefix: 1.2.3.4 43 prefixLen: 32`), 44 }, 45 { 46 name: "destPortGenerator", 47 g: destPortGenerator{}, 48 value: "80", 49 want: yamlPermission(t, ` 50 destinationPort: 80`), 51 }, 52 { 53 name: "connSNIGenerator", 54 g: connSNIGenerator{}, 55 value: "exact.com", 56 want: yamlPermission(t, ` 57 requestedServerName: 58 exact: exact.com`), 59 }, 60 { 61 name: "envoyFilterGenerator-string", 62 g: envoyFilterGenerator{}, 63 key: "experimental.a.b.c[d]", 64 value: "val", 65 want: yamlPermission(t, ` 66 metadata: 67 filter: a.b.c 68 path: 69 - key: d 70 value: 71 stringMatch: 72 exact: val`), 73 }, 74 { 75 name: "envoyFilterGenerator-list", 76 g: envoyFilterGenerator{}, 77 key: "experimental.a.b.c[d]", 78 value: "[v1, v2]", 79 want: yamlPermission(t, ` 80 metadata: 81 filter: a.b.c 82 path: 83 - key: d 84 value: 85 listMatch: 86 oneOf: 87 stringMatch: 88 exact: v1, v2`), 89 }, 90 { 91 name: "srcIPGenerator", 92 g: srcIPGenerator{}, 93 value: "1.2.3.4", 94 want: yamlPrincipal(t, ` 95 sourceIp: 96 addressPrefix: 1.2.3.4 97 prefixLen: 32`), 98 }, 99 { 100 name: "srcNamespaceGenerator-http", 101 g: srcNamespaceGenerator{}, 102 value: "foo", 103 want: yamlPrincipal(t, ` 104 metadata: 105 filter: istio_authn 106 path: 107 - key: source.principal 108 value: 109 stringMatch: 110 safeRegex: 111 googleRe2: {} 112 regex: .*/ns/foo/.*`), 113 }, 114 { 115 name: "srcNamespaceGenerator-tcp", 116 g: srcNamespaceGenerator{}, 117 value: "foo", 118 forTCP: true, 119 want: yamlPrincipal(t, ` 120 authenticated: 121 principalName: 122 safeRegex: 123 googleRe2: {} 124 regex: .*/ns/foo/.*`), 125 }, 126 { 127 name: "srcPrincipalGenerator-http", 128 g: srcPrincipalGenerator{}, 129 key: "source.principal", 130 value: "foo", 131 want: yamlPrincipal(t, ` 132 metadata: 133 filter: istio_authn 134 path: 135 - key: source.principal 136 value: 137 stringMatch: 138 exact: foo`), 139 }, 140 { 141 name: "srcPrincipalGenerator-tcp", 142 g: srcPrincipalGenerator{}, 143 key: "source.principal", 144 value: "foo", 145 forTCP: true, 146 want: yamlPrincipal(t, ` 147 authenticated: 148 principalName: 149 exact: spiffe://foo`), 150 }, 151 { 152 name: "requestPrincipalGenerator", 153 g: requestPrincipalGenerator{}, 154 key: "request.auth.principal", 155 value: "foo", 156 want: yamlPrincipal(t, ` 157 metadata: 158 filter: istio_authn 159 path: 160 - key: request.auth.principal 161 value: 162 stringMatch: 163 exact: foo`), 164 }, 165 { 166 name: "requestAudiencesGenerator", 167 g: requestAudiencesGenerator{}, 168 key: "request.auth.audiences", 169 value: "foo", 170 want: yamlPrincipal(t, ` 171 metadata: 172 filter: istio_authn 173 path: 174 - key: request.auth.audiences 175 value: 176 stringMatch: 177 exact: foo`), 178 }, 179 { 180 name: "requestPresenterGenerator", 181 g: requestPresenterGenerator{}, 182 key: "request.auth.presenter", 183 value: "foo", 184 want: yamlPrincipal(t, ` 185 metadata: 186 filter: istio_authn 187 path: 188 - key: request.auth.presenter 189 value: 190 stringMatch: 191 exact: foo`), 192 }, 193 { 194 name: "requestHeaderGenerator", 195 g: requestHeaderGenerator{}, 196 key: "request.headers[x-foo]", 197 value: "foo", 198 want: yamlPrincipal(t, ` 199 header: 200 exactMatch: foo 201 name: x-foo`), 202 }, 203 { 204 name: "requestClaimGenerator", 205 g: requestClaimGenerator{}, 206 key: "request.auth.claims[bar]", 207 value: "foo", 208 want: yamlPrincipal(t, ` 209 metadata: 210 filter: istio_authn 211 path: 212 - key: request.auth.claims 213 - key: bar 214 value: 215 listMatch: 216 oneOf: 217 stringMatch: 218 exact: foo`), 219 }, 220 { 221 name: "hostGenerator", 222 g: hostGenerator{}, 223 value: "foo", 224 want: yamlPermission(t, ` 225 header: 226 exactMatch: foo 227 name: :authority`), 228 }, 229 { 230 name: "pathGenerator14", 231 g: pathGenerator{isIstioVersionGE15: false}, 232 value: "/abc", 233 want: yamlPermission(t, ` 234 header: 235 exactMatch: /abc 236 name: :path`), 237 }, 238 { 239 name: "pathGenerator15", 240 g: pathGenerator{isIstioVersionGE15: true}, 241 value: "/abc", 242 want: yamlPermission(t, ` 243 urlPath: 244 path: 245 exact: /abc`), 246 }, 247 { 248 name: "methodGenerator", 249 g: methodGenerator{}, 250 value: "GET", 251 want: yamlPermission(t, ` 252 header: 253 exactMatch: GET 254 name: :method`), 255 }, 256 } 257 258 for _, tc := range cases { 259 t.Run(tc.name, func(t *testing.T) { 260 var got interface{} 261 var err error 262 if _, ok := tc.want.(*rbacpb.Permission); ok { 263 got, err = tc.g.permission(tc.key, tc.value, tc.forTCP) 264 if err != nil { 265 t.Errorf("both permission and principal returned error") 266 } 267 } else { 268 got, err = tc.g.principal(tc.key, tc.value, tc.forTCP) 269 if err != nil { 270 t.Errorf("both permission and principal returned error") 271 } 272 } 273 if !reflect.DeepEqual(got, tc.want) { 274 var gotYaml string 275 gotProto, ok := got.(proto.Message) 276 if !ok { 277 t.Fatal("failed to extract proto") 278 } 279 if gotYaml, err = protomarshal.ToYAML(gotProto); err != nil { 280 t.Fatalf("%s: failed to parse yaml: %s", tc.name, err) 281 } 282 t.Errorf("got:\n %v\n but want:\n %v", gotYaml, tc.want) 283 } 284 }) 285 } 286} 287 288func yamlPermission(t *testing.T, yaml string) *rbacpb.Permission { 289 t.Helper() 290 p := &rbacpb.Permission{} 291 if err := protomarshal.ApplyYAML(yaml, p); err != nil { 292 t.Fatalf("failed to parse yaml: %s", err) 293 } 294 return p 295} 296 297func yamlPrincipal(t *testing.T, yaml string) *rbacpb.Principal { 298 t.Helper() 299 p := &rbacpb.Principal{} 300 if err := protomarshal.ApplyYAML(yaml, p); err != nil { 301 t.Fatalf("failed to parse yaml: %s", err) 302 } 303 return p 304} 305