github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/validator/auth.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package validator 16 17 import ( 18 "context" 19 "github.com/greenpau/go-authcrunch/pkg/authproxy" 20 "github.com/greenpau/go-authcrunch/pkg/errors" 21 "github.com/greenpau/go-authcrunch/pkg/requests" 22 addrutil "github.com/greenpau/go-authcrunch/pkg/util/addr" 23 "net/http" 24 "strings" 25 ) 26 27 // parseCustomAuthHeader authorizes HTTP requests based on the presence and the 28 // content of HTTP Authorization or X-API-Key headers. 29 func (v *TokenValidator) parseCustomAuthHeader(ctx context.Context, r *http.Request, ar *requests.AuthorizationRequest) error { 30 if v.basicAuthEnabled { 31 if err := v.parseCustomBasicAuthHeader(ctx, r, ar); err != nil { 32 return err 33 } 34 } 35 if !ar.Token.Found && v.apiKeyAuthEnabled { 36 return v.parseCustomAPIKeyAuthHeader(ctx, r, ar) 37 } 38 return nil 39 } 40 41 func (v *TokenValidator) parseCustomBasicAuthHeader(ctx context.Context, r *http.Request, ar *requests.AuthorizationRequest) error { 42 var tokenSecret, tokenRealm string 43 hdr := r.Header.Get("Authorization") 44 if hdr == "" { 45 return nil 46 } 47 entries := strings.Split(hdr, ",") 48 for _, entry := range entries { 49 entry = strings.TrimSpace(entry) 50 if !strings.HasPrefix(entry, "Basic") { 51 continue 52 } 53 entry = strings.TrimPrefix(entry, "Basic") 54 entry = strings.TrimSpace(entry) 55 56 ar.Token.Source = "basicauth" 57 ar.Token.Name = "Basic" 58 ar.Token.Found = true 59 60 sep := strings.Index(entry, " ") 61 if sep < 0 { 62 tokenSecret = entry 63 } else { 64 tokenSecret = entry[:sep] 65 directives := parseAuthHeaderDirectives(entry[sep+1:]) 66 if directives != nil { 67 if realm, exists := directives["realm"]; exists { 68 tokenRealm = realm 69 } 70 } 71 } 72 break 73 } 74 75 if ar.Token.Found { 76 if tokenRealm != "" { 77 // Check if the realm is registered. 78 if _, exists := v.authProxyConfig.BasicAuth.Realms[tokenRealm]; !exists { 79 return errors.ErrBasicAuthFailed 80 } 81 } 82 83 apr := &authproxy.Request{ 84 Address: addrutil.GetSourceAddress(r), 85 Realm: tokenRealm, 86 Secret: tokenSecret, 87 } 88 89 if err := v.authProxy.BasicAuth(apr); err != nil { 90 return err 91 } 92 93 ar.Token.Name = apr.Response.Name 94 ar.Token.Payload = apr.Response.Payload 95 } 96 97 return nil 98 } 99 100 func (v *TokenValidator) parseCustomAPIKeyAuthHeader(ctx context.Context, r *http.Request, ar *requests.AuthorizationRequest) error { 101 var tokenSecret, tokenRealm string 102 hdr := r.Header.Get("X-API-Key") 103 if hdr == "" { 104 return nil 105 } 106 entry := strings.TrimSpace(hdr) 107 108 ar.Token.Source = "apikey" 109 ar.Token.Name = "X-API-Key" 110 ar.Token.Found = true 111 112 sep := strings.Index(entry, " ") 113 if sep < 0 { 114 tokenSecret = entry 115 } else { 116 tokenSecret = entry[:sep] 117 directives := parseAuthHeaderDirectives(entry[sep+1:]) 118 if directives != nil { 119 if realm, exists := directives["realm"]; exists { 120 tokenRealm = realm 121 } 122 } 123 } 124 125 if tokenRealm != "" { 126 // Check if the realm is registered. 127 if _, exists := v.authProxyConfig.APIKeyAuth.Realms[tokenRealm]; !exists { 128 return errors.ErrAPIKeyAuthFailed 129 } 130 } 131 132 apr := &authproxy.Request{ 133 Address: addrutil.GetSourceAddress(r), 134 Realm: tokenRealm, 135 Secret: tokenSecret, 136 } 137 138 if err := v.authProxy.APIKeyAuth(apr); err != nil { 139 return err 140 } 141 ar.Token.Name = apr.Response.Name 142 ar.Token.Payload = apr.Response.Payload 143 return nil 144 } 145 146 func parseAuthHeaderDirectives(s string) map[string]string { 147 m := make(map[string]string) 148 for _, entry := range strings.Split(s, ",") { 149 kv := strings.SplitN(strings.TrimSpace(entry), "=", 2) 150 if len(kv) != 2 { 151 continue 152 } 153 m[kv[0]] = strings.Trim(kv[1], `"'`) 154 } 155 return m 156 }