<commit_before>package middlewares

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/cozy/cozy-stack/pkg/config/config"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/labstack/echo/v4"
)

// BasicAuth use HTTP basic authentication to authenticate a user. The secret
// of the user should be stored in a file with the specified name, stored in
// one of the the config.Paths directories.
//
// The format of the secret is the same as our hashed passwords in database: a
// scrypt hash with a salt contained in the value.
func BasicAuth(secretFileName string) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if c.QueryParam("Trace") == "true" {
				t := time.Now()
				defer func() {
					elapsed := time.Since(t)
					logger.
						WithDomain("admin").
						WithField("nspace", "trace").
						Printf("Check basic auth: %v", elapsed)
				}()
			}

			_, passphrase, ok := c.Request().BasicAuth()
			if !ok {
				return echo.NewHTTPError(http.StatusUnauthorized, "missing basic auth")
			}

			shadowFile, err := config.FindConfigFile(secretFileName)
			if err != nil {
				return echo.NewHTTPError(http.StatusInternalServerError, err)
			}

			f, err := os.Open(shadowFile)
			if err != nil {
				return echo.NewHTTPError(http.StatusInternalServerError, err)
			}
			defer f.Close()

			b, err := ioutil.ReadAll(f)
			if err != nil {
				return echo.NewHTTPError(http.StatusInternalServerError, err)
			}
			b = bytes.TrimSpace(b)

			needUpdate, err := crypto.CompareHashAndPassphrase(b, []byte(passphrase))
			if err != nil {
				return echo.NewHTTPError(http.StatusForbidden, "bad passphrase")
			}
			if needUpdate {
				logger.
					WithDomain("admin").
					Warnf("Passphrase hash from %q needs update and should be regenerated", secretFileName)
			}

			return next(c)
		}
	}
}<commit_msg>Fix trace in BasicAuth middleware (#2646)<commit_after>package middlewares

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/cozy/cozy-stack/pkg/config/config"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/labstack/echo/v4"
)

// BasicAuth use HTTP basic authentication to authenticate a user. The secret
// of the user should be stored in a file with the specified name, stored in
// one of the the config.Paths directories.
//
// The format of the secret is the same as our hashed passwords in database: a
// scrypt hash with a salt contained in the value.
func BasicAuth(secretFileName string) echo.MiddlewareFunc {
	check := func(next echo.HandlerFunc, c echo.Context) error {
		if c.QueryParam("Trace") == "true" {
			t := time.Now()
			defer func() {
				elapsed := time.Since(t)
				logger.
					WithDomain("admin").
					WithField("nspace", "trace").
					Printf("Check basic auth: %v", elapsed)
			}()
		}

		_, passphrase, ok := c.Request().BasicAuth()
		if !ok {
			return echo.NewHTTPError(http.StatusUnauthorized, "missing basic auth")
		}

		shadowFile, err := config.FindConfigFile(secretFileName)
		if err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, err)
		}

		f, err := os.Open(shadowFile)
		if err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, err)
		}
		defer f.Close()

		b, err := ioutil.ReadAll(f)
		if err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, err)
		}
		b = bytes.TrimSpace(b)

		needUpdate, err := crypto.CompareHashAndPassphrase(b, []byte(passphrase))
		if err != nil {
			return echo.NewHTTPError(http.StatusForbidden, "bad passphrase")
		}
		if needUpdate {
			logger.
				WithDomain("admin").
				Warnf("Passphrase hash from %q needs update and should be regenerated", secretFileName)
		}

		return nil
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if err := check(next, c); err != nil {
				return err
			}

			return next(c)
		}
	}
}
{'fraction_non_alphanumeric': 0.12534332084893882, 'fraction_numerical': 0.00149812734082397, 'mean_word_length': 3.451111111111111, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7532222', 'n_tokens_mistral': 1521, 'n_tokens_neox': 1338, 'n_words': 355}