github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/storage/s3.go (about) 1 // Copyright (c) 2016 Readium Foundation 2 // 3 // Redistribution and use in source and binary forms, with or without modification, 4 // are permitted provided that the following conditions are met: 5 // 6 // 1. Redistributions of source code must retain the above copyright notice, this 7 // list of conditions and the following disclaimer. 8 // 2. Redistributions in binary form must reproduce the above copyright notice, 9 // this list of conditions and the following disclaimer in the documentation and/or 10 // other materials provided with the distribution. 11 // 3. Neither the name of the organization nor the names of its contributors may be 12 // used to endorse or promote products derived from this software without specific 13 // prior written permission 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 package storage 27 28 import ( 29 "fmt" 30 "io" 31 32 "github.com/aws/aws-sdk-go/aws" 33 "github.com/aws/aws-sdk-go/aws/credentials" 34 "github.com/aws/aws-sdk-go/aws/session" 35 "github.com/aws/aws-sdk-go/service/s3" 36 ) 37 38 type s3store struct { 39 bucket string 40 client *s3.S3 41 } 42 43 type s3item struct { 44 bucket string 45 key string 46 store *s3store 47 } 48 49 func (i s3item) Key() string { 50 return i.key 51 } 52 53 func (i s3item) PublicURL() string { 54 return fmt.Sprintf("http://%s/%s/%s", i.store.client.Endpoint, i.bucket, i.key) 55 } 56 57 func (i s3item) Contents() (io.ReadCloser, error) { 58 resp, err := i.store.client.GetObject(&s3.GetObjectInput{ 59 Bucket: aws.String(i.store.bucket), 60 Key: aws.String(i.key), 61 }) 62 63 return resp.Body, err 64 } 65 66 func (s *s3store) Add(key string, r io.ReadSeeker) (Item, error) { 67 _, err := s.client.PutObject(&s3.PutObjectInput{ 68 Bucket: aws.String(s.bucket), 69 Key: aws.String(key), 70 Body: r, 71 }) 72 73 item := s3item{bucket: s.bucket, key: key, store: s} 74 75 return item, err 76 } 77 78 func (s *s3store) Get(key string) (Item, error) { 79 _, err := s.client.HeadObject(&s3.HeadObjectInput{ 80 Bucket: aws.String(s.bucket), 81 Key: aws.String(key), 82 }) 83 return s3item{bucket: s.bucket, key: key, store: s}, err 84 } 85 86 func (s *s3store) Remove(key string) error { 87 _, err := s.client.DeleteObject(&s3.DeleteObjectInput{ 88 Bucket: aws.String(s.bucket), 89 Key: aws.String(key), 90 }) 91 92 return err 93 } 94 95 func (s *s3store) List() ([]Item, error) { 96 objects, err := s.client.ListObjects(&s3.ListObjectsInput{ 97 Bucket: aws.String(s.bucket), 98 }) 99 100 if err != nil { 101 return nil, err 102 } 103 104 var items []Item 105 106 for _, o := range objects.Contents { 107 items = append(items, s3item{bucket: s.bucket, key: *o.Key, store: s}) 108 } 109 110 return items, nil 111 } 112 113 // S3Config structure 114 type S3Config struct { 115 Bucket string 116 Endpoint string 117 Region string 118 119 ID string 120 Secret string 121 Token string 122 123 DisableSSL bool 124 ForcePathStyle bool 125 } 126 127 // S3 inits and S3 storage 128 func S3(config S3Config) (Store, error) { 129 awsConfig := &aws.Config{ 130 CredentialsChainVerboseErrors: aws.Bool(true), 131 DisableSSL: aws.Bool(config.DisableSSL), 132 S3ForcePathStyle: aws.Bool(config.ForcePathStyle), 133 Region: aws.String(config.Region), 134 Endpoint: aws.String(config.Endpoint)} 135 136 // Credentials defaults to a chain of credential providers to search for credentials in environment 137 // variables, shared credential file, and EC2 Instance Roles. 138 // Therefore, we only explicitly define static credentials if these are present in config 139 if config.ID != "" && config.Secret != "" { 140 awsConfig.Credentials = credentials.NewStaticCredentials(config.ID, config.Secret, config.Token) 141 } 142 143 s3session, err := session.NewSession(awsConfig) 144 return &s3store{client: s3.New(s3session), bucket: config.Bucket}, err 145 }