mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 10:06:14 +00:00
172 lines
4.8 KiB
Go
172 lines
4.8 KiB
Go
// Copyright 2020 The CubeFS Authors.
|
|
//
|
|
// 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 objectnode
|
|
|
|
// https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var methodsRequest = []string{"GET", "PUT", "HEAD", "POST", "DELETE", "*"}
|
|
|
|
type CORSConfiguration struct {
|
|
XMLName xml.Name `xml:"CORSConfiguration" json:"xml_name"`
|
|
CORSRule []*CORSRule `xml:"CORSRule" json:"cors_rule"`
|
|
}
|
|
|
|
type CORSRule struct {
|
|
AllowedHeader []string `xml:"AllowedHeader" json:"allowed_header"`
|
|
AllowedMethod []string `xml:"AllowedMethod" json:"allowed_method"`
|
|
AllowedOrigin []string `xml:"AllowedOrigin" json:"allowed_origin"`
|
|
ExposeHeader []string `xml:"ExposeHeader" json:"expose_header"`
|
|
MaxAgeSeconds uint16 `xml:"MaxAgeSeconds,omitempty" json:"max_age_seconds,omitempty"`
|
|
}
|
|
|
|
func (rule *CORSRule) match(origin, method string, headers string) bool {
|
|
if !matchOrigin(rule.AllowedOrigin, origin) {
|
|
return false
|
|
}
|
|
if !matchMethod(rule.AllowedMethod, method) {
|
|
return false
|
|
}
|
|
if headers != "" && !matchHeaders(rule.AllowedHeader, headers) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (corsConfig *CORSConfiguration) validate() *ErrorCode {
|
|
if len(corsConfig.CORSRule) == 0 {
|
|
return NewError("BadRequest", "No CORS Rules found in request.", 400)
|
|
}
|
|
if len(corsConfig.CORSRule) > 10 {
|
|
return TooManyCorsRules
|
|
}
|
|
for _, rule := range corsConfig.CORSRule {
|
|
if err := valid(*rule); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseCorsConfig(bytes []byte) (corsConfig *CORSConfiguration, errCode *ErrorCode) {
|
|
corsConfig = &CORSConfiguration{}
|
|
if err := xml.Unmarshal(bytes, corsConfig); err != nil {
|
|
return nil, MalformedXML
|
|
}
|
|
if err := corsConfig.validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return corsConfig, nil
|
|
}
|
|
|
|
func valid(r CORSRule) *ErrorCode {
|
|
if len(r.AllowedOrigin) == 0 {
|
|
return NewError("InvalidCORSRule", "Missing AllowedOrigins.", 400)
|
|
}
|
|
if len(r.AllowedMethod) == 0 {
|
|
return NewError("InvalidCORSRule", "Missing AllowedMethods.", 400)
|
|
}
|
|
// check origin, at most contain one *
|
|
for _, origin := range r.AllowedOrigin {
|
|
if strings.Count(origin, "*") > 1 {
|
|
return NewError("InvalidCORSRule", "AllowedOrigin can not have more than one wildcard: "+origin, 400)
|
|
}
|
|
}
|
|
// AllowedMethod is case sensitive
|
|
for _, method := range r.AllowedMethod {
|
|
if !StringListContain(methodsRequest, method) {
|
|
return NewError("InvalidCORSRule", "AllowedMethod is unsupport: "+method, 400)
|
|
}
|
|
}
|
|
// check allowedheaders, at most contain one *
|
|
for _, header := range r.AllowedHeader {
|
|
if strings.Count(header, "*") > 1 {
|
|
return NewError("InvalidCORSRule", "AllowedHeaders can not have more than one wildcard: "+header, 400)
|
|
}
|
|
}
|
|
// exposed headers can't include *
|
|
for _, exheader := range r.ExposeHeader {
|
|
if strings.Contains(exheader, "*") {
|
|
return NewError("InvalidCORSRule", "ExposedHeaders can not include wildcard: "+exheader, 400)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func StringListContain(list []string, element string) bool {
|
|
for _, str := range list {
|
|
if str == element {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func storeBucketCors(bytes []byte, vol *Volume) (err error) {
|
|
return vol.store.Put(vol.name, bucketRootPath, XAttrKeyOSSCORS, bytes)
|
|
}
|
|
|
|
func deleteBucketCors(vol *Volume) (err error) {
|
|
return vol.store.Delete(vol.name, bucketRootPath, XAttrKeyOSSCORS)
|
|
}
|
|
|
|
func matchOrigin(allowedOrigins []string, origin string) bool {
|
|
for _, allowed := range allowedOrigins {
|
|
if allowed == "*" {
|
|
return true
|
|
}
|
|
pattern := makeRegexPattern(allowed)
|
|
if ok, _ := regexp.MatchString(pattern, origin); ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func matchMethod(allowedMethod []string, method string) bool {
|
|
return StringListContain(allowedMethod, method)
|
|
}
|
|
|
|
func matchHeaders(allowedHeaders []string, reqHeaders string) bool {
|
|
if len(allowedHeaders) == 0 {
|
|
return false
|
|
}
|
|
headers := strings.Split(strings.ToLower(reqHeaders), ",")
|
|
for _, h := range headers {
|
|
if !matchHeader(allowedHeaders, h) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func matchHeader(allowedHeaders []string, reqHeader string) bool {
|
|
for _, allowed := range allowedHeaders {
|
|
if allowed == "*" {
|
|
return true
|
|
}
|
|
pattern := makeRegexPattern(strings.ToLower(allowed))
|
|
if ok, _ := regexp.MatchString(pattern, reqHeader); ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|