2019-09-18 22:00:07 +03:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
2019-11-10 16:46:55 +03:00
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/mattn/go-isatty"
|
2019-10-12 12:10:44 +03:00
|
|
|
"github.com/pkg/errors"
|
2019-09-18 22:00:07 +03:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
2019-11-10 16:46:55 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge"
|
2019-09-18 22:00:07 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge/core"
|
2019-12-08 23:15:06 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge/core/auth"
|
2020-02-05 00:05:34 +03:00
|
|
|
"github.com/MichaelMure/git-bug/cache"
|
2019-09-18 22:00:07 +03:00
|
|
|
)
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
type bridgeAuthAddTokenOptions struct {
|
|
|
|
target string
|
|
|
|
login string
|
|
|
|
user string
|
|
|
|
}
|
|
|
|
|
|
|
|
func newBridgeAuthAddTokenCommand() *cobra.Command {
|
|
|
|
env := newEnv()
|
|
|
|
options := bridgeAuthAddTokenOptions{}
|
|
|
|
|
|
|
|
cmd := &cobra.Command{
|
2020-07-28 21:24:24 +03:00
|
|
|
Use: "add-token [TOKEN]",
|
2020-06-28 20:09:32 +03:00
|
|
|
Short: "Store a new token",
|
|
|
|
PreRunE: loadBackendEnsureUser(env),
|
|
|
|
PostRunE: closeBackend(env),
|
2020-06-28 19:26:29 +03:00
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
return runBridgeAuthAddToken(env, options, args)
|
|
|
|
},
|
|
|
|
Args: cobra.MaximumNArgs(1),
|
|
|
|
}
|
|
|
|
|
|
|
|
flags := cmd.Flags()
|
|
|
|
flags.SortFlags = false
|
|
|
|
|
|
|
|
flags.StringVarP(&options.target, "target", "t", "",
|
|
|
|
fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ",")))
|
|
|
|
flags.StringVarP(&options.login,
|
|
|
|
"login", "l", "", "The login in the remote bug-tracker")
|
|
|
|
flags.StringVarP(&options.user,
|
|
|
|
"user", "u", "", "The user to add the token to. Default is the current user")
|
2019-09-18 22:00:07 +03:00
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func runBridgeAuthAddToken(env *Env, opts bridgeAuthAddTokenOptions, args []string) error {
|
2020-02-29 17:04:48 +03:00
|
|
|
// Note: as bridgeAuthAddTokenLogin is not checked against the remote bug-tracker,
|
|
|
|
// it's possible to register a credential with an incorrect login (including bad case).
|
|
|
|
// The consequence is that it will not get picked later by the bridge. I find that
|
|
|
|
// checking it would require a cumbersome UX (need to provide a base URL for some bridges, ...)
|
|
|
|
// so it's probably not worth it, unless we refactor that entirely.
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
if opts.target == "" {
|
2019-12-08 23:15:06 +03:00
|
|
|
return fmt.Errorf("flag --target is required")
|
2019-11-10 16:46:55 +03:00
|
|
|
}
|
2020-06-28 19:26:29 +03:00
|
|
|
if opts.login == "" {
|
2020-02-05 00:05:34 +03:00
|
|
|
return fmt.Errorf("flag --login is required")
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
if !core.TargetExist(opts.target) {
|
2019-11-10 16:46:55 +03:00
|
|
|
return fmt.Errorf("unknown target")
|
|
|
|
}
|
|
|
|
|
2020-02-05 00:05:34 +03:00
|
|
|
var value string
|
|
|
|
|
2019-11-10 16:46:55 +03:00
|
|
|
if len(args) == 1 {
|
|
|
|
value = args[0]
|
|
|
|
} else {
|
|
|
|
// Read from Stdin
|
|
|
|
if isatty.IsTerminal(os.Stdin.Fd()) {
|
2020-06-28 19:26:29 +03:00
|
|
|
env.err.Println("Enter the token:")
|
2019-11-10 16:46:55 +03:00
|
|
|
}
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
raw, err := reader.ReadString('\n')
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("reading from stdin: %v", err)
|
|
|
|
}
|
|
|
|
value = strings.TrimSuffix(raw, "\n")
|
|
|
|
}
|
|
|
|
|
2020-02-05 00:05:34 +03:00
|
|
|
var user *cache.IdentityCache
|
2020-06-28 20:09:32 +03:00
|
|
|
var err error
|
2020-02-05 00:05:34 +03:00
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
if opts.user == "" {
|
2020-06-28 20:09:32 +03:00
|
|
|
user, err = env.backend.GetUserIdentity()
|
2020-02-05 00:05:34 +03:00
|
|
|
} else {
|
2020-06-28 20:09:32 +03:00
|
|
|
user, err = env.backend.ResolveIdentityPrefix(opts.user)
|
2020-02-05 00:05:34 +03:00
|
|
|
}
|
2019-12-08 23:15:06 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
metaKey, _ := bridge.LoginMetaKey(opts.target)
|
2020-02-05 00:05:34 +03:00
|
|
|
login, ok := user.ImmutableMetadata()[metaKey]
|
|
|
|
|
|
|
|
switch {
|
2020-06-28 19:26:29 +03:00
|
|
|
case ok && login == opts.login:
|
2020-02-05 00:05:34 +03:00
|
|
|
// nothing to do
|
2020-06-28 19:26:29 +03:00
|
|
|
case ok && login != opts.login:
|
|
|
|
return fmt.Errorf("this user is already tagged with a different %s login", opts.target)
|
2020-02-05 00:05:34 +03:00
|
|
|
default:
|
2020-06-28 19:26:29 +03:00
|
|
|
user.SetMetadata(metaKey, opts.login)
|
2020-02-05 00:05:34 +03:00
|
|
|
err = user.Commit()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
token := auth.NewToken(opts.target, value)
|
|
|
|
token.SetMetadata(auth.MetaKeyLogin, opts.login)
|
2020-02-05 00:05:34 +03:00
|
|
|
|
2019-11-08 16:49:24 +03:00
|
|
|
if err := token.Validate(); err != nil {
|
2019-10-12 12:10:44 +03:00
|
|
|
return errors.Wrap(err, "invalid token")
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
err = auth.Store(env.repo, token)
|
2019-11-10 16:46:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:26:29 +03:00
|
|
|
env.out.Printf("token %s added\n", token.ID())
|
2019-11-10 16:46:55 +03:00
|
|
|
return nil
|
2019-09-18 22:00:07 +03:00
|
|
|
}
|