token.go

  1package core
  2
  3import (
  4	"fmt"
  5	"regexp"
  6	"strconv"
  7	"strings"
  8
  9	"github.com/MichaelMure/git-bug/repository"
 10)
 11
 12const (
 13	tokenConfigKeyPrefix = "git-bug.token"
 14	tokenKeyValue        = "value"
 15	tokenKeyTarget       = "target"
 16	tokenKeyGlobal       = "global"
 17	tokenKeyScopes       = "scopes"
 18)
 19
 20// Token represent token related informations
 21type Token struct {
 22	Name   string
 23	Value  string
 24	Target string
 25	Global bool
 26	Scopes []string
 27}
 28
 29// NewToken instantiate a new token
 30func NewToken(name, value, target string, global bool, scopes []string) *Token {
 31	return &Token{
 32		Name:   name,
 33		Value:  value,
 34		Target: target,
 35		Global: global,
 36		Scopes: scopes,
 37	}
 38}
 39
 40// Validate ensure token important fields are valid
 41func (t *Token) Validate() error {
 42	if t.Name == "" {
 43		return fmt.Errorf("missing token name")
 44	}
 45	if t.Value == "" {
 46		return fmt.Errorf("missing token value")
 47	}
 48	if t.Target == "" {
 49		return fmt.Errorf("missing token target")
 50	}
 51	return nil
 52}
 53
 54func loadToken(repo repository.RepoConfig, name string, global bool) (*Token, error) {
 55	keyPrefix := fmt.Sprintf("git-bug.token.%s", name)
 56	var pairs map[string]string
 57	var err error
 58
 59	// read token config pairs
 60	if global {
 61		pairs, err = repo.ReadGlobalConfigs(keyPrefix)
 62		if err != nil {
 63			return nil, err
 64		}
 65
 66	} else {
 67		pairs, err = repo.ReadConfigs(keyPrefix)
 68		if err != nil {
 69			return nil, err
 70		}
 71	}
 72
 73	// trim key prefix
 74	result := make(Configuration, len(pairs))
 75	for key, value := range pairs {
 76		key := strings.TrimPrefix(key, keyPrefix)
 77		result[key] = value
 78	}
 79
 80	var ok bool
 81	token := &Token{Name: name}
 82	token.Value, ok = result[tokenKeyValue]
 83	if !ok {
 84		return nil, fmt.Errorf("empty token value")
 85	}
 86
 87	token.Target, ok = result[tokenKeyTarget]
 88	if !ok {
 89		return nil, fmt.Errorf("empty token key")
 90	}
 91
 92	if g, ok := result[tokenKeyGlobal]; !ok {
 93		return nil, fmt.Errorf("empty token global")
 94	} else if g == "true" {
 95		token.Global = true
 96	}
 97
 98	scopesString, ok := result[tokenKeyScopes]
 99	if !ok {
100		return nil, fmt.Errorf("missing scopes config")
101	}
102
103	token.Scopes = strings.Split(scopesString, ",")
104	return token, nil
105}
106
107// GetToken loads a token from repo config
108func GetToken(repo repository.RepoConfig, name string) (*Token, error) {
109	return loadToken(repo, name, false)
110}
111
112// GetGlobalToken loads a token from the global config
113func GetGlobalToken(repo repository.RepoConfig, name string) (*Token, error) {
114	return loadToken(repo, name, true)
115}
116
117func listTokens(repo repository.RepoConfig, global bool) ([]string, error) {
118	var configs map[string]string
119	var err error
120	if global {
121		configs, err = repo.ReadConfigs(tokenConfigKeyPrefix + ".")
122		if err != nil {
123			return nil, err
124		}
125	} else {
126		configs, err = repo.ReadGlobalConfigs(tokenConfigKeyPrefix + ".")
127		if err != nil {
128			return nil, err
129		}
130	}
131
132	re, err := regexp.Compile(tokenConfigKeyPrefix + `.([^.]+)`)
133	if err != nil {
134		panic(err)
135	}
136
137	set := make(map[string]interface{})
138
139	for key := range configs {
140		res := re.FindStringSubmatch(key)
141
142		if res == nil {
143			continue
144		}
145
146		set[res[1]] = nil
147	}
148
149	result := make([]string, len(set))
150	i := 0
151	for key := range set {
152		result[i] = key
153		i++
154	}
155
156	return result, nil
157}
158
159// ListTokens return the list of stored tokens in the repo config
160func ListTokens(repo repository.RepoConfig) ([]string, error) {
161	return listTokens(repo, false)
162}
163
164// ListGlobalTokens return the list of stored tokens in the global config
165func ListGlobalTokens(repo repository.RepoConfig) ([]string, error) {
166	return listTokens(repo, true)
167}
168
169func storeToken(repo repository.RepoConfig, token *Token) error {
170	var store func(key, value string) error
171	if token.Global {
172		store = repo.StoreGlobalConfig
173	} else {
174		store = repo.StoreConfig
175	}
176
177	var err error
178	storeValueKey := fmt.Sprintf("git-bug.token.%s.%s", token.Name, tokenKeyValue)
179	err = store(storeValueKey, token.Value)
180	if err != nil {
181		return err
182	}
183
184	storeTargetKey := fmt.Sprintf("git-bug.token.%s.%s", token.Name, tokenKeyTarget)
185	err = store(storeTargetKey, token.Target)
186	if err != nil {
187		return err
188	}
189
190	storeGlobalKey := fmt.Sprintf("git-bug.token.%s.%s", token.Name, tokenKeyGlobal)
191	err = store(storeGlobalKey, strconv.FormatBool(token.Global))
192	if err != nil {
193		return err
194	}
195
196	storeScopesKey := fmt.Sprintf("git-bug.token.%s.%s", token.Name, tokenKeyScopes)
197	err = store(storeScopesKey, strings.Join(token.Scopes, ","))
198	if err != nil {
199		return err
200	}
201
202	return nil
203}
204
205// StoreToken stores a token in the repo config
206func StoreToken(repo repository.RepoConfig, name, value, target string, scopes []string) error {
207	return storeToken(repo, NewToken(name, value, target, false, scopes))
208}
209
210// StoreGlobalToken stores a token in global config
211func StoreGlobalToken(repo repository.RepoConfig, name, value, target string, scopes []string) error {
212	return storeToken(repo, NewToken(name, value, target, true, scopes))
213}
214
215// RemoveToken removes a token from the repo config
216func RemoveToken(repo repository.RepoConfig, name string) error {
217	keyPrefix := fmt.Sprintf("git-bug.token.%s", name)
218	return repo.RmConfigs(keyPrefix)
219}
220
221// RemoveGlobalToken removes a token from the repo config
222func RemoveGlobalToken(repo repository.RepoConfig, name string) error {
223	keyPrefix := fmt.Sprintf("git-bug.token.%s", name)
224	return repo.RmGlobalConfigs(keyPrefix)
225}