Remove GetByBean method because sometimes it's danger when query condition parameter is zero and also introduce new generic methods (#28220)

The function `GetByBean` has an obvious defect that when the fields are
empty values, it will be ignored. Then users will get a wrong result
which is possibly used to make a security problem.

To avoid the possibility, this PR removed function `GetByBean` and all
references.
And some new generic functions have been introduced to be used.

The recommand usage like below.

```go
// if query an object according id
obj, err := db.GetByID[Object](ctx, id)
// query with other conditions
obj, err := db.Get[Object](ctx, builder.Eq{"a": a, "b":b})
```
This commit is contained in:
Lunny Xiao 2023-12-07 15:27:36 +08:00 committed by GitHub
parent beb71f5ef6
commit dd30d9d5c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 189 additions and 174 deletions

View File

@ -131,24 +131,22 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
} }
defer committer.Close() defer committer.Close()
pkey := &PublicKey{ pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
Fingerprint: fingerprint,
}
has, err := db.GetByBean(ctx, pkey)
if err != nil { if err != nil {
return nil, err return nil, err
} } else if exist {
if has {
if pkey.Type != KeyTypeDeploy { if pkey.Type != KeyTypeDeploy {
return nil, ErrKeyAlreadyExist{0, fingerprint, ""} return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
} }
} else { } else {
// First time use this deploy key. // First time use this deploy key.
pkey.Mode = accessMode pkey = &PublicKey{
pkey.Type = KeyTypeDeploy Fingerprint: fingerprint,
pkey.Content = content Mode: accessMode,
pkey.Name = name Type: KeyTypeDeploy,
Content: content,
Name: name,
}
if err = addKey(ctx, pkey); err != nil { if err = addKey(ctx, pkey); err != nil {
return nil, fmt.Errorf("addKey: %w", err) return nil, fmt.Errorf("addKey: %w", err)
} }
@ -164,11 +162,10 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
// GetDeployKeyByID returns deploy key by given ID. // GetDeployKeyByID returns deploy key by given ID.
func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) { func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
key := new(DeployKey) key, exist, err := db.GetByID[DeployKey](ctx, id)
has, err := db.GetEngine(ctx).ID(id).Get(key)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrDeployKeyNotExist{id, 0, 0} return nil, ErrDeployKeyNotExist{id, 0, 0}
} }
return key, nil return key, nil
@ -176,14 +173,10 @@ func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
// GetDeployKeyByRepo returns deploy key by given public key ID and repository ID. // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, error) { func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, error) {
key := &DeployKey{ key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"key_id": keyID, "repo_id": repoID})
KeyID: keyID,
RepoID: repoID,
}
has, err := db.GetByBean(ctx, key)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrDeployKeyNotExist{0, keyID, repoID} return nil, ErrDeployKeyNotExist{0, keyID, repoID}
} }
return key, nil return key, nil

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"xorm.io/builder"
) )
// ___________.__ .__ __ // ___________.__ .__ __
@ -31,9 +32,7 @@ import (
// checkKeyFingerprint only checks if key fingerprint has been used as public key, // checkKeyFingerprint only checks if key fingerprint has been used as public key,
// it is OK to use same key as deploy key for multiple repositories/users. // it is OK to use same key as deploy key for multiple repositories/users.
func checkKeyFingerprint(ctx context.Context, fingerprint string) error { func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
has, err := db.GetByBean(ctx, &PublicKey{ has, err := db.Exist[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
Fingerprint: fingerprint,
})
if err != nil { if err != nil {
return err return err
} else if has { } else if has {

View File

@ -9,6 +9,8 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
) )
// Session represents a session compatible for go-chi session // Session represents a session compatible for go-chi session
@ -33,34 +35,28 @@ func UpdateSession(ctx context.Context, key string, data []byte) error {
// ReadSession reads the data for the provided session // ReadSession reads the data for the provided session
func ReadSession(ctx context.Context, key string) (*Session, error) { func ReadSession(ctx context.Context, key string) (*Session, error) {
session := Session{
Key: key,
}
ctx, committer, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer committer.Close() defer committer.Close()
if has, err := db.GetByBean(ctx, &session); err != nil { session, exist, err := db.Get[Session](ctx, builder.Eq{"key": key})
if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
session.Expiry = timeutil.TimeStampNow() session.Expiry = timeutil.TimeStampNow()
if err := db.Insert(ctx, &session); err != nil { if err := db.Insert(ctx, &session); err != nil {
return nil, err return nil, err
} }
} }
return &session, committer.Commit() return session, committer.Commit()
} }
// ExistSession checks if a session exists // ExistSession checks if a session exists
func ExistSession(ctx context.Context, key string) (bool, error) { func ExistSession(ctx context.Context, key string) (bool, error) {
session := Session{ return db.Exist[Session](ctx, builder.Eq{"key": key})
Key: key,
}
return db.GetEngine(ctx).Get(&session)
} }
// DestroySession destroys a session // DestroySession destroys a session
@ -79,17 +75,13 @@ func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, er
} }
defer committer.Close() defer committer.Close()
if has, err := db.GetByBean(ctx, &Session{ if has, err := db.Exist[Session](ctx, builder.Eq{"key": newKey}); err != nil {
Key: newKey,
}); err != nil {
return nil, err return nil, err
} else if has { } else if has {
return nil, fmt.Errorf("session Key: %s already exists", newKey) return nil, fmt.Errorf("session Key: %s already exists", newKey)
} }
if has, err := db.GetByBean(ctx, &Session{ if has, err := db.Exist[Session](ctx, builder.Eq{"key": oldKey}); err != nil {
Key: oldKey,
}); err != nil {
return nil, err return nil, err
} else if !has { } else if !has {
if err := db.Insert(ctx, &Session{ if err := db.Insert(ctx, &Session{
@ -104,14 +96,13 @@ func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, er
return nil, err return nil, err
} }
s := Session{ s, _, err := db.Get[Session](ctx, builder.Eq{"key": newKey})
Key: newKey, if err != nil {
} // is not exist, it should be impossible
if _, err := db.GetByBean(ctx, &s); err != nil {
return nil, err return nil, err
} }
return &s, committer.Commit() return s, committer.Commit()
} }
// CountSessions returns the number of sessions // CountSessions returns the number of sessions

View File

@ -265,10 +265,10 @@ func IsSSPIEnabled(ctx context.Context) bool {
return false return false
} }
exist, err := db.Exists[Source](ctx, FindSourcesOptions{ exist, err := db.Exist[Source](ctx, FindSourcesOptions{
IsActive: util.OptionalBoolTrue, IsActive: util.OptionalBoolTrue,
LoginType: SSPI, LoginType: SSPI,
}) }.ToConds())
if err != nil { if err != nil {
log.Error("Active SSPI Sources: %v", err) log.Error("Active SSPI Sources: %v", err)
return false return false

View File

@ -173,9 +173,44 @@ func Exec(ctx context.Context, sqlAndArgs ...any) (sql.Result, error) {
return GetEngine(ctx).Exec(sqlAndArgs...) return GetEngine(ctx).Exec(sqlAndArgs...)
} }
// GetByBean filled empty fields of the bean according non-empty fields to query in database. func Get[T any](ctx context.Context, cond builder.Cond) (object *T, exist bool, err error) {
func GetByBean(ctx context.Context, bean any) (bool, error) { if !cond.IsValid() {
return GetEngine(ctx).Get(bean) return nil, false, ErrConditionRequired{}
}
var bean T
has, err := GetEngine(ctx).Where(cond).NoAutoCondition().Get(&bean)
if err != nil {
return nil, false, err
} else if !has {
return nil, false, nil
}
return &bean, true, nil
}
func GetByID[T any](ctx context.Context, id int64) (object *T, exist bool, err error) {
var bean T
has, err := GetEngine(ctx).ID(id).NoAutoCondition().Get(&bean)
if err != nil {
return nil, false, err
} else if !has {
return nil, false, nil
}
return &bean, true, nil
}
func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) {
if !cond.IsValid() {
return false, ErrConditionRequired{}
}
var bean T
return GetEngine(ctx).Where(cond).NoAutoCondition().Exist(&bean)
}
func ExistByID[T any](ctx context.Context, id int64) (bool, error) {
var bean T
return GetEngine(ctx).ID(id).NoAutoCondition().Exist(&bean)
} }
// DeleteByBean deletes all records according non-empty fields of the bean as conditions. // DeleteByBean deletes all records according non-empty fields of the bean as conditions.
@ -264,8 +299,3 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) {
return nil, false return nil, false
} }
} }
func Exists[T any](ctx context.Context, opts FindOptions) (bool, error) {
var bean T
return GetEngine(ctx).Where(opts.ToConds()).Exist(&bean)
}

View File

@ -72,3 +72,21 @@ func (err ErrNotExist) Error() string {
func (err ErrNotExist) Unwrap() error { func (err ErrNotExist) Unwrap() error {
return util.ErrNotExist return util.ErrNotExist
} }
// ErrConditionRequired represents an error which require condition.
type ErrConditionRequired struct{}
// IsErrConditionRequired checks if an error is an ErrConditionRequired
func IsErrConditionRequired(err error) bool {
_, ok := err.(ErrConditionRequired)
return ok
}
func (err ErrConditionRequired) Error() string {
return "condition is required"
}
// Unwrap unwraps this as a ErrNotExist err
func (err ErrConditionRequired) Unwrap() error {
return util.ErrInvalidArgument
}

View File

@ -31,11 +31,11 @@ func TestIterate(t *testing.T) {
assert.EqualValues(t, cnt, repoUnitCnt) assert.EqualValues(t, cnt, repoUnitCnt)
err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error { err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error {
reopUnit2 := repo_model.RepoUnit{ID: repoUnit.ID} has, err := db.ExistByID[repo_model.RepoUnit](ctx, repoUnit.ID)
has, err := db.GetByBean(ctx, &reopUnit2)
if err != nil { if err != nil {
return err return err
} else if !has { }
if !has {
return db.ErrNotExist{Resource: "repo_unit", ID: repoUnit.ID} return db.ErrNotExist{Resource: "repo_unit", ID: repoUnit.ID}
} }
assert.EqualValues(t, repoUnit.RepoID, repoUnit.RepoID) assert.EqualValues(t, repoUnit.RepoID, repoUnit.RepoID)

View File

@ -135,7 +135,7 @@ var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database // NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
// if it is not already present. // if it is not already present.
func NewLFSMetaObject(ctx context.Context, m *LFSMetaObject) (*LFSMetaObject, error) { func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) {
var err error var err error
ctx, committer, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
@ -144,16 +144,15 @@ func NewLFSMetaObject(ctx context.Context, m *LFSMetaObject) (*LFSMetaObject, er
} }
defer committer.Close() defer committer.Close()
has, err := db.GetByBean(ctx, m) m, exist, err := db.Get[LFSMetaObject](ctx, builder.Eq{"repository_id": repoID, "oid": p.Oid})
if err != nil { if err != nil {
return nil, err return nil, err
} } else if exist {
if has {
m.Existing = true m.Existing = true
return m, committer.Commit() return m, committer.Commit()
} }
m = &LFSMetaObject{Pointer: p, RepositoryID: repoID}
if err = db.Insert(ctx, m); err != nil { if err = db.Insert(ctx, m); err != nil {
return nil, err return nil, err
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/gobwas/glob/syntax" "github.com/gobwas/glob/syntax"
"xorm.io/builder"
) )
var ErrBranchIsProtected = errors.New("branch is protected") var ErrBranchIsProtected = errors.New("branch is protected")
@ -274,12 +275,11 @@ func (protectBranch *ProtectedBranch) IsUnprotectedFile(patterns []glob.Glob, pa
// GetProtectedBranchRuleByName getting protected branch rule by name // GetProtectedBranchRuleByName getting protected branch rule by name
func GetProtectedBranchRuleByName(ctx context.Context, repoID int64, ruleName string) (*ProtectedBranch, error) { func GetProtectedBranchRuleByName(ctx context.Context, repoID int64, ruleName string) (*ProtectedBranch, error) {
rel := &ProtectedBranch{RepoID: repoID, RuleName: ruleName} // branch_name is legacy name, it actually is rule name
has, err := db.GetByBean(ctx, rel) rel, exist, err := db.Get[ProtectedBranch](ctx, builder.Eq{"repo_id": repoID, "branch_name": ruleName})
if err != nil { if err != nil {
return nil, err return nil, err
} } else if !exist {
if !has {
return nil, nil return nil, nil
} }
return rel, nil return rel, nil
@ -287,12 +287,10 @@ func GetProtectedBranchRuleByName(ctx context.Context, repoID int64, ruleName st
// GetProtectedBranchRuleByID getting protected branch rule by rule ID // GetProtectedBranchRuleByID getting protected branch rule by rule ID
func GetProtectedBranchRuleByID(ctx context.Context, repoID, ruleID int64) (*ProtectedBranch, error) { func GetProtectedBranchRuleByID(ctx context.Context, repoID, ruleID int64) (*ProtectedBranch, error) {
rel := &ProtectedBranch{ID: ruleID, RepoID: repoID} rel, exist, err := db.Get[ProtectedBranch](ctx, builder.Eq{"repo_id": repoID, "id": ruleID})
has, err := db.GetByBean(ctx, rel)
if err != nil { if err != nil {
return nil, err return nil, err
} } else if !exist {
if !has {
return nil, nil return nil, nil
} }
return rel, nil return rel, nil

View File

@ -10,6 +10,8 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
) )
// IssueAssignees saves all issue assignees // IssueAssignees saves all issue assignees
@ -59,7 +61,7 @@ func GetAssigneeIDsByIssue(ctx context.Context, issueID int64) ([]int64, error)
// IsUserAssignedToIssue returns true when the user is assigned to the issue // IsUserAssignedToIssue returns true when the user is assigned to the issue
func IsUserAssignedToIssue(ctx context.Context, issue *Issue, user *user_model.User) (isAssigned bool, err error) { func IsUserAssignedToIssue(ctx context.Context, issue *Issue, user *user_model.User) (isAssigned bool, err error) {
return db.GetByBean(ctx, &IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID}) return db.Exist[IssueAssignees](ctx, builder.Eq{"assignee_id": user.ID, "issue_id": issue.ID})
} }
// ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it. // ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.

View File

@ -304,15 +304,11 @@ func GetLabelInRepoByName(ctx context.Context, repoID int64, labelName string) (
return nil, ErrRepoLabelNotExist{0, repoID} return nil, ErrRepoLabelNotExist{0, repoID}
} }
l := &Label{ l, exist, err := db.Get[Label](ctx, builder.Eq{"name": labelName, "repo_id": repoID})
Name: labelName,
RepoID: repoID,
}
has, err := db.GetByBean(ctx, l)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrRepoLabelNotExist{0, l.RepoID} return nil, ErrRepoLabelNotExist{0, repoID}
} }
return l, nil return l, nil
} }
@ -323,15 +319,11 @@ func GetLabelInRepoByID(ctx context.Context, repoID, labelID int64) (*Label, err
return nil, ErrRepoLabelNotExist{labelID, repoID} return nil, ErrRepoLabelNotExist{labelID, repoID}
} }
l := &Label{ l, exist, err := db.Get[Label](ctx, builder.Eq{"id": labelID, "repo_id": repoID})
ID: labelID,
RepoID: repoID,
}
has, err := db.GetByBean(ctx, l)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrRepoLabelNotExist{l.ID, l.RepoID} return nil, ErrRepoLabelNotExist{labelID, repoID}
} }
return l, nil return l, nil
} }
@ -408,15 +400,11 @@ func GetLabelInOrgByName(ctx context.Context, orgID int64, labelName string) (*L
return nil, ErrOrgLabelNotExist{0, orgID} return nil, ErrOrgLabelNotExist{0, orgID}
} }
l := &Label{ l, exist, err := db.Get[Label](ctx, builder.Eq{"name": labelName, "org_id": orgID})
Name: labelName,
OrgID: orgID,
}
has, err := db.GetByBean(ctx, l)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrOrgLabelNotExist{0, l.OrgID} return nil, ErrOrgLabelNotExist{0, orgID}
} }
return l, nil return l, nil
} }
@ -427,15 +415,11 @@ func GetLabelInOrgByID(ctx context.Context, orgID, labelID int64) (*Label, error
return nil, ErrOrgLabelNotExist{labelID, orgID} return nil, ErrOrgLabelNotExist{labelID, orgID}
} }
l := &Label{ l, exist, err := db.Get[Label](ctx, builder.Eq{"id": labelID, "org_id": orgID})
ID: labelID,
OrgID: orgID,
}
has, err := db.GetByBean(ctx, l)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrOrgLabelNotExist{l.ID, l.OrgID} return nil, ErrOrgLabelNotExist{labelID, orgID}
} }
return l, nil return l, nil
} }

View File

@ -660,13 +660,10 @@ func GetPullRequestByIssueIDWithNoAttributes(ctx context.Context, issueID int64)
// GetPullRequestByIssueID returns pull request by given issue ID. // GetPullRequestByIssueID returns pull request by given issue ID.
func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest, error) { func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest, error) {
pr := &PullRequest{ pr, exist, err := db.Get[PullRequest](ctx, builder.Eq{"issue_id": issueID})
IssueID: issueID,
}
has, err := db.GetByBean(ctx, pr)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrPullRequestNotExist{0, issueID, 0, 0, "", ""} return nil, ErrPullRequestNotExist{0, issueID, 0, 0, "", ""}
} }
return pr, pr.LoadAttributes(ctx) return pr, pr.LoadAttributes(ctx)

View File

@ -162,7 +162,7 @@ func NewTeam(ctx context.Context, t *organization.Team) (err error) {
return err return err
} }
has, err := db.GetEngine(ctx).ID(t.OrgID).Get(new(user_model.User)) has, err := db.ExistByID[user_model.User](ctx, t.OrgID)
if err != nil { if err != nil {
return err return err
} }
@ -171,10 +171,10 @@ func NewTeam(ctx context.Context, t *organization.Team) (err error) {
} }
t.LowerName = strings.ToLower(t.Name) t.LowerName = strings.ToLower(t.Name)
has, err = db.GetEngine(ctx). has, err = db.Exist[organization.Team](ctx, builder.Eq{
Where("org_id=?", t.OrgID). "org_id": t.OrgID,
And("lower_name=?", t.LowerName). "lower_name": t.LowerName,
Get(new(organization.Team)) })
if err != nil { if err != nil {
return err return err
} }
@ -232,20 +232,20 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA
return err return err
} }
defer committer.Close() defer committer.Close()
sess := db.GetEngine(ctx)
t.LowerName = strings.ToLower(t.Name) t.LowerName = strings.ToLower(t.Name)
has, err := sess. has, err := db.Exist[organization.Team](ctx, builder.Eq{
Where("org_id=?", t.OrgID). "org_id": t.OrgID,
And("lower_name=?", t.LowerName). "lower_name": t.LowerName,
And("id!=?", t.ID). }.And(builder.Neq{"id": t.ID}),
Get(new(organization.Team)) )
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName} return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
} }
sess := db.GetEngine(ctx)
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description", if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil { "can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
return fmt.Errorf("update: %w", err) return fmt.Errorf("update: %w", err)

View File

@ -16,6 +16,8 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
) )
// ___________ // ___________
@ -203,14 +205,10 @@ func IsUsableTeamName(name string) error {
// GetTeam returns team by given team name and organization. // GetTeam returns team by given team name and organization.
func GetTeam(ctx context.Context, orgID int64, name string) (*Team, error) { func GetTeam(ctx context.Context, orgID int64, name string) (*Team, error) {
t := &Team{ t, exist, err := db.Get[Team](ctx, builder.Eq{"org_id": orgID, "lower_name": strings.ToLower(name)})
OrgID: orgID,
LowerName: strings.ToLower(name),
}
has, err := db.GetByBean(ctx, t)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrTeamNotExist{orgID, 0, name} return nil, ErrTeamNotExist{orgID, 0, name}
} }
return t, nil return t, nil

View File

@ -13,6 +13,8 @@ import (
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"xorm.io/builder"
) )
// Access represents the highest access level of a user to the repository. The only access type // Access represents the highest access level of a user to the repository. The only access type
@ -51,9 +53,11 @@ func accessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Re
return perm.AccessModeOwner, nil return perm.AccessModeOwner, nil
} }
a := &Access{UserID: userID, RepoID: repo.ID} a, exist, err := db.Get[Access](ctx, builder.Eq{"user_id": userID, "repo_id": repo.ID})
if has, err := db.GetByBean(ctx, a); !has || err != nil { if err != nil {
return mode, err return mode, err
} else if !exist {
return mode, nil
} }
return a.Mode, nil return a.Mode, nil
} }

View File

@ -13,6 +13,8 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
) )
type Setting struct { type Setting struct {
@ -36,16 +38,17 @@ func init() {
const keyRevision = "revision" const keyRevision = "revision"
func GetRevision(ctx context.Context) int { func GetRevision(ctx context.Context) int {
revision := &Setting{SettingKey: keyRevision} revision, exist, err := db.Get[Setting](ctx, builder.Eq{"setting_key": keyRevision})
if has, err := db.GetByBean(ctx, revision); err != nil { if err != nil {
return 0 return 0
} else if !has { } else if !exist {
err = db.Insert(ctx, &Setting{SettingKey: keyRevision, Version: 1}) err = db.Insert(ctx, &Setting{SettingKey: keyRevision, Version: 1})
if err != nil { if err != nil {
return 0 return 0
} }
return 1 return 1
} else if revision.Version <= 0 || revision.Version >= math.MaxInt-1 { }
if revision.Version <= 0 || revision.Version >= math.MaxInt-1 {
_, err = db.Exec(ctx, "UPDATE system_setting SET version=1 WHERE setting_key=?", keyRevision) _, err = db.Exec(ctx, "UPDATE system_setting SET version=1 WHERE setting_key=?", keyRevision)
if err != nil { if err != nil {
return 0 return 0

View File

@ -41,14 +41,11 @@ func TestSettings(t *testing.T) {
assert.EqualValues(t, "false", settings[keyName]) assert.EqualValues(t, "false", settings[keyName])
// setting the same value should not trigger DuplicateKey error, and the "version" should be increased // setting the same value should not trigger DuplicateKey error, and the "version" should be increased
setting := &system.Setting{SettingKey: keyName}
_, err = db.GetByBean(db.DefaultContext, setting)
assert.NoError(t, err)
assert.EqualValues(t, 2, setting.Version)
err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"}) err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"})
assert.NoError(t, err) assert.NoError(t, err)
setting = &system.Setting{SettingKey: keyName}
_, err = db.GetByBean(db.DefaultContext, setting) rev, settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 3, setting.Version) assert.Len(t, settings, 2)
assert.EqualValues(t, 4, rev)
} }

View File

@ -527,12 +527,13 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate
// Activate/deactivate a user's secondary email address // Activate/deactivate a user's secondary email address
// First check if there's another user active with the same address // First check if there's another user active with the same address
addr := EmailAddress{UID: userID, LowerEmail: strings.ToLower(email)} addr, exist, err := db.Get[EmailAddress](ctx, builder.Eq{"uid": userID, "lower_email": strings.ToLower(email)})
if has, err := db.GetByBean(ctx, &addr); err != nil { if err != nil {
return err return err
} else if !has { } else if !exist {
return fmt.Errorf("no such email: %d (%s)", userID, email) return fmt.Errorf("no such email: %d (%s)", userID, email)
} }
if addr.IsActivated == activate { if addr.IsActivated == activate {
// Already in the desired state; no action // Already in the desired state; no action
return nil return nil
@ -544,25 +545,26 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate
return ErrEmailAlreadyUsed{Email: email} return ErrEmailAlreadyUsed{Email: email}
} }
} }
if err = updateActivation(ctx, &addr, activate); err != nil { if err = updateActivation(ctx, addr, activate); err != nil {
return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err) return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err)
} }
// Activate/deactivate a user's primary email address and account // Activate/deactivate a user's primary email address and account
if addr.IsPrimary { if addr.IsPrimary {
user := User{ID: userID, Email: email} user, exist, err := db.Get[User](ctx, builder.Eq{"id": userID, "email": email})
if has, err := db.GetByBean(ctx, &user); err != nil { if err != nil {
return err return err
} else if !has { } else if !exist {
return fmt.Errorf("no user with ID: %d and Email: %s", userID, email) return fmt.Errorf("no user with ID: %d and Email: %s", userID, email)
} }
// The user's activation state should be synchronized with the primary email // The user's activation state should be synchronized with the primary email
if user.IsActive != activate { if user.IsActive != activate {
user.IsActive = activate user.IsActive = activate
if user.Rands, err = GetUserSalt(); err != nil { if user.Rands, err = GetUserSalt(); err != nil {
return fmt.Errorf("unable to generate salt: %w", err) return fmt.Errorf("unable to generate salt: %w", err)
} }
if err = UpdateUserCols(ctx, &user, "is_active", "rands"); err != nil { if err = UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err) return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err)
} }
} }

View File

@ -98,9 +98,10 @@ func GetExternalLogin(ctx context.Context, externalLoginUser *ExternalLoginUser)
// LinkExternalToUser link the external user to the user // LinkExternalToUser link the external user to the user
func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error { func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error {
has, err := db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID). has, err := db.Exist[ExternalLoginUser](ctx, builder.Eq{
NoAutoCondition(). "external_id": externalLoginUser.ExternalID,
Exist(externalLoginUser) "login_source_id": externalLoginUser.LoginSourceID,
})
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
@ -145,9 +146,10 @@ func GetUserIDByExternalUserID(ctx context.Context, provider, userID string) (in
// UpdateExternalUserByExternalID updates an external user's information // UpdateExternalUserByExternalID updates an external user's information
func UpdateExternalUserByExternalID(ctx context.Context, external *ExternalLoginUser) error { func UpdateExternalUserByExternalID(ctx context.Context, external *ExternalLoginUser) error {
has, err := db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID). has, err := db.Exist[ExternalLoginUser](ctx, builder.Eq{
NoAutoCondition(). "external_id": external.ExternalID,
Exist(external) "login_source_id": external.LoginSourceID,
})
if err != nil { if err != nil {
return err return err
} else if !has { } else if !has {

View File

@ -16,6 +16,7 @@ import (
webhook_module "code.gitea.io/gitea/modules/webhook" webhook_module "code.gitea.io/gitea/modules/webhook"
gouuid "github.com/google/uuid" gouuid "github.com/google/uuid"
"xorm.io/builder"
) )
// ___ ___ __ ___________ __ // ___ ___ __ ___________ __
@ -150,14 +151,10 @@ func UpdateHookTask(ctx context.Context, t *HookTask) error {
// ReplayHookTask copies a hook task to get re-delivered // ReplayHookTask copies a hook task to get re-delivered
func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask, error) { func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask, error) {
task := &HookTask{ task, exist, err := db.Get[HookTask](ctx, builder.Eq{"hook_id": hookID, "uuid": uuid})
HookID: hookID,
UUID: uuid,
}
has, err := db.GetByBean(ctx, task)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !exist {
return nil, ErrHookTaskNotExist{ return nil, ErrHookTaskNotExist{
HookID: hookID, HookID: hookID,
UUID: uuid, UUID: uuid,

View File

@ -11,24 +11,27 @@ import (
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"xorm.io/builder"
) )
func AddCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error { func AddCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
return db.WithTx(ctx, func(ctx context.Context) error { return db.WithTx(ctx, func(ctx context.Context) error {
collaboration := &repo_model.Collaboration{ has, err := db.Exist[repo_model.Collaboration](ctx, builder.Eq{
RepoID: repo.ID, "repo_id": repo.ID,
UserID: u.ID, "user_id": u.ID,
} })
has, err := db.GetByBean(ctx, collaboration)
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
return nil return nil
} }
collaboration.Mode = perm.AccessModeWrite
if err = db.Insert(ctx, collaboration); err != nil { if err = db.Insert(ctx, &repo_model.Collaboration{
RepoID: repo.ID,
UserID: u.ID,
Mode: perm.AccessModeWrite,
}); err != nil {
return err return err
} }

View File

@ -409,7 +409,7 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
defer content.Close() defer content.Close()
_, err := git_model.NewLFSMetaObject(ctx, &git_model.LFSMetaObject{Pointer: p, RepositoryID: repo.ID}) _, err := git_model.NewLFSMetaObject(ctx, repo.ID, p)
if err != nil { if err != nil {
log.Error("Repo[%-v]: Error creating LFS meta object %-v: %v", repo, p, err) log.Error("Repo[%-v]: Error creating LFS meta object %-v: %v", repo, p, err)
return err return err
@ -456,7 +456,7 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
if exist { if exist {
log.Trace("Repo[%-v]: LFS object %-v already present; creating meta object", repo, pointerBlob.Pointer) log.Trace("Repo[%-v]: LFS object %-v already present; creating meta object", repo, pointerBlob.Pointer)
_, err := git_model.NewLFSMetaObject(ctx, &git_model.LFSMetaObject{Pointer: pointerBlob.Pointer, RepositoryID: repo.ID}) _, err := git_model.NewLFSMetaObject(ctx, repo.ID, pointerBlob.Pointer)
if err != nil { if err != nil {
log.Error("Repo[%-v]: Error creating LFS meta object %-v: %v", repo, pointerBlob.Pointer, err) log.Error("Repo[%-v]: Error creating LFS meta object %-v: %v", repo, pointerBlob.Pointer, err)
return err return err

View File

@ -232,7 +232,7 @@ func BatchHandler(ctx *context.Context) {
return return
} }
if accessible { if accessible {
_, err := git_model.NewLFSMetaObject(ctx, &git_model.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) _, err := git_model.NewLFSMetaObject(ctx, repository.ID, p)
if err != nil { if err != nil {
log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err) log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
writeStatus(ctx, http.StatusInternalServerError) writeStatus(ctx, http.StatusInternalServerError)
@ -325,7 +325,7 @@ func UploadHandler(ctx *context.Context) {
log.Error("Error putting LFS MetaObject [%s] into content store. Error: %v", p.Oid, err) log.Error("Error putting LFS MetaObject [%s] into content store. Error: %v", p.Oid, err)
return err return err
} }
_, err := git_model.NewLFSMetaObject(ctx, &git_model.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) _, err := git_model.NewLFSMetaObject(ctx, repository.ID, p)
return err return err
} }

View File

@ -127,9 +127,7 @@ func createLFSMetaObjectsFromCatFileBatch(ctx context.Context, catFileBatchReade
// OK we have a pointer that is associated with the head repo // OK we have a pointer that is associated with the head repo
// and is actually a file in the LFS // and is actually a file in the LFS
// Therefore it should be associated with the base repo // Therefore it should be associated with the base repo
meta := &git_model.LFSMetaObject{Pointer: pointer} if _, err := git_model.NewLFSMetaObject(ctx, pr.BaseRepoID, pointer); err != nil {
meta.RepositoryID = pr.BaseRepoID
if _, err := git_model.NewLFSMetaObject(ctx, meta); err != nil {
_ = catFileBatchReader.CloseWithError(err) _ = catFileBatchReader.CloseWithError(err)
break break
} }

View File

@ -438,7 +438,7 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
if lfsMetaObject != nil { if lfsMetaObject != nil {
// We have an LFS object - create it // We have an LFS object - create it
lfsMetaObject, err = git_model.NewLFSMetaObject(ctx, lfsMetaObject) lfsMetaObject, err = git_model.NewLFSMetaObject(ctx, lfsMetaObject.RepositoryID, lfsMetaObject.Pointer)
if err != nil { if err != nil {
return err return err
} }

View File

@ -143,7 +143,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
if infos[i].lfsMetaObject == nil { if infos[i].lfsMetaObject == nil {
continue continue
} }
infos[i].lfsMetaObject, err = git_model.NewLFSMetaObject(ctx, infos[i].lfsMetaObject) infos[i].lfsMetaObject, err = git_model.NewLFSMetaObject(ctx, infos[i].lfsMetaObject.RepositoryID, infos[i].lfsMetaObject.Pointer)
if err != nil { if err != nil {
// OK Now we need to cleanup // OK Now we need to cleanup
return cleanUpAfterFailure(ctx, &infos, t, err) return cleanUpAfterFailure(ctx, &infos, t, err)

View File

@ -52,7 +52,7 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
pointer, err := lfs.GeneratePointer(bytes.NewReader(*content)) pointer, err := lfs.GeneratePointer(bytes.NewReader(*content))
assert.NoError(t, err) assert.NoError(t, err)
_, err = git_model.NewLFSMetaObject(db.DefaultContext, &git_model.LFSMetaObject{Pointer: pointer, RepositoryID: repositoryID}) _, err = git_model.NewLFSMetaObject(db.DefaultContext, repositoryID, pointer)
assert.NoError(t, err) assert.NoError(t, err)
contentStore := lfs.NewContentStore() contentStore := lfs.NewContentStore()
exist, err := contentStore.Exists(pointer) exist, err := contentStore.Exists(pointer)

View File

@ -29,7 +29,7 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
pointer, err := lfs.GeneratePointer(bytes.NewReader(*content)) pointer, err := lfs.GeneratePointer(bytes.NewReader(*content))
assert.NoError(t, err) assert.NoError(t, err)
_, err = git_model.NewLFSMetaObject(db.DefaultContext, &git_model.LFSMetaObject{Pointer: pointer, RepositoryID: repositoryID}) _, err = git_model.NewLFSMetaObject(db.DefaultContext, repositoryID, pointer)
assert.NoError(t, err) assert.NoError(t, err)
contentStore := lfs.NewContentStore() contentStore := lfs.NewContentStore()
exist, err := contentStore.Exists(pointer) exist, err := contentStore.Exists(pointer)