Files
seaweedfs/weed/credential/propagating_store_auth_test.go
T
Chris LuandClaude Opus 5.5 d727c6970f plugin worker: stop forwarding the stream's admin token to job RPCs (#12391)
* plugin worker: keep the stream's admin token out of job contexts

The bearer token minted when the worker opens its control stream was
attached to connCtx, which every detection and execution context derives
from. Job RPCs therefore forwarded that connect-time token, and when a
job added its own fresh token (s3_lifecycle's LifecycleDelete) the S3
server validated the stale one first and rejected the call once it
expired.

Attach the token only to the WorkerStream call, as the Rust worker does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* credential: replace an inherited admin token instead of appending one

WithS3InternalAdminAuth appended its fresh Bearer token, so a context
that already carried an authorization value sent both, and the S3
gateway's checkAdminAuth validates only the first. Set the header on a
copy of the outgoing metadata so the fresh token is the only one sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* plugin worker: read the captured job metadata after the stream ends

Both result channels could be ready at once, letting select fail a
successful run. The handler reports before JobCompleted reaches admin,
so wait for the stream to finish and then read the captured metadata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* plugin worker: test that dropping the connection cancels running jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:26:12 -08:00

90 lines
3.1 KiB
Go

package credential
import (
"context"
"strings"
"testing"
"time"
"github.com/seaweedfs/seaweedfs/weed/security"
"github.com/seaweedfs/seaweedfs/weed/util"
"google.golang.org/grpc/metadata"
)
func TestWithS3InternalAdminAuth_NoKey_NoOp(t *testing.T) {
util.GetViper().Set("jwt.filer_signing.key", "")
ctx, ttl := WithS3InternalAdminAuth(context.Background())
if ttl != 0 {
t.Fatalf("expected zero TTL without key, got %v", ttl)
}
md, ok := metadata.FromOutgoingContext(ctx)
if ok && len(md.Get("authorization")) > 0 {
t.Fatalf("expected no authorization metadata without key, got %v", md.Get("authorization"))
}
}
func TestWithS3InternalAdminAuth_WithKey_AttachesBearer(t *testing.T) {
const k = "propagation-test-signing-key"
util.GetViper().Set("jwt.filer_signing.key", k)
defer util.GetViper().Set("jwt.filer_signing.key", "")
util.GetViper().Set("jwt.filer_signing.expires_after_seconds", 60)
ctx, ttl := WithS3InternalAdminAuth(context.Background())
if ttl != 60*time.Second {
t.Fatalf("expected 60s TTL, got %v", ttl)
}
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
t.Fatal("expected outgoing metadata to be set")
}
headers := md.Get("authorization")
if len(headers) != 1 {
t.Fatalf("expected one authorization header, got %v", headers)
}
if !strings.HasPrefix(headers[0], security.BearerPrefix) {
t.Fatalf("expected Bearer scheme, got %q", headers[0])
}
token := strings.TrimPrefix(headers[0], security.BearerPrefix)
parsed, err := security.DecodeJwt(security.SigningKey(k), security.EncodedJwt(token), &security.SeaweedFilerAdminClaims{})
if err != nil || parsed == nil || !parsed.Valid {
t.Fatalf("attached token failed signature validation: %v", err)
}
}
func TestWithS3InternalAdminAuth_ZeroExpiry_ZeroTTL(t *testing.T) {
const k = "propagation-test-signing-key"
util.GetViper().Set("jwt.filer_signing.key", k)
defer util.GetViper().Set("jwt.filer_signing.key", "")
util.GetViper().Set("jwt.filer_signing.expires_after_seconds", 0)
_, ttl := WithS3InternalAdminAuth(context.Background())
if ttl != 0 {
t.Fatalf("expected zero TTL for no-expiry token, got %v", ttl)
}
}
func TestWithS3InternalAdminAuth_ReplacesInheritedToken(t *testing.T) {
const k = "propagation-test-signing-key"
util.GetViper().Set("jwt.filer_signing.key", k)
defer util.GetViper().Set("jwt.filer_signing.key", "")
util.GetViper().Set("jwt.filer_signing.expires_after_seconds", 60)
parent := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
"authorization", security.BearerPrefix+"stale-token",
"x-other", "kept",
))
ctx, _ := WithS3InternalAdminAuth(parent)
md, _ := metadata.FromOutgoingContext(ctx)
headers := md.Get("authorization")
if len(headers) != 1 || headers[0] == security.BearerPrefix+"stale-token" {
t.Fatalf("expected only the fresh token, got %v", headers)
}
if got := md.Get("x-other"); len(got) != 1 || got[0] != "kept" {
t.Fatalf("expected other metadata to be kept, got %v", got)
}
parentMD, _ := metadata.FromOutgoingContext(parent)
if got := parentMD.Get("authorization"); len(got) != 1 || got[0] != security.BearerPrefix+"stale-token" {
t.Fatalf("parent metadata was modified: %v", got)
}
}