istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/server/ca/authenticate/cert_authenticator_test.go (about)

     1  // Copyright 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  
    15  package authenticate
    16  
    17  import (
    18  	"context"
    19  	"crypto/tls"
    20  	"crypto/x509"
    21  	"crypto/x509/pkix"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"google.golang.org/grpc/credentials"
    26  	"google.golang.org/grpc/peer"
    27  
    28  	"istio.io/istio/pkg/security"
    29  	"istio.io/istio/security/pkg/pki/util"
    30  )
    31  
    32  type mockAuthInfo struct {
    33  	authType string
    34  }
    35  
    36  func (ai mockAuthInfo) AuthType() string {
    37  	return ai.authType
    38  }
    39  
    40  func TestAuthenticate_clientCertAuthenticator(t *testing.T) {
    41  	callerID := "test.identity"
    42  	ids := []util.Identity{
    43  		{Type: util.TypeURI, Value: []byte(callerID)},
    44  	}
    45  	sanExt, err := util.BuildSANExtension(ids)
    46  	if err != nil {
    47  		t.Error(err)
    48  	}
    49  
    50  	testCases := map[string]struct {
    51  		certChain          [][]*x509.Certificate
    52  		caller             *security.Caller
    53  		authenticateErrMsg string
    54  		fakeAuthInfo       *mockAuthInfo
    55  	}{
    56  		"No client certificate": {
    57  			certChain:          nil,
    58  			caller:             nil,
    59  			authenticateErrMsg: "no client certificate is presented",
    60  		},
    61  		"Unsupported auth type": {
    62  			certChain:          nil,
    63  			caller:             nil,
    64  			authenticateErrMsg: "unsupported auth type: \"not-tls\"",
    65  			fakeAuthInfo:       &mockAuthInfo{"not-tls"},
    66  		},
    67  		"Empty cert chain": {
    68  			certChain:          [][]*x509.Certificate{},
    69  			caller:             nil,
    70  			authenticateErrMsg: "no verified chain is found",
    71  		},
    72  		"Certificate has no SAN": {
    73  			certChain: [][]*x509.Certificate{
    74  				{
    75  					{
    76  						Version: 1,
    77  					},
    78  				},
    79  			},
    80  			authenticateErrMsg: "the SAN extension does not exist",
    81  		},
    82  		"With client certificate": {
    83  			certChain: [][]*x509.Certificate{
    84  				{
    85  					{
    86  						Extensions: []pkix.Extension{*sanExt},
    87  					},
    88  				},
    89  			},
    90  			caller: &security.Caller{Identities: []string{callerID}},
    91  		},
    92  	}
    93  
    94  	auth := &ClientCertAuthenticator{}
    95  
    96  	for id, tc := range testCases {
    97  		ctx := context.Background()
    98  		if tc.certChain != nil {
    99  			tlsInfo := credentials.TLSInfo{
   100  				State: tls.ConnectionState{VerifiedChains: tc.certChain},
   101  			}
   102  			p := &peer.Peer{AuthInfo: tlsInfo}
   103  			ctx = peer.NewContext(ctx, p)
   104  		}
   105  		if tc.fakeAuthInfo != nil {
   106  			ctx = peer.NewContext(ctx, &peer.Peer{AuthInfo: tc.fakeAuthInfo})
   107  		}
   108  
   109  		result, err := auth.Authenticate(security.AuthContext{GrpcContext: ctx})
   110  		if len(tc.authenticateErrMsg) > 0 {
   111  			if err == nil {
   112  				t.Errorf("Case %s: Succeeded. Error expected: %v", id, err)
   113  			} else if err.Error() != tc.authenticateErrMsg {
   114  				t.Errorf("Case %s: Incorrect error message: want %s but got %s",
   115  					id, tc.authenticateErrMsg, err.Error())
   116  			}
   117  			continue
   118  		} else if err != nil {
   119  			t.Fatalf("Case %s: Unexpected Error: %v", id, err)
   120  		}
   121  
   122  		if !reflect.DeepEqual(tc.caller, result) {
   123  			t.Errorf("Case %q: Unexpected authentication result: want %v but got %v",
   124  				id, tc.caller, result)
   125  		}
   126  	}
   127  }