cmd.go

  1package cmd
  2
  3import (
  4	"context"
  5	"errors"
  6	"fmt"
  7	"io/fs"
  8	"os"
  9
 10	"github.com/charmbracelet/soft-serve/pkg/access"
 11	"github.com/charmbracelet/soft-serve/pkg/backend"
 12	"github.com/charmbracelet/soft-serve/pkg/config"
 13	"github.com/charmbracelet/soft-serve/pkg/db"
 14	"github.com/charmbracelet/soft-serve/pkg/hooks"
 15	"github.com/charmbracelet/soft-serve/pkg/proto"
 16	"github.com/charmbracelet/soft-serve/pkg/sshutils"
 17	"github.com/charmbracelet/soft-serve/pkg/store"
 18	"github.com/charmbracelet/soft-serve/pkg/store/database"
 19	"github.com/spf13/cobra"
 20)
 21
 22// InitBackendContext initializes the backend context.
 23// When a public-key is provided via the "SOFT_SERVE_PUBLIC_KEY" environment
 24// variable, it will be used to try to find the corresponding user in the
 25// database and set the user in the context.
 26func InitBackendContext(cmd *cobra.Command, _ []string) error {
 27	ctx := cmd.Context()
 28	cfg := config.FromContext(ctx)
 29	if _, err := os.Stat(cfg.DataPath); errors.Is(err, fs.ErrNotExist) {
 30		if err := os.MkdirAll(cfg.DataPath, os.ModePerm); err != nil {
 31			return fmt.Errorf("create data directory: %w", err)
 32		}
 33	}
 34	dbx, err := db.Open(ctx, cfg.DB.Driver, cfg.DB.DataSource)
 35	if err != nil {
 36		return fmt.Errorf("open database: %w", err)
 37	}
 38
 39	ctx = db.WithContext(ctx, dbx)
 40	dbstore := database.New(ctx, dbx)
 41	ctx = store.WithContext(ctx, dbstore)
 42	be := backend.New(ctx, cfg, dbx)
 43	ctx = backend.WithContext(ctx, be)
 44
 45	// Store user in context if public key is provided
 46	// via environment variable.
 47	if ak, ok := os.LookupEnv("SOFT_SERVE_PUBLIC_KEY"); ok {
 48		pk, _, err := sshutils.ParseAuthorizedKey(ak)
 49		if err == nil && pk != nil {
 50			user, err := be.UserByPublicKey(ctx, pk)
 51			if err == nil && user != nil {
 52				ctx = proto.WithUserContext(ctx, user)
 53			}
 54		}
 55	}
 56
 57	cmd.SetContext(ctx)
 58
 59	return nil
 60}
 61
 62// CloseDBContext closes the database context.
 63func CloseDBContext(cmd *cobra.Command, _ []string) error {
 64	ctx := cmd.Context()
 65	dbx := db.FromContext(ctx)
 66	if dbx != nil {
 67		if err := dbx.Close(); err != nil {
 68			return fmt.Errorf("close database: %w", err)
 69		}
 70	}
 71
 72	return nil
 73}
 74
 75// InitializeHooks initializes the hooks.
 76func InitializeHooks(ctx context.Context, cfg *config.Config, be *backend.Backend) error {
 77	repos, err := be.Repositories(ctx)
 78	if err != nil {
 79		return err
 80	}
 81
 82	for _, repo := range repos {
 83		if err := hooks.GenerateHooks(ctx, cfg, repo.Name()); err != nil {
 84			return err
 85		}
 86	}
 87
 88	return nil
 89}
 90
 91// CheckUserHasAccess checks if the user in context has access to the repository.
 92// If there is no user in context, it will skip the check and return true.
 93// It won't skip this check if the "strict" flag is set to true.
 94func CheckUserHasAccess(cmd *cobra.Command, repo string, level access.AccessLevel) bool {
 95	ctx := cmd.Context()
 96	isStrict := cmd.Flag("strict").Value.String() == "true"
 97	if _, ok := os.LookupEnv("SOFT_SERVE_PUBLIC_KEY"); !ok && isStrict {
 98		return false
 99	}
100
101	user := proto.UserFromContext(ctx)
102	be := backend.FromContext(ctx)
103	al := be.AccessLevelForUser(ctx, repo, user)
104	return al >= level
105}