github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/api/certpool_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package api_test
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/juju/loggo"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api"
    17  	"github.com/juju/juju/pki"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  type certPoolSuite struct {
    22  	testing.BaseSuite
    23  	logs *certLogs
    24  }
    25  
    26  var _ = gc.Suite(&certPoolSuite{})
    27  
    28  func (s *certPoolSuite) SetUpTest(c *gc.C) {
    29  	s.BaseSuite.SetUpTest(c)
    30  	s.logs = &certLogs{}
    31  	loggo.GetLogger("juju.api").SetLogLevel(loggo.TRACE)
    32  	loggo.RegisterWriter("api-certs", s.logs)
    33  }
    34  
    35  func (*certPoolSuite) TestCreateCertPoolNoCert(c *gc.C) {
    36  	pool, err := api.CreateCertPool("")
    37  	c.Assert(err, jc.ErrorIsNil)
    38  	c.Assert(pool.Subjects(), gc.HasLen, 0)
    39  }
    40  
    41  func (*certPoolSuite) TestCreateCertPoolTestCert(c *gc.C) {
    42  	pool, err := api.CreateCertPool(testing.CACert)
    43  	c.Assert(err, jc.ErrorIsNil)
    44  	c.Assert(pool.Subjects(), gc.HasLen, 1)
    45  }
    46  
    47  func (s *certPoolSuite) TestCreateCertPoolNoDir(c *gc.C) {
    48  	certDir := filepath.Join(c.MkDir(), "missing")
    49  	s.PatchValue(api.CertDir, certDir)
    50  
    51  	pool, err := api.CreateCertPool("")
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	c.Assert(pool.Subjects(), gc.HasLen, 0)
    54  
    55  	c.Assert(s.logs.messages, gc.HasLen, 1)
    56  	// The directory not existing is likely to happen a lot, so it is only
    57  	// logged out at trace to help be explicit in the case where detailed
    58  	// debugging is needed.
    59  	c.Assert(s.logs.messages[0], gc.Matches, `TRACE cert dir ".*" does not exist`)
    60  }
    61  
    62  func (s *certPoolSuite) TestCreateCertPoolNotADir(c *gc.C) {
    63  	certDir := filepath.Join(c.MkDir(), "missing")
    64  	s.PatchValue(api.CertDir, certDir)
    65  	// Make the certDir a file instead...
    66  	c.Assert(os.WriteFile(certDir, []byte("blah"), 0644), jc.ErrorIsNil)
    67  
    68  	pool, err := api.CreateCertPool("")
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	c.Assert(pool.Subjects(), gc.HasLen, 0)
    71  
    72  	c.Assert(s.logs.messages, gc.HasLen, 1)
    73  	c.Assert(s.logs.messages[0], gc.Matches, `INFO cert dir ".*" is not a directory`)
    74  }
    75  
    76  func (s *certPoolSuite) TestCreateCertPoolEmptyDir(c *gc.C) {
    77  	certDir := c.MkDir()
    78  	s.PatchValue(api.CertDir, certDir)
    79  
    80  	pool, err := api.CreateCertPool("")
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	c.Assert(pool.Subjects(), gc.HasLen, 0)
    83  	c.Assert(s.logs.messages, gc.HasLen, 1)
    84  	c.Assert(s.logs.messages[0], gc.Matches, `DEBUG added 0 certs to the pool from .*`)
    85  }
    86  
    87  func (s *certPoolSuite) TestCreateCertPoolLoadsPEMFiles(c *gc.C) {
    88  	certDir := c.MkDir()
    89  	s.PatchValue(api.CertDir, certDir)
    90  	s.addCert(c, filepath.Join(certDir, "first.pem"))
    91  	s.addCert(c, filepath.Join(certDir, "second.pem"))
    92  	s.addCert(c, filepath.Join(certDir, "third.pem"))
    93  
    94  	pool, err := api.CreateCertPool("")
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	c.Assert(pool.Subjects(), gc.HasLen, 3)
    97  	c.Assert(s.logs.messages, gc.HasLen, 1)
    98  	c.Assert(s.logs.messages[0], gc.Matches, `DEBUG added 3 certs to the pool from .*`)
    99  }
   100  
   101  func (s *certPoolSuite) TestCreateCertPoolLoadsOnlyPEMFiles(c *gc.C) {
   102  	certDir := c.MkDir()
   103  	s.PatchValue(api.CertDir, certDir)
   104  	s.addCert(c, filepath.Join(certDir, "first.pem"))
   105  	c.Assert(os.WriteFile(filepath.Join(certDir, "second.cert"), []byte("blah"), 0644), jc.ErrorIsNil)
   106  
   107  	pool, err := api.CreateCertPool("")
   108  	c.Assert(err, jc.ErrorIsNil)
   109  	c.Assert(pool.Subjects(), gc.HasLen, 1)
   110  	c.Assert(s.logs.messages, gc.HasLen, 1)
   111  	c.Assert(s.logs.messages[0], gc.Matches, `DEBUG added 1 certs to the pool from .*`)
   112  }
   113  
   114  func (s *certPoolSuite) TestCreateCertPoolLogsBadCerts(c *gc.C) {
   115  	certDir := c.MkDir()
   116  	s.PatchValue(api.CertDir, certDir)
   117  	c.Assert(os.WriteFile(filepath.Join(certDir, "broken.pem"), []byte("blah"), 0644), jc.ErrorIsNil)
   118  
   119  	pool, err := api.CreateCertPool("")
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	c.Assert(pool.Subjects(), gc.HasLen, 0)
   122  	c.Assert(s.logs.messages, gc.HasLen, 2)
   123  	c.Assert(s.logs.messages[0], gc.Matches, `INFO error parsing cert ".*broken.pem": .*`)
   124  	c.Assert(s.logs.messages[1], gc.Matches, `DEBUG added 0 certs to the pool from .*`)
   125  }
   126  
   127  func (s *certPoolSuite) addCert(c *gc.C, filename string) {
   128  	signer, err := pki.DefaultKeyProfile()
   129  	c.Assert(err, jc.ErrorIsNil)
   130  
   131  	caCert, err := pki.NewCA("random model name", signer)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  
   134  	caCertPem, err := pki.CertificateToPemString(pki.DefaultPemHeaders, caCert)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	err = os.WriteFile(filename, []byte(caCertPem), 0644)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  }
   141  
   142  type certLogs struct {
   143  	messages []string
   144  }
   145  
   146  func (c *certLogs) Write(entry loggo.Entry) {
   147  	if strings.HasSuffix(entry.Filename, "certpool.go") {
   148  		c.messages = append(c.messages, fmt.Sprintf("%s %s", entry.Level, entry.Message))
   149  	}
   150  }