k8s.io/apiserver@v0.31.1/pkg/authentication/request/websocket/protocol_test.go (about)

     1  /*
     2  Copyright 2017 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 websocket
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"net/http"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"k8s.io/apiserver/pkg/authentication/authenticator"
    27  	"k8s.io/apiserver/pkg/authentication/user"
    28  )
    29  
    30  func TestAuthenticateRequest(t *testing.T) {
    31  	auth := NewProtocolAuthenticator(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
    32  		if token != "token" {
    33  			t.Errorf("unexpected token: %s", token)
    34  		}
    35  		return &authenticator.Response{User: &user.DefaultInfo{Name: "user"}}, true, nil
    36  	}))
    37  	resp, ok, err := auth.AuthenticateRequest(&http.Request{
    38  		Header: http.Header{
    39  			"Connection":             []string{"upgrade"},
    40  			"Upgrade":                []string{"websocket"},
    41  			"Sec-Websocket-Protocol": []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
    42  		},
    43  	})
    44  	if !ok || resp == nil || err != nil {
    45  		t.Errorf("expected valid user")
    46  	}
    47  }
    48  
    49  func TestAuthenticateRequestMultipleConnectionHeaders(t *testing.T) {
    50  	auth := NewProtocolAuthenticator(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
    51  		if token != "token" {
    52  			t.Errorf("unexpected token: %s", token)
    53  		}
    54  		return &authenticator.Response{User: &user.DefaultInfo{Name: "user"}}, true, nil
    55  	}))
    56  	resp, ok, err := auth.AuthenticateRequest(&http.Request{
    57  		Header: http.Header{
    58  			"Connection":             []string{"not", "upgrade"},
    59  			"Upgrade":                []string{"websocket"},
    60  			"Sec-Websocket-Protocol": []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
    61  		},
    62  	})
    63  	if !ok || resp == nil || err != nil {
    64  		t.Errorf("expected valid user")
    65  	}
    66  }
    67  
    68  func TestAuthenticateRequestTokenInvalid(t *testing.T) {
    69  	auth := NewProtocolAuthenticator(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
    70  		return nil, false, nil
    71  	}))
    72  	resp, ok, err := auth.AuthenticateRequest(&http.Request{
    73  		Header: http.Header{
    74  			"Connection":             []string{"upgrade"},
    75  			"Upgrade":                []string{"websocket"},
    76  			"Sec-Websocket-Protocol": []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
    77  		},
    78  	})
    79  	if ok || resp != nil {
    80  		t.Errorf("expected not authenticated user")
    81  	}
    82  	if err != errInvalidToken {
    83  		t.Errorf("expected errInvalidToken error, got %v", err)
    84  	}
    85  }
    86  
    87  func TestAuthenticateRequestTokenInvalidCustomError(t *testing.T) {
    88  	customError := errors.New("custom")
    89  	auth := NewProtocolAuthenticator(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
    90  		return nil, false, customError
    91  	}))
    92  	resp, ok, err := auth.AuthenticateRequest(&http.Request{
    93  		Header: http.Header{
    94  			"Connection":             []string{"upgrade"},
    95  			"Upgrade":                []string{"websocket"},
    96  			"Sec-Websocket-Protocol": []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
    97  		},
    98  	})
    99  	if ok || resp != nil {
   100  		t.Errorf("expected not authenticated user")
   101  	}
   102  	if err != customError {
   103  		t.Errorf("expected custom error, got %v", err)
   104  	}
   105  }
   106  
   107  func TestAuthenticateRequestTokenError(t *testing.T) {
   108  	auth := NewProtocolAuthenticator(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
   109  		return nil, false, errors.New("error")
   110  	}))
   111  	resp, ok, err := auth.AuthenticateRequest(&http.Request{
   112  		Header: http.Header{
   113  			"Connection":             []string{"upgrade"},
   114  			"Upgrade":                []string{"websocket"},
   115  			"Sec-Websocket-Protocol": []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
   116  		},
   117  	})
   118  	if ok || resp != nil || err == nil {
   119  		t.Errorf("expected error")
   120  	}
   121  }
   122  
   123  func TestAuthenticateRequestBadValue(t *testing.T) {
   124  	testCases := []struct {
   125  		Req *http.Request
   126  	}{
   127  		{Req: &http.Request{}},
   128  		{Req: &http.Request{Header: http.Header{
   129  			"Connection":             []string{"upgrade"},
   130  			"Upgrade":                []string{"websocket"},
   131  			"Sec-Websocket-Protocol": []string{"other-protocol"}}},
   132  		},
   133  		{Req: &http.Request{Header: http.Header{
   134  			"Connection":             []string{"upgrade"},
   135  			"Upgrade":                []string{"websocket"},
   136  			"Sec-Websocket-Protocol": []string{"base64url.bearer.authorization.k8s.io."}}},
   137  		},
   138  	}
   139  	for i, testCase := range testCases {
   140  		auth := NewProtocolAuthenticator(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
   141  			t.Errorf("authentication should not have been called")
   142  			return nil, false, nil
   143  		}))
   144  		resp, ok, err := auth.AuthenticateRequest(testCase.Req)
   145  		if ok || resp != nil || err != nil {
   146  			t.Errorf("%d: expected not authenticated (no token)", i)
   147  		}
   148  	}
   149  }
   150  
   151  func TestBearerToken(t *testing.T) {
   152  	tests := map[string]struct {
   153  		ProtocolHeaders []string
   154  		TokenAuth       authenticator.Token
   155  
   156  		ExpectedUserName        string
   157  		ExpectedOK              bool
   158  		ExpectedErr             bool
   159  		ExpectedProtocolHeaders []string
   160  	}{
   161  		"no header": {
   162  			ProtocolHeaders:         nil,
   163  			ExpectedUserName:        "",
   164  			ExpectedOK:              false,
   165  			ExpectedErr:             false,
   166  			ExpectedProtocolHeaders: nil,
   167  		},
   168  		"empty header": {
   169  			ProtocolHeaders:         []string{""},
   170  			ExpectedUserName:        "",
   171  			ExpectedOK:              false,
   172  			ExpectedErr:             false,
   173  			ExpectedProtocolHeaders: []string{""},
   174  		},
   175  		"non-bearer header": {
   176  			ProtocolHeaders:         []string{"undefined"},
   177  			ExpectedUserName:        "",
   178  			ExpectedOK:              false,
   179  			ExpectedErr:             false,
   180  			ExpectedProtocolHeaders: []string{"undefined"},
   181  		},
   182  		"empty bearer token": {
   183  			ProtocolHeaders:         []string{"base64url.bearer.authorization.k8s.io."},
   184  			ExpectedUserName:        "",
   185  			ExpectedOK:              false,
   186  			ExpectedErr:             false,
   187  			ExpectedProtocolHeaders: []string{"base64url.bearer.authorization.k8s.io."},
   188  		},
   189  		"valid bearer token removing header": {
   190  			ProtocolHeaders: []string{"base64url.bearer.authorization.k8s.io.dG9rZW4", "dummy, dummy2"},
   191  			TokenAuth: authenticator.TokenFunc(func(ctx context.Context, t string) (*authenticator.Response, bool, error) {
   192  				return &authenticator.Response{User: &user.DefaultInfo{Name: "myuser"}}, true, nil
   193  			}),
   194  			ExpectedUserName:        "myuser",
   195  			ExpectedOK:              true,
   196  			ExpectedErr:             false,
   197  			ExpectedProtocolHeaders: []string{"dummy,dummy2"},
   198  		},
   199  		"invalid bearer token": {
   200  			ProtocolHeaders:         []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
   201  			TokenAuth:               authenticator.TokenFunc(func(ctx context.Context, t string) (*authenticator.Response, bool, error) { return nil, false, nil }),
   202  			ExpectedUserName:        "",
   203  			ExpectedOK:              false,
   204  			ExpectedErr:             true,
   205  			ExpectedProtocolHeaders: []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
   206  		},
   207  		"error bearer token": {
   208  			ProtocolHeaders: []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
   209  			TokenAuth: authenticator.TokenFunc(func(ctx context.Context, t string) (*authenticator.Response, bool, error) {
   210  				return nil, false, errors.New("error")
   211  			}),
   212  			ExpectedUserName:        "",
   213  			ExpectedOK:              false,
   214  			ExpectedErr:             true,
   215  			ExpectedProtocolHeaders: []string{"base64url.bearer.authorization.k8s.io.dG9rZW4,dummy"},
   216  		},
   217  	}
   218  
   219  	for k, tc := range tests {
   220  		req, _ := http.NewRequest("GET", "/", nil)
   221  		req.Header.Set("Connection", "upgrade")
   222  		req.Header.Set("Upgrade", "websocket")
   223  		for _, h := range tc.ProtocolHeaders {
   224  			req.Header.Add("Sec-Websocket-Protocol", h)
   225  		}
   226  
   227  		bearerAuth := NewProtocolAuthenticator(tc.TokenAuth)
   228  		resp, ok, err := bearerAuth.AuthenticateRequest(req)
   229  		if tc.ExpectedErr != (err != nil) {
   230  			t.Errorf("%s: Expected err=%v, got %v", k, tc.ExpectedErr, err)
   231  			continue
   232  		}
   233  		if ok != tc.ExpectedOK {
   234  			t.Errorf("%s: Expected ok=%v, got %v", k, tc.ExpectedOK, ok)
   235  			continue
   236  		}
   237  		if ok && resp.User.GetName() != tc.ExpectedUserName {
   238  			t.Errorf("%s: Expected username=%v, got %v", k, tc.ExpectedUserName, resp.User.GetName())
   239  			continue
   240  		}
   241  		if !reflect.DeepEqual(req.Header["Sec-Websocket-Protocol"], tc.ExpectedProtocolHeaders) {
   242  			t.Errorf("%s: Expected headers=%#v, got %#v", k, tc.ExpectedProtocolHeaders, req.Header["Sec-Websocket-Protocol"])
   243  			continue
   244  		}
   245  	}
   246  }