tenseleyflow/shithub / 2add545

Browse files

ssh_dispatch: dispatch owner lookup on principal kind; safe.directory env

Authored by espadonne
SHA
2add54574bfb507a2a10be36fc6a98864b3539a2
Parents
8c78ecd
Tree
90b4cde

1 changed file

StatusFile+-
M internal/git/protocol/ssh_dispatch.go 35 10
internal/git/protocol/ssh_dispatch.gomodified
@@ -18,6 +18,7 @@ import (
1818
 
1919
 	"github.com/tenseleyFlow/shithub/internal/auth/policy"
2020
 	"github.com/tenseleyFlow/shithub/internal/infra/storage"
21
+	"github.com/tenseleyFlow/shithub/internal/orgs"
2122
 	reposdb "github.com/tenseleyFlow/shithub/internal/repos/sqlc"
2223
 	usersdb "github.com/tenseleyFlow/shithub/internal/users/sqlc"
2324
 )
@@ -95,16 +96,30 @@ func PrepareDispatch(ctx context.Context, deps SSHDispatchDeps, in SSHDispatchIn
9596
 		return nil, parsed, ErrSSHSuspended
9697
 	}
9798
 
98
-	owner, err := uq.GetUserByUsername(ctx, deps.Pool, parsed.Owner)
99
+	// Owner can be a user OR an org; orgs.Resolve hits the principals
100
+	// table to dispatch on kind. Mirrors the same lookup the HTTP git
101
+	// handler uses (web/handlers/githttp/handler.go::lookupRepo) — see
102
+	// the regression that #20 fixed for the HTTPS path; this is the SSH
103
+	// twin of that bug.
104
+	principal, err := orgs.Resolve(ctx, deps.Pool, parsed.Owner)
99105
 	if err != nil {
100
-		// Unknown owner — surface as not-found regardless of whether
101
-		// the row never existed or was soft-deleted.
102106
 		return nil, parsed, ErrSSHRepoNotFound
103107
 	}
104
-	repo, err := rq.GetRepoByOwnerUserAndName(ctx, deps.Pool, reposdb.GetRepoByOwnerUserAndNameParams{
105
-		OwnerUserID: pgtype.Int8{Int64: owner.ID, Valid: true},
106
-		Name:        parsed.Repo,
107
-	})
108
+	var repo reposdb.Repo
109
+	switch principal.Kind {
110
+	case orgs.PrincipalUser:
111
+		repo, err = rq.GetRepoByOwnerUserAndName(ctx, deps.Pool, reposdb.GetRepoByOwnerUserAndNameParams{
112
+			OwnerUserID: pgtype.Int8{Int64: principal.ID, Valid: true},
113
+			Name:        parsed.Repo,
114
+		})
115
+	case orgs.PrincipalOrg:
116
+		repo, err = rq.GetRepoByOwnerOrgAndName(ctx, deps.Pool, reposdb.GetRepoByOwnerOrgAndNameParams{
117
+			OwnerOrgID: pgtype.Int8{Int64: principal.ID, Valid: true},
118
+			Name:       parsed.Repo,
119
+		})
120
+	default:
121
+		return nil, parsed, ErrSSHRepoNotFound
122
+	}
108123
 	if err != nil {
109124
 		if errors.Is(err, pgx.ErrNoRows) {
110125
 			return nil, parsed, ErrSSHRepoNotFound
@@ -144,7 +159,7 @@ func PrepareDispatch(ctx context.Context, deps SSHDispatchDeps, in SSHDispatchIn
144159
 	if err != nil {
145160
 		return nil, parsed, fmt.Errorf("%w: %v", ErrSSHInternal, err)
146161
 	}
147
-	env := buildSSHEnv(user, owner, repo, in.RemoteIP, requestID)
162
+	env := buildSSHEnv(user, parsed.Owner, repo, in.RemoteIP, requestID)
148163
 
149164
 	return &SSHDispatchResult{
150165
 		Argv0:     string(parsed.Service),
@@ -180,18 +195,28 @@ func FriendlyMessageFor(err error, requestID string) string {
180195
 // buildSSHEnv assembles the SHITHUB_* env vars that S14's hooks read.
181196
 // The shape matches the HTTP path so receive-pack hooks see identical
182197
 // vars regardless of transport.
183
-func buildSSHEnv(user usersdb.User, owner usersdb.User, repo reposdb.Repo, remoteIP, requestID string) []string {
198
+func buildSSHEnv(user usersdb.User, ownerName string, repo reposdb.Repo, remoteIP, requestID string) []string {
184199
 	return []string{
185200
 		"SHITHUB_USER_ID=" + strconv.FormatInt(user.ID, 10),
186201
 		"SHITHUB_USERNAME=" + user.Username,
187202
 		"SHITHUB_REPO_ID=" + strconv.FormatInt(repo.ID, 10),
188
-		"SHITHUB_REPO_FULL_NAME=" + owner.Username + "/" + repo.Name,
203
+		"SHITHUB_REPO_FULL_NAME=" + ownerName + "/" + repo.Name,
189204
 		"SHITHUB_PROTOCOL=ssh",
190205
 		"SHITHUB_REMOTE_IP=" + remoteIP,
191206
 		"SHITHUB_REQUEST_ID=" + requestID,
192207
 		// PATH must be inherited so the exec'd git binary can find its
193208
 		// sub-helpers (git-pack-objects, git-index-pack, etc.).
194209
 		"PATH=" + os.Getenv("PATH"),
210
+		// safe.directory: when sshd runs ssh-shell as the `git` user
211
+		// but the bare repo dir is owned by `shithub`, git's
212
+		// dubious-ownership check rejects the invocation. We're
213
+		// invoking on a path shithubd resolved (not user input), so
214
+		// the trust gate already happened upstream; tell git to
215
+		// trust this directory. Inline GIT_CONFIG_* avoids touching
216
+		// /etc/gitconfig and confines the override to this exec.
217
+		"GIT_CONFIG_COUNT=1",
218
+		"GIT_CONFIG_KEY_0=safe.directory",
219
+		"GIT_CONFIG_VALUE_0=*",
195220
 	}
196221
 }
197222