Auth against users in config.yaml

Toby Padilla created

Change summary

config/auth.go     | 40 +++++++++++++++++++++++++++++++++++++---
config/config.go   |  9 ++++-----
config/defaults.go | 29 ++++++++++++++---------------
3 files changed, 55 insertions(+), 23 deletions(-)

Detailed changes

config/auth.go 🔗

@@ -1,13 +1,14 @@
 package config
 
 import (
+	"log"
+
 	gm "github.com/charmbracelet/wish/git"
 	"github.com/gliderlabs/ssh"
 )
 
 func (cfg *Config) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel {
-	// TODO: check yaml for access rules
-	return gm.ReadWriteAccess
+	return cfg.accessForKey(repo, pk)
 }
 
 func (cfg *Config) PasswordHandler(ctx ssh.Context, password string) bool {
@@ -15,6 +16,39 @@ func (cfg *Config) PasswordHandler(ctx ssh.Context, password string) bool {
 }
 
 func (cfg *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
-	// TODO: check yaml for access rules
+	if cfg.accessForKey("", pk) == gm.NoAccess {
+		return false
+	}
 	return true
 }
+
+func (cfg *Config) accessForKey(repo string, pk ssh.PublicKey) gm.AccessLevel {
+	for _, u := range cfg.Users {
+		apk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(u.PublicKey))
+		if err != nil {
+			log.Printf("error: malformed authorized key: '%s'", u.PublicKey)
+			return gm.NoAccess
+		}
+		if ssh.KeysEqual(pk, apk) {
+			if u.Admin {
+				return gm.AdminAccess
+			}
+			for _, r := range u.CollabRepos {
+				if repo == r {
+					return gm.ReadWriteAccess
+				}
+			}
+			return gm.ReadOnlyAccess
+		}
+	}
+	switch cfg.AnonAccess {
+	case "no-access":
+		return gm.NoAccess
+	case "read-only":
+		return gm.ReadOnlyAccess
+	case "read-write":
+		return gm.ReadWriteAccess
+	default:
+		return gm.NoAccess
+	}
+}

config/config.go 🔗

@@ -29,7 +29,7 @@ type Config struct {
 type User struct {
 	Name        string   `yaml:"name"`
 	Admin       bool     `yaml:"admin"`
-	PublicKey   string   `yaml:"pk"`
+	PublicKey   string   `yaml:"public-key"`
 	CollabRepos []string `yaml:"collab_repos"`
 }
 
@@ -72,13 +72,13 @@ func NewConfig(host string, port int, pk string, rs *git.RepoSource) (*Config, e
 }
 
 func (cfg *Config) Pushed(repo string, pk ssh.PublicKey) {
-	err := cfg.Reload()
+	err := cfg.reload()
 	if err != nil {
 		log.Printf("error reloading after push: %s", err)
 	}
 }
 
-func (cfg *Config) Reload() error {
+func (cfg *Config) reload() error {
 	err := cfg.Source.LoadRepos()
 	if err != nil {
 		return err
@@ -157,12 +157,11 @@ func (cfg *Config) createDefaultConfigRepo(yaml string) error {
 		if err != nil {
 			return err
 		}
-		err = rs.LoadRepos()
 		if err != nil {
 			return err
 		}
 	} else if err != nil {
 		return err
 	}
-	return nil
+	return cfg.reload()
 }

config/defaults.go 🔗

@@ -2,8 +2,7 @@ package config
 
 const defaultReadme = "# Soft Serve\n\n Welcome! You can configure your Soft Serve server by cloning this repo and pushing changes.\n\n## Repos\n\n{{ range .Repos }}* {{ .Name }}{{ if .Note }} - {{ .Note }} {{ end }}\n  - `git clone ssh://{{$.Host}}:{{$.Port}}/{{.Repo}}`\n{{ end }}"
 
-const defaultConfig = `
-name: Soft Serve
+const defaultConfig = `name: Soft Serve
 host: %s
 port: %d
 
@@ -16,27 +15,27 @@ allow-no-keys: false
 # Customize repo display in menu
 repos:
   - name: Home
-	  repo: config
-		note: "Configuration and content repo for this server"`
+    repo: config
+    note: "Configuration and content repo for this server"`
 
 const hasKeyUserConfig = `
 # Users can read all repos, and push to collab-repos, admin can push to all repos
 users:
   - name: admin
-	  admin: true
-		public-key: |
-		  %s`
+    admin: true
+    public-key:
+      %s`
 
 const defaultUserConfig = `
 # users:
 #   - name: admin
-# 	  admin: true
-# 		public-key: |
-# 		  KEY TEXT`
+#     admin: true
+#     public-key: |
+#       KEY TEXT`
 
 const exampleUserConfig = `
-#  - name: little-buddy
-#	   collab-repos:
-#		   - soft-serve
-#		 public-key: |
-#		   KEY TEXT`
+#   - name: little-buddy
+#     collab-repos:
+#       - soft-serve
+#     public-key: |
+#       KEY TEXT`