Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: lfs http and jwt tests #351

Merged
merged 2 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 2 additions & 41 deletions cmd/soft/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ import (
"io/fs"
"os"
"runtime/debug"
"strings"
"time"

"github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/db"
logr "github.com/charmbracelet/soft-serve/server/log"
"github.com/charmbracelet/soft-serve/server/store"
"github.com/charmbracelet/soft-serve/server/store/database"
_ "github.com/lib/pq" // postgres driver
Expand Down Expand Up @@ -78,7 +77,7 @@ func main() {
}

ctx = config.WithContext(ctx, cfg)
logger, f, err := newDefaultLogger(cfg)
logger, f, err := logr.NewLogger(cfg)
if err != nil {
log.Errorf("failed to create logger: %v", err)
}
Expand Down Expand Up @@ -107,44 +106,6 @@ func main() {
}
}

// newDefaultLogger returns a new logger with default settings.
func newDefaultLogger(cfg *config.Config) (*log.Logger, *os.File, error) {
logger := log.NewWithOptions(os.Stderr, log.Options{
ReportTimestamp: true,
TimeFormat: time.DateOnly,
})

switch {
case config.IsVerbose():
logger.SetReportCaller(true)
fallthrough
case config.IsDebug():
logger.SetLevel(log.DebugLevel)
}

logger.SetTimeFormat(cfg.Log.TimeFormat)

switch strings.ToLower(cfg.Log.Format) {
case "json":
logger.SetFormatter(log.JSONFormatter)
case "logfmt":
logger.SetFormatter(log.LogfmtFormatter)
case "text":
logger.SetFormatter(log.TextFormatter)
}

var f *os.File
if cfg.Log.Path != "" {
f, err := os.OpenFile(cfg.Log.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return nil, nil, err
}
logger.SetOutput(f)
}

return logger, f, nil
}

func initBackendContext(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
cfg := config.FromContext(ctx)
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ require (
github.com/gobwas/glob v0.2.3
github.com/gogs/git-module v1.8.2
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/hashicorp/golang-lru/v2 v2.0.4
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.9
Expand All @@ -41,7 +43,6 @@ require (
github.com/rubyist/tracerx v0.0.0-20170927163412-787959303086
github.com/spf13/cobra v1.7.0
go.uber.org/automaxprocs v1.5.3
goji.io v2.0.2+incompatible
golang.org/x/crypto v0.11.0
golang.org/x/sync v0.3.0
gopkg.in/yaml.v3 v3.0.1
Expand All @@ -58,6 +59,7 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
Expand Down Expand Up @@ -80,6 +82,10 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hashicorp/golang-lru/v2 v2.0.4 h1:7GHuZcgid37q8o5i3QI9KMT4nCWQQ3Kx3Ov6bb9MfK0=
github.com/hashicorp/golang-lru/v2 v2.0.4/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand Down Expand Up @@ -192,8 +198,6 @@ github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18W
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
goji.io v2.0.2+incompatible h1:uIssv/elbKRLznFUy3Xj4+2Mz/qKhek/9aZQDUMae7c=
goji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
Expand Down
38 changes: 38 additions & 0 deletions server/backend/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package backend

import "testing"

func TestHashPassword(t *testing.T) {
hash, err := HashPassword("password")
if err != nil {
t.Fatal(err)
}
if hash == "" {
t.Fatal("hash is empty")
}
}

func TestVerifyPassword(t *testing.T) {
hash, err := HashPassword("password")
if err != nil {
t.Fatal(err)
}
if !VerifyPassword("password", hash) {
t.Fatal("password did not verify")
}
}

func TestGenerateToken(t *testing.T) {
token := GenerateToken()
if token == "" {
t.Fatal("token is empty")
}
}

func TestHashToken(t *testing.T) {
token := GenerateToken()
hash := HashToken(token)
if hash == "" {
t.Fatal("hash is empty")
}
}
19 changes: 19 additions & 0 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@
DataSource string `env:"DATA_SOURCE" yaml:"data_source"`
}

// LFSConfig is the configuration for Git LFS.
type LFSConfig struct {
// Enabled is whether or not Git LFS is enabled.
Enabled bool `env:"ENABLED" yaml:"enabled"`

// SSHEnabled is whether or not Git LFS over SSH is enabled.
// This is only used if LFS is enabled.
SSHEnabled bool `env:"SSH_ENABLED" yaml:"ssh_enabled"`
}

// Config is the configuration for Soft Serve.
type Config struct {
// Name is the name of the server.
Expand All @@ -118,6 +128,9 @@
// DB is the database configuration.
DB DBConfig `envPrefix:"DB_" yaml:"db"`

// LFS is the configuration for Git LFS.
LFS LFSConfig `envPrefix:"LFS_" yaml:"lfs"`

// InitialAdminKeys is a list of public keys that will be added to the list of admins.
InitialAdminKeys []string `env:"INITIAL_ADMIN_KEYS" envSeparator:"\n" yaml:"initial_admin_keys"`

Expand All @@ -132,7 +145,7 @@
return envs
}

// TODO: do this dynamically

Check failure on line 148 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

server/config/config.go:148: Line contains TODO/BUG/FIXME: "TODO: do this dynamically" (godox)
envs = append(envs, []string{
fmt.Sprintf("SOFT_SERVE_DATA_PATH=%s", c.DataPath),
fmt.Sprintf("SOFT_SERVE_NAME=%s", c.Name),
Expand All @@ -156,6 +169,8 @@
fmt.Sprintf("SOFT_SERVE_LOG_TIME_FORMAT=%s", c.Log.TimeFormat),
fmt.Sprintf("SOFT_SERVE_DB_DRIVER=%s", c.DB.Driver),
fmt.Sprintf("SOFT_SERVE_DB_DATA_SOURCE=%s", c.DB.DataSource),
fmt.Sprintf("SOFT_SERVE_LFS_ENABLED=%t", c.LFS.Enabled),
fmt.Sprintf("SOFT_SERVE_LFS_SSH_ENABLED=%t", c.LFS.SSHEnabled),
}...)

return envs
Expand All @@ -179,7 +194,7 @@
func parseFile(cfg *Config, path string) error {
f, err := os.Open(path)
if err != nil {
return err

Check failure on line 197 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.Open(name string) (*os.File, error) (wrapcheck)
}

defer f.Close() // nolint: errcheck
Expand Down Expand Up @@ -235,9 +250,9 @@
// writeConfig writes the configuration to the given file.
func writeConfig(cfg *Config, path string) error {
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return err

Check failure on line 253 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.MkdirAll(path string, perm io/fs.FileMode) error (wrapcheck)
}
return os.WriteFile(path, []byte(newConfigFile(cfg)), 0o644) // nolint: errcheck, gosec

Check failure on line 255 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

mnd: Magic number: 0o644, in <argument> detected (gomnd)
}

// WriteConfig writes the configuration to the default file.
Expand Down Expand Up @@ -285,13 +300,13 @@
KeyPath: filepath.Join("ssh", "soft_serve_host_ed25519"),
ClientKeyPath: filepath.Join("ssh", "soft_serve_client_ed25519"),
MaxTimeout: 0,
IdleTimeout: 10 * 60, // 10 minutes

Check failure on line 303 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

mnd: Magic number: 10, in <assign> detected (gomnd)
},
Git: GitConfig{
ListenAddr: ":9418",
MaxTimeout: 0,
IdleTimeout: 3,

Check failure on line 308 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

mnd: Magic number: 3, in <assign> detected (gomnd)
MaxConnections: 32,

Check failure on line 309 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

mnd: Magic number: 32, in <assign> detected (gomnd)
},
HTTP: HTTPConfig{
ListenAddr: ":23232",
Expand All @@ -309,6 +324,10 @@
DataSource: "soft-serve.db" +
"?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)",
},
LFS: LFSConfig{
Enabled: true,
SSHEnabled: true,
},
}
}

Expand All @@ -319,7 +338,7 @@
if !filepath.IsAbs(c.DataPath) {
dp, err := filepath.Abs(c.DataPath)
if err != nil {
return err

Check failure on line 341 in server/config/config.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func path/filepath.Abs(path string) (string, error) (wrapcheck)
}
c.DataPath = dp
}
Expand Down
2 changes: 1 addition & 1 deletion server/config/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ func FromContext(ctx context.Context) *Config {
return c
}

return DefaultConfig()
return nil
}
7 changes: 7 additions & 0 deletions server/config/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ db:
# This is driver specific and can be a file path or connection string.
data_source: "{{ .DB.DataSource }}"

# Git LFS configuration.
lfs:
# Enable Git LFS.
enabled: {{ .LFS.Enabled }}
# Enable Git SSH transfer.
ssh_enabled: {{ .LFS.SSHEnabled }}

# Additional admin keys.
#initial_admin_keys:
# - "ssh-rsa AAAAB3NzaC1yc2..."
Expand Down
56 changes: 56 additions & 0 deletions server/git/git_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package git

import (
"bytes"
"fmt"
"testing"
)

func TestPktline(t *testing.T) {
cases := []struct {
name string
in []byte
err error
out []byte
}{
{
name: "empty",
in: []byte{},
out: []byte("0005\n0000"),
},
{
name: "simple",
in: []byte("hello"),
out: []byte("000ahello\n0000"),
},
{
name: "newline",
in: []byte("hello\n"),
out: []byte("000bhello\n\n0000"),
},
{
name: "error",
err: fmt.Errorf("foobar"),
out: []byte("000fERR foobar\n0000"),
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var out bytes.Buffer
if c.err == nil {
if err := WritePktline(&out, string(c.in)); err != nil {
t.Fatal(err)
}
} else {
if err := WritePktlineErr(&out, c.err); err != nil {
t.Fatal(err)
}
}

if !bytes.Equal(out.Bytes(), c.out) {
t.Errorf("expected %q, got %q", c.out, out.Bytes())
}
})
}
}
95 changes: 95 additions & 0 deletions server/lfs/pointer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package lfs

import (
"errors"
"strconv"
"strings"
"testing"
)

func TestReadPointer(t *testing.T) {
cases := []struct {
name string
content string
want Pointer
wantErr error
wantErrp interface{}
}{
{
name: "valid pointer",
content: `version https://git-lfs.github.com/spec/v1
oid sha256:1234567890123456789012345678901234567890123456789012345678901234
size 1234
`,
want: Pointer{
Oid: "1234567890123456789012345678901234567890123456789012345678901234",
Size: 1234,
},
},
{
name: "invalid prefix",
content: `version https://foobar/spec/v2
oid sha256:1234567890123456789012345678901234567890123456789012345678901234
size 1234
`,
wantErr: ErrMissingPrefix,
},
{
name: "invalid oid",
content: `version https://git-lfs.github.com/spec/v1
oid sha256:&2345a78$012345678901234567890123456789012345678901234567890123
size 1234
`,
wantErr: ErrInvalidOIDFormat,
},
{
name: "invalid size",
content: `version https://git-lfs.github.com/spec/v1
oid sha256:1234567890123456789012345678901234567890123456789012345678901234
size abc
`,
wantErrp: &strconv.NumError{},
},
{
name: "invalid structure",
content: `version https://git-lfs.github.com/spec/v1
`,
wantErr: ErrInvalidStructure,
},
{
name: "empty pointer",
wantErr: ErrMissingPrefix,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
p, err := ReadPointerFromBuffer([]byte(tc.content))
if err != tc.wantErr && !errors.As(err, &tc.wantErrp) {
t.Errorf("ReadPointerFromBuffer() error = %v(%T), wantErr %v(%T)", err, err, tc.wantErr, tc.wantErr)
return
}
if err != nil {
return
}

if err == nil {
if !p.IsValid() {
t.Errorf("Expected a valid pointer")
return
}
if p.Oid != strings.ReplaceAll(p.RelativePath(), "/", "") {
t.Errorf("Expected oid to be the relative path without slashes")
return
}
}

if p.Oid != tc.want.Oid {
t.Errorf("ReadPointerFromBuffer() oid = %v, want %v", p.Oid, tc.want.Oid)
}
if p.Size != tc.want.Size {
t.Errorf("ReadPointerFromBuffer() size = %v, want %v", p.Size, tc.want.Size)
}
})
}
}
Loading
Loading