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}