You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							275 lines
						
					
					
						
							8.8 KiB
						
					
					
				
			
		
		
	
	
							275 lines
						
					
					
						
							8.8 KiB
						
					
					
				| // Copyright 2015 go-swagger maintainers
 | |
| //
 | |
| // 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 security
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/go-openapi/errors"
 | |
| 	"github.com/go-openapi/runtime"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	query  = "query"
 | |
| 	header = "header"
 | |
| )
 | |
| 
 | |
| // HttpAuthenticator is a function that authenticates a HTTP request
 | |
| func HttpAuthenticator(handler func(*http.Request) (bool, interface{}, error)) runtime.Authenticator {
 | |
| 	return runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) {
 | |
| 		if request, ok := params.(*http.Request); ok {
 | |
| 			return handler(request)
 | |
| 		}
 | |
| 		if scoped, ok := params.(*ScopedAuthRequest); ok {
 | |
| 			return handler(scoped.Request)
 | |
| 		}
 | |
| 		return false, nil, nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // ScopedAuthenticator is a function that authenticates a HTTP request against a list of valid scopes
 | |
| func ScopedAuthenticator(handler func(*ScopedAuthRequest) (bool, interface{}, error)) runtime.Authenticator {
 | |
| 	return runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) {
 | |
| 		if request, ok := params.(*ScopedAuthRequest); ok {
 | |
| 			return handler(request)
 | |
| 		}
 | |
| 		return false, nil, nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // UserPassAuthentication authentication function
 | |
| type UserPassAuthentication func(string, string) (interface{}, error)
 | |
| 
 | |
| // UserPassAuthenticationCtx authentication function with context.Context
 | |
| type UserPassAuthenticationCtx func(context.Context, string, string) (context.Context, interface{}, error)
 | |
| 
 | |
| // TokenAuthentication authentication function
 | |
| type TokenAuthentication func(string) (interface{}, error)
 | |
| 
 | |
| // TokenAuthenticationCtx authentication function with context.Context
 | |
| type TokenAuthenticationCtx func(context.Context, string) (context.Context, interface{}, error)
 | |
| 
 | |
| // ScopedTokenAuthentication authentication function
 | |
| type ScopedTokenAuthentication func(string, []string) (interface{}, error)
 | |
| 
 | |
| // ScopedTokenAuthenticationCtx authentication function with context.Context
 | |
| type ScopedTokenAuthenticationCtx func(context.Context, string, []string) (context.Context, interface{}, error)
 | |
| 
 | |
| var DefaultRealmName = "API"
 | |
| 
 | |
| type secCtxKey uint8
 | |
| 
 | |
| const (
 | |
| 	failedBasicAuth secCtxKey = iota
 | |
| 	oauth2SchemeName
 | |
| )
 | |
| 
 | |
| func FailedBasicAuth(r *http.Request) string {
 | |
| 	return FailedBasicAuthCtx(r.Context())
 | |
| }
 | |
| 
 | |
| func FailedBasicAuthCtx(ctx context.Context) string {
 | |
| 	v, ok := ctx.Value(failedBasicAuth).(string)
 | |
| 	if !ok {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func OAuth2SchemeName(r *http.Request) string {
 | |
| 	return OAuth2SchemeNameCtx(r.Context())
 | |
| }
 | |
| 
 | |
| func OAuth2SchemeNameCtx(ctx context.Context) string {
 | |
| 	v, ok := ctx.Value(oauth2SchemeName).(string)
 | |
| 	if !ok {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // BasicAuth creates a basic auth authenticator with the provided authentication function
 | |
| func BasicAuth(authenticate UserPassAuthentication) runtime.Authenticator {
 | |
| 	return BasicAuthRealm(DefaultRealmName, authenticate)
 | |
| }
 | |
| 
 | |
| // BasicAuthRealm creates a basic auth authenticator with the provided authentication function and realm name
 | |
| func BasicAuthRealm(realm string, authenticate UserPassAuthentication) runtime.Authenticator {
 | |
| 	if realm == "" {
 | |
| 		realm = DefaultRealmName
 | |
| 	}
 | |
| 
 | |
| 	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
 | |
| 		if usr, pass, ok := r.BasicAuth(); ok {
 | |
| 			p, err := authenticate(usr, pass)
 | |
| 			if err != nil {
 | |
| 				*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm))
 | |
| 			}
 | |
| 			return true, p, err
 | |
| 		}
 | |
| 		*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm))
 | |
| 		return false, nil, nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // BasicAuthCtx creates a basic auth authenticator with the provided authentication function with support for context.Context
 | |
| func BasicAuthCtx(authenticate UserPassAuthenticationCtx) runtime.Authenticator {
 | |
| 	return BasicAuthRealmCtx(DefaultRealmName, authenticate)
 | |
| }
 | |
| 
 | |
| // BasicAuthRealmCtx creates a basic auth authenticator with the provided authentication function and realm name with support for context.Context
 | |
| func BasicAuthRealmCtx(realm string, authenticate UserPassAuthenticationCtx) runtime.Authenticator {
 | |
| 	if realm == "" {
 | |
| 		realm = DefaultRealmName
 | |
| 	}
 | |
| 
 | |
| 	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
 | |
| 		if usr, pass, ok := r.BasicAuth(); ok {
 | |
| 			ctx, p, err := authenticate(r.Context(), usr, pass)
 | |
| 			if err != nil {
 | |
| 				ctx = context.WithValue(ctx, failedBasicAuth, realm)
 | |
| 			}
 | |
| 			*r = *r.WithContext(ctx)
 | |
| 			return true, p, err
 | |
| 		}
 | |
| 		*r = *r.WithContext(context.WithValue(r.Context(), failedBasicAuth, realm))
 | |
| 		return false, nil, nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // APIKeyAuth creates an authenticator that uses a token for authorization.
 | |
| // This token can be obtained from either a header or a query string
 | |
| func APIKeyAuth(name, in string, authenticate TokenAuthentication) runtime.Authenticator {
 | |
| 	inl := strings.ToLower(in)
 | |
| 	if inl != query && inl != header {
 | |
| 		// panic because this is most likely a typo
 | |
| 		panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\"."))
 | |
| 	}
 | |
| 
 | |
| 	var getToken func(*http.Request) string
 | |
| 	switch inl {
 | |
| 	case header:
 | |
| 		getToken = func(r *http.Request) string { return r.Header.Get(name) }
 | |
| 	case query:
 | |
| 		getToken = func(r *http.Request) string { return r.URL.Query().Get(name) }
 | |
| 	}
 | |
| 
 | |
| 	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
 | |
| 		token := getToken(r)
 | |
| 		if token == "" {
 | |
| 			return false, nil, nil
 | |
| 		}
 | |
| 
 | |
| 		p, err := authenticate(token)
 | |
| 		return true, p, err
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // APIKeyAuthCtx creates an authenticator that uses a token for authorization with support for context.Context.
 | |
| // This token can be obtained from either a header or a query string
 | |
| func APIKeyAuthCtx(name, in string, authenticate TokenAuthenticationCtx) runtime.Authenticator {
 | |
| 	inl := strings.ToLower(in)
 | |
| 	if inl != query && inl != header {
 | |
| 		// panic because this is most likely a typo
 | |
| 		panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\"."))
 | |
| 	}
 | |
| 
 | |
| 	var getToken func(*http.Request) string
 | |
| 	switch inl {
 | |
| 	case header:
 | |
| 		getToken = func(r *http.Request) string { return r.Header.Get(name) }
 | |
| 	case query:
 | |
| 		getToken = func(r *http.Request) string { return r.URL.Query().Get(name) }
 | |
| 	}
 | |
| 
 | |
| 	return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) {
 | |
| 		token := getToken(r)
 | |
| 		if token == "" {
 | |
| 			return false, nil, nil
 | |
| 		}
 | |
| 
 | |
| 		ctx, p, err := authenticate(r.Context(), token)
 | |
| 		*r = *r.WithContext(ctx)
 | |
| 		return true, p, err
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // ScopedAuthRequest contains both a http request and the required scopes for a particular operation
 | |
| type ScopedAuthRequest struct {
 | |
| 	Request        *http.Request
 | |
| 	RequiredScopes []string
 | |
| }
 | |
| 
 | |
| // BearerAuth for use with oauth2 flows
 | |
| func BearerAuth(name string, authenticate ScopedTokenAuthentication) runtime.Authenticator {
 | |
| 	const prefix = "Bearer "
 | |
| 	return ScopedAuthenticator(func(r *ScopedAuthRequest) (bool, interface{}, error) {
 | |
| 		var token string
 | |
| 		hdr := r.Request.Header.Get("Authorization")
 | |
| 		if strings.HasPrefix(hdr, prefix) {
 | |
| 			token = strings.TrimPrefix(hdr, prefix)
 | |
| 		}
 | |
| 		if token == "" {
 | |
| 			qs := r.Request.URL.Query()
 | |
| 			token = qs.Get("access_token")
 | |
| 		}
 | |
| 		//#nosec
 | |
| 		ct, _, _ := runtime.ContentType(r.Request.Header)
 | |
| 		if token == "" && (ct == "application/x-www-form-urlencoded" || ct == "multipart/form-data") {
 | |
| 			token = r.Request.FormValue("access_token")
 | |
| 		}
 | |
| 
 | |
| 		if token == "" {
 | |
| 			return false, nil, nil
 | |
| 		}
 | |
| 
 | |
| 		rctx := context.WithValue(r.Request.Context(), oauth2SchemeName, name)
 | |
| 		*r.Request = *r.Request.WithContext(rctx)
 | |
| 		p, err := authenticate(token, r.RequiredScopes)
 | |
| 		return true, p, err
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // BearerAuthCtx for use with oauth2 flows with support for context.Context.
 | |
| func BearerAuthCtx(name string, authenticate ScopedTokenAuthenticationCtx) runtime.Authenticator {
 | |
| 	const prefix = "Bearer "
 | |
| 	return ScopedAuthenticator(func(r *ScopedAuthRequest) (bool, interface{}, error) {
 | |
| 		var token string
 | |
| 		hdr := r.Request.Header.Get("Authorization")
 | |
| 		if strings.HasPrefix(hdr, prefix) {
 | |
| 			token = strings.TrimPrefix(hdr, prefix)
 | |
| 		}
 | |
| 		if token == "" {
 | |
| 			qs := r.Request.URL.Query()
 | |
| 			token = qs.Get("access_token")
 | |
| 		}
 | |
| 		//#nosec
 | |
| 		ct, _, _ := runtime.ContentType(r.Request.Header)
 | |
| 		if token == "" && (ct == "application/x-www-form-urlencoded" || ct == "multipart/form-data") {
 | |
| 			token = r.Request.FormValue("access_token")
 | |
| 		}
 | |
| 
 | |
| 		if token == "" {
 | |
| 			return false, nil, nil
 | |
| 		}
 | |
| 
 | |
| 		rctx := context.WithValue(r.Request.Context(), oauth2SchemeName, name)
 | |
| 		ctx, p, err := authenticate(rctx, token, r.RequiredScopes)
 | |
| 		*r.Request = *r.Request.WithContext(ctx)
 | |
| 		return true, p, err
 | |
| 	})
 | |
| }
 | |
| 
 |