2020-08-19 23:36:00 +00:00
package handler
import (
2020-08-22 02:45:15 +00:00
"encoding/xml"
2024-04-24 12:20:11 +00:00
"fmt"
2020-08-19 23:36:00 +00:00
"net/http"
2021-09-07 06:17:12 +00:00
"strconv"
2022-05-26 13:11:14 +00:00
"strings"
2020-08-19 23:36:00 +00:00
2023-03-07 14:38:08 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
2023-07-05 14:05:45 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
2023-03-07 14:38:08 +00:00
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
2024-04-09 14:16:12 +00:00
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
2020-08-19 23:36:00 +00:00
)
2023-02-20 14:49:12 +00:00
// limitation of AWS https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html
const maxObjectsToDelete = 1000
2022-04-13 16:56:58 +00:00
// DeleteObjectsRequest -- xml carrying the object key names which should be deleted.
2020-08-22 02:45:15 +00:00
type DeleteObjectsRequest struct {
2023-10-09 12:34:51 +00:00
XMLName xml . Name ` xml:"http://s3.amazonaws.com/doc/2006-03-01/ Delete" json:"-" `
2020-08-22 02:45:15 +00:00
// Element to enable quiet mode for the request
2023-10-09 12:34:51 +00:00
Quiet bool ` xml:"Quiet,omitempty" `
2020-08-22 02:45:15 +00:00
// List of objects to be deleted
Objects [ ] ObjectIdentifier ` xml:"Object" `
}
2022-04-13 16:56:58 +00:00
// ObjectIdentifier carries the key name for the object to delete.
2020-08-22 02:45:15 +00:00
type ObjectIdentifier struct {
ObjectName string ` xml:"Key" `
2021-08-10 12:08:15 +00:00
VersionID string ` xml:"VersionId,omitempty" `
2020-08-22 02:45:15 +00:00
}
2022-04-13 16:56:58 +00:00
// DeletedObject carries the key name for the object to delete.
2021-09-07 06:17:12 +00:00
type DeletedObject struct {
ObjectIdentifier
DeleteMarker bool ` xml:"DeleteMarker,omitempty" `
DeleteMarkerVersionID string ` xml:"DeleteMarkerVersionId,omitempty" `
}
2020-08-22 02:45:15 +00:00
// DeleteError structure.
type DeleteError struct {
2023-10-17 08:20:37 +00:00
Code string ` xml:"Code,omitempty" `
Message string ` xml:"Message,omitempty" `
Key string ` xml:"Key,omitempty" `
VersionID string ` xml:"VersionId,omitempty" `
2020-08-22 02:45:15 +00:00
}
// DeleteObjectsResponse container for multiple object deletes.
type DeleteObjectsResponse struct {
XMLName xml . Name ` xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-" `
// Collection of all deleted objects
2021-09-07 06:17:12 +00:00
DeletedObjects [ ] DeletedObject ` xml:"Deleted,omitempty" `
2020-08-22 02:45:15 +00:00
// Collection of errors deleting certain objects.
Errors [ ] DeleteError ` xml:"Error,omitempty" `
}
2020-08-19 23:36:00 +00:00
func ( h * handler ) DeleteObjectHandler ( w http . ResponseWriter , r * http . Request ) {
2023-06-09 13:19:23 +00:00
ctx := r . Context ( )
2023-07-05 14:05:45 +00:00
reqInfo := middleware . GetReqInfo ( ctx )
2022-03-31 06:24:57 +00:00
versionID := reqInfo . URL . Query ( ) . Get ( api . QueryVersionID )
2021-08-10 12:08:15 +00:00
versionedObject := [ ] * layer . VersionedObject { {
Name : reqInfo . ObjectName ,
2022-03-31 06:24:57 +00:00
VersionID : versionID ,
2021-08-10 12:08:15 +00:00
} }
2021-08-05 09:18:52 +00:00
2022-03-18 13:04:09 +00:00
bktInfo , err := h . getBucketAndCheckOwner ( r , reqInfo . BucketName )
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get bucket info" , reqInfo , err )
2021-08-23 08:19:41 +00:00
return
}
2023-06-09 13:19:23 +00:00
bktSettings , err := h . obj . GetBucketSettings ( ctx , bktInfo )
2022-03-31 06:24:29 +00:00
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get bucket settings" , reqInfo , err )
2022-03-31 06:24:29 +00:00
return
}
2024-07-23 15:12:16 +00:00
networkInfo , err := h . obj . GetNetworkInfo ( ctx )
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get network info" , reqInfo , err )
2024-07-23 15:12:16 +00:00
return
}
2022-03-31 06:24:29 +00:00
p := & layer . DeleteObjectParams {
2024-07-23 15:12:16 +00:00
BktInfo : bktInfo ,
Objects : versionedObject ,
Settings : bktSettings ,
NetworkInfo : networkInfo ,
2022-03-31 06:24:29 +00:00
}
2023-06-09 13:19:23 +00:00
deletedObjects := h . obj . DeleteObjects ( ctx , p )
2021-09-07 06:17:12 +00:00
deletedObject := deletedObjects [ 0 ]
2022-06-24 12:39:30 +00:00
if deletedObject . Error != nil {
if isErrObjectLocked ( deletedObject . Error ) {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "object is locked" , reqInfo , errors . GetAPIError ( errors . ErrAccessDenied ) )
2022-07-25 13:00:35 +00:00
} else {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not delete object" , reqInfo , deletedObject . Error )
2022-03-09 10:23:33 +00:00
}
2022-07-25 13:00:35 +00:00
return
2021-09-07 06:17:12 +00:00
}
2020-08-19 23:36:00 +00:00
2022-07-25 13:00:35 +00:00
if deletedObject . VersionID != "" {
w . Header ( ) . Set ( api . AmzVersionID , deletedObject . VersionID )
}
2021-09-07 06:17:12 +00:00
if deletedObject . DeleteMarkVersion != "" {
w . Header ( ) . Set ( api . AmzDeleteMarker , strconv . FormatBool ( true ) )
2022-07-25 13:00:35 +00:00
if deletedObject . VersionID == "" {
w . Header ( ) . Set ( api . AmzVersionID , deletedObject . DeleteMarkVersion )
}
2020-08-19 23:36:00 +00:00
}
w . WriteHeader ( http . StatusNoContent )
}
2020-08-20 19:35:39 +00:00
2022-04-21 08:49:56 +00:00
func isErrObjectLocked ( err error ) bool {
switch err . ( type ) {
default :
2022-05-26 13:11:14 +00:00
return strings . Contains ( err . Error ( ) , "object is locked" )
2023-08-16 14:08:12 +00:00
case * apistatus . ObjectLocked :
2022-04-21 08:49:56 +00:00
return true
}
}
2021-05-13 19:25:32 +00:00
// DeleteMultipleObjectsHandler handles multiple delete requests.
2020-08-20 19:35:39 +00:00
func ( h * handler ) DeleteMultipleObjectsHandler ( w http . ResponseWriter , r * http . Request ) {
2023-06-09 13:19:23 +00:00
ctx := r . Context ( )
2023-07-05 14:05:45 +00:00
reqInfo := middleware . GetReqInfo ( ctx )
2020-08-22 02:45:15 +00:00
2022-04-13 16:56:58 +00:00
// Content-Md5 is required and should be set
2020-08-22 02:45:15 +00:00
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if _ , ok := r . Header [ api . ContentMD5 ] ; ! ok {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "missing Content-MD5" , reqInfo , errors . GetAPIError ( errors . ErrMissingContentMD5 ) )
2020-08-22 02:45:15 +00:00
return
}
// Content-Length is required and should be non-zero
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if r . ContentLength <= 0 {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "missing Content-Length" , reqInfo , errors . GetAPIError ( errors . ErrMissingContentLength ) )
2020-08-22 02:45:15 +00:00
return
}
// Unmarshal list of keys to be deleted.
requested := & DeleteObjectsRequest { }
2023-10-09 12:34:51 +00:00
if err := h . cfg . NewXMLDecoder ( r . Body ) . Decode ( requested ) ; err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "couldn't decode body" , reqInfo , fmt . Errorf ( "%w: %s" , errors . GetAPIError ( errors . ErrMalformedXML ) , err . Error ( ) ) )
2020-08-22 02:45:15 +00:00
return
}
2023-02-20 14:49:12 +00:00
if len ( requested . Objects ) == 0 || len ( requested . Objects ) > maxObjectsToDelete {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "number of objects to delete must be greater than 0 and less or equal to 1000" , reqInfo , errors . GetAPIError ( errors . ErrMalformedXML ) )
2023-02-20 14:49:12 +00:00
return
}
2024-06-26 13:31:43 +00:00
unique := make ( map [ string ] struct { } )
2021-08-10 12:08:15 +00:00
toRemove := make ( [ ] * layer . VersionedObject , 0 , len ( requested . Objects ) )
2020-08-22 02:45:15 +00:00
for _ , obj := range requested . Objects {
2021-08-10 12:08:15 +00:00
versionedObj := & layer . VersionedObject {
Name : obj . ObjectName ,
VersionID : obj . VersionID ,
}
2024-06-26 13:31:43 +00:00
key := versionedObj . String ( )
if _ , ok := unique [ key ] ; ! ok {
toRemove = append ( toRemove , versionedObj )
unique [ key ] = struct { } { }
}
2020-08-22 02:45:15 +00:00
}
response := & DeleteObjectsResponse {
Errors : make ( [ ] DeleteError , 0 , len ( toRemove ) ) ,
2021-09-07 06:17:12 +00:00
DeletedObjects : make ( [ ] DeletedObject , 0 , len ( toRemove ) ) ,
2020-08-22 02:45:15 +00:00
}
2022-03-18 13:04:09 +00:00
bktInfo , err := h . getBucketAndCheckOwner ( r , reqInfo . BucketName )
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get bucket info" , reqInfo , err )
2021-08-23 08:19:41 +00:00
return
}
2023-06-09 13:19:23 +00:00
bktSettings , err := h . obj . GetBucketSettings ( ctx , bktInfo )
2022-06-24 12:39:30 +00:00
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get bucket settings" , reqInfo , err )
2022-06-24 12:39:30 +00:00
return
}
2024-07-23 15:12:16 +00:00
networkInfo , err := h . obj . GetNetworkInfo ( ctx )
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get network info" , reqInfo , err )
2024-07-23 15:12:16 +00:00
return
}
2022-03-31 06:24:29 +00:00
p := & layer . DeleteObjectParams {
2024-07-23 15:12:16 +00:00
BktInfo : bktInfo ,
Objects : toRemove ,
Settings : bktSettings ,
NetworkInfo : networkInfo ,
IsMultiple : true ,
2021-09-07 06:17:12 +00:00
}
2023-06-09 13:19:23 +00:00
deletedObjects := h . obj . DeleteObjects ( ctx , p )
2020-08-22 02:45:15 +00:00
2021-09-07 06:17:12 +00:00
for _ , obj := range deletedObjects {
2022-04-04 11:38:27 +00:00
if obj . Error != nil {
2021-09-07 06:17:12 +00:00
code := "BadRequest"
if s3err , ok := obj . Error . ( errors . Error ) ; ok {
code = s3err . Code
}
response . Errors = append ( response . Errors , DeleteError {
Code : code ,
Message : obj . Error . Error ( ) ,
Key : obj . Name ,
VersionID : obj . VersionID ,
} )
2022-04-04 11:38:27 +00:00
} else if ! requested . Quiet {
deletedObj := DeletedObject {
ObjectIdentifier : ObjectIdentifier {
ObjectName : obj . Name ,
VersionID : obj . VersionID ,
} ,
DeleteMarkerVersionID : obj . DeleteMarkVersion ,
}
if deletedObj . DeleteMarkerVersionID != "" {
deletedObj . DeleteMarker = true
}
response . DeletedObjects = append ( response . DeletedObjects , deletedObj )
2020-08-22 02:45:15 +00:00
}
}
2023-07-05 14:05:45 +00:00
if err = middleware . EncodeToResponse ( w , response ) ; err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not write response" , reqInfo , err )
2020-10-24 13:09:22 +00:00
return
2020-08-22 02:45:15 +00:00
}
2020-08-20 19:35:39 +00:00
}
2021-06-23 20:25:00 +00:00
func ( h * handler ) DeleteBucketHandler ( w http . ResponseWriter , r * http . Request ) {
2024-10-25 01:36:18 +00:00
ctx := r . Context ( )
reqInfo := middleware . GetReqInfo ( ctx )
2022-03-18 13:04:09 +00:00
bktInfo , err := h . getBucketAndCheckOwner ( r , reqInfo . BucketName )
if err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "could not get bucket info" , reqInfo , err )
2021-08-23 08:19:41 +00:00
return
}
2022-03-18 13:04:09 +00:00
2022-06-22 13:22:28 +00:00
var sessionToken * session . Container
2024-10-25 01:36:18 +00:00
boxData , err := middleware . GetBoxData ( ctx )
2022-06-22 13:22:28 +00:00
if err == nil {
sessionToken = boxData . Gate . SessionTokenForDelete ( )
}
2024-07-19 19:20:40 +00:00
skipObjCheck := false
if value , ok := r . Header [ api . AmzForceBucketDelete ] ; ok {
s := value [ 0 ]
if s == "true" {
skipObjCheck = true
}
}
2024-10-25 01:36:18 +00:00
if err = h . obj . DeleteBucket ( ctx , & layer . DeleteBucketParams {
2022-06-22 13:22:28 +00:00
BktInfo : bktInfo ,
SessionToken : sessionToken ,
2024-07-19 19:20:40 +00:00
SkipCheck : skipObjCheck ,
2022-06-22 13:22:28 +00:00
} ) ; err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "couldn't delete bucket" , reqInfo , err )
2024-05-17 17:34:37 +00:00
return
2021-06-23 20:25:00 +00:00
}
2024-04-09 14:16:12 +00:00
chainIDs := [ ] chain . ID {
getBucketChainID ( chain . S3 , bktInfo ) ,
getBucketChainID ( chain . Ingress , bktInfo ) ,
getBucketCannedChainID ( chain . S3 , bktInfo . CID ) ,
getBucketCannedChainID ( chain . Ingress , bktInfo . CID ) ,
}
if err = h . ape . DeleteBucketPolicy ( reqInfo . Namespace , bktInfo . CID , chainIDs ) ; err != nil {
2024-10-25 01:36:18 +00:00
h . logAndSendError ( ctx , w , "failed to delete policy from storage" , reqInfo , err )
2024-04-09 14:16:12 +00:00
return
}
2021-09-21 09:31:23 +00:00
w . WriteHeader ( http . StatusNoContent )
2021-06-23 20:25:00 +00:00
}