Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add blob policy import and show commands #1126

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cmd/notation/blob/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package blob provides the command for blob trust policy configuration.
// Package blob provides blob sign, verify, inspect, and policy commands.
package blob

import (
"github.com/notaryproject/notation/cmd/notation/blob/policy"
"github.com/spf13/cobra"
)

// Cmd returns the command for blob
// Cmd returns the commands for blob
func Cmd() *cobra.Command {
command := &cobra.Command{
Use: "blob [commnad]",
Short: "Sign, verify and inspect signatures associated with blobs",
Long: "Sign, inspect, and verify signatures and configure trust policies.",
Use: "blob [command]",
Short: "Sign, inspect, verify signatures, and configure trust policies for blob artifacts",
Long: "Sign, inspect, verify signatures, and configure trust policies for blob artifacts.",
}

command.AddCommand(
Expand Down
2 changes: 1 addition & 1 deletion cmd/notation/blob/policy/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package policy provides the import and show command for blob trust policy.
// Package policy provides the import and show commands for blob trust policy.
package policy

import (
Expand Down
13 changes: 8 additions & 5 deletions cmd/notation/blob/policy/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@
var opts importOpts
command := &cobra.Command{
Use: "import [flags] <file_path>",
Short: "Import trust policy configuration from a JSON file",
Short: "Import blob trust policy configuration from a JSON file",
Long: `Import blob trust policy configuration from a JSON file.

Example - Import trust policy configuration from a file:
Example - Import blob trust policy configuration from a file:
notation blob policy import my_policy.json

Example - Import blob trust policy and override existing configuration without prompt:
notation blob policy import my_policy.json --force
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
Expand All @@ -51,7 +54,7 @@
return runImport(opts)
},
}
command.Flags().BoolVar(&opts.force, "force", false, "override the existing trust policy configuration, never prompt")
command.Flags().BoolVar(&opts.force, "force", false, "override the existing blob trust policy configuration without prompt")
return command
}

Expand All @@ -75,25 +78,25 @@
if _, err = trustpolicy.LoadBlobDocument(); err == nil {
confirmed, err := cmdutil.AskForConfirmation(os.Stdin, "The blob trust policy file already exists, do you want to overwrite it?", opts.force)
if err != nil {
return err
}

Check warning on line 82 in cmd/notation/blob/policy/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/notation/blob/policy/import.go#L81-L82

Added lines #L81 - L82 were not covered by tests
if !confirmed {
return nil
}
}
} else {
fmt.Fprintln(os.Stderr, "Warning: existing blob trust policy configuration file will be overwritten")
fmt.Fprintln(os.Stderr, "Warning: existing blob trust policy file will be overwritten")
}

// write
policyPath, err := dir.ConfigFS().SysPath(dir.PathBlobTrustPolicy)
if err != nil {
return fmt.Errorf("failed to obtain path of blob trust policy file: %w", err)
}

Check warning on line 95 in cmd/notation/blob/policy/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/notation/blob/policy/import.go#L94-L95

Added lines #L94 - L95 were not covered by tests
if err = osutil.WriteFile(policyPath, policyJSON); err != nil {
return fmt.Errorf("failed to write blob trust policy file: %w", err)
}

_, err = fmt.Fprintln(os.Stdout, "Blob trust policy configuration imported successfully.")
_, err = fmt.Fprintln(os.Stdout, "Successfully imported blob trust policy file.")
return err
}
8 changes: 4 additions & 4 deletions cmd/notation/blob/policy/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
func showCmd() *cobra.Command {
command := &cobra.Command{
Use: "show [flags]",
Short: "Show trust policy configuration",
Short: "Show blob trust policy configuration",
Long: `Show blob trust policy configuration.

Example - Show current blob trust policy configuration:
Expand Down Expand Up @@ -59,9 +59,9 @@ func runShow() error {
err = doc.Validate()
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
fmt.Fprintf(os.Stderr, "Existing blob trust policy configuration is invalid, you may update or create a new one via `notation blob policy import <path-to-policy.json>`\n")
// not returning to show the invalid policy configuration
fmt.Fprintf(os.Stderr, "Existing blob trust policy file is invalid, you may update or create a new one via `notation blob policy import <path-to-policy.json>`. See https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md#trust-policy-for-blobs for a blob trust policy example.\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main is referenced. Do we have a tagged version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1126 (comment)
We don't have a tagged version for now. Can we merge it now and update the link before next release which is suggested by @Two-Hearts ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use digested link

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added issue #1138 to update it before v1.4

os.Stdout.Write(policyJSON)
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behavior differs with the OCI policy show. Do you want to make them consistent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1126 (comment)
As suggested by @Two-Hearts , OCI policy command related changes have been removed. Can we send another PR for update OCI policy command?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added issue #1138 to update it before v1.4

}

// show policy content
Expand Down
1 change: 0 additions & 1 deletion cmd/notation/policy/cmd.go
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package policy provides the import and show command for OCI trust policy.
package policy

import "github.com/spf13/cobra"
Expand Down
18 changes: 4 additions & 14 deletions cmd/notation/policy/import.go
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ Example - Import trust policy configuration from a file:
},
RunE: func(cmd *cobra.Command, args []string) error {
opts.filePath = args[0]
return runImport(opts)
return runImport(cmd, opts)
},
}
command.Flags().BoolVar(&opts.force, "force", false, "override the existing trust policy configuration, never prompt")
return command
}

func runImport(opts importOpts) error {
func runImport(command *cobra.Command, opts importOpts) error {
// read configuration
policyJSON, err := os.ReadFile(opts.filePath)
if err != nil {
Expand All @@ -75,7 +75,7 @@ func runImport(opts importOpts) error {

// optional confirmation
if !opts.force {
if _, err := trustpolicy.LoadOCIDocument(); err == nil {
if _, err := trustpolicy.LoadDocument(); err == nil {
confirmed, err := cmdutil.AskForConfirmation(os.Stdin, "The trust policy file already exists, do you want to overwrite it?", opts.force)
if err != nil {
return err
Expand All @@ -89,23 +89,13 @@ func runImport(opts importOpts) error {
}

// write
policyPath, err := dir.ConfigFS().SysPath(dir.PathOCITrustPolicy)
policyPath, err := dir.ConfigFS().SysPath(dir.PathTrustPolicy)
if err != nil {
return fmt.Errorf("failed to obtain path of trust policy file: %w", err)
}
if err = osutil.WriteFile(policyPath, policyJSON); err != nil {
return fmt.Errorf("failed to write trust policy file: %w", err)
}

// clear old trust policy
oldPolicyPath, err := dir.ConfigFS().SysPath(dir.PathTrustPolicy)
if err != nil {
return fmt.Errorf("failed to obtain path of trust policy file: %w", err)
}
if err := osutil.RemoveIfExists(oldPolicyPath); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to clear old trust policy %q: %v\n", oldPolicyPath, err)
}

_, err = fmt.Fprintln(os.Stdout, "Trust policy configuration imported successfully.")
return err
}
37 changes: 14 additions & 23 deletions cmd/notation/policy/show.go
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"

Expand All @@ -26,7 +25,11 @@ import (
"github.com/spf13/cobra"
)

type showOpts struct {
}

func showCmd() *cobra.Command {
var opts showOpts
command := &cobra.Command{
Use: "show [flags]",
Short: "Show trust policy configuration",
Expand All @@ -42,14 +45,21 @@ Example - Save current trust policy configuration to a file:
`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return runShow()
return runShow(cmd, opts)
},
}
return command
}

func runShow() error {
policyJSON, err := loadOCITrustPolicy()
func runShow(command *cobra.Command, opts showOpts) error {
// get policy file path
policyPath, err := dir.ConfigFS().SysPath(dir.PathTrustPolicy)
if err != nil {
return fmt.Errorf("failed to obtain path of trust policy file: %w", err)
}

// core process
policyJSON, err := os.ReadFile(policyPath)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("failed to show trust policy as the trust policy file does not exist.\nYou can import one using `notation policy import <path-to-policy.json>`")
Expand All @@ -70,22 +80,3 @@ func runShow() error {
_, err = os.Stdout.Write(policyJSON)
return err
}

// loadOCITrustPolicy loads OCI trust policy from notation configuration directory.
//
// It tries to load OCI trust policy (trustpolicy.oci.json) first, if it does
// not exist, it falls back to old trust policy (trustpolicy.json).
func loadOCITrustPolicy() ([]byte, error) {
f, err := dir.ConfigFS().Open(dir.PathOCITrustPolicy)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, err
}
f, err = dir.ConfigFS().Open(dir.PathTrustPolicy)
if err != nil {
return nil, err
}
}
defer f.Close()
return io.ReadAll(f)
}
12 changes: 0 additions & 12 deletions internal/osutil/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package osutil
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"io/fs"
Expand Down Expand Up @@ -61,17 +60,6 @@ func WriteFileWithPermission(path string, data []byte, perm fs.FileMode, overwri
return file.Close()
}

// RemoveIfExists removes a file if it exists.
func RemoveIfExists(filepath string) error {
if _, err := os.Stat(filepath); err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}
return os.Remove(filepath)
}

// CopyToDir copies the src file to dst. Existing file will be overwritten.
func CopyToDir(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
Expand Down
47 changes: 0 additions & 47 deletions internal/osutil/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ package osutil

import (
"bytes"
"errors"
"io/fs"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -271,48 +269,3 @@ func TestValidateChecksum(t *testing.T) {
t.Fatalf("expected nil err, but got %v", err)
}
}

func TestRemoveIfExists(t *testing.T) {
t.Run("remove existing file", func(t *testing.T) {
tempDir := t.TempDir()
filename := filepath.Join(tempDir, "file.txt")
data := []byte("data")
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}
if err := RemoveIfExists(filename); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filename); !errors.Is(err, fs.ErrNotExist) {
t.Fatal("file should be removed")
}
})

t.Run("remove non-existing file", func(t *testing.T) {
tempDir := t.TempDir()
filename := filepath.Join(tempDir, "file.txt")
if err := RemoveIfExists(filename); err != nil {
t.Fatal(err)
}
})

t.Run("remove file in directory without permission", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
tempDir := t.TempDir()
filename := filepath.Join(tempDir, "file.txt")
data := []byte("data")
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}
if err := os.Chmod(tempDir, 0000); err != nil {
t.Fatal(err)
}
defer os.Chmod(tempDir, 0700)

if err := RemoveIfExists(filename); err == nil {
t.Fatal("expected an error when removing file from restricted directory")
}
})
}
1 change: 0 additions & 1 deletion test/e2e/internal/notation/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
const (
NotationDirName = "notation"
TrustPolicyName = "trustpolicy.json"
OCITrustPolicyName = "trustpolicy.oci.json"
BlobTrustPolicyName = "trustpolicy.blob.json"
TrustStoreDirName = "truststore"
TrustStoreTypeCA = "ca"
Expand Down
10 changes: 5 additions & 5 deletions test/e2e/suite/command/blob/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ var _ = Describe("blob trust policy maintainer", func() {
content, err := os.ReadFile(filepath.Join(NotationE2ETrustPolicyDir, policyName))
Expect(err).NotTo(HaveOccurred())
Host(Opts(AddBlobTrustPolicyOption(policyName)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) {
notation.Exec("blob", "policy", "show").
MatchErrKeyWords("existing blob trust policy configuration is invalid").
notation.ExpectFailure().Exec("blob", "policy", "show").
MatchErrKeyWords("existing blob trust policy file is invalid").
MatchContent(string(content))
})
})
Expand Down Expand Up @@ -190,7 +190,7 @@ var _ = Describe("blob trust policy maintainer", func() {
Host(Opts(AddBlobTrustPolicyOption("invalid_format_trustpolicy.json")), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) {
policyFileName := "skip_trustpolicy.json"
notation.Exec("blob", "policy", "import", filepath.Join(NotationE2ETrustPolicyDir, policyFileName)).MatchKeyWords().
MatchKeyWords("Blob trust policy configuration imported successfully.")
MatchKeyWords("Successfully imported blob trust policy file.")
// validate
content, err := os.ReadFile(filepath.Join(NotationE2ETrustPolicyDir, policyFileName))
Expect(err).NotTo(HaveOccurred())
Expand All @@ -202,7 +202,7 @@ var _ = Describe("blob trust policy maintainer", func() {
Host(opts, func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) {
policyFileName := "skip_trustpolicy.json"
notation.WithInput(strings.NewReader("Y\n")).Exec("blob", "policy", "import", filepath.Join(NotationE2ETrustPolicyDir, policyFileName)).
MatchKeyWords("Blob trust policy configuration imported successfully.")
MatchKeyWords("Successfully imported blob trust policy file.")
// validate
content, err := os.ReadFile(filepath.Join(NotationE2ETrustPolicyDir, policyFileName))
Expect(err).NotTo(HaveOccurred())
Expand All @@ -214,7 +214,7 @@ var _ = Describe("blob trust policy maintainer", func() {
Host(opts, func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) {
policyFileName := "skip_trustpolicy.json"
notation.Exec("blob", "policy", "import", filepath.Join(NotationE2ETrustPolicyDir, policyFileName), "--force").
MatchKeyWords("Blob trust policy configuration imported successfully.")
MatchKeyWords("Successfully imported blob trust policy file.")
// validate
content, err := os.ReadFile(filepath.Join(NotationE2ETrustPolicyDir, policyFileName))
Expect(err).NotTo(HaveOccurred())
Expand Down
Loading
Loading