github.com/thajeztah/cli@v0.0.0-20240223162942-dc6bfac81a8b/cli/command/trust/revoke.go (about)

     1  package trust
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/docker/cli/cli"
     9  	"github.com/docker/cli/cli/command"
    10  	"github.com/docker/cli/cli/command/image"
    11  	"github.com/docker/cli/cli/trust"
    12  	"github.com/pkg/errors"
    13  	"github.com/spf13/cobra"
    14  	"github.com/theupdateframework/notary/client"
    15  	"github.com/theupdateframework/notary/tuf/data"
    16  )
    17  
    18  type revokeOptions struct {
    19  	forceYes bool
    20  }
    21  
    22  func newRevokeCommand(dockerCLI command.Cli) *cobra.Command {
    23  	options := revokeOptions{}
    24  	cmd := &cobra.Command{
    25  		Use:   "revoke [OPTIONS] IMAGE[:TAG]",
    26  		Short: "Remove trust for an image",
    27  		Args:  cli.ExactArgs(1),
    28  		RunE: func(cmd *cobra.Command, args []string) error {
    29  			return revokeTrust(cmd.Context(), dockerCLI, args[0], options)
    30  		},
    31  	}
    32  	flags := cmd.Flags()
    33  	flags.BoolVarP(&options.forceYes, "yes", "y", false, "Do not prompt for confirmation")
    34  	return cmd
    35  }
    36  
    37  func revokeTrust(ctx context.Context, dockerCLI command.Cli, remote string, options revokeOptions) error {
    38  	imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(dockerCLI), remote)
    39  	if err != nil {
    40  		return err
    41  	}
    42  	tag := imgRefAndAuth.Tag()
    43  	if imgRefAndAuth.Tag() == "" && imgRefAndAuth.Digest() != "" {
    44  		return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
    45  	}
    46  	if imgRefAndAuth.Tag() == "" && !options.forceYes {
    47  		deleteRemote := command.PromptForConfirmation(os.Stdin, dockerCLI.Out(), fmt.Sprintf("Please confirm you would like to delete all signature data for %s?", remote))
    48  		if !deleteRemote {
    49  			fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
    50  			return nil
    51  		}
    52  	}
    53  
    54  	notaryRepo, err := dockerCLI.NotaryClient(imgRefAndAuth, trust.ActionsPushAndPull)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	if err = clearChangeList(notaryRepo); err != nil {
    60  		return err
    61  	}
    62  	defer clearChangeList(notaryRepo)
    63  	if err := revokeSignature(notaryRepo, tag); err != nil {
    64  		return errors.Wrapf(err, "could not remove signature for %s", remote)
    65  	}
    66  	fmt.Fprintf(dockerCLI.Out(), "Successfully deleted signature for %s\n", remote)
    67  	return nil
    68  }
    69  
    70  func revokeSignature(notaryRepo client.Repository, tag string) error {
    71  	if tag != "" {
    72  		// Revoke signature for the specified tag
    73  		if err := revokeSingleSig(notaryRepo, tag); err != nil {
    74  			return err
    75  		}
    76  	} else {
    77  		// revoke all signatures for the image, as no tag was given
    78  		if err := revokeAllSigs(notaryRepo); err != nil {
    79  			return err
    80  		}
    81  	}
    82  
    83  	//  Publish change
    84  	return notaryRepo.Publish()
    85  }
    86  
    87  func revokeSingleSig(notaryRepo client.Repository, tag string) error {
    88  	releasedTargetWithRole, err := notaryRepo.GetTargetByName(tag, trust.ReleasesRole, data.CanonicalTargetsRole)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	releasedTarget := releasedTargetWithRole.Target
    93  	return getSignableRolesForTargetAndRemove(releasedTarget, notaryRepo)
    94  }
    95  
    96  func revokeAllSigs(notaryRepo client.Repository) error {
    97  	releasedTargetWithRoleList, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	if len(releasedTargetWithRoleList) == 0 {
   103  		return fmt.Errorf("no signed tags to remove")
   104  	}
   105  
   106  	// we need all the roles that signed each released target so we can remove from all roles.
   107  	for _, releasedTargetWithRole := range releasedTargetWithRoleList {
   108  		// remove from all roles
   109  		if err := getSignableRolesForTargetAndRemove(releasedTargetWithRole.Target, notaryRepo); err != nil {
   110  			return err
   111  		}
   112  	}
   113  	return nil
   114  }
   115  
   116  // get all the roles that signed the target and removes it from all roles.
   117  func getSignableRolesForTargetAndRemove(releasedTarget client.Target, notaryRepo client.Repository) error {
   118  	signableRoles, err := trust.GetSignableRoles(notaryRepo, &releasedTarget)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	// remove from all roles
   123  	return notaryRepo.RemoveTarget(releasedTarget.Name, signableRoles...)
   124  }