2366f903b743e11f79eb2a9e25abcf54aff4f0f9
- Author
- Ayman Bagabas <ayman.bagabas@gmail.com>
- Committer
- Ayman Bagabas <ayman.bagabas@gmail.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/server/backend/sqlite/sqlite.go b/server/backend/sqlite/sqlite.go
2index a1160d6ebf421bd6972e04a19e140766f425fa57..263885b492c9cb28a2e699de2d4cd0493011a7cb 100644
3--- a/server/backend/sqlite/sqlite.go
4+++ b/server/backend/sqlite/sqlite.go
5@@ -7,6 +7,7 @@ import (
6 "os"
7 "path/filepath"
8 "strconv"
9+ "strings"
10 "text/template"
11
12 "github.com/charmbracelet/log"
13@@ -410,6 +411,11 @@ func (d *SqliteBackend) SetProjectName(repo string, name string) error {
14 //
15 // It implements backend.Backend.
16 func (d *SqliteBackend) AddCollaborator(repo string, username string) error {
17+ username = strings.ToLower(username)
18+ if err := utils.ValidateUsername(username); err != nil {
19+ return err
20+ }
21+
22 repo = utils.SanitizeRepo(repo)
23 return wrapDbErr(wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
24 _, err := tx.Exec(`INSERT INTO collab (user_id, repo_id, updated_at)
25diff --git a/server/backend/sqlite/user.go b/server/backend/sqlite/user.go
26index fa5cff166f27e8bea24772942594b9c8d2691f10..d03410277ae45f60457eb48521198ff34f3646a5 100644
27--- a/server/backend/sqlite/user.go
28+++ b/server/backend/sqlite/user.go
29@@ -2,8 +2,10 @@ package sqlite
30
31 import (
32 "context"
33+ "strings"
34
35 "github.com/charmbracelet/soft-serve/server/backend"
36+ "github.com/charmbracelet/soft-serve/server/utils"
37 "github.com/jmoiron/sqlx"
38 "golang.org/x/crypto/ssh"
39 )
40@@ -136,6 +138,11 @@ func (d *SqliteBackend) AccessLevelByPublicKey(repo string, pk ssh.PublicKey) ba
41 //
42 // It implements backend.Backend.
43 func (d *SqliteBackend) AddPublicKey(username string, pk ssh.PublicKey) error {
44+ username = strings.ToLower(username)
45+ if err := utils.ValidateUsername(username); err != nil {
46+ return err
47+ }
48+
49 return wrapDbErr(
50 wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
51 var userID int
52@@ -154,16 +161,16 @@ func (d *SqliteBackend) AddPublicKey(username string, pk ssh.PublicKey) error {
53 //
54 // It implements backend.Backend.
55 func (d *SqliteBackend) CreateUser(username string, opts backend.UserOptions) (backend.User, error) {
56+ username = strings.ToLower(username)
57+ if err := utils.ValidateUsername(username); err != nil {
58+ return nil, err
59+ }
60+
61 var user *User
62 if err := wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
63- into := "INSERT INTO user (username"
64- values := "VALUES (?"
65- args := []interface{}{username}
66- if opts.Admin {
67- into += ", admin"
68- values += ", ?"
69- args = append(args, opts.Admin)
70- }
71+ into := "INSERT INTO user (username, admin"
72+ values := "VALUES (?, ?"
73+ args := []interface{}{username, opts.Admin}
74 into += ", updated_at)"
75 values += ", CURRENT_TIMESTAMP)"
76
77@@ -202,6 +209,11 @@ func (d *SqliteBackend) CreateUser(username string, opts backend.UserOptions) (b
78 //
79 // It implements backend.Backend.
80 func (d *SqliteBackend) DeleteUser(username string) error {
81+ username = strings.ToLower(username)
82+ if err := utils.ValidateUsername(username); err != nil {
83+ return err
84+ }
85+
86 return wrapDbErr(
87 wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
88 _, err := tx.Exec("DELETE FROM user WHERE username = ?", username)
89@@ -226,6 +238,11 @@ func (d *SqliteBackend) RemovePublicKey(username string, pk ssh.PublicKey) error
90
91 // ListPublicKeys lists the public keys of a user.
92 func (d *SqliteBackend) ListPublicKeys(username string) ([]ssh.PublicKey, error) {
93+ username = strings.ToLower(username)
94+ if err := utils.ValidateUsername(username); err != nil {
95+ return nil, err
96+ }
97+
98 keys := make([]ssh.PublicKey, 0)
99 if err := wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
100 var keyStrings []string
101@@ -256,6 +273,11 @@ func (d *SqliteBackend) ListPublicKeys(username string) ([]ssh.PublicKey, error)
102 //
103 // It implements backend.Backend.
104 func (d *SqliteBackend) SetUsername(username string, newUsername string) error {
105+ username = strings.ToLower(username)
106+ if err := utils.ValidateUsername(username); err != nil {
107+ return err
108+ }
109+
110 return wrapDbErr(
111 wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
112 _, err := tx.Exec("UPDATE user SET username = ? WHERE username = ?", newUsername, username)
113@@ -268,6 +290,11 @@ func (d *SqliteBackend) SetUsername(username string, newUsername string) error {
114 //
115 // It implements backend.Backend.
116 func (d *SqliteBackend) SetAdmin(username string, admin bool) error {
117+ username = strings.ToLower(username)
118+ if err := utils.ValidateUsername(username); err != nil {
119+ return err
120+ }
121+
122 return wrapDbErr(
123 wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
124 _, err := tx.Exec("UPDATE user SET admin = ? WHERE username = ?", admin, username)
125@@ -280,6 +307,11 @@ func (d *SqliteBackend) SetAdmin(username string, admin bool) error {
126 //
127 // It implements backend.Backend.
128 func (d *SqliteBackend) User(username string) (backend.User, error) {
129diff --git a/server/cmd/user.go b/server/cmd/user.go
130index 364f54b3c7c7ac0937523c49ca04e017e123671e..a2845d6311e378060b6568d6b9eb13d13e353a0c 100644
131--- a/server/cmd/user.go
132+++ b/server/cmd/user.go
133@@ -25,19 +25,24 @@ func userCommand() *cobra.Command {
134 Args: cobra.ExactArgs(1),
135 PersistentPreRunE: checkIfAdmin,
136 RunE: func(cmd *cobra.Command, args []string) error {
137+ var pubkeys []ssh.PublicKey
138 cfg, _ := fromContext(cmd)
139 username := args[0]
140- pk, _, err := backend.ParseAuthorizedKey(key)
141- if err != nil {
142- return err
143+ if key != "" {
144+ pk, _, err := backend.ParseAuthorizedKey(key)
145+ if err != nil {
146+ return err
147+ }
148+
149+ pubkeys = []ssh.PublicKey{pk}
150 }
151
152 opts := backend.UserOptions{
153 Admin: admin,
154- PublicKeys: []ssh.PublicKey{pk},
155+ PublicKeys: pubkeys,
156 }
157
158- _, err = cfg.Backend.CreateUser(username, opts)
159+ _, err := cfg.Backend.CreateUser(username, opts)
160 return err
161 },
162 }
163diff --git a/server/config/config.go b/server/config/config.go
164index 9c8954de383fb36c1d13b77979a13256b69c510f..35de493ed86353b3385eb14d4e008b5013a651a6 100644
165--- a/server/config/config.go
166+++ b/server/config/config.go
167@@ -109,6 +109,11 @@ func ParseConfig(path string) (*Config, error) {
168 return cfg, nil
169 }
170
171+// WriteConfig writes the configuration to the given file.
172+func WriteConfig(path string, cfg *Config) error {
173+ return os.WriteFile(path, []byte(newConfigFile(cfg)), 0o600) // nolint: errcheck
174+}
175+
176 // DefaultConfig returns a Config with the values populated with the defaults
177 // or specified environment variables.
178 func DefaultConfig() *Config {
179diff --git a/server/utils/utils.go b/server/utils/utils.go
180index 3f2fe5dabe16b0daa646a56d1bf3e01c0b5572cb..e6fcc332c611233124cb7692691d0d63dbd23bd2 100644
181--- a/server/utils/utils.go
182+++ b/server/utils/utils.go
183@@ -1,8 +1,10 @@
184 package utils
185
186 import (
187+ "fmt"
188 "path/filepath"
189 "strings"
190+ "unicode"
191 )
192
193 // SanitizeRepo returns a sanitized version of the given repository name.
194@@ -12,3 +14,22 @@ func SanitizeRepo(repo string) string {
195 repo = strings.TrimSuffix(repo, ".git")
196 return repo
197 }
198+
199+// ValidateUsername returns an error if any of the given usernames are invalid.
200+func ValidateUsername(username string) error {
201+ if username == "" {
202+ return fmt.Errorf("username cannot be empty")
203+ }
204+
205+ if !unicode.IsLetter(rune(username[0])) {
206+ return fmt.Errorf("username must start with a letter")
207+ }
208+
209+ for _, r := range username {
210+ if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '-' {
211+ return fmt.Errorf("username can only contain letters, numbers, and hyphens")
212+ }
213+ }
214+
215+ return nil
216+}