access.go

 1package backend
 2
 3import (
 4	"context"
 5
 6	"github.com/charmbracelet/soft-serve/pkg/access"
 7	"github.com/charmbracelet/soft-serve/pkg/proto"
 8	"github.com/charmbracelet/soft-serve/pkg/sshutils"
 9	"golang.org/x/crypto/ssh"
10)
11
12// AccessLevel returns the access level of a user for a repository.
13//
14// It implements backend.Backend.
15func (d *Backend) AccessLevel(ctx context.Context, repo string, username string) access.AccessLevel {
16	user, _ := d.User(ctx, username)
17	return d.AccessLevelForUser(ctx, repo, user)
18}
19
20// AccessLevelByPublicKey returns the access level of a user's public key for a repository.
21//
22// It implements backend.Backend.
23func (d *Backend) AccessLevelByPublicKey(ctx context.Context, repo string, pk ssh.PublicKey) access.AccessLevel {
24	for _, k := range d.cfg.AdminKeys() {
25		if sshutils.KeysEqual(pk, k) {
26			return access.AdminAccess
27		}
28	}
29
30	user, _ := d.UserByPublicKey(ctx, pk)
31	if user != nil {
32		return d.AccessLevel(ctx, repo, user.Username())
33	}
34
35	return d.AccessLevel(ctx, repo, "")
36}
37
38// AccessLevelForUser returns the access level of a user for a repository.
39// TODO: user repository ownership
40func (d *Backend) AccessLevelForUser(ctx context.Context, repo string, user proto.User) access.AccessLevel {
41	var username string
42	anon := d.AnonAccess(ctx)
43	if user != nil {
44		username = user.Username()
45	}
46
47	// If the user is an admin, they have admin access.
48	if user != nil && user.IsAdmin() {
49		return access.AdminAccess
50	}
51
52	// If the repository exists, check if the user is a collaborator.
53	r := proto.RepositoryFromContext(ctx)
54	if r == nil {
55		r, _ = d.Repository(ctx, repo)
56	}
57
58	if r != nil {
59		if user != nil {
60			// If the user is the owner, they have admin access.
61			if r.UserID() == user.ID() {
62				return access.AdminAccess
63			}
64		}
65
66		// If the user is a collaborator, they have return their access level.
67		collabAccess, isCollab, _ := d.IsCollaborator(ctx, repo, username)
68		if isCollab {
69			if anon > collabAccess {
70				return anon
71			}
72			return collabAccess
73		}
74
75		// If the repository is private, the user has no access.
76		if r.IsPrivate() {
77			return access.NoAccess
78		}
79
80		// Otherwise, the user has read-only access.
81		return access.ReadOnlyAccess
82	}
83
84	if user != nil {
85		// If the repository doesn't exist, the user has read/write access.
86		if anon > access.ReadWriteAccess {
87			return anon
88		}
89
90		return access.ReadWriteAccess
91	}
92
93	// If the user doesn't exist, give them the anonymous access level.
94	return anon
95}