You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1112 lines
36 KiB

  1. // Copyright 2014 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package storage
  15. import (
  16. "bytes"
  17. "context"
  18. "crypto"
  19. "crypto/rand"
  20. "crypto/rsa"
  21. "crypto/sha256"
  22. "crypto/x509"
  23. "encoding/base64"
  24. "encoding/pem"
  25. "errors"
  26. "fmt"
  27. "net/http"
  28. "net/url"
  29. "reflect"
  30. "regexp"
  31. "sort"
  32. "strconv"
  33. "strings"
  34. "time"
  35. "unicode/utf8"
  36. "cloud.google.com/go/internal/optional"
  37. "cloud.google.com/go/internal/trace"
  38. "cloud.google.com/go/internal/version"
  39. "google.golang.org/api/googleapi"
  40. "google.golang.org/api/option"
  41. raw "google.golang.org/api/storage/v1"
  42. htransport "google.golang.org/api/transport/http"
  43. )
  44. var (
  45. // ErrBucketNotExist indicates that the bucket does not exist.
  46. ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
  47. // ErrObjectNotExist indicates that the object does not exist.
  48. ErrObjectNotExist = errors.New("storage: object doesn't exist")
  49. )
  50. const userAgent = "gcloud-golang-storage/20151204"
  51. const (
  52. // ScopeFullControl grants permissions to manage your
  53. // data and permissions in Google Cloud Storage.
  54. ScopeFullControl = raw.DevstorageFullControlScope
  55. // ScopeReadOnly grants permissions to
  56. // view your data in Google Cloud Storage.
  57. ScopeReadOnly = raw.DevstorageReadOnlyScope
  58. // ScopeReadWrite grants permissions to manage your
  59. // data in Google Cloud Storage.
  60. ScopeReadWrite = raw.DevstorageReadWriteScope
  61. )
  62. var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo)
  63. func setClientHeader(headers http.Header) {
  64. headers.Set("x-goog-api-client", xGoogHeader)
  65. }
  66. // Client is a client for interacting with Google Cloud Storage.
  67. //
  68. // Clients should be reused instead of created as needed.
  69. // The methods of Client are safe for concurrent use by multiple goroutines.
  70. type Client struct {
  71. hc *http.Client
  72. raw *raw.Service
  73. }
  74. // NewClient creates a new Google Cloud Storage client.
  75. // The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use option.WithScopes.
  76. func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
  77. o := []option.ClientOption{
  78. option.WithScopes(ScopeFullControl),
  79. option.WithUserAgent(userAgent),
  80. }
  81. opts = append(o, opts...)
  82. hc, ep, err := htransport.NewClient(ctx, opts...)
  83. if err != nil {
  84. return nil, fmt.Errorf("dialing: %v", err)
  85. }
  86. rawService, err := raw.New(hc)
  87. if err != nil {
  88. return nil, fmt.Errorf("storage client: %v", err)
  89. }
  90. if ep != "" {
  91. rawService.BasePath = ep
  92. }
  93. return &Client{
  94. hc: hc,
  95. raw: rawService,
  96. }, nil
  97. }
  98. // Close closes the Client.
  99. //
  100. // Close need not be called at program exit.
  101. func (c *Client) Close() error {
  102. // Set fields to nil so that subsequent uses will panic.
  103. c.hc = nil
  104. c.raw = nil
  105. return nil
  106. }
  107. // SignedURLOptions allows you to restrict the access to the signed URL.
  108. type SignedURLOptions struct {
  109. // GoogleAccessID represents the authorizer of the signed URL generation.
  110. // It is typically the Google service account client email address from
  111. // the Google Developers Console in the form of "xxx@developer.gserviceaccount.com".
  112. // Required.
  113. GoogleAccessID string
  114. // PrivateKey is the Google service account private key. It is obtainable
  115. // from the Google Developers Console.
  116. // At https://console.developers.google.com/project/<your-project-id>/apiui/credential,
  117. // create a service account client ID or reuse one of your existing service account
  118. // credentials. Click on the "Generate new P12 key" to generate and download
  119. // a new private key. Once you download the P12 file, use the following command
  120. // to convert it into a PEM file.
  121. //
  122. // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
  123. //
  124. // Provide the contents of the PEM file as a byte slice.
  125. // Exactly one of PrivateKey or SignBytes must be non-nil.
  126. PrivateKey []byte
  127. // SignBytes is a function for implementing custom signing.
  128. // If your application is running on Google App Engine, you can use appengine's internal signing function:
  129. // ctx := appengine.NewContext(request)
  130. // acc, _ := appengine.ServiceAccount(ctx)
  131. // url, err := SignedURL("bucket", "object", &SignedURLOptions{
  132. // GoogleAccessID: acc,
  133. // SignBytes: func(b []byte) ([]byte, error) {
  134. // _, signedBytes, err := appengine.SignBytes(ctx, b)
  135. // return signedBytes, err
  136. // },
  137. // // etc.
  138. // })
  139. //
  140. // Exactly one of PrivateKey or SignBytes must be non-nil.
  141. SignBytes func([]byte) ([]byte, error)
  142. // Method is the HTTP method to be used with the signed URL.
  143. // Signed URLs can be used with GET, HEAD, PUT, and DELETE requests.
  144. // Required.
  145. Method string
  146. // Expires is the expiration time on the signed URL. It must be
  147. // a datetime in the future.
  148. // Required.
  149. Expires time.Time
  150. // ContentType is the content type header the client must provide
  151. // to use the generated signed URL.
  152. // Optional.
  153. ContentType string
  154. // Headers is a list of extension headers the client must provide
  155. // in order to use the generated signed URL.
  156. // Optional.
  157. Headers []string
  158. // MD5 is the base64 encoded MD5 checksum of the file.
  159. // If provided, the client should provide the exact value on the request
  160. // header in order to use the signed URL.
  161. // Optional.
  162. MD5 string
  163. }
  164. var (
  165. canonicalHeaderRegexp = regexp.MustCompile(`(?i)^(x-goog-[^:]+):(.*)?$`)
  166. excludedCanonicalHeaders = map[string]bool{
  167. "x-goog-encryption-key": true,
  168. "x-goog-encryption-key-sha256": true,
  169. }
  170. )
  171. // sanitizeHeaders applies the specifications for canonical extension headers at
  172. // https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers.
  173. func sanitizeHeaders(hdrs []string) []string {
  174. headerMap := map[string][]string{}
  175. for _, hdr := range hdrs {
  176. // No leading or trailing whitespaces.
  177. sanitizedHeader := strings.TrimSpace(hdr)
  178. // Only keep canonical headers, discard any others.
  179. headerMatches := canonicalHeaderRegexp.FindStringSubmatch(sanitizedHeader)
  180. if len(headerMatches) == 0 {
  181. continue
  182. }
  183. header := strings.ToLower(strings.TrimSpace(headerMatches[1]))
  184. if excludedCanonicalHeaders[headerMatches[1]] {
  185. // Do not keep any deliberately excluded canonical headers when signing.
  186. continue
  187. }
  188. value := strings.TrimSpace(headerMatches[2])
  189. if len(value) > 0 {
  190. // Remove duplicate headers by appending the values of duplicates
  191. // in their order of appearance.
  192. headerMap[header] = append(headerMap[header], value)
  193. }
  194. }
  195. var sanitizedHeaders []string
  196. for header, values := range headerMap {
  197. // There should be no spaces around the colon separating the
  198. // header name from the header value or around the values
  199. // themselves. The values should be separated by commas.
  200. // NOTE: The semantics for headers without a value are not clear.
  201. // However from specifications these should be edge-cases
  202. // anyway and we should assume that there will be no
  203. // canonical headers using empty values. Any such headers
  204. // are discarded at the regexp stage above.
  205. sanitizedHeaders = append(
  206. sanitizedHeaders,
  207. fmt.Sprintf("%s:%s", header, strings.Join(values, ",")),
  208. )
  209. }
  210. sort.Strings(sanitizedHeaders)
  211. return sanitizedHeaders
  212. }
  213. // SignedURL returns a URL for the specified object. Signed URLs allow
  214. // the users access to a restricted resource for a limited time without having a
  215. // Google account or signing in. For more information about the signed
  216. // URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
  217. func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
  218. if opts == nil {
  219. return "", errors.New("storage: missing required SignedURLOptions")
  220. }
  221. if opts.GoogleAccessID == "" {
  222. return "", errors.New("storage: missing required GoogleAccessID")
  223. }
  224. if (opts.PrivateKey == nil) == (opts.SignBytes == nil) {
  225. return "", errors.New("storage: exactly one of PrivateKey or SignedBytes must be set")
  226. }
  227. if opts.Method == "" {
  228. return "", errors.New("storage: missing required method option")
  229. }
  230. if opts.Expires.IsZero() {
  231. return "", errors.New("storage: missing required expires option")
  232. }
  233. if opts.MD5 != "" {
  234. md5, err := base64.StdEncoding.DecodeString(opts.MD5)
  235. if err != nil || len(md5) != 16 {
  236. return "", errors.New("storage: invalid MD5 checksum")
  237. }
  238. }
  239. opts.Headers = sanitizeHeaders(opts.Headers)
  240. signBytes := opts.SignBytes
  241. if opts.PrivateKey != nil {
  242. key, err := parseKey(opts.PrivateKey)
  243. if err != nil {
  244. return "", err
  245. }
  246. signBytes = func(b []byte) ([]byte, error) {
  247. sum := sha256.Sum256(b)
  248. return rsa.SignPKCS1v15(
  249. rand.Reader,
  250. key,
  251. crypto.SHA256,
  252. sum[:],
  253. )
  254. }
  255. }
  256. u := &url.URL{
  257. Path: fmt.Sprintf("/%s/%s", bucket, name),
  258. }
  259. buf := &bytes.Buffer{}
  260. fmt.Fprintf(buf, "%s\n", opts.Method)
  261. fmt.Fprintf(buf, "%s\n", opts.MD5)
  262. fmt.Fprintf(buf, "%s\n", opts.ContentType)
  263. fmt.Fprintf(buf, "%d\n", opts.Expires.Unix())
  264. if len(opts.Headers) > 0 {
  265. fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n"))
  266. }
  267. fmt.Fprintf(buf, "%s", u.String())
  268. b, err := signBytes(buf.Bytes())
  269. if err != nil {
  270. return "", err
  271. }
  272. encoded := base64.StdEncoding.EncodeToString(b)
  273. u.Scheme = "https"
  274. u.Host = "storage.googleapis.com"
  275. q := u.Query()
  276. q.Set("GoogleAccessId", opts.GoogleAccessID)
  277. q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix()))
  278. q.Set("Signature", string(encoded))
  279. u.RawQuery = q.Encode()
  280. return u.String(), nil
  281. }
  282. // ObjectHandle provides operations on an object in a Google Cloud Storage bucket.
  283. // Use BucketHandle.Object to get a handle.
  284. type ObjectHandle struct {
  285. c *Client
  286. bucket string
  287. object string
  288. acl ACLHandle
  289. gen int64 // a negative value indicates latest
  290. conds *Conditions
  291. encryptionKey []byte // AES-256 key
  292. userProject string // for requester-pays buckets
  293. readCompressed bool // Accept-Encoding: gzip
  294. }
  295. // ACL provides access to the object's access control list.
  296. // This controls who can read and write this object.
  297. // This call does not perform any network operations.
  298. func (o *ObjectHandle) ACL() *ACLHandle {
  299. return &o.acl
  300. }
  301. // Generation returns a new ObjectHandle that operates on a specific generation
  302. // of the object.
  303. // By default, the handle operates on the latest generation. Not
  304. // all operations work when given a specific generation; check the API
  305. // endpoints at https://cloud.google.com/storage/docs/json_api/ for details.
  306. func (o *ObjectHandle) Generation(gen int64) *ObjectHandle {
  307. o2 := *o
  308. o2.gen = gen
  309. return &o2
  310. }
  311. // If returns a new ObjectHandle that applies a set of preconditions.
  312. // Preconditions already set on the ObjectHandle are ignored.
  313. // Operations on the new handle will return an error if the preconditions are not
  314. // satisfied. See https://cloud.google.com/storage/docs/generations-preconditions
  315. // for more details.
  316. func (o *ObjectHandle) If(conds Conditions) *ObjectHandle {
  317. o2 := *o
  318. o2.conds = &conds
  319. return &o2
  320. }
  321. // Key returns a new ObjectHandle that uses the supplied encryption
  322. // key to encrypt and decrypt the object's contents.
  323. //
  324. // Encryption key must be a 32-byte AES-256 key.
  325. // See https://cloud.google.com/storage/docs/encryption for details.
  326. func (o *ObjectHandle) Key(encryptionKey []byte) *ObjectHandle {
  327. o2 := *o
  328. o2.encryptionKey = encryptionKey
  329. return &o2
  330. }
  331. // Attrs returns meta information about the object.
  332. // ErrObjectNotExist will be returned if the object is not found.
  333. func (o *ObjectHandle) Attrs(ctx context.Context) (attrs *ObjectAttrs, err error) {
  334. ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Attrs")
  335. defer func() { trace.EndSpan(ctx, err) }()
  336. if err := o.validate(); err != nil {
  337. return nil, err
  338. }
  339. call := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx)
  340. if err := applyConds("Attrs", o.gen, o.conds, call); err != nil {
  341. return nil, err
  342. }
  343. if o.userProject != "" {
  344. call.UserProject(o.userProject)
  345. }
  346. if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
  347. return nil, err
  348. }
  349. var obj *raw.Object
  350. setClientHeader(call.Header())
  351. err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
  352. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  353. return nil, ErrObjectNotExist
  354. }
  355. if err != nil {
  356. return nil, err
  357. }
  358. return newObject(obj), nil
  359. }
  360. // Update updates an object with the provided attributes.
  361. // All zero-value attributes are ignored.
  362. // ErrObjectNotExist will be returned if the object is not found.
  363. func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (oa *ObjectAttrs, err error) {
  364. ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Update")
  365. defer func() { trace.EndSpan(ctx, err) }()
  366. if err := o.validate(); err != nil {
  367. return nil, err
  368. }
  369. var attrs ObjectAttrs
  370. // Lists of fields to send, and set to null, in the JSON.
  371. var forceSendFields, nullFields []string
  372. if uattrs.ContentType != nil {
  373. attrs.ContentType = optional.ToString(uattrs.ContentType)
  374. // For ContentType, sending the empty string is a no-op.
  375. // Instead we send a null.
  376. if attrs.ContentType == "" {
  377. nullFields = append(nullFields, "ContentType")
  378. } else {
  379. forceSendFields = append(forceSendFields, "ContentType")
  380. }
  381. }
  382. if uattrs.ContentLanguage != nil {
  383. attrs.ContentLanguage = optional.ToString(uattrs.ContentLanguage)
  384. // For ContentLanguage it's an error to send the empty string.
  385. // Instead we send a null.
  386. if attrs.ContentLanguage == "" {
  387. nullFields = append(nullFields, "ContentLanguage")
  388. } else {
  389. forceSendFields = append(forceSendFields, "ContentLanguage")
  390. }
  391. }
  392. if uattrs.ContentEncoding != nil {
  393. attrs.ContentEncoding = optional.ToString(uattrs.ContentEncoding)
  394. forceSendFields = append(forceSendFields, "ContentEncoding")
  395. }
  396. if uattrs.ContentDisposition != nil {
  397. attrs.ContentDisposition = optional.ToString(uattrs.ContentDisposition)
  398. forceSendFields = append(forceSendFields, "ContentDisposition")
  399. }
  400. if uattrs.CacheControl != nil {
  401. attrs.CacheControl = optional.ToString(uattrs.CacheControl)
  402. forceSendFields = append(forceSendFields, "CacheControl")
  403. }
  404. if uattrs.EventBasedHold != nil {
  405. attrs.EventBasedHold = optional.ToBool(uattrs.EventBasedHold)
  406. forceSendFields = append(forceSendFields, "EventBasedHold")
  407. }
  408. if uattrs.TemporaryHold != nil {
  409. attrs.TemporaryHold = optional.ToBool(uattrs.TemporaryHold)
  410. forceSendFields = append(forceSendFields, "TemporaryHold")
  411. }
  412. if uattrs.Metadata != nil {
  413. attrs.Metadata = uattrs.Metadata
  414. if len(attrs.Metadata) == 0 {
  415. // Sending the empty map is a no-op. We send null instead.
  416. nullFields = append(nullFields, "Metadata")
  417. } else {
  418. forceSendFields = append(forceSendFields, "Metadata")
  419. }
  420. }
  421. if uattrs.ACL != nil {
  422. attrs.ACL = uattrs.ACL
  423. // It's an error to attempt to delete the ACL, so
  424. // we don't append to nullFields here.
  425. forceSendFields = append(forceSendFields, "Acl")
  426. }
  427. rawObj := attrs.toRawObject(o.bucket)
  428. rawObj.ForceSendFields = forceSendFields
  429. rawObj.NullFields = nullFields
  430. call := o.c.raw.Objects.Patch(o.bucket, o.object, rawObj).Projection("full").Context(ctx)
  431. if err := applyConds("Update", o.gen, o.conds, call); err != nil {
  432. return nil, err
  433. }
  434. if o.userProject != "" {
  435. call.UserProject(o.userProject)
  436. }
  437. if uattrs.PredefinedACL != "" {
  438. call.PredefinedAcl(uattrs.PredefinedACL)
  439. }
  440. if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
  441. return nil, err
  442. }
  443. var obj *raw.Object
  444. setClientHeader(call.Header())
  445. err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
  446. if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
  447. return nil, ErrObjectNotExist
  448. }
  449. if err != nil {
  450. return nil, err
  451. }
  452. return newObject(obj), nil
  453. }
  454. // BucketName returns the name of the bucket.
  455. func (o *ObjectHandle) BucketName() string {
  456. return o.bucket
  457. }
  458. // ObjectName returns the name of the object.
  459. func (o *ObjectHandle) ObjectName() string {
  460. return o.object
  461. }
  462. // ObjectAttrsToUpdate is used to update the attributes of an object.
  463. // Only fields set to non-nil values will be updated.
  464. // Set a field to its zero value to delete it.
  465. //
  466. // For example, to change ContentType and delete ContentEncoding and
  467. // Metadata, use
  468. // ObjectAttrsToUpdate{
  469. // ContentType: "text/html",
  470. // ContentEncoding: "",
  471. // Metadata: map[string]string{},
  472. // }
  473. type ObjectAttrsToUpdate struct {
  474. EventBasedHold optional.Bool
  475. TemporaryHold optional.Bool
  476. ContentType optional.String
  477. ContentLanguage optional.String
  478. ContentEncoding optional.String
  479. ContentDisposition optional.String
  480. CacheControl optional.String
  481. Metadata map[string]string // set to map[string]string{} to delete
  482. ACL []ACLRule
  483. // If not empty, applies a predefined set of access controls. ACL must be nil.
  484. // See https://cloud.google.com/storage/docs/json_api/v1/objects/patch.
  485. PredefinedACL string
  486. }
  487. // Delete deletes the single specified object.
  488. func (o *ObjectHandle) Delete(ctx context.Context) error {
  489. if err := o.validate(); err != nil {
  490. return err
  491. }
  492. call := o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx)
  493. if err := applyConds("Delete", o.gen, o.conds, call); err != nil {
  494. return err
  495. }
  496. if o.userProject != "" {
  497. call.UserProject(o.userProject)
  498. }
  499. // Encryption doesn't apply to Delete.
  500. setClientHeader(call.Header())
  501. err := runWithRetry(ctx, func() error { return call.Do() })
  502. switch e := err.(type) {
  503. case nil:
  504. return nil
  505. case *googleapi.Error:
  506. if e.Code == http.StatusNotFound {
  507. return ErrObjectNotExist
  508. }
  509. }
  510. return err
  511. }
  512. // ReadCompressed when true causes the read to happen without decompressing.
  513. func (o *ObjectHandle) ReadCompressed(compressed bool) *ObjectHandle {
  514. o2 := *o
  515. o2.readCompressed = compressed
  516. return &o2
  517. }
  518. // NewWriter returns a storage Writer that writes to the GCS object
  519. // associated with this ObjectHandle.
  520. //
  521. // A new object will be created unless an object with this name already exists.
  522. // Otherwise any previous object with the same name will be replaced.
  523. // The object will not be available (and any previous object will remain)
  524. // until Close has been called.
  525. //
  526. // Attributes can be set on the object by modifying the returned Writer's
  527. // ObjectAttrs field before the first call to Write. If no ContentType
  528. // attribute is specified, the content type will be automatically sniffed
  529. // using net/http.DetectContentType.
  530. //
  531. // It is the caller's responsibility to call Close when writing is done. To
  532. // stop writing without saving the data, cancel the context.
  533. func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer {
  534. return &Writer{
  535. ctx: ctx,
  536. o: o,
  537. donec: make(chan struct{}),
  538. ObjectAttrs: ObjectAttrs{Name: o.object},
  539. ChunkSize: googleapi.DefaultUploadChunkSize,
  540. }
  541. }
  542. func (o *ObjectHandle) validate() error {
  543. if o.bucket == "" {
  544. return errors.New("storage: bucket name is empty")
  545. }
  546. if o.object == "" {
  547. return errors.New("storage: object name is empty")
  548. }
  549. if !utf8.ValidString(o.object) {
  550. return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
  551. }
  552. return nil
  553. }
  554. // parseKey converts the binary contents of a private key file to an
  555. // *rsa.PrivateKey. It detects whether the private key is in a PEM container or
  556. // not. If so, it extracts the private key from PEM container before
  557. // conversion. It only supports PEM containers with no passphrase.
  558. func parseKey(key []byte) (*rsa.PrivateKey, error) {
  559. if block, _ := pem.Decode(key); block != nil {
  560. key = block.Bytes
  561. }
  562. parsedKey, err := x509.ParsePKCS8PrivateKey(key)
  563. if err != nil {
  564. parsedKey, err = x509.ParsePKCS1PrivateKey(key)
  565. if err != nil {
  566. return nil, err
  567. }
  568. }
  569. parsed, ok := parsedKey.(*rsa.PrivateKey)
  570. if !ok {
  571. return nil, errors.New("oauth2: private key is invalid")
  572. }
  573. return parsed, nil
  574. }
  575. // toRawObject copies the editable attributes from o to the raw library's Object type.
  576. func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object {
  577. var ret string
  578. if !o.RetentionExpirationTime.IsZero() {
  579. ret = o.RetentionExpirationTime.Format(time.RFC3339)
  580. }
  581. return &raw.Object{
  582. Bucket: bucket,
  583. Name: o.Name,
  584. EventBasedHold: o.EventBasedHold,
  585. TemporaryHold: o.TemporaryHold,
  586. RetentionExpirationTime: ret,
  587. ContentType: o.ContentType,
  588. ContentEncoding: o.ContentEncoding,
  589. ContentLanguage: o.ContentLanguage,
  590. CacheControl: o.CacheControl,
  591. ContentDisposition: o.ContentDisposition,
  592. StorageClass: o.StorageClass,
  593. Acl: toRawObjectACL(o.ACL),
  594. Metadata: o.Metadata,
  595. }
  596. }
  597. // ObjectAttrs represents the metadata for a Google Cloud Storage (GCS) object.
  598. type ObjectAttrs struct {
  599. // Bucket is the name of the bucket containing this GCS object.
  600. // This field is read-only.
  601. Bucket string
  602. // Name is the name of the object within the bucket.
  603. // This field is read-only.
  604. Name string
  605. // ContentType is the MIME type of the object's content.
  606. ContentType string
  607. // ContentLanguage is the content language of the object's content.
  608. ContentLanguage string
  609. // CacheControl is the Cache-Control header to be sent in the response
  610. // headers when serving the object data.
  611. CacheControl string
  612. // EventBasedHold specifies whether an object is under event-based hold. New
  613. // objects created in a bucket whose DefaultEventBasedHold is set will
  614. // default to that value.
  615. EventBasedHold bool
  616. // TemporaryHold specifies whether an object is under temporary hold. While
  617. // this flag is set to true, the object is protected against deletion and
  618. // overwrites.
  619. TemporaryHold bool
  620. // RetentionExpirationTime is a server-determined value that specifies the
  621. // earliest time that the object's retention period expires.
  622. // This is a read-only field.
  623. RetentionExpirationTime time.Time
  624. // ACL is the list of access control rules for the object.
  625. ACL []ACLRule
  626. // If not empty, applies a predefined set of access controls. It should be set
  627. // only when writing, copying or composing an object. When copying or composing,
  628. // it acts as the destinationPredefinedAcl parameter.
  629. // PredefinedACL is always empty for ObjectAttrs returned from the service.
  630. // See https://cloud.google.com/storage/docs/json_api/v1/objects/insert
  631. // for valid values.
  632. PredefinedACL string
  633. // Owner is the owner of the object. This field is read-only.
  634. //
  635. // If non-zero, it is in the form of "user-<userId>".
  636. Owner string
  637. // Size is the length of the object's content. This field is read-only.
  638. Size int64
  639. // ContentEncoding is the encoding of the object's content.
  640. ContentEncoding string
  641. // ContentDisposition is the optional Content-Disposition header of the object
  642. // sent in the response headers.
  643. ContentDisposition string
  644. // MD5 is the MD5 hash of the object's content. This field is read-only,
  645. // except when used from a Writer. If set on a Writer, the uploaded
  646. // data is rejected if its MD5 hash does not match this field.
  647. MD5 []byte
  648. // CRC32C is the CRC32 checksum of the object's content using
  649. // the Castagnoli93 polynomial. This field is read-only, except when
  650. // used from a Writer. If set on a Writer and Writer.SendCRC32C
  651. // is true, the uploaded data is rejected if its CRC32c hash does not
  652. // match this field.
  653. CRC32C uint32
  654. // MediaLink is an URL to the object's content. This field is read-only.
  655. MediaLink string
  656. // Metadata represents user-provided metadata, in key/value pairs.
  657. // It can be nil if no metadata is provided.
  658. Metadata map[string]string
  659. // Generation is the generation number of the object's content.
  660. // This field is read-only.
  661. Generation int64
  662. // Metageneration is the version of the metadata for this
  663. // object at this generation. This field is used for preconditions
  664. // and for detecting changes in metadata. A metageneration number
  665. // is only meaningful in the context of a particular generation
  666. // of a particular object. This field is read-only.
  667. Metageneration int64
  668. // StorageClass is the storage class of the object.
  669. // This value defines how objects in the bucket are stored and
  670. // determines the SLA and the cost of storage. Typical values are
  671. // "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
  672. // and "DURABLE_REDUCED_AVAILABILITY".
  673. // It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
  674. // or "REGIONAL" depending on the bucket's location settings.
  675. StorageClass string
  676. // Created is the time the object was created. This field is read-only.
  677. Created time.Time
  678. // Deleted is the time the object was deleted.
  679. // If not deleted, it is the zero value. This field is read-only.
  680. Deleted time.Time
  681. // Updated is the creation or modification time of the object.
  682. // For buckets with versioning enabled, changing an object's
  683. // metadata does not change this property. This field is read-only.
  684. Updated time.Time
  685. // CustomerKeySHA256 is the base64-encoded SHA-256 hash of the
  686. // customer-supplied encryption key for the object. It is empty if there is
  687. // no customer-supplied encryption key.
  688. // See // https://cloud.google.com/storage/docs/encryption for more about
  689. // encryption in Google Cloud Storage.
  690. CustomerKeySHA256 string
  691. // Cloud KMS key name, in the form
  692. // projects/P/locations/L/keyRings/R/cryptoKeys/K, used to encrypt this object,
  693. // if the object is encrypted by such a key.
  694. //
  695. // Providing both a KMSKeyName and a customer-supplied encryption key (via
  696. // ObjectHandle.Key) will result in an error when writing an object.
  697. KMSKeyName string
  698. // Prefix is set only for ObjectAttrs which represent synthetic "directory
  699. // entries" when iterating over buckets using Query.Delimiter. See
  700. // ObjectIterator.Next. When set, no other fields in ObjectAttrs will be
  701. // populated.
  702. Prefix string
  703. }
  704. // convertTime converts a time in RFC3339 format to time.Time.
  705. // If any error occurs in parsing, the zero-value time.Time is silently returned.
  706. func convertTime(t string) time.Time {
  707. var r time.Time
  708. if t != "" {
  709. r, _ = time.Parse(time.RFC3339, t)
  710. }
  711. return r
  712. }
  713. func newObject(o *raw.Object) *ObjectAttrs {
  714. if o == nil {
  715. return nil
  716. }
  717. owner := ""
  718. if o.Owner != nil {
  719. owner = o.Owner.Entity
  720. }
  721. md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
  722. crc32c, _ := decodeUint32(o.Crc32c)
  723. var sha256 string
  724. if o.CustomerEncryption != nil {
  725. sha256 = o.CustomerEncryption.KeySha256
  726. }
  727. return &ObjectAttrs{
  728. Bucket: o.Bucket,
  729. Name: o.Name,
  730. ContentType: o.ContentType,
  731. ContentLanguage: o.ContentLanguage,
  732. CacheControl: o.CacheControl,
  733. EventBasedHold: o.EventBasedHold,
  734. TemporaryHold: o.TemporaryHold,
  735. RetentionExpirationTime: convertTime(o.RetentionExpirationTime),
  736. ACL: toObjectACLRules(o.Acl),
  737. Owner: owner,
  738. ContentEncoding: o.ContentEncoding,
  739. ContentDisposition: o.ContentDisposition,
  740. Size: int64(o.Size),
  741. MD5: md5,
  742. CRC32C: crc32c,
  743. MediaLink: o.MediaLink,
  744. Metadata: o.Metadata,
  745. Generation: o.Generation,
  746. Metageneration: o.Metageneration,
  747. StorageClass: o.StorageClass,
  748. CustomerKeySHA256: sha256,
  749. KMSKeyName: o.KmsKeyName,
  750. Created: convertTime(o.TimeCreated),
  751. Deleted: convertTime(o.TimeDeleted),
  752. Updated: convertTime(o.Updated),
  753. }
  754. }
  755. // Decode a uint32 encoded in Base64 in big-endian byte order.
  756. func decodeUint32(b64 string) (uint32, error) {
  757. d, err := base64.StdEncoding.DecodeString(b64)
  758. if err != nil {
  759. return 0, err
  760. }
  761. if len(d) != 4 {
  762. return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d)
  763. }
  764. return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil
  765. }
  766. // Encode a uint32 as Base64 in big-endian byte order.
  767. func encodeUint32(u uint32) string {
  768. b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)}
  769. return base64.StdEncoding.EncodeToString(b)
  770. }
  771. // Query represents a query to filter objects from a bucket.
  772. type Query struct {
  773. // Delimiter returns results in a directory-like fashion.
  774. // Results will contain only objects whose names, aside from the
  775. // prefix, do not contain delimiter. Objects whose names,
  776. // aside from the prefix, contain delimiter will have their name,
  777. // truncated after the delimiter, returned in prefixes.
  778. // Duplicate prefixes are omitted.
  779. // Optional.
  780. Delimiter string
  781. // Prefix is the prefix filter to query objects
  782. // whose names begin with this prefix.
  783. // Optional.
  784. Prefix string
  785. // Versions indicates whether multiple versions of the same
  786. // object will be included in the results.
  787. Versions bool
  788. }
  789. // Conditions constrain methods to act on specific generations of
  790. // objects.
  791. //
  792. // The zero value is an empty set of constraints. Not all conditions or
  793. // combinations of conditions are applicable to all methods.
  794. // See https://cloud.google.com/storage/docs/generations-preconditions
  795. // for details on how these operate.
  796. type Conditions struct {
  797. // Generation constraints.
  798. // At most one of the following can be set to a non-zero value.
  799. // GenerationMatch specifies that the object must have the given generation
  800. // for the operation to occur.
  801. // If GenerationMatch is zero, it has no effect.
  802. // Use DoesNotExist to specify that the object does not exist in the bucket.
  803. GenerationMatch int64
  804. // GenerationNotMatch specifies that the object must not have the given
  805. // generation for the operation to occur.
  806. // If GenerationNotMatch is zero, it has no effect.
  807. GenerationNotMatch int64
  808. // DoesNotExist specifies that the object must not exist in the bucket for
  809. // the operation to occur.
  810. // If DoesNotExist is false, it has no effect.
  811. DoesNotExist bool
  812. // Metadata generation constraints.
  813. // At most one of the following can be set to a non-zero value.
  814. // MetagenerationMatch specifies that the object must have the given
  815. // metageneration for the operation to occur.
  816. // If MetagenerationMatch is zero, it has no effect.
  817. MetagenerationMatch int64
  818. // MetagenerationNotMatch specifies that the object must not have the given
  819. // metageneration for the operation to occur.
  820. // If MetagenerationNotMatch is zero, it has no effect.
  821. MetagenerationNotMatch int64
  822. }
  823. func (c *Conditions) validate(method string) error {
  824. if *c == (Conditions{}) {
  825. return fmt.Errorf("storage: %s: empty conditions", method)
  826. }
  827. if !c.isGenerationValid() {
  828. return fmt.Errorf("storage: %s: multiple conditions specified for generation", method)
  829. }
  830. if !c.isMetagenerationValid() {
  831. return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
  832. }
  833. return nil
  834. }
  835. func (c *Conditions) isGenerationValid() bool {
  836. n := 0
  837. if c.GenerationMatch != 0 {
  838. n++
  839. }
  840. if c.GenerationNotMatch != 0 {
  841. n++
  842. }
  843. if c.DoesNotExist {
  844. n++
  845. }
  846. return n <= 1
  847. }
  848. func (c *Conditions) isMetagenerationValid() bool {
  849. return c.MetagenerationMatch == 0 || c.MetagenerationNotMatch == 0
  850. }
  851. // applyConds modifies the provided call using the conditions in conds.
  852. // call is something that quacks like a *raw.WhateverCall.
  853. func applyConds(method string, gen int64, conds *Conditions, call interface{}) error {
  854. cval := reflect.ValueOf(call)
  855. if gen >= 0 {
  856. if !setConditionField(cval, "Generation", gen) {
  857. return fmt.Errorf("storage: %s: generation not supported", method)
  858. }
  859. }
  860. if conds == nil {
  861. return nil
  862. }
  863. if err := conds.validate(method); err != nil {
  864. return err
  865. }
  866. switch {
  867. case conds.GenerationMatch != 0:
  868. if !setConditionField(cval, "IfGenerationMatch", conds.GenerationMatch) {
  869. return fmt.Errorf("storage: %s: ifGenerationMatch not supported", method)
  870. }
  871. case conds.GenerationNotMatch != 0:
  872. if !setConditionField(cval, "IfGenerationNotMatch", conds.GenerationNotMatch) {
  873. return fmt.Errorf("storage: %s: ifGenerationNotMatch not supported", method)
  874. }
  875. case conds.DoesNotExist:
  876. if !setConditionField(cval, "IfGenerationMatch", int64(0)) {
  877. return fmt.Errorf("storage: %s: DoesNotExist not supported", method)
  878. }
  879. }
  880. switch {
  881. case conds.MetagenerationMatch != 0:
  882. if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
  883. return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
  884. }
  885. case conds.MetagenerationNotMatch != 0:
  886. if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
  887. return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
  888. }
  889. }
  890. return nil
  891. }
  892. func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error {
  893. if gen >= 0 {
  894. call.SourceGeneration(gen)
  895. }
  896. if conds == nil {
  897. return nil
  898. }
  899. if err := conds.validate("CopyTo source"); err != nil {
  900. return err
  901. }
  902. switch {
  903. case conds.GenerationMatch != 0:
  904. call.IfSourceGenerationMatch(conds.GenerationMatch)
  905. case conds.GenerationNotMatch != 0:
  906. call.IfSourceGenerationNotMatch(conds.GenerationNotMatch)
  907. case conds.DoesNotExist:
  908. call.IfSourceGenerationMatch(0)
  909. }
  910. switch {
  911. case conds.MetagenerationMatch != 0:
  912. call.IfSourceMetagenerationMatch(conds.MetagenerationMatch)
  913. case conds.MetagenerationNotMatch != 0:
  914. call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch)
  915. }
  916. return nil
  917. }
  918. // setConditionField sets a field on a *raw.WhateverCall.
  919. // We can't use anonymous interfaces because the return type is
  920. // different, since the field setters are builders.
  921. func setConditionField(call reflect.Value, name string, value interface{}) bool {
  922. m := call.MethodByName(name)
  923. if !m.IsValid() {
  924. return false
  925. }
  926. m.Call([]reflect.Value{reflect.ValueOf(value)})
  927. return true
  928. }
  929. // conditionsQuery returns the generation and conditions as a URL query
  930. // string suitable for URL.RawQuery. It assumes that the conditions
  931. // have been validated.
  932. func conditionsQuery(gen int64, conds *Conditions) string {
  933. // URL escapes are elided because integer strings are URL-safe.
  934. var buf []byte
  935. appendParam := func(s string, n int64) {
  936. if len(buf) > 0 {
  937. buf = append(buf, '&')
  938. }
  939. buf = append(buf, s...)
  940. buf = strconv.AppendInt(buf, n, 10)
  941. }
  942. if gen >= 0 {
  943. appendParam("generation=", gen)
  944. }
  945. if conds == nil {
  946. return string(buf)
  947. }
  948. switch {
  949. case conds.GenerationMatch != 0:
  950. appendParam("ifGenerationMatch=", conds.GenerationMatch)
  951. case conds.GenerationNotMatch != 0:
  952. appendParam("ifGenerationNotMatch=", conds.GenerationNotMatch)
  953. case conds.DoesNotExist:
  954. appendParam("ifGenerationMatch=", 0)
  955. }
  956. switch {
  957. case conds.MetagenerationMatch != 0:
  958. appendParam("ifMetagenerationMatch=", conds.MetagenerationMatch)
  959. case conds.MetagenerationNotMatch != 0:
  960. appendParam("ifMetagenerationNotMatch=", conds.MetagenerationNotMatch)
  961. }
  962. return string(buf)
  963. }
  964. // composeSourceObj wraps a *raw.ComposeRequestSourceObjects, but adds the methods
  965. // that modifyCall searches for by name.
  966. type composeSourceObj struct {
  967. src *raw.ComposeRequestSourceObjects
  968. }
  969. func (c composeSourceObj) Generation(gen int64) {
  970. c.src.Generation = gen
  971. }
  972. func (c composeSourceObj) IfGenerationMatch(gen int64) {
  973. // It's safe to overwrite ObjectPreconditions, since its only field is
  974. // IfGenerationMatch.
  975. c.src.ObjectPreconditions = &raw.ComposeRequestSourceObjectsObjectPreconditions{
  976. IfGenerationMatch: gen,
  977. }
  978. }
  979. func setEncryptionHeaders(headers http.Header, key []byte, copySource bool) error {
  980. if key == nil {
  981. return nil
  982. }
  983. // TODO(jbd): Ask the API team to return a more user-friendly error
  984. // and avoid doing this check at the client level.
  985. if len(key) != 32 {
  986. return errors.New("storage: not a 32-byte AES-256 key")
  987. }
  988. var cs string
  989. if copySource {
  990. cs = "copy-source-"
  991. }
  992. headers.Set("x-goog-"+cs+"encryption-algorithm", "AES256")
  993. headers.Set("x-goog-"+cs+"encryption-key", base64.StdEncoding.EncodeToString(key))
  994. keyHash := sha256.Sum256(key)
  995. headers.Set("x-goog-"+cs+"encryption-key-sha256", base64.StdEncoding.EncodeToString(keyHash[:]))
  996. return nil
  997. }
  998. // ServiceAccount fetches the email address of the given project's Google Cloud Storage service account.
  999. func (c *Client) ServiceAccount(ctx context.Context, projectID string) (string, error) {
  1000. r := c.raw.Projects.ServiceAccount.Get(projectID)
  1001. res, err := r.Context(ctx).Do()
  1002. if err != nil {
  1003. return "", err
  1004. }
  1005. return res.EmailAddress, nil
  1006. }