diff options
Diffstat (limited to 'bridge/core/token.go')
-rw-r--r-- | bridge/core/token.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/bridge/core/token.go b/bridge/core/token.go new file mode 100644 index 00000000..2ceabca2 --- /dev/null +++ b/bridge/core/token.go @@ -0,0 +1,182 @@ +package core + +import ( + "crypto/sha256" + "errors" + "fmt" + "regexp" + "sort" + "strings" + "time" + + "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/repository" +) + +const ( + tokenConfigKeyPrefix = "git-bug.token" + tokenValueKey = "value" + tokenTargetKey = "target" + tokenCreateTimeKey = "createtime" +) + +var ErrTokenNotExist = errors.New("token doesn't exist") + +func NewErrMultipleMatchToken(matching []entity.Id) *entity.ErrMultipleMatch { + return entity.NewErrMultipleMatch("token", matching) +} + +// Token holds an API access token data +type Token struct { + Value string + Target string + CreateTime time.Time +} + +// NewToken instantiate a new token +func NewToken(value, target string) *Token { + return &Token{ + Value: value, + Target: target, + CreateTime: time.Now(), + } +} + +func (t *Token) ID() entity.Id { + sum := sha256.Sum256([]byte(t.Value)) + return entity.Id(fmt.Sprintf("%x", sum)) +} + +// Validate ensure token important fields are valid +func (t *Token) Validate() error { + if t.Value == "" { + return fmt.Errorf("missing value") + } + if t.Target == "" { + return fmt.Errorf("missing target") + } + if t.CreateTime.IsZero() || t.CreateTime.Equal(time.Time{}) { + return fmt.Errorf("missing creation time") + } + if !TargetExist(t.Target) { + return fmt.Errorf("unknown target") + } + return nil +} + +// LoadToken loads a token from the repo config +func LoadToken(repo repository.RepoCommon, id entity.Id) (*Token, error) { + keyPrefix := fmt.Sprintf("git-bug.token.%s.", id) + + // read token config pairs + rawconfigs, err := repo.GlobalConfig().ReadAll(keyPrefix) + if err != nil { + // Not exactly right due to the limitation of ReadAll() + return nil, ErrTokenNotExist + } + + // trim key prefix + configs := make(map[string]string) + for key, value := range rawconfigs { + newKey := strings.TrimPrefix(key, keyPrefix) + configs[newKey] = value + } + + token := &Token{} + + token.Value = configs[tokenValueKey] + token.Target = configs[tokenTargetKey] + if createTime, ok := configs[tokenCreateTimeKey]; ok { + if t, err := repository.ParseTimestamp(createTime); err == nil { + token.CreateTime = t + } + } + + return token, nil +} + +// LoadTokenPrefix load a token from the repo config with a prefix +func LoadTokenPrefix(repo repository.RepoCommon, prefix string) (*Token, error) { + tokens, err := ListTokens(repo) + if err != nil { + return nil, err + } + + // preallocate but empty + matching := make([]entity.Id, 0, 5) + + for _, id := range tokens { + if id.HasPrefix(prefix) { + matching = append(matching, id) + } + } + + if len(matching) > 1 { + return nil, NewErrMultipleMatchToken(matching) + } + + if len(matching) == 0 { + return nil, ErrTokenNotExist + } + + return LoadToken(repo, matching[0]) +} + +// 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) ([]entity.Id, error) { + configs, err := repo.GlobalConfig().ReadAll(tokenConfigKeyPrefix + ".") + if err != nil { + return nil, err + } + + re, err := regexp.Compile(tokenConfigKeyPrefix + `.([^.]+)`) + if err != nil { + panic(err) + } + + set := make(map[string]interface{}) + + for key := range configs { + res := re.FindStringSubmatch(key) + + if res == nil { + continue + } + + set[res[1]] = nil + } + + result := make([]entity.Id, 0, len(set)) + for key := range set { + result = append(result, entity.Id(key)) + } + + sort.Sort(entity.Alphabetical(result)) + + return result, nil +} + +// 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().String(), tokenTargetKey) + err = repo.GlobalConfig().StoreString(storeTargetKey, token.Target) + if err != nil { + return err + } + + createTimeKey := fmt.Sprintf("git-bug.token.%s.%s", token.ID().String(), tokenCreateTimeKey) + return repo.GlobalConfig().StoreTimestamp(createTimeKey, token.CreateTime) +} + +// RemoveToken removes a token from the repo config +func RemoveToken(repo repository.RepoCommon, id entity.Id) error { + keyPrefix := fmt.Sprintf("git-bug.token.%s", id) + return repo.GlobalConfig().RemoveAll(keyPrefix) +} |