| // 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) |
| |
| // BasicAuth creates a basic auth authenticator with the provided authentication function |
| func BasicAuth(authenticate UserPassAuthentication) runtime.Authenticator { |
| return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { |
| if usr, pass, ok := r.BasicAuth(); ok { |
| p, err := authenticate(usr, pass) |
| return true, p, err |
| } |
| |
| 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 HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { |
| if usr, pass, ok := r.BasicAuth(); ok { |
| ctx, p, err := authenticate(r.Context(), usr, pass) |
| *r = *r.WithContext(ctx) |
| return true, p, err |
| } |
| |
| 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 |
| } |
| |
| 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 |
| } |
| |
| ctx, p, err := authenticate(r.Request.Context(), token, r.RequiredScopes) |
| *r.Request = *r.Request.WithContext(ctx) |
| return true, p, err |
| }) |
| } |