github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/gateway/sig/sig.go (about) 1 package sig 2 3 import ( 4 "crypto/hmac" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "net/http" 9 "regexp" 10 "strings" 11 "unicode/utf8" 12 13 "github.com/treeverse/lakefs/pkg/auth/model" 14 gwErrors "github.com/treeverse/lakefs/pkg/gateway/errors" 15 ) 16 17 var ( 18 ErrHeaderMalformed = errors.New("header malformed") 19 20 // reservedObjectNames - if object matches reserved string, no need to encode them 21 reservedObjectNames = regexp.MustCompile("^[a-zA-Z0-9-_.~/]+$") 22 ) 23 24 // taken from https://github.com/minio/minio-go/blob/master/pkg/s3utils/utils.go 25 /* 26 * MinIO Go Library for Amazon S3 Compatible Cloud Storage 27 * Copyright 2015-2017 MinIO, Inc. 28 * 29 * Licensed under the Apache License, Version 2.0 (the "License"); 30 * you may not use this file except in compliance with the License. 31 * You may obtain a copy of the License at 32 * 33 * http://www.apache.org/licenses/LICENSE-2.0 34 * 35 * Unless required by applicable law or agreed to in writing, software 36 * distributed under the License is distributed on an "AS IS" BASIS, 37 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 38 * See the License for the specific language governing permissions and 39 * limitations under the License. 40 */ 41 42 // EncodePath encode the strings from UTF-8 byte representations to HTML hex escape sequences 43 // This is necessary since regular url.Parse() and url.Encode() functions do not support UTF-8 44 // non english characters cannot be parsed due to the nature in which url.Encode() is written 45 // This function on the other hand is a direct replacement for url.Encode() technique to support 46 // pretty much every UTF-8 character. 47 func EncodePath(pathName string) string { 48 if reservedObjectNames.MatchString(pathName) { 49 return pathName 50 } 51 var encodedPathname string 52 for _, s := range pathName { 53 if 'A' <= s && s <= 'Z' || 'a' <= s && s <= 'z' || '0' <= s && s <= '9' { // §2.3 Unreserved characters (mark) 54 encodedPathname += string(s) 55 continue 56 } 57 switch s { 58 case '-', '_', '.', '~', '/': // §2.3 Unreserved characters (mark) 59 encodedPathname += string(s) 60 continue 61 default: 62 runeLen := utf8.RuneLen(s) 63 if runeLen < 0 { 64 // if utf8 cannot convert return the same string as is 65 return pathName 66 } 67 u := make([]byte, runeLen) 68 utf8.EncodeRune(u, s) 69 for _, r := range u { 70 h := hex.EncodeToString([]byte{r}) 71 encodedPathname += "%" + strings.ToUpper(h) 72 } 73 } 74 } 75 return encodedPathname 76 } 77 78 type SigContext interface { 79 GetAccessKeyID() string 80 } 81 82 type SigAuthenticator interface { 83 Parse() (SigContext, error) 84 Verify(*model.Credential) error 85 } 86 87 type chainedAuthenticator struct { 88 methods []SigAuthenticator 89 chosen SigAuthenticator 90 } 91 92 func ChainedAuthenticator(methods ...SigAuthenticator) SigAuthenticator { 93 return &chainedAuthenticator{methods, nil} 94 } 95 96 func (c *chainedAuthenticator) Parse() (SigContext, error) { 97 for _, method := range c.methods { 98 sigContext, err := method.Parse() 99 if err == nil { 100 c.chosen = method 101 return sigContext, nil 102 } 103 } 104 return nil, gwErrors.ErrMissingFields 105 } 106 107 func Equal(sig1, sig2 []byte) bool { 108 return hmac.Equal(sig1, sig2) 109 } 110 111 func (c *chainedAuthenticator) Verify(creds *model.Credential) error { 112 return c.chosen.Verify(creds) 113 } 114 115 func (c *chainedAuthenticator) String() string { 116 if c.chosen == nil { 117 return "chained authenticator" 118 } 119 return fmt.Sprintf("%s", c.chosen) 120 } 121 122 func IsAWSSignedRequest(req *http.Request) bool { 123 // headers first 124 headers := req.Header 125 v4Value := headers.Get(v4SignatureHeader) 126 if len(v4Value) > 0 { 127 return true 128 } 129 v4AuthHeader := headers.Get(V4authHeaderName) 130 if strings.HasPrefix(v4AuthHeader, "AWS4") { 131 return true 132 } 133 v2Value := headers.Get(v2authHeaderName) 134 if strings.HasPrefix(v2Value, "AWS ") { 135 return true 136 } 137 138 // then request params 139 queryParams := req.URL.Query() 140 // sigv2: https://docs.aws.amazon.com/general/latest/gr/signature-version-2.html 141 if len(queryParams.Get("AWSAccessKeyId")) > 0 { 142 return true 143 } 144 // sigv4: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html 145 if len(queryParams.Get("X-Amz-Credential")) > 0 { 146 return true 147 } 148 return false 149 }