diff --git a/backend/azure/azure.go b/backend/azure/azure.go index ce12ccc..f484ab0 100644 --- a/backend/azure/azure.go +++ b/backend/azure/azure.go @@ -399,16 +399,40 @@ func (az *Azure) DeleteObjects(ctx context.Context, input *s3.DeleteObjectsInput } func (az *Azure) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3.CopyObjectOutput, error) { - client, err := az.getBlobClient(*input.Bucket, *input.Key) + containerClient, err := az.getContainerClient(*input.Bucket) if err != nil { return nil, err } + res, err := containerClient.GetProperties(ctx, &container.GetPropertiesOptions{}) + if err != nil { + return nil, azureErrToS3Err(err) + } + + dstContainerAcl, err := getAclFromMetadata(res.Metadata, aclKeyCapital) + if err != nil { + return nil, err + } + + err = auth.VerifyACL(*dstContainerAcl, *input.ExpectedBucketOwner, types.PermissionWrite, false) + if err != nil { + return nil, err + } + + if strings.Join([]string{*input.Bucket, *input.Key}, "/") == *input.CopySource && isMetaSame(res.Metadata, input.Metadata) { + return nil, s3err.GetAPIError(s3err.ErrInvalidCopyDest) + } + tags, err := parseTags(input.Tagging) if err != nil { return nil, err } + client, err := az.getBlobClient(*input.Bucket, *input.Key) + if err != nil { + return nil, err + } + resp, err := client.CopyFromURL(ctx, az.serviceURL+"/"+*input.CopySource, &blob.CopyFromURLOptions{ BlobTags: tags, Metadata: parseMetadata(input.Metadata), @@ -942,3 +966,21 @@ func getAclFromMetadata(meta map[string]*string, key aclKey) (*auth.ACL, error) return &acl, nil } + +func isMetaSame(azMeta map[string]*string, awsMeta map[string]string) bool { + if len(azMeta) != len(awsMeta)+1 { + return false + } + + for key, val := range azMeta { + if key == string(aclKeyCapital) || key == string(aclKeyLower) { + continue + } + awsVal, ok := awsMeta[key] + if !ok || awsVal != *val { + return false + } + } + + return true +}