blob: 2c6e66ed6ba6d1b2f50db9c52cc0de6e72852a13 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package filters
18
19import (
20 "net/http"
21 "regexp"
22 "strings"
23
24 "github.com/golang/glog"
25)
26
27// TODO: use restful.CrossOriginResourceSharing
28// See github.com/emicklei/go-restful/blob/master/examples/restful-CORS-filter.go, and
29// github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go
30// Or, for a more detailed implementation use https://github.com/martini-contrib/cors
31// or implement CORS at your proxy layer.
32
33// WithCORS is a simple CORS implementation that wraps an http Handler.
34// Pass nil for allowedMethods and allowedHeaders to use the defaults. If allowedOriginPatterns
35// is empty or nil, no CORS support is installed.
36func WithCORS(handler http.Handler, allowedOriginPatterns []string, allowedMethods []string, allowedHeaders []string, exposedHeaders []string, allowCredentials string) http.Handler {
37 if len(allowedOriginPatterns) == 0 {
38 return handler
39 }
40 allowedOriginPatternsREs := allowedOriginRegexps(allowedOriginPatterns)
41 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
42 origin := req.Header.Get("Origin")
43 if origin != "" {
44 allowed := false
45 for _, re := range allowedOriginPatternsREs {
46 if allowed = re.MatchString(origin); allowed {
47 break
48 }
49 }
50 if allowed {
51 w.Header().Set("Access-Control-Allow-Origin", origin)
52 // Set defaults for methods and headers if nothing was passed
53 if allowedMethods == nil {
54 allowedMethods = []string{"POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"}
55 }
56 if allowedHeaders == nil {
57 allowedHeaders = []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-Requested-With", "If-Modified-Since"}
58 }
59 if exposedHeaders == nil {
60 exposedHeaders = []string{"Date"}
61 }
62 w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowedMethods, ", "))
63 w.Header().Set("Access-Control-Allow-Headers", strings.Join(allowedHeaders, ", "))
64 w.Header().Set("Access-Control-Expose-Headers", strings.Join(exposedHeaders, ", "))
65 w.Header().Set("Access-Control-Allow-Credentials", allowCredentials)
66
67 // Stop here if its a preflight OPTIONS request
68 if req.Method == "OPTIONS" {
69 w.WriteHeader(http.StatusNoContent)
70 return
71 }
72 }
73 }
74 // Dispatch to the next handler
75 handler.ServeHTTP(w, req)
76 })
77}
78
79func allowedOriginRegexps(allowedOrigins []string) []*regexp.Regexp {
80 res, err := compileRegexps(allowedOrigins)
81 if err != nil {
82 glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(allowedOrigins, ","), err)
83 }
84 return res
85}
86
87// Takes a list of strings and compiles them into a list of regular expressions
88func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
89 regexps := []*regexp.Regexp{}
90 for _, regexpStr := range regexpStrings {
91 r, err := regexp.Compile(regexpStr)
92 if err != nil {
93 return []*regexp.Regexp{}, err
94 }
95 regexps = append(regexps, r)
96 }
97 return regexps, nil
98}