fix(s3/audit): populate requester for GET/HEAD/IAM operations (#9581)

Authentication records the identity with r.WithContext, which returns a
request copy. Handlers that log their own audit entry (PUT, DELETE,
tagging) see it, but GET/HEAD object and IAM operations rely on track()'s
fallback entry, which is built from the original request the auth copy
never reached - so requester came out empty.

Install a mutable identity holder on the request before authentication
and have SetIdentityNameInContext record into it. The holder is shared by
pointer across every request copy, so the fallback entry recovers the
authenticated requester. The per-request context value still takes
precedence, so nothing changes for handlers that see the auth copy.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-05-20 10:13:33 -07:00
1 parent e332b97d52
commit c00aa90990
4 files changed
+132 -4

No files matched your search

+28
View File
@@ -5,6 +5,7 @@ import (
"net/http/httptest"
"testing"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
"github.com/seaweedfs/seaweedfs/weed/util/request_id"
"github.com/stretchr/testify/assert"
)
@@ -86,6 +87,33 @@ func TestGetAccessLogRemoteIP(t *testing.T) {
}
}
// TestGetAccessLogRequesterFromFallback reproduces the fallback audit path for
// GET/HEAD/IAM operations: authentication records the requester on a request
// copy the track() middleware never sees, yet the fallback audit entry (built
// from the original request) must still report the authenticated user.
func TestGetAccessLogRequesterFromFallback(t *testing.T) {
// track() installs the holder before authentication runs.
outer := s3_constants.EnsureIdentityHolder(httptest.NewRequest(http.MethodGet, "/bucket/object", nil))
// auth records the identity on a copy and hands that copy to the handler;
// the copy itself is discarded once the handler returns.
_ = outer.WithContext(s3_constants.SetIdentityNameInContext(outer.Context(), "admin"))
// The handler returned without logging, so track() builds the fallback entry
// from the original request.
log := GetAccessLog(outer, http.StatusOK, ErrNone)
assert.Equal(t, "admin", log.Requester, "fallback audit entry must report the authenticated requester")
}
func TestGetAccessLogRequesterAnonymous(t *testing.T) {
req := s3_constants.EnsureIdentityHolder(httptest.NewRequest(http.MethodGet, "/bucket/object", nil))
log := GetAccessLog(req, http.StatusOK, ErrNone)
assert.Empty(t, log.Requester, "anonymous request must not report a requester")
}
func TestAuditTrackingFlag(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/bucket/object", nil)
assert.False(t, AuditAlreadyLogged(req), "untracked request reports not logged")