bridge/core: store token in the global config and replace scopes with create date

amine created

Change summary

bridge/core/token.go         | 168 ++++++++++---------------------------
commands/bridge_token.go     |  29 ++----
commands/bridge_token_add.go |  16 +--
commands/bridge_token_rm.go  |   8 -
4 files changed, 64 insertions(+), 157 deletions(-)

Detailed changes

bridge/core/token.go 🔗

@@ -2,11 +2,14 @@ package core
 
 import (
 	"crypto/sha256"
-	"encoding/json"
 	"fmt"
-	"github.com/MichaelMure/git-bug/entity"
 	"regexp"
+	"strconv"
 	"strings"
+	"time"
+
+	"github.com/MichaelMure/git-bug/entity"
+	"github.com/araddon/dateparse"
 
 	"github.com/MichaelMure/git-bug/repository"
 )
@@ -15,58 +18,37 @@ const (
 	tokenConfigKeyPrefix = "git-bug.token"
 	tokenValueKey        = "value"
 	tokenTargetKey       = "target"
-	tokenScopesKey       = "scopes"
+	tokenCreateTimeKey   = "createtime"
 )
 
 // Token holds an API access token data
 type Token struct {
-	id     entity.Id
-	Value  string
-	Target string
-	Global bool
-	Scopes []string
+	ID         entity.Id
+	Value      string
+	Target     string
+	CreateTime time.Time
 }
 
 // NewToken instantiate a new token
-func NewToken(value, target string, global bool, scopes []string) *Token {
+func NewToken(value, target string) *Token {
 	token := &Token{
-		Value:  value,
-		Target: target,
-		Global: global,
-		Scopes: scopes,
+		Value:      value,
+		Target:     target,
+		CreateTime: time.Now(),
 	}
 
-	token.id = entity.Id(hashToken(token))
+	token.ID = entity.Id(hashToken(token))
 	return token
 }
 
-// Id return full token identifier. It will compute the Id if it's empty
-func (t *Token) Id() string {
-	if t.id == "" {
-		t.id = entity.Id(hashToken(t))
-	}
-
-	return t.id.String()
-}
-
-// HumanId return the truncated token id
-func (t *Token) HumanId() string {
-	return t.id.Human()
-}
-
 func hashToken(token *Token) string {
-	tokenJson, err := json.Marshal(&token)
-	if err != nil {
-		panic(err)
-	}
-
-	sum := sha256.Sum256(tokenJson)
+	sum := sha256.Sum256([]byte(token.Value))
 	return fmt.Sprintf("%x", sum)
 }
 
 // Validate ensure token important fields are valid
 func (t *Token) Validate() error {
-	if t.id == "" {
+	if t.ID == "" {
 		return fmt.Errorf("missing id")
 	}
 	if t.Value == "" {
@@ -75,31 +57,21 @@ func (t *Token) Validate() error {
 	if t.Target == "" {
 		return fmt.Errorf("missing target")
 	}
+	if t.CreateTime.Equal(time.Time{}) {
+		return fmt.Errorf("missing creation time")
+	}
 	if _, ok := bridgeImpl[t.Target]; !ok {
 		return fmt.Errorf("unknown target")
 	}
 	return nil
 }
 
-// Kind return the type of the token as string
-func (t *Token) Kind() string {
-	if t.Global {
-		return "global"
-	}
-
-	return "local"
-}
-
-func loadToken(repo repository.RepoConfig, id string, global bool) (*Token, error) {
+// LoadToken loads a token from repo config
+func LoadToken(repo repository.RepoCommon, id string) (*Token, error) {
 	keyPrefix := fmt.Sprintf("git-bug.token.%s.", id)
 
-	readerFn := repo.ReadConfigs
-	if global {
-		readerFn = repo.ReadGlobalConfigs
-	}
-
 	// read token config pairs
-	configs, err := readerFn(keyPrefix)
+	configs, err := repo.GlobalConfig().ReadAll(keyPrefix)
 	if err != nil {
 		return nil, err
 	}
@@ -111,9 +83,9 @@ func loadToken(repo repository.RepoConfig, id string, global bool) (*Token, erro
 		configs[newKey] = value
 	}
 
-	var ok bool
-	token := &Token{id: entity.Id(id), Global: global}
+	token := &Token{ID: entity.Id(id)}
 
+	var ok bool
 	token.Value, ok = configs[tokenValueKey]
 	if !ok {
 		return nil, fmt.Errorf("empty token value")
@@ -124,32 +96,22 @@ func loadToken(repo repository.RepoConfig, id string, global bool) (*Token, erro
 		return nil, fmt.Errorf("empty token key")
 	}
 
-	scopesString, ok := configs[tokenScopesKey]
+	createTime, ok := configs[tokenCreateTimeKey]
 	if !ok {
-		return nil, fmt.Errorf("missing scopes config")
+		return nil, fmt.Errorf("missing createtime key")
 	}
 
-	token.Scopes = strings.Split(scopesString, ",")
+	token.CreateTime, err = dateparse.ParseLocal(createTime)
+	if err != nil {
+		return nil, err
+	}
 	return token, nil
 }
 
-// GetToken loads a token from repo config
-func GetToken(repo repository.RepoConfig, id string) (*Token, error) {
-	return loadToken(repo, id, false)
-}
-
-// GetGlobalToken loads a token from the global config
-func GetGlobalToken(repo repository.RepoConfig, id string) (*Token, error) {
-	return loadToken(repo, id, true)
-}
-
-func listTokens(repo repository.RepoConfig, global bool) ([]string, error) {
-	readerFn := repo.ReadConfigs
-	if global {
-		readerFn = repo.ReadGlobalConfigs
-	}
-
-	configs, err := readerFn(tokenConfigKeyPrefix + ".")
+// ListTokens return a map representing the stored tokens in the repo config and global config
+// along with their type (global: true, local:false)
+func ListTokens(repo repository.RepoCommon) ([]string, error) {
+	configs, err := repo.GlobalConfig().ReadAll(tokenConfigKeyPrefix + ".")
 	if err != nil {
 		return nil, err
 	}
@@ -181,66 +143,26 @@ func listTokens(repo repository.RepoConfig, global bool) ([]string, error) {
 	return result, nil
 }
 
-// ListTokens return a map representing the stored tokens in the repo config and global config
-// along with their type (global: true, local:false)
-func ListTokens(repo repository.RepoConfig) (map[string]bool, error) {
-	localTokens, err := listTokens(repo, false)
-	if err != nil {
-		return nil, err
-	}
-
-	globalTokens, err := listTokens(repo, true)
-	if err != nil {
-		return nil, err
-	}
-
-	tokens := map[string]bool{}
-	for _, token := range localTokens {
-		tokens[token] = false
-	}
-
-	for _, token := range globalTokens {
-		tokens[token] = true
-	}
-
-	return tokens, nil
-}
-
-func storeToken(repo repository.RepoConfig, token *Token) error {
-	storeFn := repo.StoreConfig
-	if token.Global {
-		storeFn = repo.StoreGlobalConfig
-	}
-
-	storeValueKey := fmt.Sprintf("git-bug.token.%s.%s", token.Id(), tokenValueKey)
-	err := storeFn(storeValueKey, token.Value)
+// StoreToken stores a token in the repo config
+func StoreToken(repo repository.RepoCommon, token *Token) error {
+	storeValueKey := fmt.Sprintf("git-bug.token.%s.%s", token.ID.String(), tokenValueKey)
+	err := repo.GlobalConfig().StoreString(storeValueKey, token.Value)
 	if err != nil {
 		return err
 	}
 
-	storeTargetKey := fmt.Sprintf("git-bug.token.%s.%s", token.Id(), tokenTargetKey)
-	err = storeFn(storeTargetKey, token.Target)
+	storeTargetKey := fmt.Sprintf("git-bug.token.%s.%s", token.ID.String(), tokenTargetKey)
+	err = repo.GlobalConfig().StoreString(storeTargetKey, token.Target)
 	if err != nil {
 		return err
 	}
 
-	storeScopesKey := fmt.Sprintf("git-bug.token.%s.%s", token.Id(), tokenScopesKey)
-	return storeFn(storeScopesKey, strings.Join(token.Scopes, ","))
-}
-
-// StoreToken stores a token in the repo config
-func StoreToken(repo repository.RepoConfig, token *Token) error {
-	return storeToken(repo, token)
+	createTimeKey := fmt.Sprintf("git-bug.token.%s.%s", token.ID.String(), tokenCreateTimeKey)
+	return repo.GlobalConfig().StoreString(createTimeKey, strconv.Itoa(int(token.CreateTime.Unix())))
 }
 
 // RemoveToken removes a token from the repo config
-func RemoveToken(repo repository.RepoConfig, id string) error {
-	keyPrefix := fmt.Sprintf("git-bug.token.%s", id)
-	return repo.RmConfigs(keyPrefix)
-}
-
-// RemoveGlobalToken removes a token from the repo config
-func RemoveGlobalToken(repo repository.RepoConfig, id string) error {
+func RemoveToken(repo repository.RepoCommon, id string) error {
 	keyPrefix := fmt.Sprintf("git-bug.token.%s", id)
-	return repo.RmGlobalConfigs(keyPrefix)
+	return repo.GlobalConfig().RemoveAll(keyPrefix)
 }

commands/bridge_token.go 🔗

@@ -2,7 +2,7 @@ package commands
 
 import (
 	"fmt"
-	"strings"
+	"time"
 
 	"github.com/spf13/cobra"
 
@@ -10,7 +10,7 @@ import (
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/util/colors"
 	"github.com/MichaelMure/git-bug/util/interrupt"
-	"github.com/MichaelMure/git-bug/util/text"
+	text "github.com/MichaelMure/go-term-text"
 )
 
 var (
@@ -31,14 +31,8 @@ func runTokenBridge(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	for token, global := range tokens {
-		// TODO: filter tokens using flags
-		getTokenFn := core.GetToken
-		if global {
-			getTokenFn = core.GetGlobalToken
-		}
-
-		token, err := getTokenFn(repo, token)
+	for _, token := range tokens {
+		token, err := core.LoadToken(repo, token)
 		if err != nil {
 			return err
 		}
@@ -49,17 +43,16 @@ func runTokenBridge(cmd *cobra.Command, args []string) error {
 }
 
 func printToken(token *core.Token) {
-	idFmt := text.LeftPadMaxLine(token.HumanId(), 7, 0)
-	valueFmt := text.LeftPadMaxLine(token.Value, 8, 0)
-	targetFmt := text.LeftPadMaxLine(token.Target, 8, 0)
-	scopesFmt := text.LeftPadMaxLine(strings.Join(token.Scopes, ","), 20, 0)
+	idFmt := text.LeftPadMaxLine(token.ID.Human(), 7, 0)
+	valueFmt := text.LeftPadMaxLine(token.Value, 15, 0)
+	targetFmt := text.LeftPadMaxLine(token.Target, 7, 0)
+	createTimeFmt := text.LeftPadMaxLine(token.CreateTime.Format(time.RFC822), 20, 0)
 
-	fmt.Printf("%s %s %s %s %s\n",
+	fmt.Printf("%s %s %s %s\n",
 		idFmt,
-		valueFmt,
 		colors.Magenta(targetFmt),
-		colors.Yellow(token.Kind()),
-		scopesFmt,
+		valueFmt,
+		createTimeFmt,
 	)
 }
 

commands/bridge_token_add.go 🔗

@@ -8,17 +8,17 @@ import (
 )
 
 var (
-	bridgeToken core.Token
+	bridgeTokenValue  string
+	bridgeTokenTarget string
 )
 
 func runBridgeTokenAdd(cmd *cobra.Command, args []string) error {
-	_ = bridgeToken.Id() // TODO: a better design to avoid doing this
-
-	if err := bridgeToken.Validate(); err != nil {
+	token := core.NewToken(bridgeTokenValue, bridgeTokenTarget)
+	if err := token.Validate(); err != nil {
 		return errors.Wrap(err, "invalid token")
 	}
 
-	return core.StoreToken(repo, &bridgeToken)
+	return core.StoreToken(repo, token)
 }
 
 var bridgeTokenAddCmd = &cobra.Command{
@@ -31,9 +31,7 @@ var bridgeTokenAddCmd = &cobra.Command{
 
 func init() {
 	bridgeTokenCmd.AddCommand(bridgeTokenAddCmd)
-	bridgeTokenAddCmd.Flags().BoolVarP(&bridgeToken.Global, "global", "g", false, "")
-	bridgeTokenAddCmd.Flags().StringVarP(&bridgeToken.Value, "value", "v", "", "")
-	bridgeTokenAddCmd.Flags().StringVarP(&bridgeToken.Target, "target", "t", "", "")
-	bridgeTokenAddCmd.Flags().StringArrayVarP(&bridgeToken.Scopes, "scopes", "s", []string{}, "")
+	bridgeTokenAddCmd.Flags().StringVarP(&bridgeTokenValue, "value", "v", "", "")
+	bridgeTokenAddCmd.Flags().StringVarP(&bridgeTokenTarget, "target", "t", "", "")
 	bridgeTokenAddCmd.Flags().SortFlags = false
 }

commands/bridge_token_rm.go 🔗

@@ -7,13 +7,7 @@ import (
 )
 
 func runBridgeTokenRm(cmd *cobra.Command, args []string) error {
-	err := core.RemoveToken(repo, args[0])
-	if err == nil {
-		return nil
-	}
-
-	err = core.RemoveGlobalToken(repo, args[0])
-	return err
+	return core.RemoveToken(repo, args[0])
 }
 
 var bridgeTokenRmCmd = &cobra.Command{