k8s.io/apiserver@v0.31.1/pkg/authentication/request/headerrequest/requestheader_test.go (about) 1 /* 2 Copyright 2016 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 headerrequest 18 19 import ( 20 "net/http" 21 "reflect" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 26 "k8s.io/apiserver/pkg/authentication/user" 27 ) 28 29 func TestRequestHeader(t *testing.T) { 30 testcases := map[string]struct { 31 nameHeaders []string 32 groupHeaders []string 33 extraPrefixHeaders []string 34 requestHeaders http.Header 35 finalHeaders http.Header 36 37 expectedUser user.Info 38 expectedOk bool 39 }{ 40 "empty": {}, 41 "user no match": { 42 nameHeaders: []string{"X-Remote-User"}, 43 }, 44 "user match": { 45 nameHeaders: []string{"X-Remote-User"}, 46 requestHeaders: http.Header{"X-Remote-User": {"Bob"}}, 47 expectedUser: &user.DefaultInfo{ 48 Name: "Bob", 49 Groups: []string{}, 50 Extra: map[string][]string{}, 51 }, 52 expectedOk: true, 53 }, 54 "user exact match": { 55 nameHeaders: []string{"X-Remote-User"}, 56 requestHeaders: http.Header{ 57 "Prefixed-X-Remote-User-With-Suffix": {"Bob"}, 58 "X-Remote-User-With-Suffix": {"Bob"}, 59 }, 60 }, 61 "user first match": { 62 nameHeaders: []string{ 63 "X-Remote-User", 64 "A-Second-X-Remote-User", 65 "Another-X-Remote-User", 66 }, 67 requestHeaders: http.Header{ 68 "X-Remote-User": {"", "First header, second value"}, 69 "A-Second-X-Remote-User": {"Second header, first value", "Second header, second value"}, 70 "Another-X-Remote-User": {"Third header, first value"}}, 71 expectedUser: &user.DefaultInfo{ 72 Name: "Second header, first value", 73 Groups: []string{}, 74 Extra: map[string][]string{}, 75 }, 76 expectedOk: true, 77 }, 78 "user case-insensitive": { 79 nameHeaders: []string{"x-REMOTE-user"}, // configured headers can be case-insensitive 80 requestHeaders: http.Header{"X-Remote-User": {"Bob"}}, // the parsed headers are normalized by the http package 81 expectedUser: &user.DefaultInfo{ 82 Name: "Bob", 83 Groups: []string{}, 84 Extra: map[string][]string{}, 85 }, 86 expectedOk: true, 87 }, 88 89 "groups none": { 90 nameHeaders: []string{"X-Remote-User"}, 91 groupHeaders: []string{"X-Remote-Group"}, 92 requestHeaders: http.Header{ 93 "X-Remote-User": {"Bob"}, 94 }, 95 expectedUser: &user.DefaultInfo{ 96 Name: "Bob", 97 Groups: []string{}, 98 Extra: map[string][]string{}, 99 }, 100 expectedOk: true, 101 }, 102 "groups all matches": { 103 nameHeaders: []string{"X-Remote-User"}, 104 groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"}, 105 requestHeaders: http.Header{ 106 "X-Remote-User": {"Bob"}, 107 "X-Remote-Group-1": {"one-a", "one-b"}, 108 "X-Remote-Group-2": {"two-a", "two-b"}, 109 }, 110 expectedUser: &user.DefaultInfo{ 111 Name: "Bob", 112 Groups: []string{"one-a", "one-b", "two-a", "two-b"}, 113 Extra: map[string][]string{}, 114 }, 115 expectedOk: true, 116 }, 117 "groups case-insensitive": { 118 nameHeaders: []string{"X-REMOTE-User"}, 119 groupHeaders: []string{"X-REMOTE-Group"}, 120 requestHeaders: http.Header{ 121 "X-Remote-User": {"Bob"}, 122 "X-Remote-Group": {"Users"}, 123 }, 124 expectedUser: &user.DefaultInfo{ 125 Name: "Bob", 126 Groups: []string{"Users"}, 127 Extra: map[string][]string{}, 128 }, 129 expectedOk: true, 130 }, 131 132 "extra prefix matches case-insensitive": { 133 nameHeaders: []string{"X-Remote-User"}, 134 groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"}, 135 extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"}, 136 requestHeaders: http.Header{ 137 "X-Remote-User": {"Bob"}, 138 "X-Remote-Group-1": {"one-a", "one-b"}, 139 "X-Remote-Group-2": {"two-a", "two-b"}, 140 "X-Remote-extra-1-key1": {"alfa", "bravo"}, 141 "X-Remote-Extra-1-Key2": {"charlie", "delta"}, 142 "X-Remote-Extra-1-": {"india", "juliet"}, 143 "X-Remote-extra-2-": {"kilo", "lima"}, 144 "X-Remote-extra-2-Key1": {"echo", "foxtrot"}, 145 "X-Remote-Extra-2-key2": {"golf", "hotel"}, 146 }, 147 expectedUser: &user.DefaultInfo{ 148 Name: "Bob", 149 Groups: []string{"one-a", "one-b", "two-a", "two-b"}, 150 Extra: map[string][]string{ 151 "key1": {"alfa", "bravo", "echo", "foxtrot"}, 152 "key2": {"charlie", "delta", "golf", "hotel"}, 153 "": {"india", "juliet", "kilo", "lima"}, 154 }, 155 }, 156 expectedOk: true, 157 }, 158 159 "extra prefix matches case-insensitive with unrelated headers": { 160 nameHeaders: []string{"X-Remote-User"}, 161 groupHeaders: []string{"X-Remote-Group-1", "X-Remote-Group-2"}, 162 extraPrefixHeaders: []string{"X-Remote-Extra-1-", "X-Remote-Extra-2-"}, 163 requestHeaders: http.Header{ 164 "X-Group-Remote": {"snorlax"}, // unrelated header 165 "X-Group-Bear": {"panda"}, // another unrelated header 166 "X-Remote-User": {"Bob"}, 167 "X-Remote-Group-1": {"one-a", "one-b"}, 168 "X-Remote-Group-2": {"two-a", "two-b"}, 169 "X-Remote-extra-1-key1": {"alfa", "bravo"}, 170 "X-Remote-Extra-1-Key2": {"charlie", "delta"}, 171 "X-Remote-Extra-1-": {"india", "juliet"}, 172 "X-Remote-extra-2-": {"kilo", "lima"}, 173 "X-Remote-extra-2-Key1": {"echo", "foxtrot"}, 174 "X-Remote-Extra-2-key2": {"golf", "hotel"}, 175 }, 176 finalHeaders: http.Header{ 177 "X-Group-Remote": {"snorlax"}, 178 "X-Group-Bear": {"panda"}, 179 }, 180 expectedUser: &user.DefaultInfo{ 181 Name: "Bob", 182 Groups: []string{"one-a", "one-b", "two-a", "two-b"}, 183 Extra: map[string][]string{ 184 "key1": {"alfa", "bravo", "echo", "foxtrot"}, 185 "key2": {"charlie", "delta", "golf", "hotel"}, 186 "": {"india", "juliet", "kilo", "lima"}, 187 }, 188 }, 189 expectedOk: true, 190 }, 191 192 "escaped extra keys": { 193 nameHeaders: []string{"X-Remote-User"}, 194 groupHeaders: []string{"X-Remote-Group"}, 195 extraPrefixHeaders: []string{"X-Remote-Extra-"}, 196 requestHeaders: http.Header{ 197 "X-Remote-User": {"Bob"}, 198 "X-Remote-Group": {"one-a", "one-b"}, 199 "X-Remote-Extra-Alpha": {"alphabetical"}, 200 "X-Remote-Extra-Alph4num3r1c": {"alphanumeric"}, 201 "X-Remote-Extra-Percent%20encoded": {"percent encoded"}, 202 "X-Remote-Extra-Almost%zzpercent%xxencoded": {"not quite percent encoded"}, 203 "X-Remote-Extra-Example.com%2fpercent%2520encoded": {"url with double percent encoding"}, 204 "X-Remote-Extra-Example.com%2F%E4%BB%8A%E6%97%A5%E3%81%AF": {"url with unicode"}, 205 "X-Remote-Extra-Abc123!#$+.-_*\\^`~|'": {"header key legal characters"}, 206 }, 207 expectedUser: &user.DefaultInfo{ 208 Name: "Bob", 209 Groups: []string{"one-a", "one-b"}, 210 Extra: map[string][]string{ 211 "alpha": {"alphabetical"}, 212 "alph4num3r1c": {"alphanumeric"}, 213 "percent encoded": {"percent encoded"}, 214 "almost%zzpercent%xxencoded": {"not quite percent encoded"}, 215 "example.com/percent%20encoded": {"url with double percent encoding"}, 216 "example.com/今日は": {"url with unicode"}, 217 "abc123!#$+.-_*\\^`~|'": {"header key legal characters"}, 218 }, 219 }, 220 expectedOk: true, 221 }, 222 } 223 224 for k, testcase := range testcases { 225 t.Run(k, func(t *testing.T) { 226 auth, err := New(testcase.nameHeaders, testcase.groupHeaders, testcase.extraPrefixHeaders) 227 if err != nil { 228 t.Fatal(err) 229 } 230 req := &http.Request{Header: testcase.requestHeaders} 231 232 resp, ok, _ := auth.AuthenticateRequest(req) 233 if testcase.expectedOk != ok { 234 t.Errorf("%v: expected %v, got %v", k, testcase.expectedOk, ok) 235 } 236 if !ok { 237 return 238 } 239 if e, a := testcase.expectedUser, resp.User; !reflect.DeepEqual(e, a) { 240 t.Errorf("%v: expected %#v, got %#v", k, e, a) 241 } 242 243 want := testcase.finalHeaders 244 if want == nil && testcase.requestHeaders != nil { 245 want = http.Header{} 246 } 247 if diff := cmp.Diff(want, testcase.requestHeaders); len(diff) > 0 { 248 t.Errorf("unexpected final headers (-want +got):\n%s", diff) 249 } 250 }) 251 } 252 }