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}