4f07abf55c5324ab16a7ee2c3bfec428dfbd1573
- 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/backend.go b/server/backend/backend.go
2index e43b839411d11615faeadd4aac39394c2391c6c3..5d771e35720b41012ad29c2e7c684ff62335fac2 100644
3--- a/server/backend/backend.go
4+++ b/server/backend/backend.go
5@@ -9,56 +9,10 @@ import (
6 // Backend is an interface that handles repositories management and any
7 // non-Git related operations.
8 type Backend interface {
9- // ServerName returns the server's name.
10- ServerName() string
11- // SetServerName sets the server's name.
12- SetServerName(name string) error
13- // ServerHost returns the server's host.
14- ServerHost() string
15- // SetServerHost sets the server's host.
16- SetServerHost(host string) error
17- // ServerPort returns the server's port.
18- ServerPort() string
19- // SetServerPort sets the server's port.
20- SetServerPort(port string) error
21-
22- // AnonAccess returns the access level for anonymous users.
23- AnonAccess() AccessLevel
24- // SetAnonAccess sets the access level for anonymous users.
25- SetAnonAccess(level AccessLevel) error
26- // AllowKeyless returns true if keyless access is allowed.
27- AllowKeyless() bool
28- // SetAllowKeyless sets whether or not keyless access is allowed.
29- SetAllowKeyless(allow bool) error
30-
31- // Repository finds the given repository.
32- Repository(repo string) (Repository, error)
33- // Repositories returns a list of all repositories.
34- Repositories() ([]Repository, error)
35- // CreateRepository creates a new repository.
36- CreateRepository(name string, private bool) (Repository, error)
37- // DeleteRepository deletes a repository.
38- DeleteRepository(name string) error
39- // RenameRepository renames a repository.
40- RenameRepository(oldName, newName string) error
41-
42- // Description returns the repo's description.
43- Description(repo string) string
44- // SetDescription sets the repo's description.
45- SetDescription(repo, desc string) error
46- // IsPrivate returns true if the repository is private.
47- IsPrivate(repo string) bool
48- // SetPrivate sets the repository's private status.
49- SetPrivate(repo string, priv bool) error
50-
51- // IsCollaborator returns true if the authorized key is a collaborator on the repository.
52- IsCollaborator(pk ssh.PublicKey, repo string) bool
53- // AddCollaborator adds the authorized key as a collaborator on the repository.
54- AddCollaborator(pk ssh.PublicKey, repo string) error
55- // IsAdmin returns true if the authorized key is an admin.
56- IsAdmin(pk ssh.PublicKey) bool
57- // AddAdmin adds the authorized key as an admin.
58- AddAdmin(pk ssh.PublicKey) error
59+ ServerBackend
60+ RepositoryStore
61+ RepositoryMetadata
62+ RepositoryAccess
63 }
64
65 // ParseAuthorizedKey parses an authorized key string into a public key.
66diff --git a/server/backend/file/file.go b/server/backend/file/file.go
67index da8196d63d0688a97ed267aa9ae7d130a90867c0..05949556bb186e35b05fe5e7e144b37fb46a5eb0 100644
68--- a/server/backend/file/file.go
69+++ b/server/backend/file/file.go
70@@ -89,7 +89,7 @@ func (fb *FileBackend) adminsPath() string {
71 }
72
73 func (fb *FileBackend) collabsPath(repo string) string {
74- return filepath.Join(fb.reposPath(), repo, collabs)
75+ return filepath.Join(fb.path, collabs, repo)
76 }
77
78 func sanatizeRepo(repo string) string {
79@@ -117,10 +117,16 @@ func readAll(path string) (string, error) {
80 return string(bts), err
81 }
82
83+// exists returns true if the given path exists.
84+func exists(path string) bool {
85+ _, err := os.Stat(path)
86+ return err == nil
87+}
88+
89 // NewFileBackend creates a new FileBackend.
90 func NewFileBackend(path string) (*FileBackend, error) {
91 fb := &FileBackend{path: path}
92- for _, dir := range []string{repos, settings} {
93+ for _, dir := range []string{repos, settings, collabs} {
94 if err := os.MkdirAll(filepath.Join(path, dir), 0755); err != nil {
95 return nil, err
96 }
97@@ -181,10 +187,10 @@ func (fb *FileBackend) AccessLevel(repo string, pk gossh.PublicKey) backend.Acce
98 // AddAdmin adds a public key to the list of server admins.
99 //
100 // It implements backend.Backend.
101-func (fb *FileBackend) AddAdmin(pk gossh.PublicKey) error {
102+func (fb *FileBackend) AddAdmin(pk gossh.PublicKey, memo string) error {
103 // Skip if the key already exists.
104 if fb.IsAdmin(pk) {
105- return nil
106+ return fmt.Errorf("key already exists")
107 }
108
109 ak := backend.MarshalAuthorizedKey(pk)
110@@ -195,32 +201,206 @@ func (fb *FileBackend) AddAdmin(pk gossh.PublicKey) error {
111 }
112
113 defer f.Close() //nolint:errcheck
114- _, err = fmt.Fprintln(f, ak)
115+ if memo != "" {
116+ memo = " " + memo
117+ }
118+ _, err = fmt.Fprintf(f, "%s%s\n", ak, memo)
119 return err
120 }
121
122 // AddCollaborator adds a public key to the list of collaborators for the given repo.
123 //
124 // It implements backend.Backend.
125-func (fb *FileBackend) AddCollaborator(pk gossh.PublicKey, repo string) error {
126+func (fb *FileBackend) AddCollaborator(pk gossh.PublicKey, memo string, name string) error {
127+ // Check if repo exists
128+ if !exists(filepath.Join(fb.reposPath(), sanatizeRepo(name)+".git")) {
129+ return fmt.Errorf("repository %s does not exist", name)
130+ }
131+
132 // Skip if the key already exists.
133- if fb.IsCollaborator(pk, repo) {
134- return nil
135+ if fb.IsCollaborator(pk, name) {
136+ return fmt.Errorf("key already exists")
137 }
138
139 ak := backend.MarshalAuthorizedKey(pk)
140- repo = sanatizeRepo(repo) + ".git"
141- f, err := os.OpenFile(fb.collabsPath(repo), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
142+ name = sanatizeRepo(name)
143+ if err := os.MkdirAll(filepath.Dir(fb.collabsPath(name)), 0755); err != nil {
144+ logger.Debug("failed to create collaborators directory",
145+ "err", err, "path", filepath.Dir(fb.collabsPath(name)))
146+ return err
147+ }
148+
149+ f, err := os.OpenFile(fb.collabsPath(name), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
150 if err != nil {
151- logger.Debug("failed to open collaborators file", "err", err, "path", fb.collabsPath(repo))
152+ logger.Debug("failed to open collaborators file", "err", err, "path", fb.collabsPath(name))
153 return err
154 }
155
156 defer f.Close() //nolint:errcheck
157- _, err = fmt.Fprintln(f, ak)
158+ if memo != "" {
159+ memo = " " + memo
160+ }
161+ _, err = fmt.Fprintf(f, "%s%s\n", ak, memo)
162 return err
163 }
164
165+// Admins returns a list of public keys that are admins.
166+//
167+// It implements backend.Backend.
168+func (fb *FileBackend) Admins() ([]string, error) {
169+ admins := make([]string, 0)
170diff --git a/server/backend/noop/noop.go b/server/backend/noop/noop.go
171index 72c863b9503fedbba5215300b7cc222edd235ee1..45cf143964aeb2e7f1ba098c0f061b064f06fde5 100644
172--- a/server/backend/noop/noop.go
173+++ b/server/backend/noop/noop.go
174@@ -21,18 +21,38 @@ type Noop struct {
175 Port string
176 }
177
178+// Admins implements backend.Backend
179+func (*Noop) Admins() ([]string, error) {
180+ return nil, nil
181+}
182+
183+// Collaborators implements backend.Backend
184+func (*Noop) Collaborators(repo string) ([]string, error) {
185+ return nil, nil
186+}
187+
188+// RemoveAdmin implements backend.Backend
189+func (*Noop) RemoveAdmin(pk ssh.PublicKey) error {
190+ return nil
191+}
192+
193+// RemoveCollaborator implements backend.Backend
194+func (*Noop) RemoveCollaborator(pk ssh.PublicKey, repo string) error {
195+ return nil
196+}
197+
198 // AccessLevel implements backend.AccessMethod
199 func (*Noop) AccessLevel(repo string, pk ssh.PublicKey) backend.AccessLevel {
200 return backend.AdminAccess
201 }
202
203 // AddAdmin implements backend.Backend
204-func (*Noop) AddAdmin(pk ssh.PublicKey) error {
205+func (*Noop) AddAdmin(pk ssh.PublicKey, memo string) error {
206 return ErrNotImpl
207 }
208
209 // AddCollaborator implements backend.Backend
210-func (*Noop) AddCollaborator(pk ssh.PublicKey, repo string) error {
211+func (*Noop) AddCollaborator(pk ssh.PublicKey, memo string, repo string) error {
212 return ErrNotImpl
213 }
214
215diff --git a/server/backend/repo.go b/server/backend/repo.go
216index 61bd6c1ea14c7378d84f03ec9bf23002900cd58f..d9238ae762438130e9260e421112b086a0bc3b8e 100644
217--- a/server/backend/repo.go
218+++ b/server/backend/repo.go
219@@ -1,6 +1,55 @@
220 package backend
221
222-import "github.com/charmbracelet/soft-serve/git"
223+import (
224+ "github.com/charmbracelet/soft-serve/git"
225+ "golang.org/x/crypto/ssh"
226+)
227+
228+// RepositoryStore is an interface for managing repositories.
229+type RepositoryStore interface {
230+ // Repository finds the given repository.
231+ Repository(repo string) (Repository, error)
232+ // Repositories returns a list of all repositories.
233+ Repositories() ([]Repository, error)
234+ // CreateRepository creates a new repository.
235+ CreateRepository(name string, private bool) (Repository, error)
236+ // DeleteRepository deletes a repository.
237+ DeleteRepository(name string) error
238+ // RenameRepository renames a repository.
239+ RenameRepository(oldName, newName string) error
240+}
241+
242+// RepositoryMetadata is an interface for managing repository metadata.
243+type RepositoryMetadata interface {
244+ // Description returns the repository's description.
245+ Description(repo string) string
246+ // SetDescription sets the repository's description.
247+ SetDescription(repo, desc string) error
248+ // IsPrivate returns whether the repository is private.
249+ IsPrivate(repo string) bool
250+ // SetPrivate sets whether the repository is private.
251+ SetPrivate(repo string, private bool) error
252+}
253+
254+// RepositoryAccess is an interface for managing repository access.
255+type RepositoryAccess interface {
256+ // IsCollaborator returns true if the authorized key is a collaborator on the repository.
257+ IsCollaborator(pk ssh.PublicKey, repo string) bool
258+ // AddCollaborator adds the authorized key as a collaborator on the repository.
259+ AddCollaborator(pk ssh.PublicKey, memo string, repo string) error
260+ // RemoveCollaborator removes the authorized key as a collaborator on the repository.
261+ RemoveCollaborator(pk ssh.PublicKey, repo string) error
262+ // Collaborators returns a list of all collaborators on the repository.
263+ Collaborators(repo string) ([]string, error)
264+ // IsAdmin returns true if the authorized key is an admin.
265+ IsAdmin(pk ssh.PublicKey) bool
266+ // AddAdmin adds the authorized key as an admin.
267+ AddAdmin(pk ssh.PublicKey, memo string) error
268+ // RemoveAdmin removes the authorized key as an admin.
269+ RemoveAdmin(pk ssh.PublicKey) error
270+ // Admins returns a list of all admins.
271+ Admins() ([]string, error)
272+}
273
274 // Repository is a Git repository interface.
275 type Repository interface {
276diff --git a/server/backend/server.go b/server/backend/server.go
277new file mode 100644
278index 0000000000000000000000000000000000000000..97c02b0966e631ac09139960bd3f09d94e06a560
279--- /dev/null
280+++ b/server/backend/server.go
281@@ -0,0 +1,26 @@
282+package backend
283+
284+// ServerBackend is an interface that handles server configuration.
285+type ServerBackend interface {
286+ // ServerName returns the server's name.
287+ ServerName() string
288+ // SetServerName sets the server's name.
289+ SetServerName(name string) error
290+ // ServerHost returns the server's host.
291+ ServerHost() string
292+ // SetServerHost sets the server's host.
293+ SetServerHost(host string) error
294+ // ServerPort returns the server's port.
295+ ServerPort() string
296+ // SetServerPort sets the server's port.
297+ SetServerPort(port string) error
298+
299+ // AnonAccess returns the access level for anonymous users.
300+ AnonAccess() AccessLevel
301+ // SetAnonAccess sets the access level for anonymous users.
302+ SetAnonAccess(level AccessLevel) error
303+ // AllowKeyless returns true if keyless access is allowed.
304+ AllowKeyless() bool
305+ // SetAllowKeyless sets whether or not keyless access is allowed.
306+ SetAllowKeyless(allow bool) error
307+}