d88ccb97d3e756be489d680d29e7dc24b9792fb3
- 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/config/auth.go b/config/auth.go
2index a1ba59fe9de47cd8a9ca1981f9326da4560158e4..0bbcaa6e1dbaaebc7ac1d3ec966a709bc540d3b4 100644
3--- a/config/auth.go
4+++ b/config/auth.go
5@@ -59,43 +59,79 @@ func (cfg *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
6 return cfg.accessForKey("", pk) != gm.NoAccess
7 }
8
9+func (cfg *Config) anonAccessLevel() gm.AccessLevel {
10+ switch cfg.AnonAccess {
11+ case "no-access":
12+ return gm.NoAccess
13+ case "read-only":
14+ return gm.ReadOnlyAccess
15+ case "read-write":
16+ return gm.ReadWriteAccess
17+ case "admin-access":
18+ return gm.AdminAccess
19+ default:
20+ return gm.NoAccess
21+ }
22+}
23+
24+// accessForKey returns the access level for the given repo.
25+//
26+// If repo doesn't exist, then access is based on user's admin privileges, or
27+// config.AnonAccess.
28+// If repo exists, and private, then admins and collabs are allowed access.
29+// If repo exists, and not private, then access is based on config.AnonAccess.
30 func (cfg *Config) accessForKey(repo string, pk ssh.PublicKey) gm.AccessLevel {
31- private := cfg.isPrivate(repo)
32- for _, u := range cfg.Users {
33- for _, k := range u.PublicKeys {
34+ var u *User
35+ var r *RepoConfig
36+ anon := cfg.anonAccessLevel()
37+OUT:
38+ // Find user
39+ for _, user := range cfg.Users {
40+ for _, k := range user.PublicKeys {
41 apk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strings.TrimSpace(k)))
42 if err != nil {
43 log.Printf("error: malformed authorized key: '%s'", k)
44 return gm.NoAccess
45 }
46 if ssh.KeysEqual(pk, apk) {
47- if u.Admin {
48- return gm.AdminAccess
49- }
50- for _, r := range u.CollabRepos {
51- if repo == r {
52- return gm.ReadWriteAccess
53- }
54- }
55- if !private {
56- return gm.ReadOnlyAccess
57- }
58+ us := user
59+ u = &us
60+ break OUT
61 }
62 }
63 }
64- if private && len(cfg.Users) > 0 {
65- return gm.NoAccess
66+ // Find repo
67+ for _, rp := range cfg.Repos {
68+ if rp.Repo == repo {
69+ rr := rp
70+ r = &rr
71+ break
72+ }
73 }
74- switch cfg.AnonAccess {
75- case "no-access":
76- return gm.NoAccess
77- case "read-only":
78- return gm.ReadOnlyAccess
79- case "read-write":
80- return gm.ReadWriteAccess
81- case "admin-access":
82+ if u != nil && u.Admin {
83 return gm.AdminAccess
84- default:
85- return gm.NoAccess
86 }
87+ if r == nil || len(cfg.Users) == 0 {
88+ return anon
89+ }
90+ // Collabs default access is read-write
91+ if u != nil {
92+ ac := gm.ReadWriteAccess
93+ if anon > ac {
94+ ac = anon
95+ }
96+ for _, rr := range u.CollabRepos {
97+ if rr == r.Repo {
98+ return ac
99+ }
100+ }
101+ }
102+ // Users default access is read-only
103+ if !r.Private {
104+ if anon > gm.ReadOnlyAccess {
105diff --git a/config/config.go b/config/config.go
106index 85e7ce831e16c5c3b075324a1ea6a9d0cc0e3ce3..da6ff3e5d0c9d657e7d3e8c01d89197350c54946 100644
107--- a/config/config.go
108+++ b/config/config.go
109@@ -27,11 +27,6 @@ import (
110 "github.com/go-git/go-git/v5/storage/memory"
111 )
112
113-var (
114- // ErrNoConfig is returned when no config file is found.
115- ErrNoConfig = errors.New("no config file found")
116-)
117-
118 // Config is the Soft Serve configuration.
119 type Config struct {
120 Name string `yaml:"name" json:"name"`
121@@ -40,7 +35,7 @@ type Config struct {
122 AnonAccess string `yaml:"anon-access" json:"anon-access"`
123 AllowKeyless bool `yaml:"allow-keyless" json:"allow-keyless"`
124 Users []User `yaml:"users" json:"users"`
125- Repos []MenuRepo `yaml:"repos" json:"repos"`
126+ Repos []RepoConfig `yaml:"repos" json:"repos"`
127 Source *RepoSource `yaml:"-" json:"-"`
128 Cfg *config.Config `yaml:"-" json:"-"`
129 mtx sync.Mutex
130@@ -54,8 +49,8 @@ type User struct {
131 CollabRepos []string `yaml:"collab-repos" json:"collab-repos"`
132 }
133
134-// Repo contains repository configuration information.
135-type MenuRepo struct {
136+// RepoConfig is a repository configuration.
137+type RepoConfig struct {
138 Name string `yaml:"name" json:"name"`
139 Repo string `yaml:"repo" json:"repo"`
140 Note string `yaml:"note" json:"note"`
141@@ -128,38 +123,45 @@ func NewConfig(cfg *config.Config) (*Config, error) {
142 return c, nil
143 }
144
145-// Reload reloads the configuration.
146-func (cfg *Config) Reload() error {
147- cfg.mtx.Lock()
148- defer cfg.mtx.Unlock()
149- err := cfg.Source.LoadRepos()
150+func (cfg *Config) readConfig(repo string, v interface{}) error {
151+ cr, err := cfg.Source.GetRepo(repo)
152 if err != nil {
153 return err
154 }
155- cr, err := cfg.Source.GetRepo("config")
156- if err != nil {
157- return err
158- }
159- cy, _, err := cr.LatestFile("config.yaml")
160+ cy, _, err := cr.LatestFile(repo + ".yaml")
161 if err != nil && !errors.Is(err, git.ErrFileNotFound) {
162- return fmt.Errorf("error reading config.yaml: %w", err)
163+ return fmt.Errorf("error reading %s.yaml: %w", repo, err)
164 }
165- cj, _, err := cr.LatestFile("config.json")
166+ cj, _, err := cr.LatestFile(repo + ".json")
167 if err != nil && !errors.Is(err, git.ErrFileNotFound) {
168- return fmt.Errorf("error reading config.json: %w", err)
169+ return fmt.Errorf("error reading %s.json: %w", repo, err)
170 }
171 if cy != "" {
172- err = yaml.Unmarshal([]byte(cy), cfg)
173+ err = yaml.Unmarshal([]byte(cy), v)
174 if err != nil {
175- return fmt.Errorf("bad yaml in config.yaml: %s", err)
176+ return fmt.Errorf("bad yaml in %s.yaml: %s", repo, err)
177 }
178 } else if cj != "" {
179- err = json.Unmarshal([]byte(cj), cfg)
180+ err = json.Unmarshal([]byte(cj), v)
181 if err != nil {
182- return fmt.Errorf("bad json in config.json: %s", err)
183+ return fmt.Errorf("bad json in %s.json: %s", repo, err)
184 }
185 } else {
186- return ErrNoConfig
187+ return fmt.Errorf("no config file found for %q", repo)
188+ }
189+ return nil
190+}
191+
192+// Reload reloads the configuration.
193+func (cfg *Config) Reload() error {
194+ cfg.mtx.Lock()
195+ defer cfg.mtx.Unlock()
196+ err := cfg.Source.LoadRepos()
197+ if err != nil {
198+ return err
199+ }
200+ if err := cfg.readConfig("config", cfg); err != nil {
201+ return fmt.Errorf("error reading config: %w", err)
202 }
203 for _, r := range cfg.Source.AllRepos() {
204 name := r.Name()
205@@ -276,15 +278,6 @@ func (cfg *Config) createDefaultConfigRepo(yaml string) error {
206 return cfg.Reload()
207 }
208