...

Source file src/github.com/letsencrypt/boulder/cmd/contact-auditor/main_test.go

Documentation: github.com/letsencrypt/boulder/cmd/contact-auditor

     1  package notmain
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/jmhodges/clock"
    13  	corepb "github.com/letsencrypt/boulder/core/proto"
    14  	"github.com/letsencrypt/boulder/db"
    15  	blog "github.com/letsencrypt/boulder/log"
    16  	"github.com/letsencrypt/boulder/metrics"
    17  	"github.com/letsencrypt/boulder/sa"
    18  	"github.com/letsencrypt/boulder/test"
    19  	"github.com/letsencrypt/boulder/test/vars"
    20  )
    21  
    22  var (
    23  	regA *corepb.Registration
    24  	regB *corepb.Registration
    25  	regC *corepb.Registration
    26  	regD *corepb.Registration
    27  )
    28  
    29  const (
    30  	emailARaw = "test@example.com"
    31  	emailBRaw = "example@notexample.com"
    32  	emailCRaw = "test-example@notexample.com"
    33  	telNum    = "666-666-7777"
    34  )
    35  
    36  func TestContactAuditor(t *testing.T) {
    37  	testCtx := setup(t)
    38  	defer testCtx.cleanUp()
    39  
    40  	// Add some test registrations.
    41  	testCtx.addRegistrations(t)
    42  
    43  	resChan := make(chan *result, 10)
    44  	err := testCtx.c.run(context.Background(), resChan)
    45  	test.AssertNotError(t, err, "received error")
    46  
    47  	// We should get back A, B, C, and D
    48  	test.AssertEquals(t, len(resChan), 4)
    49  	for entry := range resChan {
    50  		err := validateContacts(entry.id, entry.createdAt, entry.contacts)
    51  		switch entry.id {
    52  		case regA.Id:
    53  			// Contact validation policy sad path.
    54  			test.AssertDeepEquals(t, entry.contacts, []string{"mailto:test@example.com"})
    55  			test.AssertError(t, err, "failed to error on a contact that violates our e-mail policy")
    56  		case regB.Id:
    57  			// Ensure grace period was respected.
    58  			test.AssertDeepEquals(t, entry.contacts, []string{"mailto:example@notexample.com"})
    59  			test.AssertNotError(t, err, "received error for a valid contact entry")
    60  		case regC.Id:
    61  			// Contact validation happy path.
    62  			test.AssertDeepEquals(t, entry.contacts, []string{"mailto:test-example@notexample.com"})
    63  			test.AssertNotError(t, err, "received error for a valid contact entry")
    64  
    65  			// Unmarshal Contact sad path.
    66  			_, err := unmarshalContact([]byte("[ mailto:test@example.com ]"))
    67  			test.AssertError(t, err, "failed to error while unmarshaling invalid Contact JSON")
    68  
    69  			// Fix our JSON and ensure that the contact field returns
    70  			// errors for our 2 additional contacts
    71  			contacts, err := unmarshalContact([]byte(`[ "mailto:test@example.com", "tel:666-666-7777" ]`))
    72  			test.AssertNotError(t, err, "received error while unmarshaling valid Contact JSON")
    73  
    74  			// Ensure Contact validation now fails.
    75  			err = validateContacts(entry.id, entry.createdAt, contacts)
    76  			test.AssertError(t, err, "failed to error on 2 invalid Contact entries")
    77  		case regD.Id:
    78  			test.AssertDeepEquals(t, entry.contacts, []string{"tel:666-666-7777"})
    79  			test.AssertError(t, err, "failed to error on an invalid contact entry")
    80  		default:
    81  			t.Errorf("ID: %d was not expected", entry.id)
    82  		}
    83  	}
    84  
    85  	// Load results file.
    86  	data, err := os.ReadFile(testCtx.c.resultsFile.Name())
    87  	if err != nil {
    88  		t.Error(err)
    89  	}
    90  
    91  	// Results file should contain 2 newlines, 1 for each result.
    92  	contentLines := strings.Split(strings.TrimRight(string(data), "\n"), "\n")
    93  	test.AssertEquals(t, len(contentLines), 2)
    94  
    95  	// Each result entry should contain six tab separated columns.
    96  	for _, line := range contentLines {
    97  		test.AssertEquals(t, len(strings.Split(line, "\t")), 6)
    98  	}
    99  }
   100  
   101  type testCtx struct {
   102  	c       contactAuditor
   103  	dbMap   *db.WrappedMap
   104  	ssa     *sa.SQLStorageAuthority
   105  	cleanUp func()
   106  }
   107  
   108  func (tc testCtx) addRegistrations(t *testing.T) {
   109  	emailA := "mailto:" + emailARaw
   110  	emailB := "mailto:" + emailBRaw
   111  	emailC := "mailto:" + emailCRaw
   112  	tel := "tel:" + telNum
   113  
   114  	// Every registration needs a unique JOSE key
   115  	jsonKeyA := []byte(`{
   116    "kty":"RSA",
   117    "n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
   118    "e":"AQAB"
   119  }`)
   120  	jsonKeyB := []byte(`{
   121    "kty":"RSA",
   122    "n":"z8bp-jPtHt4lKBqepeKF28g_QAEOuEsCIou6sZ9ndsQsEjxEOQxQ0xNOQezsKa63eogw8YS3vzjUcPP5BJuVzfPfGd5NVUdT-vSSwxk3wvk_jtNqhrpcoG0elRPQfMVsQWmxCAXCVRz3xbcFI8GTe-syynG3l-g1IzYIIZVNI6jdljCZML1HOMTTW4f7uJJ8mM-08oQCeHbr5ejK7O2yMSSYxW03zY-Tj1iVEebROeMv6IEEJNFSS4yM-hLpNAqVuQxFGetwtwjDMC1Drs1dTWrPuUAAjKGrP151z1_dE74M5evpAhZUmpKv1hY-x85DC6N0hFPgowsanmTNNiV75w",
   123    "e":"AAEAAQ"
   124  }`)
   125  	jsonKeyC := []byte(`{
   126    "kty":"RSA",
   127    "n":"rFH5kUBZrlPj73epjJjyCxzVzZuV--JjKgapoqm9pOuOt20BUTdHqVfC2oDclqM7HFhkkX9OSJMTHgZ7WaVqZv9u1X2yjdx9oVmMLuspX7EytW_ZKDZSzL-sCOFCuQAuYKkLbsdcA3eHBK_lwc4zwdeHFMKIulNvLqckkqYB9s8GpgNXBDIQ8GjR5HuJke_WUNjYHSd8jY1LU9swKWsLQe2YoQUz_ekQvBvBCoaFEtrtRaSJKNLIVDObXFr2TLIiFiM0Em90kK01-eQ7ZiruZTKomll64bRFPoNo4_uwubddg3xTqur2vdF3NyhTrYdvAgTem4uC0PFjEQ1bK_djBQ",
   128    "e":"AQAB"
   129  }`)
   130  	jsonKeyD := []byte(`{
   131    "kty":"RSA",
   132    "n":"rFH5kUBZrlPj73epjJjyCxzVzZuV--JjKgapoqm9pOuOt20BUTdHqVfC2oDclqM7HFhkkX9OSJMTHgZ7WaVqZv9u1X2yjdx9oVmMLuspX7EytW_ZKDZSzL-FCOFCuQAuYKkLbsdcA3eHBK_lwc4zwdeHFMKIulNvLqckkqYB9s8GpgNXBDIQ8GjR5HuJke_WUNjYHSd8jY1LU9swKWsLQe2YoQUz_ekQvBvBCoaFEtrtRaSJKNLIVDObXFr2TLIiFiM0Em90kK01-eQ7ZiruZTKomll64bRFPoNo4_uwubddg3xTqur2vdF3NyhTrYdvAgTem4uC0PFjEQ1bK_djBQ",
   133    "e":"AQAB"
   134  }`)
   135  
   136  	initialIP, err := net.ParseIP("127.0.0.1").MarshalText()
   137  	test.AssertNotError(t, err, "Couldn't create initialIP")
   138  
   139  	regA = &corepb.Registration{
   140  		Id:        1,
   141  		Contact:   []string{emailA},
   142  		Key:       jsonKeyA,
   143  		InitialIP: initialIP,
   144  	}
   145  	regB = &corepb.Registration{
   146  		Id:        2,
   147  		Contact:   []string{emailB},
   148  		Key:       jsonKeyB,
   149  		InitialIP: initialIP,
   150  	}
   151  	regC = &corepb.Registration{
   152  		Id:        3,
   153  		Contact:   []string{emailC},
   154  		Key:       jsonKeyC,
   155  		InitialIP: initialIP,
   156  	}
   157  	// Reg D has a `tel:` contact ACME URL
   158  	regD = &corepb.Registration{
   159  		Id:        4,
   160  		Contact:   []string{tel},
   161  		Key:       jsonKeyD,
   162  		InitialIP: initialIP,
   163  	}
   164  
   165  	// Add the four test registrations
   166  	ctx := context.Background()
   167  	regA, err = tc.ssa.NewRegistration(ctx, regA)
   168  	test.AssertNotError(t, err, "Couldn't store regA")
   169  	regB, err = tc.ssa.NewRegistration(ctx, regB)
   170  	test.AssertNotError(t, err, "Couldn't store regB")
   171  	regC, err = tc.ssa.NewRegistration(ctx, regC)
   172  	test.AssertNotError(t, err, "Couldn't store regC")
   173  	regD, err = tc.ssa.NewRegistration(ctx, regD)
   174  	test.AssertNotError(t, err, "Couldn't store regD")
   175  }
   176  
   177  func setup(t *testing.T) testCtx {
   178  	log := blog.UseMock()
   179  
   180  	// Using DBConnSAFullPerms to be able to insert registrations and
   181  	// certificates
   182  	dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
   183  	if err != nil {
   184  		t.Fatalf("Couldn't connect to the database: %s", err)
   185  	}
   186  
   187  	// Make temp results file
   188  	file, err := os.CreateTemp("", fmt.Sprintf("audit-%s", time.Now().Format("2006-01-02T15:04")))
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  
   193  	cleanUp := func() {
   194  		test.ResetBoulderTestDatabase(t)
   195  		file.Close()
   196  		os.Remove(file.Name())
   197  	}
   198  
   199  	db, err := sa.DBMapForTest(vars.DBConnSAMailer)
   200  	if err != nil {
   201  		t.Fatalf("Couldn't connect to the database: %s", err)
   202  	}
   203  
   204  	ssa, err := sa.NewSQLStorageAuthority(dbMap, dbMap, nil, 1, 0, clock.New(), log, metrics.NoopRegisterer)
   205  	if err != nil {
   206  		t.Fatalf("unable to create SQLStorageAuthority: %s", err)
   207  	}
   208  
   209  	return testCtx{
   210  		c: contactAuditor{
   211  			db:          db,
   212  			resultsFile: file,
   213  			logger:      blog.NewMock(),
   214  		},
   215  		dbMap:   dbMap,
   216  		ssa:     ssa,
   217  		cleanUp: cleanUp,
   218  	}
   219  }
   220  

View as plain text