/* * Copyright 2026 Oleg Borodin * * This work is published and licensed under a Creative Commons * Attribution-NonCommercial-NoDerivatives 4.0 International License. * * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ package handler import ( "context" "fmt" "regexp" "mstore/app/router" "mstore/pkg/auxhttp" "mstore/pkg/auxpwd" "mstore/pkg/terms" ) const ( authTag = "authpass" userTag = "accountID" ) func (hand *Handler) AuthMiddleware(next router.Handler) router.Handler { var handlerFunc router.HandlerFunc handlerFunc = func(rctx *router.Context) { success, accountID, err := hand.CheckAccess(rctx) if success { rctx.SetBool(authTag, true) rctx.SetString(userTag, string(accountID)) } if err != nil { hand.logg.Errorf("Authorization middleware error: %v", err) } next.ServeHTTP(rctx) } return handlerFunc } // Authentification func (hand *Handler) CheckAccess(rctx *router.Context) (bool, string, error) { var err error var success bool var username string var password string var accountID string accountID = terms.AnonymousID hand.logg.Debugf("URL: %s", rctx.URL().String()) authHeader := rctx.GetHeader("Authorization") hand.logg.Debugf("Authorization: %s", authHeader) if authHeader != "" { username, password, err = auxhttp.ParseBasicAuth(authHeader) if err != nil { return success, accountID, err } if username == "" || password == "" { goto anonymous } success, id, err := hand.ValidatePassword(rctx.Ctx, username, password) if err != nil { return false, accountID, err } if !success { err = fmt.Errorf("Incorrect username or password") return false, accountID, err } accountID = id return success, accountID, err } anonymous: success = true accountID = terms.AnonymousID return success, accountID, err } func (hand *Handler) ValidatePassword(ctx context.Context, username, password string) (bool, string, error) { var err error var accountID string valid := false accountExists, accountDescr, err := hand.mdb.GetAccountByUsername(ctx, username) if !accountExists { err := fmt.Errorf("Account not exists") return valid, accountID, err } if !auxpwd.PasswordMatch([]byte(password), accountDescr.Passhash) { err := fmt.Errorf("Login data mismatch") return valid, accountID, err } valid = true accountID = accountDescr.ID return valid, accountID, err } // Authorization func (hand *Handler) CheckRight(ctx context.Context, accountID, reqRight, subject string) (bool, error) { var err error var res bool hand.logg.Debugf("Check right %s: right=%s subj=%s", accountID, reqRight, subject) exists, grants, err := hand.mdb.ListGrantsByAccoundIDRight(ctx, accountID, reqRight) if err != nil { return res, err } if !exists { return res, err } switch reqRight { case terms.RightReadFiles, terms.RightWriteFiles, terms.RightReadImages, terms.RightWriteImages: for _, grant := range grants { hand.logg.Debugf("Grant pattern=[%s], subject=[%s]", grant.Pattern, subject) re, err := regexp.Compile(grant.Pattern) if err != nil { hand.logg.Warningf("Wrong regexp %s for grant %s: %v", grant.Pattern, grant.ID, err) err = nil continue } if re.MatchString(subject) { res = true break } } default: res = true } return res, err }