Fix tests to work with Wire `UserID` and `DeviceID`

pull/1689/head
Herman Slatman 4 months ago
parent 9eed61a9c5
commit 93ba1654ea
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -50,12 +50,12 @@ func (n *NewOrderRequest) Validate() error {
return acme.NewError(acme.ErrorMalformedType, "permanent identifier cannot be empty")
}
case acme.WireUser:
_, err := wire.ParseID([]byte(id.Value))
_, err := wire.ParseUserID([]byte(id.Value))
if err != nil {
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing Wire ID")
}
case acme.WireDevice:
wireID, err := wire.ParseID([]byte(id.Value))
wireID, err := wire.ParseDeviceID([]byte(id.Value))
if err != nil {
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing Wire ID")
}
@ -297,7 +297,7 @@ func newAuthorization(ctx context.Context, az *acme.Authorization) error {
return acme.WrapError(acme.ErrorMalformedType, err, "invalid Go template registered for 'target'")
}
case acme.WireDevice:
wireID, err := wire.ParseID([]byte(az.Identifier.Value))
wireID, err := wire.ParseDeviceID([]byte(az.Identifier.Value))
if err != nil {
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing WireUser")
}

@ -101,7 +101,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
return test{
nor: &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-id", Value: "{}"},
{Type: "wireapp-device", Value: "{}"},
},
},
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "": invalid Wire client ID URI "": error parsing : scheme is missing`),
@ -111,7 +111,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
return test{
nor: &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-id", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "nowireapp://example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
{Type: "wireapp-device", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "nowireapp://example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
},
},
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "nowireapp://example.com": invalid Wire client ID scheme "nowireapp"; expected "wireapp"`),
@ -121,7 +121,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
return test{
nor: &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-id", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://user-device@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
{Type: "wireapp-device", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://user-device@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
},
},
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "wireapp://user-device@example.com": invalid Wire client ID username "user-device"`),
@ -205,13 +205,28 @@ func TestNewOrderRequest_Validate(t *testing.T) {
naf: naf,
}
},
"ok/wireapp-idd": func(t *testing.T) test {
"ok/wireapp-user": func(t *testing.T) test {
nbf := time.Now().UTC().Add(time.Minute)
naf := time.Now().UTC().Add(5 * time.Minute)
return test{
nor: &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-id", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
{Type: "wireapp-user", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
},
NotAfter: naf,
NotBefore: nbf,
},
nbf: nbf,
naf: naf,
}
},
"ok/wireapp-device": func(t *testing.T) test {
nbf := time.Now().UTC().Add(time.Minute)
naf := time.Now().UTC().Add(5 * time.Minute)
return test{
nor: &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-device", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
},
NotAfter: naf,
NotBefore: nbf,
@ -1719,7 +1734,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
},
}
},
"ok/default-naf-nbf-wireapp": func(t *testing.T) test {
"ok/default-naf-nbf-wireapp-user": func(t *testing.T) test {
acmeWireProv := newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wire.Options{
OIDC: &wire.OIDCOptions{
@ -1749,7 +1764,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
acc := &acme.Account{ID: "accID"}
nor := &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-id", Value: `{"client-id": "wireapp://user!client@domain"}`},
{Type: "wireapp-user", Value: `{"name": "Alice Smith", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
},
}
b, err := json.Marshal(nor)
@ -1758,9 +1773,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
ctx = context.WithValue(ctx, accContextKey, acc)
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
var (
ch1, ch2 **acme.Challenge
az1ID *string
count = 0
ch1 **acme.Challenge
az1ID *string
)
return test{
ctx: ctx,
@ -1769,20 +1783,113 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
ca: &mockCA{},
db: &acme.MockDB{
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
switch count {
case 0:
ch.ID = "wireapp-oidc"
assert.Equals(t, ch.Type, acme.WIREOIDC01)
ch1 = &ch
case 1:
ch.ID = "wireapp-dpop"
assert.Equals(t, ch.Type, acme.WIREDPOP01)
ch2 = &ch
default:
assert.FatalError(t, errors.New("test logic error"))
return errors.New("force")
}
count++
ch.ID = "wireapp-oidc"
assert.Equals(t, ch.Type, acme.WIREOIDC01)
ch1 = &ch
assert.Equals(t, ch.AccountID, "accID")
assert.NotEquals(t, ch.Token, "")
assert.Equals(t, ch.Status, acme.StatusPending)
assert.Equals(t, ch.Value, `{"name": "Alice Smith", "handle": "wireapp://%40alice.smith.qa@example.com"}`)
return nil
},
MockCreateAuthorization: func(ctx context.Context, az *acme.Authorization) error {
az.ID = "az1ID"
az1ID = &az.ID
assert.Equals(t, az.AccountID, "accID")
assert.NotEquals(t, az.Token, "")
assert.Equals(t, az.Status, acme.StatusPending)
assert.Equals(t, az.Identifier, nor.Identifiers[0])
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1})
assert.Equals(t, az.Wildcard, false)
return nil
},
MockCreateOrder: func(ctx context.Context, o *acme.Order) error {
o.ID = "ordID"
assert.Equals(t, o.AccountID, "accID")
assert.Equals(t, o.ProvisionerID, prov.GetID())
assert.Equals(t, o.Status, acme.StatusPending)
assert.Equals(t, o.Identifiers, nor.Identifiers)
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
return nil
},
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
assert.Equals(t, prov.GetID(), provisionerID)
assert.Equals(t, "accID", accountID)
return nil, nil
},
},
vr: func(t *testing.T, o *acme.Order) {
now := clock.Now()
testBufferDur := 5 * time.Second
orderExpiry := now.Add(defaultOrderExpiry)
expNbf := now.Add(-defaultOrderBackdate)
expNaf := now.Add(prov.DefaultTLSCertDuration())
assert.Equals(t, o.ID, "ordID")
assert.Equals(t, o.Status, acme.StatusPending)
assert.Equals(t, o.Identifiers, nor.Identifiers)
assert.Equals(t, o.AuthorizationURLs, []string{fmt.Sprintf("%s/acme/%s/authz/az1ID", baseURL.String(), escProvName)})
assert.True(t, o.NotBefore.Add(-testBufferDur).Before(expNbf))
assert.True(t, o.NotBefore.Add(testBufferDur).After(expNbf))
assert.True(t, o.NotAfter.Add(-testBufferDur).Before(expNaf))
assert.True(t, o.NotAfter.Add(testBufferDur).After(expNaf))
assert.True(t, o.ExpiresAt.Add(-testBufferDur).Before(orderExpiry))
assert.True(t, o.ExpiresAt.Add(testBufferDur).After(orderExpiry))
},
}
},
"ok/default-naf-nbf-wireapp-device": func(t *testing.T) test {
acmeWireProv := newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wire.Options{
OIDC: &wire.OIDCOptions{
Provider: &wire.Provider{
IssuerURL: "https://issuer.example.com",
AuthURL: "",
TokenURL: "",
JWKSURL: "",
UserInfoURL: "",
Algorithms: []string{"ES256"},
},
Config: &wire.Config{
ClientID: "integration test",
SignatureAlgorithms: []string{"ES256"},
SkipClientIDCheck: true,
SkipExpiryCheck: true,
SkipIssuerCheck: true,
InsecureSkipSignatureCheck: true,
Now: time.Now,
},
},
DPOP: &wire.DPOPOptions{
SigningKey: []byte(fakeWireSigningKey),
},
},
})
acc := &acme.Account{ID: "accID"}
nor := &NewOrderRequest{
Identifiers: []acme.Identifier{
{Type: "wireapp-device", Value: `{"client-id": "wireapp://user!client@domain"}`},
},
}
b, err := json.Marshal(nor)
assert.FatalError(t, err)
ctx := acme.NewProvisionerContext(context.Background(), acmeWireProv)
ctx = context.WithValue(ctx, accContextKey, acc)
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
var (
ch1 **acme.Challenge
az1ID *string
)
return test{
ctx: ctx,
statusCode: 201,
nor: nor,
ca: &mockCA{},
db: &acme.MockDB{
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
ch.ID = "wireapp-dpop"
assert.Equals(t, ch.Type, acme.WIREDPOP01)
ch1 = &ch
assert.Equals(t, ch.AccountID, "accID")
assert.NotEquals(t, ch.Token, "")
assert.Equals(t, ch.Status, acme.StatusPending)
@ -1796,7 +1903,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
assert.NotEquals(t, az.Token, "")
assert.Equals(t, az.Status, acme.StatusPending)
assert.Equals(t, az.Identifier, nor.Identifiers[0])
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1, *ch2})
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1})
assert.Equals(t, az.Wildcard, false)
return nil
},

@ -234,7 +234,11 @@ func TestWireIntegration(t *testing.T) {
nor := &NewOrderRequest{
Identifiers: []acme.Identifier{
{
Type: "wireapp-id",
Type: "wireapp-user",
Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`,
},
{
Type: "wireapp-device",
Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`,
},
},

@ -373,7 +373,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire OIDC challenge payload")
}
wireID, err := wire.ParseID([]byte(ch.Value))
wireID, err := wire.ParseUserID([]byte(ch.Value))
if err != nil {
return WrapErrorISE(err, "error unmarshalling challenge data")
}
@ -451,7 +451,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
return nil
}
func validateWireOIDCClaims(o *wireprovisioner.OIDCOptions, token *oidc.IDToken, wireID wire.ID) (map[string]any, error) {
func validateWireOIDCClaims(o *wireprovisioner.OIDCOptions, token *oidc.IDToken, wireID wire.UserID) (map[string]any, error) {
var m map[string]any
if err := token.Claims(&m); err != nil {
return nil, fmt.Errorf("failed extracting OIDC ID token claims: %w", err)
@ -500,7 +500,7 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire DPoP challenge payload")
}
wireID, err := wire.ParseID([]byte(ch.Value))
wireID, err := wire.ParseDeviceID([]byte(ch.Value))
if err != nil {
return WrapErrorISE(err, "error unmarshalling challenge data")
}
@ -598,7 +598,7 @@ type wireVerifyParams struct {
dpopKeyID string
issuer string
audience string
wireID wire.ID
wireID wire.DeviceID
chToken string
t time.Time
}

@ -100,7 +100,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Type: "urn:ietf:params:acme:error:serverInternal",
Detail: "The server experienced an internal error",
Status: 500,
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.ID`),
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.DeviceID`),
},
}
},
@ -1096,7 +1096,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Type: "urn:ietf:params:acme:error:serverInternal",
Detail: "The server experienced an internal error",
Status: 500,
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.ID`),
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.UserID`),
},
}
},
@ -2030,7 +2030,7 @@ MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA=
require.True(t, ok)
issuer := "http://wire.com:19983/clients/7a41cf5b79683410/access-token"
wireID := wire.ID{
wireID := wire.DeviceID{
ClientID: "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com",
Handle: "wireapp://%40alice_wire@wire.com",
}
@ -2127,7 +2127,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
idToken, err := verifier.Verify(ctx, idTokenString)
require.NoError(t, err)
wireID := wire.ID{
wireID := wire.UserID{
Name: "Alice Smith",
Handle: "wireapp://%40alice_wire@wire.com",
}

@ -340,7 +340,7 @@ func createWireSubject(o *Order, csr *x509.CertificateRequest) (subject x509util
for _, identifier := range o.Identifiers {
switch identifier.Type {
case WireUser:
wireID, err := wire.ParseID([]byte(identifier.Value))
wireID, err := wire.ParseUserID([]byte(identifier.Value))
if err != nil {
return subject, NewErrorISE("unmarshal wireID: %s", err)
}
@ -406,7 +406,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
orderPIDs[indexPID] = n.Value
indexPID++
case WireUser:
wireID, err := wire.ParseID([]byte(n.Value))
wireID, err := wire.ParseUserID([]byte(n.Value))
if err != nil {
return sans, NewErrorISE("unsupported identifier value in order: %s", n.Value)
}
@ -417,7 +417,7 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
tmpOrderURIs[indexURI] = handle
indexURI++
case WireDevice:
wireID, err := wire.ParseID([]byte(n.Value))
wireID, err := wire.ParseDeviceID([]byte(n.Value))
if err != nil {
return sans, NewErrorISE("unsupported identifier value in order: %s", n.Value)
}

@ -8,15 +8,26 @@ import (
"go.step.sm/crypto/kms/uri"
)
type ID struct {
type UserID struct {
Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"`
Handle string `json:"handle,omitempty"`
}
type DeviceID struct {
Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"`
ClientID string `json:"client-id,omitempty"`
Handle string `json:"handle,omitempty"`
}
func ParseID(data []byte) (wireID ID, err error) {
err = json.Unmarshal(data, &wireID)
func ParseUserID(data []byte) (id UserID, err error) {
err = json.Unmarshal(data, &id)
return
}
func ParseDeviceID(data []byte) (id DeviceID, err error) {
err = json.Unmarshal(data, &id)
return
}

@ -7,19 +7,43 @@ import (
"github.com/stretchr/testify/assert"
)
func TestParseID(t *testing.T) {
ok := `{"name": "Alice Smith", "domain": "wire.com", "client-id": "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", "handle": "wireapp://%40alice_wire@wire.com"}`
func TestParseUserID(t *testing.T) {
ok := `{"name": "Alice Smith", "domain": "wire.com", "handle": "wireapp://%40alice_wire@wire.com"}`
tests := []struct {
name string
data []byte
wantWireID ID
wantWireID UserID
expectedErr error
}{
{name: "ok", data: []byte(ok), wantWireID: ID{Name: "Alice Smith", Domain: "wire.com", ClientID: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Handle: "wireapp://%40alice_wire@wire.com"}},
{name: "ok", data: []byte(ok), wantWireID: UserID{Name: "Alice Smith", Domain: "wire.com", Handle: "wireapp://%40alice_wire@wire.com"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotWireID, err := ParseID(tt.data)
gotWireID, err := ParseUserID(tt.data)
if tt.expectedErr != nil {
assert.EqualError(t, err, tt.expectedErr.Error())
return
}
assert.NoError(t, err)
assert.Equal(t, tt.wantWireID, gotWireID)
})
}
}
func TestParseDeviceID(t *testing.T) {
ok := `{"name": "device", "domain": "wire.com", "client-id": "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", "handle": "wireapp://%40alice_wire@wire.com"}`
tests := []struct {
name string
data []byte
wantWireID DeviceID
expectedErr error
}{
{name: "ok", data: []byte(ok), wantWireID: DeviceID{Name: "device", Domain: "wire.com", ClientID: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Handle: "wireapp://%40alice_wire@wire.com"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotWireID, err := ParseDeviceID(tt.data)
if tt.expectedErr != nil {
assert.EqualError(t, err, tt.expectedErr.Error())
return

@ -11,8 +11,6 @@ import (
"github.com/pkg/errors"
"go.step.sm/linkedca"
"github.com/smallstep/certificates/acme/wire"
)
// ACMEChallenge represents the supported acme challenges.
@ -252,11 +250,12 @@ func (p *ACME) AuthorizeOrderIdentifier(_ context.Context, identifier ACMEIdenti
case DNS:
err = x509Policy.IsDNSAllowed(identifier.Value)
case WireID:
var wireID wire.ID
if wireID, err = wire.ParseID([]byte(identifier.Value)); err != nil {
return fmt.Errorf("failed parsing Wire SANs: %w", err)
}
err = x509Policy.AreSANsAllowed([]string{wireID.ClientID, wireID.Handle})
// TODO: parse the value as user or device ID
// var wireID wire.ID
// if wireID, err = wire.ParseID([]byte(identifier.Value)); err != nil {
// return fmt.Errorf("failed parsing Wire SANs: %w", err)
// }
// err = x509Policy.AreSANsAllowed([]string{wireID.ClientID, wireID.Handle})
default:
err = fmt.Errorf("invalid ACME identifier type '%s' provided", identifier.Type)
}

Loading…
Cancel
Save