Skip to content

Commit

Permalink
Add functional-option NewPRSigstoreSigned
Browse files Browse the repository at this point in the history
Possibly reject nil values of pointer fields at input?

Signed-off-by: Miloslav Trmač <[email protected]>
  • Loading branch information
mtrmac committed Jan 21, 2023
1 parent e88a98f commit 5158076
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 100 deletions.
110 changes: 73 additions & 37 deletions signature/policy_config_sigstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,90 @@ package signature

import (
"encoding/json"
"errors"
"fmt"

"github.com/containers/image/v5/signature/internal"
)

// newPRSigstoreSigned returns a new prSigstoreSigned if parameters are valid.
func newPRSigstoreSigned(keyPath string, keyData []byte, signedIdentity PolicyReferenceMatch) (*prSigstoreSigned, error) {
if keyPath != "" && keyData != nil {
// PRSigstoreSignedOption is way to pass values to NewPRSigstoreSigned
type PRSigstoreSignedOption func(*prSigstoreSigned) error

// PRSigstoreSignedWithKeyPath specifies a value for the "keyPath" field when calling NewPRSigstoreSigned.
func PRSigstoreSignedWithKeyPath(keyPath string) PRSigstoreSignedOption {
return func(pr *prSigstoreSigned) error {
if pr.KeyPath != "" {
return errors.New(`"keyPath" already specified`)
}
pr.KeyPath = keyPath
return nil
}
}

// PRSigstoreSignedWithKeyData specifies a value for the "keyData" field when calling NewPRSigstoreSigned.
func PRSigstoreSignedWithKeyData(keyData []byte) PRSigstoreSignedOption {
return func(pr *prSigstoreSigned) error {
if pr.KeyData != nil {
return errors.New(`"keyData" already specified`)
}
pr.KeyData = keyData
return nil
}
}

// PRSigstoreSignedWithSignedIdentity specifies a value for the "signedIdentity" field when calling NewPRSigstoreSigned.
func PRSigstoreSignedWithSignedIdentity(signedIdentity PolicyReferenceMatch) PRSigstoreSignedOption {
return func(pr *prSigstoreSigned) error {
if pr.SignedIdentity != nil {
return errors.New(`"signedIdentity" already specified`)
}
pr.SignedIdentity = signedIdentity
return nil
}
}

// newPRSigstoreSigned is NewPRSigstoreSigned, except it returns the private type.
func newPRSigstoreSigned(options ...PRSigstoreSignedOption) (*prSigstoreSigned, error) {
res := prSigstoreSigned{
prCommon: prCommon{Type: prTypeSigstoreSigned},
}
for _, o := range options {
if err := o(&res); err != nil {
return nil, err
}
}
if res.KeyPath != "" && res.KeyData != nil {
return nil, InvalidPolicyFormatError("keyType and keyData cannot be used simultaneously")
}
if keyPath == "" && keyData == nil {
return nil, InvalidPolicyFormatError("neither keyType nor keyData specified")
if res.KeyPath == "" && res.KeyData == nil {
return nil, InvalidPolicyFormatError("At least one of keyPath and keyData must be specified")
}
if signedIdentity == nil {
if res.SignedIdentity == nil {
return nil, InvalidPolicyFormatError("signedIdentity not specified")
}
return &prSigstoreSigned{
prCommon: prCommon{Type: prTypeSigstoreSigned},
KeyPath: keyPath,
KeyData: keyData,
SignedIdentity: signedIdentity,
}, nil

return &res, nil
}

// newPRSigstoreSignedKeyPath is NewPRSigstoreSignedKeyPath, except it returns the private type.
func newPRSigstoreSignedKeyPath(keyPath string, signedIdentity PolicyReferenceMatch) (*prSigstoreSigned, error) {
return newPRSigstoreSigned(keyPath, nil, signedIdentity)
// NewPRSigstoreSigned returns a new "sigstoreSigned" PolicyRequirement based on options.
func NewPRSigstoreSigned(options ...PRSigstoreSignedOption) (PolicyRequirement, error) {
return newPRSigstoreSigned(options...)
}

// NewPRSigstoreSignedKeyPath returns a new "sigstoreSigned" PolicyRequirement using a KeyPath
func NewPRSigstoreSignedKeyPath(keyPath string, signedIdentity PolicyReferenceMatch) (PolicyRequirement, error) {
return newPRSigstoreSignedKeyPath(keyPath, signedIdentity)
}

// newPRSigstoreSignedKeyData is NewPRSigstoreSignedKeyData, except it returns the private type.
func newPRSigstoreSignedKeyData(keyData []byte, signedIdentity PolicyReferenceMatch) (*prSigstoreSigned, error) {
return newPRSigstoreSigned("", keyData, signedIdentity)
return NewPRSigstoreSigned(
PRSigstoreSignedWithKeyPath(keyPath),
PRSigstoreSignedWithSignedIdentity(signedIdentity),
)
}

// NewPRSigstoreSignedKeyData returns a new "sigstoreSigned" PolicyRequirement using a KeyData
func NewPRSigstoreSignedKeyData(keyData []byte, signedIdentity PolicyReferenceMatch) (PolicyRequirement, error) {
return newPRSigstoreSignedKeyData(keyData, signedIdentity)
return NewPRSigstoreSigned(
PRSigstoreSignedWithKeyData(keyData),
PRSigstoreSignedWithSignedIdentity(signedIdentity),
)
}

// Compile-time check that prSigstoreSigned implements json.Unmarshaler.
Expand Down Expand Up @@ -87,23 +129,17 @@ func (pr *prSigstoreSigned) UnmarshalJSON(data []byte) error {
tmp.SignedIdentity = si
}

var res *prSigstoreSigned
var err error
switch {
case gotKeyPath && gotKeyData:
return InvalidPolicyFormatError("keyPath and keyData cannot be used simultaneously")
case gotKeyPath && !gotKeyData:
res, err = newPRSigstoreSignedKeyPath(tmp.KeyPath, tmp.SignedIdentity)
case !gotKeyPath && gotKeyData:
res, err = newPRSigstoreSignedKeyData(tmp.KeyData, tmp.SignedIdentity)
case !gotKeyPath && !gotKeyData:
return InvalidPolicyFormatError("At least one of keyPath and keyData must be specified")
default: // Coverage: This should never happen
return fmt.Errorf("Impossible keyPath/keyData presence combination!?")
var opts []PRSigstoreSignedOption
if gotKeyPath {
opts = append(opts, PRSigstoreSignedWithKeyPath(tmp.KeyPath))
}
if gotKeyData {
opts = append(opts, PRSigstoreSignedWithKeyData(tmp.KeyData))
}
opts = append(opts, PRSigstoreSignedWithSignedIdentity(tmp.SignedIdentity))

res, err := newPRSigstoreSigned(opts...)
if err != nil {
// Coverage: This cannot currently happen, creating a prSigstoreSigned only fails
// if signedIdentity is nil, which we replace with a default above.
return err
}
*pr = *res
Expand Down
86 changes: 55 additions & 31 deletions signature/policy_config_sigstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,11 @@ import (
"github.com/stretchr/testify/require"
)

// xNewPRSigstoreSignedKeyPath is like NewPRSigstoreSignedKeyPath, except it must not fail.
func xNewPRSigstoreSignedKeyPath(keyPath string, signedIdentity PolicyReferenceMatch) PolicyRequirement {
pr, err := NewPRSigstoreSignedKeyPath(keyPath, signedIdentity)
// xNewPRSigstoreSigned is like NewPRSigstoreSigned, except it must not fail.
func xNewPRSigstoreSigned(options ...PRSigstoreSignedOption) PolicyRequirement {
pr, err := NewPRSigstoreSigned(options...)
if err != nil {
panic("xNewPRSigstoreSignedKeyPath failed")
}
return pr
}

// xNewPRSigstoreSignedKeyData is like NewPRSigstoreSignedKeyData, except it must not fail.
func xNewPRSigstoreSignedKeyData(keyData []byte, signedIdentity PolicyReferenceMatch) PolicyRequirement {
pr, err := NewPRSigstoreSignedKeyData(keyData, signedIdentity)
if err != nil {
panic("xNewPRSigstoreSignedKeyData failed")
panic("xNewPRSigstoreSigned failed")
}
return pr
}
Expand All @@ -32,15 +23,21 @@ func TestNewPRSigstoreSigned(t *testing.T) {
testIdentity := NewPRMMatchRepoDigestOrExact()

// Success
pr, err := newPRSigstoreSigned(testPath, nil, testIdentity)
pr, err := newPRSigstoreSigned(
PRSigstoreSignedWithKeyPath(testPath),
PRSigstoreSignedWithSignedIdentity(testIdentity),
)
require.NoError(t, err)
assert.Equal(t, &prSigstoreSigned{
prCommon: prCommon{prTypeSigstoreSigned},
KeyPath: testPath,
KeyData: nil,
SignedIdentity: testIdentity,
}, pr)
pr, err = newPRSigstoreSigned("", testData, testIdentity)
pr, err = newPRSigstoreSigned(
PRSigstoreSignedWithKeyData(testData),
PRSigstoreSignedWithSignedIdentity(testIdentity),
)
require.NoError(t, err)
assert.Equal(t, &prSigstoreSigned{
prCommon: prCommon{prTypeSigstoreSigned},
Expand All @@ -49,36 +46,63 @@ func TestNewPRSigstoreSigned(t *testing.T) {
SignedIdentity: testIdentity,
}, pr)

// Both keyPath and keyData specified
_, err = newPRSigstoreSigned(testPath, testData, testIdentity)
assert.Error(t, err)
// Neither keyPath nor keyData specified
_, err = newPRSigstoreSigned("", nil, testIdentity)
assert.Error(t, err)

// Invalid signedIdentity
_, err = newPRSigstoreSigned(testPath, nil, nil)
assert.Error(t, err)
for _, c := range [][]PRSigstoreSignedOption{
{ // Both keyPath and keyData specified
PRSigstoreSignedWithKeyPath(testPath),
PRSigstoreSignedWithKeyData(testData),
PRSigstoreSignedWithSignedIdentity(testIdentity),
},
{}, // Neither keyPath nor keyData specified
{ // Duplicate keyPath
PRSigstoreSignedWithKeyPath(testPath),
PRSigstoreSignedWithKeyPath(testPath + "1"),
PRSigstoreSignedWithSignedIdentity(testIdentity),
},
{ // Duplicate keyData
PRSigstoreSignedWithKeyData(testData),
PRSigstoreSignedWithKeyData([]byte("def")),
PRSigstoreSignedWithSignedIdentity(testIdentity),
},
{ // Missing signedIdentity
PRSigstoreSignedWithKeyPath(testPath),
},
{ // Duplicate signedIdentity}
PRSigstoreSignedWithKeyPath(testPath),
PRSigstoreSignedWithSignedIdentity(testIdentity),
PRSigstoreSignedWithSignedIdentity(newPRMMatchRepository()),
},
} {
_, err = newPRSigstoreSigned(c...)
assert.Error(t, err)
}
}

func TestNewPRSigstoreSignedKeyPath(t *testing.T) {
const testPath = "/foo/bar"
_pr, err := NewPRSigstoreSignedKeyPath(testPath, NewPRMMatchRepoDigestOrExact())
signedIdentity := NewPRMMatchRepoDigestOrExact()
_pr, err := NewPRSigstoreSignedKeyPath(testPath, signedIdentity)
require.NoError(t, err)
pr, ok := _pr.(*prSigstoreSigned)
require.True(t, ok)
assert.Equal(t, testPath, pr.KeyPath)
// Failure cases tested in TestNewPRSigstoreSigned.
assert.Equal(t, &prSigstoreSigned{
prCommon: prCommon{Type: prTypeSigstoreSigned},
KeyPath: testPath,
SignedIdentity: NewPRMMatchRepoDigestOrExact(),
}, pr)
}

func TestNewPRSigstoreSignedKeyData(t *testing.T) {
testData := []byte("abc")
_pr, err := NewPRSigstoreSignedKeyData(testData, NewPRMMatchRepoDigestOrExact())
signedIdentity := NewPRMMatchRepoDigestOrExact()
_pr, err := NewPRSigstoreSignedKeyData(testData, signedIdentity)
require.NoError(t, err)
pr, ok := _pr.(*prSigstoreSigned)
require.True(t, ok)
assert.Equal(t, testData, pr.KeyData)
// Failure cases tested in TestNewPRSigstoreSigned.
assert.Equal(t, &prSigstoreSigned{
prCommon: prCommon{Type: prTypeSigstoreSigned},
KeyData: testData,
SignedIdentity: NewPRMMatchRepoDigestOrExact(),
}, pr)
}

// Return the result of modifying validJSON with fn and unmarshaling it into *pr
Expand Down
12 changes: 8 additions & 4 deletions signature/policy_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,16 @@ var policyFixtureContents = &Policy{
xNewPRSignedBaseLayer(xNewPRMExactReference("registry.access.redhat.com/rhel7/rhel:latest")),
},
"example.com/sigstore/key-data-example": {
xNewPRSigstoreSignedKeyData([]byte("nonsense"),
NewPRMMatchRepoDigestOrExact()),
xNewPRSigstoreSigned(
PRSigstoreSignedWithKeyData([]byte("nonsense")),
PRSigstoreSignedWithSignedIdentity(NewPRMMatchRepoDigestOrExact()),
),
},
"example.com/sigstore/key-path-example": {
xNewPRSigstoreSignedKeyPath("/keys/public-key",
NewPRMMatchRepository()),
xNewPRSigstoreSigned(
PRSigstoreSignedWithKeyPath("/keys/public-key"),
PRSigstoreSignedWithSignedIdentity(NewPRMMatchRepository()),
),
},
},
},
Expand Down
Loading

0 comments on commit 5158076

Please sign in to comment.