package auth import ( "crypto/rand" "crypto/rsa" "net/http" "net/http/httptest" "testing" "time" "git.unkin.net/unkin/artifactapi/internal/testsupport" ) func serve(h http.Handler, method, token string) int { r := httptest.NewRequest(method, "/x", nil) if token != "" { r.Header.Set("Authorization", "Bearer "+token) } w := httptest.NewRecorder() h.ServeHTTP(w, r) return w.Code } var ok = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) }) func TestBearerOIDC(t *testing.T) { iss := testsupport.NewOIDCIssuer() defer iss.Close() h := NewBearerOIDC(iss.URL, []string{"other", "artifactapi"}).Require(ok) otherKey, _ := rsa.GenerateKey(rand.Reader, 2048) cases := []struct { name string token string want int }{ {"valid", iss.Token(nil, "artifactapi", "ci", time.Hour), http.StatusNoContent}, {"missing", "", http.StatusUnauthorized}, {"garbage", "not.a.jwt", http.StatusUnauthorized}, {"bad signature", iss.Token(otherKey, "artifactapi", "ci", time.Hour), http.StatusUnauthorized}, {"wrong aud", iss.Token(nil, "nope", "ci", time.Hour), http.StatusUnauthorized}, {"expired", iss.Token(nil, "artifactapi", "ci", -time.Hour), http.StatusUnauthorized}, } for _, c := range cases { if got := serve(h, http.MethodPost, c.token); got != c.want { t.Errorf("%s: status %d, want %d", c.name, got, c.want) } } } func TestBearerOIDCIssuerDown(t *testing.T) { iss := testsupport.NewOIDCIssuer() b := NewBearerOIDC(iss.URL, []string{"artifactapi"}) tok := iss.Token(nil, "artifactapi", "ci", time.Hour) iss.Close() if got := serve(b.Require(ok), http.MethodPost, tok); got != http.StatusServiceUnavailable { t.Errorf("issuer down: status %d, want 503", got) } } func TestBearerOIDCDisabled(t *testing.T) { if got := serve(NewBearerOIDC("", nil).RequireWrites(ok), http.MethodDelete, ""); got != http.StatusNoContent { t.Errorf("disabled: status %d, want 204", got) } } func TestRequireWritesLeavesReadsOpen(t *testing.T) { iss := testsupport.NewOIDCIssuer() defer iss.Close() h := NewBearerOIDC(iss.URL, []string{"artifactapi"}).RequireWrites(ok) for _, m := range []string{http.MethodGet, http.MethodHead, http.MethodOptions} { if got := serve(h, m, ""); got != http.StatusNoContent { t.Errorf("%s: status %d, want 204", m, got) } } for _, m := range []string{http.MethodPost, http.MethodPut, http.MethodDelete} { if got := serve(h, m, ""); got != http.StatusUnauthorized { t.Errorf("%s: status %d, want 401", m, got) } } }