view pkg/middleware/modifyquery.go @ 5490:5f47eeea988d logging

Use own logging package.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 20 Sep 2021 17:45:39 +0200
parents 04967d6565fa
children
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package middleware

import (
	"encoding/hex"
	"net/http"
	"net/url"
	"strings"

	"gemma.intevation.de/gemma/pkg/auth"
	"gemma.intevation.de/gemma/pkg/log"
)

// 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
}

// ModifyQuery creates an http.Handler which calls a modify function first before
// calling the the nested next http.Handler.
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.Errorf("parsing query failed: %v\n", err)
			http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			return
		}

		if err := modify(req, parameters); err != nil {
			log.Errorf("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)
	})
}

// InjectUser is a modify function fitting into ModifyQuery to inject the
// user's name from the current session hex-encoded into the 'env' query parameter.
// This is used by the GeoServer to restrict the access to this the user/role
// to show show the requested map.
func InjectUser(req *http.Request, parameters url.Values) error {
	// To prevent SQL injections
	parameters.Del("env")

	if session, ok := auth.GetSession(req); ok {
		// log.Infof("injecting user %s\n", session.User)
		parameters.Set("env", "user:"+hex.EncodeToString([]byte(session.User)))
	}
	return nil
}