mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-11-10 15:16:20 -06:00
A by-index address that no longer resolves falls back to the alias table and answers 307 with the task's current address and Cache-Control: no-store. The fallback requires read access to the project in the URL and to the target task, using the same 403 body as DoReadOne.
212 lines
5.7 KiB
Go
212 lines
5.7 KiB
Go
// Vikunja is a to-do list application to facilitate your life.
|
|
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package handler
|
|
|
|
import (
|
|
"context"
|
|
|
|
"code.vikunja.io/api/pkg/db"
|
|
"code.vikunja.io/api/pkg/events"
|
|
"code.vikunja.io/api/pkg/log"
|
|
"code.vikunja.io/api/pkg/web"
|
|
)
|
|
|
|
// DoCreate runs the permission check + model Create + commit pipeline for a
|
|
// CObject. Framework-agnostic: callable from both Echo (CreateWeb) and Huma.
|
|
// Caller is responsible for body/path binding and validation before calling.
|
|
func DoCreate(ctx context.Context, obj CObject, a web.Auth) error {
|
|
s := db.NewSession()
|
|
defer func() {
|
|
if err := s.Close(); err != nil {
|
|
log.Errorf("Could not close session: %s", err)
|
|
}
|
|
}()
|
|
|
|
canCreate, err := obj.CanCreate(s, a)
|
|
if err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
if !canCreate {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
log.Warningf("Tried to create while not having the permissions for it (User: %v)", a.GetID())
|
|
return ErrGenericForbidden{}
|
|
}
|
|
|
|
if err := obj.Create(s, a); err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
|
|
if err := s.Commit(); err != nil {
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
|
|
events.DispatchPending(ctx, s)
|
|
return nil
|
|
}
|
|
|
|
// DoReadOne runs the permission check + model ReadOne + commit pipeline for a
|
|
// CObject. obj should have its identifying fields set before call. On success,
|
|
// obj is fully populated. maxPermission is exposed via the x-max-permission
|
|
// header in the Echo wrapper; Huma wrapper may ignore it.
|
|
func DoReadOne(ctx context.Context, obj CObject, a web.Auth) (maxPermission int, err error) {
|
|
s := db.NewReadSession()
|
|
defer func() {
|
|
if cerr := s.Close(); cerr != nil {
|
|
log.Errorf("Could not close session: %s", cerr)
|
|
}
|
|
}()
|
|
|
|
canRead, maxPermission, err := obj.CanRead(s, a)
|
|
if err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return 0, err
|
|
}
|
|
if !canRead {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
log.Warningf("Tried to read while not having the permissions for it (User: %v)", a.GetID())
|
|
return 0, ErrReadForbidden()
|
|
}
|
|
|
|
if err := obj.ReadOne(s, a); err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return 0, err
|
|
}
|
|
|
|
if err := s.Commit(); err != nil {
|
|
events.CleanupPending(s)
|
|
return 0, err
|
|
}
|
|
|
|
events.DispatchPending(ctx, s)
|
|
return maxPermission, nil
|
|
}
|
|
|
|
// DoReadAll runs the ReadAll + commit pipeline for a CObject. obj may carry
|
|
// scoping context (e.g., TaskID on LabelTask). Returns the result slice/
|
|
// interface, the result count, and total count. Pagination header math and
|
|
// nil-slice normalization remain the caller's responsibility.
|
|
func DoReadAll(ctx context.Context, obj CObject, a web.Auth, search string, page, perPage int) (result any, resultCount int, total int64, err error) {
|
|
s := db.NewReadSession()
|
|
defer func() {
|
|
if cerr := s.Close(); cerr != nil {
|
|
log.Errorf("Could not close session: %s", cerr)
|
|
}
|
|
}()
|
|
|
|
result, resultCount, total, err = obj.ReadAll(s, a, search, page, perPage)
|
|
if err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return nil, 0, 0, err
|
|
}
|
|
|
|
if err = s.Commit(); err != nil {
|
|
events.CleanupPending(s)
|
|
return nil, 0, 0, err
|
|
}
|
|
|
|
events.DispatchPending(ctx, s)
|
|
return result, resultCount, total, nil
|
|
}
|
|
|
|
// DoUpdate runs the permission check + model Update + commit pipeline for a
|
|
// CObject. Framework-agnostic. Caller is responsible for body/path binding
|
|
// and validation before calling.
|
|
func DoUpdate(ctx context.Context, obj CObject, a web.Auth) error {
|
|
s := db.NewSession()
|
|
defer func() {
|
|
if err := s.Close(); err != nil {
|
|
log.Errorf("Could not close session: %s", err)
|
|
}
|
|
}()
|
|
|
|
canUpdate, err := obj.CanUpdate(s, a)
|
|
if err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
if !canUpdate {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
log.Warningf("Tried to update while not having the permissions for it (User: %v)", a.GetID())
|
|
return ErrGenericForbidden{}
|
|
}
|
|
|
|
if err := obj.Update(s, a); err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
|
|
if err := s.Commit(); err != nil {
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
|
|
events.DispatchPending(ctx, s)
|
|
return nil
|
|
}
|
|
|
|
// DoDelete runs the permission check + model Delete + commit pipeline for a
|
|
// CObject. Framework-agnostic. Caller is responsible for path binding before
|
|
// calling.
|
|
func DoDelete(ctx context.Context, obj CObject, a web.Auth) error {
|
|
s := db.NewSession()
|
|
defer func() {
|
|
if err := s.Close(); err != nil {
|
|
log.Errorf("Could not close session: %s", err)
|
|
}
|
|
}()
|
|
|
|
canDelete, err := obj.CanDelete(s, a)
|
|
if err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
if !canDelete {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
log.Warningf("Tried to delete while not having the permissions for it (User: %v)", a.GetID())
|
|
return ErrGenericForbidden{}
|
|
}
|
|
|
|
if err := obj.Delete(s, a); err != nil {
|
|
_ = s.Rollback()
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
|
|
if err := s.Commit(); err != nil {
|
|
events.CleanupPending(s)
|
|
return err
|
|
}
|
|
|
|
events.DispatchPending(ctx, s)
|
|
return nil
|
|
}
|