diff --git a/commands/webui.go b/commands/webui.go index e1f592df..24bdeced 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -19,7 +19,6 @@ import ( "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/graphql" - "github.com/MichaelMure/git-bug/graphql/config" "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/git" @@ -35,6 +34,15 @@ var ( const webUIOpenConfigKey = "git-bug.webui.open" +func authMiddleware(repo repository.RepoCommon, id *identity.Identity) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := identity.AttachToContext(r.Context(), repo, id) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + func runWebUI(cmd *cobra.Command, args []string) error { if webUIPort == 0 { var err error @@ -44,9 +52,12 @@ func runWebUI(cmd *cobra.Command, args []string) error { } } + var id *identity.Identity if !webUIReadOnly { // Verify that we have an identity. - if _, err := identity.GetUserIdentity(repo); err != nil { + var err error + id, err = identity.GetUserIdentity(repo) + if err != nil { return err } } @@ -56,7 +67,7 @@ func runWebUI(cmd *cobra.Command, args []string) error { router := mux.NewRouter() - graphqlHandler, err := graphql.NewHandler(repo, config.Config{ReadOnly: webUIReadOnly}) + graphqlHandler, err := graphql.NewHandler(repo) if err != nil { return err } @@ -70,10 +81,9 @@ func runWebUI(cmd *cobra.Command, args []string) error { router.Path("/playground").Handler(playground.Handler("git-bug", "/graphql")) router.Path("/graphql").Handler(graphqlHandler) router.Path("/gitfile/{hash}").Handler(newGitFileHandler(repo)) - if !webUIReadOnly { - router.Path("/upload").Methods("POST").Handler(newGitUploadFileHandler(repo)) - } + router.Path("/upload").Methods("POST").Handler(newGitUploadFileHandler(repo)) router.PathPrefix("/").Handler(http.FileServer(assetsHandler)) + router.Use(authMiddleware(repo, id)) srv := &http.Server{ Addr: addr, @@ -200,6 +210,11 @@ func newGitUploadFileHandler(repo repository.Repo) http.Handler { } func (gufh *gitUploadFileHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + if identity.ForContext(r.Context(), gufh.repo) == nil { + http.Error(rw, fmt.Sprintf("read-only mode or not logged in"), http.StatusForbidden) + return + } + // 100MB (github limit) var maxUploadSize int64 = 100 * 1000 * 1000 r.Body = http.MaxBytesReader(rw, r.Body, maxUploadSize) diff --git a/graphql/config/config.go b/graphql/config/config.go deleted file mode 100644 index 2e5f11b3..00000000 --- a/graphql/config/config.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package config contains configuration for GraphQL stuff. -package config - -// Config holds configuration elements. -type Config struct { - ReadOnly bool -} diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index 7c1ae66b..0ff2c3fb 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -5,7 +5,6 @@ import ( "github.com/99designs/gqlgen/client" - "github.com/MichaelMure/git-bug/graphql/config" "github.com/MichaelMure/git-bug/graphql/models" "github.com/MichaelMure/git-bug/misc/random_bugs" "github.com/MichaelMure/git-bug/repository" @@ -17,7 +16,7 @@ func TestQueries(t *testing.T) { random_bugs.FillRepoWithSeed(repo, 10, 42) - handler, err := NewHandler(repo, config.Config{}) + handler, err := NewHandler(repo) if err != nil { t.Fatal(err) } diff --git a/graphql/handler.go b/graphql/handler.go index a1be7352..55ef6fc4 100644 --- a/graphql/handler.go +++ b/graphql/handler.go @@ -8,7 +8,6 @@ import ( "github.com/99designs/gqlgen/graphql/handler" - "github.com/MichaelMure/git-bug/graphql/config" "github.com/MichaelMure/git-bug/graphql/graph" "github.com/MichaelMure/git-bug/graphql/resolvers" "github.com/MichaelMure/git-bug/repository" @@ -20,9 +19,9 @@ type Handler struct { *resolvers.RootResolver } -func NewHandler(repo repository.ClockedRepo, cfg config.Config) (Handler, error) { +func NewHandler(repo repository.ClockedRepo) (Handler, error) { h := Handler{ - RootResolver: resolvers.NewRootResolver(cfg), + RootResolver: resolvers.NewRootResolver(), } err := h.RootResolver.RegisterDefaultRepository(repo) diff --git a/graphql/resolvers/mutation.go b/graphql/resolvers/mutation.go index 80d6fb1a..62b92aaa 100644 --- a/graphql/resolvers/mutation.go +++ b/graphql/resolvers/mutation.go @@ -2,36 +2,17 @@ package resolvers import ( "context" + "errors" + "time" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/graphql/graph" "github.com/MichaelMure/git-bug/graphql/models" - "github.com/vektah/gqlparser/gqlerror" + "github.com/MichaelMure/git-bug/identity" ) -var _ graph.MutationResolver = &readonlyMutationResolver{} - -type readonlyMutationResolver struct{} - -func (readonlyMutationResolver) NewBug(_ context.Context, _ models.NewBugInput) (*models.NewBugPayload, error) { - return nil, gqlerror.Errorf("readonly mode") -} -func (readonlyMutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) { - return nil, gqlerror.Errorf("readonly mode") -} -func (readonlyMutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) { - return nil, gqlerror.Errorf("readonly mode") -} -func (readonlyMutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) { - return nil, gqlerror.Errorf("readonly mode") -} -func (readonlyMutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) { - return nil, gqlerror.Errorf("readonly mode") -} -func (readonlyMutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) { - return nil, gqlerror.Errorf("readonly mode") -} +var ErrNotAuthenticated = errors.New("not authenticated or read-only") var _ graph.MutationResolver = &mutationResolver{} @@ -47,22 +28,32 @@ func (r mutationResolver) getRepo(ref *string) (*cache.RepoCache, error) { return r.cache.DefaultRepo() } -func (r mutationResolver) getBug(repoRef *string, bugPrefix string) (*cache.BugCache, error) { +func (r mutationResolver) getBug(repoRef *string, bugPrefix string) (*cache.RepoCache, *cache.BugCache, error) { repo, err := r.getRepo(repoRef) if err != nil { - return nil, err + return nil, nil, err } - return repo.ResolveBugPrefix(bugPrefix) + bug, err := repo.ResolveBugPrefix(bugPrefix) + if err != nil { + return nil, nil, err + } + return repo, bug, nil } -func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (*models.NewBugPayload, error) { +func (r mutationResolver) NewBug(ctx context.Context, input models.NewBugInput) (*models.NewBugPayload, error) { repo, err := r.getRepo(input.RepoRef) if err != nil { return nil, err } - b, op, err := repo.NewBugWithFiles(input.Title, input.Message, input.Files) + id := identity.ForContext(ctx, repo) + if id == nil { + return nil, ErrNotAuthenticated + } + author := cache.NewIdentityCache(repo, id) + + b, op, err := repo.NewBugRaw(author, time.Now().Unix(), input.Title, input.Message, input.Files, nil) if err != nil { return nil, err } @@ -74,13 +65,19 @@ func (r mutationResolver) NewBug(_ context.Context, input models.NewBugInput) (* }, nil } -func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) { - b, err := r.getBug(input.RepoRef, input.Prefix) +func (r mutationResolver) AddComment(ctx context.Context, input models.AddCommentInput) (*models.AddCommentPayload, error) { + repo, b, err := r.getBug(input.RepoRef, input.Prefix) if err != nil { return nil, err } - op, err := b.AddCommentWithFiles(input.Message, input.Files) + id := identity.ForContext(ctx, repo) + if id == nil { + return nil, ErrNotAuthenticated + } + author := cache.NewIdentityCache(repo, id) + + op, err := b.AddCommentRaw(author, time.Now().Unix(), input.Message, input.Files, nil) if err != nil { return nil, err } @@ -97,13 +94,19 @@ func (r mutationResolver) AddComment(_ context.Context, input models.AddCommentI }, nil } -func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) { - b, err := r.getBug(input.RepoRef, input.Prefix) +func (r mutationResolver) ChangeLabels(ctx context.Context, input *models.ChangeLabelInput) (*models.ChangeLabelPayload, error) { + repo, b, err := r.getBug(input.RepoRef, input.Prefix) if err != nil { return nil, err } - results, op, err := b.ChangeLabels(input.Added, input.Removed) + id := identity.ForContext(ctx, repo) + if id == nil { + return nil, ErrNotAuthenticated + } + author := cache.NewIdentityCache(repo, id) + + results, op, err := b.ChangeLabelsRaw(author, time.Now().Unix(), input.Added, input.Removed, nil) if err != nil { return nil, err } @@ -126,13 +129,19 @@ func (r mutationResolver) ChangeLabels(_ context.Context, input *models.ChangeLa }, nil } -func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) { - b, err := r.getBug(input.RepoRef, input.Prefix) +func (r mutationResolver) OpenBug(ctx context.Context, input models.OpenBugInput) (*models.OpenBugPayload, error) { + repo, b, err := r.getBug(input.RepoRef, input.Prefix) if err != nil { return nil, err } - op, err := b.Open() + id := identity.ForContext(ctx, repo) + if id == nil { + return nil, ErrNotAuthenticated + } + author := cache.NewIdentityCache(repo, id) + + op, err := b.OpenRaw(author, time.Now().Unix(), nil) if err != nil { return nil, err } @@ -149,13 +158,19 @@ func (r mutationResolver) OpenBug(_ context.Context, input models.OpenBugInput) }, nil } -func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) { - b, err := r.getBug(input.RepoRef, input.Prefix) +func (r mutationResolver) CloseBug(ctx context.Context, input models.CloseBugInput) (*models.CloseBugPayload, error) { + repo, b, err := r.getBug(input.RepoRef, input.Prefix) if err != nil { return nil, err } - op, err := b.Close() + id := identity.ForContext(ctx, repo) + if id == nil { + return nil, ErrNotAuthenticated + } + author := cache.NewIdentityCache(repo, id) + + op, err := b.CloseRaw(author, time.Now().Unix(), nil) if err != nil { return nil, err } @@ -172,13 +187,19 @@ func (r mutationResolver) CloseBug(_ context.Context, input models.CloseBugInput }, nil } -func (r mutationResolver) SetTitle(_ context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) { - b, err := r.getBug(input.RepoRef, input.Prefix) +func (r mutationResolver) SetTitle(ctx context.Context, input models.SetTitleInput) (*models.SetTitlePayload, error) { + repo, b, err := r.getBug(input.RepoRef, input.Prefix) if err != nil { return nil, err } - op, err := b.SetTitle(input.Title) + id := identity.ForContext(ctx, repo) + if id == nil { + return nil, ErrNotAuthenticated + } + author := cache.NewIdentityCache(repo, id) + + op, err := b.SetTitleRaw(author, time.Now().Unix(), input.Title, nil) if err != nil { return nil, err } diff --git a/graphql/resolvers/repo.go b/graphql/resolvers/repo.go index e30b49f0..ded98636 100644 --- a/graphql/resolvers/repo.go +++ b/graphql/resolvers/repo.go @@ -5,16 +5,16 @@ import ( "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/graphql/config" "github.com/MichaelMure/git-bug/graphql/connections" "github.com/MichaelMure/git-bug/graphql/graph" "github.com/MichaelMure/git-bug/graphql/models" + "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/query" ) var _ graph.RepositoryResolver = &repoResolver{} -type repoResolver struct{ cfg config.Config } +type repoResolver struct{} func (repoResolver) Name(_ context.Context, obj *models.Repository) (*string, error) { name := obj.Repo.Name() @@ -150,16 +150,12 @@ func (repoResolver) Identity(_ context.Context, obj *models.Repository, prefix s return models.NewLazyIdentity(obj.Repo, excerpt), nil } -func (r repoResolver) UserIdentity(_ context.Context, obj *models.Repository) (models.IdentityWrapper, error) { - if r.cfg.ReadOnly { +func (repoResolver) UserIdentity(ctx context.Context, obj *models.Repository) (models.IdentityWrapper, error) { + id := identity.ForContext(ctx, obj.Repo) + if id == nil { return nil, nil } - excerpt, err := obj.Repo.GetUserIdentityExcerpt() - if err != nil { - return nil, err - } - - return models.NewLazyIdentity(obj.Repo, excerpt), nil + return models.NewLoadedIdentity(id), nil } func (repoResolver) ValidLabels(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) { diff --git a/graphql/resolvers/root.go b/graphql/resolvers/root.go index 214bbae3..9973ff59 100644 --- a/graphql/resolvers/root.go +++ b/graphql/resolvers/root.go @@ -3,7 +3,6 @@ package resolvers import ( "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/graphql/config" "github.com/MichaelMure/git-bug/graphql/graph" ) @@ -11,13 +10,11 @@ var _ graph.ResolverRoot = &RootResolver{} type RootResolver struct { cache.MultiRepoCache - cfg config.Config } -func NewRootResolver(cfg config.Config) *RootResolver { +func NewRootResolver() *RootResolver { return &RootResolver{ MultiRepoCache: cache.NewMultiRepoCache(), - cfg: cfg, } } @@ -28,16 +25,13 @@ func (r RootResolver) Query() graph.QueryResolver { } func (r RootResolver) Mutation() graph.MutationResolver { - if r.cfg.ReadOnly { - return &readonlyMutationResolver{} - } return &mutationResolver{ cache: &r.MultiRepoCache, } } -func (r RootResolver) Repository() graph.RepositoryResolver { - return &repoResolver{r.cfg} +func (RootResolver) Repository() graph.RepositoryResolver { + return &repoResolver{} } func (RootResolver) Bug() graph.BugResolver { @@ -56,7 +50,7 @@ func (RootResolver) Label() graph.LabelResolver { return &labelResolver{} } -func (RootResolver) Identity() graph.IdentityResolver { +func (r RootResolver) Identity() graph.IdentityResolver { return &identityResolver{} }