Skip to content

Commit 84cb588

Browse files
committed
fix(backend): update backend to use handles table
1 parent a222f24 commit 84cb588

File tree

17 files changed

+395
-126
lines changed

17 files changed

+395
-126
lines changed

pkg/backend/access.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package backend
2+
3+
import (
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.
15+
func (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.
23+
func (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
40+
func (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+
}

pkg/backend/collab.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,33 @@ func (d *Backend) AddCollaborator(ctx context.Context, repo string, username str
5050
func (d *Backend) Collaborators(ctx context.Context, repo string) ([]string, error) {
5151
repo = utils.SanitizeRepo(repo)
5252
var users []models.User
53+
var usernames []string
5354
if err := d.db.TransactionContext(ctx, func(tx *db.Tx) error {
5455
var err error
5556
users, err = d.store.ListCollabsByRepoAsUsers(ctx, tx, repo)
56-
return err
57+
if err != nil {
58+
return err
59+
}
60+
61+
ids := make([]int64, len(users))
62+
for i, u := range users {
63+
ids[i] = u.ID
64+
}
65+
66+
handles, err := d.store.ListHandlesForIDs(ctx, tx, ids)
67+
if err != nil {
68+
return err
69+
}
70+
71+
for _, h := range handles {
72+
usernames = append(usernames, h.Handle)
73+
}
74+
75+
return nil
5776
}); err != nil {
5877
return nil, db.WrapError(err)
5978
}
6079

61-
var usernames []string
62-
for _, u := range users {
63-
usernames = append(usernames, u.Username)
64-
}
65-
6680
return usernames, nil
6781
}
6882

pkg/backend/user.go

Lines changed: 42 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"strings"
77
"time"
88

9-
"github.com/charmbracelet/soft-serve/pkg/access"
109
"github.com/charmbracelet/soft-serve/pkg/db"
1110
"github.com/charmbracelet/soft-serve/pkg/db/models"
1211
"github.com/charmbracelet/soft-serve/pkg/proto"
@@ -15,91 +14,6 @@ import (
1514
"golang.org/x/crypto/ssh"
1615
)
1716

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

11226
var m models.User
11327
var pks []ssh.PublicKey
28+
var hl models.Handle
11429
if err := d.db.TransactionContext(ctx, func(tx *db.Tx) error {
11530
var err error
11631
m, err = d.store.FindUserByUsername(ctx, tx, username)
@@ -119,6 +34,11 @@ func (d *Backend) User(ctx context.Context, username string) (proto.User, error)
11934
}
12035

12136
pks, err = d.store.ListPublicKeysByUserID(ctx, tx, m.ID)
37+
if err != nil {
38+
return err
39+
}
40+
41+
hl, err = d.store.GetHandleByUserID(ctx, tx, m.ID)
12242
return err
12343
}); err != nil {
12444
err = db.WrapError(err)
@@ -132,13 +52,15 @@ func (d *Backend) User(ctx context.Context, username string) (proto.User, error)
13252
return &user{
13353
user: m,
13454
publicKeys: pks,
55+
handle: hl,
13556
}, nil
13657
}
13758

13859
// UserByID finds a user by ID.
13960
func (d *Backend) UserByID(ctx context.Context, id int64) (proto.User, error) {
14061
var m models.User
14162
var pks []ssh.PublicKey
63+
var hl models.Handle
14264
if err := d.db.TransactionContext(ctx, func(tx *db.Tx) error {
14365
var err error
14466
m, err = d.store.GetUserByID(ctx, tx, id)
@@ -147,6 +69,11 @@ func (d *Backend) UserByID(ctx context.Context, id int64) (proto.User, error) {
14769
}
14870

14971
pks, err = d.store.ListPublicKeysByUserID(ctx, tx, m.ID)
72+
if err != nil {
73+
return err
74+
}
75+
76+
hl, err = d.store.GetHandleByUserID(ctx, tx, m.ID)
15077
return err
15178
}); err != nil {
15279
err = db.WrapError(err)
@@ -160,6 +87,7 @@ func (d *Backend) UserByID(ctx context.Context, id int64) (proto.User, error) {
16087
return &user{
16188
user: m,
16289
publicKeys: pks,
90+
handle: hl,
16391
}, nil
16492
}
16593

@@ -169,6 +97,7 @@ func (d *Backend) UserByID(ctx context.Context, id int64) (proto.User, error) {
16997
func (d *Backend) UserByPublicKey(ctx context.Context, pk ssh.PublicKey) (proto.User, error) {
17098
var m models.User
17199
var pks []ssh.PublicKey
100+
var hl models.Handle
172101
if err := d.db.TransactionContext(ctx, func(tx *db.Tx) error {
173102
var err error
174103
m, err = d.store.FindUserByPublicKey(ctx, tx, pk)
@@ -177,6 +106,11 @@ func (d *Backend) UserByPublicKey(ctx context.Context, pk ssh.PublicKey) (proto.
177106
}
178107

179108
pks, err = d.store.ListPublicKeysByUserID(ctx, tx, m.ID)
109+
if err != nil {
110+
return err
111+
}
112+
113+
hl, err = d.store.GetHandleByUserID(ctx, tx, m.ID)
180114
return err
181115
}); err != nil {
182116
err = db.WrapError(err)
@@ -190,6 +124,7 @@ func (d *Backend) UserByPublicKey(ctx context.Context, pk ssh.PublicKey) (proto.
190124
return &user{
191125
user: m,
192126
publicKeys: pks,
127+
handle: hl,
193128
}, nil
194129
}
195130

@@ -198,6 +133,7 @@ func (d *Backend) UserByPublicKey(ctx context.Context, pk ssh.PublicKey) (proto.
198133
func (d *Backend) UserByAccessToken(ctx context.Context, token string) (proto.User, error) {
199134
var m models.User
200135
var pks []ssh.PublicKey
136+
var hl models.Handle
201137
token = HashToken(token)
202138

203139
if err := d.db.TransactionContext(ctx, func(tx *db.Tx) error {
@@ -216,6 +152,11 @@ func (d *Backend) UserByAccessToken(ctx context.Context, token string) (proto.Us
216152
}
217153

218154
pks, err = d.store.ListPublicKeysByUserID(ctx, tx, m.ID)
155+
if err != nil {
156+
return err
157+
}
158+
159+
hl, err = d.store.GetHandleByUserID(ctx, tx, m.ID)
219160
return err
220161
}); err != nil {
221162
err = db.WrapError(err)
@@ -229,6 +170,7 @@ func (d *Backend) UserByAccessToken(ctx context.Context, token string) (proto.Us
229170
return &user{
230171
user: m,
231172
publicKeys: pks,
173+
handle: hl,
232174
}, nil
233175
}
234176

@@ -243,8 +185,18 @@ func (d *Backend) Users(ctx context.Context) ([]string, error) {
243185
return err
244186
}
245187

246-
for _, m := range ms {
247-
users = append(users, m.Username)
188+
ids := make([]int64, len(ms))
189+
for i, m := range ms {
190+
ids[i] = m.ID
191+
}
192+
193+
handles, err := d.store.ListHandlesForIDs(ctx, tx, ids)
194+
if err != nil {
195+
return err
196+
}
197+
198+
for _, h := range handles {
199+
users = append(users, h.Handle)
248200
}
249201

250202
return nil
@@ -391,6 +343,7 @@ func (d *Backend) SetPassword(ctx context.Context, username string, rawPassword
391343
type user struct {
392344
user models.User
393345
publicKeys []ssh.PublicKey
346+
handle models.Handle
394347
}
395348

396349
var _ proto.User = (*user)(nil)
@@ -407,7 +360,7 @@ func (u *user) PublicKeys() []ssh.PublicKey {
407360

408361
// Username implements proto.User
409362
func (u *user) Username() string {
410-
return u.user.Username
363+
return u.handle.Handle
411364
}
412365

413366
// ID implements proto.User.

pkg/db/models/repo.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Repo struct {
1515
Mirror bool `db:"mirror"`
1616
Hidden bool `db:"hidden"`
1717
UserID sql.NullInt64 `db:"user_id"`
18+
OrgID sql.NullInt64 `db:"org_id"`
1819
CreatedAt time.Time `db:"created_at"`
1920
UpdatedAt time.Time `db:"updated_at"`
2021
}

0 commit comments

Comments
 (0)