Git backed by object storage because you can't stop me
git object-storage kefka
10

Configure Feed

Select the types of activity you want to include in your feed.

feat(auth): add transport-neutral authorization seam

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso (May 28, 2026, 10:53 PM EDT) 9c4bbba4 9f0d642f

+102
+72
internal/auth/auth.go
··· 1 + package auth 2 + 3 + import ( 4 + "context" 5 + 6 + gossh "golang.org/x/crypto/ssh" 7 + ) 8 + 9 + // Operation is the access a request needs. Transports map the git service: 10 + // upload-pack/upload-archive → Read, receive-pack → Write. 11 + type Operation int 12 + 13 + const ( 14 + Read Operation = iota 15 + Write 16 + ) 17 + 18 + // Credential is what the client presented. Exactly one concrete type per 19 + // scheme; a transport constructs the variant it can produce, or Anonymous. 20 + type Credential interface{ isCredential() } 21 + 22 + // Anonymous is "no credential presented" (git://, or HTTP/SSH with none). 23 + type Anonymous struct{} 24 + 25 + // PublicKey is an SSH public key. Uses x/crypto/ssh's type (gliderlabs/ssh 26 + // keys satisfy it) so this package stays free of the SSH server library. 27 + type PublicKey struct{ Key gossh.PublicKey } 28 + 29 + // BasicAuth is an HTTP Basic credential. Unvalidated — the Authorizer owns the 30 + // user store. 31 + type BasicAuth struct{ Username, Password string } 32 + 33 + func (Anonymous) isCredential() {} 34 + func (PublicKey) isCredential() {} 35 + func (BasicAuth) isCredential() {} 36 + 37 + // Request is a transport-neutral authorization request. 38 + type Request struct { 39 + Repo string 40 + Operation Operation 41 + Cred Credential 42 + Transport string // "git", "ssh", "http" — for policy/logging 43 + } 44 + 45 + // Decision is the outcome. Unauthenticated is the seam that lets HTTP issue a 46 + // 401 challenge; SSH and git:// treat it as Deny. 47 + type Decision int 48 + 49 + const ( 50 + Deny Decision = iota 51 + Allow 52 + Unauthenticated 53 + ) 54 + 55 + // Authorizer decides whether a request may proceed. This is the seam a real 56 + // authn/authz layer plugs into later. 57 + type Authorizer interface { 58 + Authorize(ctx context.Context, req Request) Decision 59 + } 60 + 61 + // AllowAnonymous is the permissive default: read for everyone, write only when 62 + // AllowWrite is set. "Dangerously allow everything the server is configured to 63 + // allow" — never more open than the -allow-push gate. It ignores the credential 64 + // entirely and never returns Unauthenticated. 65 + type AllowAnonymous struct{ AllowWrite bool } 66 + 67 + func (a AllowAnonymous) Authorize(_ context.Context, req Request) Decision { 68 + if req.Operation == Write && !a.AllowWrite { 69 + return Deny 70 + } 71 + return Allow 72 + }
+30
internal/auth/auth_test.go
··· 1 + package auth 2 + 3 + import ( 4 + "context" 5 + "testing" 6 + ) 7 + 8 + func TestAllowAnonymousAuthorize(t *testing.T) { 9 + tests := []struct { 10 + name string 11 + allowWrite bool 12 + op Operation 13 + want Decision 14 + }{ 15 + {name: "no-write/read", allowWrite: false, op: Read, want: Allow}, 16 + {name: "no-write/write", allowWrite: false, op: Write, want: Deny}, 17 + {name: "allow-write/read", allowWrite: true, op: Read, want: Allow}, 18 + {name: "allow-write/write", allowWrite: true, op: Write, want: Allow}, 19 + } 20 + 21 + for _, tt := range tests { 22 + t.Run(tt.name, func(t *testing.T) { 23 + a := AllowAnonymous{AllowWrite: tt.allowWrite} 24 + got := a.Authorize(context.Background(), Request{Operation: tt.op}) 25 + if got != tt.want { 26 + t.Errorf("Authorize() = %v, want %v", got, tt.want) 27 + } 28 + }) 29 + } 30 + }