diff --git a/cmd/gateway-gcs-errors.go b/cmd/gateway-gcs-errors.go new file mode 100644 index 000000000..591d76d45 --- /dev/null +++ b/cmd/gateway-gcs-errors.go @@ -0,0 +1,27 @@ +/* + * Minio Cloud Storage, (C) 2017 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cmd + +import "errors" + +var ( + // Project is format is not valid + errGCSInvalidProjectID = errors.New("invalid project id") + + // Multipart identifier is not in the correct form + errGCSNotValidMultipartIdentifier = errors.New("Not a valid multipart identifier") +) diff --git a/cmd/gateway-gcs.go b/cmd/gateway-gcs.go index 07fdb4772..8f0b87e1c 100644 --- a/cmd/gateway-gcs.go +++ b/cmd/gateway-gcs.go @@ -22,11 +22,11 @@ import ( "crypto/sha256" "encoding/base64" "encoding/hex" - "errors" "fmt" "hash" "io" "path" + "regexp" "strconv" "strings" @@ -39,11 +39,6 @@ import ( "github.com/minio/minio-go/pkg/policy" ) -var ( - // ErrNotValidMultipartIdentifier the multipart identifier is not in the correct form - ErrNotValidMultipartIdentifier = errors.New("Not a valid multipart identifier") -) - const ( // ZZZZMinioPrefix is used for metadata and multiparts. The prefix is being filtered out, // hence the naming of ZZZZ (last prefix) @@ -154,6 +149,17 @@ func gcsToObjectError(err error, params ...string) error { return e } +// gcsProjectIDRegex defines a valid gcs project id format +var gcsProjectIDRegex = regexp.MustCompile("^[a-z][a-z0-9-]{5,29}$") + +// isValidGCSProjectId - checks if a given project id is valid or not. +// Project IDs must start with a lowercase letter and can have lowercase +// ASCII letters, digits or hyphens. Project IDs must be between 6 and 30 characters. +// Ref: https://cloud.google.com/resource-manager/reference/rest/v1/projects#Project (projectId section) +func isValidGCSProjectID(projectID string) bool { + return gcsProjectIDRegex.MatchString(projectID) +} + // gcsGateway - Implements gateway for Minio and GCS compatible object storage servers. type gcsGateway struct { client *storage.Client @@ -171,6 +177,11 @@ func newGCSGateway(args cli.Args) (GatewayLayer, error) { endpoint := "storage.googleapis.com" secure := true projectID := args.First() + + if !isValidGCSProjectID(projectID) { + fatalIf(errGCSInvalidProjectID, "Unable to initialize GCS gateway") + } + ctx := context.Background() // Creates a client. @@ -611,11 +622,11 @@ func fromGCSMultipartKey(s string) (key, uploadID string, partID int, err error) parts := strings.Split(s, "-") if parts[0] != "multipart" { - return "", "", 0, ErrNotValidMultipartIdentifier + return "", "", 0, errGCSNotValidMultipartIdentifier } if len(parts) != 4 { - return "", "", 0, ErrNotValidMultipartIdentifier + return "", "", 0, errGCSNotValidMultipartIdentifier } key = unescape(parts[1]) diff --git a/cmd/gateway-gcs_test.go b/cmd/gateway-gcs_test.go index 9cf6fffa6..0466a8d65 100644 --- a/cmd/gateway-gcs_test.go +++ b/cmd/gateway-gcs_test.go @@ -95,3 +95,37 @@ func TestToGCSPageToken(t *testing.T) { } } + +// TestValidGCSProjectID tests the behavior of isValidGCSProjectID +func TestValidGCSProjectID(t *testing.T) { + testCases := []struct { + ProjectID string + Valid bool + }{ + {"", false}, + {"a", false}, + {"Abc", false}, + {"1bcd", false}, + // 5 chars + {"abcdb", false}, + // 6 chars + {"abcdbz", true}, + // 30 chars + {"project-id-1-project-id-more-1", true}, + // 31 chars + {"project-id-1-project-id-more-11", false}, + {"storage.googleapis.com", false}, + {"http://storage.googleapis.com", false}, + {"http://localhost:9000", false}, + {"project-id-1", true}, + {"project-id-1988832", true}, + {"projectid1414", true}, + } + + // + for i, testCase := range testCases { + if isValidGCSProjectID(testCase.ProjectID) != testCase.Valid { + t.Errorf("Test %d: Expected %v, got %v", i+1, isValidGCSProjectID(testCase.ProjectID), testCase.Valid) + } + } +}