mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'main' into lunny/issue_dev
This commit is contained in:
+26
-1
@@ -25,7 +25,12 @@ var (
|
||||
)
|
||||
|
||||
// BasicMethodName is the constant name of the basic authentication method
|
||||
const BasicMethodName = "basic"
|
||||
const (
|
||||
BasicMethodName = "basic"
|
||||
AccessTokenMethodName = "access_token"
|
||||
OAuth2TokenMethodName = "oauth2_token"
|
||||
ActionTokenMethodName = "action_token"
|
||||
)
|
||||
|
||||
// Basic implements the Auth interface and authenticates requests (API requests
|
||||
// only) by looking for Basic authentication data or "x-oauth-basic" token in the "Authorization"
|
||||
@@ -82,6 +87,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store.GetData()["LoginMethod"] = OAuth2TokenMethodName
|
||||
store.GetData()["IsApiToken"] = true
|
||||
return u, nil
|
||||
}
|
||||
@@ -101,6 +107,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||
log.Error("UpdateAccessToken: %v", err)
|
||||
}
|
||||
|
||||
store.GetData()["LoginMethod"] = AccessTokenMethodName
|
||||
store.GetData()["IsApiToken"] = true
|
||||
store.GetData()["ApiTokenScope"] = token.Scope
|
||||
return u, nil
|
||||
@@ -113,6 +120,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||
if err == nil && task != nil {
|
||||
log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
|
||||
|
||||
store.GetData()["LoginMethod"] = ActionTokenMethodName
|
||||
store.GetData()["IsActionsToken"] = true
|
||||
store.GetData()["ActionsTaskID"] = task.ID
|
||||
|
||||
@@ -138,6 +146,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||
}
|
||||
}
|
||||
|
||||
store.GetData()["LoginMethod"] = BasicMethodName
|
||||
log.Trace("Basic Authorization: Logged in user %-v", u)
|
||||
|
||||
return u, nil
|
||||
@@ -159,3 +168,19 @@ func validateTOTP(req *http.Request, u *user_model.User) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAccessScope(store DataStore) auth_model.AccessTokenScope {
|
||||
if v, ok := store.GetData()["ApiTokenScope"]; ok {
|
||||
return v.(auth_model.AccessTokenScope)
|
||||
}
|
||||
switch store.GetData()["LoginMethod"] {
|
||||
case OAuth2TokenMethodName:
|
||||
fallthrough
|
||||
case BasicMethodName, AccessTokenMethodName:
|
||||
return auth_model.AccessTokenScopeAll
|
||||
case ActionTokenMethodName:
|
||||
fallthrough
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-fed/httpsig"
|
||||
"github.com/42wim/httpsig"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
@@ -205,7 +205,7 @@ func doVerify(verifier httpsig.Verifier, sshPublicKeys []ssh.PublicKey) error {
|
||||
case strings.HasPrefix(publicKey.Type(), "ssh-ed25519"):
|
||||
algos = []httpsig.Algorithm{httpsig.ED25519}
|
||||
case strings.HasPrefix(publicKey.Type(), "ssh-rsa"):
|
||||
algos = []httpsig.Algorithm{httpsig.RSA_SHA1, httpsig.RSA_SHA256, httpsig.RSA_SHA512}
|
||||
algos = []httpsig.Algorithm{httpsig.RSA_SHA256, httpsig.RSA_SHA512}
|
||||
}
|
||||
for _, algo := range algos {
|
||||
if err := verifier.Verify(cryptoPubkey, algo); err == nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -18,10 +19,14 @@ import (
|
||||
|
||||
type packageClaims struct {
|
||||
jwt.RegisteredClaims
|
||||
PackageMeta
|
||||
}
|
||||
type PackageMeta struct {
|
||||
UserID int64
|
||||
Scope auth_model.AccessTokenScope
|
||||
}
|
||||
|
||||
func CreateAuthorizationToken(u *user_model.User) (string, error) {
|
||||
func CreateAuthorizationToken(u *user_model.User, packageScope auth_model.AccessTokenScope) (string, error) {
|
||||
now := time.Now()
|
||||
|
||||
claims := packageClaims{
|
||||
@@ -29,7 +34,10 @@ func CreateAuthorizationToken(u *user_model.User) (string, error) {
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)),
|
||||
NotBefore: jwt.NewNumericDate(now),
|
||||
},
|
||||
UserID: u.ID,
|
||||
PackageMeta: PackageMeta{
|
||||
UserID: u.ID,
|
||||
Scope: packageScope,
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
@@ -41,32 +49,36 @@ func CreateAuthorizationToken(u *user_model.User) (string, error) {
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
func ParseAuthorizationToken(req *http.Request) (int64, error) {
|
||||
func ParseAuthorizationRequest(req *http.Request) (*PackageMeta, error) {
|
||||
h := req.Header.Get("Authorization")
|
||||
if h == "" {
|
||||
return 0, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
parts := strings.SplitN(h, " ", 2)
|
||||
if len(parts) != 2 {
|
||||
log.Error("split token failed: %s", h)
|
||||
return 0, fmt.Errorf("split token failed")
|
||||
return nil, fmt.Errorf("split token failed")
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (any, error) {
|
||||
return ParseAuthorizationToken(parts[1])
|
||||
}
|
||||
|
||||
func ParseAuthorizationToken(tokenStr string) (*PackageMeta, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenStr, &packageClaims{}, 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.GetGeneralTokenSigningSecret(), nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, ok := token.Claims.(*packageClaims)
|
||||
if !token.Valid || !ok {
|
||||
return 0, fmt.Errorf("invalid token claim")
|
||||
return nil, fmt.Errorf("invalid token claim")
|
||||
}
|
||||
|
||||
return c.UserID, nil
|
||||
return &c.PackageMeta, nil
|
||||
}
|
||||
|
||||
+10
-3
@@ -21,6 +21,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
@@ -334,9 +335,15 @@ func handler(items ...string) []string {
|
||||
}
|
||||
|
||||
func testPR(id int64) {
|
||||
pullWorkingPool.CheckIn(fmt.Sprint(id))
|
||||
defer pullWorkingPool.CheckOut(fmt.Sprint(id))
|
||||
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("Test PR[%d] from patch checking queue", id))
|
||||
ctx := graceful.GetManager().HammerContext()
|
||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(id))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Test PR[%d] from patch checking queue", id))
|
||||
defer finished()
|
||||
|
||||
pr, err := issues_model.GetPullRequestByID(ctx, id)
|
||||
|
||||
+18
-7
@@ -23,6 +23,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
@@ -169,9 +170,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
|
||||
return fmt.Errorf("unable to load head repo: %w", err)
|
||||
}
|
||||
|
||||
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
|
||||
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
|
||||
|
||||
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
|
||||
if err != nil {
|
||||
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
|
||||
@@ -184,11 +182,18 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
|
||||
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
||||
}
|
||||
|
||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return fmt.Errorf("lock.Lock: %w", err)
|
||||
}
|
||||
defer releaser()
|
||||
defer func() {
|
||||
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
|
||||
}()
|
||||
|
||||
_, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message, repo_module.PushTriggerPRMergeToBase)
|
||||
releaser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -487,10 +492,14 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
|
||||
|
||||
// MergedManually mark pr as merged manually
|
||||
func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, commitID string) error {
|
||||
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
|
||||
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
|
||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return fmt.Errorf("lock.Lock: %w", err)
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
err = db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -540,7 +549,9 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
|
||||
return fmt.Errorf("SetMerged failed")
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
})
|
||||
releaser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+10
-5
@@ -26,20 +26,21 @@ import (
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
)
|
||||
|
||||
// TODO: use clustered lock (unique queue? or *abuse* cache)
|
||||
var pullWorkingPool = sync.NewExclusivePool()
|
||||
func getPullWorkingLockKey(prID int64) string {
|
||||
return fmt.Sprintf("pull_working_%d", prID)
|
||||
}
|
||||
|
||||
// NewPullRequest creates new pull request with labels for repository.
|
||||
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *issues_model.Issue, labelIDs []int64, uuids []string, pr *issues_model.PullRequest, assigneeIDs []int64) error {
|
||||
@@ -220,8 +221,12 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
|
||||
|
||||
// ChangeTargetBranch changes the target branch of this pull request, as the given user.
|
||||
func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) {
|
||||
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
|
||||
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
|
||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return fmt.Errorf("lock.Lock: %w", err)
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
// Current target branch is already the same
|
||||
if pr.BaseBranch == targetBranch {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
)
|
||||
@@ -25,8 +26,12 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
|
||||
return fmt.Errorf("update of agit flow pull request's head branch is unsupported")
|
||||
}
|
||||
|
||||
pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
|
||||
defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
|
||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return fmt.Errorf("lock.Lock: %w", err)
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
diffCount, err := GetDiverging(ctx, pr)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,16 +18,16 @@ import (
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
)
|
||||
|
||||
// repoWorkingPool represents a working pool to order the parallel changes to the same repository
|
||||
// TODO: use clustered lock (unique queue? or *abuse* cache)
|
||||
var repoWorkingPool = sync.NewExclusivePool()
|
||||
func getRepoWorkingLockKey(repoID int64) string {
|
||||
return fmt.Sprintf("repo_working_%d", repoID)
|
||||
}
|
||||
|
||||
// TransferOwnership transfers all corresponding setting from old user to new one.
|
||||
func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
|
||||
@@ -42,12 +42,17 @@ func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, rep
|
||||
|
||||
oldOwner := repo.Owner
|
||||
|
||||
repoWorkingPool.CheckIn(fmt.Sprint(repo.ID))
|
||||
releaser, err := globallock.Lock(ctx, getRepoWorkingLockKey(repo.ID))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return fmt.Errorf("lock.Lock: %w", err)
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
if err := transferOwnership(ctx, doer, newOwner.Name, repo); err != nil {
|
||||
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||
return err
|
||||
}
|
||||
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||
releaser()
|
||||
|
||||
newRepo, err := repo_model.GetRepositoryByID(ctx, repo.ID)
|
||||
if err != nil {
|
||||
@@ -360,15 +365,20 @@ func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo
|
||||
oldRepoName := repo.Name
|
||||
|
||||
// Change repository directory name. We must lock the local copy of the
|
||||
// repo so that we can atomically rename the repo path and updates the
|
||||
// repo so that we can automatically rename the repo path and updates the
|
||||
// local copy's origin accordingly.
|
||||
|
||||
repoWorkingPool.CheckIn(fmt.Sprint(repo.ID))
|
||||
releaser, err := globallock.Lock(ctx, getRepoWorkingLockKey(repo.ID))
|
||||
if err != nil {
|
||||
log.Error("lock.Lock(): %v", err)
|
||||
return fmt.Errorf("lock.Lock: %w", err)
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
if err := changeRepositoryName(ctx, repo, newRepoName); err != nil {
|
||||
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||
return err
|
||||
}
|
||||
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||
releaser()
|
||||
|
||||
repo.Name = newRepoName
|
||||
notify_service.RenameRepository(ctx, doer, repo, oldRepoName)
|
||||
|
||||
+15
-8
@@ -18,19 +18,20 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
||||
// TODO: use clustered lock (unique queue? or *abuse* cache)
|
||||
var wikiWorkingPool = sync.NewExclusivePool()
|
||||
|
||||
const DefaultRemote = "origin"
|
||||
|
||||
func getWikiWorkingLockKey(repoID int64) string {
|
||||
return fmt.Sprintf("wiki_working_%d", repoID)
|
||||
}
|
||||
|
||||
// InitWiki initializes a wiki for repository,
|
||||
// it does nothing when repository already has wiki.
|
||||
func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
|
||||
@@ -89,8 +90,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
||||
if err = validateWebPath(newWikiName); err != nil {
|
||||
return err
|
||||
}
|
||||
wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID))
|
||||
defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||
releaser, err := globallock.Lock(ctx, getWikiWorkingLockKey(repo.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
if err = InitWiki(ctx, repo); err != nil {
|
||||
return fmt.Errorf("InitWiki: %w", err)
|
||||
@@ -250,8 +254,11 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
||||
return err
|
||||
}
|
||||
|
||||
wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID))
|
||||
defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||
releaser, err := globallock.Lock(ctx, getWikiWorkingLockKey(repo.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer releaser()
|
||||
|
||||
if err = InitWiki(ctx, repo); err != nil {
|
||||
return fmt.Errorf("InitWiki: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user