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