GCS: add ACL support

This commit is contained in:
Nicola Murino 2021-11-15 21:57:41 +01:00
parent 52f3a98cc8
commit 24b0352eb6
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
9 changed files with 40 additions and 3 deletions

View file

@ -2637,6 +2637,7 @@ func TestUserHiddenFields(t *testing.T) {
u2.FsConfig.Provider = sdk.GCSFilesystemProvider u2.FsConfig.Provider = sdk.GCSFilesystemProvider
u2.FsConfig.GCSConfig.Bucket = "test" u2.FsConfig.GCSConfig.Bucket = "test"
u2.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("fake credentials") u2.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("fake credentials")
u2.FsConfig.GCSConfig.ACL = "bucketOwnerRead"
user2, _, err := httpdtest.AddUser(u2, http.StatusCreated) user2, _, err := httpdtest.AddUser(u2, http.StatusCreated)
assert.NoError(t, err) assert.NoError(t, err)
@ -2804,6 +2805,7 @@ func TestUserHiddenFields(t *testing.T) {
// update the GCS user and check that the credentials are preserved // update the GCS user and check that the credentials are preserved
user2.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret() user2.FsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
user2.FsConfig.GCSConfig.ACL = "private"
_, _, err = httpdtest.UpdateUser(user2, http.StatusOK, "") _, _, err = httpdtest.UpdateUser(user2, http.StatusOK, "")
assert.NoError(t, err) assert.NoError(t, err)
@ -13385,6 +13387,7 @@ func TestWebUserGCSMock(t *testing.T) {
user.FsConfig.GCSConfig.Bucket = "test" user.FsConfig.GCSConfig.Bucket = "test"
user.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/" user.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir/"
user.FsConfig.GCSConfig.StorageClass = "standard" user.FsConfig.GCSConfig.StorageClass = "standard"
user.FsConfig.GCSConfig.ACL = "publicReadWrite"
form := make(url.Values) form := make(url.Values)
form.Set(csrfFormToken, csrfToken) form.Set(csrfFormToken, csrfToken)
form.Set("username", user.Username) form.Set("username", user.Username)
@ -13405,6 +13408,7 @@ func TestWebUserGCSMock(t *testing.T) {
form.Set("fs_provider", "2") form.Set("fs_provider", "2")
form.Set("gcs_bucket", user.FsConfig.GCSConfig.Bucket) form.Set("gcs_bucket", user.FsConfig.GCSConfig.Bucket)
form.Set("gcs_storage_class", user.FsConfig.GCSConfig.StorageClass) form.Set("gcs_storage_class", user.FsConfig.GCSConfig.StorageClass)
form.Set("gcs_acl", user.FsConfig.GCSConfig.ACL)
form.Set("gcs_key_prefix", user.FsConfig.GCSConfig.KeyPrefix) form.Set("gcs_key_prefix", user.FsConfig.GCSConfig.KeyPrefix)
form.Set("pattern_path0", "/dir1") form.Set("pattern_path0", "/dir1")
form.Set("patterns0", "*.jpg,*.png") form.Set("patterns0", "*.jpg,*.png")
@ -13441,6 +13445,7 @@ func TestWebUserGCSMock(t *testing.T) {
assert.Equal(t, user.FsConfig.Provider, updateUser.FsConfig.Provider) assert.Equal(t, user.FsConfig.Provider, updateUser.FsConfig.Provider)
assert.Equal(t, user.FsConfig.GCSConfig.Bucket, updateUser.FsConfig.GCSConfig.Bucket) assert.Equal(t, user.FsConfig.GCSConfig.Bucket, updateUser.FsConfig.GCSConfig.Bucket)
assert.Equal(t, user.FsConfig.GCSConfig.StorageClass, updateUser.FsConfig.GCSConfig.StorageClass) assert.Equal(t, user.FsConfig.GCSConfig.StorageClass, updateUser.FsConfig.GCSConfig.StorageClass)
assert.Equal(t, user.FsConfig.GCSConfig.ACL, updateUser.FsConfig.GCSConfig.ACL)
assert.Equal(t, user.FsConfig.GCSConfig.KeyPrefix, updateUser.FsConfig.GCSConfig.KeyPrefix) assert.Equal(t, user.FsConfig.GCSConfig.KeyPrefix, updateUser.FsConfig.GCSConfig.KeyPrefix)
if assert.Len(t, updateUser.Filters.FilePatterns, 1) { if assert.Len(t, updateUser.Filters.FilePatterns, 1) {
assert.Equal(t, "/dir1", updateUser.Filters.FilePatterns[0].Path) assert.Equal(t, "/dir1", updateUser.Filters.FilePatterns[0].Path)

View file

@ -4234,6 +4234,9 @@ components:
* `1` - enabled, we try to use the Application Default Credentials (ADC) strategy to find your application's credentials * `1` - enabled, we try to use the Application Default Credentials (ADC) strategy to find your application's credentials
storage_class: storage_class:
type: string type: string
acl:
type: string
description: 'The ACL to apply to uploaded objects. Leave empty to use the default ACL. For more information and available ACLs, refer to the JSON API here: https://cloud.google.com/storage/docs/access-control/lists#predefined-acl'
key_prefix: key_prefix:
type: string type: string
description: 'key_prefix is similar to a chroot directory for a local filesystem. If specified the user will only see contents that starts with this prefix and so you can restrict access to a specific virtual folder. The prefix, if not empty, must not start with "/" and must end with "/". If empty the whole bucket contents will be available' description: 'key_prefix is similar to a chroot directory for a local filesystem. If specified the user will only see contents that starts with this prefix and so you can restrict access to a specific virtual folder. The prefix, if not empty, must not start with "/" and must end with "/". If empty the whole bucket contents will be available'

View file

@ -859,6 +859,7 @@ func getGCSConfig(r *http.Request) (vfs.GCSFsConfig, error) {
config.Bucket = r.Form.Get("gcs_bucket") config.Bucket = r.Form.Get("gcs_bucket")
config.StorageClass = r.Form.Get("gcs_storage_class") config.StorageClass = r.Form.Get("gcs_storage_class")
config.ACL = r.Form.Get("gcs_acl")
config.KeyPrefix = r.Form.Get("gcs_key_prefix") config.KeyPrefix = r.Form.Get("gcs_key_prefix")
autoCredentials := r.Form.Get("gcs_auto_credentials") autoCredentials := r.Form.Get("gcs_auto_credentials")
if autoCredentials != "" { if autoCredentials != "" {

View file

@ -1299,6 +1299,9 @@ func compareGCSConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
if expected.GCSConfig.StorageClass != actual.GCSConfig.StorageClass { if expected.GCSConfig.StorageClass != actual.GCSConfig.StorageClass {
return errors.New("GCS storage class mismatch") return errors.New("GCS storage class mismatch")
} }
if expected.GCSConfig.ACL != actual.GCSConfig.ACL {
return errors.New("GCS ACL mismatch")
}
if expected.GCSConfig.KeyPrefix != actual.GCSConfig.KeyPrefix && if expected.GCSConfig.KeyPrefix != actual.GCSConfig.KeyPrefix &&
expected.GCSConfig.KeyPrefix+"/" != actual.GCSConfig.KeyPrefix { expected.GCSConfig.KeyPrefix+"/" != actual.GCSConfig.KeyPrefix {
return errors.New("GCS key prefix mismatch") return errors.New("GCS key prefix mismatch")

View file

@ -144,6 +144,10 @@ type GCSFsConfig struct {
// 0 explicit, 1 automatic // 0 explicit, 1 automatic
AutomaticCredentials int `json:"automatic_credentials,omitempty"` AutomaticCredentials int `json:"automatic_credentials,omitempty"`
StorageClass string `json:"storage_class,omitempty"` StorageClass string `json:"storage_class,omitempty"`
// The ACL to apply to uploaded objects. Leave empty to use the default ACL.
// For more information and available ACLs, refer to the JSON API here:
// https://cloud.google.com/storage/docs/access-control/lists#predefined-acl
ACL string `json:"acl,omitempty"`
} }
// AzBlobFsConfig defines the configuration for Azure Blob Storage based filesystem // AzBlobFsConfig defines the configuration for Azure Blob Storage based filesystem

View file

@ -109,7 +109,7 @@
</small> </small>
</div> </div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<label for="idS3KeyPrefix" class="col-sm-2 col-form-label">ACL</label> <label for="idS3ACL" class="col-sm-2 col-form-label">ACL</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" id="idS3ACL" name="s3_acl" placeholder="" <input type="text" class="form-control" id="idS3ACL" name="s3_acl" placeholder=""
value="{{.S3Config.ACL}}" maxlength="255" aria-describedby="S3ACLHelpBlock"> value="{{.S3Config.ACL}}" maxlength="255" aria-describedby="S3ACLHelpBlock">
@ -174,13 +174,22 @@
<div class="form-group row fsconfig fsconfig-gcsfs"> <div class="form-group row fsconfig fsconfig-gcsfs">
<label for="idGCSKeyPrefix" class="col-sm-2 col-form-label">Key Prefix</label> <label for="idGCSKeyPrefix" class="col-sm-2 col-form-label">Key Prefix</label>
<div class="col-sm-10"> <div class="col-sm-3">
<input type="text" class="form-control" id="idGCSKeyPrefix" name="gcs_key_prefix" placeholder="" <input type="text" class="form-control" id="idGCSKeyPrefix" name="gcs_key_prefix" placeholder=""
value="{{.GCSConfig.KeyPrefix}}" maxlength="255" aria-describedby="GCSKeyPrefixHelpBlock"> value="{{.GCSConfig.KeyPrefix}}" maxlength="255" aria-describedby="GCSKeyPrefixHelpBlock">
<small id="GCSKeyPrefixHelpBlock" class="form-text text-muted"> <small id="GCSKeyPrefixHelpBlock" class="form-text text-muted">
Similar to a chroot for local filesystem. Cannot start with "/". Example: "somedir/subdir/". Similar to a chroot for local filesystem. Cannot start with "/". Example: "somedir/subdir/".
</small> </small>
</div> </div>
<div class="col-sm-2"></div>
<label for="idGCSACL" class="col-sm-2 col-form-label">ACL</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="idGCSACL" name="gcs_acl" placeholder=""
value="{{.GCSConfig.ACL}}" maxlength="255" aria-describedby="GCSACLHelpBlock">
<small id="GCSACLHelpBlock" class="form-text text-muted">
ACL for uploaded objects. For more info refer to the JSON API <a href="https://cloud.google.com/storage/docs/access-control/lists#predefined-acl" target="_blank">here</a>
</small>
</div>
</div> </div>
<div class="form-group row fsconfig fsconfig-azblobfs"> <div class="form-group row fsconfig fsconfig-azblobfs">

View file

@ -252,6 +252,7 @@ func (f *Filesystem) GetACopy() Filesystem {
Credentials: f.GCSConfig.Credentials.Clone(), Credentials: f.GCSConfig.Credentials.Clone(),
AutomaticCredentials: f.GCSConfig.AutomaticCredentials, AutomaticCredentials: f.GCSConfig.AutomaticCredentials,
StorageClass: f.GCSConfig.StorageClass, StorageClass: f.GCSConfig.StorageClass,
ACL: f.GCSConfig.ACL,
KeyPrefix: f.GCSConfig.KeyPrefix, KeyPrefix: f.GCSConfig.KeyPrefix,
}, },
}, },

View file

@ -185,6 +185,9 @@ func (fs *GCSFs) Create(name string, flag int) (File, *PipeWriter, func(), error
if fs.config.StorageClass != "" { if fs.config.StorageClass != "" {
objectWriter.ObjectAttrs.StorageClass = fs.config.StorageClass objectWriter.ObjectAttrs.StorageClass = fs.config.StorageClass
} }
if fs.config.ACL != "" {
objectWriter.PredefinedACL = fs.config.ACL
}
go func() { go func() {
defer cancelFn() defer cancelFn()
@ -195,7 +198,8 @@ func (fs *GCSFs) Create(name string, flag int) (File, *PipeWriter, func(), error
} }
r.CloseWithError(err) //nolint:errcheck r.CloseWithError(err) //nolint:errcheck
p.Done(err) p.Done(err)
fsLog(fs, logger.LevelDebug, "upload completed, path: %#v, readed bytes: %v, err: %v", name, n, err) fsLog(fs, logger.LevelDebug, "upload completed, path: %#v, acl: %#v, readed bytes: %v, err: %v",
name, fs.config.ACL, n, err)
metric.GCSTransferCompleted(n, 0, err) metric.GCSTransferCompleted(n, 0, err)
}() }()
return nil, p, cancelFn, nil return nil, p, cancelFn, nil
@ -234,6 +238,9 @@ func (fs *GCSFs) Rename(source, target string) error {
if fs.config.StorageClass != "" { if fs.config.StorageClass != "" {
copier.StorageClass = fs.config.StorageClass copier.StorageClass = fs.config.StorageClass
} }
if fs.config.ACL != "" {
copier.PredefinedACL = fs.config.ACL
}
var contentType string var contentType string
if fi.IsDir() { if fi.IsDir() {
contentType = dirMimeType contentType = dirMimeType

View file

@ -300,6 +300,9 @@ func (c *GCSFsConfig) isEqual(other *GCSFsConfig) bool {
if c.StorageClass != other.StorageClass { if c.StorageClass != other.StorageClass {
return false return false
} }
if c.ACL != other.ACL {
return false
}
if c.Credentials == nil { if c.Credentials == nil {
c.Credentials = kms.NewEmptySecret() c.Credentials = kms.NewEmptySecret()
} }
@ -339,6 +342,7 @@ func (c *GCSFsConfig) Validate(credentialsFilePath string) error {
} }
} }
c.StorageClass = strings.TrimSpace(c.StorageClass) c.StorageClass = strings.TrimSpace(c.StorageClass)
c.ACL = strings.TrimSpace(c.ACL)
return nil return nil
} }