podman

Форк
0
488 строк · 14.4 Кб
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

15
package middleware
16

17
import (
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
40
type RouteParam struct {
41
	Name  string
42
	Value string
43
}
44

45
// RouteParams the collection of route params
46
type RouteParams []RouteParam
47

48
// Get gets the value for the route param for the specified key
49
func (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
61
func (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
71
func 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
94
type 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
107
type Router interface {
108
	Lookup(method, path string) (*MatchedRoute, bool)
109
	OtherMethods(method, path string) []string
110
}
111

112
type defaultRouteBuilder struct {
113
	spec     *loads.Document
114
	analyzer *analysis.Spec
115
	api      RoutableAPI
116
	records  map[string][]denco.Record
117
}
118

119
type defaultRouter struct {
120
	spec    *loads.Document
121
	routers map[string]*denco.Router
122
}
123

124
func 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
134
func 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
151
type 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

160
func (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
166
func (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
172
func (ra *RouteAuthenticator) CommonScopes() []string {
173
	return ra.commonScopes
174
}
175

176
// Authenticate Authenticator interface implementation
177
func (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

204
func 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

219
func 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
243
type RouteAuthenticators []RouteAuthenticator
244

245
// AllowsAnonymous returns true when there is an authenticator that means optional auth
246
func (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
256
func (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

284
type 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
301
type 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
310
func (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
316
func (m *MatchedRoute) NeedsAuth() bool {
317
	return m.HasAuth() && m.Authenticator == nil
318
}
319

320
func (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

367
func (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
382
var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`)
383

384
func 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

408
func (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

450
func (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

477
func (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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.