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 }