Mercurial > gemma
changeset 419:6627c48363a0
First attempt for user injection of proxy for using GeoServer with role based security.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Thu, 16 Aug 2018 13:39:13 +0200 |
parents | c70ddc6eb168 |
children | be38eec5cc25 |
files | pkg/controllers/proxy.go pkg/controllers/routes.go pkg/middleware/modifyquery.go |
diffstat | 3 files changed, 159 insertions(+), 47 deletions(-) [+] |
line wrap: on
line diff
--- a/pkg/controllers/proxy.go Thu Aug 16 13:14:46 2018 +0200 +++ b/pkg/controllers/proxy.go Thu Aug 16 13:39:13 2018 +0200 @@ -152,49 +152,52 @@ } } -func proxyModifyResponse(resp *http.Response) error { +func proxyModifyResponse(suffix string) func(*http.Response) error { + + return func(resp *http.Response) error { + + if !isXML(resp.Header) { + return nil + } + + pr, pw := io.Pipe() + + var ( + r io.ReadCloser + w io.WriteCloser + err error + ) + + reader, writer := encoding(resp.Header) + + if r, err = reader(resp.Body); err != nil { + return err + } - if !isXML(resp.Header) { + if w, err = writer(pw); err != nil { + return err + } + + go func(force io.ReadCloser) { + start := time.Now() + defer func() { + //r.Close() + w.Close() + pw.Close() + force.Close() + log.Printf("rewrite took %s\n", time.Since(start)) + }() + if err := rewrite(suffix, w, r); err != nil { + log.Printf("rewrite failed: %v\n", err) + return + } + log.Println("rewrite successful") + }(resp.Body) + + resp.Body = pr + return nil } - - pr, pw := io.Pipe() - - var ( - r io.ReadCloser - w io.WriteCloser - err error - ) - - reader, writer := encoding(resp.Header) - - if r, err = reader(resp.Body); err != nil { - return err - } - - if w, err = writer(pw); err != nil { - return err - } - - go func(force io.ReadCloser) { - start := time.Now() - defer func() { - //r.Close() - w.Close() - pw.Close() - force.Close() - log.Printf("rewrite took %s\n", time.Since(start)) - }() - if err := rewrite(w, r); err != nil { - log.Printf("rewrite failed: %v\n", err) - return - } - log.Println("rewrite successful") - }(resp.Body) - - resp.Body = pr - - return nil } var xmlContentTypes = []string{ @@ -217,10 +220,10 @@ var replaceRe = regexp.MustCompile(`\b(https?://[^\s\?]*)`) -func replace(s string) string { +func replace(suffix, s string) string { proxyKey := config.ProxyKey() - proxyPrefix := config.ProxyPrefix() + "/api/proxy/" + proxyPrefix := config.ProxyPrefix() + suffix return replaceRe.ReplaceAllStringFunc(s, func(s string) string { if _, found := proxyBlackList[s]; found { @@ -237,7 +240,7 @@ }) } -func rewrite(w io.Writer, r io.Reader) error { +func rewrite(suffix string, w io.Writer, r io.Reader) error { decoder := xml.NewDecoder(r) decoder.CharsetReader = charset.NewReaderLabel @@ -264,7 +267,7 @@ n = n.push() for i := range t.Attr { - t.Attr[i].Value = replace(t.Attr[i].Value) + t.Attr[i].Value = replace(suffix, t.Attr[i].Value) n.checkDef(&t.Attr[i]) } @@ -284,7 +287,7 @@ tok = t case xml.CharData: - tok = xml.CharData(replace(string(t))) + tok = xml.CharData(replace(suffix, string(t))) case xml.EndElement: s := n.lookup(t.Name.Space)
--- a/pkg/controllers/routes.go Thu Aug 16 13:14:46 2018 +0200 +++ b/pkg/controllers/routes.go Thu Aug 16 13:39:13 2018 +0200 @@ -7,6 +7,7 @@ "github.com/gorilla/mux" "gemma.intevation.de/gemma/pkg/auth" + "gemma.intevation.de/gemma/pkg/middleware" ) func BindRoutes(m *mux.Router) { @@ -54,10 +55,10 @@ // Proxy for external WFSs. proxy := &httputil.ReverseProxy{ Director: proxyDirector, - ModifyResponse: proxyModifyResponse, + ModifyResponse: proxyModifyResponse("/api/proxy/"), } - api.Handle(`/proxy/{hash}/{url}`, proxy). + api.Handle("/proxy/{hash}/{url}", proxy). Methods( http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete) @@ -67,6 +68,25 @@ http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete) + // Proxy for external WFSs. + internal := &httputil.ReverseProxy{ + Director: proxyDirector, + ModifyResponse: proxyModifyResponse("/api/internal"), + } + + internalAuth := all( + middleware.ModifyQuery(internal, middleware.InjectUser)) + + api.Handle("/internal/{hash}/{url}", internalAuth). + Methods( + http.MethodGet, http.MethodPost, + http.MethodPut, http.MethodDelete) + + api.Handle("/internal/{entry}", internalAuth). + Methods( + http.MethodGet, http.MethodPost, + http.MethodPut, http.MethodDelete) + // Token handling: Login/Logout. api.HandleFunc("/login", login). Methods(http.MethodGet, http.MethodPost)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/middleware/modifyquery.go Thu Aug 16 13:39:13 2018 +0200 @@ -0,0 +1,89 @@ +package middleware + +import ( + "log" + "net/http" + "net/url" + "strings" + + "gemma.intevation.de/gemma/pkg/auth" +) + +// ParseQuery is a modified version of the internal query +// parser of the url.parseQuery of the standard library. +func ParseQuery( + m url.Values, + query string, + keySep, valueSep string, + unescape func(string) (string, error), +) error { + if unescape == nil { + unescape = url.QueryUnescape + } + for query != "" { + key := query + if i := strings.Index(key, keySep); i >= 0 { + key, query = key[:i], key[i+1:] + + } else { + query = "" + } + if key == "" { + continue + } + value := "" + if i := strings.Index(key, valueSep); i >= 0 { + key, value = key[:i], key[i+1:] + } + key, err := unescape(key) + if err != nil { + return err + } + value, err = unescape(value) + if err != nil { + return err + } + m[key] = append(m[key], value) + } + return nil +} + +func ModifyQuery(next http.Handler, modify func(*http.Request, url.Values) error) http.Handler { + + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + + // GeoServer query parameters contain ';' as sub key separators. + // If we would use req.URL.Query() this would be split + // at the wrong level resulting in broken key/value pairs. + // So we do the splitting ourselves. + + parameters := make(url.Values) + + if err := ParseQuery(parameters, req.URL.RawQuery, "&", "=", nil); err != nil { + log.Printf("parsing query failed: %v\n", err) + http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + + if err := modify(req, parameters); err != nil { + log.Printf("modifying query parameters failed: %v\n", err) + http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + } + + req.URL.RawQuery = parameters.Encode() + + next.ServeHTTP(rw, req) + }) +} + +func InjectUser(req *http.Request, parameters url.Values) error { + // To prevent SQL injections + parameters.Del("env") + + session, ok := auth.GetSession(req) + if ok && !strings.ContainsAny(session.User, `\"':;`) { + log.Printf("Injecting user %s\n", session.User) + parameters.Set("env", "user:"+session.User) + } + return nil +}