1// Copyright 2015 go-swagger maintainers
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
15package middleware
16
17import (
18	"fmt"
19	"net/http"
20	fpath "path"
21	"regexp"
22	"strings"
23
24	"github.com/go-openapi/runtime/security"
25	"github.com/go-openapi/swag"
26
27	"github.com/go-openapi/analysis"
28	"github.com/go-openapi/errors"
29	"github.com/go-openapi/loads"
30	"github.com/go-openapi/spec"
31	"github.com/go-openapi/strfmt"
32
33	"github.com/go-openapi/runtime"
34	"github.com/go-openapi/runtime/middleware/denco"
35)
36
37// RouteParam is a object to capture route params in a framework agnostic way.
38// implementations of the muxer should use these route params to communicate with the
39// swagger framework
40type RouteParam struct {
41	Name  string
42	Value string
43}
44
45// RouteParams the collection of route params
46type RouteParams []RouteParam
47
48// Get gets the value for the route param for the specified key
49func (r RouteParams) Get(name string) string {
50	vv, _, _ := r.GetOK(name)
51	if len(vv) > 0 {
52		return vv[len(vv)-1]
53	}
54	return ""
55}
56
57// GetOK gets the value but also returns booleans to indicate if a key or value
58// is present. This aids in validation and satisfies an interface in use there
59//
60// The returned values are: data, has key, has value
61func (r RouteParams) GetOK(name string) ([]string, bool, bool) {
62	for _, p := range r {
63		if p.Name == name {
64			return []string{p.Value}, true, p.Value != ""
65		}
66	}
67	return nil, false, false
68}
69
70// NewRouter creates a new context aware router middleware
71func NewRouter(ctx *Context, next http.Handler) http.Handler {
72	if ctx.router == nil {
73		ctx.router = DefaultRouter(ctx.spec, ctx.api)
74	}
75
76	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
77		if _, rCtx, ok := ctx.RouteInfo(r); ok {
78			next.ServeHTTP(rw, rCtx)
79			return
80		}
81
82		// Not found, check if it exists in the other methods first
83		if others := ctx.AllowedMethods(r); len(others) > 0 {
84			ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
85			return
86		}
87
88		ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.EscapedPath()))
89	})
90}
91
92// RoutableAPI represents an interface for things that can serve
93// as a provider of implementations for the swagger router
94type RoutableAPI interface {
95	HandlerFor(string, string) (http.Handler, bool)
96	ServeErrorFor(string) func(http.ResponseWriter, *http.Request, error)
97	ConsumersFor([]string) map[string]runtime.Consumer
98	ProducersFor([]string) map[string]runtime.Producer
99	AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator
100	Authorizer() runtime.Authorizer
101	Formats() strfmt.Registry
102	DefaultProduces() string
103	DefaultConsumes() string
104}
105
106// Router represents a swagger aware router
107type Router interface {
108	Lookup(method, path string) (*MatchedRoute, bool)
109	OtherMethods(method, path string) []string
110}
111
112type defaultRouteBuilder struct {
113	spec     *loads.Document
114	analyzer *analysis.Spec
115	api      RoutableAPI
116	records  map[string][]denco.Record
117}
118
119type defaultRouter struct {
120	spec    *loads.Document
121	routers map[string]*denco.Router
122}
123
124func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder {
125	return &defaultRouteBuilder{
126		spec:     spec,
127		analyzer: analysis.New(spec.Spec()),
128		api:      api,
129		records:  make(map[string][]denco.Record),
130	}
131}
132
133// DefaultRouter creates a default implemenation of the router
134func DefaultRouter(spec *loads.Document, api RoutableAPI) Router {
135	builder := newDefaultRouteBuilder(spec, api)
136	if spec != nil {
137		for method, paths := range builder.analyzer.Operations() {
138			for path, operation := range paths {
139				fp := fpath.Join(spec.BasePath(), path)
140				debugLog("adding route %s %s %q", method, fp, operation.ID)
141				builder.AddRoute(method, fp, operation)
142			}
143		}
144	}
145	return builder.Build()
146}
147
148// RouteAuthenticator is an authenticator that can compose several authenticators together.
149// It also knows when it contains an authenticator that allows for anonymous pass through.
150// Contains a group of 1 or more authenticators that have a logical AND relationship
151type RouteAuthenticator struct {
152	Authenticator  map[string]runtime.Authenticator
153	Schemes        []string
154	Scopes         map[string][]string
155	allScopes      []string
156	commonScopes   []string
157	allowAnonymous bool
158}
159
160func (ra *RouteAuthenticator) AllowsAnonymous() bool {
161	return ra.allowAnonymous
162}
163
164// AllScopes returns a list of unique scopes that is the combination
165// of all the scopes in the requirements
166func (ra *RouteAuthenticator) AllScopes() []string {
167	return ra.allScopes
168}
169
170// CommonScopes returns a list of unique scopes that are common in all the
171// scopes in the requirements
172func (ra *RouteAuthenticator) CommonScopes() []string {
173	return ra.commonScopes
174}
175
176// Authenticate Authenticator interface implementation
177func (ra *RouteAuthenticator) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) {
178	if ra.allowAnonymous {
179		route.Authenticator = ra
180		return true, nil, nil
181	}
182	// iterate in proper order
183	var lastResult interface{}
184	for _, scheme := range ra.Schemes {
185		if authenticator, ok := ra.Authenticator[scheme]; ok {
186			applies, princ, err := authenticator.Authenticate(&security.ScopedAuthRequest{
187				Request:        req,
188				RequiredScopes: ra.Scopes[scheme],
189			})
190			if !applies {
191				return false, nil, nil
192			}
193			if err != nil {
194				route.Authenticator = ra
195				return true, nil, err
196			}
197			lastResult = princ
198		}
199	}
200	route.Authenticator = ra
201	return true, lastResult, nil
202}
203
204func stringSliceUnion(slices ...[]string) []string {
205	unique := make(map[string]struct{})
206	var result []string
207	for _, slice := range slices {
208		for _, entry := range slice {
209			if _, ok := unique[entry]; ok {
210				continue
211			}
212			unique[entry] = struct{}{}
213			result = append(result, entry)
214		}
215	}
216	return result
217}
218
219func stringSliceIntersection(slices ...[]string) []string {
220	unique := make(map[string]int)
221	var intersection []string
222
223	total := len(slices)
224	var emptyCnt int
225	for _, slice := range slices {
226		if len(slice) == 0 {
227			emptyCnt++
228			continue
229		}
230
231		for _, entry := range slice {
232			unique[entry]++
233			if unique[entry] == total-emptyCnt { // this entry appeared in all the non-empty slices
234				intersection = append(intersection, entry)
235			}
236		}
237	}
238
239	return intersection
240}
241
242// RouteAuthenticators represents a group of authenticators that represent a logical OR
243type RouteAuthenticators []RouteAuthenticator
244
245// AllowsAnonymous returns true when there is an authenticator that means optional auth
246func (ras RouteAuthenticators) AllowsAnonymous() bool {
247	for _, ra := range ras {
248		if ra.AllowsAnonymous() {
249			return true
250		}
251	}
252	return false
253}
254
255// Authenticate method implemention so this collection can be used as authenticator
256func (ras RouteAuthenticators) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) {
257	var lastError error
258	var allowsAnon bool
259	var anonAuth RouteAuthenticator
260
261	for _, ra := range ras {
262		if ra.AllowsAnonymous() {
263			anonAuth = ra
264			allowsAnon = true
265			continue
266		}
267		applies, usr, err := ra.Authenticate(req, route)
268		if !applies || err != nil || usr == nil {
269			if err != nil {
270				lastError = err
271			}
272			continue
273		}
274		return applies, usr, nil
275	}
276
277	if allowsAnon && lastError == nil {
278		route.Authenticator = &anonAuth
279		return true, nil, lastError
280	}
281	return lastError != nil, nil, lastError
282}
283
284type routeEntry struct {
285	PathPattern    string
286	BasePath       string
287	Operation      *spec.Operation
288	Consumes       []string
289	Consumers      map[string]runtime.Consumer
290	Produces       []string
291	Producers      map[string]runtime.Producer
292	Parameters     map[string]spec.Parameter
293	Handler        http.Handler
294	Formats        strfmt.Registry
295	Binder         *UntypedRequestBinder
296	Authenticators RouteAuthenticators
297	Authorizer     runtime.Authorizer
298}
299
300// MatchedRoute represents the route that was matched in this request
301type MatchedRoute struct {
302	routeEntry
303	Params        RouteParams
304	Consumer      runtime.Consumer
305	Producer      runtime.Producer
306	Authenticator *RouteAuthenticator
307}
308
309// HasAuth returns true when the route has a security requirement defined
310func (m *MatchedRoute) HasAuth() bool {
311	return len(m.Authenticators) > 0
312}
313
314// NeedsAuth returns true when the request still
315// needs to perform authentication
316func (m *MatchedRoute) NeedsAuth() bool {
317	return m.HasAuth() && m.Authenticator == nil
318}
319
320func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
321	mth := strings.ToUpper(method)
322	debugLog("looking up route for %s %s", method, path)
323	if Debug {
324		if len(d.routers) == 0 {
325			debugLog("there are no known routers")
326		}
327		for meth := range d.routers {
328			debugLog("got a router for %s", meth)
329		}
330	}
331	if router, ok := d.routers[mth]; ok {
332		if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil {
333			if entry, ok := m.(*routeEntry); ok {
334				debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
335				var params RouteParams
336				for _, p := range rp {
337					v, err := pathUnescape(p.Value)
338					if err != nil {
339						debugLog("failed to escape %q: %v", p.Value, err)
340						v = p.Value
341					}
342					// a workaround to handle fragment/composing parameters until they are supported in denco router
343					// check if this parameter is a fragment within a path segment
344					if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + 2; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' {
345						// extract fragment parameters
346						ep := strings.Split(entry.PathPattern[xpos:], "/")[0]
347						pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil)
348						for i, pname := range pnames {
349							params = append(params, RouteParam{Name: pname, Value: pvalues[i]})
350						}
351					} else {
352						// use the parameter directly
353						params = append(params, RouteParam{Name: p.Name, Value: v})
354					}
355				}
356				return &MatchedRoute{routeEntry: *entry, Params: params}, true
357			}
358		} else {
359			debugLog("couldn't find a route by path for %s %s", method, path)
360		}
361	} else {
362		debugLog("couldn't find a route by method for %s %s", method, path)
363	}
364	return nil, false
365}
366
367func (d *defaultRouter) OtherMethods(method, path string) []string {
368	mn := strings.ToUpper(method)
369	var methods []string
370	for k, v := range d.routers {
371		if k != mn {
372			if _, _, ok := v.Lookup(fpath.Clean(path)); ok {
373				methods = append(methods, k)
374				continue
375			}
376		}
377	}
378	return methods
379}
380
381// convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco
382var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`)
383
384func decodeCompositParams(name string, value string, pattern string, names []string, values []string) ([]string, []string) {
385	pleft := strings.Index(pattern, "{")
386	names = append(names, name)
387	if pleft < 0 {
388		if strings.HasSuffix(value, pattern) {
389			values = append(values, value[:len(value)-len(pattern)])
390		} else {
391			values = append(values, "")
392		}
393	} else {
394		toskip := pattern[:pleft]
395		pright := strings.Index(pattern, "}")
396		vright := strings.Index(value, toskip)
397		if vright >= 0 {
398			values = append(values, value[:vright])
399		} else {
400			values = append(values, "")
401			value = ""
402		}
403		return decodeCompositParams(pattern[pleft+1:pright], value[vright+len(toskip):], pattern[pright+1:], names, values)
404	}
405	return names, values
406}
407
408func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) {
409	mn := strings.ToUpper(method)
410
411	bp := fpath.Clean(d.spec.BasePath())
412	if len(bp) > 0 && bp[len(bp)-1] == '/' {
413		bp = bp[:len(bp)-1]
414	}
415
416	debugLog("operation: %#v", *operation)
417	if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok {
418		consumes := d.analyzer.ConsumesFor(operation)
419		produces := d.analyzer.ProducesFor(operation)
420		parameters := d.analyzer.ParamsFor(method, strings.TrimPrefix(path, bp))
421
422		// add API defaults if not part of the spec
423		if defConsumes := d.api.DefaultConsumes(); defConsumes != "" && !swag.ContainsStringsCI(consumes, defConsumes) {
424			consumes = append(consumes, defConsumes)
425		}
426
427		if defProduces := d.api.DefaultProduces(); defProduces != "" && !swag.ContainsStringsCI(produces, defProduces) {
428			produces = append(produces, defProduces)
429		}
430
431		record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{
432			BasePath:       bp,
433			PathPattern:    path,
434			Operation:      operation,
435			Handler:        handler,
436			Consumes:       consumes,
437			Produces:       produces,
438			Consumers:      d.api.ConsumersFor(normalizeOffers(consumes)),
439			Producers:      d.api.ProducersFor(normalizeOffers(produces)),
440			Parameters:     parameters,
441			Formats:        d.api.Formats(),
442			Binder:         NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()),
443			Authenticators: d.buildAuthenticators(operation),
444			Authorizer:     d.api.Authorizer(),
445		})
446		d.records[mn] = append(d.records[mn], record)
447	}
448}
449
450func (d *defaultRouteBuilder) buildAuthenticators(operation *spec.Operation) RouteAuthenticators {
451	requirements := d.analyzer.SecurityRequirementsFor(operation)
452	var auths []RouteAuthenticator
453	for _, reqs := range requirements {
454		var schemes []string
455		scopes := make(map[string][]string, len(reqs))
456		var scopeSlices [][]string
457		for _, req := range reqs {
458			schemes = append(schemes, req.Name)
459			scopes[req.Name] = req.Scopes
460			scopeSlices = append(scopeSlices, req.Scopes)
461		}
462
463		definitions := d.analyzer.SecurityDefinitionsForRequirements(reqs)
464		authenticators := d.api.AuthenticatorsFor(definitions)
465		auths = append(auths, RouteAuthenticator{
466			Authenticator:  authenticators,
467			Schemes:        schemes,
468			Scopes:         scopes,
469			allScopes:      stringSliceUnion(scopeSlices...),
470			commonScopes:   stringSliceIntersection(scopeSlices...),
471			allowAnonymous: len(reqs) == 1 && reqs[0].Name == "",
472		})
473	}
474	return auths
475}
476
477func (d *defaultRouteBuilder) Build() *defaultRouter {
478	routers := make(map[string]*denco.Router)
479	for method, records := range d.records {
480		router := denco.New()
481		_ = router.Build(records)
482		routers[method] = router
483	}
484	return &defaultRouter{
485		spec:    d.spec,
486		routers: routers,
487	}
488}
489