Compare commits

...

37 Commits

Author SHA1 Message Date
Lunny Xiao
494017e478 Add release note for v1.22.3 (#32205)
Co-authored-by: sebastian-sauer <sauer.sebastian@gmail.com>
2024-10-09 11:23:48 +08:00
Lunny Xiao
56051d9b3b Fix bug when a token is given public only (#32204) (#32218)
Backport #32204
2024-10-09 02:16:37 +00:00
Giteabot
4815c4aeae Add null check for responseData.invalidTopics (#32212) (#32217)
Backport #32212 by @cloudchamb3r

<img width="553" alt="Screenshot 2024-10-08 at 10 49 10 AM"
src="https://github.com/user-attachments/assets/faeef64d-684a-4aba-b7fc-c7c6a0301abe">

`responseData.invalidTopics` can be null but it wasn't handled.

Co-authored-by: cloudchamb3r <jizon0123@protonmail.com>
2024-10-09 09:18:29 +08:00
Lunny Xiao
2e3a191097 Fix javascript error when an anonymous user visiting migration page (#32144) (#32179)
backport #32144

This PR fixes javascript errors when an anonymous user visits the
migration page.
It also makes task view checking more restrictive.

The router moved from `/user/task/{id}/status` to
`/username/reponame/-/migrate/status` because it's a migrate status.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-10-04 17:58:04 +00:00
Giteabot
361221c531 Fixed race condition when deleting documents by repoId in ElasticSearch (#32185) (#32188)
Backport #32185 by @bsofiato

Resolves #32184

Signed-off-by: Bruno Sofiato <bruno.sofiato@gmail.com>
Co-authored-by: Bruno Sofiato <bruno.sofiato@gmail.com>
2024-10-03 16:33:26 +00:00
Lunny Xiao
d86433cce2 Don't init singing keys if oauth2 provider disabled (#32177)
Backport #32148
2024-10-03 11:34:56 -04:00
Lunny Xiao
85897f9b28 Upgrade some dependencies include minio-go (#32166)
backport #32156

Co-authored-by: Manuel Valls Fernández <manuel@valls.dev>
2024-10-03 02:08:06 +00:00
Giteabot
4703e5270f Ensure GetCSRF doesn't return an empty token (#32130) (#32157)
Backport #32130 by @wolfogre

Since page templates keep changing, some pages that contained forms with
CSRF token no longer have them.

It leads to some calls of `GetCSRF` returning an empty string, which
fails the tests. Like


3269b04d61/tests/integration/attachment_test.go (L62-L63)

The test did try to get the CSRF token and provided it, but it was
empty.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-10-01 05:27:37 +00:00
Giteabot
9fc3915e04 Fix the logic of finding the latest pull review commit ID (#32139) (#32165)
Backport #32139 by @Zettat123

Fix #31423

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-10-01 13:10:03 +09:00
Lunny Xiao
a4a6c785b4 Don't join repository when loading action table data (#32127) (#32143)
backport #32127
2024-09-30 11:04:08 +08:00
Giteabot
634454c48c Fix wrong status of Set up Job when first step is skipped (#32120) (#32125)
Backport #32120 by @yp05327

Fix #32089

Co-authored-by: yp05327 <576951401@qq.com>
2024-09-25 10:19:35 +08:00
Zettat123
737c947287 Fix bug in getting merged pull request by commit (#32079) (#32117)
Backport #32079

Fix #32027
2024-09-25 00:12:02 +08:00
Giteabot
1ef74004a2 Fix bug when deleting a migrated branch (#32075) (#32123)
Backport #32075 by @lunny

After migrating a repository with pull request, the branch is missed and
after the pull request merged, the branch cannot be deleted.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 17:01:05 +08:00
Giteabot
5c73da7c54 Include collaboration repositories on dashboard source/forks/mirrors list (#31946) (#32122)
Backport #31946 by @lunny

Fix #13489

In the original implementation, only `All` will display your owned and
collaborated repositories. For other filters like `Source`, `Mirrors`
and etc. will only display your owned repositories.

This PR removed the limitations. Now except `collbrations`, other
filters will always display your owned and collaborated repositories.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 15:17:30 +08:00
Giteabot
a32aaf4d43 Truncate commit message during Discord webhook push events (#31970) (#32121)
Backport #31970 by @kemzeb

Resolves #31668.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-09-24 13:28:01 +08:00
Giteabot
0f834f052b Allow set branch protection in an empty repository (#32095) (#32119)
Backport #32095 by @lunny

Resolve #32093

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 11:42:52 +09:00
Giteabot
a3c660f89a Fix panic when cloning with wrong ssh format. (#32076) (#32118)
Backport #32076 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 01:58:58 +00:00
Giteabot
d5d5fb1925 Fix Bug in Issue/pulls list (#32081) (#32115) 2024-09-24 01:26:10 +00:00
Giteabot
ae37f31df6 use rebuilt mssql-2017 image (#32109) (#32114)
Backport #32109 by @techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2024-09-23 21:23:04 +00:00
Giteabot
1f8cbbab3d Fix rename branch permission bug (#32066) (#32108)
Backport #32066 by @lunny

The previous implementation requires admin permission to rename branches
which should be write permission.

Fix #31993

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-22 19:43:13 +00:00
Giteabot
af0cab23ea Fix wrong last modify time (#32102) (#32104)
Backport #32102 by @lunny

Fix #31930 and more places which use `http.TimeFormat` wrongly.
`http.TimeFormat` requires a UTC time. refer to
https://pkg.go.dev/net/http#TimeFormat

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-22 19:12:57 +00:00
Giteabot
73066e3f97 Add bin to Composer Metadata (#32099) (#32106)
Backport #32099 by @maantje

This PR addresses the missing `bin` field in Composer metadata, which
currently causes vendor-provided binaries to not be symlinked to
`vendor/bin` during installation.

In the current implementation, running `composer install` does not
publish the binaries, leading to issues where expected binaries are not
available.

By properly declaring the `bin` field, this PR ensures that binaries are
correctly symlinked upon installation, as described in the [Composer
documentation](https://getcomposer.org/doc/articles/vendor-binaries.md).

Co-authored-by: Jamie Schouten <j4mie@hey.com>
2024-09-22 18:42:02 +00:00
Giteabot
919b82461a Fix incorrect /tokens api (#32085) (#32092)
Backport #32085 by @KN4CK3R

Fixes #32078

- Add missing scopes output.
- Disallow empty scope.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-09-22 18:02:09 +00:00
Lunny Xiao
69ba37e9fd Fix mssql ci with a new mssql version on ci (#32094)
backport from https://github.com/go-gitea/gitea/pull/32060
2024-09-23 01:32:26 +08:00
Giteabot
ea9e09abe5 Fix: database not update release when using git push --tags --force (#32040) (#32074)
Backport #32040 by @ExplodingDragon

link: https://codeberg.org/forgejo/forgejo/issues/4274

Co-authored-by: Exploding Dragon <explodingfkl@gmail.com>
2024-09-19 07:57:28 +08:00
wxiaoguang
2891edbbcb Refactor CSRF protector (#32057) (#32069)
#32057 improves the CSRF handling and is worth to backport
2024-09-18 17:02:45 +00:00
Giteabot
8dbe83d205 Add missing comment reply handling (#32050) (#32065)
Backport #32050 by @KN4CK3R

Fixes #31937

- Add missing comment reply handling
- Use `onGiteaRun` in the test because the fixtures are not present
otherwise (did this behaviour change?)

Compare without whitespaces.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-09-18 09:23:28 +00:00
Giteabot
2831ae369e Lazy load avatar images (#32051) (#32063)
Backport #32051 by @hiifong

Before:

https://github.com/user-attachments/assets/7b1681ba-4781-432a-ae20-c07e94c1dbb6

After:

https://github.com/user-attachments/assets/5154e160-e22c-460e-b0d9-28768486c178

Co-authored-by: hiifong <i@hiif.ong>
2024-09-18 16:52:44 +08:00
Giteabot
e6395e1e81 Handle invalid target when creating releases using API (#31841) (#32043)
Backport #31841 by @kemzeb

A 500 status code was thrown when passing a non-existent target to the
create release API. This snapshot handles this error and instead throws
a 404 status code.

Discovered while working on #31840.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-09-17 02:23:40 +00:00
Giteabot
8a39a4812f Do not escape relative path in RPM primary index (#32038) (#32054)
Backport #32038 by @KN4CK3R

Fixes #32021

Do not escape the relative path.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-09-17 08:19:35 +08:00
Giteabot
3d7d0c36e7 Check if the due_date is nil when editing issues (#32035) (#32042)
Backport #32035 by @Zettat123

Fix #32030

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-09-15 01:31:34 +08:00
Lunny Xiao
30d989d411 Fix container parallel upload bugs (#32022)
This PR should be replaced by #31860 in v1.23. The aim of creating this
PR is to fix it in 1.22 because globallock hasn't been introduced.

Fix #27640
Fix #29563
Fix #31215
2024-09-12 03:11:03 +00:00
Giteabot
b3af359cc6 Fix /repos/{owner}/{repo}/pulls/{index}/files endpoint not populating previous_filename (#32017) (#32028)
Backport #32017 by @charles-plutohealth

---
`status == "rename"` should have read `status == "renamed"`. The typo
means that file.PreviousFilename would never be populated, which e.g.
breaks usage of the Github Action at
https://github.com/dorny/paths-filter.

Co-authored-by: charles-plutohealth <143208583+charles-plutohealth@users.noreply.github.com>
2024-09-12 08:58:43 +09:00
Giteabot
0629c08a6d Support allowed hosts for migrations to work with proxy (#32025) (#32026)
Backport #32025 by @wolfogre

Fix #32024. Follow #27655.

After this PR, all usage of "new dial context" needs to provide a proxy,
so I dropped the old `NewDialContext` and renamed
`NewDialContextWithProxy` to `NewDialContext`.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-09-11 14:54:19 +08:00
Giteabot
54d828f8ec Increase cacheContextLifetime to reduce false reports (#32011) (#32023)
Backport #32011 by @wolfogre

Replace #32001.

To prevent the context cache from being misused for long-term work
(which would result in using invalid cache without awareness), the
context cache is designed to exist for a maximum of 10 seconds. This
leads to many false reports, especially in the case of slow SQL.

This PR increases it to 5 minutes to reduce false reports.

5 minutes is not a very safe value, as a lot of changes may have
occurred within that time frame. However, as far as I know, there has
not been a case of misuse of context cache discovered so far, so I think
5 minutes should be OK.

Please note that after this PR, if warning logs are found again, it
should get attention, at that time it can be almost 100% certain that it
is a misuse.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-09-11 11:14:40 +08:00
Lunny Xiao
6d4dfcd187 Upgrade cache to v0.2.1 (#32003) (#32009)
Fix #31757
Backport #32003
2024-09-07 21:44:28 +00:00
Giteabot
f7f4256c82 Use forum.gitea.com instead of old URL (#31989) (#31992)
Backport #31989 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-07 03:47:23 +08:00
96 changed files with 1041 additions and 777 deletions

View File

@@ -11,7 +11,7 @@ body:
value: |
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Make sure you are using the latest release and
take a moment to check that your issue hasn't been reported before.
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)

View File

@@ -7,7 +7,7 @@ contact_links:
url: https://discord.gg/Gitea
about: Please ask questions and discuss configuration or deployment problems here.
- name: Discourse Forum
url: https://discourse.gitea.io
url: https://forum.gitea.com
about: Questions and configuration or deployment problems can also be discussed on our forum.
- name: Frequently Asked Questions
url: https://docs.gitea.com/help/faq

View File

@@ -7,7 +7,7 @@ body:
value: |
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Please take a moment to check that your feature hasn't already been suggested.
- type: textarea
id: description

View File

@@ -11,7 +11,7 @@ body:
value: |
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Please take a moment to check that your issue doesn't already exist.
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
5. Please give all relevant information below for bug reports, because

View File

@@ -194,7 +194,9 @@ jobs:
test-mssql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
# specifying the version of ubuntu in use as mssql fails on newer kernels
# pending resolution from vendor
runs-on: ubuntu-20.04
services:
mssql:
image: mcr.microsoft.com/mssql/server:2017-latest

View File

@@ -4,6 +4,47 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.22.3](https://github.com/go-gitea/gitea/releases/tag/1.22.3) - 2024-10-08
* SECURITY
* Fix bug when a token is given public only (#32204) (#32218)
* PERFORMANCE
* Increase `cacheContextLifetime` to reduce false reports (#32011) (#32023)
* Don't join repository when loading action table data (#32127) (#32143)
* BUGFIXES
* Fix javascript error when an anonymous user visits migration page (#32144) (#32179)
* Don't init signing keys if oauth2 provider is disabled (#32177)
* Fix wrong status of `Set up Job` when first step is skipped (#32120) (#32125)
* Fix bug when deleting a migrated branch (#32075) (#32123)
* Truncate commit message during Discord webhook push events (#31970) (#32121)
* Allow to set branch protection in an empty repository (#32095) (#32119)
* Fix panic when cloning with wrong ssh format. (#32076) (#32118)
* Fix rename branch permission bug (#32066) (#32108)
* Fix: database not update release when using `git push --tags --force` (#32040) (#32074)
* Add missing comment reply handling (#32050) (#32065)
* Do not escape relative path in RPM primary index (#32038) (#32054)
* Fix `/repos/{owner}/{repo}/pulls/{index}/files` endpoint not populating `previous_filename` (#32017) (#32028)
* Support allowed hosts for migrations to work with proxy (#32025) (#32026)
* Fix the logic of finding the latest pull review commit ID (#32139) (#32165)
* Fix bug in getting merged pull request by commit (#32079) (#32117)
* Fix wrong last modify time (#32102) (#32104)
* Fix incorrect `/tokens` api (#32085) (#32092)
* Handle invalid target when creating releases using API (#31841) (#32043)
* Check if the `due_date` is nil when editing issues (#32035) (#32042)
* Fix container parallel upload bugs (#32022)
* Fixed race condition when deleting documents by repoId in ElasticSearch (#32185) (#32188)
* Refactor CSRF protector (#32057) (#32069)
* Fix Bug in Issue/pulls list (#32081) (#32115)
* Include collaboration repositories on dashboard source/forks/mirrors list (#31946) (#32122)
* Add null check for responseData.invalidTopics (#32212) (#32217)
* TESTING
* Fix mssql ci with a new mssql version on ci (#32094)
* MISC
* Upgrade some dependencies include minio-go (#32166)
* Add bin to Composer Metadata (#32099) (#32106)
* Lazy load avatar images (#32051) (#32063)
* Upgrade cache to v0.2.1 (#32003) (#32009)
## [1.22.2](https://github.com/go-gitea/gitea/releases/tag/1.22.2) - 2024-08-28
* Security

File diff suppressed because one or more lines are too long

View File

@@ -143,6 +143,12 @@ func runServ(c *cli.Context) error {
return nil
}
defer func() {
if err := recover(); err != nil {
_ = fail(ctx, "Internal Server Error", "Panic: %v\n%s", err, log.Stack(2))
}
}()
keys := strings.Split(c.Args().First(), "-")
if len(keys) != 2 || keys[0] != "key" {
return fail(ctx, "Key ID format error", "Invalid key argument: %s", c.Args().First())
@@ -189,10 +195,7 @@ func runServ(c *cli.Context) error {
}
verb := words[0]
repoPath := words[1]
if repoPath[0] == '/' {
repoPath = repoPath[1:]
}
repoPath := strings.TrimPrefix(words[1], "/")
var lfsVerb string
if verb == lfsAuthenticateVerb {

36
go.mod
View File

@@ -9,7 +9,7 @@ require (
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.15.0
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/cache v0.2.1
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
@@ -66,8 +66,8 @@ require (
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.17.8
github.com/klauspost/cpuid/v2 v2.2.7
github.com/klauspost/compress v1.17.9
github.com/klauspost/cpuid/v2 v2.2.8
github.com/lib/pq v1.10.9
github.com/markbates/goth v1.79.0
github.com/mattn/go-isatty v0.0.20
@@ -75,8 +75,8 @@ require (
github.com/meilisearch/meilisearch-go v0.26.2
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.26
github.com/microsoft/go-mssqldb v1.7.0
github.com/minio/minio-go/v7 v7.0.69
github.com/microsoft/go-mssqldb v1.7.2
github.com/minio/minio-go/v7 v7.0.77
github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.7.0
@@ -104,13 +104,13 @@ require (
github.com/yuin/goldmark v1.7.0
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.23.0
golang.org/x/crypto v0.26.0
golang.org/x/image v0.18.0
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.18.0
golang.org/x/sys v0.20.0
golang.org/x/text v0.16.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
golang.org/x/net v0.28.0
golang.org/x/oauth2 v0.21.0
golang.org/x/sys v0.24.0
golang.org/x/text v0.17.0
golang.org/x/tools v0.24.0
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -123,8 +123,7 @@ require (
)
require (
cloud.google.com/go/compute v1.25.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
@@ -186,6 +185,7 @@ require (
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/inflect v0.21.0 // indirect
@@ -198,7 +198,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-webauthn/x v0.1.9 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
@@ -232,7 +232,6 @@ require (
github.com/mholt/acmez/v2 v2.0.1 // indirect
github.com/miekg/dns v1.1.59 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -255,7 +254,7 @@ require (
github.com/rhysd/actionlint v1.6.27 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@@ -291,10 +290,9 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect

110
go.sum
View File

@@ -1,7 +1,5 @@
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
@@ -22,8 +20,8 @@ gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw=
gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w=
gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE=
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 h1:IFDiMBObsP6CZIRaDLd54SR6zPYAffPXiXck5Xslu0Q=
@@ -166,7 +164,6 @@ github.com/blevesearch/zapx/v16 v16.1.5/go.mod h1:J4mSF39w1QELc11EWRSBFkPeZuO7r/
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
@@ -210,7 +207,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -243,7 +239,6 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.1 h1:iPCqofzMO41WVbcS/B5Ym7AwHQg9cyQ7Ie/R2XU5L3A=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.1/go.mod h1:VY4oyqUnpULFB3SCRpl24GFDIN1PmfiQIvN/G4ScSNg=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
@@ -312,6 +307,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
@@ -336,8 +333,6 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-swagger/go-swagger v0.30.5 h1:SQ2+xSonWjjoEMOV5tcOnZJVlfyUfCBhGQGArS1b9+U=
@@ -358,8 +353,8 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
@@ -378,7 +373,6 @@ github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1Ix
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -387,7 +381,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -418,7 +411,6 @@ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -507,13 +499,13 @@ github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -530,13 +522,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI=
github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -556,7 +545,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/meilisearch/meilisearch-go v0.26.2 h1:3gTlmiV1dHHumVUhYdJbvh3camiNiyqQ1hNveVsU2OE=
@@ -565,16 +553,14 @@ github.com/mholt/acmez/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/microsoft/go-mssqldb v1.7.0 h1:sgMPW0HA6Ihd37Yx0MzHyKD726C2kY/8KJsQtXHNaAs=
github.com/microsoft/go-mssqldb v1.7.0/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/minio/minio-go/v7 v7.0.77 h1:GaGghJRg9nwDVlNbwYjSDJT1rqltQkBFDsypWX1v3Bw=
github.com/minio/minio-go/v7 v7.0.77/go.mod h1:AVM3IUN6WwKzmwBxVdjzhH8xq+f57JSbbvzqvUzR6eg=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -614,12 +600,10 @@ github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5Bn
github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
@@ -633,7 +617,6 @@ github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
@@ -680,8 +663,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -703,16 +686,11 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
@@ -771,7 +749,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
@@ -847,7 +824,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
@@ -859,8 +835,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
@@ -871,14 +847,11 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -893,10 +866,10 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -905,15 +878,13 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -944,8 +915,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -955,27 +926,23 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
@@ -985,15 +952,12 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
@@ -1005,7 +969,6 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
@@ -1020,7 +983,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

View File

@@ -450,9 +450,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, err
}
sess := db.GetEngine(ctx).Where(cond).
Select("`action`.*"). // this line will avoid select other joined table's columns
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
sess := db.GetEngine(ctx).Where(cond)
opts.SetDefaultValues()
sess = db.SetSessionPagination(sess, &opts)

View File

@@ -226,6 +226,8 @@ func TestNotifyWatchers(t *testing.T) {
}
func TestGetFeedsCorrupted(t *testing.T) {
// Now we will not check for corrupted data in the feeds
// users should run doctor to fix their data
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
@@ -239,8 +241,8 @@ func TestGetFeedsCorrupted(t *testing.T) {
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 0)
assert.Equal(t, int64(0), count)
assert.Len(t, actions, 1)
assert.Equal(t, int64(1), count)
}
func TestConsistencyUpdateAction(t *testing.T) {

View File

@@ -179,27 +179,6 @@ func GetMigratingTask(ctx context.Context, repoID int64) (*Task, error) {
return &task, nil
}
// GetMigratingTaskByID returns the migrating task by repo's id
func GetMigratingTaskByID(ctx context.Context, id, doerID int64) (*Task, *migration.MigrateOptions, error) {
task := Task{
ID: id,
DoerID: doerID,
Type: structs.TaskTypeMigrateRepo,
}
has, err := db.GetEngine(ctx).Get(&task)
if err != nil {
return nil, nil, err
} else if !has {
return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type}
}
var opts migration.MigrateOptions
if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil {
return nil, nil, err
}
return &task, &opts, nil
}
// CreateTask creates a task on database
func CreateTask(ctx context.Context, task *Task) error {
return db.Insert(ctx, task)

View File

@@ -83,3 +83,22 @@
issue_id: 2 # in repo_id 1
review_id: 20
created_unix: 946684810
-
id: 10
type: 22 # review
poster_id: 5
issue_id: 3 # in repo_id 1
content: "reviewed by user5"
review_id: 21
created_unix: 946684816
-
id: 11
type: 27 # review request
poster_id: 2
issue_id: 3 # in repo_id 1
content: "review request for user5"
review_id: 22
assignee_id: 5
created_unix: 946684817

View File

@@ -179,3 +179,22 @@
content: "Review Comment"
updated_unix: 946684810
created_unix: 946684810
-
id: 21
type: 2
reviewer_id: 5
issue_id: 3
content: "reviewed by user5"
commit_id: 4a357436d925b5c974181ff12a994538ddc5a269
updated_unix: 946684816
created_unix: 946684816
-
id: 22
type: 4
reviewer_id: 5
issue_id: 3
content: "review request for user5"
updated_unix: 946684817
created_unix: 946684817

View File

@@ -387,7 +387,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer)
// Note: This doesn't page as we only expect a very limited number of reviews
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
Type: ReviewTypeApprove,
Types: []ReviewType{ReviewTypeApprove},
IssueID: pr.IssueID,
OfficialOnly: setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly,
})

View File

@@ -364,7 +364,7 @@ func GetCurrentReview(ctx context.Context, reviewer *user_model.User, issue *Iss
return nil, nil
}
reviews, err := FindReviews(ctx, FindReviewOptions{
Type: ReviewTypePending,
Types: []ReviewType{ReviewTypePending},
IssueID: issue.ID,
ReviewerID: reviewer.ID,
})

View File

@@ -63,7 +63,7 @@ func (reviews ReviewList) LoadIssues(ctx context.Context) error {
// FindReviewOptions represent possible filters to find reviews
type FindReviewOptions struct {
db.ListOptions
Type ReviewType
Types []ReviewType
IssueID int64
ReviewerID int64
OfficialOnly bool
@@ -78,8 +78,8 @@ func (opts *FindReviewOptions) toCond() builder.Cond {
if opts.ReviewerID > 0 {
cond = cond.And(builder.Eq{"reviewer_id": opts.ReviewerID})
}
if opts.Type != ReviewTypeUnknown {
cond = cond.And(builder.Eq{"type": opts.Type})
if len(opts.Types) > 0 {
cond = cond.And(builder.In("type", opts.Types))
}
if opts.OfficialOnly {
cond = cond.And(builder.Eq{"official": true})

View File

@@ -63,7 +63,7 @@ func TestReviewType_Icon(t *testing.T) {
func TestFindReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
Type: issues_model.ReviewTypeApprove,
Types: []issues_model.ReviewType{issues_model.ReviewTypeApprove},
IssueID: 2,
ReviewerID: 1,
})
@@ -75,7 +75,7 @@ func TestFindReviews(t *testing.T) {
func TestFindLatestReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
reviews, err := issues_model.FindLatestReviews(db.DefaultContext, issues_model.FindReviewOptions{
Type: issues_model.ReviewTypeApprove,
Types: []issues_model.ReviewType{issues_model.ReviewTypeApprove},
IssueID: 11,
})
assert.NoError(t, err)

View File

@@ -398,6 +398,10 @@ func (u *User) IsIndividual() bool {
return u.Type == UserTypeIndividual
}
func (u *User) IsUser() bool {
return u.Type == UserTypeIndividual || u.Type == UserTypeBot
}
// IsBot returns whether or not the user is of type bot
func (u *User) IsBot() bool {
return u.Type == UserTypeBot

View File

@@ -18,8 +18,32 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
return fullStepsOfEmptySteps(task)
}
firstStep := task.Steps[0]
// firstStep is the first step that has run or running, not include preStep.
// For example,
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): firstStep is step1.
// 2. preStep(Success) -> step1(Skipped) -> step2(Success) -> postStep(Success): firstStep is step2.
// 3. preStep(Success) -> step1(Running) -> step2(Waiting) -> postStep(Waiting): firstStep is step1.
// 4. preStep(Success) -> step1(Skipped) -> step2(Skipped) -> postStep(Skipped): firstStep is nil.
// 5. preStep(Success) -> step1(Cancelled) -> step2(Cancelled) -> postStep(Cancelled): firstStep is nil.
var firstStep *actions_model.ActionTaskStep
// lastHasRunStep is the last step that has run.
// For example,
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
// So its Stopped is the Started of postStep when there are no more steps to run.
var lastHasRunStep *actions_model.ActionTaskStep
var logIndex int64
for _, step := range task.Steps {
if firstStep == nil && (step.Status.HasRun() || step.Status.IsRunning()) {
firstStep = step
}
if step.Status.HasRun() {
lastHasRunStep = step
}
logIndex += step.LogLength
}
preStep := &actions_model.ActionTaskStep{
Name: preStepName,
@@ -28,32 +52,17 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
Status: actions_model.StatusRunning,
}
if firstStep.Status.HasRun() || firstStep.Status.IsRunning() {
// No step has run or is running, so preStep is equal to the task
if firstStep == nil {
preStep.Stopped = task.Stopped
preStep.Status = task.Status
} else {
preStep.LogLength = firstStep.LogIndex
preStep.Stopped = firstStep.Started
preStep.Status = actions_model.StatusSuccess
} else if task.Status.IsDone() {
preStep.Stopped = task.Stopped
preStep.Status = actions_model.StatusFailure
if task.Status.IsSkipped() {
preStep.Status = actions_model.StatusSkipped
}
}
logIndex += preStep.LogLength
// lastHasRunStep is the last step that has run.
// For example,
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
// So its Stopped is the Started of postStep when there are no more steps to run.
var lastHasRunStep *actions_model.ActionTaskStep
for _, step := range task.Steps {
if step.Status.HasRun() {
lastHasRunStep = step
}
logIndex += step.LogLength
}
if lastHasRunStep == nil {
lastHasRunStep = preStep
}

View File

@@ -137,6 +137,25 @@ func TestFullSteps(t *testing.T) {
{Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
},
},
{
name: "first step is skipped",
task: &actions_model.ActionTask{
Steps: []*actions_model.ActionTaskStep{
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
},
Status: actions_model.StatusSuccess,
Started: 10000,
Stopped: 10100,
LogLength: 100,
},
want: []*actions_model.ActionTaskStep{
{Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010},
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
{Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
_ "code.gitea.io/gitea/models" // https://discourse.gitea.io/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4
_ "code.gitea.io/gitea/models" // https://forum.gitea.com/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4
"github.com/stretchr/testify/assert"
)

View File

@@ -63,9 +63,9 @@ func (cc *cacheContext) isDiscard() bool {
}
// cacheContextLifetime is the max lifetime of cacheContext.
// Since cacheContext is used to cache data in a request level context, 10s is enough.
// If a cacheContext is used more than 10s, it's probably misuse.
const cacheContextLifetime = 10 * time.Second
// Since cacheContext is used to cache data in a request level context, 5 minutes is enough.
// If a cacheContext is used more than 5 minutes, it's probably misuse.
const cacheContextLifetime = 5 * time.Minute
var timeNow = time.Now
@@ -131,7 +131,7 @@ func GetContextData(ctx context.Context, tp, key any) any {
if c.Expired() {
// The warning means that the cache context is misused for long-life task,
// it can be resolved with WithNoCacheContext(ctx).
log.Warn("cache context is expired, may be misused for long-life tasks: %v", c)
log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
return nil
}
return c.Get(tp, key)
@@ -144,7 +144,7 @@ func SetContextData(ctx context.Context, tp, key, value any) {
if c.Expired() {
// The warning means that the cache context is misused for long-life task,
// it can be resolved with WithNoCacheContext(ctx).
log.Warn("cache context is expired, may be misused for long-life tasks: %v", c)
log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
return
}
c.Put(tp, key, value)
@@ -157,7 +157,7 @@ func RemoveContextData(ctx context.Context, tp, key any) {
if c.Expired() {
// The warning means that the cache context is misused for long-life task,
// it can be resolved with WithNoCacheContext(ctx).
log.Warn("cache context is expired, may be misused for long-life tasks: %v", c)
log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
return
}
c.Delete(tp, key)

View File

@@ -45,7 +45,7 @@ func TestWithCacheContext(t *testing.T) {
timeNow = now
}()
timeNow = func() time.Time {
return now().Add(10 * time.Second)
return now().Add(5 * time.Minute)
}
v = GetContextData(ctx, field, "my_config1")
assert.Nil(t, v)

View File

@@ -13,11 +13,7 @@ import (
)
// NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check
func NewDialContext(usage string, allowList, blockList *HostMatchList) func(ctx context.Context, network, addr string) (net.Conn, error) {
return NewDialContextWithProxy(usage, allowList, blockList, nil)
}
func NewDialContextWithProxy(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
func NewDialContext(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
// How Go HTTP Client works with redirection:
// transport.RoundTrip URL=http://domain.com, Host=domain.com
// transport.DialContext addrOrHost=domain.com:80

View File

@@ -75,7 +75,8 @@ func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag s
w.Header().Set("Etag", etag)
}
if lastModified != nil && !lastModified.IsZero() {
w.Header().Set("Last-Modified", lastModified.Format(http.TimeFormat))
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
w.Header().Set("Last-Modified", lastModified.UTC().Format(http.TimeFormat))
}
if len(etag) > 0 {

View File

@@ -79,6 +79,7 @@ func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
httpcache.SetCacheControlInHeader(header, duration)
if !opts.LastModified.IsZero() {
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
header.Set("Last-Modified", opts.LastModified.UTC().Format(http.TimeFormat))
}
}

View File

@@ -20,6 +20,7 @@ import (
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/typesniffer"
@@ -197,8 +198,33 @@ func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha st
return nil
}
// Delete deletes indexes by ids
// Delete entries by repoId
func (b *Indexer) Delete(ctx context.Context, repoID int64) error {
if err := b.doDelete(ctx, repoID); err != nil {
// Maybe there is a conflict during the delete operation, so we should retry after a refresh
log.Warn("Deletion of entries of repo %v within index %v was erroneus. Trying to refresh index before trying again", repoID, b.inner.VersionedIndexName(), err)
if err := b.refreshIndex(ctx); err != nil {
return err
}
if err := b.doDelete(ctx, repoID); err != nil {
log.Error("Could not delete entries of repo %v within index %v", repoID, b.inner.VersionedIndexName())
return err
}
}
return nil
}
func (b *Indexer) refreshIndex(ctx context.Context) error {
if _, err := b.inner.Client.Refresh(b.inner.VersionedIndexName()).Do(ctx); err != nil {
log.Error("Error while trying to refresh index %v", b.inner.VersionedIndexName(), err)
return err
}
return nil
}
// Delete entries by repoId
func (b *Indexer) doDelete(ctx context.Context, repoID int64) error {
_, err := b.inner.Client.DeleteByQuery(b.inner.VersionedIndexName()).
Query(elastic.NewTermsQuery("repo_id", repoID)).
Do(ctx)

View File

@@ -43,6 +43,7 @@ type Metadata struct {
Homepage string `json:"homepage,omitempty"`
License Licenses `json:"license,omitempty"`
Authors []Author `json:"authors,omitempty"`
Bin []string `json:"bin,omitempty"`
Autoload map[string]any `json:"autoload,omitempty"`
AutoloadDev map[string]any `json:"autoload-dev,omitempty"`
Extra map[string]any `json:"extra,omitempty"`

View File

@@ -34,7 +34,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML {
name = "avatar"
}
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
return template.HTML(`<img loading="lazy" class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
}
// Avatar renders user avatars. args: user, size (int), class (string)

View File

@@ -218,8 +218,6 @@ string.desc = Z - A
[error]
occurred = An error occurred
report_message = If you believe that this is a Gitea bug, please search for issues on <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> or open a new issue if necessary.
missing_csrf = Bad Request: no CSRF token present
invalid_csrf = Bad Request: invalid CSRF token
not_found = The target couldn't be found.
network_error = Network error

View File

@@ -63,6 +63,20 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
return
}
// check if scope only applies to public resources
publicOnly, err := scope.PublicOnly()
if err != nil {
ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error())
return
}
if publicOnly {
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
return
}
}
}
}

View File

@@ -10,23 +10,29 @@ import (
"fmt"
"os"
"strings"
"sync"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
)
var uploadVersionMutex sync.Mutex
// TODO: use clustered lock
var uploadVersionMutex = sync.NewExclusivePool()
// saveAsPackageBlob creates a package blob from an upload
// The uploaded blob gets stored in a special upload version to link them to the package/image
func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) {
pkgPath := pci.PackageInfo.Owner.LowerName + "/" + pci.PackageInfo.Name
uploadVersionMutex.CheckIn(pkgPath)
defer uploadVersionMutex.CheckOut(pkgPath)
pb := packages_service.NewPackageBlob(hsr)
exists := false
@@ -80,6 +86,10 @@ func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader
// mountBlob mounts the specific blob to a different package
func mountBlob(ctx context.Context, pi *packages_service.PackageInfo, pb *packages_model.PackageBlob) error {
pkgPath := pi.Owner.LowerName + "/" + pi.Name
uploadVersionMutex.CheckIn(pkgPath)
defer uploadVersionMutex.CheckOut(pkgPath)
uploadVersion, err := getOrCreateUploadVersion(ctx, pi)
if err != nil {
return err
@@ -93,9 +103,6 @@ func mountBlob(ctx context.Context, pi *packages_service.PackageInfo, pb *packag
func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) {
var uploadVersion *packages_model.PackageVersion
// FIXME: Replace usage of mutex with database transaction
// https://github.com/go-gitea/gitea/pull/21862
uploadVersionMutex.Lock()
err := db.WithTx(ctx, func(ctx context.Context) error {
created := true
p := &packages_model.Package{
@@ -140,7 +147,6 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
return nil
})
uploadVersionMutex.Unlock()
return uploadVersion, err
}
@@ -172,10 +178,14 @@ func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, p
return nil
}
func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error {
func deleteBlob(ctx context.Context, owner *user_model.User, image, digest string) error {
pkgPath := owner.LowerName + "/" + image
uploadVersionMutex.CheckIn(pkgPath)
defer uploadVersionMutex.CheckOut(pkgPath)
return db.WithTx(ctx, func(ctx context.Context) error {
pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{
OwnerID: ownerID,
OwnerID: owner.ID,
Image: image,
Digest: digest,
})

View File

@@ -24,6 +24,7 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
auth_service "code.gitea.io/gitea/services/auth"
@@ -540,7 +541,7 @@ func DeleteBlob(ctx *context.Context) {
return
}
if err := deleteBlob(ctx, ctx.Package.Owner.ID, ctx.Params("image"), d); err != nil {
if err := deleteBlob(ctx, ctx.Package.Owner, ctx.Params("image"), d); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
@@ -550,6 +551,9 @@ func DeleteBlob(ctx *context.Context) {
})
}
// TODO: use clustered lock
var lockManifest = sync.NewExclusivePool()
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
func UploadManifest(ctx *context.Context) {
reference := ctx.Params("reference")
@@ -581,6 +585,10 @@ func UploadManifest(ctx *context.Context) {
return
}
imagePath := ctx.Package.Owner.Name + "/" + ctx.Params("image")
lockManifest.CheckIn(imagePath)
defer lockManifest.CheckOut(imagePath)
digest, err := processManifest(ctx, mci, buf)
if err != nil {
var namedError *namedError
@@ -679,6 +687,10 @@ func DeleteManifest(ctx *context.Context) {
return
}
imagePath := ctx.Package.Owner.Name + "/" + ctx.Params("image")
lockManifest.CheckIn(imagePath)
defer lockManifest.CheckOut(imagePath)
pvs, err := container_model.GetManifestVersions(ctx, opts)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)

View File

@@ -115,7 +115,9 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
xmlMetadataWithHeader := append([]byte(xml.Header), xmlMetadata...)
latest := pds[len(pds)-1]
ctx.Resp.Header().Set("Last-Modified", latest.Version.CreatedUnix.Format(http.TimeFormat))
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
lastModifed := latest.Version.CreatedUnix.AsTime().UTC().Format(http.TimeFormat)
ctx.Resp.Header().Set("Last-Modified", lastModifed)
ext := strings.ToLower(filepath.Ext(params.Filename))
if isChecksumExtension(ext) {

View File

@@ -235,6 +235,62 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext)
}
}
func checkTokenPublicOnly() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.PublicOnly {
return
}
requiredScopeCategories, ok := ctx.Data["requiredScopeCategories"].([]auth_model.AccessTokenScopeCategory)
if !ok || len(requiredScopeCategories) == 0 {
return
}
// public Only permission check
switch {
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository):
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryIssue):
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public issues")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization):
if ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs")
return
}
if ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryUser):
if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public users")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryActivityPub):
if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public activitypub")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryNotification):
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public notifications")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryPackage):
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
return
}
}
}
}
// if a token is being used for auth, we check that it contains the required scope
// if a token is not being used, reqToken will enforce other sign in methods
func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) {
@@ -250,9 +306,6 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
return
}
ctx.Data["ApiTokenScopePublicRepoOnly"] = false
ctx.Data["ApiTokenScopePublicOrgOnly"] = false
// use the http method to determine the access level
requiredScopeLevel := auth_model.Read
if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" {
@@ -261,6 +314,18 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
// get the required scope for the given access level and category
requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...)
allow, err := scope.HasScope(requiredScopes...)
if err != nil {
ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error())
return
}
if !allow {
ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes))
return
}
ctx.Data["requiredScopeCategories"] = requiredScopeCategories
// check if scope only applies to public resources
publicOnly, err := scope.PublicOnly()
@@ -269,21 +334,8 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
return
}
// this context is used by the middleware in the specific route
ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository)
ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization)
allow, err := scope.HasScope(requiredScopes...)
if err != nil {
ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error())
return
}
if allow {
return
}
ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes))
// assign to true so that those searching should only filter public repositories/users/organizations
ctx.PublicOnly = publicOnly
}
}
@@ -295,25 +347,6 @@ func reqToken() func(ctx *context.APIContext) {
return
}
if true == ctx.Data["IsApiToken"] {
publicRepo, pubRepoExists := ctx.Data["ApiTokenScopePublicRepoOnly"]
publicOrg, pubOrgExists := ctx.Data["ApiTokenScopePublicOrgOnly"]
if pubRepoExists && publicRepo.(bool) &&
ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos")
return
}
if pubOrgExists && publicOrg.(bool) &&
ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic {
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs")
return
}
return
}
if ctx.IsSigned {
return
}
@@ -879,11 +912,11 @@ func Routes() *web.Route {
m.Group("/user/{username}", func() {
m.Get("", activitypub.Person)
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
}, context.UserAssignmentAPI())
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
m.Group("/user-id/{user-id}", func() {
m.Get("", activitypub.Person)
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
}, context.UserIDAssignmentAPI())
}, context.UserIDAssignmentAPI(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub))
}
@@ -939,7 +972,7 @@ func Routes() *web.Route {
}, reqSelfOrAdmin(), reqBasicOrRevProxyAuth())
m.Get("/activities/feeds", user.ListUserActivityFeeds)
}, context.UserAssignmentAPI(), individualPermsChecker)
}, context.UserAssignmentAPI(), checkTokenPublicOnly(), individualPermsChecker)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser))
// Users (requires user scope)
@@ -957,7 +990,7 @@ func Routes() *web.Route {
m.Get("/starred", user.GetStarredRepos)
m.Get("/subscriptions", user.GetWatchedRepos)
}, context.UserAssignmentAPI())
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
// Users (requires user scope)
@@ -1044,7 +1077,7 @@ func Routes() *web.Route {
m.Get("", user.IsStarring)
m.Put("", user.Star)
m.Delete("", user.Unstar)
}, repoAssignment())
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
m.Get("/times", repo.ListMyTrackedTimes)
m.Get("/stopwatches", repo.GetStopwatches)
@@ -1069,18 +1102,20 @@ func Routes() *web.Route {
m.Get("", user.CheckUserBlock)
m.Put("", user.BlockUser)
m.Delete("", user.UnblockUser)
}, context.UserAssignmentAPI())
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
})
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
// Repositories (requires repo scope, org scope)
m.Post("/org/{org}/repos",
// FIXME: we need org in context
tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository),
reqToken(),
bind(api.CreateRepoOption{}),
repo.CreateOrgRepoDeprecated)
// requires repo scope
// FIXME: Don't expose repository id outside of the system
m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(repo.GetByID)
// Repos (requires repo scope)
@@ -1274,6 +1309,8 @@ func Routes() *web.Route {
m.Group("/{ref}", func() {
m.Get("/status", repo.GetCombinedCommitStatusByRef)
m.Get("/statuses", repo.GetCommitStatusesByRef)
}, context.ReferencesGitRepo())
m.Group("/{sha}", func() {
m.Get("/pull", repo.GetCommitPullRequest)
}, context.ReferencesGitRepo())
}, reqRepoReader(unit.TypeCode))
@@ -1319,7 +1356,7 @@ func Routes() *web.Route {
m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar)
m.Delete("", repo.DeleteAvatar)
}, reqAdmin(), reqToken())
}, repoAssignment())
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
// Notifications (requires notifications scope)
@@ -1328,7 +1365,7 @@ func Routes() *web.Route {
m.Combo("/notifications", reqToken()).
Get(notify.ListRepoNotifications).
Put(notify.ReadRepoNotifications)
}, repoAssignment())
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification))
// Issue (requires issue scope)
@@ -1442,7 +1479,7 @@ func Routes() *web.Route {
Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone).
Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone)
})
}, repoAssignment())
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue))
// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
@@ -1453,14 +1490,14 @@ func Routes() *web.Route {
m.Get("/files", reqToken(), packages.ListPackageFiles)
})
m.Get("/", reqToken(), packages.ListPackages)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead))
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
// Organizations
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI(), checkTokenPublicOnly())
m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create)
m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization))
m.Group("/orgs/{org}", func() {
@@ -1518,7 +1555,7 @@ func Routes() *web.Route {
m.Delete("", org.UnblockUser)
})
}, reqToken(), reqOrgOwnership())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly())
m.Group("/teams/{teamid}", func() {
m.Combo("").Get(reqToken(), org.GetTeam).
Patch(reqToken(), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam).
@@ -1538,7 +1575,7 @@ func Routes() *web.Route {
Get(reqToken(), org.GetTeamRepo)
})
m.Get("/activities/feeds", org.ListTeamActivityFeeds)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership(), checkTokenPublicOnly())
m.Group("/admin", func() {
m.Group("/cron", func() {

View File

@@ -191,7 +191,7 @@ func GetAll(ctx *context.APIContext) {
// "$ref": "#/responses/OrganizationList"
vMode := []api.VisibleType{api.VisibleTypePublic}
if ctx.IsSigned {
if ctx.IsSigned && !ctx.PublicOnly {
vMode = append(vMode, api.VisibleTypeLimited)
if ctx.Doer.IsAdmin {
vMode = append(vMode, api.VisibleTypePrivate)

View File

@@ -325,11 +325,11 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) {
}
}
// GetCommitPullRequest returns the pull request of the commit
// GetCommitPullRequest returns the merged pull request of the commit
func GetCommitPullRequest(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/commits/{sha}/pull repository repoGetCommitPullRequest
// ---
// summary: Get the pull request of the commit
// summary: Get the merged pull request of the commit
// produces:
// - application/json
// parameters:
@@ -354,7 +354,7 @@ func GetCommitPullRequest(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.Params(":sha"))
pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.Params("sha"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
ctx.Error(http.StatusNotFound, "GetPullRequestByMergedCommit", err)

View File

@@ -149,7 +149,7 @@ func SearchIssues(ctx *context.APIContext) {
Actor: ctx.Doer,
}
if ctx.IsSigned {
opts.Private = true
opts.Private = !ctx.PublicOnly
opts.AllLimited = true
}
if ctx.FormString("owner") != "" {
@@ -828,10 +828,16 @@ func EditIssue(ctx *context.APIContext) {
if (form.Deadline != nil || form.RemoveDeadline != nil) && canWrite {
var deadlineUnix timeutil.TimeStamp
if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
23, 59, 59, 0, form.Deadline.Location())
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
if form.RemoveDeadline == nil || !*form.RemoveDeadline {
if form.Deadline == nil {
ctx.Error(http.StatusBadRequest, "", "The due_date cannot be empty")
return
}
if !form.Deadline.IsZero() {
deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
23, 59, 59, 0, form.Deadline.Location())
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
}
}
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {

View File

@@ -83,7 +83,6 @@ func ListPullReviews(ctx *context.APIContext) {
opts := issues_model.FindReviewOptions{
ListOptions: utils.GetListOptions(ctx),
Type: issues_model.ReviewTypeUnknown,
IssueID: pr.IssueID,
}

View File

@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
@@ -251,6 +252,8 @@ func CreateRelease(ctx *context.APIContext) {
ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
} else if models.IsErrProtectedTagName(err) {
ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err)
} else if git.IsErrNotExist(err) {
ctx.Error(http.StatusNotFound, "ErrNotExist", fmt.Errorf("target \"%v\" not found: %w", rel.Target, err))
} else {
ctx.Error(http.StatusInternalServerError, "CreateRelease", err)
}

View File

@@ -129,6 +129,11 @@ func Search(ctx *context.APIContext) {
// "422":
// "$ref": "#/responses/validationError"
private := ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private"))
if ctx.PublicOnly {
private = false
}
opts := &repo_model.SearchRepoOptions{
ListOptions: utils.GetListOptions(ctx),
Actor: ctx.Doer,
@@ -138,7 +143,7 @@ func Search(ctx *context.APIContext) {
TeamID: ctx.FormInt64("team_id"),
TopicOnly: ctx.FormBool("topic"),
Collaborate: optional.None[bool](),
Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")),
Private: private,
Template: optional.None[bool](),
StarredByID: ctx.FormInt64("starredBy"),
IncludeDescription: ctx.FormBool("includeDesc"),

View File

@@ -118,6 +118,10 @@ func CreateAccessToken(ctx *context.APIContext) {
ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err))
return
}
if scope == "" {
ctx.Error(http.StatusBadRequest, "AccessTokenScope", "access token must have a scope")
return
}
t.Scope = scope
if err := auth_model.NewAccessToken(ctx, t); err != nil {
@@ -129,6 +133,7 @@ func CreateAccessToken(ctx *context.APIContext) {
Token: t.Token,
ID: t.ID,
TokenLastEight: t.TokenLastEight,
Scopes: t.Scope.StringSlice(),
})
}

View File

@@ -9,6 +9,7 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
@@ -67,11 +68,16 @@ func Search(ctx *context.APIContext) {
maxResults = 1
users = []*user_model.User{user_model.NewActionsUser()}
default:
var visible []structs.VisibleType
if ctx.PublicOnly {
visible = []structs.VisibleType{structs.VisibleTypePublic}
}
users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"),
UID: uid,
Type: user_model.UserTypeIndividual,
Visible: visible,
ListOptions: listOptions,
})
if err != nil {

View File

@@ -395,7 +395,8 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string
ctx.Resp.Header().Set("Content-Type", contentType)
ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", fi.Size()))
ctx.Resp.Header().Set("Last-Modified", fi.ModTime().Format(http.TimeFormat))
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
ctx.Resp.Header().Set("Last-Modified", fi.ModTime().UTC().Format(http.TimeFormat))
http.ServeFile(ctx.Resp, ctx.Req, reqFile)
}

View File

@@ -461,6 +461,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["AssigneeID"] = assigneeID
ctx.Data["PosterID"] = posterID
ctx.Data["Keyword"] = keyword
ctx.Data["IsShowClosed"] = isShowClosed
switch {
case isShowClosed.Value():
ctx.Data["State"] = "closed"

View File

@@ -15,6 +15,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -284,3 +285,40 @@ func MigrateCancelPost(ctx *context.Context) {
}
ctx.Redirect(ctx.Repo.Repository.Link())
}
// MigrateStatus returns migrate task's status
func MigrateStatus(ctx *context.Context) {
task, err := admin_model.GetMigratingTask(ctx, ctx.Repo.Repository.ID)
if err != nil {
if admin_model.IsErrTaskDoesNotExist(err) {
ctx.JSON(http.StatusNotFound, map[string]any{
"err": "task does not exist or you do not have access to this task",
})
return
}
log.Error("GetMigratingTask: %v", err)
ctx.JSON(http.StatusInternalServerError, map[string]any{
"err": http.StatusText(http.StatusInternalServerError),
})
return
}
message := task.Message
if task.Message != "" && task.Message[0] == '{' {
// assume message is actually a translatable string
var translatableMessage admin_model.TranslatableMessage
if err := json.Unmarshal([]byte(message), &translatableMessage); err != nil {
translatableMessage = admin_model.TranslatableMessage{
Format: "migrate.migrating_failed.error",
Args: []any{task.Message},
}
}
message = ctx.Locale.TrString(translatableMessage.Format, translatableMessage.Args...)
}
ctx.JSON(http.StatusOK, map[string]any{
"status": task.Status,
"message": message,
})
}

View File

@@ -1,53 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"net/http"
"strconv"
admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/services/context"
)
// TaskStatus returns task's status
func TaskStatus(ctx *context.Context) {
task, opts, err := admin_model.GetMigratingTaskByID(ctx, ctx.ParamsInt64("task"), ctx.Doer.ID)
if err != nil {
if admin_model.IsErrTaskDoesNotExist(err) {
ctx.JSON(http.StatusNotFound, map[string]any{
"error": "task `" + strconv.FormatInt(ctx.ParamsInt64("task"), 10) + "` does not exist",
})
return
}
ctx.JSON(http.StatusInternalServerError, map[string]any{
"err": err,
})
return
}
message := task.Message
if task.Message != "" && task.Message[0] == '{' {
// assume message is actually a translatable string
var translatableMessage admin_model.TranslatableMessage
if err := json.Unmarshal([]byte(message), &translatableMessage); err != nil {
translatableMessage = admin_model.TranslatableMessage{
Format: "migrate.migrating_failed.error",
Args: []any{task.Message},
}
}
message = ctx.Locale.TrString(translatableMessage.Format, translatableMessage.Args...)
}
ctx.JSON(http.StatusOK, map[string]any{
"status": task.Status,
"message": message,
"repo-id": task.RepoID,
"repo-name": opts.RepoName,
"start": task.StartTime,
"end": task.EndTime,
})
}

View File

@@ -129,6 +129,8 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
// ensure the session uid is deleted
_ = ctx.Session.Delete("uid")
}
ctx.Csrf.PrepareForSessionUser(ctx)
}
}
@@ -665,7 +667,6 @@ func registerRoutes(m *web.Route) {
m.Get("/forgot_password", auth.ForgotPasswd)
m.Post("/forgot_password", auth.ForgotPasswdPost)
m.Post("/logout", auth.SignOut)
m.Get("/task/{task}", reqSignIn, user.TaskStatus)
m.Get("/stopwatches", reqSignIn, user.GetStopwatches)
m.Get("/search", ignExploreSignIn, user.Search)
m.Group("/oauth2", func() {
@@ -1034,6 +1035,13 @@ func registerRoutes(m *web.Route) {
}, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment())
// end "/{username}/-": packages, projects, code
m.Group("/{username}/{reponame}/-", func() {
m.Group("/migrate", func() {
m.Get("/status", repo.MigrateStatus)
})
}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
// end "/{username}/{reponame}/-": migrate
m.Group("/{username}/{reponame}/settings", func() {
m.Group("", func() {
m.Combo("").Get(repo_setting.Settings).
@@ -1061,9 +1069,7 @@ func registerRoutes(m *web.Route) {
m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch).
Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost)
m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost)
}, repo.MustBeNotEmpty)
m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost)
})
m.Group("/tags", func() {
m.Get("", repo_setting.ProtectedTags)
@@ -1296,6 +1302,7 @@ func registerRoutes(m *web.Route) {
}, web.Bind(forms.NewBranchForm{}))
m.Post("/delete", repo.DeleteBranchPost)
m.Post("/restore", repo.RestoreBranchPost)
m.Post("/rename", web.Bind(forms.RenameBranchForm{}), repo_setting.RenameBranchPost)
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)

View File

@@ -30,10 +30,14 @@ const ProviderHeaderKey = "gitea-oauth2-provider"
// Init initializes the oauth source
func Init(ctx context.Context) error {
if err := InitSigningKey(); err != nil {
return err
// this is for oauth2 provider
if setting.OAuth2.Enabled {
if err := InitSigningKey(); err != nil {
return err
}
}
// others for oauth2 clients
// Lock our mutex
gothRWMutex.Lock()

View File

@@ -35,9 +35,10 @@ type APIContext struct {
ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
Repo *Repository
Org *APIOrganization
Package *Package
Repo *Repository
Org *APIOrganization
Package *Package
PublicOnly bool // Whether the request is for a public endpoint
}
func init() {

View File

@@ -138,10 +138,8 @@ func Contexter() func(next http.Handler) http.Handler {
csrfOpts := CsrfOptions{
Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
Cookie: setting.CSRFCookieName,
SetCookie: true,
Secure: setting.SessionConfig.Secure,
CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
Header: "X-Csrf-Token",
CookieDomain: setting.SessionConfig.Domain,
CookiePath: setting.SessionConfig.CookiePath,
SameSite: setting.SessionConfig.SameSite,
@@ -167,7 +165,7 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Base.AppendContextValue(WebContextKey, ctx)
ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
ctx.Csrf = NewCSRFProtector(csrfOpts)
// Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
@@ -204,8 +202,6 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["SystemConfig"] = setting.Config()
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations

View File

@@ -20,64 +20,42 @@
package context
import (
"encoding/base32"
"fmt"
"html/template"
"net/http"
"strconv"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
)
const (
CsrfHeaderName = "X-Csrf-Token"
CsrfFormName = "_csrf"
)
// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
type CSRFProtector interface {
// GetHeaderName returns HTTP header to search for token.
GetHeaderName() string
// GetFormName returns form value to search for token.
GetFormName() string
// GetToken returns the token.
GetToken() string
// Validate validates the token in http context.
// PrepareForSessionUser prepares the csrf protector for the current session user.
PrepareForSessionUser(ctx *Context)
// Validate validates the csrf token in http context.
Validate(ctx *Context)
// DeleteCookie deletes the cookie
// DeleteCookie deletes the csrf cookie
DeleteCookie(ctx *Context)
}
type csrfProtector struct {
opt CsrfOptions
// Token generated to pass via header, cookie, or hidden form value.
Token string
// This value must be unique per user.
ID string
}
// GetHeaderName returns the name of the HTTP header for csrf token.
func (c *csrfProtector) GetHeaderName() string {
return c.opt.Header
}
// GetFormName returns the name of the form value for csrf token.
func (c *csrfProtector) GetFormName() string {
return c.opt.Form
}
// GetToken returns the current token. This is typically used
// to populate a hidden form in an HTML template.
func (c *csrfProtector) GetToken() string {
return c.Token
// id must be unique per user.
id string
// token is the valid one which wil be used by end user and passed via header, cookie, or hidden form value.
token string
}
// CsrfOptions maintains options to manage behavior of Generate.
type CsrfOptions struct {
// The global secret value used to generate Tokens.
Secret string
// HTTP header used to set and get token.
Header string
// Form value used to set and get token.
Form string
// Cookie value used to set and get token.
Cookie string
// Cookie domain.
@@ -87,103 +65,64 @@ type CsrfOptions struct {
CookieHTTPOnly bool
// SameSite set the cookie SameSite type
SameSite http.SameSite
// Key used for getting the unique ID per user.
SessionKey string
// oldSessionKey saves old value corresponding to SessionKey.
oldSessionKey string
// If true, send token via X-Csrf-Token header.
SetHeader bool
// If true, send token via _csrf cookie.
SetCookie bool
// Set the Secure flag to true on the cookie.
Secure bool
// Disallow Origin appear in request header.
Origin bool
// Cookie lifetime. Default is 0
CookieLifeTime int
// sessionKey is the key used for getting the unique ID per user.
sessionKey string
// oldSessionKey saves old value corresponding to sessionKey.
oldSessionKey string
}
func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
if opt.Secret == "" {
randBytes, err := util.CryptoRandomBytes(8)
if err != nil {
// this panic can be handled by the recover() in http handlers
panic(fmt.Errorf("failed to generate random bytes: %w", err))
}
opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
}
if opt.Header == "" {
opt.Header = "X-Csrf-Token"
}
if opt.Form == "" {
opt.Form = "_csrf"
}
if opt.Cookie == "" {
opt.Cookie = "_csrf"
}
if opt.CookiePath == "" {
opt.CookiePath = "/"
}
if opt.SessionKey == "" {
opt.SessionKey = "uid"
}
if opt.CookieLifeTime == 0 {
opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
}
opt.oldSessionKey = "_old_" + opt.SessionKey
return opt
}
func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie {
return &http.Cookie{
Name: c.opt.Cookie,
Name: opt.Cookie,
Value: value,
Path: c.opt.CookiePath,
Domain: c.opt.CookieDomain,
MaxAge: c.opt.CookieLifeTime,
Secure: c.opt.Secure,
HttpOnly: c.opt.CookieHTTPOnly,
SameSite: c.opt.SameSite,
Path: opt.CookiePath,
Domain: opt.CookieDomain,
MaxAge: int(CsrfTokenTimeout.Seconds()),
Secure: opt.Secure,
HttpOnly: opt.CookieHTTPOnly,
SameSite: opt.SameSite,
}
}
// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
opt = prepareDefaultCsrfOptions(opt)
x := &csrfProtector{opt: opt}
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
return x
func NewCSRFProtector(opt CsrfOptions) CSRFProtector {
if opt.Secret == "" {
panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code
}
opt.Cookie = util.IfZero(opt.Cookie, "_csrf")
opt.CookiePath = util.IfZero(opt.CookiePath, "/")
opt.sessionKey = "uid"
opt.oldSessionKey = "_old_" + opt.sessionKey
return &csrfProtector{opt: opt}
}
x.ID = "0"
uidAny := ctx.Session.Get(opt.SessionKey)
if uidAny != nil {
func (c *csrfProtector) PrepareForSessionUser(ctx *Context) {
c.id = "0"
if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil {
switch uidVal := uidAny.(type) {
case string:
x.ID = uidVal
c.id = uidVal
case int64:
x.ID = strconv.FormatInt(uidVal, 10)
c.id = strconv.FormatInt(uidVal, 10)
default:
log.Error("invalid uid type in session: %T", uidAny)
}
}
oldUID := ctx.Session.Get(opt.oldSessionKey)
uidChanged := oldUID == nil || oldUID.(string) != x.ID
cookieToken := ctx.GetSiteCookie(opt.Cookie)
oldUID := ctx.Session.Get(c.opt.oldSessionKey)
uidChanged := oldUID == nil || oldUID.(string) != c.id
cookieToken := ctx.GetSiteCookie(c.opt.Cookie)
needsNew := true
if uidChanged {
_ = ctx.Session.Set(opt.oldSessionKey, x.ID)
_ = ctx.Session.Set(c.opt.oldSessionKey, c.id)
} else if cookieToken != "" {
// If cookie token presents, re-use existing unexpired token, else generate a new one.
if issueTime, ok := ParseCsrfToken(cookieToken); ok {
dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
x.Token = cookieToken
c.token = cookieToken
needsNew = false
}
}
@@ -191,42 +130,33 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
if needsNew {
// FIXME: actionId.
x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
if opt.SetCookie {
cookie := newCsrfCookie(x, x.Token)
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now())
cookie := newCsrfCookie(&c.opt, c.token)
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
if opt.SetHeader {
ctx.Resp.Header().Add(opt.Header, x.Token)
}
return x
ctx.Data["CsrfToken"] = c.token
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + template.HTMLEscapeString(c.token) + `">`)
}
func (c *csrfProtector) validateToken(ctx *Context, token string) {
if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) {
c.DeleteCookie(ctx)
if middleware.IsAPIPath(ctx.Req) {
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
} else {
ctx.Flash.Error(ctx.Tr("error.invalid_csrf"))
ctx.Redirect(setting.AppSubURL + "/")
}
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
// FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch)
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
}
}
// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
// If this validation fails, custom Error is sent in the reply.
// If neither a header nor form value is found, http.StatusBadRequest is sent.
// If this validation fails, http.StatusBadRequest is sent.
func (c *csrfProtector) Validate(ctx *Context) {
if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" {
if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" {
c.validateToken(ctx, token)
return
}
if token := ctx.Req.FormValue(c.GetFormName()); token != "" {
if token := ctx.Req.FormValue(CsrfFormName); token != "" {
c.validateToken(ctx, token)
return
}
@@ -234,9 +164,7 @@ func (c *csrfProtector) Validate(ctx *Context) {
}
func (c *csrfProtector) DeleteCookie(ctx *Context) {
if c.opt.SetCookie {
cookie := newCsrfCookie(c, "")
cookie.MaxAge = -1
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
cookie := newCsrfCookie(&c.opt, "")
cookie.MaxAge = -1
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}

View File

@@ -607,7 +607,10 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
}
}
isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || ctx.Link == ctx.Repo.RepoLink+"/settings" || strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/")
isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink ||
ctx.Link == ctx.Repo.RepoLink+"/settings" ||
strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/") ||
ctx.Link == ctx.Repo.RepoLink+"/-/migrate/status"
// Disable everything when the repo is being created
if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {

View File

@@ -424,6 +424,7 @@ func ToLFSLock(ctx context.Context, l *git_model.LFSLock) *api.LFSLock {
// ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
status := "changed"
previousFilename := ""
if f.IsDeleted {
status = "deleted"
} else if f.IsCreated {
@@ -432,23 +433,21 @@ func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit stri
status = "copied"
} else if f.IsRenamed && f.Type == gitdiff.DiffFileRename {
status = "renamed"
previousFilename = f.OldName
} else if f.Addition == 0 && f.Deletion == 0 {
status = "unchanged"
}
file := &api.ChangedFile{
Filename: f.GetDiffFileName(),
Status: status,
Additions: f.Addition,
Deletions: f.Deletion,
Changes: f.Addition + f.Deletion,
HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
}
if status == "rename" {
file.PreviousFilename = f.OldName
Filename: f.GetDiffFileName(),
Status: status,
Additions: f.Addition,
Deletions: f.Deletion,
Changes: f.Addition + f.Deletion,
PreviousFilename: previousFilename,
HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
}
return file

View File

@@ -82,43 +82,40 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
return nil
}
attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled {
for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name,
UploaderID: doer.ID,
RepoID: issue.Repo.ID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
log.Info("Skipping disallowed attachment type: %s", attachment.Name)
continue
}
return err
}
attachmentIDs = append(attachmentIDs, a.UUID)
}
}
if content.Content == "" && len(attachmentIDs) == 0 {
return nil
}
switch r := ref.(type) {
case *issues_model.Issue:
attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled {
for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name,
UploaderID: doer.ID,
RepoID: issue.Repo.ID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
log.Info("Skipping disallowed attachment type: %s", attachment.Name)
continue
}
return err
}
attachmentIDs = append(attachmentIDs, a.UUID)
}
}
if content.Content == "" && len(attachmentIDs) == 0 {
return nil
}
_, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
_, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err)
}
case *issues_model.Comment:
comment := r
if content.Content == "" {
return nil
}
if comment.Type == issues_model.CommentTypeCode {
switch comment.Type {
case issues_model.CommentTypeCode:
_, err := pull_service.CreateCodeComment(
ctx,
doer,
@@ -130,11 +127,16 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
false, // not pending review but a single review
comment.ReviewID,
"",
nil,
attachmentIDs,
)
if err != nil {
return fmt.Errorf("CreateCodeComment failed: %w", err)
}
default:
_, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err)
}
}
}
return nil

View File

@@ -24,6 +24,6 @@ func NewMigrationHTTPTransport() *http.Transport {
return &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Migrations.SkipTLSVerify},
Proxy: proxy.Proxy(),
DialContext: hostmatcher.NewDialContext("migration", allowList, blockList),
DialContext: hostmatcher.NewDialContext("migration", allowList, blockList, setting.Proxy.ProxyURLFixed),
}
}

View File

@@ -499,9 +499,5 @@ func Init() error {
// TODO: at the moment, if ALLOW_LOCALNETWORKS=false, ALLOWED_DOMAINS=domain.com, and domain.com has IP 127.0.0.1, then it's still allowed.
// if we want to block such case, the private&loopback should be added to the blockList when ALLOW_LOCALNETWORKS=false
if setting.Proxy.Enabled && setting.Proxy.ProxyURLFixed != nil {
allowList.AppendPattern(setting.Proxy.ProxyURLFixed.Host)
}
return nil
}

View File

@@ -13,7 +13,6 @@ import (
"errors"
"fmt"
"io"
"net/url"
"strings"
"time"
@@ -438,7 +437,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
Archive: pd.FileMetadata.ArchiveSize,
},
Location: Location{
Href: fmt.Sprintf("package/%s/%s/%s/%s", url.PathEscape(pd.Package.Name), url.PathEscape(packageVersion), url.PathEscape(pd.FileMetadata.Architecture), url.PathEscape(fmt.Sprintf("%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture))),
Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture, pd.Package.Name, packageVersion, pd.FileMetadata.Architecture),
},
Format: Format{
License: pd.VersionMetadata.License,

View File

@@ -990,6 +990,8 @@ type CommitInfo struct {
}
// GetPullCommits returns all commits on given pull request and the last review commit sha
// Attention: The last review commit sha must be from the latest review whose commit id is not empty.
// So the type of the latest review cannot be "ReviewTypeRequest".
func GetPullCommits(ctx *gitea_context.Context, issue *issues_model.Issue) ([]CommitInfo, string, error) {
pull := issue.PullRequest
@@ -1035,7 +1037,11 @@ func GetPullCommits(ctx *gitea_context.Context, issue *issues_model.Issue) ([]Co
lastreview, err := issues_model.FindLatestReviews(ctx, issues_model.FindReviewOptions{
IssueID: issue.ID,
ReviewerID: ctx.Doer.ID,
Type: issues_model.ReviewTypeUnknown,
Types: []issues_model.ReviewType{
issues_model.ReviewTypeApprove,
issues_model.ReviewTypeComment,
issues_model.ReviewTypeReject,
},
})
if err != nil && !issues_model.IsErrReviewNotExist(err) {

View File

@@ -348,7 +348,7 @@ func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *is
reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{
ListOptions: db.ListOptionsAll,
IssueID: pull.IssueID,
Type: issues_model.ReviewTypeApprove,
Types: []issues_model.ReviewType{issues_model.ReviewTypeApprove},
Dismissed: optional.Some(false),
})
if err != nil {

View File

@@ -65,7 +65,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
commit, err := gitRepo.GetCommit(rel.Target)
if err != nil {
return false, fmt.Errorf("createTag::GetCommit[%v]: %w", rel.Target, err)
return false, err
}
if len(msg) > 0 {

View File

@@ -483,13 +483,12 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
}
rawBranch, err := git_model.GetBranch(ctx, repo.ID, branchName)
if err != nil {
if err != nil && !git_model.IsErrBranchNotExist(err) {
return fmt.Errorf("GetBranch: %vc", err)
}
if rawBranch.IsDeleted {
return nil
}
// database branch record not exist or it's a deleted branch
notExist := git_model.IsErrBranchNotExist(err) || rawBranch.IsDeleted
commit, err := gitRepo.GetBranchCommit(branchName)
if err != nil {
@@ -497,8 +496,10 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
}
if err := db.WithTx(ctx, func(ctx context.Context) error {
if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil {
return err
if !notExist {
if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, doer.ID); err != nil {
return err
}
}
return gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{

View File

@@ -320,8 +320,9 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
}
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
RepoID: repo.ID,
TagNames: tags,
RepoID: repo.ID,
TagNames: tags,
IncludeTags: true,
})
if err != nil {
return fmt.Errorf("db.Find[repo_model.Release]: %w", err)
@@ -382,12 +383,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
rel, has := relMap[lowerTag]
parts := strings.SplitN(tag.Message, "\n", 2)
note := ""
if len(parts) > 1 {
note = parts[1]
}
if !has {
parts := strings.SplitN(tag.Message, "\n", 2)
note := ""
if len(parts) > 1 {
note = parts[1]
}
rel = &repo_model.Release{
RepoID: repo.ID,
Title: parts[0],
@@ -408,10 +409,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
newReleases = append(newReleases, rel)
} else {
rel.Title = parts[0]
rel.Note = note
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}

View File

@@ -303,7 +303,7 @@ func Init() error {
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify},
Proxy: webhookProxy(allowedHostMatcher),
DialContext: hostmatcher.NewDialContextWithProxy("webhook", allowedHostMatcher, nil, setting.Webhook.ProxyURLFixed),
DialContext: hostmatcher.NewDialContext("webhook", allowedHostMatcher, nil, setting.Webhook.ProxyURLFixed),
},
}

View File

@@ -11,6 +11,7 @@ import (
"net/url"
"strconv"
"strings"
"unicode/utf8"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
@@ -149,8 +150,14 @@ func (d discordConvertor) Push(p *api.PushPayload) (DiscordPayload, error) {
var text string
// for each commit, generate attachment text
for i, commit := range p.Commits {
text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
// limit the commit message display to just the summary, otherwise it would be hard to read
message := strings.TrimRight(strings.SplitN(commit.Message, "\n", 1)[0], "\r")
// a limit of 50 is set because GitHub does the same
if utf8.RuneCountInString(message) > 50 {
message = fmt.Sprintf("%.47s...", message)
}
text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, message, commit.Author.Name)
// add linebreak to each commit but the last
if i < len(p.Commits)-1 {
text += "\n"

View File

@@ -80,6 +80,20 @@ func TestDiscordPayload(t *testing.T) {
assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL)
})
t.Run("PushWithLongCommitMessage", func(t *testing.T) {
p := pushTestMultilineCommitMessagePayload()
pl, err := dc.Push(p)
require.NoError(t, err)
assert.Len(t, pl.Embeds, 1)
assert.Equal(t, "[test/repo:test] 2 new commits", pl.Embeds[0].Title)
assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好... - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好... - user1", pl.Embeds[0].Description)
assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL)
assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL)
})
t.Run("Issue", func(t *testing.T) {
p := issueTestPayload()

View File

@@ -64,9 +64,17 @@ func forkTestPayload() *api.ForkPayload {
}
func pushTestPayload() *api.PushPayload {
return pushTestPayloadWithCommitMessage("commit message")
}
func pushTestMultilineCommitMessagePayload() *api.PushPayload {
return pushTestPayloadWithCommitMessage("This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好 ⚠️⚠️️\n\nThis is the message body.")
}
func pushTestPayloadWithCommitMessage(message string) *api.PushPayload {
commit := &api.PayloadCommit{
ID: "2020558fe2e34debb818a514715839cabd25e778",
Message: "commit message",
Message: message,
URL: "http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778",
Author: &api.PayloadUser{
Name: "user1",

View File

@@ -124,7 +124,7 @@
</div>
<div class="inline required field">
<label for="domain">{{ctx.Locale.Tr "install.domain"}}</label>
<input id="domain" name="domain" value="{{.domain}}" placeholder="try.gitea.io" required>
<input id="domain" name="domain" value="{{.domain}}" placeholder="demo.gitea.com" required>
<span class="help">{{ctx.Locale.Tr "install.domain_helper"}}</span>
</div>
<div class="inline field">
@@ -139,7 +139,7 @@
</div>
<div class="inline required field">
<label for="app_url">{{ctx.Locale.Tr "install.app_url"}}</label>
<input id="app_url" name="app_url" value="{{.app_url}}" placeholder="https://try.gitea.io" required>
<input id="app_url" name="app_url" value="{{.app_url}}" placeholder="https://demo.gitea.com" required>
<span class="help">{{ctx.Locale.Tr "install.app_url_helper"}}</span>
</div>
<div class="inline required field">

View File

@@ -236,7 +236,7 @@
<div class="header">
{{ctx.Locale.Tr "repo.settings.rename_branch"}}
</div>
<form class="ui form" action="{{$.Repository.Link}}/settings/rename_branch" method="post">
<form class="ui form" action="{{$.Repository.Link}}/branches/rename" method="post">
<div class="content">
{{.CsrfTokenHtml}}
<div class="field default-branch-warning">

View File

@@ -1,9 +1,9 @@
<div class="ui secondary filter menu">
{{if not .Repository.IsArchived}}
<!-- Action Button -->
{{if .IsShowClosed}}
{{if and .IsShowClosed.Has .IsShowClosed.Value}}
<button class="ui primary basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status">{{ctx.Locale.Tr "repo.issues.action_open"}}</button>
{{else}}
{{else if and .IsShowClosed.Has (not .IsShowClosed.Value)}}
<button class="ui red basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status">{{ctx.Locale.Tr "repo.issues.action_close"}}</button>
{{end}}
{{if $.IsRepoAdmin}}

View File

@@ -7,7 +7,7 @@
{{template "base/alert" .}}
<div class="home">
<div class="ui stackable middle very relaxed page grid">
<div id="repo_migrating" class="sixteen wide center aligned centered column" data-migrating-task-id="{{.MigrateTask.ID}}">
<div id="repo_migrating" class="sixteen wide center aligned centered column" data-migrating-repo-link="{{.Link}}">
<div>
<img src="{{AssetUrlPrefix}}/img/loading.png">
</div>

View File

@@ -15,19 +15,17 @@
<form class="tw-flex" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="default_branch">
{{if not .Repository.IsEmpty}}
<div class="ui dropdown selection search tw-flex-1 tw-mr-2 tw-max-w-96">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<input type="hidden" name="branch" value="{{.Repository.DefaultBranch}}">
<div class="default text">{{.Repository.DefaultBranch}}</div>
<div class="menu">
{{range .Branches}}
<div class="item" data-value="{{.}}">{{.}}</div>
{{end}}
</div>
<div class="ui dropdown selection search tw-flex-1 tw-mr-2 tw-max-w-96">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<input type="hidden" name="branch" value="{{.Repository.DefaultBranch}}">
<div class="default text">{{.Repository.DefaultBranch}}</div>
<div class="menu">
{{range .Branches}}
<div class="item" data-value="{{.}}">{{.}}</div>
{{end}}
</div>
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.branches.update_default_branch"}}</button>
{{end}}
</div>
<button class="ui primary button"{{if .Repository.IsEmpty}} disabled{{end}}>{{ctx.Locale.Tr "repo.settings.branches.update_default_branch"}}</button>
</form>
</div>

View File

@@ -13,11 +13,9 @@
</a>
{{end}}
{{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeCode}}
{{if not .Repository.IsEmpty}}
<a class="{{if .PageIsSettingsBranches}}active {{end}}item" href="{{.RepoLink}}/settings/branches">
{{ctx.Locale.Tr "repo.settings.branches"}}
</a>
{{end}}
<a class="{{if .PageIsSettingsBranches}}active {{end}}item" href="{{.RepoLink}}/settings/branches">
{{ctx.Locale.Tr "repo.settings.branches"}}
</a>
<a class="{{if .PageIsSettingsTags}}active {{end}}item" href="{{.RepoLink}}/settings/tags">
{{ctx.Locale.Tr "repo.settings.tags"}}
</a>

View File

@@ -5386,7 +5386,7 @@
"tags": [
"repository"
],
"summary": "Get the pull request of the commit",
"summary": "Get the merged pull request of the commit",
"operationId": "repoGetCommitPullRequest",
"parameters": [
{

View File

@@ -75,6 +75,34 @@ func TestAPIListIssues(t *testing.T) {
}
}
func TestAPIListIssuesPublicOnly(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
owner1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo1.OwnerID})
session := loginUser(t, owner1.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue)
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner1.Name, repo1.Name))
link.RawQuery = url.Values{"state": {"all"}}.Encode()
req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
owner2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID})
session = loginUser(t, owner2.Name)
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue)
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner2.Name, repo2.Name))
link.RawQuery = url.Values{"state": {"all"}}.Encode()
req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopePublicOnly)
req = NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken)
MakeRequest(t, req, http.StatusForbidden)
}
func TestAPICreateIssue(t *testing.T) {
defer tests.PrepareTestEnv(t)()
const body, title = "apiTestBody", "apiTestTitle"
@@ -243,6 +271,12 @@ func TestAPISearchIssues(t *testing.T) {
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, expectedIssueCount)
publicOnlyToken := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopePublicOnly)
req = NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, 15) // 15 public issues
since := "2000-01-01T00:50:01+00:00" // 946687801
before := time.Unix(999307200, 0).Format(time.RFC3339)
query.Add("since", since)

View File

@@ -36,6 +36,7 @@ func TestPackageComposer(t *testing.T) {
packageType := "composer-plugin"
packageAuthor := "Gitea Authors"
packageLicense := "MIT"
packageBin := "./bin/script"
var buf bytes.Buffer
archive := zip.NewWriter(&buf)
@@ -49,6 +50,9 @@ func TestPackageComposer(t *testing.T) {
{
"name": "` + packageAuthor + `"
}
],
"bin": [
"` + packageBin + `"
]
}`))
archive.Close()
@@ -210,6 +214,8 @@ func TestPackageComposer(t *testing.T) {
assert.Len(t, pkgs[0].Authors, 1)
assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name)
assert.Equal(t, "zip", pkgs[0].Dist.Type)
assert.Equal(t, "7b40bfd6da811b2b78deec1e944f156dbb2c747b", pkgs[0].Dist.Checksum)
assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum)
assert.Len(t, pkgs[0].Bin, 1)
assert.Equal(t, packageBin, pkgs[0].Bin[0])
})
}

View File

@@ -38,7 +38,7 @@ func TestAPIPullReview(t *testing.T) {
var reviews []*api.PullReview
DecodeJSON(t, resp, &reviews)
if !assert.Len(t, reviews, 6) {
if !assert.Len(t, reviews, 8) {
return
}
for _, r := range reviews {

View File

@@ -334,3 +334,19 @@ func doAPIGetPullFiles(ctx APITestContext, pr *api.PullRequest, callback func(*t
}
}
}
func TestAPICommitPullRequest(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository)
mergedCommitSHA := "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3"
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/commits/%s/pull", owner.Name, repo.Name, mergedCommitSHA).AddTokenAuth(ctx.Token)
ctx.Session.MakeRequest(t, req, http.StatusOK)
invalidCommitSHA := "abcd1234abcd1234abcd1234abcd1234abcd1234"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/commits/%s/pull", owner.Name, repo.Name, invalidCommitSHA).AddTokenAuth(ctx.Token)
ctx.Session.MakeRequest(t, req, http.StatusNotFound)
}

View File

@@ -208,6 +208,24 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
}
func TestAPICreateReleaseGivenInvalidTarget(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
session := loginUser(t, owner.LowerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases", owner.Name, repo.Name)
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateReleaseOption{
TagName: "i-point-to-an-invalid-target",
Title: "Invalid Target",
Target: "invalid-target",
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
}
func TestAPIGetLatestRelease(t *testing.T) {
defer tests.PrepareTestEnv(t)()

View File

@@ -28,9 +28,13 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
session := loginUser(t, user1.LowerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
// public only token should be forbidden
publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopePublicOnly, auth_model.AccessTokenScopeWriteRepository)
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches", repo3.Name)) // a plain repo
MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
resp := MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
@@ -42,6 +46,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
assert.EqualValues(t, "master", branches[1].Name)
link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch", repo3.Name))
MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden)
resp = MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(token), http.StatusOK)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
@@ -49,6 +55,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
assert.NoError(t, json.Unmarshal(bs, &branch))
assert.EqualValues(t, "test_branch", branch.Name)
MakeRequest(t, NewRequest(t, "POST", link.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden)
req := NewRequest(t, "POST", link.String()).AddTokenAuth(token)
req.Header.Add("Content-Type", "application/json")
req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
@@ -73,6 +81,7 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
link3, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch2", repo3.Name))
MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNotFound)
MakeRequest(t, NewRequest(t, "DELETE", link3.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden)
MakeRequest(t, NewRequest(t, "DELETE", link3.String()).AddTokenAuth(token), http.StatusNoContent)
assert.NoError(t, err)

View File

@@ -23,10 +23,10 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
newAccessToken := createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user, nil)
newAccessToken := createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
deleteAPIAccessToken(t, newAccessToken, user)
newAccessToken = createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user, nil)
newAccessToken = createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
deleteAPIAccessToken(t, newAccessToken, user)
}
@@ -72,19 +72,19 @@ func TestAPIDeleteTokensPermission(t *testing.T) {
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
// admin can delete tokens for other users
createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user2, nil)
createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user2, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
req := NewRequest(t, "DELETE", "/api/v1/users/"+user2.LoginName+"/tokens/test-key-1").
AddBasicAuth(admin.Name)
MakeRequest(t, req, http.StatusNoContent)
// non-admin can delete tokens for himself
createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user2, nil)
createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user2, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
req = NewRequest(t, "DELETE", "/api/v1/users/"+user2.LoginName+"/tokens/test-key-2").
AddBasicAuth(user2.Name)
MakeRequest(t, req, http.StatusNoContent)
// non-admin can't delete tokens for other users
createAPIAccessTokenWithoutCleanUp(t, "test-key-3", user2, nil)
createAPIAccessTokenWithoutCleanUp(t, "test-key-3", user2, []auth_model.AccessTokenScope{auth_model.AccessTokenScopeAll})
req = NewRequest(t, "DELETE", "/api/v1/users/"+user2.LoginName+"/tokens/test-key-3").
AddBasicAuth(user4.Name)
MakeRequest(t, req, http.StatusForbidden)
@@ -520,7 +520,7 @@ func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model
unauthorizedScopes = append(unauthorizedScopes, cateogoryUnauthorizedScopes...)
}
accessToken := createAPIAccessTokenWithoutCleanUp(t, "test-token", user, &unauthorizedScopes)
accessToken := createAPIAccessTokenWithoutCleanUp(t, "test-token", user, unauthorizedScopes)
defer deleteAPIAccessToken(t, accessToken, user)
// Request the endpoint. Verify that permission is denied.
@@ -532,20 +532,12 @@ func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model
// createAPIAccessTokenWithoutCleanUp Create an API access token and assert that
// creation succeeded. The caller is responsible for deleting the token.
func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *user_model.User, scopes *[]auth_model.AccessTokenScope) api.AccessToken {
func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *user_model.User, scopes []auth_model.AccessTokenScope) api.AccessToken {
payload := map[string]any{
"name": tokenName,
}
if scopes != nil {
for _, scope := range *scopes {
scopes, scopesExists := payload["scopes"].([]string)
if !scopesExists {
scopes = make([]string, 0)
}
scopes = append(scopes, string(scope))
payload["scopes"] = scopes
}
"name": tokenName,
"scopes": scopes,
}
log.Debug("Requesting creation of token with scopes: %v", scopes)
req := NewRequestWithJSON(t, "POST", "/api/v1/users/"+user.LoginName+"/tokens", payload).
AddBasicAuth(user.Name)
@@ -563,8 +555,7 @@ func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *us
return newAccessToken
}
// createAPIAccessTokenWithoutCleanUp Delete an API access token and assert that
// deletion succeeded.
// deleteAPIAccessToken deletes an API access token and assert that deletion succeeded.
func deleteAPIAccessToken(t *testing.T, accessToken api.AccessToken, user *user_model.User) {
req := NewRequestf(t, "DELETE", "/api/v1/users/"+user.LoginName+"/tokens/%d", accessToken.ID).
AddBasicAuth(user.Name)

View File

@@ -38,6 +38,19 @@ func TestAPIUserSearchLoggedIn(t *testing.T) {
assert.Contains(t, user.UserName, query)
assert.NotEmpty(t, user.Email)
}
publicToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopePublicOnly)
req = NewRequestf(t, "GET", "/api/v1/users/search?q=%s", query).
AddTokenAuth(publicToken)
resp = MakeRequest(t, req, http.StatusOK)
results = SearchResults{}
DecodeJSON(t, resp, &results)
assert.NotEmpty(t, results.Data)
for _, user := range results.Data {
assert.Contains(t, user.UserName, query)
assert.NotEmpty(t, user.Email)
assert.True(t, user.Visibility == "public")
}
}
func TestAPIUserSearchNotLoggedIn(t *testing.T) {

View File

@@ -29,7 +29,7 @@ func generateImg() bytes.Buffer {
return buff
}
func createAttachment(t *testing.T, session *TestSession, repoURL, filename string, buff bytes.Buffer, expectedStatus int) string {
func createAttachment(t *testing.T, session *TestSession, csrf, repoURL, filename string, buff bytes.Buffer, expectedStatus int) string {
body := &bytes.Buffer{}
// Setup multi-part
@@ -41,8 +41,6 @@ func createAttachment(t *testing.T, session *TestSession, repoURL, filename stri
err = writer.Close()
assert.NoError(t, err)
csrf := GetCSRF(t, session, repoURL)
req := NewRequestWithBody(t, "POST", repoURL+"/issues/attachments", body)
req.Header.Add("X-Csrf-Token", csrf)
req.Header.Add("Content-Type", writer.FormDataContentType())
@@ -59,14 +57,14 @@ func createAttachment(t *testing.T, session *TestSession, repoURL, filename stri
func TestCreateAnonymousAttachment(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := emptyTestSession(t)
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusSeeOther)
createAttachment(t, session, GetCSRF(t, session, "/user/login"), "user2/repo1", "image.png", generateImg(), http.StatusSeeOther)
}
func TestCreateIssueAttachment(t *testing.T) {
defer tests.PrepareTestEnv(t)()
const repoURL = "user2/repo1"
session := loginUser(t, "user2")
uuid := createAttachment(t, session, repoURL, "image.png", generateImg(), http.StatusOK)
uuid := createAttachment(t, session, GetCSRF(t, session, repoURL), repoURL, "image.png", generateImg(), http.StatusOK)
req := NewRequest(t, "GET", repoURL+"/issues/new")
resp := session.MakeRequest(t, req, http.StatusOK)

View File

@@ -5,12 +5,10 @@ package integration
import (
"net/http"
"strings"
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -25,28 +23,12 @@ func TestCsrfProtection(t *testing.T) {
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": "fake_csrf",
})
session.MakeRequest(t, req, http.StatusSeeOther)
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
// test web form csrf via header. TODO: should use an UI api to test
req = NewRequest(t, "POST", "/user/settings")
req.Header.Add("X-Csrf-Token", "fake_csrf")
session.MakeRequest(t, req, http.StatusSeeOther)
resp = session.MakeRequest(t, req, http.StatusSeeOther)
loc = resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp = session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}

View File

@@ -7,6 +7,7 @@ import (
"io"
"net"
"net/smtp"
"net/url"
"strings"
"testing"
"time"
@@ -26,187 +27,190 @@ import (
)
func TestIncomingEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onGiteaRun(t, func(t *testing.T, u *url.URL) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
t.Run("Payload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Payload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
_, err := incoming_payload.CreateReferencePayload(user)
assert.Error(t, err)
_, err := incoming_payload.CreateReferencePayload(user)
assert.Error(t, err)
issuePayload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
issuePayload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
_, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3})
assert.Error(t, err)
_, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3})
assert.Error(t, err)
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
})
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
})
t.Run("Token", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Token", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
payload := []byte{1, 2, 3, 4, 5}
payload := []byte{1, 2, 3, 4, 5}
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NotEmpty(t, token)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NotEmpty(t, token)
ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token)
assert.NoError(t, err)
assert.Equal(t, token_service.ReplyHandlerType, ht)
assert.Equal(t, user.ID, u.ID)
assert.Equal(t, payload, p)
})
ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token)
assert.NoError(t, err)
assert.Equal(t, token_service.ReplyHandlerType, ht)
assert.Equal(t, user.ID, u.ID)
assert.Equal(t, payload, p)
})
t.Run("Handler", func(t *testing.T) {
t.Run("Reply", func(t *testing.T) {
t.Run("Comment", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("Handler", func(t *testing.T) {
t.Run("Reply", func(t *testing.T) {
t.Run("Comment", func(t *testing.T) {
handler := &incoming.ReplyHandler{}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
})
t.Run("CodeComment", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
handler := &incoming.ReplyHandler{}
content := &incoming.MailContent{
Content: "code reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
payload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeCode,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment = comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
})
})
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
handler := &incoming.ReplyHandler{}
watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
assert.False(t, watching)
})
})
t.Run("CodeComment", func(t *testing.T) {
if setting.IncomingEmail.Enabled {
// This test connects to the configured email server and is currently only enabled for MySql integration tests.
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails.
t.Run("IMAP", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
handler := &incoming.ReplyHandler{}
content := &incoming.MailContent{
Content: "code reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
payload, err := incoming_payload.CreateReferencePayload(comment)
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeCode,
})
msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
msg.SetHeader("From", user.Email)
msg.SetBody("text/plain", token)
err = gomail.Send(&smtpTestSender{}, msg)
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment = comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Empty(t, comment.Attachments)
assert.Eventually(t, func() bool {
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
return comment.PosterID == user.ID && comment.Content == token
}, 10*time.Second, 1*time.Second)
})
})
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.False(t, watching)
})
}
})
if setting.IncomingEmail.Enabled {
// This test connects to the configured email server and is currently only enabled for MySql integration tests.
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails.
t.Run("IMAP", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
msg.SetHeader("From", user.Email)
msg.SetBody("text/plain", token)
err = gomail.Send(&smtpTestSender{}, msg)
assert.NoError(t, err)
assert.Eventually(t, func() bool {
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
return comment.PosterID == user.ID && comment.Content == token
}, 10*time.Second, 1*time.Second)
})
}
}
// A simple SMTP mail sender used for integration tests.

View File

@@ -37,6 +37,7 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/xeipuuv/gojsonschema"
)
@@ -486,12 +487,16 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile
}
// GetCSRF returns CSRF token from body
// If it fails, it means the CSRF token is not found in the response body returned by the url with the given session.
// In this case, you should find a better url to get it.
func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
t.Helper()
req := NewRequest(t, "GET", urlStr)
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
return doc.GetCSRF()
csrf := doc.GetCSRF()
require.NotEmpty(t, csrf)
return csrf
}
// GetCSRFFrom returns CSRF token from body

View File

@@ -204,9 +204,7 @@ func TestTeamSearch(t *testing.T) {
var results TeamSearchResults
session := loginUser(t, user.Name)
csrf := GetCSRF(t, session, "/"+org.Name)
req := NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "_team")
req.Header.Add("X-Csrf-Token", csrf)
resp := session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &results)
assert.NotEmpty(t, results.Data)
@@ -217,8 +215,6 @@ func TestTeamSearch(t *testing.T) {
// no access if not organization member
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
session = loginUser(t, user5.Name)
csrf = GetCSRF(t, session, "/"+org.Name)
req = NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "team")
req.Header.Add("X-Csrf-Token", csrf)
session.MakeRequest(t, req, http.StatusNotFound)
}

View File

@@ -0,0 +1,34 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"net/http"
"net/url"
"testing"
pull_service "code.gitea.io/gitea/services/pull"
"github.com/stretchr/testify/assert"
)
func TestListPullCommits(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user5")
req := NewRequest(t, "GET", "/user2/repo1/pulls/3/commits/list")
resp := session.MakeRequest(t, req, http.StatusOK)
var pullCommitList struct {
Commits []pull_service.CommitInfo `json:"commits"`
LastReviewCommitSha string `json:"last_review_commit_sha"`
}
DecodeJSON(t, resp, &pullCommitList)
if assert.Len(t, pullCommitList.Commits, 2) {
assert.Equal(t, "5f22f7d0d95d614d25a5b68592adb345a4b5c7fd", pullCommitList.Commits[0].ID)
assert.Equal(t, "4a357436d925b5c974181ff12a994538ddc5a269", pullCommitList.Commits[1].ID)
}
assert.Equal(t, "4a357436d925b5c974181ff12a994538ddc5a269", pullCommitList.LastReviewCommitSha)
})
}

View File

@@ -28,11 +28,11 @@ func testRenameBranch(t *testing.T, u *url.URL) {
// get branch setting page
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user2/repo1/settings/branches")
req := NewRequest(t, "GET", "/user2/repo1/branches")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/rename", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"from": "master",
"to": "main",
@@ -76,7 +76,7 @@ func testRenameBranch(t *testing.T, u *url.URL) {
assert.Equal(t, "branch2", branch2.Name)
// rename branch2 to branch1
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/rename", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"from": "branch2",
"to": "branch1",
@@ -103,7 +103,7 @@ func testRenameBranch(t *testing.T, u *url.URL) {
assert.True(t, branch1.IsDeleted) // virtual deletion
// rename branch2 to branch1 again
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/rename", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"from": "branch2",
"to": "branch1",

View File

@@ -17,7 +17,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
@@ -146,15 +145,8 @@ func TestCreateBranchInvalidCSRF(t *testing.T) {
"_csrf": "fake_csrf",
"new_branch_name": "test",
})
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t,
"Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}
func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) {

View File

@@ -4,6 +4,7 @@
package integration
import (
"net/http"
"net/url"
"testing"
@@ -18,6 +19,7 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateNewTagProtected(t *testing.T) {
@@ -60,6 +62,40 @@ func TestCreateNewTagProtected(t *testing.T) {
})
})
t.Run("GitTagForce", func(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
httpContext := NewAPITestContext(t, owner.Name, repo.Name)
dstPath := t.TempDir()
u.Path = httpContext.GitPath()
u.User = url.UserPassword(owner.Name, userPassword)
doGitClone(dstPath, u)(t)
_, _, err := git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update v2", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
require.Error(t, err)
assert.Contains(t, err.Error(), "the tag already exists in the remote")
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
req := NewRequestf(t, "GET", "/%s/releases/tag/v-1.1", repo.FullName())
resp := MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
tagsTab := htmlDoc.Find(".release-list-title")
assert.Contains(t, tagsTab.Text(), "force update v2")
})
})
// Cleanup
releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{
IncludeTags: true,

View File

@@ -78,7 +78,6 @@ const sfc = {
searchURL() {
return `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery
}&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode
}${this.reposFilter !== 'all' ? '&exclusive=1' : ''
}${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : ''
}${this.privateFilter === 'private' ? '&is_private=true' : ''}${this.privateFilter === 'public' ? '&is_private=false' : ''
}`;

View File

@@ -60,7 +60,7 @@ export function initRepoTopicBar() {
// how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save"
const responseData = await response.json();
lastErrorToast = showErrorToast(responseData.message, {duration: 5000});
if (responseData.invalidTopics.length > 0) {
if (responseData.invalidTopics && responseData.invalidTopics.length > 0) {
const {invalidTopics} = responseData;
const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label');
for (const [index, value] of topics.split(',').entries()) {

View File

@@ -1,19 +1,17 @@
import {hideElem, showElem} from '../utils/dom.js';
import {GET, POST} from '../modules/fetch.js';
const {appSubUrl} = window.config;
export function initRepoMigrationStatusChecker() {
const repoMigrating = document.getElementById('repo_migrating');
if (!repoMigrating) return;
document.getElementById('repo_migrating_retry').addEventListener('click', doMigrationRetry);
document.getElementById('repo_migrating_retry')?.addEventListener('click', doMigrationRetry);
const task = repoMigrating.getAttribute('data-migrating-task-id');
const repoLink = repoMigrating.getAttribute('data-migrating-repo-link');
// returns true if the refresh still needs to be called after a while
const refresh = async () => {
const res = await GET(`${appSubUrl}/user/task/${task}`);
const res = await GET(`${repoLink}/-/migrate/status`);
if (res.status !== 200) return true; // continue to refresh if network error occurs
const data = await res.json();