mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-29 09:31:11 -05:00
6844258c67
Fix #31707. Also related to #31715. Some Actions resources could has different types of ownership. It could be: - global: all repos and orgs/users can use it. - org/user level: only the org/user can use it. - repo level: only the repo can use it. There are two ways to distinguish org/user level from repo level: 1. `{owner_id: 1, repo_id: 2}` for repo level, and `{owner_id: 1, repo_id: 0}` for org level. 2. `{owner_id: 0, repo_id: 2}` for repo level, and `{owner_id: 1, repo_id: 0}` for org level. The first way seems more reasonable, but it may not be true. The point is that although a resource, like a runner, belongs to a repo (it can be used by the repo), the runner doesn't belong to the repo's org (other repos in the same org cannot use the runner). So, the second method makes more sense. And the first way is not user-friendly to query, we must set the repo id to zero to avoid wrong results. So, #31715 should be right. And the most simple way to fix #31707 is just: ```diff - shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) + shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID) ``` However, it is quite intuitive to set both owner id and repo id since the repo belongs to the owner. So I prefer to be compatible with it. If we get both owner id and repo id not zero when creating or finding, it's very clear that the caller want one with repo level, but set owner id accidentally. So it's OK to accept it but fix the owner id to zero. (cherry picked from commit a33e74d40d356e8f628ac06a131cb203a3609dec)
120 lines
3.9 KiB
Go
120 lines
3.9 KiB
Go
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
"code.gitea.io/gitea/modules/util"
|
|
)
|
|
|
|
// ActionRunnerToken represents runner tokens
|
|
//
|
|
// It can be:
|
|
// 1. global token, OwnerID is 0 and RepoID is 0
|
|
// 2. org/user level token, OwnerID is org/user ID and RepoID is 0
|
|
// 3. repo level token, OwnerID is 0 and RepoID is repo ID
|
|
//
|
|
// Please note that it's not acceptable to have both OwnerID and RepoID to be non-zero,
|
|
// or it will be complicated to find tokens belonging to a specific owner.
|
|
// For example, conditions like `OwnerID = 1` will also return token {OwnerID: 1, RepoID: 1},
|
|
// but it's a repo level token, not an org/user level token.
|
|
// To avoid this, make it clear with {OwnerID: 0, RepoID: 1} for repo level tokens.
|
|
type ActionRunnerToken struct {
|
|
ID int64
|
|
Token string `xorm:"UNIQUE"`
|
|
OwnerID int64 `xorm:"index"`
|
|
Owner *user_model.User `xorm:"-"`
|
|
RepoID int64 `xorm:"index"`
|
|
Repo *repo_model.Repository `xorm:"-"`
|
|
IsActive bool // true means it can be used
|
|
|
|
Created timeutil.TimeStamp `xorm:"created"`
|
|
Updated timeutil.TimeStamp `xorm:"updated"`
|
|
Deleted timeutil.TimeStamp `xorm:"deleted"`
|
|
}
|
|
|
|
func init() {
|
|
db.RegisterModel(new(ActionRunnerToken))
|
|
}
|
|
|
|
// GetRunnerToken returns a action runner via token
|
|
func GetRunnerToken(ctx context.Context, token string) (*ActionRunnerToken, error) {
|
|
var runnerToken ActionRunnerToken
|
|
has, err := db.GetEngine(ctx).Where("token=?", token).Get(&runnerToken)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !has {
|
|
return nil, fmt.Errorf("runner token %q: %w", token, util.ErrNotExist)
|
|
}
|
|
return &runnerToken, nil
|
|
}
|
|
|
|
// UpdateRunnerToken updates runner token information.
|
|
func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string) (err error) {
|
|
e := db.GetEngine(ctx)
|
|
|
|
if len(cols) == 0 {
|
|
_, err = e.ID(r.ID).AllCols().Update(r)
|
|
} else {
|
|
_, err = e.ID(r.ID).Cols(cols...).Update(r)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// NewRunnerToken creates a new active runner token and invalidate all old tokens
|
|
// ownerID will be ignored and treated as 0 if repoID is non-zero.
|
|
func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
|
if ownerID != 0 && repoID != 0 {
|
|
// It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
|
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
|
ownerID = 0
|
|
}
|
|
|
|
token, err := util.CryptoRandomString(40)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
runnerToken := &ActionRunnerToken{
|
|
OwnerID: ownerID,
|
|
RepoID: repoID,
|
|
IsActive: true,
|
|
Token: token,
|
|
}
|
|
|
|
return runnerToken, db.WithTx(ctx, func(ctx context.Context) error {
|
|
if _, err := db.GetEngine(ctx).Where("owner_id =? AND repo_id = ?", ownerID, repoID).Cols("is_active").Update(&ActionRunnerToken{
|
|
IsActive: false,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = db.GetEngine(ctx).Insert(runnerToken)
|
|
return err
|
|
})
|
|
}
|
|
|
|
// GetLatestRunnerToken returns the latest runner token
|
|
func GetLatestRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
|
if ownerID != 0 && repoID != 0 {
|
|
// It's trying to get a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
|
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
|
ownerID = 0
|
|
}
|
|
|
|
var runnerToken ActionRunnerToken
|
|
has, err := db.GetEngine(ctx).Where("owner_id=? AND repo_id=?", ownerID, repoID).
|
|
OrderBy("id DESC").Get(&runnerToken)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !has {
|
|
return nil, fmt.Errorf("runner token: %w", util.ErrNotExist)
|
|
}
|
|
return &runnerToken, nil
|
|
}
|