From 864d3ed33597211f22177fce6ecb7e741db795b5 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Wed, 25 Dec 2019 22:55:53 +0100 Subject: bridge: allow to configure and pull without having set a user first - init() only the importer or exporter as required - assign a "default user" user Id to credentials at creation if no user has been set - "bridge auth": also display the user - "bridge auth show": adapt to a potential "default user" user Id - "bridge configure": allow to run without a user set - "bridge pull": allow to run without a user set - "user adopt": replace "default user" by the actual user id when run --- bridge/core/auth/credential.go | 30 ++++++++++++++++++++++++++++-- bridge/core/auth/token.go | 6 +++++- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'bridge/core/auth') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index a462a116..fd026c5d 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -32,9 +32,15 @@ func NewErrMultipleMatchCredential(matching []entity.Id) *entity.ErrMultipleMatc return entity.NewErrMultipleMatch("credential", matching) } +// Special Id to mark a credential as being associated to the default user, whoever it might be. +// The intended use is for the bridge configuration, to be able to create and store a credential +// with no identities created yet, and then select one with `git-bug user adopt` +const DefaultUserId = entity.Id("default-user") + type Credential interface { ID() entity.Id UserId() entity.Id + updateUserId(id entity.Id) Target() string Kind() CredentialKind CreateTime() time.Time @@ -42,7 +48,7 @@ type Credential interface { // 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 + toConfig() map[string]string } // Load loads a credential from the repo config @@ -90,6 +96,7 @@ func LoadWithPrefix(repo repository.RepoConfig, prefix string) (Credential, erro return matching[0], nil } +// loadFromConfig is a helper to construct a Credential from the set of git configs func loadFromConfig(rawConfigs map[string]string, id entity.Id) (Credential, error) { keyPrefix := fmt.Sprintf("%s.%s.", configKeyPrefix, id) @@ -168,7 +175,7 @@ func PrefixExist(repo repository.RepoConfig, prefix string) bool { // Store stores a credential in the global git config func Store(repo repository.RepoConfig, cred Credential) error { - confs := cred.ToConfig() + confs := cred.toConfig() prefix := fmt.Sprintf("%s.%s.", configKeyPrefix, cred.ID()) @@ -213,6 +220,25 @@ func Remove(repo repository.RepoConfig, id entity.Id) error { return repo.GlobalConfig().RemoveAll(keyPrefix) } +// ReplaceDefaultUser update all the credential attributed to the temporary "default user" +// with a real user Id +func ReplaceDefaultUser(repo repository.RepoConfig, id entity.Id) error { + list, err := List(repo, WithUserId(DefaultUserId)) + if err != nil { + return err + } + + for _, cred := range list { + cred.updateUserId(id) + err = Store(repo, cred) + if err != nil { + return err + } + } + + return nil +} + /* * Sorting */ diff --git a/bridge/core/auth/token.go b/bridge/core/auth/token.go index 12a3bfc0..8333ef12 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -59,6 +59,10 @@ func (t *Token) UserId() entity.Id { return t.userId } +func (t *Token) updateUserId(id entity.Id) { + t.userId = id +} + func (t *Token) Target() string { return t.target } @@ -88,7 +92,7 @@ func (t *Token) Validate() error { return nil } -func (t *Token) ToConfig() map[string]string { +func (t *Token) toConfig() map[string]string { return map[string]string{ tokenValueKey: t.Value, } -- cgit From 26f0152384f77d2bfd16c6762f5618bc966809a6 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 7 Jan 2020 22:07:25 +0100 Subject: WIP --- bridge/core/auth/credential.go | 56 +++++++++++++++++------------------------- bridge/core/auth/options.go | 32 ++++++++++-------------- bridge/core/auth/token.go | 20 ++++++--------- 3 files changed, 44 insertions(+), 64 deletions(-) (limited to 'bridge/core/auth') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index fd026c5d..228eb006 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -14,9 +14,11 @@ import ( const ( configKeyPrefix = "git-bug.auth" configKeyKind = "kind" - configKeyUserId = "userid" configKeyTarget = "target" configKeyCreateTime = "createtime" + configKeyPrefixMeta = "meta." + + MetaKeyLogin = "login" ) type CredentialKind string @@ -32,19 +34,13 @@ func NewErrMultipleMatchCredential(matching []entity.Id) *entity.ErrMultipleMatc return entity.NewErrMultipleMatch("credential", matching) } -// Special Id to mark a credential as being associated to the default user, whoever it might be. -// The intended use is for the bridge configuration, to be able to create and store a credential -// with no identities created yet, and then select one with `git-bug user adopt` -const DefaultUserId = entity.Id("default-user") - type Credential interface { ID() entity.Id - UserId() entity.Id - updateUserId(id entity.Id) Target() string Kind() CredentialKind CreateTime() time.Time Validate() error + Metadata() map[string]string // 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. @@ -120,6 +116,17 @@ func loadFromConfig(rawConfigs map[string]string, id entity.Id) (Credential, err return cred, nil } +func metaFromConfig(configs map[string]string) map[string]string { + result := make(map[string]string) + for key, val := range configs { + if strings.HasPrefix(key, configKeyPrefixMeta) { + key = strings.TrimPrefix(key, configKeyPrefixMeta) + result[key] = val + } + } + return result +} + // List load all existing credentials func List(repo repository.RepoConfig, opts ...Option) ([]Credential, error) { rawConfigs, err := repo.GlobalConfig().ReadAll(configKeyPrefix + ".") @@ -185,12 +192,6 @@ func Store(repo repository.RepoConfig, cred Credential) error { 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 { @@ -203,6 +204,14 @@ func Store(repo repository.RepoConfig, cred Credential) error { return err } + // Metadata + for key, val := range cred.Metadata() { + err := repo.GlobalConfig().StoreString(prefix+configKeyPrefixMeta+key, val) + if err != nil { + return err + } + } + // Custom for key, val := range confs { err := repo.GlobalConfig().StoreString(prefix+key, val) @@ -220,25 +229,6 @@ func Remove(repo repository.RepoConfig, id entity.Id) error { return repo.GlobalConfig().RemoveAll(keyPrefix) } -// ReplaceDefaultUser update all the credential attributed to the temporary "default user" -// with a real user Id -func ReplaceDefaultUser(repo repository.RepoConfig, id entity.Id) error { - list, err := List(repo, WithUserId(DefaultUserId)) - if err != nil { - return err - } - - for _, cred := range list { - cred.updateUserId(id) - err = Store(repo, cred) - if err != nil { - return err - } - } - - return nil -} - /* * Sorting */ diff --git a/bridge/core/auth/options.go b/bridge/core/auth/options.go index 7bcda68e..0c780dc1 100644 --- a/bridge/core/auth/options.go +++ b/bridge/core/auth/options.go @@ -1,14 +1,9 @@ package auth -import ( - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" -) - type options struct { target string - userId entity.Id kind CredentialKind + meta map[string]string } type Option func(opts *options) @@ -26,12 +21,14 @@ func (opts *options) Match(cred Credential) bool { return false } - if opts.userId != "" && cred.UserId() != opts.userId { + if opts.kind != "" && cred.Kind() != opts.kind { return false } - if opts.kind != "" && cred.Kind() != opts.kind { - return false + for key, val := range opts.meta { + if v, ok := cred.Metadata()[key]; !ok || v != val { + return false + } } return true @@ -43,20 +40,17 @@ func WithTarget(target string) Option { } } -func WithUser(user identity.Interface) Option { - return func(opts *options) { - opts.userId = user.Id() - } -} - -func WithUserId(userId entity.Id) Option { +func WithKind(kind CredentialKind) Option { return func(opts *options) { - opts.userId = userId + opts.kind = kind } } -func WithKind(kind CredentialKind) Option { +func WithMeta(key string, val string) Option { return func(opts *options) { - opts.kind = kind + if opts.meta == nil { + opts.meta = make(map[string]string) + } + opts.meta[key] = val } } diff --git a/bridge/core/auth/token.go b/bridge/core/auth/token.go index 8333ef12..60137cd9 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -18,26 +18,25 @@ var _ Credential = &Token{} // Token holds an API access token data type Token struct { - userId entity.Id target string createTime time.Time Value string + meta map[string]string } // NewToken instantiate a new token -func NewToken(userId entity.Id, value, target string) *Token { +func NewToken(value, target string) *Token { return &Token{ - userId: userId, target: target, createTime: time.Now(), Value: value, + meta: make(map[string]string), } } func NewTokenFromConfig(conf map[string]string) *Token { token := &Token{} - token.userId = entity.Id(conf[configKeyUserId]) token.target = conf[configKeyTarget] if createTime, ok := conf[configKeyCreateTime]; ok { if t, err := repository.ParseTimestamp(createTime); err == nil { @@ -46,6 +45,7 @@ func NewTokenFromConfig(conf map[string]string) *Token { } token.Value = conf[tokenValueKey] + token.meta = metaFromConfig(conf) return token } @@ -55,14 +55,6 @@ func (t *Token) ID() entity.Id { return entity.Id(fmt.Sprintf("%x", sum)) } -func (t *Token) UserId() entity.Id { - return t.userId -} - -func (t *Token) updateUserId(id entity.Id) { - t.userId = id -} - func (t *Token) Target() string { return t.target } @@ -92,6 +84,10 @@ func (t *Token) Validate() error { return nil } +func (t *Token) Metadata() map[string]string { + return t.meta +} + func (t *Token) toConfig() map[string]string { return map[string]string{ tokenValueKey: t.Value, -- cgit From ae2f942ef907161af0aba5f3511db72cf9801dca Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 12 Jan 2020 16:38:16 +0100 Subject: more wip --- bridge/core/auth/credential.go | 4 ++-- bridge/core/auth/credential_test.go | 41 +++++++++++++++---------------------- 2 files changed, 19 insertions(+), 26 deletions(-) (limited to 'bridge/core/auth') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index 228eb006..e843ede7 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -43,7 +43,7 @@ type Credential interface { Metadata() map[string]string // 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. + // This does not include Target, Kind, CreateTime and Metadata. toConfig() map[string]string } @@ -134,7 +134,7 @@ func List(repo repository.RepoConfig, opts ...Option) ([]Credential, error) { return nil, err } - re, err := regexp.Compile(configKeyPrefix + `.([^.]+).([^.]+)`) + re, err := regexp.Compile(`^` + configKeyPrefix + `\.([^.]+)\.([^.]+(?:\.[^.]+)*)$`) if err != nil { panic(err) } diff --git a/bridge/core/auth/credential_test.go b/bridge/core/auth/credential_test.go index f91d273d..49c138cf 100644 --- a/bridge/core/auth/credential_test.go +++ b/bridge/core/auth/credential_test.go @@ -7,32 +7,23 @@ import ( "github.com/stretchr/testify/require" "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/repository" ) func TestCredential(t *testing.T) { repo := repository.NewMockRepoForTest() - user1 := identity.NewIdentity("user1", "email") - err := user1.Commit(repo) - assert.NoError(t, err) - - user2 := identity.NewIdentity("user2", "email") - err = user2.Commit(repo) - assert.NoError(t, err) - - storeToken := func(user identity.Interface, val string, target string) *Token { - token := NewToken(user.Id(), val, target) - err = Store(repo, token) + storeToken := func(val string, target string) *Token { + token := NewToken(val, target) + err := Store(repo, token) require.NoError(t, err) return token } - token := storeToken(user1, "foobar", "github") + token := storeToken("foobar", "github") // Store + Load - err = Store(repo, token) + err := Store(repo, token) assert.NoError(t, err) token2, err := LoadWithId(repo, token.ID()) @@ -50,8 +41,8 @@ func TestCredential(t *testing.T) { token.createTime = token3.CreateTime() assert.Equal(t, token, token3) - token4 := storeToken(user1, "foo", "gitlab") - token5 := storeToken(user2, "bar", "github") + token4 := storeToken("foo", "gitlab") + token5 := storeToken("bar", "github") // List + options creds, err := List(repo, WithTarget("github")) @@ -62,14 +53,6 @@ func TestCredential(t *testing.T) { assert.NoError(t, err) sameIds(t, creds, []Credential{token4}) - creds, err = List(repo, WithUser(user1)) - assert.NoError(t, err) - sameIds(t, creds, []Credential{token, token4}) - - creds, err = List(repo, WithUserId(user1.Id())) - assert.NoError(t, err) - sameIds(t, creds, []Credential{token, token4}) - creds, err = List(repo, WithKind(KindToken)) assert.NoError(t, err) sameIds(t, creds, []Credential{token, token4, token5}) @@ -78,6 +61,16 @@ func TestCredential(t *testing.T) { assert.NoError(t, err) sameIds(t, creds, []Credential{}) + // Metadata + + token4.Metadata()["key"] = "value" + err = Store(repo, token4) + assert.NoError(t, err) + + creds, err = List(repo, WithMeta("key", "value")) + assert.NoError(t, err) + sameIds(t, creds, []Credential{token4}) + // Exist exist := IdExist(repo, token.ID()) assert.True(t, exist) -- cgit From 74e91144105790cc997c1d79a7f638e1e3a1f3f8 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Fri, 24 Jan 2020 00:30:13 +0100 Subject: more more wip --- bridge/core/auth/credential.go | 6 ++++++ bridge/core/auth/credential_test.go | 2 +- bridge/core/auth/options.go | 2 +- bridge/core/auth/token.go | 13 ++++++++++++- 4 files changed, 20 insertions(+), 3 deletions(-) (limited to 'bridge/core/auth') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index e843ede7..c1255aa6 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -40,7 +40,10 @@ type Credential interface { Kind() CredentialKind CreateTime() time.Time Validate() error + Metadata() map[string]string + GetMetadata(key string) (string, bool) + SetMetadata(key string, value string) // Return all the specific properties of the credential that need to be saved into the configuration. // This does not include Target, Kind, CreateTime and Metadata. @@ -124,6 +127,9 @@ func metaFromConfig(configs map[string]string) map[string]string { result[key] = val } } + if len(result) == 0 { + return nil + } return result } diff --git a/bridge/core/auth/credential_test.go b/bridge/core/auth/credential_test.go index 49c138cf..2f8806c9 100644 --- a/bridge/core/auth/credential_test.go +++ b/bridge/core/auth/credential_test.go @@ -63,7 +63,7 @@ func TestCredential(t *testing.T) { // Metadata - token4.Metadata()["key"] = "value" + token4.SetMetadata("key", "value") err = Store(repo, token4) assert.NoError(t, err) diff --git a/bridge/core/auth/options.go b/bridge/core/auth/options.go index 0c780dc1..74189874 100644 --- a/bridge/core/auth/options.go +++ b/bridge/core/auth/options.go @@ -26,7 +26,7 @@ func (opts *options) Match(cred Credential) bool { } for key, val := range opts.meta { - if v, ok := cred.Metadata()[key]; !ok || v != val { + if v, ok := cred.GetMetadata(key); !ok || v != val { return false } } diff --git a/bridge/core/auth/token.go b/bridge/core/auth/token.go index 60137cd9..42f960bf 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -30,7 +30,6 @@ func NewToken(value, target string) *Token { target: target, createTime: time.Now(), Value: value, - meta: make(map[string]string), } } @@ -88,6 +87,18 @@ func (t *Token) Metadata() map[string]string { return t.meta } +func (t *Token) GetMetadata(key string) (string, bool) { + val, ok := t.meta[key] + return val, ok +} + +func (t *Token) SetMetadata(key string, value string) { + if t.meta == nil { + t.meta = make(map[string]string) + } + t.meta[key] = value +} + func (t *Token) toConfig() map[string]string { return map[string]string{ tokenValueKey: t.Value, -- cgit