diff options
author | Michael Muré <batolettre@gmail.com> | 2019-12-08 21:15:06 +0100 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2019-12-08 21:28:27 +0100 |
commit | b92adfcb5f79f2b32c3dafb0fc3e7f1b753b6197 (patch) | |
tree | 69202c4021b10f3ab7b7f5ebf229d501e95c4786 /bridge/core/auth/credential.go | |
parent | 981a4a848b1329da1a73270e27633911f9298bb1 (diff) | |
download | git-bug-b92adfcb5f79f2b32c3dafb0fc3e7f1b753b6197.tar.gz |
bridge: huge refactor to accept multiple kind of credentials
Diffstat (limited to 'bridge/core/auth/credential.go')
-rw-r--r-- | bridge/core/auth/credential.go | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go new file mode 100644 index 00000000..7aad7552 --- /dev/null +++ b/bridge/core/auth/credential.go @@ -0,0 +1,232 @@ +package auth + +import ( + "errors" + "fmt" + "regexp" + "strings" + "time" + + "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/repository" +) + +const ( + configKeyPrefix = "git-bug.auth" + configKeyKind = "kind" + configKeyUserId = "userid" + configKeyTarget = "target" + configKeyCreateTime = "createtime" +) + +type CredentialKind string + +const ( + KindToken = "token" + KindLoginPassword = "login-password" +) + +var ErrCredentialNotExist = errors.New("credential doesn't exist") + +func NewErrMultipleMatchCredential(matching []entity.Id) *entity.ErrMultipleMatch { + return entity.NewErrMultipleMatch("credential", matching) +} + +type Credential interface { + ID() entity.Id + UserId() entity.Id + Target() string + Kind() CredentialKind + CreateTime() time.Time + Validate() error + + // Return all the specific properties of the credential that need to be saved into the configuration. + // This does not include Target, User, Kind and CreateTime. + ToConfig() map[string]string +} + +// Load loads a credential from the repo config +func LoadWithId(repo repository.RepoConfig, id entity.Id) (Credential, error) { + keyPrefix := fmt.Sprintf("%s.%s.", configKeyPrefix, 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, ErrCredentialNotExist + } + + return loadFromConfig(rawconfigs, id) +} + +// LoadWithPrefix load a credential from the repo config with a prefix +func LoadWithPrefix(repo repository.RepoConfig, prefix string) (Credential, error) { + creds, err := List(repo) + if err != nil { + return nil, err + } + + // preallocate but empty + matching := make([]Credential, 0, 5) + + for _, cred := range creds { + if cred.ID().HasPrefix(prefix) { + matching = append(matching, cred) + } + } + + if len(matching) > 1 { + ids := make([]entity.Id, len(matching)) + for i, cred := range matching { + ids[i] = cred.ID() + } + return nil, NewErrMultipleMatchCredential(ids) + } + + if len(matching) == 0 { + return nil, ErrCredentialNotExist + } + + return matching[0], nil +} + +func loadFromConfig(rawConfigs map[string]string, id entity.Id) (Credential, error) { + keyPrefix := fmt.Sprintf("%s.%s.", configKeyPrefix, id) + + // trim key prefix + configs := make(map[string]string) + for key, value := range rawConfigs { + newKey := strings.TrimPrefix(key, keyPrefix) + configs[newKey] = value + } + + var cred Credential + + switch configs[configKeyKind] { + case KindToken: + cred = NewTokenFromConfig(configs) + case KindLoginPassword: + default: + return nil, fmt.Errorf("unknown credential type %s", configs[configKeyKind]) + } + + return cred, nil +} + +// List load all existing credentials +func List(repo repository.RepoConfig, opts ...Option) ([]Credential, error) { + rawConfigs, err := repo.GlobalConfig().ReadAll(configKeyPrefix + ".") + if err != nil { + return nil, err + } + + re, err := regexp.Compile(configKeyPrefix + `.([^.]+).([^.]+)`) + if err != nil { + panic(err) + } + + mapped := make(map[string]map[string]string) + + for key, val := range rawConfigs { + res := re.FindStringSubmatch(key) + if res == nil { + continue + } + if mapped[res[1]] == nil { + mapped[res[1]] = make(map[string]string) + } + mapped[res[1]][res[2]] = val + } + + matcher := matcher(opts) + + var credentials []Credential + for id, kvs := range mapped { + cred, err := loadFromConfig(kvs, entity.Id(id)) + if err != nil { + return nil, err + } + if matcher.Match(cred) { + credentials = append(credentials, cred) + } + } + + return credentials, nil +} + +// IdExist return whether a credential id exist or not +func IdExist(repo repository.RepoConfig, id entity.Id) bool { + _, err := LoadWithId(repo, id) + return err == nil +} + +// PrefixExist return whether a credential id prefix exist or not +func PrefixExist(repo repository.RepoConfig, prefix string) bool { + _, err := LoadWithPrefix(repo, prefix) + return err == nil +} + +// Store stores a credential in the global git config +func Store(repo repository.RepoConfig, cred Credential) error { + confs := cred.ToConfig() + + prefix := fmt.Sprintf("%s.%s.", configKeyPrefix, cred.ID()) + + // Kind + err := repo.GlobalConfig().StoreString(prefix+configKeyKind, string(cred.Kind())) + if err != nil { + return err + } + + // UserId + err = repo.GlobalConfig().StoreString(prefix+configKeyUserId, cred.UserId().String()) + if err != nil { + return err + } + + // Target + err = repo.GlobalConfig().StoreString(prefix+configKeyTarget, cred.Target()) + if err != nil { + return err + } + + // CreateTime + err = repo.GlobalConfig().StoreTimestamp(prefix+configKeyCreateTime, cred.CreateTime()) + if err != nil { + return err + } + + // Custom + for key, val := range confs { + err := repo.GlobalConfig().StoreString(prefix+key, val) + if err != nil { + return err + } + } + + return nil +} + +// Remove removes a credential from the global git config +func Remove(repo repository.RepoConfig, id entity.Id) error { + keyPrefix := fmt.Sprintf("%s.%s", configKeyPrefix, id) + return repo.GlobalConfig().RemoveAll(keyPrefix) +} + +/* + * Sorting + */ + +type ById []Credential + +func (b ById) Len() int { + return len(b) +} + +func (b ById) Less(i, j int) bool { + return b[i].ID() < b[j].ID() +} + +func (b ById) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} |