| 
							- // 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 middleware
 - 
 - import (
 - 	"fmt"
 - 	"net/http"
 - 	fpath "path"
 - 	"regexp"
 - 	"strings"
 - 
 - 	"github.com/go-openapi/runtime/security"
 - 
 - 	"github.com/go-openapi/analysis"
 - 	"github.com/go-openapi/errors"
 - 	"github.com/go-openapi/loads"
 - 	"github.com/go-openapi/runtime"
 - 	"github.com/go-openapi/runtime/middleware/denco"
 - 	"github.com/go-openapi/spec"
 - 	"github.com/go-openapi/strfmt"
 - )
 - 
 - // RouteParam is a object to capture route params in a framework agnostic way.
 - // implementations of the muxer should use these route params to communicate with the
 - // swagger framework
 - type RouteParam struct {
 - 	Name  string
 - 	Value string
 - }
 - 
 - // RouteParams the collection of route params
 - type RouteParams []RouteParam
 - 
 - // Get gets the value for the route param for the specified key
 - func (r RouteParams) Get(name string) string {
 - 	vv, _, _ := r.GetOK(name)
 - 	if len(vv) > 0 {
 - 		return vv[len(vv)-1]
 - 	}
 - 	return ""
 - }
 - 
 - // GetOK gets the value but also returns booleans to indicate if a key or value
 - // is present. This aids in validation and satisfies an interface in use there
 - //
 - // The returned values are: data, has key, has value
 - func (r RouteParams) GetOK(name string) ([]string, bool, bool) {
 - 	for _, p := range r {
 - 		if p.Name == name {
 - 			return []string{p.Value}, true, p.Value != ""
 - 		}
 - 	}
 - 	return nil, false, false
 - }
 - 
 - // NewRouter creates a new context aware router middleware
 - func NewRouter(ctx *Context, next http.Handler) http.Handler {
 - 	if ctx.router == nil {
 - 		ctx.router = DefaultRouter(ctx.spec, ctx.api)
 - 	}
 - 
 - 	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
 - 		if _, rCtx, ok := ctx.RouteInfo(r); ok {
 - 			next.ServeHTTP(rw, rCtx)
 - 			return
 - 		}
 - 
 - 		// Not found, check if it exists in the other methods first
 - 		if others := ctx.AllowedMethods(r); len(others) > 0 {
 - 			ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
 - 			return
 - 		}
 - 
 - 		ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.EscapedPath()))
 - 	})
 - }
 - 
 - // RoutableAPI represents an interface for things that can serve
 - // as a provider of implementations for the swagger router
 - type RoutableAPI interface {
 - 	HandlerFor(string, string) (http.Handler, bool)
 - 	ServeErrorFor(string) func(http.ResponseWriter, *http.Request, error)
 - 	ConsumersFor([]string) map[string]runtime.Consumer
 - 	ProducersFor([]string) map[string]runtime.Producer
 - 	AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator
 - 	Authorizer() runtime.Authorizer
 - 	Formats() strfmt.Registry
 - 	DefaultProduces() string
 - 	DefaultConsumes() string
 - }
 - 
 - // Router represents a swagger aware router
 - type Router interface {
 - 	Lookup(method, path string) (*MatchedRoute, bool)
 - 	OtherMethods(method, path string) []string
 - }
 - 
 - type defaultRouteBuilder struct {
 - 	spec     *loads.Document
 - 	analyzer *analysis.Spec
 - 	api      RoutableAPI
 - 	records  map[string][]denco.Record
 - }
 - 
 - type defaultRouter struct {
 - 	spec    *loads.Document
 - 	routers map[string]*denco.Router
 - }
 - 
 - func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder {
 - 	return &defaultRouteBuilder{
 - 		spec:     spec,
 - 		analyzer: analysis.New(spec.Spec()),
 - 		api:      api,
 - 		records:  make(map[string][]denco.Record),
 - 	}
 - }
 - 
 - // DefaultRouter creates a default implemenation of the router
 - func DefaultRouter(spec *loads.Document, api RoutableAPI) Router {
 - 	builder := newDefaultRouteBuilder(spec, api)
 - 	if spec != nil {
 - 		for method, paths := range builder.analyzer.Operations() {
 - 			for path, operation := range paths {
 - 				fp := fpath.Join(spec.BasePath(), path)
 - 				debugLog("adding route %s %s %q", method, fp, operation.ID)
 - 				builder.AddRoute(method, fp, operation)
 - 			}
 - 		}
 - 	}
 - 	return builder.Build()
 - }
 - 
 - // RouteAuthenticator is an authenticator that can compose several authenticators together.
 - // It also knows when it contains an authenticator that allows for anonymous pass through.
 - // Contains a group of 1 or more authenticators that have a logical AND relationship
 - type RouteAuthenticator struct {
 - 	Authenticator  map[string]runtime.Authenticator
 - 	Schemes        []string
 - 	Scopes         map[string][]string
 - 	allScopes      []string
 - 	commonScopes   []string
 - 	allowAnonymous bool
 - }
 - 
 - func (ra *RouteAuthenticator) AllowsAnonymous() bool {
 - 	return ra.allowAnonymous
 - }
 - 
 - // AllScopes returns a list of unique scopes that is the combination
 - // of all the scopes in the requirements
 - func (ra *RouteAuthenticator) AllScopes() []string {
 - 	return ra.allScopes
 - }
 - 
 - // CommonScopes returns a list of unique scopes that are common in all the
 - // scopes in the requirements
 - func (ra *RouteAuthenticator) CommonScopes() []string {
 - 	return ra.commonScopes
 - }
 - 
 - // Authenticate Authenticator interface implementation
 - func (ra *RouteAuthenticator) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) {
 - 	if ra.allowAnonymous {
 - 		route.Authenticator = ra
 - 		return true, nil, nil
 - 	}
 - 	// iterate in proper order
 - 	var lastResult interface{}
 - 	for _, scheme := range ra.Schemes {
 - 		if authenticator, ok := ra.Authenticator[scheme]; ok {
 - 			applies, princ, err := authenticator.Authenticate(&security.ScopedAuthRequest{
 - 				Request:        req,
 - 				RequiredScopes: ra.Scopes[scheme],
 - 			})
 - 			if !applies {
 - 				return false, nil, nil
 - 			}
 - 			if err != nil {
 - 				route.Authenticator = ra
 - 				return true, nil, err
 - 			}
 - 			lastResult = princ
 - 		}
 - 	}
 - 	route.Authenticator = ra
 - 	return true, lastResult, nil
 - }
 - 
 - func stringSliceUnion(slices ...[]string) []string {
 - 	unique := make(map[string]struct{})
 - 	var result []string
 - 	for _, slice := range slices {
 - 		for _, entry := range slice {
 - 			if _, ok := unique[entry]; ok {
 - 				continue
 - 			}
 - 			unique[entry] = struct{}{}
 - 			result = append(result, entry)
 - 		}
 - 	}
 - 	return result
 - }
 - 
 - func stringSliceIntersection(slices ...[]string) []string {
 - 	unique := make(map[string]int)
 - 	var intersection []string
 - 
 - 	total := len(slices)
 - 	var emptyCnt int
 - 	for _, slice := range slices {
 - 		if len(slice) == 0 {
 - 			emptyCnt++
 - 			continue
 - 		}
 - 
 - 		for _, entry := range slice {
 - 			unique[entry]++
 - 			if unique[entry] == total-emptyCnt { // this entry appeared in all the non-empty slices
 - 				intersection = append(intersection, entry)
 - 			}
 - 		}
 - 	}
 - 
 - 	return intersection
 - }
 - 
 - // RouteAuthenticators represents a group of authenticators that represent a logical OR
 - type RouteAuthenticators []RouteAuthenticator
 - 
 - // AllowsAnonymous returns true when there is an authenticator that means optional auth
 - func (ras RouteAuthenticators) AllowsAnonymous() bool {
 - 	for _, ra := range ras {
 - 		if ra.AllowsAnonymous() {
 - 			return true
 - 		}
 - 	}
 - 	return false
 - }
 - 
 - // Authenticate method implemention so this collection can be used as authenticator
 - func (ras RouteAuthenticators) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) {
 - 	var lastError error
 - 	var allowsAnon bool
 - 	var anonAuth RouteAuthenticator
 - 
 - 	for _, ra := range ras {
 - 		if ra.AllowsAnonymous() {
 - 			anonAuth = ra
 - 			allowsAnon = true
 - 			continue
 - 		}
 - 		applies, usr, err := ra.Authenticate(req, route)
 - 		if !applies || err != nil || usr == nil {
 - 			if err != nil {
 - 				lastError = err
 - 			}
 - 			continue
 - 		}
 - 		return applies, usr, nil
 - 	}
 - 
 - 	if allowsAnon && lastError == nil {
 - 		route.Authenticator = &anonAuth
 - 		return true, nil, lastError
 - 	}
 - 	return lastError != nil, nil, lastError
 - }
 - 
 - type routeEntry struct {
 - 	PathPattern    string
 - 	BasePath       string
 - 	Operation      *spec.Operation
 - 	Consumes       []string
 - 	Consumers      map[string]runtime.Consumer
 - 	Produces       []string
 - 	Producers      map[string]runtime.Producer
 - 	Parameters     map[string]spec.Parameter
 - 	Handler        http.Handler
 - 	Formats        strfmt.Registry
 - 	Binder         *untypedRequestBinder
 - 	Authenticators RouteAuthenticators
 - 	Authorizer     runtime.Authorizer
 - }
 - 
 - // MatchedRoute represents the route that was matched in this request
 - type MatchedRoute struct {
 - 	routeEntry
 - 	Params        RouteParams
 - 	Consumer      runtime.Consumer
 - 	Producer      runtime.Producer
 - 	Authenticator *RouteAuthenticator
 - }
 - 
 - // HasAuth returns true when the route has a security requirement defined
 - func (m *MatchedRoute) HasAuth() bool {
 - 	return len(m.Authenticators) > 0
 - }
 - 
 - // NeedsAuth returns true when the request still
 - // needs to perform authentication
 - func (m *MatchedRoute) NeedsAuth() bool {
 - 	return m.HasAuth() && m.Authenticator == nil
 - }
 - 
 - func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
 - 	mth := strings.ToUpper(method)
 - 	debugLog("looking up route for %s %s", method, path)
 - 	if Debug {
 - 		if len(d.routers) == 0 {
 - 			debugLog("there are no known routers")
 - 		}
 - 		for meth := range d.routers {
 - 			debugLog("got a router for %s", meth)
 - 		}
 - 	}
 - 	if router, ok := d.routers[mth]; ok {
 - 		if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil {
 - 			if entry, ok := m.(*routeEntry); ok {
 - 				debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
 - 				var params RouteParams
 - 				for _, p := range rp {
 - 					v, err := pathUnescape(p.Value)
 - 					if err != nil {
 - 						debugLog("failed to escape %q: %v", p.Value, err)
 - 						v = p.Value
 - 					}
 - 					// a workaround to handle fragment/composing parameters until they are supported in denco router
 - 					// check if this parameter is a fragment within a path segment
 - 					if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + 2; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' {
 - 						// extract fragment parameters
 - 						ep := strings.Split(entry.PathPattern[xpos:], "/")[0]
 - 						pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil)
 - 						for i, pname := range pnames {
 - 							params = append(params, RouteParam{Name: pname, Value: pvalues[i]})
 - 						}
 - 					} else {
 - 						// use the parameter directly
 - 						params = append(params, RouteParam{Name: p.Name, Value: v})
 - 					}
 - 				}
 - 				return &MatchedRoute{routeEntry: *entry, Params: params}, true
 - 			}
 - 		} else {
 - 			debugLog("couldn't find a route by path for %s %s", method, path)
 - 		}
 - 	} else {
 - 		debugLog("couldn't find a route by method for %s %s", method, path)
 - 	}
 - 	return nil, false
 - }
 - 
 - func (d *defaultRouter) OtherMethods(method, path string) []string {
 - 	mn := strings.ToUpper(method)
 - 	var methods []string
 - 	for k, v := range d.routers {
 - 		if k != mn {
 - 			if _, _, ok := v.Lookup(fpath.Clean(path)); ok {
 - 				methods = append(methods, k)
 - 				continue
 - 			}
 - 		}
 - 	}
 - 	return methods
 - }
 - 
 - // convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco
 - var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`)
 - 
 - func decodeCompositParams(name string, value string, pattern string, names []string, values []string) ([]string, []string) {
 - 	pleft := strings.Index(pattern, "{")
 - 	names = append(names, name)
 - 	if pleft < 0 {
 - 		if strings.HasSuffix(value, pattern) {
 - 			values = append(values, value[:len(value)-len(pattern)])
 - 		} else {
 - 			values = append(values, "")
 - 		}
 - 	} else {
 - 		toskip := pattern[:pleft]
 - 		pright := strings.Index(pattern, "}")
 - 		vright := strings.Index(value, toskip)
 - 		if vright >= 0 {
 - 			values = append(values, value[:vright])
 - 		} else {
 - 			values = append(values, "")
 - 			value = ""
 - 		}
 - 		return decodeCompositParams(pattern[pleft+1:pright], value[vright+len(toskip):], pattern[pright+1:], names, values)
 - 	}
 - 	return names, values
 - }
 - 
 - func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) {
 - 	mn := strings.ToUpper(method)
 - 
 - 	bp := fpath.Clean(d.spec.BasePath())
 - 	if len(bp) > 0 && bp[len(bp)-1] == '/' {
 - 		bp = bp[:len(bp)-1]
 - 	}
 - 
 - 	debugLog("operation: %#v", *operation)
 - 	if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok {
 - 		consumes := d.analyzer.ConsumesFor(operation)
 - 		produces := d.analyzer.ProducesFor(operation)
 - 		parameters := d.analyzer.ParamsFor(method, strings.TrimPrefix(path, bp))
 - 
 - 		record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{
 - 			BasePath:       bp,
 - 			PathPattern:    path,
 - 			Operation:      operation,
 - 			Handler:        handler,
 - 			Consumes:       consumes,
 - 			Produces:       produces,
 - 			Consumers:      d.api.ConsumersFor(normalizeOffers(consumes)),
 - 			Producers:      d.api.ProducersFor(normalizeOffers(produces)),
 - 			Parameters:     parameters,
 - 			Formats:        d.api.Formats(),
 - 			Binder:         newUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()),
 - 			Authenticators: d.buildAuthenticators(operation),
 - 			Authorizer:     d.api.Authorizer(),
 - 		})
 - 		d.records[mn] = append(d.records[mn], record)
 - 	}
 - }
 - 
 - func (d *defaultRouteBuilder) buildAuthenticators(operation *spec.Operation) RouteAuthenticators {
 - 	requirements := d.analyzer.SecurityRequirementsFor(operation)
 - 	var auths []RouteAuthenticator
 - 	for _, reqs := range requirements {
 - 		var schemes []string
 - 		scopes := make(map[string][]string, len(reqs))
 - 		var scopeSlices [][]string
 - 		for _, req := range reqs {
 - 			schemes = append(schemes, req.Name)
 - 			scopes[req.Name] = req.Scopes
 - 			scopeSlices = append(scopeSlices, req.Scopes)
 - 		}
 - 
 - 		definitions := d.analyzer.SecurityDefinitionsForRequirements(reqs)
 - 		authenticators := d.api.AuthenticatorsFor(definitions)
 - 		auths = append(auths, RouteAuthenticator{
 - 			Authenticator:  authenticators,
 - 			Schemes:        schemes,
 - 			Scopes:         scopes,
 - 			allScopes:      stringSliceUnion(scopeSlices...),
 - 			commonScopes:   stringSliceIntersection(scopeSlices...),
 - 			allowAnonymous: len(reqs) == 1 && reqs[0].Name == "",
 - 		})
 - 	}
 - 	return auths
 - }
 - 
 - func (d *defaultRouteBuilder) Build() *defaultRouter {
 - 	routers := make(map[string]*denco.Router)
 - 	for method, records := range d.records {
 - 		router := denco.New()
 - 		_ = router.Build(records)
 - 		routers[method] = router
 - 	}
 - 	return &defaultRouter{
 - 		spec:    d.spec,
 - 		routers: routers,
 - 	}
 - }
 
 
  |