Repository: go-gitea/gitea
File: services/lfs/server.go (Go)

Code before:
// to proceed. This server assumes an HTTP Basic auth format.
func authenticate(ctx *context.Context, repository *repo_model.Repository, authorization string, requireSigned, requireWrite bool) bool {
	accessMode := perm_model.AccessModeRead
	if requireWrite {
		accessMode = perm_model.AccessModeWrite
	}

	var perm access_model.Permission
	var err error
	if ctx.Data["IsActionsToken"] == true {
		taskID := ctx.Data["ActionsTaskID"].(int64)
		perm, err = access_model.GetActionsUserRepoPermission(ctx, repository, ctx.Doer, taskID)
		if err != nil {
			log.Error("Unable to GetActionsUserRepoPermission for task[%d] Error: %v", taskID, err)
			return false
		}
	} else {
		// ctx.IsSigned is unnecessary here, this will be checked in perm.CanAccess
		perm, err = access_model.GetUserRepoPermission(ctx, repository, ctx.Doer)
		if err != nil {
			log.Error("Unable to GetUserRepoPermission for user %-v in repo %-v Error: %v", ctx.Doer, repository, err)
			return false
		}
	}

	canRead := perm.CanAccess(accessMode, unit.TypeCode)
	if canRead && (!requireSigned || ctx.IsSigned) {
		return true
	}

	user, err := parseToken(ctx, authorization, repository, accessMode)
	if err != nil {
		// Most of these are Warn level - the true internal server errors are logged in parseToken already
		log.Warn("Authentication failure for provided token with Error: %v", err)
		return false
	}
	ctx.Doer = user
	return true
}

func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repository, mode perm_model.AccessMode) (*user_model.User, error) {
	if !strings.Contains(tokenSHA, ".") {
		return nil, nil
	}
	token, err := jwt.ParseWithClaims(tokenSHA, &Claims{}, func(t *jwt.Token) (any, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return setting.LFS.JWTSecretBytes, nil
	})
	if err != nil {

Review comment (security):
Still have some questions about the legacy code:

* `canRead` seems misleading, it sometimes checks "write" permission?
* The changed logic makes the Actions user also calls `parseToken` and related logic (which depends on LFS JWT), is it fine?



Code after:
			maps.Copy(verifyHeader, header)

			// This is only needed to workaround https://github.com/git-lfs/git-lfs/issues/3662
			verifyHeader["Accept"] = lfs_module.AcceptHeader

			rep.Actions["verify"] = &lfs_module.Link{Href: rc.VerifyLink(pointer), Header: verifyHeader}
		}
	}
	return rep
}

func writeStatus(ctx *context.Context, status int) {
	writeStatusMessage(ctx, status, http.StatusText(status))
}

func writeStatusMessage(ctx *context.Context, status int, message string) {
	ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType)
	ctx.Resp.WriteHeader(status)

	er := lfs_module.ErrorResponse{Message: message}

	enc := json.NewEncoder(ctx.Resp)
	if err := enc.Encode(er); err != nil {
		log.Error("Failed to encode error response as json. Error: %v", err)
	}
}

// authenticate uses the authorization string to determine whether
// to proceed. This server assumes an HTTP Basic auth format.
func authenticate(ctx *context.Context, repository *repo_model.Repository, authorization string, requireSigned, requireWrite bool) bool {
	accessMode := perm_model.AccessModeRead
	if requireWrite {
		accessMode = perm_model.AccessModeWrite
	}

	if ctx.Data["IsActionsToken"] == true {
		taskID := ctx.Data["ActionsTaskID"].(int64)
		perm, err := access_model.GetActionsUserRepoPermission(ctx, repository, ctx.Doer, taskID)
		if err != nil {
			log.Error("Unable to GetActionsUserRepoPermission for task[%d] Error: %v", taskID, err)
			return false
		}
		return perm.CanAccess(accessMode, unit.TypeCode)
	}

	// it works for both anonymous request and signed-in user, then perm.CanAccess will do the permission check
	perm, err := access_model.GetUserRepoPermission(ctx, repository, ctx.Doer)
	if err != nil {
		log.Error("Unable to GetUserRepoPermission for user %-v in repo %-v Error: %v", ctx.Doer, repository, err)
		return false
	}