s3api_bucket_handlers.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. package s3api
  2. import (
  3. "context"
  4. "encoding/xml"
  5. "errors"
  6. "fmt"
  7. "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
  8. "github.com/seaweedfs/seaweedfs/weed/util"
  9. "math"
  10. "net/http"
  11. "time"
  12. "github.com/seaweedfs/seaweedfs/weed/filer"
  13. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  14. "github.com/seaweedfs/seaweedfs/weed/storage/needle"
  15. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  16. "github.com/aws/aws-sdk-go/aws"
  17. "github.com/aws/aws-sdk-go/service/s3"
  18. "github.com/seaweedfs/seaweedfs/weed/glog"
  19. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  20. )
  21. type ListAllMyBucketsResult struct {
  22. XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult"`
  23. Owner *s3.Owner
  24. Buckets []*s3.Bucket `xml:"Buckets>Bucket"`
  25. }
  26. func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
  27. glog.V(3).Infof("ListBucketsHandler")
  28. var identity *Identity
  29. var s3Err s3err.ErrorCode
  30. if s3a.iam.isEnabled() {
  31. identity, s3Err = s3a.iam.authUser(r)
  32. if s3Err != s3err.ErrNone {
  33. s3err.WriteErrorResponse(w, r, s3Err)
  34. return
  35. }
  36. }
  37. var response ListAllMyBucketsResult
  38. entries, _, err := s3a.list(s3a.option.BucketsPath, "", "", false, math.MaxInt32)
  39. if err != nil {
  40. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  41. return
  42. }
  43. identityId := r.Header.Get(s3_constants.AmzIdentityId)
  44. var buckets []*s3.Bucket
  45. for _, entry := range entries {
  46. if entry.IsDirectory {
  47. if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name, "") {
  48. continue
  49. }
  50. buckets = append(buckets, &s3.Bucket{
  51. Name: aws.String(entry.Name),
  52. CreationDate: aws.Time(time.Unix(entry.Attributes.Crtime, 0).UTC()),
  53. })
  54. }
  55. }
  56. response = ListAllMyBucketsResult{
  57. Owner: &s3.Owner{
  58. ID: aws.String(identityId),
  59. DisplayName: aws.String(identityId),
  60. },
  61. Buckets: buckets,
  62. }
  63. writeSuccessResponseXML(w, r, response)
  64. }
  65. func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
  66. bucket, _ := s3_constants.GetBucketAndObject(r)
  67. glog.V(3).Infof("PutBucketHandler %s", bucket)
  68. // avoid duplicated buckets
  69. errCode := s3err.ErrNone
  70. if err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  71. if resp, err := client.CollectionList(context.Background(), &filer_pb.CollectionListRequest{
  72. IncludeEcVolumes: true,
  73. IncludeNormalVolumes: true,
  74. }); err != nil {
  75. glog.Errorf("list collection: %v", err)
  76. return fmt.Errorf("list collections: %v", err)
  77. } else {
  78. for _, c := range resp.Collections {
  79. if bucket == c.Name {
  80. errCode = s3err.ErrBucketAlreadyExists
  81. break
  82. }
  83. }
  84. }
  85. return nil
  86. }); err != nil {
  87. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  88. return
  89. }
  90. if exist, err := s3a.exists(s3a.option.BucketsPath, bucket, true); err == nil && exist {
  91. errCode = s3err.ErrBucketAlreadyExists
  92. }
  93. if errCode != s3err.ErrNone {
  94. s3err.WriteErrorResponse(w, r, errCode)
  95. return
  96. }
  97. if s3a.iam.isEnabled() {
  98. if _, errCode = s3a.iam.authRequest(r, s3_constants.ACTION_ADMIN); errCode != s3err.ErrNone {
  99. s3err.WriteErrorResponse(w, r, errCode)
  100. return
  101. }
  102. }
  103. fn := func(entry *filer_pb.Entry) {
  104. if identityId := r.Header.Get(s3_constants.AmzIdentityId); identityId != "" {
  105. if entry.Extended == nil {
  106. entry.Extended = make(map[string][]byte)
  107. }
  108. entry.Extended[s3_constants.AmzIdentityId] = []byte(identityId)
  109. }
  110. }
  111. // create the folder for bucket, but lazily create actual collection
  112. if err := s3a.mkdir(s3a.option.BucketsPath, bucket, fn); err != nil {
  113. glog.Errorf("PutBucketHandler mkdir: %v", err)
  114. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  115. return
  116. }
  117. w.Header().Set("Location", "/"+bucket)
  118. writeSuccessResponseEmpty(w, r)
  119. }
  120. func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
  121. bucket, _ := s3_constants.GetBucketAndObject(r)
  122. glog.V(3).Infof("DeleteBucketHandler %s", bucket)
  123. if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
  124. s3err.WriteErrorResponse(w, r, err)
  125. return
  126. }
  127. err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  128. if !s3a.option.AllowDeleteBucketNotEmpty {
  129. entries, _, err := s3a.list(s3a.option.BucketsPath+"/"+bucket, "", "", false, 2)
  130. if err != nil {
  131. return fmt.Errorf("failed to list bucket %s: %v", bucket, err)
  132. }
  133. for _, entry := range entries {
  134. if entry.Name != s3_constants.MultipartUploadsFolder {
  135. return errors.New(s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code)
  136. }
  137. }
  138. }
  139. // delete collection
  140. deleteCollectionRequest := &filer_pb.DeleteCollectionRequest{
  141. Collection: bucket,
  142. }
  143. glog.V(1).Infof("delete collection: %v", deleteCollectionRequest)
  144. if _, err := client.DeleteCollection(context.Background(), deleteCollectionRequest); err != nil {
  145. return fmt.Errorf("delete collection %s: %v", bucket, err)
  146. }
  147. return nil
  148. })
  149. if err != nil {
  150. s3ErrorCode := s3err.ErrInternalError
  151. if err.Error() == s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code {
  152. s3ErrorCode = s3err.ErrBucketNotEmpty
  153. }
  154. s3err.WriteErrorResponse(w, r, s3ErrorCode)
  155. return
  156. }
  157. err = s3a.rm(s3a.option.BucketsPath, bucket, false, true)
  158. if err != nil {
  159. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  160. return
  161. }
  162. s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
  163. }
  164. func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
  165. bucket, _ := s3_constants.GetBucketAndObject(r)
  166. glog.V(3).Infof("HeadBucketHandler %s", bucket)
  167. if entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket); entry == nil || err == filer_pb.ErrNotFound {
  168. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  169. return
  170. }
  171. writeSuccessResponseEmpty(w, r)
  172. }
  173. func (s3a *S3ApiServer) checkBucket(r *http.Request, bucket string) s3err.ErrorCode {
  174. entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  175. if entry == nil || err == filer_pb.ErrNotFound {
  176. return s3err.ErrNoSuchBucket
  177. }
  178. if !s3a.hasAccess(r, entry) {
  179. return s3err.ErrAccessDenied
  180. }
  181. return s3err.ErrNone
  182. }
  183. func (s3a *S3ApiServer) hasAccess(r *http.Request, entry *filer_pb.Entry) bool {
  184. isAdmin := r.Header.Get(s3_constants.AmzIsAdmin) != ""
  185. if isAdmin {
  186. return true
  187. }
  188. if entry.Extended == nil {
  189. return true
  190. }
  191. identityId := r.Header.Get(s3_constants.AmzIdentityId)
  192. if id, ok := entry.Extended[s3_constants.AmzIdentityId]; ok {
  193. if identityId != string(id) {
  194. return false
  195. }
  196. }
  197. return true
  198. }
  199. // GetBucketAclHandler Get Bucket ACL
  200. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html
  201. func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Request) {
  202. // collect parameters
  203. bucket, _ := s3_constants.GetBucketAndObject(r)
  204. glog.V(3).Infof("GetBucketAclHandler %s", bucket)
  205. if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
  206. s3err.WriteErrorResponse(w, r, err)
  207. return
  208. }
  209. response := AccessControlPolicy{}
  210. for _, ident := range s3a.iam.identities {
  211. if len(ident.Credentials) == 0 {
  212. continue
  213. }
  214. for _, action := range ident.Actions {
  215. if !action.overBucket(bucket) || action.getPermission() == "" {
  216. continue
  217. }
  218. id := ident.Credentials[0].AccessKey
  219. if response.Owner.DisplayName == "" && action.isOwner(bucket) && len(ident.Credentials) > 0 {
  220. response.Owner.DisplayName = ident.Name
  221. response.Owner.ID = id
  222. }
  223. response.AccessControlList.Grant = append(response.AccessControlList.Grant, Grant{
  224. Grantee: Grantee{
  225. ID: id,
  226. DisplayName: ident.Name,
  227. Type: "CanonicalUser",
  228. XMLXSI: "CanonicalUser",
  229. XMLNS: "http://www.w3.org/2001/XMLSchema-instance"},
  230. Permission: action.getPermission(),
  231. })
  232. }
  233. }
  234. writeSuccessResponseXML(w, r, response)
  235. }
  236. // GetBucketLifecycleConfigurationHandler Get Bucket Lifecycle configuration
  237. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html
  238. func (s3a *S3ApiServer) GetBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {
  239. // collect parameters
  240. bucket, _ := s3_constants.GetBucketAndObject(r)
  241. glog.V(3).Infof("GetBucketLifecycleConfigurationHandler %s", bucket)
  242. if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
  243. s3err.WriteErrorResponse(w, r, err)
  244. return
  245. }
  246. fc, err := filer.ReadFilerConf(s3a.option.Filer, s3a.option.GrpcDialOption, nil)
  247. if err != nil {
  248. glog.Errorf("GetBucketLifecycleConfigurationHandler: %s", err)
  249. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  250. return
  251. }
  252. ttls := fc.GetCollectionTtls(bucket)
  253. if len(ttls) == 0 {
  254. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchLifecycleConfiguration)
  255. return
  256. }
  257. response := Lifecycle{}
  258. for prefix, internalTtl := range ttls {
  259. ttl, _ := needle.ReadTTL(internalTtl)
  260. days := int(ttl.Minutes() / 60 / 24)
  261. if days == 0 {
  262. continue
  263. }
  264. response.Rules = append(response.Rules, Rule{
  265. Status: Enabled, Filter: Filter{
  266. Prefix: Prefix{string: prefix, set: true},
  267. set: true,
  268. },
  269. Expiration: Expiration{Days: days, set: true},
  270. })
  271. }
  272. writeSuccessResponseXML(w, r, response)
  273. }
  274. // PutBucketLifecycleConfigurationHandler Put Bucket Lifecycle configuration
  275. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html
  276. func (s3a *S3ApiServer) PutBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {
  277. s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
  278. }
  279. // DeleteBucketMetricsConfiguration Delete Bucket Lifecycle
  280. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html
  281. func (s3a *S3ApiServer) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
  282. s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
  283. }
  284. // GetBucketLocationHandler Get bucket location
  285. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html
  286. func (s3a *S3ApiServer) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
  287. writeSuccessResponseXML(w, r, LocationConstraint{})
  288. }
  289. // GetBucketRequestPaymentHandler Get bucket location
  290. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketRequestPayment.html
  291. func (s3a *S3ApiServer) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
  292. writeSuccessResponseXML(w, r, RequestPaymentConfiguration{Payer: "BucketOwner"})
  293. }
  294. // PutBucketOwnershipControls https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketOwnershipControls.html
  295. func (s3a *S3ApiServer) PutBucketOwnershipControls(w http.ResponseWriter, r *http.Request) {
  296. bucket, _ := s3_constants.GetBucketAndObject(r)
  297. glog.V(3).Infof("PutBucketOwnershipControls %s", bucket)
  298. errCode := s3a.checkAccessByOwnership(r, bucket)
  299. if errCode != s3err.ErrNone {
  300. s3err.WriteErrorResponse(w, r, errCode)
  301. return
  302. }
  303. if r.Body == nil || r.Body == http.NoBody {
  304. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  305. return
  306. }
  307. var v s3.OwnershipControls
  308. defer util.CloseRequest(r)
  309. err := xmlutil.UnmarshalXML(&v, xml.NewDecoder(r.Body), "")
  310. if err != nil {
  311. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  312. return
  313. }
  314. if len(v.Rules) != 1 {
  315. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  316. return
  317. }
  318. printOwnership := true
  319. ownership := *v.Rules[0].ObjectOwnership
  320. switch ownership {
  321. case s3_constants.OwnershipObjectWriter:
  322. case s3_constants.OwnershipBucketOwnerPreferred:
  323. case s3_constants.OwnershipBucketOwnerEnforced:
  324. printOwnership = false
  325. default:
  326. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  327. return
  328. }
  329. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  330. if err != nil {
  331. if err == filer_pb.ErrNotFound {
  332. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  333. return
  334. }
  335. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  336. return
  337. }
  338. oldOwnership, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey]
  339. if !ok || string(oldOwnership) != ownership {
  340. if bucketEntry.Extended == nil {
  341. bucketEntry.Extended = make(map[string][]byte)
  342. }
  343. bucketEntry.Extended[s3_constants.ExtOwnershipKey] = []byte(ownership)
  344. err = s3a.updateEntry(s3a.option.BucketsPath, bucketEntry)
  345. if err != nil {
  346. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  347. return
  348. }
  349. }
  350. if printOwnership {
  351. result := &s3.PutBucketOwnershipControlsInput{
  352. OwnershipControls: &v,
  353. }
  354. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, result)
  355. } else {
  356. writeSuccessResponseEmpty(w, r)
  357. }
  358. }
  359. // GetBucketOwnershipControls https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketOwnershipControls.html
  360. func (s3a *S3ApiServer) GetBucketOwnershipControls(w http.ResponseWriter, r *http.Request) {
  361. bucket, _ := s3_constants.GetBucketAndObject(r)
  362. glog.V(3).Infof("GetBucketOwnershipControls %s", bucket)
  363. errCode := s3a.checkAccessByOwnership(r, bucket)
  364. if errCode != s3err.ErrNone {
  365. s3err.WriteErrorResponse(w, r, errCode)
  366. return
  367. }
  368. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  369. if err != nil {
  370. if err == filer_pb.ErrNotFound {
  371. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  372. return
  373. }
  374. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  375. return
  376. }
  377. v, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey]
  378. if !ok {
  379. s3err.WriteErrorResponse(w, r, s3err.OwnershipControlsNotFoundError)
  380. return
  381. }
  382. ownership := string(v)
  383. result := &s3.PutBucketOwnershipControlsInput{
  384. OwnershipControls: &s3.OwnershipControls{
  385. Rules: []*s3.OwnershipControlsRule{
  386. {
  387. ObjectOwnership: &ownership,
  388. },
  389. },
  390. },
  391. }
  392. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, result)
  393. }
  394. // DeleteBucketOwnershipControls https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketOwnershipControls.html
  395. func (s3a *S3ApiServer) DeleteBucketOwnershipControls(w http.ResponseWriter, r *http.Request) {
  396. bucket, _ := s3_constants.GetBucketAndObject(r)
  397. glog.V(3).Infof("PutBucketOwnershipControls %s", bucket)
  398. errCode := s3a.checkAccessByOwnership(r, bucket)
  399. if errCode != s3err.ErrNone {
  400. s3err.WriteErrorResponse(w, r, errCode)
  401. return
  402. }
  403. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  404. if err != nil {
  405. if err == filer_pb.ErrNotFound {
  406. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  407. return
  408. }
  409. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  410. return
  411. }
  412. _, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey]
  413. if !ok {
  414. s3err.WriteErrorResponse(w, r, s3err.OwnershipControlsNotFoundError)
  415. return
  416. }
  417. delete(bucketEntry.Extended, s3_constants.ExtOwnershipKey)
  418. err = s3a.updateEntry(s3a.option.BucketsPath, bucketEntry)
  419. if err != nil {
  420. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  421. return
  422. }
  423. emptyOwnershipControls := &s3.OwnershipControls{
  424. Rules: []*s3.OwnershipControlsRule{},
  425. }
  426. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, emptyOwnershipControls)
  427. }