cubefs/objectnode/cors.go
yhjiango 135da4ec9f refactor(object): add CubeFS's statement comment
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2024-01-10 14:58:30 +08:00

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
}