fix: respect anon-access on ssh

Ayman Bagabas created

This will also allow access to anonymous user connections with public-keys

Fixes: https://github.com/charmbracelet/soft-serve/issues/524

Change summary

pkg/backend/user.go                   |  4 +++
pkg/ssh/ssh.go                        | 18 +++++++-------
testscript/script_test.go             |  7 +++--
testscript/testdata/anon-access.txtar | 33 +++++++++++++++++++++++++++++
4 files changed, 50 insertions(+), 12 deletions(-)

Detailed changes

pkg/backend/user.go 🔗

@@ -84,6 +84,10 @@ func (d *Backend) AccessLevelForUser(ctx context.Context, repo string, user prot
 		}
 
 		// Otherwise, the user has read-only access.
+		if user == nil {
+			return anon
+		}
+
 		return access.ReadOnlyAccess
 	}
 

pkg/ssh/ssh.go 🔗

@@ -171,6 +171,7 @@ func (s *SSHServer) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) (allowed
 		return false
 	}
 
+	allowed = true
 	defer func(allowed *bool) {
 		publicKeyCounter.WithLabelValues(strconv.FormatBool(*allowed)).Inc()
 	}(&allowed)
@@ -178,17 +179,16 @@ func (s *SSHServer) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) (allowed
 	user, _ := s.be.UserByPublicKey(ctx, pk)
 	if user != nil {
 		ctx.SetValue(proto.ContextKeyUser, user)
-		allowed = true
+	}
 
-		// XXX: store the first "approved" public-key fingerprint in the
-		// permissions block to use for authentication later.
-		initializePermissions(ctx)
-		perms := ctx.Permissions()
+	// XXX: store the first "approved" public-key fingerprint in the
+	// permissions block to use for authentication later.
+	initializePermissions(ctx)
+	perms := ctx.Permissions()
 
-		// Set the public key fingerprint to be used for authentication.
-		perms.Extensions["pubkey-fp"] = gossh.FingerprintSHA256(pk)
-		ctx.SetValue(ssh.ContextKeyPermissions, perms)
-	}
+	// Set the public key fingerprint to be used for authentication.
+	perms.Extensions["pubkey-fp"] = gossh.FingerprintSHA256(pk)
+	ctx.SetValue(ssh.ContextKeyPermissions, perms)
 
 	return
 }

testscript/script_test.go 🔗

@@ -70,9 +70,9 @@ func TestScript(t *testing.T) {
 		return path, pair
 	}
 
-	key, admin1 := mkkey("admin1")
+	admin1Key, admin1 := mkkey("admin1")
 	_, admin2 := mkkey("admin2")
-	_, user1 := mkkey("user1")
+	user1Key, user1 := mkkey("user1")
 
 	testscript.Run(t, testscript.Params{
 		Dir:                 "./testdata/",
@@ -81,7 +81,8 @@ func TestScript(t *testing.T) {
 		Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
 			"soft":          cmdSoft("admin", admin1.Signer()),
 			"usoft":         cmdSoft("user1", user1.Signer()),
-			"git":           cmdGit(key),
+			"git":           cmdGit(admin1Key),
+			"ugit":          cmdGit(user1Key),
 			"curl":          cmdCurl,
 			"mkfile":        cmdMkfile,
 			"envfile":       cmdEnvfile,

testscript/testdata/anon-access.txtar 🔗

@@ -0,0 +1,33 @@
+# vi: set ft=conf
+
+# start soft serve
+exec soft serve &
+# wait for server to start
+waitforserver
+
+# set settings
+soft settings allow-keyless true
+soft settings anon-access no-access
+
+# create a repo
+soft repo create repo1
+git clone ssh://localhost:$SSH_PORT/repo1 repo1
+mkfile ./repo1/README.md '# Hello\n\nwelcome'
+git -C repo1 add -A
+git -C repo1 commit -m 'first'
+git -C repo1 push origin HEAD
+
+# access repo from anon
+! ugit clone ssh://localhost:$SSH_PORT/repo1 urepo1
+stderr 'Error: you are not authorized to do this'
+
+# list repo as anon
+usoft repo list
+stdout ''
+
+# create repo as anon
+! usoft repo create urepo2
+stderr 'Error: unauthorized'
+
+# stop the server
+[windows] stopserver