bridge_auth_addtoken.go

  1package commands
  2
  3import (
  4	"bufio"
  5	"fmt"
  6	"os"
  7	"strings"
  8
  9	"github.com/mattn/go-isatty"
 10	"github.com/pkg/errors"
 11	"github.com/spf13/cobra"
 12
 13	"github.com/MichaelMure/git-bug/bridge"
 14	"github.com/MichaelMure/git-bug/bridge/core"
 15	"github.com/MichaelMure/git-bug/bridge/core/auth"
 16	"github.com/MichaelMure/git-bug/cache"
 17	"github.com/MichaelMure/git-bug/util/interrupt"
 18)
 19
 20var (
 21	bridgeAuthAddTokenTarget string
 22	bridgeAuthAddTokenLogin  string
 23	bridgeAuthAddTokenUser   string
 24)
 25
 26func runBridgeTokenAdd(cmd *cobra.Command, args []string) error {
 27	// Note: as bridgeAuthAddTokenLogin is not checked against the remote bug-tracker,
 28	// it's possible to register a credential with an incorrect login (including bad case).
 29	// The consequence is that it will not get picked later by the bridge. I find that
 30	// checking it would require a cumbersome UX (need to provide a base URL for some bridges, ...)
 31	// so it's probably not worth it, unless we refactor that entirely.
 32
 33	if bridgeAuthAddTokenTarget == "" {
 34		return fmt.Errorf("flag --target is required")
 35	}
 36	if bridgeAuthAddTokenLogin == "" {
 37		return fmt.Errorf("flag --login is required")
 38	}
 39
 40	backend, err := cache.NewRepoCache(repo)
 41	if err != nil {
 42		return err
 43	}
 44	defer backend.Close()
 45	interrupt.RegisterCleaner(backend.Close)
 46
 47	if !core.TargetExist(bridgeAuthAddTokenTarget) {
 48		return fmt.Errorf("unknown target")
 49	}
 50
 51	var value string
 52
 53	if len(args) == 1 {
 54		value = args[0]
 55	} else {
 56		// Read from Stdin
 57		if isatty.IsTerminal(os.Stdin.Fd()) {
 58			fmt.Println("Enter the token:")
 59		}
 60		reader := bufio.NewReader(os.Stdin)
 61		raw, err := reader.ReadString('\n')
 62		if err != nil {
 63			return fmt.Errorf("reading from stdin: %v", err)
 64		}
 65		value = strings.TrimSuffix(raw, "\n")
 66	}
 67
 68	var user *cache.IdentityCache
 69
 70	if bridgeAuthAddTokenUser == "" {
 71		user, err = backend.GetUserIdentity()
 72	} else {
 73		user, err = backend.ResolveIdentityPrefix(bridgeAuthAddTokenUser)
 74	}
 75	if err != nil {
 76		return err
 77	}
 78
 79	metaKey, _ := bridge.LoginMetaKey(bridgeAuthAddTokenTarget)
 80	login, ok := user.ImmutableMetadata()[metaKey]
 81
 82	switch {
 83	case ok && login == bridgeAuthAddTokenLogin:
 84		// nothing to do
 85	case ok && login != bridgeAuthAddTokenLogin:
 86		return fmt.Errorf("this user is already tagged with a different %s login", bridgeAuthAddTokenTarget)
 87	default:
 88		user.SetMetadata(metaKey, bridgeAuthAddTokenLogin)
 89		err = user.Commit()
 90		if err != nil {
 91			return err
 92		}
 93	}
 94
 95	token := auth.NewToken(bridgeAuthAddTokenTarget, value)
 96	token.SetMetadata(auth.MetaKeyLogin, bridgeAuthAddTokenLogin)
 97
 98	if err := token.Validate(); err != nil {
 99		return errors.Wrap(err, "invalid token")
100	}
101
102	err = auth.Store(repo, token)
103	if err != nil {
104		return err
105	}
106
107	fmt.Printf("token %s added\n", token.ID())
108	return nil
109}
110
111var bridgeAuthAddTokenCmd = &cobra.Command{
112	Use:     "add-token [<token>]",
113	Short:   "Store a new token",
114	PreRunE: loadRepoEnsureUser,
115	RunE:    runBridgeTokenAdd,
116	Args:    cobra.MaximumNArgs(1),
117}
118
119func init() {
120	bridgeAuthCmd.AddCommand(bridgeAuthAddTokenCmd)
121	bridgeAuthAddTokenCmd.Flags().StringVarP(&bridgeAuthAddTokenTarget, "target", "t", "",
122		fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ",")))
123	bridgeAuthAddTokenCmd.Flags().StringVarP(&bridgeAuthAddTokenLogin,
124		"login", "l", "", "The login in the remote bug-tracker")
125	bridgeAuthAddTokenCmd.Flags().StringVarP(&bridgeAuthAddTokenUser,
126		"user", "u", "", "The user to add the token to. Default is the current user")
127	bridgeAuthAddTokenCmd.Flags().SortFlags = false
128}