// 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 . package models import ( "strings" "code.vikunja.io/api/pkg/user" "code.vikunja.io/api/pkg/web" "xorm.io/xorm" ) // BotUser is a thin wrapper around user.User that implements CRUDable + Permissions // for bot-management endpoints. Ownership lives on users.bot_owner_id, so there is // no separate table. type BotUser struct { // Status shadows user.User.Status so it is included in JSON responses // (the original has json:"-"). Status user.Status `xorm:"-" json:"status" doc:"The bot's status: 0=active, 2=disabled. Set to 2 to disable the bot, 0 to re-enable it."` user.User `xorm:"extends"` web.CRUDable `xorm:"-" json:"-"` web.Permissions `xorm:"-" json:"-"` } // Create creates a new bot user. func (b *BotUser) Create(s *xorm.Session, a web.Auth) error { owner, ok := a.(*user.User) if !ok { return ErrGenericForbidden{} } b.ID = 0 created, err := user.CreateBotUser(s, &b.User, owner) if err != nil { return err } b.User = *created b.Status = created.Status return nil } // ReadAll returns all bots owned by the calling user. func (b *BotUser) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result any, resultCount int, numberOfTotalItems int64, err error) { limit, start := getLimitFromPageIndex(page, perPage) var bots []*BotUser q := s.Where("bot_owner_id = ?", a.GetID()) if search != "" { q = q.And("(username LIKE ? OR name LIKE ?)", "%"+search+"%", "%"+search+"%") } if limit > 0 { q = q.Limit(limit, start) } total, err := q.FindAndCount(&bots) if err != nil { return nil, 0, 0, err } for _, bot := range bots { bot.Status = bot.User.Status } return bots, len(bots), total, nil } // ReadOne returns a single bot user. // Ownership is verified in CanRead. func (b *BotUser) ReadOne(s *xorm.Session, _ web.Auth) error { u, err := user.GetUserByID(s, b.ID) if err != nil { return err } b.User = *u b.Status = u.Status return nil } // Update allows a narrow set of fields to be changed on an owned bot. // Ownership is verified in CanUpdate. func (b *BotUser) Update(s *xorm.Session, _ web.Auth) error { existing, err := user.GetUserByID(s, b.ID) if err != nil { return err } cols := []string{"name"} existing.Name = b.Name if b.Status == user.StatusDisabled { existing.Status = b.Status cols = append(cols, "status") } else if b.Status == user.StatusActive && existing.Status != user.StatusActive { existing.Status = b.Status cols = append(cols, "status") } if b.Username != "" && b.Username != existing.Username { if !strings.HasPrefix(b.Username, "bot-") { return &user.ErrBotUsernameMustHavePrefix{Username: b.Username} } existing.Username = b.Username cols = append(cols, "username") } if len(cols) > 0 { _, err = s.ID(existing.ID).Cols(cols...).Update(existing) } b.User = *existing b.Status = existing.Status return err } // Delete completely removes the bot user and all associated data. // Ownership is verified in CanDelete. func (b *BotUser) Delete(s *xorm.Session, _ web.Auth) error { existing, err := user.GetUserByID(s, b.ID) if err != nil { return err } return DeleteUser(s, existing) }