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.

478 lines
14 KiB

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