0c86b0a0d73bafd33be5c5c9085f8df097e8c2b0
- 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/cmd/soft/migrate_config.go b/cmd/soft/migrate_config.go
2new file mode 100644
3index 0000000000000000000000000000000000000000..9c4f23e3857a3177e6bb610cc3301a47d33e42e0
4--- /dev/null
5+++ b/cmd/soft/migrate_config.go
6@@ -0,0 +1,431 @@
7+package main
8+
9+import (
10+ "encoding/json"
11+ "fmt"
12+ "io"
13+ "os"
14+ "path/filepath"
15+ "strings"
16+
17+ "github.com/charmbracelet/log"
18+ "github.com/charmbracelet/soft-serve/git"
19+ "github.com/charmbracelet/soft-serve/server/backend"
20+ "github.com/charmbracelet/soft-serve/server/backend/sqlite"
21+ "github.com/charmbracelet/soft-serve/server/config"
22+ "github.com/charmbracelet/soft-serve/server/utils"
23+ "github.com/spf13/cobra"
24+ "golang.org/x/crypto/ssh"
25+ "gopkg.in/yaml.v3"
26+)
27+
28+var (
29+ migrateConfig = &cobra.Command{
30+ Use: "migrate-config",
31+ Short: "Migrate config to new format",
32+ RunE: func(cmd *cobra.Command, args []string) error {
33+ keyPath := os.Getenv("SOFT_SERVE_KEY_PATH")
34+ reposPath := os.Getenv("SOFT_SERVE_REPO_PATH")
35+ bindAddr := os.Getenv("SOFT_SERVE_BIND_ADDRESS")
36+ cfg := config.DefaultConfig()
37+ sb, err := sqlite.NewSqliteBackend(cfg.DataPath)
38+ if err != nil {
39+ return fmt.Errorf("failed to create sqlite backend: %w", err)
40+ }
41+
42+ cfg = cfg.WithBackend(sb)
43+
44+ // Set SSH listen address
45+ log.Info("Setting SSH listen address...")
46+ if bindAddr != "" {
47+ cfg.SSH.ListenAddr = bindAddr
48+ }
49+
50+ // Copy SSH host key
51+ log.Info("Copying SSH host key...")
52+ if keyPath != "" {
53+ if err := os.MkdirAll(filepath.Join(cfg.DataPath, "ssh"), 0700); err != nil {
54+ return fmt.Errorf("failed to create ssh directory: %w", err)
55+ }
56+
57+ if err := copyFile(keyPath, filepath.Join(cfg.DataPath, "ssh", filepath.Base(keyPath))); err != nil {
58+ return fmt.Errorf("failed to copy ssh key: %w", err)
59+ }
60+
61+ cfg.SSH.KeyPath = filepath.Join("ssh", filepath.Base(keyPath))
62+ }
63+
64+ // Read config
65+ log.Info("Reading config repository...")
66+ r, err := git.Open(filepath.Join(reposPath, "config"))
67+ if err != nil {
68+ return fmt.Errorf("failed to open config repo: %w", err)
69+ }
70+
71+ head, err := r.HEAD()
72+ if err != nil {
73+ return fmt.Errorf("failed to get head: %w", err)
74+ }
75+
76+ tree, err := r.TreePath(head, "")
77+ if err != nil {
78+ return fmt.Errorf("failed to get tree: %w", err)
79+ }
80+
81+ isJson := false
82+ te, err := tree.TreeEntry("config.yaml")
83+ if err != nil {
84+ te, err = tree.TreeEntry("config.json")
85+ if err != nil {
86+ return fmt.Errorf("failed to get config file: %w", err)
87+ }
88+ isJson = true
89+ }
90+
91+ cc, err := te.Contents()
92+ if err != nil {
93+ return fmt.Errorf("failed to get config contents: %w", err)
94+ }
95+
96+ var ocfg Config
97+ if isJson {
98+ if err := json.Unmarshal(cc, &ocfg); err != nil {
99+ return fmt.Errorf("failed to unmarshal config: %w", err)
100+ }
101+ } else {
102+ if err := yaml.Unmarshal(cc, &ocfg); err != nil {
103+ return fmt.Errorf("failed to unmarshal config: %w", err)
104+ }
105+ }
106diff --git a/cmd/soft/root.go b/cmd/soft/root.go
107index fe7cb6540a8028bda78d65df29b1a1bf46b02071..5fc9e4588e6dcc9fb57169b6ea1a857c1d899c44 100644
108--- a/cmd/soft/root.go
109+++ b/cmd/soft/root.go
110@@ -32,6 +32,7 @@ func init() {
111 serveCmd,
112 manCmd,
113 hookCmd,
114+ migrateConfig,
115 )
116 rootCmd.CompletionOptions.HiddenDefaultCmd = true
117
118diff --git a/server/backend/backend.go b/server/backend/backend.go
119index e95e6b74a88108fdb09d8b2055cf8e06f7f01c17..ae243d1a1e6232c09f048b5d8519f9bd3268b801 100644
120--- a/server/backend/backend.go
121+++ b/server/backend/backend.go
122@@ -3,7 +3,8 @@ package backend
123 import (
124 "bytes"
125
126- "golang.org/x/crypto/ssh"
127+ "github.com/charmbracelet/ssh"
128+ gossh "golang.org/x/crypto/ssh"
129 )
130
131 // Backend is an interface that handles repositories management and any
132@@ -18,8 +19,8 @@ type Backend interface {
133 }
134
135 // ParseAuthorizedKey parses an authorized key string into a public key.
136-func ParseAuthorizedKey(ak string) (ssh.PublicKey, string, error) {
137- pk, c, _, _, err := ssh.ParseAuthorizedKey([]byte(ak))
138+func ParseAuthorizedKey(ak string) (gossh.PublicKey, string, error) {
139+ pk, c, _, _, err := gossh.ParseAuthorizedKey([]byte(ak))
140 return pk, c, err
141 }
142
143@@ -28,9 +29,14 @@ func ParseAuthorizedKey(ak string) (ssh.PublicKey, string, error) {
144 // This is the inverse of ParseAuthorizedKey.
145 // This function is a copy of ssh.MarshalAuthorizedKey, but without the trailing newline.
146 // It returns an empty string if pk is nil.
147-func MarshalAuthorizedKey(pk ssh.PublicKey) string {
148+func MarshalAuthorizedKey(pk gossh.PublicKey) string {
149 if pk == nil {
150 return ""
151 }
152- return string(bytes.TrimSuffix(ssh.MarshalAuthorizedKey(pk), []byte("\n")))
153+ return string(bytes.TrimSuffix(gossh.MarshalAuthorizedKey(pk), []byte("\n")))
154+}
155+
156+// KeysEqual returns whether the two public keys are equal.
157+func KeysEqual(a, b gossh.PublicKey) bool {
158+ return ssh.KeysEqual(a, b)
159 }
160diff --git a/server/backend/sqlite/db.go b/server/backend/sqlite/db.go
161index f3ed49d74cf459a155cb558023d682ca2b541f02..a8fe46bb499183946fbea1827cfbe51d05094c54 100644
162--- a/server/backend/sqlite/db.go
163+++ b/server/backend/sqlite/db.go
164@@ -8,7 +8,6 @@ import (
165
166 "github.com/charmbracelet/soft-serve/server/backend"
167 "github.com/jmoiron/sqlx"
168- "golang.org/x/crypto/bcrypt"
169 "modernc.org/sqlite"
170 sqlite3 "modernc.org/sqlite/lib"
171 )
172@@ -96,20 +95,3 @@ func rollback(tx *sqlx.Tx, err error) error {
173
174 return err
175 }
176-
177-func hashPassword(password string) (string, error) {
178- hash, err := bcrypt.GenerateFromPassword([]byte(password+"soft-serve-v1"), 14)
179- if err != nil {
180- return "", fmt.Errorf("failed to hash password: %w", err)
181- }
182-
183- return string(hash), nil
184-}
185-
186-func checkPassword(hash, password string) error {
187- if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password+"soft-serve-v1")); err != nil {
188- return fmt.Errorf("failed to check password: %w", err)
189- }
190-
191- return nil
192-}
193diff --git a/server/backend/sqlite/sqlite.go b/server/backend/sqlite/sqlite.go
194index 263885b492c9cb28a2e699de2d4cd0493011a7cb..eb7c3413cefdb7c443300e90d0403af17aca07af 100644
195--- a/server/backend/sqlite/sqlite.go
196+++ b/server/backend/sqlite/sqlite.go
197@@ -38,6 +38,10 @@ func (d *SqliteBackend) reposPath() string {
198
199 // NewSqliteBackend creates a new SqliteBackend.
200 func NewSqliteBackend(dataPath string) (*SqliteBackend, error) {
201+ if err := os.MkdirAll(dataPath, 0755); err != nil {
202+ return nil, err
203+ }
204+
205 db, err := sqlx.Connect("sqlite", filepath.Join(dataPath, "soft-serve.db"+
206 "?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)"))
207 if err != nil {
208@@ -164,6 +168,7 @@ func (d *SqliteBackend) ImportRepository(name string, remote string, opts backen
209
210 copts := git.CloneOptions{
211 Mirror: opts.Mirror,
212+ Quiet: true,
213 }
214 if err := git.Clone(remote, rp, copts); err != nil {
215 logger.Debug("failed to clone repository", "err", err, "mirror", opts.Mirror, "remote", remote, "path", rp)
216diff --git a/server/backend/sqlite/user.go b/server/backend/sqlite/user.go
217index d03410277ae45f60457eb48521198ff34f3646a5..068c85a3a2b39251b069696c3505eaca4b2659a8 100644
218--- a/server/backend/sqlite/user.go
219+++ b/server/backend/sqlite/user.go
220@@ -168,13 +168,13 @@ func (d *SqliteBackend) CreateUser(username string, opts backend.UserOptions) (b
221
222 var user *User
223 if err := wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error {
224- into := "INSERT INTO user (username, admin"
225- values := "VALUES (?, ?"
226- args := []interface{}{username, opts.Admin}
227- into += ", updated_at)"
228- values += ", CURRENT_TIMESTAMP)"
229+ stmt, err := tx.Prepare("INSERT INTO user (username, admin, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP);")
230+ if err != nil {
231+ return err
232+ }
233
234- r, err := tx.Exec(into+" "+values, args...)
235+ defer stmt.Close() // nolint: errcheck
236+ r, err := stmt.Exec(username, opts.Admin)
237 if err != nil {
238 return err
239 }
240@@ -182,12 +182,19 @@ func (d *SqliteBackend) CreateUser(username string, opts backend.UserOptions) (b
241 if len(opts.PublicKeys) > 0 {
242 userID, err := r.LastInsertId()
243 if err != nil {
244+ logger.Error("error getting last insert id")
245 return err
246 }
247
248 for _, pk := range opts.PublicKeys {
249- if _, err := tx.Exec(`INSERT INTO public_key (user_id, public_key, updated_at)
250- VALUES (?, ?, CURRENT_TIMESTAMP);`, userID, backend.MarshalAuthorizedKey(pk)); err != nil {
251+ stmt, err := tx.Prepare(`INSERT INTO public_key (user_id, public_key, updated_at)
252+ VALUES (?, ?, CURRENT_TIMESTAMP);`)
253+ if err != nil {
254+ return err
255+ }
256+
257+ defer stmt.Close() // nolint: errcheck
258+ if _, err := stmt.Exec(userID, backend.MarshalAuthorizedKey(pk)); err != nil {
259 return err
260 }
261 }