Skip to content

Commit

Permalink
feat: update notation cert list command output (#798)
Browse files Browse the repository at this point in the history
This PR updates the output format of `notation cert list` command. Resolves #609 and #772. Example output:
```
STORE TYPE         STORE NAME         CERTIFICATE   
ca                 myStore            myCert.pem    
```

Signed-off-by: Patrick Zheng <[email protected]>
  • Loading branch information
Two-Hearts authored Oct 11, 2023
1 parent 93e6853 commit f205e6b
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 41 deletions.
76 changes: 46 additions & 30 deletions cmd/notation/cert/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ package cert
import (
"context"
"fmt"
"os"

"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/log"
notationgoTruststore "github.com/notaryproject/notation-go/verifier/truststore"
"github.com/notaryproject/notation/cmd/notation/internal/truststore"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/internal/ioutil"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -75,58 +77,72 @@ func listCerts(ctx context.Context, opts *certListOpts) error {
// List all certificates under truststore/x509, display empty if there's
// no certificate yet
if namedStore == "" && storeType == "" {
path, err := configFS.SysPath(dir.TrustStoreDir, "x509")
if err := truststore.CheckNonErrNotExistError(err); err != nil {
return err
}
if err := truststore.CheckNonErrNotExistError(truststore.ListCerts(path, 2)); err != nil {
logger.Debugln("Failed to complete list at path:", path)
return fmt.Errorf("failed to list all certificates stored in the trust store, with error: %s", err.Error())
var certPaths []string
for _, t := range notationgoTruststore.Types {
path, err := configFS.SysPath(dir.TrustStoreDir, "x509", string(t))
if err := truststore.CheckNonErrNotExistError(err); err != nil {
return err
}
certs, err := truststore.ListCerts(path, 1)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
logger.Debugln("Failed to complete list at path:", path)
return fmt.Errorf("failed to list all certificates stored in the trust store, with error: %s", err.Error())
}
certPaths = append(certPaths, certs...)
}

return nil
return ioutil.PrintCertMap(os.Stdout, certPaths)
}

// List all certificates under truststore/x509/storeType/namedStore,
// display empty if there's no such certificate
// display empty if store type is invalid or there's no certificate yet
if namedStore != "" && storeType != "" {
if !truststore.IsValidStoreType(storeType) {
return nil
}
path, err := configFS.SysPath(dir.TrustStoreDir, "x509", storeType, namedStore)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
return err
}
if err := truststore.CheckNonErrNotExistError(truststore.ListCerts(path, 0)); err != nil {
certPaths, err := truststore.ListCerts(path, 0)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
logger.Debugln("Failed to complete list at path:", path)
return fmt.Errorf("failed to list all certificates stored in the named store %s of type %s, with error: %s", namedStore, storeType, err.Error())
}

return nil
return ioutil.PrintCertMap(os.Stdout, certPaths)
}

// List all certificates under x509/storeType, display empty if
// there's no certificate yet
// List all certificates under x509/storeType, display empty if store type
// is invalid or there's no certificate yet
if storeType != "" {
if !truststore.IsValidStoreType(storeType) {
return nil
}
path, err := configFS.SysPath(dir.TrustStoreDir, "x509", storeType)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
return err
}
if err := truststore.CheckNonErrNotExistError(truststore.ListCerts(path, 1)); err != nil {
certPaths, err := truststore.ListCerts(path, 1)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
logger.Debugln("Failed to complete list at path:", path)
return fmt.Errorf("failed to list all certificates stored of type %s, with error: %s", storeType, err.Error())
}
} else {
// List all certificates under named store namedStore, display empty if
// there's no such certificate
for _, t := range notationgoTruststore.Types {
path, err := configFS.SysPath(dir.TrustStoreDir, "x509", string(t), namedStore)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
return err
}
if err := truststore.CheckNonErrNotExistError(truststore.ListCerts(path, 0)); err != nil {
logger.Debugln("Failed to complete list at path:", path)
return fmt.Errorf("failed to list all certificates stored in the named store %s, with error: %s", namedStore, err.Error())
}
}
return ioutil.PrintCertMap(os.Stdout, certPaths)
}

return nil
// List all certificates under named store namedStore, display empty if
// there's no certificate yet
var certPaths []string
for _, t := range notationgoTruststore.Types {
path, err := configFS.SysPath(dir.TrustStoreDir, "x509", string(t), namedStore)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
return err
}
certs, err := truststore.ListCerts(path, 0)
if err := truststore.CheckNonErrNotExistError(err); err != nil {
logger.Debugln("Failed to complete list at path:", path)
return fmt.Errorf("failed to list all certificates stored in the named store %s, with error: %s", namedStore, err.Error())
}
certPaths = append(certPaths, certs...)
}
return ioutil.PrintCertMap(os.Stdout, certPaths)
}
16 changes: 10 additions & 6 deletions cmd/notation/internal/truststore/truststore.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ func AddCert(path, storeType, namedStore string, display bool) error {
return nil
}

// ListCerts walks through root and lists all x509 certificates in it,
// ListCerts walks through root and returns all x509 certificates in it,
// sub-dirs are ignored.
func ListCerts(root string, depth int) error {
func ListCerts(root string, depth int) ([]string, error) {
maxDepth := strings.Count(root, string(os.PathSeparator)) + depth

return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
var certPaths []string
if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
Expand All @@ -107,11 +107,15 @@ func ListCerts(root string, depth int) error {
return err
}
if len(certs) != 0 {
fmt.Println(path)
certPaths = append(certPaths, path)
}
}
return nil
})
}); err != nil {
return nil, err
}

return certPaths, nil
}

// ShowCerts writes out details of certificates
Expand Down
22 changes: 22 additions & 0 deletions internal/ioutil/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"encoding/json"
"fmt"
"io"
"path/filepath"
"text/tabwriter"

"github.com/notaryproject/notation-go/config"
Expand All @@ -26,6 +27,7 @@ func newTabWriter(w io.Writer) *tabwriter.Writer {
return tabwriter.NewWriter(w, 0, 0, 3, ' ', 0)
}

// PrintKeyMap prints out key information given array of KeySuite
func PrintKeyMap(w io.Writer, target *string, v []config.KeySuite) error {
tw := newTabWriter(w)
fmt.Fprintln(tw, "NAME\tKEY PATH\tCERTIFICATE PATH\tID\tPLUGIN NAME\t")
Expand All @@ -47,6 +49,7 @@ func PrintKeyMap(w io.Writer, target *string, v []config.KeySuite) error {
return tw.Flush()
}

// PrintMetadataMap prints out metadata given the metatdata map
func PrintMetadataMap(w io.Writer, metadata map[string]string) error {
tw := newTabWriter(w)
fmt.Fprintln(tw, "\nKEY\tVALUE\t")
Expand All @@ -58,6 +61,25 @@ func PrintMetadataMap(w io.Writer, metadata map[string]string) error {
return tw.Flush()
}

// PrintCertMap lists certificate files in the trust store given array of cert
// paths
func PrintCertMap(w io.Writer, certPaths []string) error {
if len(certPaths) == 0 {
return nil
}
tw := newTabWriter(w)
fmt.Fprintln(tw, "STORE TYPE\tSTORE NAME\tCERTIFICATE\t")
for _, cert := range certPaths {
fileName := filepath.Base(cert)
dir := filepath.Dir(cert)
namedStore := filepath.Base(dir)
dir = filepath.Dir(dir)
storeType := filepath.Base(dir)
fmt.Fprintf(tw, "%s\t%s\t%s\t\n", storeType, namedStore, fileName)
}
return tw.Flush()
}

// PrintObjectAsJSON takes an interface and prints it as an indented JSON string
func PrintObjectAsJSON(i interface{}) error {
jsonBytes, err := json.MarshalIndent(i, "", " ")
Expand Down
16 changes: 12 additions & 4 deletions specs/commandline/certificate.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,31 +151,39 @@ Upon successful adding, the certificate files are added into directory`{NOTATION
notation certificate list
```

Upon successful listing, all the certificate files in the trust store are printed out in a format of absolute filepath. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.
Upon successful listing, all the certificate files in the trust store are printed out with information of store type, store name and certificate file name. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.

An example of the output:
```
STORE TYPE STORE NAME CERTIFICATE
ca myStore1 cert1.pem
ca myStore2 cert2.crt
signingAuthority myStore1 cert3.crt
signingAuthority myStore2 cert4.pem
```
### List all certificate files of a certain named store

```bash
notation cert list --store <name>
```

Upon successful listing, all the certificate files in the trust store named `<name>` are printed out in a format of absolute filepath. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.
Upon successful listing, all the certificate files in the trust store named `<name>` are printed out with information of store type, store name and certificate file name. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.

### List all certificate files of a certain type of store

```bash
notation cert list --type <type>
```

Upon successful listing, all the certificate files in the trust store of type `<type>` are printed out in a format of absolute filepath. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.
Upon successful listing, all the certificate files in the trust store of type `<type>` are printed out with information of store type, store name and certificate file name. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.

### List all certificate files of a certain named store of a certain type

```bash
notation cert list --type <type> --store <name>
```

Upon successful listing, all the certificate files in the trust store named `<name>` of type `<type>` are printed out in a format of absolute filepath. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.
Upon successful listing, all the certificate files in the trust store named `<name>` of type `<type>` are printed out with information of store type, store name and certificate file name. If the listing fails, an error message is printed out with specific reasons. Nothing is printed out if the trust store is empty.

### Show details of a certain certificate file

Expand Down
6 changes: 5 additions & 1 deletion test/e2e/suite/scenario/quickstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ var _ = Describe("notation quickstart E2E test", Ordered, func() {
)

notation.Exec("cert", "ls").
MatchKeyWords("notation/truststore/x509/ca/wabbit-networks.io/wabbit-networks.io.crt")
MatchKeyWords(
"ca",
"wabbit-networks.io",
"wabbit-networks.io.crt",
)
})

It("sign the container image with jws format (by default)", func() {
Expand Down

0 comments on commit f205e6b

Please sign in to comment.