Merge branch 'main' into pacman-packages

This commit is contained in:
silverwind 2023-07-09 14:27:52 +02:00 committed by GitHub
commit 2685f1a307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 383 additions and 208 deletions

View File

@ -77,16 +77,21 @@ linters-settings:
extra-rules: true
lang-version: "1.20"
depguard:
list-type: denylist
# Check the list against standard lib.
include-go-root: true
packages-with-error-message:
- encoding/json: "use gitea's modules/json instead of encoding/json"
- github.com/unknwon/com: "use gitea's util and replacements"
- io/ioutil: "use os or io instead"
- golang.org/x/exp: "it's experimental and unreliable."
- code.gitea.io/gitea/modules/git/internal: "do not use the internal package, use AddXxx function instead"
- gopkg.in/ini.v1: "do not use the ini package, use gitea's config system instead"
rules:
main:
deny:
- pkg: encoding/json
desc: use gitea's modules/json instead of encoding/json
- pkg: github.com/unknwon/com
desc: use gitea's util and replacements
- pkg: io/ioutil
desc: use os or io instead
- pkg: golang.org/x/exp
desc: it's experimental and unreliable
- pkg: code.gitea.io/gitea/modules/git/internal
desc: do not use the internal package, use AddXxx function instead
- pkg: gopkg.in/ini.v1
desc: do not use the ini package, use gitea's config system instead
issues:
max-issues-per-linter: 0

View File

@ -25,17 +25,17 @@ COMMA := ,
XGO_VERSION := go-1.20.x
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.43.0
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.44.0
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.5.0
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.3
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.4
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@latest
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@latest
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v0.2.0
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.25
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest

View File

@ -343,7 +343,7 @@ func getIssueNotification(ctx context.Context, userID, issueID int64) (*Notifica
// NotificationsForUser returns notifications for a given user and status
func NotificationsForUser(ctx context.Context, user *user_model.User, statuses []NotificationStatus, page, perPage int) (notifications NotificationList, err error) {
if len(statuses) == 0 {
return
return nil, nil
}
sess := db.GetEngine(ctx).
@ -372,16 +372,16 @@ func CountUnread(ctx context.Context, userID int64) int64 {
// LoadAttributes load Repo Issue User and Comment if not loaded
func (n *Notification) LoadAttributes(ctx context.Context) (err error) {
if err = n.loadRepo(ctx); err != nil {
return
return err
}
if err = n.loadIssue(ctx); err != nil {
return
return err
}
if err = n.loadUser(ctx); err != nil {
return
return err
}
if err = n.loadComment(ctx); err != nil {
return
return err
}
return err
}

View File

@ -111,7 +111,7 @@ func populateHash(hashFunc crypto.Hash, msg []byte) (hash.Hash, error) {
func readArmoredSign(r io.Reader) (body io.Reader, err error) {
block, err := armor.Decode(r)
if err != nil {
return
return nil, err
}
if block.Type != openpgp.SignatureType {
return nil, fmt.Errorf("expected '" + openpgp.SignatureType + "', got: " + block.Type)

View File

@ -749,7 +749,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
err = json.Unmarshal([]byte(c.Content), &data)
if err != nil {
return
return err
}
c.IsForcePush = data.IsForcePush
@ -925,7 +925,7 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is
cType = CommentTypeRemoveDependency
}
if err = issue.LoadRepo(ctx); err != nil {
return
return err
}
// Make two comments, one in each issue
@ -937,7 +937,7 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is
DependentIssueID: dependentIssue.ID,
}
if _, err = CreateComment(ctx, opts); err != nil {
return
return err
}
opts = &CreateCommentOptions{
@ -1170,11 +1170,11 @@ func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullReques
return nil, fmt.Errorf("comment type %d cannot be used to create an auto merge comment", typ)
}
if err = pr.LoadIssue(ctx); err != nil {
return
return nil, err
}
if err = pr.LoadBaseRepo(ctx); err != nil {
return
return nil, err
}
comment, err = CreateComment(ctx, &CreateCommentOptions{

View File

@ -468,42 +468,38 @@ func (comments CommentList) loadReviews(ctx context.Context) error {
// loadAttributes loads all attributes
func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
if err = comments.LoadPosters(ctx); err != nil {
return
return err
}
if err = comments.loadLabels(ctx); err != nil {
return
return err
}
if err = comments.loadMilestones(ctx); err != nil {
return
return err
}
if err = comments.loadOldMilestones(ctx); err != nil {
return
return err
}
if err = comments.loadAssignees(ctx); err != nil {
return
return err
}
if err = comments.LoadAttachments(ctx); err != nil {
return
return err
}
if err = comments.loadReviews(ctx); err != nil {
return
return err
}
if err = comments.LoadIssues(ctx); err != nil {
return
return err
}
if err = comments.loadDependentIssues(ctx); err != nil {
return
}
return nil
return comments.loadDependentIssues(ctx)
}
// LoadAttributes loads attributes of the comments, except for attachments and

View File

@ -222,8 +222,7 @@ func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
if !user_model.IsErrUserNotExist(err) {
return fmt.Errorf("getUserByID.(poster) [%d]: %w", issue.PosterID, err)
}
err = nil
return
return nil
}
}
return err
@ -316,27 +315,27 @@ func (issue *Issue) LoadMilestone(ctx context.Context) (err error) {
// LoadAttributes loads the attribute of this issue.
func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
if err = issue.LoadRepo(ctx); err != nil {
return
return err
}
if err = issue.LoadPoster(ctx); err != nil {
return
return err
}
if err = issue.LoadLabels(ctx); err != nil {
return
return err
}
if err = issue.LoadMilestone(ctx); err != nil {
return
return err
}
if err = issue.LoadProject(ctx); err != nil {
return
return err
}
if err = issue.LoadAssignees(ctx); err != nil {
return
return err
}
if err = issue.LoadPullRequest(ctx); err != nil && !IsErrPullRequestNotExist(err) {

View File

@ -39,7 +39,7 @@ func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m
}
if err = issue.LoadRepo(ctx); err != nil {
return
return err
}
opts := &CreateCommentOptions{
@ -168,7 +168,7 @@ func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use
}
if err = issue.LoadRepo(ctx); err != nil {
return
return err
}
opts := &CreateCommentOptions{

View File

@ -538,10 +538,10 @@ func FindAndUpdateIssueMentions(ctx context.Context, issue *Issue, doer *user_mo
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *user_model.User, mentions []string) (users []*user_model.User, err error) {
if len(mentions) == 0 {
return
return nil, nil
}
if err = issue.LoadRepo(ctx); err != nil {
return
return nil, err
}
resolved := make(map[string]bool, 10)
@ -635,7 +635,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
}
}
if len(mentionUsers) == 0 {
return
return users, err
}
if users == nil {
@ -702,66 +702,66 @@ func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []
// Delete content histories
if _, err = sess.In("issue_id", deleteCond).
Delete(&ContentHistory{}); err != nil {
return
return nil, err
}
// Delete comments and attachments
if _, err = sess.In("issue_id", deleteCond).
Delete(&Comment{}); err != nil {
return
return nil, err
}
// Dependencies for issues in this repository
if _, err = sess.In("issue_id", deleteCond).
Delete(&IssueDependency{}); err != nil {
return
return nil, err
}
// Delete dependencies for issues in other repositories
if _, err = sess.In("dependency_id", deleteCond).
Delete(&IssueDependency{}); err != nil {
return
return nil, err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&IssueUser{}); err != nil {
return
return nil, err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&Reaction{}); err != nil {
return
return nil, err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&IssueWatch{}); err != nil {
return
return nil, err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&Stopwatch{}); err != nil {
return
return nil, err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&TrackedTime{}); err != nil {
return
return nil, err
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&project_model.ProjectIssue{}); err != nil {
return
return nil, err
}
if _, err = sess.In("dependent_issue_id", deleteCond).
Delete(&Comment{}); err != nil {
return
return nil, err
}
var attachments []*repo_model.Attachment
if err = sess.In("issue_id", deleteCond).
Find(&attachments); err != nil {
return
return nil, err
}
for j := range attachments {
@ -770,11 +770,11 @@ func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []
if _, err = sess.In("issue_id", deleteCond).
Delete(&repo_model.Attachment{}); err != nil {
return
return nil, err
}
if _, err = db.DeleteByBean(ctx, &Issue{RepoID: repoID}); err != nil {
return
return nil, err
}
return attachmentPaths, err

View File

@ -136,10 +136,10 @@ func init() {
// LoadCodeComments loads CodeComments
func (r *Review) LoadCodeComments(ctx context.Context) (err error) {
if r.CodeComments != nil {
return
return err
}
if err = r.loadIssue(ctx); err != nil {
return
return err
}
r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false)
return err
@ -147,7 +147,7 @@ func (r *Review) LoadCodeComments(ctx context.Context) (err error) {
func (r *Review) loadIssue(ctx context.Context) (err error) {
if r.Issue != nil {
return
return err
}
r.Issue, err = GetIssueByID(ctx, r.IssueID)
return err
@ -156,7 +156,7 @@ func (r *Review) loadIssue(ctx context.Context) (err error) {
// LoadReviewer loads reviewer
func (r *Review) LoadReviewer(ctx context.Context) (err error) {
if r.ReviewerID == 0 || r.Reviewer != nil {
return
return err
}
r.Reviewer, err = user_model.GetPossibleUserByID(ctx, r.ReviewerID)
return err
@ -186,7 +186,7 @@ func LoadReviewers(ctx context.Context, reviews []*Review) (err error) {
// LoadReviewerTeam loads reviewer team
func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) {
if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil {
return
return nil
}
r.ReviewerTeam, err = organization.GetTeamByID(ctx, r.ReviewerTeamID)
@ -196,16 +196,16 @@ func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) {
// LoadAttributes loads all attributes except CodeComments
func (r *Review) LoadAttributes(ctx context.Context) (err error) {
if err = r.loadIssue(ctx); err != nil {
return
return err
}
if err = r.LoadCodeComments(ctx); err != nil {
return
return err
}
if err = r.LoadReviewer(ctx); err != nil {
return
return err
}
if err = r.LoadReviewerTeam(ctx); err != nil {
return
return err
}
return err
}

View File

@ -302,7 +302,7 @@ func DeleteTime(t *TrackedTime) error {
func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) {
removedTime, err = GetTrackedSeconds(ctx, opts)
if err != nil || removedTime == 0 {
return
return removedTime, err
}
_, err = opts.toSession(db.GetEngine(ctx)).Table("tracked_time").Cols("deleted").Update(&TrackedTime{Deleted: true})

View File

@ -78,14 +78,14 @@ func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) {
for start := 0; ; start += batchSize {
users := make([]*User, 0, batchSize)
if err = sess.Limit(batchSize, start).Where(builder.Neq{"passwd": ""}, 0).Find(&users); err != nil {
return
return err
}
if len(users) == 0 {
break
}
if err = sess.Begin(); err != nil {
return
return err
}
for _, user := range users {
@ -100,7 +100,7 @@ func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) {
}
if err = sess.Commit(); err != nil {
return
return err
}
}

View File

@ -5,11 +5,18 @@ package packages
import (
"context"
"strconv"
"time"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
// ErrPackageBlobNotExist indicates a package blob not exist error
@ -98,3 +105,42 @@ func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) {
Where("package_file.id IS NULL").
SumInt(&PackageBlob{}, "size")
}
// IsBlobAccessibleForUser tests if the user has access to the blob
func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model.User) (bool, error) {
if user.IsAdmin {
return true, nil
}
maxTeamAuthorize := builder.
Select("max(team.authorize)").
From("team").
InnerJoin("team_user", "team_user.team_id = team.id").
Where(builder.Eq{"team_user.uid": user.ID}.And(builder.Expr("team_user.org_id = `user`.id")))
maxTeamUnitAccessMode := builder.
Select("max(team_unit.access_mode)").
From("team").
InnerJoin("team_user", "team_user.team_id = team.id").
InnerJoin("team_unit", "team_unit.team_id = team.id").
Where(builder.Eq{"team_user.uid": user.ID, "team_unit.type": unit.TypePackages}.And(builder.Expr("team_user.org_id = `user`.id")))
cond := builder.Eq{"package_blob.id": blobID}.And(
// owner = user
builder.Eq{"`user`.id": user.ID}.
// user can see owner
Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})).
// owner is an organization and user has access to it
Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}.
And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))),
)
return db.GetEngine(ctx).
Table("package_blob").
Join("INNER", "package_file", "package_file.blob_id = package_blob.id").
Join("INNER", "package_version", "package_version.id = package_file.version_id").
Join("INNER", "package", "package.id = package_version.package_id").
Join("INNER", "user", "`user`.id = package.owner_id").
Where(cond).
Exist(&PackageBlob{})
}

View File

@ -628,14 +628,14 @@ func DoctorUserStarNum() (err error) {
for start := 0; ; start += batchSize {
users := make([]user_model.User, 0, batchSize)
if err = db.GetEngine(db.DefaultContext).Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil {
return
return err
}
if len(users) == 0 {
break
}
if err = updateUserStarNumbers(users); err != nil {
return
return err
}
}

View File

@ -63,19 +63,19 @@ type Client struct {
// NewClient function
func NewClient(user *user_model.User, pubID string) (c *Client, err error) {
if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil {
return
return nil, err
} else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil {
return
return nil, err
}
priv, err := GetPrivateKey(user)
if err != nil {
return
return nil, err
}
privPem, _ := pem.Decode([]byte(priv))
privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
if err != nil {
return
return nil, err
}
c = &Client{
@ -99,14 +99,14 @@ func (c *Client) NewRequest(b []byte, to string) (req *http.Request, err error)
buf := bytes.NewBuffer(b)
req, err = http.NewRequest(http.MethodPost, to, buf)
if err != nil {
return
return nil, err
}
req.Header.Add("Content-Type", ActivityStreamsContentType)
req.Header.Add("Date", CurrentTime())
req.Header.Add("User-Agent", "Gitea/"+setting.AppVer)
signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime)
if err != nil {
return
return nil, err
}
err = signer.SignRequest(c.priv, c.pubID, req, b)
return req, err
@ -116,7 +116,7 @@ func (c *Client) NewRequest(b []byte, to string) (req *http.Request, err error)
func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
var req *http.Request
if req, err = c.NewRequest(b, to); err != nil {
return
return nil, err
}
resp, err = c.client.Do(req)
return resp, err

View File

@ -15,22 +15,22 @@ func GetKeyPair(user *user_model.User) (pub, priv string, err error) {
var settings map[string]*user_model.Setting
settings, err = user_model.GetSettings(user.ID, []string{user_model.UserActivityPubPrivPem, user_model.UserActivityPubPubPem})
if err != nil {
return
return pub, priv, err
} else if len(settings) == 0 {
if priv, pub, err = util.GenerateKeyPair(rsaBits); err != nil {
return
return pub, priv, err
}
if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPrivPem, priv); err != nil {
return
return pub, priv, err
}
if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPubPem, pub); err != nil {
return
return pub, priv, err
}
return
return pub, priv, err
} else {
priv = settings[user_model.UserActivityPubPrivPem].SettingValue
pub = settings[user_model.UserActivityPubPubPem].SettingValue
return
return pub, priv, err
}
}

View File

@ -294,7 +294,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context
return func(ctx *APIContext) (cancel context.CancelFunc) {
// Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) {
return
return nil
}
// For API calls.
@ -303,7 +303,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context
gitRepo, err := git.OpenRepository(ctx, repoPath)
if err != nil {
ctx.Error(http.StatusInternalServerError, "RepoRef Invalid repo "+repoPath, err)
return
return cancel
}
ctx.Repo.GitRepo = gitRepo
// We opened it, we should close it

View File

@ -148,27 +148,25 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
typ, err = rd.ReadString('\n')
if err != nil {
return
return sha, typ, size, err
}
if len(typ) == 1 {
typ, err = rd.ReadString('\n')
if err != nil {
return
return sha, typ, size, err
}
}
idx := strings.IndexByte(typ, ' ')
if idx < 0 {
log.Debug("missing space typ: %s", typ)
err = ErrNotExist{ID: string(sha)}
return
return sha, typ, size, ErrNotExist{ID: string(sha)}
}
sha = []byte(typ[:idx])
typ = typ[idx+1:]
idx = strings.IndexByte(typ, ' ')
if idx < 0 {
err = ErrNotExist{ID: string(sha)}
return
return sha, typ, size, ErrNotExist{ID: string(sha)}
}
sizeStr := typ[idx+1 : len(typ)-1]
@ -285,14 +283,12 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
// Read the Mode & fname
readBytes, err = rd.ReadSlice('\x00')
if err != nil {
return
return mode, fname, sha, n, err
}
idx := bytes.IndexByte(readBytes, ' ')
if idx < 0 {
log.Debug("missing space in readBytes ParseTreeLine: %s", readBytes)
err = &ErrNotExist{}
return
return mode, fname, sha, n, &ErrNotExist{}
}
n += idx + 1
@ -319,7 +315,7 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
}
n += len(fnameBuf)
if err != nil {
return
return mode, fname, sha, n, err
}
fnameBuf = fnameBuf[:len(fnameBuf)-1]
fname = fnameBuf
@ -331,7 +327,7 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
read, err = rd.Read(shaBuf[idx:20])
n += read
if err != nil {
return
return mode, fname, sha, n, err
}
idx += read
}

View File

@ -435,7 +435,7 @@ func (c *Commit) GetBranchName() (string, error) {
// LoadBranchName load branch name for commit
func (c *Commit) LoadBranchName() (err error) {
if len(c.Branch) != 0 {
return
return nil
}
c.Branch, err = c.GetBranchName()

View File

@ -171,7 +171,7 @@ func InitFull(ctx context.Context) (err error) {
}
if err = InitSimple(ctx); err != nil {
return
return err
}
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands

View File

@ -87,7 +87,7 @@ func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError
// Close this repository, in particular close the underlying gogitStorage if this is not nil
func (repo *Repository) Close() (err error) {
if repo == nil {
return
return nil
}
if repo.batchCancel != nil {
repo.batchCancel()

View File

@ -48,7 +48,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error
func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (filename, tmpDir string, cancel context.CancelFunc, err error) {
tmpDir, err = os.MkdirTemp("", "index")
if err != nil {
return
return filename, tmpDir, cancel, err
}
filename = filepath.Join(tmpDir, ".tmp-index")

View File

@ -43,12 +43,13 @@ func (s *Signature) Decode(b []byte) {
//
// but without the "author " at the beginning (this method should)
// be used for author and committer.
// FIXME: there are a lot of "return sig, err" (but the err is also nil), that's the old behavior, to avoid breaking
func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
sig = new(Signature)
emailStart := bytes.LastIndexByte(line, '<')
emailEnd := bytes.LastIndexByte(line, '>')
if emailStart == -1 || emailEnd == -1 || emailEnd < emailStart {
return
return sig, err
}
if emailStart > 0 { // Empty name has already occurred, even if it shouldn't
@ -58,7 +59,7 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
hasTime := emailEnd+2 < len(line)
if !hasTime {
return
return sig, err
}
// Check date format.
@ -66,7 +67,7 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
if firstChar >= 48 && firstChar <= 57 {
idx := bytes.IndexByte(line[emailEnd+2:], ' ')
if idx < 0 {
return
return sig, err
}
timestring := string(line[emailEnd+2 : emailEnd+2+idx])
@ -75,14 +76,14 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
idx += emailEnd + 3
if idx >= len(line) || idx+5 > len(line) {
return
return sig, err
}
timezone := string(line[idx : idx+5])
tzhours, err1 := strconv.ParseInt(timezone[0:3], 10, 64)
tzmins, err2 := strconv.ParseInt(timezone[3:], 10, 64)
if err1 != nil || err2 != nil {
return
return sig, err
}
if tzhours < 0 {
tzmins *= -1
@ -92,7 +93,7 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
} else {
sig.When, err = time.Parse(GitTimeLayout, string(line[emailEnd+2:]))
if err != nil {
return
return sig, err
}
}
return sig, err

View File

@ -71,7 +71,7 @@ func valToTimeDuration(vs []string) (result time.Duration) {
result = time.Duration(val)
}
if err == nil {
return
return result
}
}
return result

View File

@ -93,7 +93,7 @@ func (q *WorkerPoolQueue[T]) GetQueueItemNumber() int {
func (q *WorkerPoolQueue[T]) FlushWithContext(ctx context.Context, timeout time.Duration) (err error) {
if q.isBaseQueueDummy() {
return
return nil
}
log.Debug("Try to flush queue %q with timeout %v", q.GetName(), timeout)

View File

@ -50,7 +50,9 @@ func NewRoute() *Route {
// Use supports two middlewares
func (r *Route) Use(middlewares ...any) {
for _, m := range middlewares {
r.R.Use(toHandlerProvider(m))
if m != nil {
r.R.Use(toHandlerProvider(m))
}
}
}
@ -79,15 +81,23 @@ func (r *Route) getPattern(pattern string) string {
}
func (r *Route) wrapMiddlewareAndHandler(h []any) ([]func(http.Handler) http.Handler, http.HandlerFunc) {
handlerProviders := make([]func(http.Handler) http.Handler, 0, len(r.curMiddlewares)+len(h))
handlerProviders := make([]func(http.Handler) http.Handler, 0, len(r.curMiddlewares)+len(h)+1)
for _, m := range r.curMiddlewares {
handlerProviders = append(handlerProviders, toHandlerProvider(m))
if m != nil {
handlerProviders = append(handlerProviders, toHandlerProvider(m))
}
}
for _, m := range h {
handlerProviders = append(handlerProviders, toHandlerProvider(m))
if h != nil {
handlerProviders = append(handlerProviders, toHandlerProvider(m))
}
}
middlewares := handlerProviders[:len(handlerProviders)-1]
handlerFunc := handlerProviders[len(handlerProviders)-1](nil).ServeHTTP
mockPoint := RouteMockPoint(MockAfterMiddlewares)
if mockPoint != nil {
middlewares = append(middlewares, mockPoint)
}
return middlewares, handlerFunc
}

61
modules/web/routemock.go Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package web
import (
"net/http"
"code.gitea.io/gitea/modules/setting"
)
// MockAfterMiddlewares is a general mock point, it's between middlewares and the handler
const MockAfterMiddlewares = "MockAfterMiddlewares"
var routeMockPoints = map[string]func(next http.Handler) http.Handler{}
// RouteMockPoint registers a mock point as a middleware for testing, example:
//
// r.Use(web.RouteMockPoint("my-mock-point-1"))
// r.Get("/foo", middleware2, web.RouteMockPoint("my-mock-point-2"), middleware2, handler)
//
// Then use web.RouteMock to mock the route execution.
// It only takes effect in testing mode (setting.IsInTesting == true).
func RouteMockPoint(pointName string) func(next http.Handler) http.Handler {
if !setting.IsInTesting {
return nil
}
routeMockPoints[pointName] = nil
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if h := routeMockPoints[pointName]; h != nil {
h(next).ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
}
// RouteMock uses the registered mock point to mock the route execution, example:
//
// defer web.RouteMockReset()
// web.RouteMock(web.MockAfterMiddlewares, func(ctx *context.Context) {
// ctx.WriteResponse(...)
// }
//
// Then the mock function will be executed as a middleware at the mock point.
// It only takes effect in testing mode (setting.IsInTesting == true).
func RouteMock(pointName string, h any) {
if _, ok := routeMockPoints[pointName]; !ok {
panic("route mock point not found: " + pointName)
}
routeMockPoints[pointName] = toHandlerProvider(h)
}
// RouteMockReset resets all mock points (no mock anymore)
func RouteMockReset() {
for k := range routeMockPoints {
routeMockPoints[k] = nil // keep the keys because RouteMock will check the keys to make sure no misspelling
}
}

View File

@ -0,0 +1,70 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package web
import (
"net/http"
"net/http/httptest"
"testing"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func TestRouteMock(t *testing.T) {
setting.IsInTesting = true
r := NewRoute()
middleware1 := func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Set("X-Test-Middleware1", "m1")
}
middleware2 := func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Set("X-Test-Middleware2", "m2")
}
handler := func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Set("X-Test-Handler", "h")
}
r.Get("/foo", middleware1, RouteMockPoint("mock-point"), middleware2, handler)
// normal request
recorder := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:8000/foo", nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Len(t, recorder.Header(), 3)
assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2"))
assert.EqualValues(t, "h", recorder.Header().Get("X-Test-Handler"))
RouteMockReset()
// mock at "mock-point"
RouteMock("mock-point", func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Set("X-Test-MockPoint", "a")
resp.WriteHeader(http.StatusOK)
})
recorder = httptest.NewRecorder()
req, err = http.NewRequest("GET", "http://localhost:8000/foo", nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Len(t, recorder.Header(), 2)
assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
assert.EqualValues(t, "a", recorder.Header().Get("X-Test-MockPoint"))
RouteMockReset()
// mock at MockAfterMiddlewares
RouteMock(MockAfterMiddlewares, func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Set("X-Test-MockPoint", "b")
resp.WriteHeader(http.StatusOK)
})
recorder = httptest.NewRecorder()
req, err = http.NewRequest("GET", "http://localhost:8000/foo", nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Len(t, recorder.Header(), 3)
assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2"))
assert.EqualValues(t, "b", recorder.Header().Get("X-Test-MockPoint"))
RouteMockReset()
}

6
package-lock.json generated
View File

@ -41,6 +41,7 @@
"sortablejs": "1.15.0",
"swagger-ui-dist": "5.1.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
@ -10265,6 +10266,11 @@
"integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==",
"dev": true
},
"node_modules/tinycolor2": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
},
"node_modules/tinypool": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.6.0.tgz",

View File

@ -40,6 +40,7 @@
"sortablejs": "1.15.0",
"swagger-ui-dist": "5.1.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
"toastify-js": "1.12.0",
"tributejs": "5.1.3",

View File

@ -203,17 +203,25 @@ func InitiateUploadBlob(ctx *context.Context) {
Digest: mount,
})
if blob != nil {
if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil {
accessible, err := packages_model.IsBlobAccessibleForUser(ctx, blob.Blob.ID, ctx.Doer)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
setResponseHeaders(ctx.Resp, &containerHeaders{
Location: fmt.Sprintf("/v2/%s/%s/blobs/%s", ctx.Package.Owner.LowerName, image, mount),
ContentDigest: mount,
Status: http.StatusCreated,
})
return
if accessible {
if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
setResponseHeaders(ctx.Resp, &containerHeaders{
Location: fmt.Sprintf("/v2/%s/%s/blobs/%s", ctx.Package.Owner.LowerName, image, mount),
ContentDigest: mount,
Status: http.StatusCreated,
})
return
}
}
}

View File

@ -25,19 +25,16 @@ func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err
person := ap.PersonNew(ap.IRI(keyID.String()))
err = person.UnmarshalJSON(b)
if err != nil {
err = fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err)
return
return nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err)
}
pubKey := person.PublicKey
if pubKey.ID.String() != keyID.String() {
err = fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
return
return nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
}
pubKeyPem := pubKey.PublicKeyPem
block, _ := pem.Decode([]byte(pubKeyPem))
if block == nil || block.Type != "PUBLIC KEY" {
err = fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
return
return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
}
p, err = x509.ParsePKIXPublicKey(block.Bytes)
return p, err
@ -49,13 +46,12 @@ func fetch(iri *url.URL) (b []byte, err error) {
req.Header("User-Agent", "Gitea/"+setting.AppVer)
resp, err := req.Response()
if err != nil {
return
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status)
return
return nil, fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status)
}
b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
return b, err
@ -67,21 +63,21 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
// 1. Figure out what key we need to verify
v, err := httpsig.NewVerifier(r)
if err != nil {
return
return false, err
}
ID := v.KeyId()
idIRI, err := url.Parse(ID)
if err != nil {
return
return false, err
}
// 2. Fetch the public key of the other actor
b, err := fetch(idIRI)
if err != nil {
return
return false, err
}
pubKey, err := getPublicKeyFromResponse(b, idIRI)
if err != nil {
return
return false, err
}
// 3. Verify the other actor's key
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])

View File

@ -71,7 +71,7 @@ func runMigrateTask(t *admin_model.Task) (err error) {
}()
if err = t.LoadRepo(); err != nil {
return
return err
}
// if repository is ready, then just finish the task
@ -80,16 +80,16 @@ func runMigrateTask(t *admin_model.Task) (err error) {
}
if err = t.LoadDoer(); err != nil {
return
return err
}
if err = t.LoadOwner(); err != nil {
return
return err
}
var opts *migration.MigrateOptions
opts, err = t.MigrateConfig()
if err != nil {
return
return err
}
opts.MigrateToRepoID = t.RepoID
@ -101,7 +101,7 @@ func runMigrateTask(t *admin_model.Task) (err error) {
t.StartTime = timeutil.TimeStampNow()
t.Status = structs.TaskStatusRunning
if err = t.UpdateCols("start_time", "status"); err != nil {
return
return err
}
// check whether the task should be canceled, this goroutine is also managed by process manager
@ -133,12 +133,11 @@ func runMigrateTask(t *admin_model.Task) (err error) {
if err == nil {
log.Trace("Repository migrated [%d]: %s/%s", t.Repo.ID, t.Owner.Name, t.Repo.Name)
return
return nil
}
if repo_model.IsErrRepoAlreadyExist(err) {
err = errors.New("the repository name is already used")
return
return errors.New("the repository name is already used")
}
// remoteAddr may contain credentials, so we sanitize it

View File

@ -34,6 +34,7 @@ func TestPackageContainer(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31})
has := func(l packages_model.PackagePropertyList, name string) bool {
for _, pp := range l {
@ -262,7 +263,16 @@ func TestPackageContainer(t *testing.T) {
t.Run("UploadBlob/Mount", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads?mount=%s", url, unknownDigest))
privateBlobDigest := "sha256:6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d"
req := NewRequestWithBody(t, "POST", fmt.Sprintf("%sv2/%s/%s/blobs/uploads?digest=%s", setting.AppURL, privateUser.Name, image, privateBlobDigest), strings.NewReader("gitea"))
req = AddBasicAuthHeader(req, privateUser.Name)
MakeRequest(t, req, http.StatusCreated)
req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads?mount=%s", url, unknownDigest))
addTokenAuthHeader(req, userToken)
MakeRequest(t, req, http.StatusAccepted)
req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads?mount=%s", url, privateBlobDigest))
addTokenAuthHeader(req, userToken)
MakeRequest(t, req, http.StatusAccepted)

View File

@ -26,7 +26,8 @@
<script>
import $ from 'jquery';
import {SvgIcon} from '../svg.js';
import {useLightTextOnBackground, hexToRGBColor} from '../utils/color.js';
import {useLightTextOnBackground} from '../utils/color.js';
import tinycolor from 'tinycolor2';
const {appSubUrl, i18n} = window.config;
@ -77,7 +78,7 @@ export default {
labels() {
return this.issue.labels.map((label) => {
let textColor;
const [r, g, b] = hexToRGBColor(label.color);
const {r, g, b} = tinycolor(label.color).toRgb();
if (useLightTextOnBackground(r, g, b)) {
textColor = '#eeeeee';
} else {

View File

@ -1,3 +1,4 @@
import tinycolor from 'tinycolor2';
import {basename, extname, isObject, isDarkTheme} from '../utils.js';
import {onInputDebounce} from '../utils/dom.js';
@ -69,33 +70,34 @@ export async function createMonaco(textarea, filename, editorOpts) {
textarea.parentNode.append(container);
// https://github.com/microsoft/monaco-editor/issues/2427
// also, monaco can only parse 6-digit hex colors, so we convert the colors to that format
const styles = window.getComputedStyle(document.documentElement);
const getProp = (name) => styles.getPropertyValue(name).trim();
const getColor = (name) => tinycolor(styles.getPropertyValue(name).trim()).toString('hex6');
monaco.editor.defineTheme('gitea', {
base: isDarkTheme() ? 'vs-dark' : 'vs',
inherit: true,
rules: [
{
background: getProp('--color-code-bg'),
background: getColor('--color-code-bg'),
}
],
colors: {
'editor.background': getProp('--color-code-bg'),
'editor.foreground': getProp('--color-text'),
'editor.inactiveSelectionBackground': getProp('--color-primary-light-4'),
'editor.lineHighlightBackground': getProp('--color-editor-line-highlight'),
'editor.selectionBackground': getProp('--color-primary-light-3'),
'editor.selectionForeground': getProp('--color-primary-light-3'),
'editorLineNumber.background': getProp('--color-code-bg'),
'editorLineNumber.foreground': getProp('--color-secondary-dark-6'),
'editorWidget.background': getProp('--color-body'),
'editorWidget.border': getProp('--color-secondary'),
'input.background': getProp('--color-input-background'),
'input.border': getProp('--color-input-border'),
'input.foreground': getProp('--color-input-text'),
'scrollbar.shadow': getProp('--color-shadow'),
'progressBar.background': getProp('--color-primary'),
'editor.background': getColor('--color-code-bg'),
'editor.foreground': getColor('--color-text'),
'editor.inactiveSelectionBackground': getColor('--color-primary-light-4'),
'editor.lineHighlightBackground': getColor('--color-editor-line-highlight'),
'editor.selectionBackground': getColor('--color-primary-light-3'),
'editor.selectionForeground': getColor('--color-primary-light-3'),
'editorLineNumber.background': getColor('--color-code-bg'),
'editorLineNumber.foreground': getColor('--color-secondary-dark-6'),
'editorWidget.background': getColor('--color-body'),
'editorWidget.border': getColor('--color-secondary'),
'input.background': getColor('--color-input-background'),
'input.border': getColor('--color-input-border'),
'input.foreground': getColor('--color-input-text'),
'scrollbar.shadow': getColor('--color-shadow'),
'progressBar.background': getColor('--color-primary'),
}
});

View File

@ -1,5 +1,6 @@
import $ from 'jquery';
import {useLightTextOnBackground, hexToRGBColor} from '../utils/color.js';
import {useLightTextOnBackground} from '../utils/color.js';
import tinycolor from 'tinycolor2';
const {csrfToken} = window.config;
@ -210,7 +211,7 @@ export function initRepoProject() {
}
function setLabelColor(label, color) {
const [r, g, b] = hexToRGBColor(color);
const {r, g, b} = tinycolor(color).toRgb();
if (useLightTextOnBackground(r, g, b)) {
label.removeClass('dark-label').addClass('light-label');
} else {

View File

@ -13,27 +13,6 @@ function getLuminance(r, g, b) {
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
// Get color as RGB values in 0..255 range from the hex color string (with or without #)
export function hexToRGBColor(backgroundColorStr) {
let backgroundColor = backgroundColorStr;
if (backgroundColorStr[0] === '#') {
backgroundColor = backgroundColorStr.substring(1);
}
// only support transfer of rgb, rgba, rrggbb and rrggbbaa
// if not in these formats, use default values 0, 0, 0
if (![3, 4, 6, 8].includes(backgroundColor.length)) {
return [0, 0, 0];
}
if ([3, 4].includes(backgroundColor.length)) {
const [r, g, b] = backgroundColor;
backgroundColor = `${r}${r}${g}${g}${b}${b}`;
}
const r = parseInt(backgroundColor.substring(0, 2), 16);
const g = parseInt(backgroundColor.substring(2, 4), 16);
const b = parseInt(backgroundColor.substring(4, 6), 16);
return [r, g, b];
}
// Reference from: https://firsching.ch/github_labels.html
// In the future WCAG 3 APCA may be a better solution.
// Check if text should use light color based on RGB of background

View File

@ -1,17 +1,5 @@
import {test, expect} from 'vitest';
import {hexToRGBColor, useLightTextOnBackground} from './color.js';
test('hexToRGBColor', () => {
expect(hexToRGBColor('2b8685')).toEqual([43, 134, 133]);
expect(hexToRGBColor('1e1')).toEqual([17, 238, 17]);
expect(hexToRGBColor('#1e1')).toEqual([17, 238, 17]);
expect(hexToRGBColor('1e16')).toEqual([17, 238, 17]);
expect(hexToRGBColor('3bb6b3')).toEqual([59, 182, 179]);
expect(hexToRGBColor('#3bb6b399')).toEqual([59, 182, 179]);
expect(hexToRGBColor('#0')).toEqual([0, 0, 0]);
expect(hexToRGBColor('#00000')).toEqual([0, 0, 0]);
expect(hexToRGBColor('#1234567')).toEqual([0, 0, 0]);
});
import {useLightTextOnBackground} from './color.js';
test('useLightTextOnBackground', () => {
expect(useLightTextOnBackground(215, 58, 74)).toBe(true);