github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/auth/share_by_link.go (about)

     1  package auth
     2  
     3  import (
     4  	"encoding/base64"
     5  	"errors"
     6  	"net/http"
     7  
     8  	"github.com/cozy/cozy-stack/model/permission"
     9  	"github.com/cozy/cozy-stack/model/session"
    10  	build "github.com/cozy/cozy-stack/pkg/config"
    11  	"github.com/cozy/cozy-stack/pkg/couchdb"
    12  	"github.com/cozy/cozy-stack/pkg/crypto"
    13  	"github.com/cozy/cozy-stack/web/middlewares"
    14  	"github.com/labstack/echo/v4"
    15  )
    16  
    17  // checkPasswordForShareByLink checks the password for a share by link
    18  // protected by password, and creates a cookie if the password is correct.
    19  func checkPasswordForShareByLink(c echo.Context) error {
    20  	res := c.Response()
    21  	origin := c.Request().Header.Get(echo.HeaderOrigin)
    22  	res.Header().Set(echo.HeaderAccessControlAllowOrigin, origin)
    23  	res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
    24  	res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
    25  
    26  	inst := middlewares.GetInstance(c)
    27  	permID := c.FormValue("perm_id")
    28  	perm, err := permission.GetByID(inst, permID)
    29  	if err != nil {
    30  		if couchdb.IsNotFoundError(err) || errors.Is(err, permission.ErrExpiredToken) {
    31  			return c.JSON(http.StatusNotFound, echo.Map{"error": err.Error()})
    32  		}
    33  		return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()})
    34  	}
    35  
    36  	hash64, _ := perm.Password.(string)
    37  	if len(hash64) == 0 {
    38  		return c.JSON(http.StatusOK, echo.Map{"password": "none"})
    39  	}
    40  	hash, err := base64.StdEncoding.DecodeString(hash64)
    41  	if err != nil {
    42  		return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()})
    43  	}
    44  
    45  	password := []byte(c.FormValue("password"))
    46  	_, err = crypto.CompareHashAndPassphrase(hash, password)
    47  	if err != nil {
    48  		msg := inst.Translate("Share by link Password Invalid")
    49  		return c.JSON(http.StatusForbidden, echo.Map{"error": msg})
    50  	}
    51  
    52  	// Put a cookie so that later requests can use the sharecode
    53  	cookieName := "pass" + permID
    54  	cfg := crypto.MACConfig{Name: cookieName, MaxLen: 256}
    55  	encoded, err := crypto.EncodeAuthMessage(cfg, inst.SessionSecret(), []byte(permID), nil)
    56  	if err != nil {
    57  		return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()})
    58  	}
    59  	cookie := &http.Cookie{
    60  		Name:     cookieName,
    61  		Value:    string(encoded),
    62  		MaxAge:   0,
    63  		Path:     "/",
    64  		Domain:   session.CookieDomain(inst),
    65  		Secure:   !build.IsDevRelease(),
    66  		HttpOnly: true,
    67  		SameSite: http.SameSiteLaxMode,
    68  	}
    69  	c.SetCookie(cookie)
    70  
    71  	return c.JSON(http.StatusOK, echo.Map{"password": "ok"})
    72  }