<commit_before>// Copyright (c) 2020 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tlscfg

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/zap/zaptest/observer"
)

const (
	serverCert = "./testdata/example-server-cert.pem"
	serverKey  = "./testdata/example-server-key.pem"
	clientCert = "./testdata/example-client-cert.pem"
	clientKey  = "./testdata/example-client-key.pem"

	caCert    = "./testdata/example-CA-cert.pem"
	badCaCert = "./testdata/bad-CA-cert.txt"
)

func TestReload(t *testing.T) {
	// copy certs to temp so we can modify them
	certFile, err := ioutil.TempFile("", "cert.crt")
	require.NoError(t, err)
	defer os.Remove(certFile.Name())
	certData, err := ioutil.ReadFile(serverCert)
	require.NoError(t, err)
	_, err = certFile.Write(certData)
	require.NoError(t, err)
	certFile.Close()

	keyFile, err := ioutil.TempFile("", "key.crt")
	require.NoError(t, err)
	defer os.Remove(keyFile.Name())
	keyData, err := ioutil.ReadFile(serverKey)
	require.NoError(t, err)
	_, err = keyFile.Write(keyData)
	require.NoError(t, err)
	keyFile.Close()

	zcore, logObserver := observer.New(zapcore.InfoLevel)
	logger := zap.New(zcore)
	opts := Options{
		CAPath:       caCert,
		ClientCAPath: caCert,
		CertPath:     certFile.Name(),
		KeyPath:      keyFile.Name(),
	}
	watcher, err := newCertWatcher(opts, logger)
	require.NoError(t, err)
	assert.NotNil(t, watcher.certificate())
	defer watcher.Close()

	certPool := x509.NewCertPool()
	require.NoError(t, err)
	go watcher.watchChangesLoop(certPool, certPool)
	cert, err := tls.LoadX509KeyPair(serverCert, serverKey)
	require.NoError(t, err)
	assert.Equal(t, &cert, watcher.certificate())

	// Write the client's public key.
	certData, err = ioutil.ReadFile(clientCert)
	require.NoError(t, err)
	err = syncWrite(certFile.Name(), certData, 0644)
	require.NoError(t, err)

	waitUntil(func() bool {
		// Logged when the cert is reloaded with mismatching client public key and existing server private key.
		return logObserver.FilterMessage("Failed to load certificate").
			FilterField(zap.String("certificate", certFile.Name())).Len() > 0
	}, 100, time.Millisecond*200)

	assert.True(t, logObserver.
		FilterMessage("Failed to load certificate").
		FilterField(zap.String("certificate", certFile.Name())).Len() > 0,
		"Unable to locate 'Failed to load certificate' in log. All logs: "+fmt.Sprint(logObserver.All()))

	// Write the client's private key.
	keyData, err = ioutil.ReadFile(clientKey)
	require.NoError(t, err)
	err = syncWrite(keyFile.Name(), keyData, 0644)
	require.NoError(t, err)

	waitUntil(func() bool {
		// Logged when the client private key is modified in the cert which enables successful reloading of
		// the cert as both private and public keys now match.
		return logObserver.FilterMessage("Loaded modified certificate").
			FilterField(zap.String("certificate", keyFile.Name())).Len() > 0
	}, 100, time.Millisecond*200)

	assert.True(t, logObserver.
		FilterMessage("Loaded modified certificate").
		FilterField(zap.String("certificate", keyFile.Name())).Len() > 0,
		"Unable to locate 'Loaded modified certificate' in log. All logs: "+fmt.Sprint(logObserver.All()))

	cert, err = tls.LoadX509KeyPair(filepath.Clean(clientCert), clientKey)
	require.NoError(t, err)
	assert.Equal(t, &cert, watcher.certificate())
}

func TestReload_ca_certs(t *testing.T) {
	// copy certs to temp so we can modify them
	caFile, err := ioutil.TempFile("", "cert.crt")<commit_msg>Address review comments from #2655 (#2673)<commit_after>// Copyright (c) 2020 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tlscfg

import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/zap/zaptest/observer"
)

const (
	serverCert = "./testdata/example-server-cert.pem"
	serverKey  = "./testdata/example-server-key.pem"
	clientCert = "./testdata/example-client-cert.pem"
	clientKey  = "./testdata/example-client-key.pem"

	caCert    = "./testdata/example-CA-cert.pem"
	badCaCert = "./testdata/bad-CA-cert.txt"
)

func TestReload(t *testing.T) {
	// copy certs to temp so we can modify them
	certFile, err := ioutil.TempFile("", "cert.crt")
	require.NoError(t, err)
	defer os.Remove(certFile.Name())
	certData, err := ioutil.ReadFile(serverCert)
	require.NoError(t, err)
	_, err = certFile.Write(certData)
	require.NoError(t, err)
	certFile.Close()

	keyFile, err := ioutil.TempFile("", "key.crt")
	require.NoError(t, err)
	defer os.Remove(keyFile.Name())
	keyData, err := ioutil.ReadFile(serverKey)
	require.NoError(t, err)
	_, err = keyFile.Write(keyData)
	require.NoError(t, err)
	keyFile.Close()

	zcore, logObserver := observer.New(zapcore.InfoLevel)
	logger := zap.New(zcore)
	opts := Options{
		CAPath:       caCert,
		ClientCAPath: caCert,
		CertPath:     certFile.Name(),
		KeyPath:      keyFile.Name(),
	}
	watcher, err := newCertWatcher(opts, logger)
	require.NoError(t, err)
	assert.NotNil(t, watcher.certificate())
	defer watcher.Close()

	certPool := x509.NewCertPool()
	require.NoError(t, err)
	go watcher.watchChangesLoop(certPool, certPool)
	cert, err := tls.LoadX509KeyPair(serverCert, serverKey)
	require.NoError(t, err)
	assert.Equal(t, &cert, watcher.certificate())

	// Write the client's public key.
	certData, err = ioutil.ReadFile(clientCert)
	require.NoError(t, err)
	err = syncWrite(certFile.Name(), certData, 0644)
	require.NoError(t, err)

	waitUntil(func() bool {
		// Logged when the cert is reloaded with mismatching client public key and existing server private key.
		return logObserver.FilterMessage("Failed to load certificate").
			FilterField(zap.String("certificate", certFile.Name())).Len() > 0
	}, 2000, time.Millisecond*10)

	assert.True(t, logObserver.
		FilterMessage("Failed to load certificate").
		FilterField(zap.String("certificate", certFile.Name())).Len() > 0,
		"Unable to locate 'Failed to load certificate' in log. All logs: %v", logObserver.All())

	// Write the client's private key.
	keyData, err = ioutil.ReadFile(clientKey)
	require.NoError(t, err)
	err = syncWrite(keyFile.Name(), keyData, 0644)
	require.NoError(t, err)

	waitUntil(func() bool {
		// Logged when the client private key is modified in the cert which enables successful reloading of
		// the cert as both private and public keys now match.
		return logObserver.FilterMessage("Loaded modified certificate").
			FilterField(zap.String("certificate", keyFile.Name())).Len() > 0
	}, 2000, time.Millisecond*10)

	assert.True(t, logObserver.
		FilterMessage("Loaded modified certificate").
		FilterField(zap.String("certificate", keyFile.Name())).Len() > 0,
		"Unable to locate 'Loaded modified certificate' in log. All logs: %v", logObserver.All())

	cert, err = tls.LoadX509KeyPair(filepath.Clean(clientCert), clientKey)
	require.NoError(t, err)
	assert.Equal(t, &cert, watcher.certificate())
}

func TestReload_ca_certs(t *testing.T) {
	// copy certs to temp so we can modify them
	caFile, err := ioutil.TempFile("", "cert.crt")
{'fraction_non_alphanumeric': 0.139723414401526, 'fraction_numerical': 0.011444921316165951, 'mean_word_length': 4.405283505154639, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3854443', 'n_tokens_mistral': 3009, 'n_tokens_neox': 2868, 'n_words': 737}