Files
go/post-policy_test.go
zeekayandHanzo Dev 970a28ec75 move: github.com/hanzoai/s3-go -> github.com/hanzos3/go
hanzoai/* is for top-level products. A product's own SDK belongs in that
product's org, which is what zap-proto/{go,zip,http} and hanzo-ds/go already do
— this is the S3 SDK catching up, not a new convention.

Module path and every internal import rewritten; builds and tests unchanged.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 12:17:27 -07:00

461 lines
11 KiB
Go

/*
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
* Copyright 2015-2023 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package minio
import (
"strings"
"testing"
"time"
"github.com/hanzos3/go/pkg/encrypt"
)
func TestPostPolicySetExpires(t *testing.T) {
tests := []struct {
name string
input time.Time
wantErr bool
wantResult string
}{
{
name: "valid time",
input: time.Date(2023, time.March, 2, 15, 4, 5, 0, time.UTC),
wantErr: false,
wantResult: "2023-03-02T15:04:05",
},
{
name: "time before 1970",
input: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetExpires(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetKey(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
wantResult string
}{
{
name: "valid key",
input: "my-object",
wantResult: `"eq","$key","my-object"`,
},
{
name: "empty key",
input: "",
wantErr: true,
},
{
name: "key with spaces",
input: " ",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetKey(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetKeyStartsWith(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "valid key prefix",
input: "my-prefix/",
want: `["starts-with","$key","my-prefix/"]`,
},
{
name: "empty prefix (allow any key)",
input: "",
want: `["starts-with","$key",""]`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetKeyStartsWith(tt.input)
if err != nil {
t.Errorf("%s: want no error, got: %v", tt.name, err)
}
if tt.want != "" {
result := pp.String()
if !strings.Contains(result, tt.want) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.want, result)
}
}
})
}
}
func TestPostPolicySetBucket(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
wantResult string
}{
{
name: "valid bucket",
input: "my-bucket",
wantResult: `"eq","$bucket","my-bucket"`,
},
{
name: "empty bucket",
input: "",
wantErr: true,
},
{
name: "bucket with spaces",
input: " ",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetBucket(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetCondition(t *testing.T) {
tests := []struct {
name string
matchType string
condition string
value string
wantErr bool
wantResult string
}{
{
name: "valid eq condition",
matchType: "eq",
condition: "X-Amz-Date",
value: "20210324T000000Z",
wantResult: `"eq","$X-Amz-Date","20210324T000000Z"`,
},
{
name: "empty value",
matchType: "eq",
condition: "X-Amz-Date",
value: "",
wantErr: true,
},
{
name: "invalid condition",
matchType: "eq",
condition: "Invalid-Condition",
value: "somevalue",
wantErr: true,
},
{
name: "valid starts-with condition",
matchType: "starts-with",
condition: "X-Amz-Credential",
value: "my-access-key",
wantResult: `"starts-with","$X-Amz-Credential","my-access-key"`,
},
{
name: "empty condition",
matchType: "eq",
condition: "",
value: "somevalue",
wantErr: true,
},
{
name: "empty matchType",
matchType: "",
condition: "X-Amz-Date",
value: "somevalue",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetCondition(tt.matchType, tt.condition, tt.value)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetTagging(t *testing.T) {
tests := []struct {
name string
tagging string
wantErr bool
wantResult string
}{
{
name: "valid tagging",
tagging: `<Tagging><TagSet><Tag><Key>key1</Key><Value>value1</Value></Tag></TagSet></Tagging>`,
wantResult: `"eq","$tagging","<Tagging><TagSet><Tag><Key>key1</Key><Value>value1</Value></Tag></TagSet></Tagging>"`,
},
{
name: "empty tagging",
tagging: "",
wantErr: true,
},
{
name: "whitespace tagging",
tagging: " ",
wantErr: true,
},
{
name: "invalid XML",
tagging: `<Tagging><TagSet><Tag><Key>key1</Key><Value>value1</Value></Tag></TagSet>`,
wantErr: true,
},
{
name: "invalid schema",
tagging: `<InvalidTagging></InvalidTagging>`,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetTagging(tt.tagging)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetUserMetadata(t *testing.T) {
tests := []struct {
name string
key string
value string
wantErr bool
wantResult string
}{
{
name: "valid metadata",
key: "user-key",
value: "user-value",
wantResult: `"eq","$x-amz-meta-user-key","user-value"`,
},
{
name: "empty key",
key: "",
value: "somevalue",
wantErr: true,
},
{
name: "empty value",
key: "user-key",
value: "",
wantErr: true,
},
{
name: "key with spaces",
key: " ",
value: "somevalue",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetUserMetadata(tt.key, tt.value)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetChecksum(t *testing.T) {
tests := []struct {
name string
checksum Checksum
wantErr bool
wantResult string
}{
{
name: "valid checksum SHA256",
checksum: ChecksumSHA256.ChecksumBytes([]byte("somerandomdata")),
wantResult: `[["eq","$x-amz-checksum-algorithm","SHA256"],["eq","$x-amz-checksum-sha256","29/7Qm/iMzZ1O3zMbO0luv6mYWyS6JIqPYV9lc8w1PA="]]`,
},
{
name: "valid checksum CRC32",
checksum: ChecksumCRC32.ChecksumBytes([]byte("somerandomdata")),
wantResult: `[["eq","$x-amz-checksum-algorithm","CRC32"],["eq","$x-amz-checksum-crc32","7sOPnw=="]]`,
},
{
name: "empty checksum",
checksum: Checksum{},
wantResult: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
err := pp.SetChecksum(tt.checksum)
if (err != nil) != tt.wantErr {
t.Errorf("%s: want error: %v, got: %v", tt.name, tt.wantErr, err)
}
if tt.wantResult != "" {
result := pp.String()
if !strings.Contains(result, tt.wantResult) {
t.Errorf("%s: want result to contain: '%s', got: '%s'", tt.name, tt.wantResult, result)
}
}
})
}
}
func TestPostPolicySetEncryption(t *testing.T) {
tests := []struct {
name string
sseType string
keyID string
want map[string]string
}{
{
name: "SSE-S3 encryption",
sseType: "SSE-S3",
keyID: "my-key-id",
want: map[string]string{
"X-Amz-Server-Side-Encryption": "aws:kms",
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": "my-key-id",
},
},
{
name: "SSE-C encryption with Key ID",
sseType: "SSE-C",
keyID: "my-key-id",
want: map[string]string{
"X-Amz-Server-Side-Encryption-Customer-Key": "bXktc2VjcmV0LWtleTEyMzQ1Njc4OTBhYmNkZWZnaGk=",
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": "T1mefJwyXBH43sRtfEgRZQ==",
"X-Amz-Server-Side-Encryption-Customer-Algorithm": "AES256",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pp := NewPostPolicy()
var sse encrypt.ServerSide
var err error
switch tt.sseType {
case "SSE-S3":
sse, err = encrypt.NewSSEKMS(tt.keyID, nil)
if err != nil {
t.Fatalf("Failed to create SSE-KMS: %v", err)
}
case "SSE-C":
sse, err = encrypt.NewSSEC([]byte("my-secret-key1234567890abcdefghi"))
if err != nil {
t.Fatalf("Failed to create SSE-C: %v", err)
}
default:
t.Fatalf("Unknown SSE type: %s", tt.sseType)
}
pp.SetEncryption(sse)
for k, v := range tt.want {
if pp.formData[k] != v {
t.Errorf("%s: want %s: %s, got: %s", tt.name, k, v, pp.formData[k])
}
}
})
}
}