more wip

Michael Muré created

Change summary

bridge/core/auth/credential.go      |  4 +-
bridge/core/auth/credential_test.go | 41 +++++++++++----------------
bridge/core/config.go               |  1 
bridge/github/config.go             |  8 ++++-
bridge/github/config_test.go        |  5 +--
bridge/github/export.go             | 45 ++++++++++--------------------
bridge/github/import.go             | 16 ----------
bridge/github/import_test.go        |  8 ----
input/prompt.go                     |  2 +
9 files changed, 47 insertions(+), 83 deletions(-)

Detailed changes

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)
 	}

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)

bridge/github/config.go 🔗

@@ -81,7 +81,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor
 
 	login := params.Login
 	if login == "" {
-		login, err = input.Prompt("Github login", "", true, validateUsername)
+		login, err = input.Prompt("Github login", "login", input.Required, validateUsername)
 		if err != nil {
 			return nil, err
 		}
@@ -128,6 +128,10 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor
 		return nil, err
 	}
 
+	// Todo: if no user exist with the given login
+	// - tag the default user with the github login
+	// - add a command to manually tag a user ?
+
 	// don't forget to store the now known valid token
 	if !auth.IdExist(repo, cred.ID()) {
 		err = auth.Store(repo, cred)
@@ -317,7 +321,7 @@ func promptToken() (string, error) {
 		return "token has incorrect format", nil
 	}
 
-	return input.Prompt("Enter token", "token", "", input.Required, validator)
+	return input.Prompt("Enter token", "token", input.Required, validator)
 }
 
 func loginAndRequestToken(login, owner, project string) (string, error) {

bridge/github/config_test.go 🔗

@@ -7,7 +7,6 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/MichaelMure/git-bug/bridge/core/auth"
-	"github.com/MichaelMure/git-bug/entity"
 )
 
 func TestSplitURL(t *testing.T) {
@@ -155,8 +154,8 @@ func TestValidateProject(t *testing.T) {
 		t.Skip("Env var GITHUB_TOKEN_PUBLIC missing")
 	}
 
-	tokenPrivate := auth.NewToken(entity.UnsetId, envPrivate, target)
-	tokenPublic := auth.NewToken(entity.UnsetId, envPublic, target)
+	tokenPrivate := auth.NewToken(envPrivate, target)
+	tokenPublic := auth.NewToken(envPublic, target)
 
 	type args struct {
 		owner   string

bridge/github/export.go 🔗

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"os"
 	"strings"
 	"time"
 
@@ -19,6 +20,7 @@ import (
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/entity"
+	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/repository"
 )
 
@@ -33,13 +35,6 @@ type githubExporter struct {
 	// cache identities clients
 	identityClient map[entity.Id]*githubv4.Client
 
-	// the client to use for non user-specific queries
-	// should be the client of the default user
-	defaultClient *githubv4.Client
-
-	// the token of the default user
-	defaultToken *auth.Token
-
 	// github repository ID
 	repositoryID string
 
@@ -58,43 +53,33 @@ func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) e
 	ge.cachedOperationIDs = make(map[entity.Id]string)
 	ge.cachedLabels = make(map[string]string)
 
-	user, err := repo.GetUserIdentity()
-	if err != nil {
-		return err
-	}
-
 	// preload all clients
-	err = ge.cacheAllClient(repo)
-	if err != nil {
-		return err
-	}
-
-	ge.defaultClient, err = ge.getClientForIdentity(user.Id())
-	if err != nil {
-		return err
-	}
-
-	creds, err := auth.List(repo, auth.WithUserId(user.Id()), auth.WithTarget(target), auth.WithKind(auth.KindToken))
+	err := ge.cacheAllClient(repo)
 	if err != nil {
 		return err
 	}
 
-	if len(creds) == 0 {
-		return ErrMissingIdentityToken
-	}
-
-	ge.defaultToken = creds[0].(*auth.Token)
-
 	return nil
 }
 
-func (ge *githubExporter) cacheAllClient(repo repository.RepoConfig) error {
+func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error {
 	creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken))
 	if err != nil {
 		return err
 	}
 
 	for _, cred := range creds {
+		login, ok := cred.Metadata()[auth.MetaKeyLogin]
+		if !ok {
+			_, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with Github login\n", cred.ID().Human())
+			continue
+		}
+
+		user, err := repo.ResolveIdentityImmutableMetadata(metaKeyGithubLogin, login)
+		if err == identity.ErrIdentityNotExist {
+			continue
+		}
+
 		if _, ok := ge.identityClient[cred.UserId()]; !ok {
 			client := buildClient(creds[0].(*auth.Token))
 			ge.identityClient[cred.UserId()] = client

bridge/github/import.go 🔗

@@ -12,7 +12,6 @@ import (
 	"github.com/MichaelMure/git-bug/bug"
 	"github.com/MichaelMure/git-bug/cache"
 	"github.com/MichaelMure/git-bug/entity"
-	"github.com/MichaelMure/git-bug/identity"
 	"github.com/MichaelMure/git-bug/util/text"
 )
 
@@ -39,20 +38,7 @@ type githubImporter struct {
 func (gi *githubImporter) Init(repo *cache.RepoCache, conf core.Configuration) error {
 	gi.conf = conf
 
-	opts := []auth.Option{
-		auth.WithTarget(target),
-		auth.WithKind(auth.KindToken),
-	}
-
-	user, err := repo.GetUserIdentity()
-	if err == nil {
-		opts = append(opts, auth.WithUserId(user.Id()))
-	}
-	if err == identity.ErrNoIdentitySet {
-		opts = append(opts, auth.WithUserId(auth.DefaultUserId))
-	}
-
-	creds, err := auth.List(repo, opts...)
+	creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken))
 	if err != nil {
 		return err
 	}

bridge/github/import_test.go 🔗

@@ -140,13 +140,7 @@ func Test_Importer(t *testing.T) {
 		t.Skip("Env var GITHUB_TOKEN_PRIVATE missing")
 	}
 
-	err = author.Commit(repo)
-	require.NoError(t, err)
-
-	err = identity.SetUserIdentity(repo, author)
-	require.NoError(t, err)
-
-	token := auth.NewToken(author.Id(), envToken, target)
+	token := auth.NewToken(envToken, target)
 	err = auth.Store(repo, token)
 	require.NoError(t, err)
 

input/prompt.go 🔗

@@ -14,6 +14,8 @@ import (
 )
 
 // PromptValidator is a validator for a user entry
+// If complaint is "", value is considered valid, otherwise it's the error reported to the user
+// If err != nil, a terminal error happened
 type PromptValidator func(name string, value string) (complaint string, err error)
 
 // Required is a validator preventing a "" value