Skip to content

Commit

Permalink
feat: store repo meta data in repo directory
Browse files Browse the repository at this point in the history
- Store auth'd user in context.
- Write description, owner, and git-daemon-export-ok files.

Fixes: #255
Fixes: #256
  • Loading branch information
aymanbagabas committed Jul 17, 2023
1 parent e398b4d commit cbd5025
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 31 deletions.
29 changes: 26 additions & 3 deletions server/backend/context.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package backend

import "context"
import (
"context"

// ContextKey is the key for the backend in the context.
var ContextKey = &struct{ string }{"backend"}
"github.com/charmbracelet/soft-serve/server/proto"
)

var (
// ContextKey is the key for the backend in the context.
ContextKey = &struct{ string }{"backend"}

// ContextKeyUser is the key for the user in the context.
ContextKeyUser = &struct{ string }{"user"}
)

// FromContext returns the backend from a context.
func FromContext(ctx context.Context) *Backend {
Expand All @@ -18,3 +27,17 @@ func FromContext(ctx context.Context) *Backend {
func WithContext(ctx context.Context, b *Backend) context.Context {
return context.WithValue(ctx, ContextKey, b)
}

// UserFromContext returns the user from a context.
func UserFromContext(ctx context.Context) proto.User {
if u, ok := ctx.Value(ContextKeyUser).(proto.User); ok {
return u
}

return nil

Check warning on line 37 in server/backend/context.go

View check run for this annotation

Codecov / codecov/patch

server/backend/context.go#L37

Added line #L37 was not covered by tests
}

// WithUserContext returns a new context with the user attached.
func WithUserContext(ctx context.Context, u proto.User) context.Context {
return context.WithValue(ctx, ContextKeyUser, u)

Check warning on line 42 in server/backend/context.go

View check run for this annotation

Codecov / codecov/patch

server/backend/context.go#L41-L42

Added lines #L41 - L42 were not covered by tests
}
50 changes: 42 additions & 8 deletions server/backend/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"time"
Expand Down Expand Up @@ -53,6 +54,25 @@ func (d *Backend) CreateRepository(ctx context.Context, name string, opts proto.
return err
}

if err := os.WriteFile(filepath.Join(rp, "description"), []byte(opts.Description), fs.ModePerm); err != nil {
d.logger.Error("failed to write description", "repo", name, "err", err)
return err

Check failure on line 59 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.WriteFile(name string, data []byte, perm io/fs.FileMode) error (wrapcheck)
}

Check warning on line 60 in server/backend/repo.go

View check run for this annotation

Codecov / codecov/patch

server/backend/repo.go#L58-L60

Added lines #L58 - L60 were not covered by tests

if !opts.Private {
if err := os.WriteFile(filepath.Join(rp, "git-daemon-export-ok"), []byte{}, fs.ModePerm); err != nil {
d.logger.Error("failed to write git-daemon-export-ok", "repo", name, "err", err)
return err

Check failure on line 65 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.WriteFile(name string, data []byte, perm io/fs.FileMode) error (wrapcheck)
}

Check warning on line 66 in server/backend/repo.go

View check run for this annotation

Codecov / codecov/patch

server/backend/repo.go#L64-L66

Added lines #L64 - L66 were not covered by tests
}

if user := UserFromContext(ctx); user != nil {
if err := os.WriteFile(filepath.Join(rp, "owner"), []byte(user.Username()), fs.ModePerm); err != nil {
d.logger.Error("failed to write owner", "repo", name, "err", err)
return err

Check failure on line 72 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.WriteFile(name string, data []byte, perm io/fs.FileMode) error (wrapcheck)
}

Check warning on line 73 in server/backend/repo.go

View check run for this annotation

Codecov / codecov/patch

server/backend/repo.go#L71-L73

Added lines #L71 - L73 were not covered by tests
}

return hooks.GenerateHooks(ctx, d.cfg, repo)
}); err != nil {
d.logger.Debug("failed to create repository in database", "err", err)
Expand Down Expand Up @@ -341,29 +361,43 @@ func (d *Backend) SetHidden(ctx context.Context, name string, hidden bool) error
// SetDescription sets the description of a repository.
//
// It implements backend.Backend.
func (d *Backend) SetDescription(ctx context.Context, repo string, desc string) error {
repo = utils.SanitizeRepo(repo)
func (d *Backend) SetDescription(ctx context.Context, name string, desc string) error {
name = utils.SanitizeRepo(name)
rp := filepath.Join(d.reposPath(), name+".git")

// Delete cache
d.cache.Delete(repo)
d.cache.Delete(name)

return d.db.TransactionContext(ctx, func(tx *db.Tx) error {
return d.store.SetRepoDescriptionByName(ctx, tx, repo, desc)
if err := os.WriteFile(filepath.Join(rp, "description"), []byte(desc), fs.ModePerm); err != nil {
d.logger.Error("failed to write description", "repo", name, "err", err)
return err

Check failure on line 374 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.WriteFile(name string, data []byte, perm io/fs.FileMode) error (wrapcheck)
}

Check warning on line 375 in server/backend/repo.go

View check run for this annotation

Codecov / codecov/patch

server/backend/repo.go#L373-L375

Added lines #L373 - L375 were not covered by tests

return d.store.SetRepoDescriptionByName(ctx, tx, name, desc)

Check failure on line 377 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from interface method should be wrapped: sig: func (github.com/charmbracelet/soft-serve/server/store.RepositoryStore).SetRepoDescriptionByName(ctx context.Context, tx *github.com/charmbracelet/soft-serve/server/db.Tx, name string, description string) error (wrapcheck)
})
}

// SetPrivate sets the private flag of a repository.
//
// It implements backend.Backend.
func (d *Backend) SetPrivate(ctx context.Context, repo string, private bool) error {
repo = utils.SanitizeRepo(repo)
func (d *Backend) SetPrivate(ctx context.Context, name string, private bool) error {
name = utils.SanitizeRepo(name)
rp := filepath.Join(d.reposPath(), name+".git")

// Delete cache
d.cache.Delete(repo)
d.cache.Delete(name)

return db.WrapError(
d.db.TransactionContext(ctx, func(tx *db.Tx) error {
return d.store.SetRepoIsPrivateByName(ctx, tx, repo, private)
if !private {
if err := os.WriteFile(filepath.Join(rp, "git-daemon-export-ok"), []byte{}, fs.ModePerm); err != nil {
d.logger.Error("failed to write git-daemon-export-ok", "repo", name, "err", err)
return err

Check failure on line 396 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func os.WriteFile(name string, data []byte, perm io/fs.FileMode) error (wrapcheck)
}

Check warning on line 397 in server/backend/repo.go

View check run for this annotation

Codecov / codecov/patch

server/backend/repo.go#L394-L397

Added lines #L394 - L397 were not covered by tests
}

return d.store.SetRepoIsPrivateByName(ctx, tx, name, private)

Check failure on line 400 in server/backend/repo.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from interface method should be wrapped: sig: func (github.com/charmbracelet/soft-serve/server/store.RepositoryStore).SetRepoIsPrivateByName(ctx context.Context, tx *github.com/charmbracelet/soft-serve/server/db.Tx, name string, isPrivate bool) error (wrapcheck)
}),
)
}
Expand Down
48 changes: 29 additions & 19 deletions server/backend/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,36 @@ import (
//
// It implements backend.Backend.
func (d *Backend) AccessLevel(ctx context.Context, repo string, username string) access.AccessLevel {
anon := d.AnonAccess(ctx)
user, _ := d.User(ctx, username)
return d.AccessLevelForUser(ctx, repo, user)
}

// AccessLevelByPublicKey returns the access level of a user's public key for a repository.
//
// It implements backend.Backend.
func (d *Backend) AccessLevelByPublicKey(ctx context.Context, repo string, pk ssh.PublicKey) access.AccessLevel {
for _, k := range d.cfg.AdminKeys() {
if sshutils.KeysEqual(pk, k) {
return access.AdminAccess
}
}

user, _ := d.UserByPublicKey(ctx, pk)
if user != nil {
return d.AccessLevel(ctx, repo, user.Username())
}

return d.AccessLevel(ctx, repo, "")
}

// AccessLevelForUser returns the access level for the given user for a repository.
func (d *Backend) AccessLevelForUser(ctx context.Context, repo string, user proto.User) access.AccessLevel {
var username string
if user != nil {
username = user.Username()
}

anon := d.AnonAccess(ctx)
// If the user is an admin, they have admin access.
if user != nil && user.IsAdmin() {
return access.AdminAccess
Expand Down Expand Up @@ -58,24 +86,6 @@ func (d *Backend) AccessLevel(ctx context.Context, repo string, username string)
return anon
}

// AccessLevelByPublicKey returns the access level of a user's public key for a repository.
//
// It implements backend.Backend.
func (d *Backend) AccessLevelByPublicKey(ctx context.Context, repo string, pk ssh.PublicKey) access.AccessLevel {
for _, k := range d.cfg.AdminKeys() {
if sshutils.KeysEqual(pk, k) {
return access.AdminAccess
}
}

user, _ := d.UserByPublicKey(ctx, pk)
if user != nil {
return d.AccessLevel(ctx, repo, user.Username())
}

return d.AccessLevel(ctx, repo, "")
}

// User finds a user by username.
//
// It implements backend.Backend.
Expand Down
7 changes: 6 additions & 1 deletion server/ssh/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,12 @@ func (s *SSHServer) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) (allowed
publicKeyCounter.WithLabelValues(strconv.FormatBool(*allowed)).Inc()
}(&allowed)

ac := s.be.AccessLevelByPublicKey(ctx, "", pk)
user, _ := s.be.UserByPublicKey(ctx, pk)
if user != nil {
ctx.SetValue(backend.ContextKeyUser, user)
}

ac := s.be.AccessLevelForUser(ctx, "", user)
s.logger.Debugf("access level for %q: %s", ak, ac)
allowed = ac >= access.ReadWriteAccess
return
Expand Down

0 comments on commit cbd5025

Please sign in to comment.