Skip to content

Commit

Permalink
add Crypto UserAgent and versionid
Browse files Browse the repository at this point in the history
  • Loading branch information
jojoliang committed May 14, 2021
1 parent 3094fa7 commit 6ddd327
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 58 deletions.
34 changes: 18 additions & 16 deletions cos.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,23 +134,23 @@ func NewClient(uri *BaseURL, httpClient *http.Client) *Client {
}

type Credential struct {
SecretID string
SecretKey string
SessionToken string
SecretID string
SecretKey string
SessionToken string
}

func (c *Client) GetCredential() *Credential {
auth, ok := c.client.Transport.(*AuthorizationTransport)
if !ok {
return nil
}
auth.rwLocker.Lock()
defer auth.rwLocker.Unlock()
return &Credential{
SecretID: auth.SecretID,
SecretKey: auth.SecretKey,
SessionToken: auth.SessionToken,
}
auth, ok := c.client.Transport.(*AuthorizationTransport)
if !ok {
return nil
}
auth.rwLocker.Lock()
defer auth.rwLocker.Unlock()
return &Credential{
SecretID: auth.SecretID,
SecretKey: auth.SecretKey,
SessionToken: auth.SessionToken,
}
}

func (c *Client) newRequest(ctx context.Context, baseURL *url.URL, uri, method string, body interface{}, optQuery interface{}, optHeader interface{}) (req *http.Request, err error) {
Expand Down Expand Up @@ -195,8 +195,10 @@ func (c *Client) newRequest(ctx context.Context, baseURL *url.URL, uri, method s
if contentMD5 != "" {
req.Header["Content-MD5"] = []string{contentMD5}
}
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
if v := req.Header.Get("User-Agent"); v == "" || !strings.HasPrefix(v, userAgent) {
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
}
}
if req.Header.Get("Content-Type") == "" && contentType != "" {
req.Header.Set("Content-Type", contentType)
Expand Down
19 changes: 12 additions & 7 deletions crypto/crypto_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (s *CryptoObjectService) Put(ctx context.Context, name string, r io.Reader,
opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentLength, strconv.FormatInt(opt.ContentLength, 10))
opt.ContentLength = cc.GetEncryptedLen(opt.ContentLength)
}
opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
addCryptoHeaders(opt.XOptionHeader, cc.GetCipherData())

return s.ObjectService.Put(ctx, name, reader, opt)
Expand All @@ -96,14 +97,14 @@ func (s *CryptoObjectService) PutFromFile(ctx context.Context, name, filePath st
return
}

func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.ObjectGetOptions) (*cos.Response, error) {
meta, err := s.ObjectService.Head(ctx, name, nil)
func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.ObjectGetOptions, id ...string) (*cos.Response, error) {
meta, err := s.ObjectService.Head(ctx, name, nil, id...)
if err != nil {
return meta, err
}
_isEncrypted := isEncrypted(&meta.Header)
if !_isEncrypted {
return s.ObjectService.Get(ctx, name, opt)
return s.ObjectService.Get(ctx, name, opt, id...)
}

envelope := getEnvelopeFromHeader(&meta.Header)
Expand All @@ -120,6 +121,10 @@ func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.Obj
return nil, fmt.Errorf("get content cipher from envelope failed: %v, object:%v", err, name)
}

opt = cos.CloneObjectGetOptions(opt)
if opt.XOptionHeader == nil {
opt.XOptionHeader = &http.Header{}
}
optRange, err := cos.GetRangeOptions(opt)
if err != nil {
return nil, err
Expand All @@ -132,7 +137,6 @@ func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.Obj
discardAlignLen = optRange.Start - adjustStart
if discardAlignLen > 0 {
optRange.Start = adjustStart
opt = cos.CloneObjectGetOptions(opt)
opt.Range = cos.FormatRangeOptions(optRange)
}

Expand All @@ -143,7 +147,8 @@ func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.Obj
return nil, fmt.Errorf("ContentCipher Clone failed:%v, bject:%v", err, name)
}
}
resp, err := s.ObjectService.Get(ctx, name, opt)
opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
resp, err := s.ObjectService.Get(ctx, name, opt, id...)
if err != nil {
return resp, err
}
Expand All @@ -161,8 +166,8 @@ func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.Obj
return resp, err
}

func (s *CryptoObjectService) GetToFile(ctx context.Context, name, localpath string, opt *cos.ObjectGetOptions) (*cos.Response, error) {
resp, err := s.Get(ctx, name, opt)
func (s *CryptoObjectService) GetToFile(ctx context.Context, name, localpath string, opt *cos.ObjectGetOptions, id ...string) (*cos.Response, error) {
resp, err := s.Get(ctx, name, opt, id...)
if err != nil {
return resp, err
}
Expand Down
18 changes: 18 additions & 0 deletions crypto/crypto_object_part.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (s *CryptoObjectService) InitiateMultipartUpload(ctx context.Context, name
opt.XOptionHeader.Add(COSClientSideEncryptionDataSize, strconv.FormatInt(cryptoCtx.DataSize, 10))
}
opt.XOptionHeader.Add(COSClientSideEncryptionPartSize, strconv.FormatInt(cryptoCtx.PartSize, 10))
opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
addCryptoHeaders(opt.XOptionHeader, contentCipher.GetCipherData())

return s.ObjectService.InitiateMultipartUpload(ctx, name, opt)
Expand All @@ -54,6 +55,10 @@ func (s *CryptoObjectService) UploadPart(ctx context.Context, name, uploadID str
return nil, fmt.Errorf("CryptoContext's PartSize is zero")
}
opt = cos.CloneObjectUploadPartOptions(opt)
if opt.XOptionHeader == nil {
opt.XOptionHeader = &http.Header{}
}
opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
if cryptoCtx.ContentCipher == nil {
return nil, fmt.Errorf("ContentCipher is nil, Please call the InitiateMultipartUpload")
}
Expand All @@ -77,3 +82,16 @@ func (s *CryptoObjectService) UploadPart(ctx context.Context, name, uploadID str
}
return s.ObjectService.UploadPart(ctx, name, uploadID, partNumber, reader, opt)
}

func (s *CryptoObjectService) CompleteMultipartUpload(ctx context.Context, name, uploadID string, opt *cos.CompleteMultipartUploadOptions) (*cos.CompleteMultipartUploadResult, *cos.Response, error) {
opt = cos.CloneCompleteMultipartUploadOptions(opt)
if opt.XOptionHeader == nil {
opt.XOptionHeader = &http.Header{}
}
opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
return s.ObjectService.CompleteMultipartUpload(ctx, name, uploadID, opt)
}

func (s *CryptoObjectService) CopyPart(ctx context.Context, name, uploadID string, partNumber int, sourceURL string, opt *cos.ObjectCopyPartOptions) (*cos.CopyPartResult, *cos.Response, error) {
return nil, nil, fmt.Errorf("CryptoObjectService doesn't support CopyPart")
}
155 changes: 155 additions & 0 deletions crypto/crypto_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,45 @@ func (s *CosTestSuite) TestPutGetDeleteObject_Normal_10MB() {
assert.Nil(s.T(), err, "DeleteObject Failed")
}

func (s *CosTestSuite) TestPutGetDeleteObject_VersionID() {
name := "test/objectPut" + time.Now().Format(time.RFC3339)
originData := make([]byte, 1024*1024*10+1)
_, err := rand.Read(originData)
f := bytes.NewReader(originData)

opt := &cos.BucketPutVersionOptions{
Status: "Enabled",
}
_, err = s.CClient.Bucket.PutVersioning(context.Background(), opt)
assert.Nil(s.T(), err, "PutVersioning Failed")
time.Sleep(3 * time.Second)

// 加密存储
resp, err := s.CClient.Object.Put(context.Background(), name, f, nil)
assert.Nil(s.T(), err, "PutObject Failed")
versionId := resp.Header.Get("x-cos-version-id")

_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")

// 解密读取
resp, err = s.CClient.Object.Get(context.Background(), name, nil, versionId)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")

delopt := &cos.ObjectDeleteOptions{
VersionId: versionId,
}
_, err = s.CClient.Object.Delete(context.Background(), name, delopt)
assert.Nil(s.T(), err, "DeleteObject Failed")

opt.Status = "Suspended"
_, err = s.CClient.Bucket.PutVersioning(context.Background(), opt)
assert.Nil(s.T(), err, "PutVersioning Failed")
}

func (s *CosTestSuite) TestPutGetDeleteObject_ZeroFile() {
name := "test/objectPut" + time.Now().Format(time.RFC3339)
// 加密存储
Expand Down Expand Up @@ -377,6 +416,122 @@ func (s *CosTestSuite) TestPutGetDeleteObject_WithListenerAndRange() {
assert.Nil(s.T(), err, "DeleteObject Failed")
}

func (s *CosTestSuite) TestPutGetDeleteObject_Copy() {
name := "test/objectPut" + time.Now().Format(time.RFC3339)
contentLength := 1024*1024*10 + 1
originData := make([]byte, contentLength)
_, err := rand.Read(originData)
f := bytes.NewReader(originData)

// 加密存储
popt := &cos.ObjectPutOptions{
nil,
&cos.ObjectPutHeaderOptions{
Listener: &cos.DefaultProgressListener{},
},
}
resp, err := s.CClient.Object.Put(context.Background(), name, f, popt)
assert.Nil(s.T(), err, "PutObject Failed")
encryptedDataCRC := resp.Header.Get("x-cos-hash-crc64ecma")
time.Sleep(3 * time.Second)
sourceURL := fmt.Sprintf("%s/%s", s.CClient.BaseURL.BucketURL.Host, name)
{
// x-cos-metadata-directive必须为Copy,否则丢失加密信息,无法解密
dest := "test/ObjectCopy1" + time.Now().Format(time.RFC3339)
res, _, err := s.CClient.Object.Copy(context.Background(), dest, sourceURL, nil)
assert.Nil(s.T(), err, "ObjectCopy Failed")
assert.Equal(s.T(), encryptedDataCRC, res.CRC64, "CRC isn't consistent, return:%v, want:%v", res.CRC64, encryptedDataCRC)

// Range解密读取
for i := 0; i < 3; i++ {
math_rand.Seed(time.Now().UnixNano())
rangeStart := math_rand.Intn(contentLength)
rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart)
if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
continue
}
opt := &cos.ObjectGetOptions{
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
Listener: &cos.DefaultProgressListener{},
}
resp, err := s.CClient.Object.Get(context.Background(), dest, opt)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData")
}
// 解密读取
resp, err := s.CClient.Object.Get(context.Background(), dest, nil)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")

_, err = s.CClient.Object.Delete(context.Background(), dest)
assert.Nil(s.T(), err, "DeleteObject Failed")
}
{
// x-cos-metadata-directive必须为Copy,否则丢失加密信息,无法解密
opt := &cos.ObjectCopyOptions{
&cos.ObjectCopyHeaderOptions{
XCosMetadataDirective: "Replaced",
},
nil,
}
dest := "test/ObjectCopy2" + time.Now().Format(time.RFC3339)
res, _, err := s.CClient.Object.Copy(context.Background(), dest, sourceURL, opt)
assert.Nil(s.T(), err, "ObjectCopy Failed")
assert.Equal(s.T(), encryptedDataCRC, res.CRC64, "CRC isn't consistent, return:%v, want:%v", res.CRC64, encryptedDataCRC)

// 解密读取
resp, err := s.CClient.Object.Get(context.Background(), dest, nil)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.NotEqual(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")

_, err = s.CClient.Object.Delete(context.Background(), dest)
assert.Nil(s.T(), err, "DeleteObject Failed")
}
{
// MultiCopy若是分块拷贝,则无法拷贝元数据
dest := "test/ObjectCopy3" + time.Now().Format(time.RFC3339)
res, _, err := s.CClient.Object.MultiCopy(context.Background(), dest, sourceURL, nil)
assert.Nil(s.T(), err, "ObjectMultiCopy Failed")
assert.Equal(s.T(), encryptedDataCRC, res.CRC64, "CRC isn't consistent, return:%v, want:%v", res.CRC64, encryptedDataCRC)
// Range解密读取
for i := 0; i < 3; i++ {
math_rand.Seed(time.Now().UnixNano())
rangeStart := math_rand.Intn(contentLength)
rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart)
if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
continue
}
opt := &cos.ObjectGetOptions{
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
Listener: &cos.DefaultProgressListener{},
}
resp, err := s.CClient.Object.Get(context.Background(), dest, opt)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData")
}
// 解密读取
resp, err := s.CClient.Object.Get(context.Background(), dest, nil)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")

_, err = s.CClient.Object.Delete(context.Background(), dest)
assert.Nil(s.T(), err, "DeleteObject Failed")
}

_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")
}

func TestCosTestSuite(t *testing.T) {
suite.Run(t, new(CosTestSuite))
}
Expand Down
2 changes: 1 addition & 1 deletion crypto/crypto_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
COSClientSideEncryptionUnencryptedContentMD5 = "x-cos-meta-client-side-encryption-unencrypted-content-md5"
COSClientSideEncryptionDataSize = "x-cos-meta-client-side-encryption-data-size"
COSClientSideEncryptionPartSize = "x-cos-meta-client-side-encryption-part-size"
COSClientUserAgent = "User-Agent"
UserAgent = "User-Agent"
)

const (
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ require (
github.com/google/uuid v1.1.1
github.com/mozillazg/go-httpheader v0.2.1
github.com/stretchr/testify v1.3.0
github.com/tencentcloud/tencentcloud-sdk-go v3.0.233+incompatible
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.233+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=

0 comments on commit 6ddd327

Please sign in to comment.