github.com/letsencrypt/boulder@v0.20251208.0/test/integration/account_test.go (about)

     1  //go:build integration
     2  
     3  package integration
     4  
     5  import (
     6  	"crypto/ecdsa"
     7  	"crypto/elliptic"
     8  	"crypto/rand"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/eggsampler/acme/v3"
    13  
    14  	"github.com/letsencrypt/boulder/core"
    15  )
    16  
    17  // TestNewAccount tests that various new-account requests are handled correctly.
    18  // It does not test malformed account contacts, as we no longer care about
    19  // how well-formed the contact string is, since we no longer store them.
    20  func TestNewAccount(t *testing.T) {
    21  	t.Parallel()
    22  
    23  	c, err := acme.NewClient("http://boulder.service.consul:4001/directory")
    24  	if err != nil {
    25  		t.Fatalf("failed to connect to acme directory: %s", err)
    26  	}
    27  
    28  	for _, tc := range []struct {
    29  		name    string
    30  		tos     bool
    31  		contact []string
    32  		wantErr string
    33  	}{
    34  		{
    35  			name:    "No TOS agreement",
    36  			tos:     false,
    37  			contact: nil,
    38  			wantErr: "must agree to terms of service",
    39  		},
    40  		{
    41  			name:    "No contacts",
    42  			tos:     true,
    43  			contact: nil,
    44  		},
    45  		{
    46  			name:    "One contact",
    47  			tos:     true,
    48  			contact: []string{"mailto:single@chisel.com"},
    49  		},
    50  		{
    51  			name:    "Many contacts",
    52  			tos:     true,
    53  			contact: []string{"mailto:one@chisel.com", "mailto:two@chisel.com", "mailto:three@chisel.com"},
    54  		},
    55  	} {
    56  		t.Run(tc.name, func(t *testing.T) {
    57  			key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    58  			if err != nil {
    59  				t.Fatalf("failed to generate account key: %s", err)
    60  			}
    61  
    62  			acct, err := c.NewAccount(key, false, tc.tos, tc.contact...)
    63  
    64  			if tc.wantErr == "" {
    65  				if err != nil {
    66  					t.Fatalf("NewAccount(tos: %t, contact: %#v) = %s, but want no err", tc.tos, tc.contact, err)
    67  				}
    68  
    69  				if len(acct.Contact) != 0 {
    70  					t.Errorf("NewAccount(tos: %t, contact: %#v) = %#v, but want empty contacts", tc.tos, tc.contact, acct)
    71  				}
    72  			} else if tc.wantErr != "" {
    73  				if err == nil {
    74  					t.Fatalf("NewAccount(tos: %t, contact: %#v) = %#v, but want error %q", tc.tos, tc.contact, acct, tc.wantErr)
    75  				}
    76  
    77  				if !strings.Contains(err.Error(), tc.wantErr) {
    78  					t.Errorf("NewAccount(tos: %t, contact: %#v) = %q, but want error %q", tc.tos, tc.contact, err, tc.wantErr)
    79  				}
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  func TestNewAccount_DuplicateKey(t *testing.T) {
    86  	t.Parallel()
    87  
    88  	c, err := acme.NewClient("http://boulder.service.consul:4001/directory")
    89  	if err != nil {
    90  		t.Fatalf("failed to connect to acme directory: %s", err)
    91  	}
    92  
    93  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    94  	if err != nil {
    95  		t.Fatalf("failed to generate account key: %s", err)
    96  	}
    97  
    98  	// OnlyReturnExisting: true with a never-before-used key should result in an error.
    99  	acct, err := c.NewAccount(key, true, true)
   100  	if err == nil {
   101  		t.Fatalf("NewAccount(key: 1, ore: true) = %#v, but want error notFound", acct)
   102  	}
   103  
   104  	// Create an account.
   105  	acct, err = c.NewAccount(key, false, true)
   106  	if err != nil {
   107  		t.Fatalf("NewAccount(key: 1, ore: false) = %#v, but want success", err)
   108  	}
   109  
   110  	// A duplicate request should just return the same account.
   111  	acct, err = c.NewAccount(key, false, true)
   112  	if err != nil {
   113  		t.Fatalf("NewAccount(key: 1, ore: false) = %#v, but want success", err)
   114  	}
   115  
   116  	// Specifying OnlyReturnExisting should do the same.
   117  	acct, err = c.NewAccount(key, true, true)
   118  	if err != nil {
   119  		t.Fatalf("NewAccount(key: 1, ore: true) = %#v, but want success", err)
   120  	}
   121  
   122  	// Deactivate the account.
   123  	acct, err = c.DeactivateAccount(acct)
   124  	if err != nil {
   125  		t.Fatalf("DeactivateAccount(acct: 1) = %#v, but want success", err)
   126  	}
   127  
   128  	// Now a new account request should return an error.
   129  	acct, err = c.NewAccount(key, false, true)
   130  	if err == nil {
   131  		t.Fatalf("NewAccount(key: 1, ore: false) = %#v, but want error deactivated", acct)
   132  	}
   133  
   134  	// Specifying OnlyReturnExisting should do the same.
   135  	acct, err = c.NewAccount(key, true, true)
   136  	if err == nil {
   137  		t.Fatalf("NewAccount(key: 1, ore: true) = %#v, but want error deactivated", acct)
   138  	}
   139  }
   140  
   141  // TestAccountDeactivate tests that account deactivation works. It does not test
   142  // that we reject requests for other account statuses, because eggsampler/acme
   143  // wisely does not allow us to construct such malformed requests.
   144  func TestAccountDeactivate(t *testing.T) {
   145  	t.Parallel()
   146  
   147  	c, err := acme.NewClient("http://boulder.service.consul:4001/directory")
   148  	if err != nil {
   149  		t.Fatalf("failed to connect to acme directory: %s", err)
   150  	}
   151  
   152  	acctKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   153  	if err != nil {
   154  		t.Fatalf("failed to generate account key: %s", err)
   155  	}
   156  
   157  	account, err := c.NewAccount(acctKey, false, true, "mailto:hello@blackhole.net")
   158  	if err != nil {
   159  		t.Fatalf("failed to create initial account: %s", err)
   160  	}
   161  
   162  	got, err := c.DeactivateAccount(account)
   163  	if err != nil {
   164  		t.Errorf("unexpected error while deactivating account: %s", err)
   165  	}
   166  
   167  	if got.Status != string(core.StatusDeactivated) {
   168  		t.Errorf("account deactivation should have set status to %q, instead got %q", core.StatusDeactivated, got.Status)
   169  	}
   170  }