package acme import ( "crypto" "encoding/base64" "testing" "time" "github.com/pkg/errors" "go.step.sm/crypto/jose" "github.com/smallstep/assert" ) func TestKeyToID(t *testing.T) { type test struct { jwk *jose.JSONWebKey exp string err *Error } tests := map[string]func(t *testing.T) test{ "fail/error-generating-thumbprint": func(t *testing.T) test { jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) assert.FatalError(t, err) jwk.Key = "foo" return test{ jwk: jwk, err: NewErrorISE("error generating jwk thumbprint: go-jose/go-jose: unknown key type 'string'"), } }, "ok": func(t *testing.T) test { jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) assert.FatalError(t, err) kid, err := jwk.Thumbprint(crypto.SHA256) assert.FatalError(t, err) return test{ jwk: jwk, exp: base64.RawURLEncoding.EncodeToString(kid), } }, } for name, run := range tests { t.Run(name, func(t *testing.T) { tc := run(t) if id, err := KeyToID(tc.jwk); err != nil { if assert.NotNil(t, tc.err) { var k *Error if errors.As(err, &k) { assert.Equals(t, k.Type, tc.err.Type) assert.Equals(t, k.Detail, tc.err.Detail) assert.Equals(t, k.Status, tc.err.Status) assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) assert.Equals(t, k.Detail, tc.err.Detail) } else { assert.FatalError(t, errors.New("unexpected error type")) } } } else { if assert.Nil(t, tc.err) { assert.Equals(t, id, tc.exp) } } }) } } func TestAccount_GetLocation(t *testing.T) { locationPrefix := "https://test.ca.smallstep.com/acme/foo/account/" type test struct { acc *Account exp string } tests := map[string]test{ "empty": {acc: &Account{LocationPrefix: ""}, exp: ""}, "not-empty": {acc: &Account{ID: "bar", LocationPrefix: locationPrefix}, exp: locationPrefix + "bar"}, } for name, tc := range tests { t.Run(name, func(t *testing.T) { assert.Equals(t, tc.acc.GetLocation(), tc.exp) }) } } func TestAccount_IsValid(t *testing.T) { type test struct { acc *Account exp bool } tests := map[string]test{ "valid": {acc: &Account{Status: StatusValid}, exp: true}, "invalid": {acc: &Account{Status: StatusInvalid}, exp: false}, } for name, tc := range tests { t.Run(name, func(t *testing.T) { assert.Equals(t, tc.acc.IsValid(), tc.exp) }) } } func TestExternalAccountKey_BindTo(t *testing.T) { boundAt := time.Now() tests := []struct { name string eak *ExternalAccountKey acct *Account err *Error }{ { name: "ok", eak: &ExternalAccountKey{ ID: "eakID", ProvisionerID: "provID", Reference: "ref", HmacKey: []byte{1, 3, 3, 7}, }, acct: &Account{ ID: "accountID", }, err: nil, }, { name: "fail/already-bound", eak: &ExternalAccountKey{ ID: "eakID", ProvisionerID: "provID", Reference: "ref", HmacKey: []byte{1, 3, 3, 7}, AccountID: "someAccountID", BoundAt: boundAt, }, acct: &Account{ ID: "accountID", }, err: NewError(ErrorUnauthorizedType, "external account binding key with id '%s' was already bound to account '%s' on %s", "eakID", "someAccountID", boundAt), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { eak := tt.eak acct := tt.acct err := eak.BindTo(acct) wantErr := tt.err != nil gotErr := err != nil if wantErr != gotErr { t.Errorf("ExternalAccountKey.BindTo() error = %v, wantErr %v", err, tt.err) } if wantErr { assert.NotNil(t, err) var ae *Error if assert.True(t, errors.As(err, &ae)) { assert.Equals(t, ae.Type, tt.err.Type) assert.Equals(t, ae.Detail, tt.err.Detail) assert.Equals(t, ae.Subproblems, tt.err.Subproblems) } } else { assert.Equals(t, eak.AccountID, acct.ID) assert.Equals(t, eak.HmacKey, []byte{}) assert.NotNil(t, eak.BoundAt) } }) } }