Compare commits

...

62 commits

Author SHA1 Message Date
9041ed88bd gitea.nulo.in: Use Alpine 3.16
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-07-31 13:07:55 +02:00
a5e9498f21 Add .woodpecker.yml for gitea.nulo.in 2022-07-31 13:07:55 +02:00
aa3dd45907 Allow hidden .READMEs 2022-07-31 13:07:55 +02:00
6543
8769df117d
Changelog v1.17.0 (#20541) 2022-07-30 21:08:25 +02:00
6543
09f2e1e1a2
WebAuthn CredentialID field needs to be increased in size (#20530) (#20555)
WebAuthn have updated their specification to set the maximum size of the
CredentialID to 1023 bytes. This is somewhat larger than our current
size and therefore we need to migrate.

The PR changes the struct to add CredentialIDBytes and migrates the CredentialID string
to the bytes field before another migration drops the old CredentialID field. Another migration
renames this field back.

Fix #20457

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: zeripath <art27@cantab.net>
2022-07-30 20:16:25 +02:00
silverwind
eeb490c7ab
Rework raw file http header logic (#20484) (#20542)
- Always respect the user's configured mime type map
- Allow more types like image/pdf/video/audio to serve with correct content-type
- Shorten cache duration of raw files to 5 minutes, matching GitHub
- Don't set `content-disposition: attachment`, let the browser decide whether it wants to download or display a file directly
- Implement rfc5987 for filenames, remove previous hack. Confirmed it working in Safari.
- Make PDF attachment work in Safari by removing `sandbox` attribute.

This change will make a lot more file types open directly in browser now. Logic should generally be more readable than before with less `if` nesting and such.

Replaces: https://github.com/go-gitea/gitea/pull/20460
Replaces: https://github.com/go-gitea/gitea/pull/20455
Fixes: https://github.com/go-gitea/gitea/issues/20404
2022-07-30 18:37:02 +02:00
6543
97a8c96c5b
Add Docker /v2/_catalog endpoint (#20469) (#20556)
* Added properties for packages.
* Fixed authenticate header format.
* Added _catalog endpoint.
* Check owner visibility.
* Extracted condition.
* Added test for _catalog.

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-07-30 17:52:04 +02:00
Gusted
d1e53bfd7f
Update notification count for non-mobile version (#20544)
- Since #20108 we have two version of the notification bell, one for
mobile the other for non-mobile. However the code only accounts for one
notification count and thus was only updating the non-mobile one.
- This code fixes that by applying the code for all `.notification_count`s.
- Frontport will be in #20543
2022-07-30 10:28:48 +08:00
6543
fc7b5afd9b
Add missing Tabs on organisation/package view (#20539)
hotfix #20106
2022-07-29 19:14:50 +02:00
6543
210b096da7
Ensure that all unmerged files are merged when conflict checking (#20528) (#20536)
There is a subtle bug in the code relating to collating the results of
`git ls-files -u -z` in `unmergedFiles()`. The code here makes the
mistake of assuming that every unmerged file will always have a stage 1
conflict, and this results in conflicts that occur in stage 3 only being
dropped.

This PR simply adjusts this code to ensure that any empty unmergedFile
will always be passed down the channel.

The PR also adds a lot of Trace commands to attempt to help find future
bugs in this code.

Fix #19527

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: zeripath <art27@cantab.net>
2022-07-29 15:37:18 +02:00
6543
d6bc1558c6
Update lunny/levelqueue to prevent NPE when reads are performed after close (#20534) (#20537)
Co-authored-by: zeripath <art27@cantab.net>
2022-07-29 20:58:56 +08:00
zeripath
6986e56791
Stop logging EOFs and exit(1)s in ssh handler (#20476) (#20529)
Backport #20476

The code in modules/ssh/ssh.go:sessionHandler() currently cause an error to be
logged if `gitea serv` exits with a exit(1). This logging is useless because the
accompanying stderr is not provided and in any case the exit(1) is most likely due
to permissions errors.

Further it then causes the EOF to be logged - even though this is not helpful.

This PR simply checks the errors returned and stops logging them.

In the case of misconfigurations causing `gitea serv` to fail with exit(1)
the current logging is not helpful at determining this and users should simply
review the message passed over the ssh connection.

Fix #20473

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-07-29 02:10:42 +02:00
6543
ae86a0bc9f
packages/generic: Do not restrict package versions to SemVer (#20414) (#20531)
There are existing packages out there whose version do not conform to SemVer, yet, one would like to have them available in a generic package repository. To this end, remove the SemVer restriction on package versions when using the Generic package registry, and replace it with a check that simply makes sure the version isn't empty.

Signed-off-by: Gergely Nagy <me@gergo.csillger.hu>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Gergely Nagy <algernon@users.noreply.github.com>
2022-07-29 01:17:56 +02:00
6543
4b53a5c3a1
Add labels to two buttons that were missing them (#20419) (#20524)
Backport #20419

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-07-28 21:21:36 +01:00
6543
da10ce8b07
Allow non-semver packages in the Conan package registry (#20412) (#20523)
Backport #20412

A lot of existing packages do not conform to SemVer, yet, they should be allowed
in the Conan package registry as-is. To achieve this, remove the SemVer check
from `NewRecipeReference`, and replace it with a simple empty string check.

A unit test with a non-semver version is also included.

Fixes #20405.

Signed-off-by: Gergely Nagy <me@gergo.csillger.hu>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: Gergely Nagy <algernon@users.noreply.github.com>
2022-07-28 21:19:56 +01:00
6543
4ed32e79b6
Fix possible panic when repository is empty (#20509) (#20526)
Backport #20509
2022-07-28 21:18:49 +01:00
6543
fa46d66835
Fix Ruby package parsing by removed unused email field (#20470) (#20525)
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2022-07-28 20:41:57 +02:00
Gusted
648ec3cfed
Fix dashboard switching on Mobile (#20238) (#20239)
- This is a regression of improving mobile experience on Gitea, currently organization dashboard aren't readable and the popup won't show up when you want to switch between users/organization(as we saw in #19978). 
- This patch fixes that, by allowing the popup to allocate the required pixels(for some absurd reason, z-index doesn't work on the popup, so it's not able to render over the existing elements, we can investigate later of why this is). And also remove the additional dropdown menu for the pages link, so it's one unified list which then can be displayed as rows.
2022-07-28 19:04:29 +02:00
KN4CK3R
a9a440e600
Fix package permission checks for organizations (#20517) (#20520) 2022-07-28 22:05:59 +08:00
wxiaoguang
39b2ede930
Fix ROOT_URL detection for URLs without trailing slash (#20503) 2022-07-27 23:49:01 +08:00
Norwin
0a32bd56eb
Show hint to link package to repo when viewing empty repo package list (#20504) (#20507) 2022-07-27 10:06:54 -04:00
Tyrone Yeh
e0f35ea00f
Modify milestone search keywords to be case insensitive (#20266) (#20498)
* Modify milestone search keywords to be case insensitive (#20266)

Milestone search keywords are now sensitive, this modification is changed to insensitive

* Modify for #18437

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-07-27 19:55:09 +08:00
Tyrone Yeh
bed13bfa9e
Add repository condition for issue count (#20496) 2022-07-27 07:09:25 +01:00
Norwin
435038b2c6
fix enabling repo packages when projects are off (#20486) (#20488) 2022-07-26 16:50:45 +02:00
silverwind
2fe0dab2d5
Add Cache-Control header to html and api responses, add no-transform (#20432) (#20459)
`no-transform` allegedly disables CloudFlare auto-minify and we did not
set caching headers on html or api requests, which seems good to have
regardless.

Transformation is still allowed for asset requests.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Andrew Thornton <art27@cantab.net>
2022-07-23 11:58:58 +01:00
6543
e930d66a9c
Dismiss prior pull reviews if done via web in review dismiss (#20197) (#20407) 2022-07-19 17:46:33 +02:00
6543
2c93bd79f2
Changelog v1.17.0-rc2 (#20350)
Co-authored-by: zeripath <art27@cantab.net>
2022-07-19 15:43:24 +02:00
zeripath
90b4a9e929
Allow RSA 2047 bit keys (#20272) (#20396)
Backport #20272

Unfortunately it appears that 2048 bit RSA keys can occasionally be created in such
a way that they appear to have 2047 bit length. This PR simply changes our defaults to
allow these.

Fix #20249

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-07-18 10:30:01 -04:00
zeripath
c16f0d2a19
Add missing return for when topic isn't found (#20351) (#20395) 2022-07-18 10:16:58 +08:00
silverwind
3f5d72709f
Fix commit status icon when in subdirectory (#20285) (#20385)
When viewing a subdirectory and the latest commit to that directory in
the table, the commit status icon incorrectly showed the status of the
HEAD commit instead of the latest for that directory.
2022-07-16 14:54:49 +02:00
Gusted
95a27eb662
Set target on create release with existing tag (#20381) (#20382)
When you create a new release(e.g. via Tea) and specify a tag that already exists on the repository, Gitea will instead use the `UpdateRelease` functionality. However it currently doesn't set the Target field. This PR fixes that.
2022-07-15 20:39:48 +02:00
zeripath
c91b8c8089
Initialize cron last (#20373) (#20384)
Backport #20373

Cron will try to run certain things at startup but these depend on multiple things
being set-up. Therefore we should initialize cron last.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-07-15 13:44:22 -04:00
6543
975a962a2f
Make sure repo_dir is an empty directory or doesn't exist before 'dump-repo' (#20205) (#20370)
Co-authored-by: a1012112796 <1012112796@qq.com>
2022-07-15 11:12:52 +08:00
6543
4c1f4ee84c
update xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f (#20371) (#20372)
Backport #20371

Xorm 1.3.2-0.20220714055524 contains a fix for interpreting db column sizes. Prior to this fix xorm would assume that the size of a column was within the range of an `int`. This is correct on 64bit machines where `int` is typical equivalent to `int64` however, on 32bit machines `int` tends to be `int32`. 

Unfortunately the size of a LONGTEXT field is actually `max_uint32`, thus using `strconv.Atoi` on these fields will fail and thus #20161 occurs on 32 bit arm. Xorm 1.3.2-0.20220714055524 changes this field to use int64 instead.

Fix  #20161
2022-07-14 18:42:23 +02:00
6543
780b198997
Prevent context deadline error propagation in GetCommitsInfo (#20346) (#20361)
Backport #20346

Although `WalkGitLog` tries to test for `context.DeadlineExceededErr`
there is a small chance that the error will propagate to the reader
before it is recognised. This will cause the error to propagate up to
`renderDirectoryFiles` and cause a http status 500.

Here we check that the error passed is a `DeadlineExceededErr` via error.Is

Fix #20329

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2022-07-14 03:44:34 +01:00
Tyrone Yeh
f4e219f668
Fix org label open count, including close count issue (#20364) 2022-07-14 03:42:46 +01:00
Ing. Jaroslav Šafka
92a43d577d
Fix checks in PR for empty commits (#20290) (#20352)
Backport #20290

* Fix #19603
* fill HeadCommitID in PullRequest
* compare real commits ID as check for merging


Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Andrew Thornton <art27@cantab.net>
2022-07-13 18:36:23 +01:00
Lunny Xiao
66686f6d0e
Hide notify mail setting ui if not enabled (#20138) (#20336)
Co-authored-by: 6543 <6543@obermui.de>
2022-07-13 09:47:29 +08:00
zeripath
26f4fe2b44
Correctly handle draft releases without a tag (#20314) (#20335)
Backport #20314

`errors.Is(err, git.ErrNotExist{})` is not working

Fixes #20313

Co-authored-by: Chongyi Zheng <harry@harryzheng.com>
2022-07-12 19:55:25 +01:00
Gusted
b8ab9298e1
Add write check for creating Commit status (#20332) (#20333)
- Backport #20332
  - Add write code checks for creating new commit status
  - Regression from #5314
  - Resolves #20331
2022-07-12 14:51:35 +02:00
wxiaoguang
54ef658861
Refactor SSH init code, fix directory creation for TrustedUserCAKeys file (#20299) (#20306)
Backport #20299. Follow #20298. Only the `GlobalInitInstalled` function should prepare the SSH files for external server or starts the builtin server.
* `trustedUserCaKeys` is removed, use `SSH.TrustedUserCAKeys` directly
* introduce `ssh.Init`, move the SSH init code from `routers/init.go` to it
* `ssh.Init` will start builtin SSH server or prepare external SSH server files
2022-07-11 23:27:51 +08:00
KN4CK3R
c556a83c35
Prevent "empty" scrollbars on Firefox (#20294) (#20308)
Addition to: Show scrollbar when necessary #20142
Fixes the "empty" scrollbars with Firefox.
2022-07-10 19:48:35 +01:00
wxiaoguang
317c565e77
Do not create empty ".ssh" directory when loading config (#20289) (#20298)
Backport #20289

The code is as old as back to 2016, creating the directory automatically is not correct IMO.

In other places for ssh key writing (RewriteAllPrincipalKeys / appendAuthorizedKeysToFile, etc), the directory will still be created when updating the keys.

This PR will resolve the confusing and annoying problem: the dummy and empty ".ssh" directory in new git home
2022-07-10 12:09:42 +02:00
Gusted
1d02a9c9fb
Bump goldmark to v1.4.13 (#20300) (#20301)
Backport #20300

  - Update goldmark to v1.4.13 to fix a issue with quotes after a empty list item(See https://github.com/yuin/goldmark/issues/313) and downstream issue https://codeberg.org/Codeberg/Community/issues/645
2022-07-09 21:39:38 +01:00
Gusted
d371ced49d
Store read access in access for team repo's (#20275) (#20276)
Backport #20275

Currently when a Team has read access to a organization's non-private repository, their access(in the `access` table) won't be stored in the database. This cause issues for code that rely on read access being stored, like retrieving all users who have read permission to that repository(even though this is confusing as this doesn't include all registered users). So from now-on if we see that the repository is owned by a organization don't increase the `minMode` to write permission.

Resolves #20083
2022-07-09 20:01:44 +02:00
wxiaoguang
5e5ff77ed7
Use git.HOME_PATH for Git HOME directory (#20114) (#20293)
Before, in #19732, the old home directory is not correct.
This PR introduces a new config option for git home: git.HOME_PATH,
which is default to %(APP_DATA_PATH)/home

And pass env GNUPGHOME to git command, force Gitea to use a stable GNUPGHOME directory
2022-07-08 21:44:36 +08:00
zeripath
039a60225a
Make notification bell more prominent on mobile (#20108, #20236, #20251) (#20269)
Backport #20108
Backport #20236
Backport #20251

Make notification bell more prominent on mobile

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Tyrone Yeh <siryeh@gmail.com>
Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-07-07 17:48:03 +02:00
Gusted
654c173b9d
Fix NPE when using non-numeric (#20277) (#20278)
- Backport (#20277
  - This code is only valid when `refNumeric` exist(otherwise we didn't find such numeric PR and can skip that check) and give a free-pas to the  "BEFORE" check when `ref` is nil.
  - Resolves #20109
2022-07-07 20:56:50 +08:00
zeripath
a92d247fdd
Only show Followers that current user can access (#20220) (#20252)
Backport #20220

Users who are following or being followed by a user should only be
displayed if the viewing user can see them.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-07-06 22:03:23 +01:00
zeripath
42be548ecc
EscapeFilter the group dn membership (#20200) (#20254)
Backport #20200

The uid provided to the group filter must be properly escaped using the provided
ldap.EscapeFilter function.

Fix #20181

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-07-06 20:51:40 +01:00
zeripath
76ba23a14f
Display full name (#20171) (#20246)
Backport #20171

The setting `DEFAULT_SHOW_FULL_NAME` promises to use the user's full name everywhere it can be used.

Unfortunately the function `*user_model.User.ShortName()` currently uses the `.Name` instead - but this should also use the `.FullName()`.

Therefore we should make `*user_model.User.ShortName()` base its pre-shortened name on the `.FullName()` function.

Co-authored-by: Baekjun Kim <36013575+kimbj95@users.noreply.github.com>
2022-07-05 16:58:10 +01:00
zeripath
c88a59bb23
Adjust max-widths for the repository file table (#20243) (#20247)
Backport #20243

Adjust the max-widths for the repository file table to allow for nicer
resizing of the names and commit messages.

Fix #20040

Signed-off-by: Andrew Thornton <art27@cantab.net>

 ## Screenshots

 ## MediaXL
![Screenshot from 2022-07-05 10-22-12](https://user-images.githubusercontent.com/1824502/177295867-7ba8cf60-8f61-4227-892f-e5a0477e4146.png)

 ## MediaLg
![Screenshot from 2022-07-05 10-24-37](https://user-images.githubusercontent.com/1824502/177296301-e066e206-10f7-4a15-a68b-0f772a95f369.png)

 ## MediaMd
![Screenshot from 2022-07-05 10-23-03](https://user-images.githubusercontent.com/1824502/177295965-69397649-16ca-456a-bc0c-ed507fcb7f44.png)

 ## MediaSm
![Screenshot from 2022-07-05 10-26-44](https://user-images.githubusercontent.com/1824502/177296700-ca2a853b-c47b-4592-baf4-4bc08a7e1c9c.png)

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-07-05 16:15:56 +01:00
wxiaoguang
01a4fb0ae6
Bypass Firefox (iOS) bug (#20244) (#20250)
Backport #20244 

* https://github.com/go-gitea/gitea/issues/20240

At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240
If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0.

This PR ignores such nonsense error event.

Fix #20240
2022-07-05 16:01:01 +01:00
Gusted
f42fc3b287
Init popup for new code comment (#20234) (#20235)
- Backport #20234
 
  - Initialize the popup for the tooltip inside the new code comment.
  - This works and is good enough to have this issue fixed for 1.17
 
Fix #20068
2022-07-05 13:29:04 +01:00
zeripath
35fd55c7df
Update Bluemonday to v1.0.19 (#20199) (#20209) 2022-07-03 22:15:35 -04:00
zeripath
e321b40bb0
Refix indices on actions table (#20158) (#20198)
Backport #20158

Unforunately the previous PR #20035 created indices that were not helpful
for SQLite. This PR adjusts these after testing using the try.gitea.io db.

Fix #20129

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-07-02 15:31:51 +02:00
wxiaoguang
d22826a28e
Fix dump-repo git init, fix wrong error type for NullDownloader (#20182) (#20186)
* Fix `dump-repo` git init

* Fix wrong error type for NullDownloader
2022-07-01 18:01:05 +02:00
wxiaoguang
bf43db10a9
Fix cli command restore-repo: "units" should be parsed as cli.String (#20183) (#20187) 2022-07-01 17:16:59 +02:00
Lunny Xiao
3e4fe009e7
Check if project has the same repository id with issue when assign project to issue (#20133) (#20188)
* Check if project has the same repository id with issue when assign project to issue

* Check if issue's repository id match project's repository id

* Add more permission checking

* Remove invalid argument

* Fix errors

* Add generic check

* Remove duplicated check

* Return error + add check for new issues

* Apply suggestions from code review

Co-authored-by: Gusted <williamzijl7@hotmail.com>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: 6543 <6543@obermui.de>
2022-07-01 15:00:05 +02:00
silverwind
1ffc700777
Update default allowed attachment types (#20193)
Synced the list to what is allowed on GitHub currently.
2022-07-01 19:37:52 +08:00
Gusted
0dab13884a
Show scrollbar when necessary (#20142) (#20143)
- Backport #20142
  - Firefox on Windows will unconditionally show scrollbars when you specify `overflow: scroll`. This is bad behavior, as you don't always need the scrollbar. Changing the scroll value to auto fixes this issue and only shows the scrollbar when necessary.
  - Resolves #20139
2022-06-27 13:20:47 +08:00
Gusted
0b7b342ab0
Fix remove file on initial comment (#20127) (#20128)
Backport #20127

Store the file uuid(which is returned by Gitea in the upload file response) onto the file object, so it can be used for the remove feature to specify this file.

Fix #20115
2022-06-25 20:50:23 +01:00
128 changed files with 1652 additions and 437 deletions

18
.woodpecker.yml Normal file
View file

@ -0,0 +1,18 @@
pipeline:
build:
image: docker.io/alpine:3.16
commands:
- apk add git nodejs npm go make rsync openssh-client-default
- GOOS=linux GOARCH=amd64 LDFLAGS="-linkmode external -extldflags '-static' $LDFLAGS" TAGS="bindata sqlite sqlite_unlock_notify" make build
- eval $(ssh-agent -s)
- echo "$${SSH_KEY}" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- echo "[nulo.in]:420,[186.136.121.7]:420 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGPkgRVWYcVcgjI0xAjDgZQsYuXU9edcya8zna01ibyUMlfKHIMD9yOoq0R+fQPTCqwiol/2tKMPJ2hlKshc+H8=" > ~/.ssh/known_hosts
- rsync --rsh='ssh -p420' --progress --chmod=755 gitea diablo@nulo.in:gitea
- ssh -p420 diablo@nulo.in 'doas mv ~diablo/gitea /usr/local/bin/gitea && doas sv restart gitea'
when:
branch: gitea.nulo.in
event: push
secrets:
- ssh_key

View file

@ -4,7 +4,7 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has 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.io). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.17.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.17.0-rc1) - 2022-06-18 ## [1.17.0](https://github.com/go-gitea/gitea/releases/tag/v1.17.0) - 2022-07-30
* BREAKING * BREAKING
* Require go1.18 for Gitea 1.17 (#19918) * Require go1.18 for Gitea 1.17 (#19918)
@ -29,6 +29,8 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Restrict email address validation (#17688) * Restrict email address validation (#17688)
* Refactor Router Logger (#17308) * Refactor Router Logger (#17308)
* SECURITY * SECURITY
* Use git.HOME_PATH for Git HOME directory (#20114) (#20293)
* Add write check for creating Commit Statuses (#20332) (#20333)
* Remove deprecated SSH ciphers from default (#18697) * Remove deprecated SSH ciphers from default (#18697)
* FEDERATION * FEDERATION
* Return statistic information for nodeinfo (#19561) * Return statistic information for nodeinfo (#19561)
@ -67,6 +69,9 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Return primary language and repository language stats API URL (#18396) * Return primary language and repository language stats API URL (#18396)
* Implement http signatures support for the API (#17565) * Implement http signatures support for the API (#17565)
* ENHANCEMENTS * ENHANCEMENTS
* Make notification bell more prominent on mobile (#20108, #20236, #20251) (#20269)
* Adjust max-widths for the repository file table (#20243) (#20247)
* Display full name (#20171) (#20246)
* Add dbconsistency checks for Stopwatches (#20010) * Add dbconsistency checks for Stopwatches (#20010)
* Add fetch.writeCommitGraph to gitconfig (#20006) * Add fetch.writeCommitGraph to gitconfig (#20006)
* Add fgprof pprof profiler (#20005) * Add fgprof pprof profiler (#20005)
@ -111,7 +116,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* PullService lock via pullID (#19520) * PullService lock via pullID (#19520)
* Make repository file list useable on mobile (#19515) * Make repository file list useable on mobile (#19515)
* more context for models (#19511) * more context for models (#19511)
* Allow package dump skipping (#19506)
* Refactor readme file renderer (#19502) * Refactor readme file renderer (#19502)
* By default force vertical tabs on mobile (#19486) * By default force vertical tabs on mobile (#19486)
* Github style following followers (#19482) * Github style following followers (#19482)
@ -128,7 +132,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Move access and repo permission to models/perm/access (#19350) * Move access and repo permission to models/perm/access (#19350)
* Disallow selecting the text of buttons (#19330) * Disallow selecting the text of buttons (#19330)
* Allow custom redirect for landing page (#19324) * Allow custom redirect for landing page (#19324)
* Repository level enable package or disable (#19323)
* Remove dependent on session auth for api/v1 routers (#19321) * Remove dependent on session auth for api/v1 routers (#19321)
* Never use /api/v1 from Gitea UI Pages (#19318) * Never use /api/v1 from Gitea UI Pages (#19318)
* Remove legacy unmaintained packages, refactor to support change default locale (#19308) * Remove legacy unmaintained packages, refactor to support change default locale (#19308)
@ -185,13 +188,44 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Allow custom default merge message with .gitea/default_merge_message/<merge_style>_TEMPLATE.md (#18177) * Allow custom default merge message with .gitea/default_merge_message/<merge_style>_TEMPLATE.md (#18177)
* Prettify number of issues (#17760) * Prettify number of issues (#17760)
* Add a "admin user generate-access-token" subcommand (#17722) * Add a "admin user generate-access-token" subcommand (#17722)
* Move project files into models/project sub package (#17704)
* Custom regexp external issues (#17624) * Custom regexp external issues (#17624)
* Add smtp password to install page (#17564) * Add smtp password to install page (#17564)
* Add config options to hide issue events (#17414) * Add config options to hide issue events (#17414)
* Prevent double click new issue/pull/comment button (#16157) * Prevent double click new issue/pull/comment button (#16157)
* Show issue assignee on project board (#15232) * Show issue assignee on project board (#15232)
* BUGFIXES * BUGFIXES
* WebAuthn CredentialID field needs to be increased in size (#20530) (#20555)
* Ensure that all unmerged files are merged when conflict checking (#20528) (#20536)
* Stop logging EOFs and exit(1)s in ssh handler (#20476) (#20529)
* Add labels to two buttons that were missing them (#20419) (#20524)
* Fix ROOT_URL detection for URLs without trailing slash (#20502) (#20503)
* Dismiss prior pull reviews if done via web in review dismiss (#20197) (#20407)
* Allow RSA 2047 bit keys (#20272) (#20396)
* Add missing return for when topic isn't found (#20351) (#20395)
* Fix commit status icon when in subdirectory (#20285) (#20385)
* Initialize cron last (#20373) (#20384)
* Set target on create release with existing tag (#20381) (#20382)
* Update xorm.io/xorm to fix a interpreting db column sizes issue on 32bit systems (#20371) (#20372)
* Make sure `repo_dir` is an empty directory or doesn't exist before 'dump-repo' (#20205) (#20370)
* Prevent context deadline error propagation in GetCommitsInfo (#20346) (#20361)
* Correctly handle draft releases without a tag (#20314) (#20335)
* Prevent "empty" scrollbars on Firefox (#20294) (#20308)
* Refactor SSH init code, fix directory creation for TrustedUserCAKeys file (#20299) (#20306)
* Bump goldmark to v1.4.13 (#20300) (#20301)
* Do not create empty ".ssh" directory when loading config (#20289) (#20298)
* Fix NPE when using non-numeric (#20277) (#20278)
* Store read access in access for team repositories (#20275) (#20276)
* EscapeFilter the group dn membership (#20200) (#20254)
* Only show Followers that current user can access (#20220) (#20252)
* Update Bluemonday to v1.0.19 (#20199) (#20209)
* Refix indices on actions table (#20158) (#20198)
* Check if project has the same repository id with issue when assign project to issue (#20133) (#20188)
* Fix remove file on initial comment (#20127) (#20128)
* Catch the error before the response is processed by goth (#20000) (#20102)
* Dashboard feed respect setting.UI.FeedPagingNum again (#20094) (#20099)
* Alter hook_task TEXT fields to LONGTEXT (#20038) (#20041)
* Respond with a 401 on git push when password isn't changed yet (#20026) (#20027)
* Return 404 when tag is broken (#20017) (#20024)
* Alter hook_task TEXT fields to LONGTEXT (#20038) (#20041) * Alter hook_task TEXT fields to LONGTEXT (#20038) (#20041)
* Respond with a 401 on git push when password isn't changed yet (#20026) (#20027) * Respond with a 401 on git push when password isn't changed yet (#20026) (#20027)
* Return 404 when tag is broken (#20017) (#20024) * Return 404 when tag is broken (#20017) (#20024)
@ -266,7 +300,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* MISC * MISC
* Fix aria for logo (#19955) * Fix aria for logo (#19955)
* In code search, get code unit accessible repos in one (main) query (#19764) * In code search, get code unit accessible repos in one (main) query (#19764)
* Enable packages by default again (#19746)
* Add tooltip to pending PR comments (#19662) * Add tooltip to pending PR comments (#19662)
* Improve sync performance for pull-mirrors (#19125) * Improve sync performance for pull-mirrors (#19125)
* Improve dashboard's repo list performance (#18963) * Improve dashboard's repo list performance (#18963)

View file

@ -7,13 +7,17 @@ package cmd
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"os"
"strings" "strings"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration" base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/migrations" "code.gitea.io/gitea/services/migrations"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -83,6 +87,11 @@ func runDumpRepository(ctx *cli.Context) error {
return err return err
} }
// migrations.GiteaLocalUploader depends on git module
if err := git.InitSimple(context.Background()); err != nil {
return err
}
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
@ -128,7 +137,9 @@ func runDumpRepository(ctx *cli.Context) error {
} else { } else {
units := strings.Split(ctx.String("units"), ",") units := strings.Split(ctx.String("units"), ",")
for _, unit := range units { for _, unit := range units {
switch strings.ToLower(unit) { switch strings.ToLower(strings.TrimSpace(unit)) {
case "":
continue
case "wiki": case "wiki":
opts.Wiki = true opts.Wiki = true
case "issues": case "issues":
@ -145,13 +156,29 @@ func runDumpRepository(ctx *cli.Context) error {
opts.Comments = true opts.Comments = true
case "pull_requests": case "pull_requests":
opts.PullRequests = true opts.PullRequests = true
default:
return errors.New("invalid unit: " + unit)
} }
} }
} }
// the repo_dir will be removed if error occurs in DumpRepository
// make sure the directory doesn't exist or is empty, prevent from deleting user files
repoDir := ctx.String("repo_dir")
if exists, err := util.IsExist(repoDir); err != nil {
return fmt.Errorf("unable to stat repo_dir %q: %v", repoDir, err)
} else if exists {
if isDir, _ := util.IsDir(repoDir); !isDir {
return fmt.Errorf("repo_dir %q already exists but it's not a directory", repoDir)
}
if dir, _ := os.ReadDir(repoDir); len(dir) > 0 {
return fmt.Errorf("repo_dir %q is not empty", repoDir)
}
}
if err := migrations.DumpRepository( if err := migrations.DumpRepository(
context.Background(), context.Background(),
ctx.String("repo_dir"), repoDir,
ctx.String("owner_name"), ctx.String("owner_name"),
opts, opts,
); err != nil { ); err != nil {

View file

@ -7,6 +7,7 @@ package cmd
import ( import (
"errors" "errors"
"net/http" "net/http"
"strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
@ -37,10 +38,10 @@ var CmdRestoreRepository = cli.Command{
Value: "", Value: "",
Usage: "Restore destination repository name", Usage: "Restore destination repository name",
}, },
cli.StringSliceFlag{ cli.StringFlag{
Name: "units", Name: "units",
Value: nil, Value: "",
Usage: `Which items will be restored, one or more units should be repeated with this flag. Usage: `Which items will be restored, one or more units should be separated as comma.
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`, wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
}, },
cli.BoolFlag{ cli.BoolFlag{
@ -55,13 +56,16 @@ func runRestoreRepository(c *cli.Context) error {
defer cancel() defer cancel()
setting.LoadFromExisting() setting.LoadFromExisting()
var units []string
if s := c.String("units"); s != "" {
units = strings.Split(s, ",")
}
statusCode, errStr := private.RestoreRepo( statusCode, errStr := private.RestoreRepo(
ctx, ctx,
c.String("repo_dir"), c.String("repo_dir"),
c.String("owner_name"), c.String("owner_name"),
c.String("repo_name"), c.String("repo_name"),
c.StringSlice("units"), units,
c.Bool("validation"), c.Bool("validation"),
) )
if statusCode == http.StatusOK { if statusCode == http.StatusOK {

View file

@ -617,7 +617,10 @@ ROUTER = console
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; The path of git executable. If empty, Gitea searches through the PATH environment. ;; The path of git executable. If empty, Gitea searches through the PATH environment.
PATH = ;PATH =
;;
;; The HOME directory for Git
;HOME_PATH = %(APP_DATA_PATH)/home
;; ;;
;; Disables highlight of added and removed changes ;; Disables highlight of added and removed changes
;DISABLE_DIFF_HIGHLIGHT = false ;DISABLE_DIFF_HIGHLIGHT = false
@ -1242,7 +1245,7 @@ PATH =
;; Define allowed algorithms and their minimum key length (use -1 to disable a type) ;; Define allowed algorithms and their minimum key length (use -1 to disable a type)
;ED25519 = 256 ;ED25519 = 256
;ECDSA = 256 ;ECDSA = 256
;RSA = 2048 ;RSA = 2047 ; we allow 2047 here because an otherwise valid 2048 bit RSA key can be reported as having 2047 bit length
;DSA = -1 ; set to 1024 to switch on ;DSA = -1 ; set to 1024 to switch on
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1687,7 +1690,7 @@ PATH =
;ENABLED = true ;ENABLED = true
;; ;;
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. ;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
;ALLOWED_TYPES = .docx,.gif,.gz,.jpeg,.jpg,.mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip ;ALLOWED_TYPES = .csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip
;; ;;
;; Max size of each file. Defaults to 4MB ;; Max size of each file. Defaults to 4MB
;MAX_SIZE = 4 ;MAX_SIZE = 4

View file

@ -620,7 +620,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
- `ED25519`: **256** - `ED25519`: **256**
- `ECDSA`: **256** - `ECDSA`: **256**
- `RSA`: **2048** - `RSA`: **2047**: We set 2047 here because an otherwise valid 2048 RSA key can be reported as 2047 length.
- `DSA`: **-1**: DSA is now disabled by default. Set to **1024** to re-enable but ensure you may need to reconfigure your SSHD provider - `DSA`: **-1**: DSA is now disabled by default. Set to **1024** to re-enable but ensure you may need to reconfigure your SSHD provider
## Webhook (`webhook`) ## Webhook (`webhook`)
@ -741,7 +741,7 @@ Default templates for project boards:
## Issue and pull request attachments (`attachment`) ## Issue and pull request attachments (`attachment`)
- `ENABLED`: **true**: Whether issue and pull request attachments are enabled. - `ENABLED`: **true**: Whether issue and pull request attachments are enabled.
- `ALLOWED_TYPES`: **.docx,.gif,.gz,.jpeg,.jpg,mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. - `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
- `MAX_SIZE`: **4**: Maximum size (MB). - `MAX_SIZE`: **4**: Maximum size (MB).
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once. - `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
- `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]` - `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
@ -947,6 +947,8 @@ Default templates for project boards:
## Git (`git`) ## Git (`git`)
- `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment. - `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment.
- `HOME_PATH`: **%(APP_DATA_PATH)/home**: The HOME directory for Git.
This directory will be used to contain the `.gitconfig` and possible `.gnupg` directories that Gitea's git calls will use. If you can confirm Gitea is the only application running in this environment, you can set it to the normal home directory for Gitea user.
- `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes. - `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes.
- `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view. - `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view.
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view. - `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.

View file

@ -97,10 +97,11 @@ repositories, `SIGNING_KEY=default` could be used to provide different
signing keys on a per-repository basis. However, this is clearly not an signing keys on a per-repository basis. However, this is clearly not an
ideal UI and therefore subject to change. ideal UI and therefore subject to change.
**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`. **Since 1.17**, Gitea runs git in its own home directory `[git].HOME_PATH` (default to `%(APP_DATA_PATH)/home`)
and uses its own config `{[git].HOME_PATH}/.gitconfig`.
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`) If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
or the Gitea internal git config `{[repository].ROOT}/.gitconfig`. or the Gitea internal git config `{[git].HOME_PATH}/.gitconfig`.
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`. Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[git].HOME_PATH`.
### `INITIAL_COMMIT` ### `INITIAL_COMMIT`

View file

@ -37,7 +37,7 @@ PUT https://gitea.example.com/api/packages/{owner}/generic/{package_name}/{packa
| ----------------- | ----------- | | ----------------- | ----------- |
| `owner` | The owner of the package. | | `owner` | The owner of the package. |
| `package_name` | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | | `package_name` | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). |
| `package_version` | The package version as described in the [SemVer](https://semver.org/) spec. | | `package_version` | The package version, a non-empty string. |
| `file_name` | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | | `file_name` | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). |
Example request using HTTP Basic authentication: Example request using HTTP Basic authentication:

12
go.mod
View file

@ -9,7 +9,7 @@ require (
gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
gitea.com/lunny/levelqueue v0.4.1 gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.8.0 github.com/PuerkitoBio/goquery v1.8.0
@ -64,7 +64,7 @@ require (
github.com/mattn/go-isatty v0.0.14 github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-sqlite3 v1.14.12 github.com/mattn/go-sqlite3 v1.14.12
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.18 github.com/microcosm-cc/bluemonday v1.0.19
github.com/minio/minio-go/v7 v7.0.26 github.com/minio/minio-go/v7 v7.0.26
github.com/msteinert/pam v1.0.0 github.com/msteinert/pam v1.0.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
@ -85,15 +85,15 @@ require (
github.com/urfave/cli v1.22.9 github.com/urfave/cli v1.22.9
github.com/xanzy/go-gitlab v0.64.0 github.com/xanzy/go-gitlab v0.64.0
github.com/yohcop/openid-go v1.0.0 github.com/yohcop/openid-go v1.0.0
github.com/yuin/goldmark v1.4.12 github.com/yuin/goldmark v1.4.13
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
github.com/yuin/goldmark-meta v1.1.0 github.com/yuin/goldmark-meta v1.1.0
go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/hcaptcha v0.0.4
go.jolheiser.com/pwn v0.0.3 go.jolheiser.com/pwn v0.0.3
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/net v0.0.0-20220630215102-69896b714898
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
golang.org/x/tools v0.1.10 golang.org/x/tools v0.1.10
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@ -102,7 +102,7 @@ require (
mvdan.cc/xurls/v2 v2.4.0 mvdan.cc/xurls/v2 v2.4.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.11 xorm.io/builder v0.3.11
xorm.io/xorm v1.3.1 xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f
) )
require ( require (

25
go.sum
View file

@ -78,8 +78,8 @@ gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 h1:J/1i8u40TbcLP/w2w
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5/go.mod h1:hQ9SYHKdOX968wJglb/NMQ+UqpOKwW4L+EYdvkWjHSo= gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5/go.mod h1:hQ9SYHKdOX968wJglb/NMQ+UqpOKwW4L+EYdvkWjHSo=
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q= gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q=
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck= gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck=
gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw= gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7 h1:Zc3RQWC2xOVglLciQH/ZIC5IqSk3Jn96LflGQLv18Rg=
gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
@ -1146,8 +1146,8 @@ github.com/mholt/acmez v1.0.2 h1:C8wsEBIUVi6e0DYoxqCcFuXtwc4AWXL/jgcDjF7mjVo=
github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@ -1540,8 +1540,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg= github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU= github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
@ -1783,14 +1783,13 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1937,8 +1936,8 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@ -2414,5 +2413,5 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.11 h1:naLkJitGyYW7ZZdncsh/JW+HF4HshmvTHTyUyPwJS00= xorm.io/builder v0.3.11 h1:naLkJitGyYW7ZZdncsh/JW+HF4HshmvTHTyUyPwJS00=
xorm.io/builder v0.3.11/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.11/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.1 h1:z5egKrDoOLqZFhMjcGF4FBHiTmE5/feQoHclfhNidfM= xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f h1:3NvNsM4lnttTsHpk8ODHqrwN1MCEjsO3bD/rpd8A47k=
xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=

View file

@ -26,6 +26,7 @@ import (
func TestPackageContainer(t *testing.T) { func TestPackageContainer(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
has := func(l packages_model.PackagePropertyList, name string) bool { has := func(l packages_model.PackagePropertyList, name string) bool {
@ -36,6 +37,15 @@ func TestPackageContainer(t *testing.T) {
} }
return false return false
} }
getAllByName := func(l packages_model.PackagePropertyList, name string) []string {
values := make([]string, 0, len(l))
for _, pp := range l {
if pp.Name == name {
values = append(values, pp.Value)
}
}
return values
}
images := []string{"test", "te/st"} images := []string{"test", "te/st"}
tags := []string{"latest", "main"} tags := []string{"latest", "main"}
@ -66,7 +76,7 @@ func TestPackageContainer(t *testing.T) {
Token string `json:"token"` Token string `json:"token"`
} }
authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token"`} authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`}
t.Run("Anonymous", func(t *testing.T) { t.Run("Anonymous", func(t *testing.T) {
defer PrintCurrentTest(t)() defer PrintCurrentTest(t)()
@ -236,7 +246,8 @@ func TestPackageContainer(t *testing.T) {
assert.Nil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, image, pd.Package.Name) assert.Equal(t, image, pd.Package.Name)
assert.Equal(t, tag, pd.Version.Version) assert.Equal(t, tag, pd.Version.Version)
assert.True(t, has(pd.Properties, container_module.PropertyManifestTagged)) assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository))
assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata) assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*container_module.Metadata) metadata := pd.Metadata.(*container_module.Metadata)
@ -330,7 +341,8 @@ func TestPackageContainer(t *testing.T) {
assert.Nil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, image, pd.Package.Name) assert.Equal(t, image, pd.Package.Name)
assert.Equal(t, untaggedManifestDigest, pd.Version.Version) assert.Equal(t, untaggedManifestDigest, pd.Version.Version)
assert.False(t, has(pd.Properties, container_module.PropertyManifestTagged)) assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository))
assert.False(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata) assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
@ -362,18 +374,10 @@ func TestPackageContainer(t *testing.T) {
assert.Nil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, image, pd.Package.Name) assert.Equal(t, image, pd.Package.Name)
assert.Equal(t, multiTag, pd.Version.Version) assert.Equal(t, multiTag, pd.Version.Version)
assert.True(t, has(pd.Properties, container_module.PropertyManifestTagged)) assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository))
assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
getAllByName := func(l packages_model.PackagePropertyList, name string) []string { assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference))
values := make([]string, 0, len(l))
for _, pp := range l {
if pp.Name == name {
values = append(values, pp.Value)
}
}
return values
}
assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.Properties, container_module.PropertyManifestReference))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata) assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*container_module.Metadata) metadata := pd.Metadata.(*container_module.Metadata)
@ -528,4 +532,56 @@ func TestPackageContainer(t *testing.T) {
}) })
}) })
} }
t.Run("OwnerNameChange", func(t *testing.T) {
defer PrintCurrentTest(t)()
checkCatalog := func(owner string) func(t *testing.T) {
return func(t *testing.T) {
defer PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%sv2/_catalog", setting.AppURL))
addTokenAuthHeader(req, userToken)
resp := MakeRequest(t, req, http.StatusOK)
type RepositoryList struct {
Repositories []string `json:"repositories"`
}
repoList := &RepositoryList{}
DecodeJSON(t, resp, &repoList)
assert.Len(t, repoList.Repositories, len(images))
names := make([]string, 0, len(images))
for _, image := range images {
names = append(names, strings.ToLower(owner+"/"+image))
}
assert.ElementsMatch(t, names, repoList.Repositories)
}
}
t.Run(fmt.Sprintf("Catalog[%s]", user.LowerName), checkCatalog(user.LowerName))
session := loginUser(t, user.Name)
newOwnerName := "newUsername"
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": GetCSRF(t, session, "/user/settings"),
"name": newOwnerName,
"email": "user2@example.com",
"language": "en-US",
})
session.MakeRequest(t, req, http.StatusSeeOther)
t.Run(fmt.Sprintf("Catalog[%s]", newOwnerName), checkCatalog(newOwnerName))
req = NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": GetCSRF(t, session, "/user/settings"),
"name": user.Name,
"email": "user2@example.com",
"language": "en-US",
})
session.MakeRequest(t, req, http.StatusSeeOther)
})
} }

View file

@ -42,7 +42,6 @@ func TestPackageGeneric(t *testing.T) {
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, pd.SemVer)
assert.Nil(t, pd.Metadata) assert.Nil(t, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version) assert.Equal(t, packageVersion, pd.Version.Version)

View file

@ -85,9 +85,9 @@ func TestPackageNpm(t *testing.T) {
assert.IsType(t, &npm.Metadata{}, pd.Metadata) assert.IsType(t, &npm.Metadata{}, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version) assert.Equal(t, packageVersion, pd.Version.Version)
assert.Len(t, pd.Properties, 1) assert.Len(t, pd.VersionProperties, 1)
assert.Equal(t, npm.TagProperty, pd.Properties[0].Name) assert.Equal(t, npm.TagProperty, pd.VersionProperties[0].Name)
assert.Equal(t, packageTag, pd.Properties[0].Value) assert.Equal(t, packageTag, pd.VersionProperties[0].Value)
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
assert.NoError(t, err) assert.NoError(t, err)

View file

@ -105,7 +105,11 @@ func doAPICreateCommitStatus(ctx APITestContext, commitID string, status api.Com
} }
} }
func TestPullCreate_EmptyChangesWithCommits(t *testing.T) { func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) {
// Merge must continue if commits SHA are different, even if content is same
// Reason: gitflow and merging master back into develop, where is high possiblity, there are no changes
// but just commit saying "Merge branch". And this meta commit can be also tagged,
// so we need to have this meta commit also in develop branch.
onGiteaRun(t, func(t *testing.T, u *url.URL) { onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1") session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
@ -126,6 +130,28 @@ func TestPullCreate_EmptyChangesWithCommits(t *testing.T) {
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) text := strings.TrimSpace(doc.doc.Find(".merge-section").Text())
assert.Contains(t, text, "This branch is equal with the target branch.") assert.Contains(t, text, "This pull request can be merged automatically.")
})
}
func TestPullCreate_EmptyChangesWithSameCommits(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testCreateBranch(t, session, "user1", "repo1", "branch/master", "status1", http.StatusSeeOther)
url := path.Join("user1", "repo1", "compare", "master...status1")
req := NewRequestWithValues(t, "POST", url,
map[string]string{
"_csrf": GetCSRF(t, session, url),
"title": "pull request from status1",
},
)
session.MakeRequest(t, req, http.StatusSeeOther)
req = NewRequest(t, "GET", "/user1/repo1/pulls/1")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
text := strings.TrimSpace(doc.doc.Find(".merge-section").Text())
assert.Contains(t, text, "This branch is already included in the target branch. There is nothing to merge.")
}) })
} }

View file

@ -92,12 +92,12 @@ func init() {
// TableIndices implements xorm's TableIndices interface // TableIndices implements xorm's TableIndices interface
func (a *Action) TableIndices() []*schemas.Index { func (a *Action) TableIndices() []*schemas.Index {
repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType) actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted") actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")
return []*schemas.Index{actUserIndex, repoIndex} return []*schemas.Index{actUserIndex, repoIndex}
} }

View file

@ -6,7 +6,6 @@ package auth
import ( import (
"context" "context"
"encoding/base32"
"fmt" "fmt"
"strings" "strings"
@ -20,14 +19,14 @@ import (
// ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error. // ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
type ErrWebAuthnCredentialNotExist struct { type ErrWebAuthnCredentialNotExist struct {
ID int64 ID int64
CredentialID string CredentialID []byte
} }
func (err ErrWebAuthnCredentialNotExist) Error() string { func (err ErrWebAuthnCredentialNotExist) Error() string {
if err.CredentialID == "" { if len(err.CredentialID) == 0 {
return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID) return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID)
} }
return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %s]", err.CredentialID) return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %x]", err.CredentialID)
} }
// IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist. // IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist.
@ -43,7 +42,7 @@ type WebAuthnCredential struct {
Name string Name string
LowerName string `xorm:"unique(s)"` LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"` UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"` CredentialID []byte `xorm:"INDEX VARBINARY(1024)"`
PublicKey []byte PublicKey []byte
AttestationType string AttestationType string
AAGUID []byte AAGUID []byte
@ -94,9 +93,8 @@ type WebAuthnCredentialList []*WebAuthnCredential
func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential { func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
creds := make([]webauthn.Credential, 0, len(list)) creds := make([]webauthn.Credential, 0, len(list))
for _, cred := range list { for _, cred := range list {
credID, _ := base32.HexEncoding.DecodeString(cred.CredentialID)
creds = append(creds, webauthn.Credential{ creds = append(creds, webauthn.Credential{
ID: credID, ID: cred.CredentialID,
PublicKey: cred.PublicKey, PublicKey: cred.PublicKey,
AttestationType: cred.AttestationType, AttestationType: cred.AttestationType,
Authenticator: webauthn.Authenticator{ Authenticator: webauthn.Authenticator{
@ -164,11 +162,11 @@ func HasWebAuthnRegistrationsByUID(uid int64) (bool, error) {
} }
// GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID // GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID
func GetWebAuthnCredentialByCredID(userID int64, credID string) (*WebAuthnCredential, error) { func GetWebAuthnCredentialByCredID(userID int64, credID []byte) (*WebAuthnCredential, error) {
return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID) return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID)
} }
func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID string) (*WebAuthnCredential, error) { func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID []byte) (*WebAuthnCredential, error) {
cred := new(WebAuthnCredential) cred := new(WebAuthnCredential)
if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil { if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil {
return nil, err return nil, err
@ -187,7 +185,7 @@ func createCredential(ctx context.Context, userID int64, name string, cred *weba
c := &WebAuthnCredential{ c := &WebAuthnCredential{
UserID: userID, UserID: userID,
Name: name, Name: name,
CredentialID: base32.HexEncoding.EncodeToString(cred.ID), CredentialID: cred.ID,
PublicKey: cred.PublicKey, PublicKey: cred.PublicKey,
AttestationType: cred.AttestationType, AttestationType: cred.AttestationType,
AAGUID: cred.Authenticator.AAGUID, AAGUID: cred.Authenticator.AAGUID,

View file

@ -5,7 +5,6 @@
package auth package auth
import ( import (
"encoding/base32"
"testing" "testing"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
@ -61,9 +60,7 @@ func TestCreateCredential(t *testing.T) {
res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")}) res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "WebAuthn Created Credential", res.Name) assert.Equal(t, "WebAuthn Created Credential", res.Name)
bs, err := base32.HexEncoding.DecodeString(res.CredentialID) assert.Equal(t, []byte("Test"), res.CredentialID)
assert.NoError(t, err)
assert.Equal(t, []byte("Test"), bs)
unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1}) unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
} }

View file

@ -124,6 +124,17 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64
func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
oldProjectID := issue.projectID(ctx) oldProjectID := issue.projectID(ctx)
// Only check if we add a new project and not remove it.
if newProjectID > 0 {
newProject, err := project_model.GetProjectByID(ctx, newProjectID)
if err != nil {
return err
}
if newProject.RepoID != issue.RepoID {
return fmt.Errorf("issue's repository is not the same as project's repository")
}
}
if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil { if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
return err return err
} }

View file

@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@ -107,6 +108,7 @@ func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
counts, _ := CountIssuesByRepo(&IssuesOptions{ counts, _ := CountIssuesByRepo(&IssuesOptions{
RepoID: repoID, RepoID: repoID,
LabelIDs: []int64{labelID}, LabelIDs: []int64{labelID},
IsClosed: util.OptionalBoolFalse,
}) })
for _, count := range counts { for _, count := range counts {

View file

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@ -124,6 +125,11 @@ func NewMilestone(m *Milestone) (err error) {
return committer.Commit() return committer.Commit()
} }
// HasMilestoneByRepoID returns if the milestone exists in the repository.
func HasMilestoneByRepoID(ctx context.Context, repoID, id int64) (bool, error) {
return db.GetEngine(ctx).ID(id).Where("repo_id=?", repoID).Exist(new(Milestone))
}
// GetMilestoneByRepoID returns the milestone in a repository. // GetMilestoneByRepoID returns the milestone in a repository.
func GetMilestoneByRepoID(ctx context.Context, repoID, id int64) (*Milestone, error) { func GetMilestoneByRepoID(ctx context.Context, repoID, id int64) (*Milestone, error) {
m := new(Milestone) m := new(Milestone)
@ -356,7 +362,11 @@ func (opts GetMilestonesOption) toCond() builder.Cond {
} }
if len(opts.Name) != 0 { if len(opts.Name) != 0 {
cond = cond.And(builder.Like{"name", opts.Name}) if setting.Database.UseSQLite3 {
cond = cond.And(builder.Like{"UPPER(name)", util.ToUpperASCII(opts.Name)})
} else {
cond = cond.And(builder.Like{"UPPER(name)", strings.ToUpper(opts.Name)})
}
} }
return cond return cond

View file

@ -122,6 +122,7 @@ const (
PullRequestStatusManuallyMerged PullRequestStatusManuallyMerged
PullRequestStatusError PullRequestStatusError
PullRequestStatusEmpty PullRequestStatusEmpty
PullRequestStatusAncestor
) )
// PullRequestFlow the flow of pull request // PullRequestFlow the flow of pull request
@ -423,6 +424,11 @@ func (pr *PullRequest) IsEmpty() bool {
return pr.Status == PullRequestStatusEmpty return pr.Status == PullRequestStatusEmpty
} }
// IsAncestor returns true if the Head Commit of this PR is an ancestor of the Base Commit
func (pr *PullRequest) IsAncestor() bool {
return pr.Status == PullRequestStatusAncestor
}
// SetMerged sets a pull request to merged and closes the corresponding issue // SetMerged sets a pull request to merged and closes the corresponding issue
func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) { func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
if pr.HasMerged { if pr.HasMerged {

View file

@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@ -474,6 +475,35 @@ func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, co
return review, comm, committer.Commit() return review, comm, committer.Commit()
} }
// GetReviewOptions represent filter options for GetReviews
type GetReviewOptions struct {
IssueID int64
ReviewerID int64
Dismissed util.OptionalBool
}
// GetReviews return reviews based on GetReviewOptions
func GetReviews(ctx context.Context, opts *GetReviewOptions) ([]*Review, error) {
if opts == nil {
return nil, fmt.Errorf("opts are nil")
}
sess := db.GetEngine(ctx)
if opts.IssueID != 0 {
sess = sess.Where("issue_id=?", opts.IssueID)
}
if opts.ReviewerID != 0 {
sess = sess.Where("reviewer_id=?", opts.ReviewerID)
}
if !opts.Dismissed.IsNone() {
sess = sess.Where("dismissed=?", opts.Dismissed.IsTrue())
}
reviews := make([]*Review, 0, 4)
return reviews, sess.Find(&reviews)
}
// GetReviewersByIssueID gets the latest review of each reviewer for a pull request // GetReviewersByIssueID gets the latest review of each reviewer for a pull request
func GetReviewersByIssueID(issueID int64) ([]*Review, error) { func GetReviewersByIssueID(issueID int64) ([]*Review, error) {
reviews := make([]*Review, 0, 10) reviews := make([]*Review, 0, 10)

View file

@ -0,0 +1,9 @@
-
id: 1
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
-
id: 2
credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR="
-
id: 4
credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="

View file

@ -0,0 +1,31 @@
-
id: 1
lower_name: "u2fkey-correctly-migrated"
name: "u2fkey-correctly-migrated"
user_id: 1
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
attestation_type: 'fido-u2f'
sign_count: 1
clone_warning: false
-
id: 2
lower_name: "non-u2f-key"
name: "non-u2f-key"
user_id: 1
credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR"
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
attestation_type: 'none'
sign_count: 1
clone_warning: false
-
id: 4
lower_name: "packed-key"
name: "packed-key"
user_id: 1
credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
attestation_type: 'fido-u2f'
sign_count: 1
clone_warning: false

View file

@ -56,6 +56,9 @@ type Version struct {
Version int64 Version int64
} }
// Use noopMigration when there is a migration that has been no-oped
var noopMigration = func(_ *xorm.Engine) error { return nil }
// This is a sequence of migrations. Add new migrations to the bottom of the list. // This is a sequence of migrations. Add new migrations to the bottom of the list.
// If you want to "retire" a migration, remove it from the top of the list and // If you want to "retire" a migration, remove it from the top of the list and
// update minDBVersion accordingly // update minDBVersion accordingly
@ -351,7 +354,7 @@ var migrations = []Migration{
// v198 -> v199 // v198 -> v199
NewMigration("Add issue content history table", addTableIssueContentHistory), NewMigration("Add issue content history table", addTableIssueContentHistory),
// v199 -> v200 // v199 -> v200
NewMigration("No-op (remote version is using AppState now)", addRemoteVersionTableNoop), NewMigration("No-op (remote version is using AppState now)", noopMigration),
// v200 -> v201 // v200 -> v201
NewMigration("Add table app_state", addTableAppState), NewMigration("Add table app_state", addTableAppState),
// v201 -> v202 // v201 -> v202
@ -388,9 +391,21 @@ var migrations = []Migration{
// v215 -> v216 // v215 -> v216
NewMigration("allow to view files in PRs", addReviewViewedFiles), NewMigration("allow to view files in PRs", addReviewViewedFiles),
// v216 -> v217 // v216 -> v217
NewMigration("Improve Action table indices", improveActionTableIndices), NewMigration("No-op (Improve Action table indices v1)", noopMigration),
// v217 -> v218 // v217 -> v218
NewMigration("Alter hook_task table TEXT fields to LONGTEXT", alterHookTaskTextFieldsToLongText), NewMigration("Alter hook_task table TEXT fields to LONGTEXT", alterHookTaskTextFieldsToLongText),
// v218 -> v219
NewMigration("Improve Action table indices v2", improveActionTableIndices),
// v219 -> v220
NewMigration("Add sync_on_commit column to push_mirror table", addSyncOnCommitColForPushMirror),
// v220 -> v221
NewMigration("Add container repository property", addContainerRepositoryProperty),
// v221 -> v222
NewMigration("Store WebAuthentication CredentialID as bytes and increase size to at least 1024", storeWebauthnCredentialIDAsBytes),
// v222 -> v223
NewMigration("Drop old CredentialID column", dropOldCredentialIDColumn),
// v223 -> v224
NewMigration("Rename CredentialIDBytes column to CredentialID", renameCredentialIDBytes),
} }
// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version

View file

@ -4,11 +4,4 @@
package migrations package migrations
import ( // We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now.
"xorm.io/xorm"
)
func addRemoteVersionTableNoop(x *xorm.Engine) error {
// we used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now.
return nil
}

View file

@ -4,43 +4,5 @@
package migrations package migrations
import ( // This migration added non-ideal indices to the action table which on larger datasets slowed things down
"code.gitea.io/gitea/modules/timeutil" // it has been superceded by v218.go
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type improveActionTableIndicesAction struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 // Receiver user id.
OpType int
ActUserID int64 // Action user id.
RepoID int64
CommentID int64 `xorm:"INDEX"`
IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName sets the name of this table
func (a *improveActionTableIndicesAction) TableName() string {
return "action"
}
// TableIndices implements xorm's TableIndices interface
func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")
return []*schemas.Index{actUserIndex, repoIndex}
}
func improveActionTableIndices(x *xorm.Engine) error {
return x.Sync2(&improveActionTableIndicesAction{})
}

46
models/migrations/v218.go Normal file
View file

@ -0,0 +1,46 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type improveActionTableIndicesAction struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 // Receiver user id.
OpType int
ActUserID int64 // Action user id.
RepoID int64
CommentID int64 `xorm:"INDEX"`
IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName sets the name of this table
func (*improveActionTableIndicesAction) TableName() string {
return "action"
}
// TableIndices implements xorm's TableIndices interface
func (*improveActionTableIndicesAction) TableIndices() []*schemas.Index {
repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
return []*schemas.Index{actUserIndex, repoIndex}
}
func improveActionTableIndices(x *xorm.Engine) error {
return x.Sync2(&improveActionTableIndicesAction{})
}

31
models/migrations/v219.go Normal file
View file

@ -0,0 +1,31 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"time"
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func addSyncOnCommitColForPushMirror(x *xorm.Engine) error {
type PushMirror struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Repo *repo.Repository `xorm:"-"`
RemoteName string
SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"`
Interval time.Duration
CreatedUnix timeutil.TimeStamp `xorm:"created"`
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
LastError string `xorm:"text"`
}
return x.Sync2(new(PushMirror))
}

29
models/migrations/v220.go Normal file
View file

@ -0,0 +1,29 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
packages_model "code.gitea.io/gitea/models/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
func addContainerRepositoryProperty(x *xorm.Engine) error {
switch x.Dialect().URI().DBType {
case schemas.SQLITE:
_, err := x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name || '/' || p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
if err != nil {
return err
}
default:
_, err := x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, CONCAT(u.lower_name, '/', p.lower_name) FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
if err != nil {
return err
}
}
return nil
}

75
models/migrations/v221.go Normal file
View file

@ -0,0 +1,75 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"encoding/base32"
"fmt"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func storeWebauthnCredentialIDAsBytes(x *xorm.Engine) error {
// Create webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"`
// Note the lack of INDEX here - these will be created once the column is renamed in v223.go
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync2(&webauthnCredential{}); err != nil {
return err
}
var start int
creds := make([]*webauthnCredential, 0, 50)
for {
err := x.Select("id, credential_id").OrderBy("id").Limit(50, start).Find(&creds)
if err != nil {
return err
}
err = func() error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return fmt.Errorf("unable to allow start session. Error: %w", err)
}
for _, cred := range creds {
cred.CredentialIDBytes, err = base32.HexEncoding.DecodeString(cred.CredentialID)
if err != nil {
return fmt.Errorf("unable to parse credential id %s for credential[%d]: %w", cred.CredentialID, cred.ID, err)
}
count, err := sess.ID(cred.ID).Cols("credential_id_bytes").Update(cred)
if count != 1 || err != nil {
return fmt.Errorf("unable to update credential id bytes for credential[%d]: %d,%w", cred.ID, count, err)
}
}
return sess.Commit()
}()
if err != nil {
return err
}
if len(creds) < 50 {
break
}
start += 50
creds = creds[:0]
}
return nil
}

View file

@ -0,0 +1,65 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"encoding/base32"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_storeWebauthnCredentialIDAsBytes(t *testing.T) {
// Create webauthnCredential table
type WebauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"`
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
}
type ExpectedWebauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
CredentialID string // CredentialID is at most 1023 bytes as per spec released 20 July 2022
}
type ConvertedWebauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
}
// Prepare and load the testing database
x, deferable := prepareTestEnv(t, 0, new(WebauthnCredential), new(ExpectedWebauthnCredential))
defer deferable()
if x == nil || t.Failed() {
return
}
if err := storeWebauthnCredentialIDAsBytes(x); err != nil {
assert.NoError(t, err)
return
}
expected := []ExpectedWebauthnCredential{}
if err := x.Table("expected_webauthn_credential").Asc("id").Find(&expected); !assert.NoError(t, err) {
return
}
got := []ConvertedWebauthnCredential{}
if err := x.Table("webauthn_credential").Select("id, credential_id_bytes").Asc("id").Find(&got); !assert.NoError(t, err) {
return
}
for i, e := range expected {
credIDBytes, _ := base32.HexEncoding.DecodeString(e.CredentialID)
assert.Equal(t, credIDBytes, got[i].CredentialIDBytes)
}
}

64
models/migrations/v222.go Normal file
View file

@ -0,0 +1,64 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"context"
"fmt"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func dropOldCredentialIDColumn(x *xorm.Engine) error {
// This migration maybe rerun so that we should check if it has been run
credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id")
if err != nil {
return err
}
if !credentialIDExist {
// Column is already non-extant
return nil
}
credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes")
if err != nil {
return err
}
if !credentialIDBytesExists {
// looks like 221 hasn't properly run
return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
}
// Create webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"`
// Note the lack of the INDEX on CredentialIDBytes - we will add this in v223.go
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync2(&webauthnCredential{}); err != nil {
return err
}
// Drop the old credential ID
sess := x.NewSession()
defer sess.Close()
if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil {
return fmt.Errorf("unable to drop old credentialID column: %w", err)
}
return sess.Commit()
}

103
models/migrations/v223.go Normal file
View file

@ -0,0 +1,103 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"context"
"fmt"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func renameCredentialIDBytes(x *xorm.Engine) error {
// This migration maybe rerun so that we should check if it has been run
credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id")
if err != nil {
return err
}
if credentialIDExist {
credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes")
if err != nil {
return err
}
if !credentialIDBytesExists {
return nil
}
}
err = func() error {
// webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
// Note the lack of INDEX here
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := sess.Sync2(new(webauthnCredential)); err != nil {
return fmt.Errorf("error on Sync2: %v", err)
}
if credentialIDExist {
// if both errors and message exist, drop message at first
if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil {
return err
}
}
switch {
case setting.Database.UseMySQL:
if _, err := sess.Exec("ALTER TABLE `webauthn_credential` CHANGE credential_id_bytes credential_id VARBINARY(1024)"); err != nil {
return err
}
case setting.Database.UseMSSQL:
if _, err := sess.Exec("sp_rename 'webauthn_credential.credential_id_bytes', 'credential_id', 'COLUMN'"); err != nil {
return err
}
default:
if _, err := sess.Exec("ALTER TABLE `webauthn_credential` RENAME COLUMN credential_id_bytes TO credential_id"); err != nil {
return err
}
}
return sess.Commit()
}()
if err != nil {
return err
}
// Create webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID []byte `xorm:"INDEX VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
return x.Sync2(&webauthnCredential{})
}

View file

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/packages"
user_model "code.gitea.io/gitea/models/user"
container_module "code.gitea.io/gitea/modules/packages/container" container_module "code.gitea.io/gitea/modules/packages/container"
"xorm.io/builder" "xorm.io/builder"
@ -210,6 +211,7 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack
return pvs, count, err return pvs, count, err
} }
// SearchExpiredUploadedBlobs gets all uploaded blobs which are older than specified
func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) { func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) {
var cond builder.Cond = builder.Eq{ var cond builder.Cond = builder.Eq{
"package_version.is_internal": true, "package_version.is_internal": true,
@ -225,3 +227,37 @@ func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([
Where(cond). Where(cond).
Find(&pfs) Find(&pfs)
} }
// GetRepositories gets a sorted list of all repositories
func GetRepositories(ctx context.Context, actor *user_model.User, n int, last string) ([]string, error) {
var cond builder.Cond = builder.Eq{
"package.type": packages.TypeContainer,
"package_property.ref_type": packages.PropertyTypePackage,
"package_property.name": container_module.PropertyRepository,
}
cond = cond.And(builder.Exists(
builder.
Select("package_version.id").
Where(builder.Eq{"package_version.is_internal": false}.And(builder.Expr("package.id = package_version.package_id"))).
From("package_version"),
))
if last != "" {
cond = cond.And(builder.Gt{"package_property.value": strings.ToLower(last)})
}
cond = cond.And(user_model.BuildCanSeeUserCondition(actor))
sess := db.GetEngine(ctx).
Table("package").
Select("package_property.value").
Join("INNER", "user", "`user`.id = package.owner_id").
Join("INNER", "package_property", "package_property.ref_id = package.id").
Where(cond).
Asc("package_property.value").
Limit(n)
repositories := make([]string, 0, n)
return repositories, sess.Find(&repositories)
}

View file

@ -40,15 +40,16 @@ func (l PackagePropertyList) GetByName(name string) string {
// PackageDescriptor describes a package // PackageDescriptor describes a package
type PackageDescriptor struct { type PackageDescriptor struct {
Package *Package Package *Package
Owner *user_model.User Owner *user_model.User
Repository *repo_model.Repository Repository *repo_model.Repository
Version *PackageVersion Version *PackageVersion
SemVer *version.Version SemVer *version.Version
Creator *user_model.User Creator *user_model.User
Properties PackagePropertyList PackageProperties PackagePropertyList
Metadata interface{} VersionProperties PackagePropertyList
Files []*PackageFileDescriptor Metadata interface{}
Files []*PackageFileDescriptor
} }
// PackageFileDescriptor describes a package file // PackageFileDescriptor describes a package file
@ -102,6 +103,10 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
return nil, err return nil, err
} }
} }
pps, err := GetProperties(ctx, PropertyTypePackage, p.ID)
if err != nil {
return nil, err
}
pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID) pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -152,15 +157,16 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
} }
return &PackageDescriptor{ return &PackageDescriptor{
Package: p, Package: p,
Owner: o, Owner: o,
Repository: repository, Repository: repository,
Version: pv, Version: pv,
SemVer: semVer, SemVer: semVer,
Creator: creator, Creator: creator,
Properties: PackagePropertyList(pvps), PackageProperties: PackagePropertyList(pps),
Metadata: metadata, VersionProperties: PackagePropertyList(pvps),
Files: pfds, Metadata: metadata,
Files: pfds,
}, nil }, nil
} }

View file

@ -131,6 +131,12 @@ func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) {
return p, nil return p, nil
} }
// DeletePackageByID deletes a package by id
func DeletePackageByID(ctx context.Context, packageID int64) error {
_, err := db.GetEngine(ctx).ID(packageID).Delete(&Package{})
return err
}
// SetRepositoryLink sets the linked repository // SetRepositoryLink sets the linked repository
func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error { func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
_, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID}) _, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID})
@ -192,21 +198,20 @@ func GetPackagesByType(ctx context.Context, ownerID int64, packageType Type) ([]
Find(&ps) Find(&ps)
} }
// DeletePackagesIfUnreferenced deletes a package if there are no associated versions // FindUnreferencedPackages gets all packages without associated versions
func DeletePackagesIfUnreferenced(ctx context.Context) error { func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
in := builder. in := builder.
Select("package.id"). Select("package.id").
From("package"). From("package").
LeftJoin("package_version", "package_version.package_id = package.id"). LeftJoin("package_version", "package_version.package_id = package.id").
Where(builder.Expr("package_version.id IS NULL")) Where(builder.Expr("package_version.id IS NULL"))
_, err := db.GetEngine(ctx). ps := make([]*Package, 0, 10)
return ps, db.GetEngine(ctx).
// double select workaround for MySQL // double select workaround for MySQL
// https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition // https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition
Where(builder.In("package.id", builder.Select("id").From(in, "temp"))). Where(builder.In("package.id", builder.Select("id").From(in, "temp"))).
Delete(&Package{}) Find(&ps)
return err
} }
// HasOwnerPackages tests if a user/org has packages // HasOwnerPackages tests if a user/org has packages

View file

@ -21,9 +21,11 @@ const (
PropertyTypeVersion PropertyType = iota // 0 PropertyTypeVersion PropertyType = iota // 0
// PropertyTypeFile means the reference is a package file // PropertyTypeFile means the reference is a package file
PropertyTypeFile // 1 PropertyTypeFile // 1
// PropertyTypePackage means the reference is a package
PropertyTypePackage // 2
) )
// PackageProperty represents a property of a package version or file // PackageProperty represents a property of a package, version or file
type PackageProperty struct { type PackageProperty struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
RefType PropertyType `xorm:"INDEX NOT NULL"` RefType PropertyType `xorm:"INDEX NOT NULL"`
@ -68,3 +70,9 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
_, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{}) _, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{})
return err return err
} }
// DeletePropertyByName deletes properties by name
func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
return err
}

View file

@ -86,7 +86,13 @@ func updateUserAccess(accessMap map[int64]*userAccess, user *user_model.User, mo
// FIXME: do cross-comparison so reduce deletions and additions to the minimum? // FIXME: do cross-comparison so reduce deletions and additions to the minimum?
func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap map[int64]*userAccess) (err error) { func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap map[int64]*userAccess) (err error) {
minMode := perm.AccessModeRead minMode := perm.AccessModeRead
if !repo.IsPrivate { if err := repo.GetOwner(ctx); err != nil {
return fmt.Errorf("GetOwner: %v", err)
}
// If the repo isn't private and isn't owned by a organization,
// increase the minMode to Write.
if !repo.IsPrivate && !repo.Owner.IsOrganization() {
minMode = perm.AccessModeWrite minMode = perm.AccessModeWrite
} }

View file

@ -107,6 +107,8 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages") setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages")
setting.Git.HomePath = filepath.Join(setting.AppDataPath, "home")
if err = storage.Init(); err != nil { if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err) fatalTestError("storage.Init: %v\n", err)
} }

View file

@ -58,31 +58,7 @@ func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session {
cond = cond.And(builder.In("visibility", opts.Visible)) cond = cond.And(builder.In("visibility", opts.Visible))
} }
if opts.Actor != nil { cond = cond.And(BuildCanSeeUserCondition(opts.Actor))
var exprCond builder.Cond = builder.Expr("org_user.org_id = `user`.id")
// If Admin - they see all users!
if !opts.Actor.IsAdmin {
// Force visibility for privacy
var accessCond builder.Cond
if !opts.Actor.IsRestricted {
accessCond = builder.Or(
builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))),
builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
} else {
// restricted users only see orgs they are a member of
accessCond = builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID})))
}
// Don't forget about self
accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID})
cond = cond.And(accessCond)
}
} else {
// Force visibility for privacy
// Not logged in - only public users
cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
}
if opts.UID > 0 { if opts.UID > 0 {
cond = cond.And(builder.Eq{"id": opts.UID}) cond = cond.And(builder.Eq{"id": opts.UID})
@ -170,3 +146,26 @@ func IterateUser(f func(user *User) error) error {
} }
} }
} }
// BuildCanSeeUserCondition creates a condition which can be used to restrict results to users/orgs the actor can see
func BuildCanSeeUserCondition(actor *User) builder.Cond {
if actor != nil {
// If Admin - they see all users!
if !actor.IsAdmin {
// Users can see an organization they are a member of
cond := builder.In("`user`.id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": actor.ID}))
if !actor.IsRestricted {
// Not-Restricted users can see public and limited users/organizations
cond = cond.Or(builder.In("`user`.visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
}
// Don't forget about self
return cond.Or(builder.Eq{"`user`.id": actor.ID})
}
return nil
}
// Force visibility for privacy
// Not logged in - only public users
return builder.In("`user`.visibility", structs.VisibleTypePublic)
}

View file

@ -316,37 +316,45 @@ func (u *User) GenerateEmailActivateCode(email string) string {
} }
// GetUserFollowers returns range of user's followers. // GetUserFollowers returns range of user's followers.
func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) { func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
sess := db.GetEngine(db.DefaultContext). sess := db.GetEngine(ctx).
Select("`user`.*").
Join("LEFT", "follow", "`user`.id=follow.user_id").
Where("follow.follow_id=?", u.ID). Where("follow.follow_id=?", u.ID).
Join("LEFT", "follow", "`user`.id=follow.user_id") And(isUserVisibleToViewerCond(viewer))
if listOptions.Page != 0 { if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions) sess = db.SetSessionPagination(sess, &listOptions)
users := make([]*User, 0, listOptions.PageSize) users := make([]*User, 0, listOptions.PageSize)
return users, sess.Find(&users) count, err := sess.FindAndCount(&users)
return users, count, err
} }
users := make([]*User, 0, 8) users := make([]*User, 0, 8)
return users, sess.Find(&users) count, err := sess.FindAndCount(&users)
return users, count, err
} }
// GetUserFollowing returns range of user's following. // GetUserFollowing returns range of user's following.
func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) { func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
sess := db.GetEngine(db.DefaultContext). sess := db.GetEngine(db.DefaultContext).
Select("`user`.*").
Join("LEFT", "follow", "`user`.id=follow.follow_id").
Where("follow.user_id=?", u.ID). Where("follow.user_id=?", u.ID).
Join("LEFT", "follow", "`user`.id=follow.follow_id") And(isUserVisibleToViewerCond(viewer))
if listOptions.Page != 0 { if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions) sess = db.SetSessionPagination(sess, &listOptions)
users := make([]*User, 0, listOptions.PageSize) users := make([]*User, 0, listOptions.PageSize)
return users, sess.Find(&users) count, err := sess.FindAndCount(&users)
return users, count, err
} }
users := make([]*User, 0, 8) users := make([]*User, 0, 8)
return users, sess.Find(&users) count, err := sess.FindAndCount(&users)
return users, count, err
} }
// NewGitSig generates and returns the signature of given user. // NewGitSig generates and returns the signature of given user.
@ -485,6 +493,9 @@ func (u *User) GitName() string {
// ShortName ellipses username to length // ShortName ellipses username to length
func (u *User) ShortName(length int) string { func (u *User) ShortName(length int) string {
if setting.UI.DefaultShowFullName && len(u.FullName) > 0 {
return base.EllipsisString(u.FullName, length)
}
return base.EllipsisString(u.Name, length) return base.EllipsisString(u.Name, length)
} }
@ -1219,6 +1230,39 @@ func GetAdminUser() (*User, error) {
return &admin, nil return &admin, nil
} }
func isUserVisibleToViewerCond(viewer *User) builder.Cond {
if viewer != nil && viewer.IsAdmin {
return builder.NewCond()
}
if viewer == nil || viewer.IsRestricted {
return builder.Eq{
"`user`.visibility": structs.VisibleTypePublic,
}
}
return builder.Neq{
"`user`.visibility": structs.VisibleTypePrivate,
}.Or(
builder.In("`user`.id",
builder.
Select("`follow`.user_id").
From("follow").
Where(builder.Eq{"`follow`.follow_id": viewer.ID})),
builder.In("`user`.id",
builder.
Select("`team_user`.uid").
From("team_user").
Join("INNER", "`team_user` AS t2", "`team_user`.id = `t2`.id").
Where(builder.Eq{"`t2`.uid": viewer.ID})),
builder.In("`user`.id",
builder.
Select("`team_user`.uid").
From("team_user").
Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id").
Where(builder.Eq{"`t2`.uid": viewer.ID})))
}
// IsUserVisibleToViewer check if viewer is able to see user profile // IsUserVisibleToViewer check if viewer is able to see user profile
func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool { func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
if viewer != nil && viewer.IsAdmin { if viewer != nil && viewer.IsAdmin {

View file

@ -16,6 +16,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
@ -268,6 +269,7 @@ func APIContexter() func(http.Handler) http.Handler {
} }
} }
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["Context"] = &ctx ctx.Data["Context"] = &ctx

View file

@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
mc "code.gitea.io/gitea/modules/cache" mc "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -767,6 +768,7 @@ func Contexter() func(next http.Handler) http.Handler {
} }
} }
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["CsrfToken"] = ctx.csrf.GetToken() ctx.Data["CsrfToken"] = ctx.csrf.GetToken()

View file

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
) )
@ -52,14 +53,30 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
} }
if ctx.Package.Owner.IsOrganization() { if ctx.Package.Owner.IsOrganization() {
org := organization.OrgFromUser(ctx.Package.Owner)
// 1. Get user max authorize level for the org (may be none, if user is not member of the org) // 1. Get user max authorize level for the org (may be none, if user is not member of the org)
if ctx.Doer != nil { if ctx.Doer != nil {
var err error var err error
ctx.Package.AccessMode, err = organization.OrgFromUser(ctx.Package.Owner).GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID) ctx.Package.AccessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
if err != nil { if err != nil {
errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err) errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err)
return return
} }
// If access mode is less than write check every team for more permissions
if ctx.Package.AccessMode < perm.AccessModeWrite {
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
if err != nil {
errCb(http.StatusInternalServerError, "GetUserOrgTeams", err)
return
}
for _, t := range teams {
perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
if ctx.Package.AccessMode < perm {
ctx.Package.AccessMode = perm
}
}
}
} }
// 2. If authorize level is none, check if org is visible to user // 2. If authorize level is none, check if org is visible to user
if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) { if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {

View file

@ -105,23 +105,36 @@ type RunOpts struct {
PipelineFunc func(context.Context, context.CancelFunc) error PipelineFunc func(context.Context, context.CancelFunc) error
} }
func commonBaseEnvs() []string {
// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
envs := []string{
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
}
// some environment variables should be passed to git command
passThroughEnvKeys := []string{
"GNUPGHOME", // git may call gnupg to do commit signing
}
for _, key := range passThroughEnvKeys {
if val, ok := os.LookupEnv(key); ok {
envs = append(envs, key+"="+val)
}
}
return envs
}
// CommonGitCmdEnvs returns the common environment variables for a "git" command. // CommonGitCmdEnvs returns the common environment variables for a "git" command.
func CommonGitCmdEnvs() []string { func CommonGitCmdEnvs() []string {
// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it return append(commonBaseEnvs(), []string{
return []string{ "LC_ALL=" + DefaultLocale,
fmt.Sprintf("LC_ALL=%s", DefaultLocale), "GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3
"GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3 }...)
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
}
} }
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command // CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
func CommonCmdServEnvs() []string { func CommonCmdServEnvs() []string {
return []string{ return commonBaseEnvs()
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
}
} }
// Run runs the command with the RunOpts // Run runs the command with the RunOpts

View file

@ -11,6 +11,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
@ -126,8 +127,8 @@ func VersionInfo() string {
} }
func checkInit() error { func checkInit() error {
if setting.RepoRootPath == "" { if setting.Git.HomePath == "" {
return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly") return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
} }
if DefaultContext != nil { if DefaultContext != nil {
log.Warn("git module has been initialized already, duplicate init should be fixed") log.Warn("git module has been initialized already, duplicate init should be fixed")
@ -137,14 +138,14 @@ func checkInit() error {
// HomeDir is the home dir for git to store the global config file used by Gitea internally // HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string { func HomeDir() string {
if setting.RepoRootPath == "" { if setting.Git.HomePath == "" {
// strict check, make sure the git module is initialized correctly. // strict check, make sure the git module is initialized correctly.
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users. // attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons. // for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly") log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
return "" return ""
} }
return setting.RepoRootPath return setting.Git.HomePath
} }
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments. // InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
@ -175,11 +176,15 @@ func InitOnceWithSync(ctx context.Context) (err error) {
} }
initOnce.Do(func() { initOnce.Do(func() {
err = InitSimple(ctx) if err = InitSimple(ctx); err != nil {
if err != nil {
return return
} }
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
}
// Since git wire protocol has been released from git v2.18 // Since git wire protocol has been released from git v2.18
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
@ -206,7 +211,7 @@ func InitOnceWithSync(ctx context.Context) (err error) {
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem) // syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig() (err error) { func syncGitConfig() (err error) {
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil { if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err) return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
} }
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults" // Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"

View file

@ -21,12 +21,12 @@ import (
func testRun(m *testing.M) error { func testRun(m *testing.M) error {
_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`) _ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos") gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
if err != nil { if err != nil {
return fmt.Errorf("unable to create temp dir: %w", err) return fmt.Errorf("unable to create temp dir: %w", err)
} }
defer util.RemoveAll(repoRootPath) defer util.RemoveAll(gitHomePath)
setting.RepoRootPath = repoRootPath setting.Git.HomePath = gitHomePath
if err = InitOnceWithSync(context.Background()); err != nil { if err = InitOnceWithSync(context.Background()); err != nil {
return fmt.Errorf("failed to call Init: %w", err) return fmt.Errorf("failed to call Init: %w", err)

View file

@ -8,6 +8,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"errors"
"io" "io"
"path" "path"
"sort" "sort"
@ -62,9 +63,10 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
}) })
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
} else { return
_ = stdoutWriter.Close()
} }
_ = stdoutWriter.Close()
}() }()
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch // For simplicities sake we'll us a buffered reader to read from the cat-file --batch
@ -354,7 +356,7 @@ heaploop:
} }
current, err := g.Next(treepath, path2idx, changed, maxpathlen) current, err := g.Next(treepath, path2idx, changed, maxpathlen)
if err != nil { if err != nil {
if err == context.DeadlineExceeded { if errors.Is(err, context.DeadlineExceeded) {
break heaploop break heaploop
} }
g.Close() g.Close()

View file

@ -17,16 +17,23 @@ import (
) )
// AddCacheControlToHeader adds suitable cache-control headers to response // AddCacheControlToHeader adds suitable cache-control headers to response
func AddCacheControlToHeader(h http.Header, d time.Duration) { func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) {
directives := make([]string, 0, 2+len(additionalDirectives))
if setting.IsProd { if setting.IsProd {
h.Set("Cache-Control", "private, max-age="+strconv.Itoa(int(d.Seconds()))) if maxAge == 0 {
directives = append(directives, "no-store")
} else {
directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds())))
}
} else { } else {
h.Set("Cache-Control", "no-store") directives = append(directives, "no-store")
// to remind users they are using non-prod setting. // to remind users they are using non-prod setting.
// some users may be confused by "Cache-Control: no-store" in their setup if they did wrong to `RUN_MODE` in `app.ini`.
h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode) h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
h.Add("X-Gitea-Debug", "CacheControl=no-store")
} }
h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", "))
} }
// generateETag generates an ETag based on size, filename and file modification time // generateETag generates an ETag based on size, filename and file modification time

View file

@ -284,7 +284,7 @@ func (b *ElasticSearchIndexer) Index(ctx context.Context, repo *repo_model.Repos
reqs := make([]elastic.BulkableRequest, 0) reqs := make([]elastic.BulkableRequest, 0)
if len(changes.Updates) > 0 { if len(changes.Updates) > 0 {
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first! // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
if err := git.EnsureValidGitRepository(git.DefaultContext, repo.RepoPath()); err != nil { if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil {
log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err) log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
return err return err
} }

View file

@ -841,9 +841,10 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
// Repos with external issue trackers might still need to reference local PRs // Repos with external issue trackers might still need to reference local PRs
// We need to concern with the first one that shows up in the text, whichever it is // We need to concern with the first one that shows up in the text, whichever it is
if hasExtTrackFormat && !isNumericStyle { if hasExtTrackFormat && !isNumericStyle && refNumeric != nil {
// If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that // If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that
if foundNumeric && refNumeric.RefLocation.Start < ref.RefLocation.Start { // Allow a free-pass when non-numeric pattern wasn't found.
if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) {
found = foundNumeric found = foundNumeric
ref = refNumeric ref = refNumeric
} }

View file

@ -315,13 +315,5 @@ func IsMarkupFile(name, markup string) bool {
// Note that the '.' should be provided in ext, e.g ".md" // Note that the '.' should be provided in ext, e.g ".md"
func IsReadmeFile(name string, ext ...string) bool { func IsReadmeFile(name string, ext ...string) bool {
name = strings.ToLower(name) name = strings.ToLower(name)
if len(ext) > 0 { return name == "readme" || strings.Index(name, "readme.") == 0 || strings.Index(name, ".readme.") == 0
return name == "readme"+ext[0]
}
if len(name) < 6 {
return false
} else if len(name) == 6 {
return name == "readme"
}
return name[:7] == "readme."
} }

View file

@ -19,52 +19,52 @@ func (n NullDownloader) SetContext(_ context.Context) {}
// GetRepoInfo returns a repository information // GetRepoInfo returns a repository information
func (n NullDownloader) GetRepoInfo() (*Repository, error) { func (n NullDownloader) GetRepoInfo() (*Repository, error) {
return nil, &ErrNotSupported{Entity: "RepoInfo"} return nil, ErrNotSupported{Entity: "RepoInfo"}
} }
// GetTopics return repository topics // GetTopics return repository topics
func (n NullDownloader) GetTopics() ([]string, error) { func (n NullDownloader) GetTopics() ([]string, error) {
return nil, &ErrNotSupported{Entity: "Topics"} return nil, ErrNotSupported{Entity: "Topics"}
} }
// GetMilestones returns milestones // GetMilestones returns milestones
func (n NullDownloader) GetMilestones() ([]*Milestone, error) { func (n NullDownloader) GetMilestones() ([]*Milestone, error) {
return nil, &ErrNotSupported{Entity: "Milestones"} return nil, ErrNotSupported{Entity: "Milestones"}
} }
// GetReleases returns releases // GetReleases returns releases
func (n NullDownloader) GetReleases() ([]*Release, error) { func (n NullDownloader) GetReleases() ([]*Release, error) {
return nil, &ErrNotSupported{Entity: "Releases"} return nil, ErrNotSupported{Entity: "Releases"}
} }
// GetLabels returns labels // GetLabels returns labels
func (n NullDownloader) GetLabels() ([]*Label, error) { func (n NullDownloader) GetLabels() ([]*Label, error) {
return nil, &ErrNotSupported{Entity: "Labels"} return nil, ErrNotSupported{Entity: "Labels"}
} }
// GetIssues returns issues according start and limit // GetIssues returns issues according start and limit
func (n NullDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) { func (n NullDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) {
return nil, false, &ErrNotSupported{Entity: "Issues"} return nil, false, ErrNotSupported{Entity: "Issues"}
} }
// GetComments returns comments of an issue or PR // GetComments returns comments of an issue or PR
func (n NullDownloader) GetComments(commentable Commentable) ([]*Comment, bool, error) { func (n NullDownloader) GetComments(commentable Commentable) ([]*Comment, bool, error) {
return nil, false, &ErrNotSupported{Entity: "Comments"} return nil, false, ErrNotSupported{Entity: "Comments"}
} }
// GetAllComments returns paginated comments // GetAllComments returns paginated comments
func (n NullDownloader) GetAllComments(page, perPage int) ([]*Comment, bool, error) { func (n NullDownloader) GetAllComments(page, perPage int) ([]*Comment, bool, error) {
return nil, false, &ErrNotSupported{Entity: "AllComments"} return nil, false, ErrNotSupported{Entity: "AllComments"}
} }
// GetPullRequests returns pull requests according page and perPage // GetPullRequests returns pull requests according page and perPage
func (n NullDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) { func (n NullDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) {
return nil, false, &ErrNotSupported{Entity: "PullRequests"} return nil, false, ErrNotSupported{Entity: "PullRequests"}
} }
// GetReviews returns pull requests review // GetReviews returns pull requests review
func (n NullDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) { func (n NullDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) {
return nil, &ErrNotSupported{Entity: "Reviews"} return nil, ErrNotSupported{Entity: "Reviews"}
} }
// FormatCloneURL add authentication into remote URLs // FormatCloneURL add authentication into remote URLs

View file

@ -8,10 +8,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"regexp" "regexp"
"strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
goversion "github.com/hashicorp/go-version"
) )
const ( const (
@ -56,7 +55,9 @@ func NewRecipeReference(name, version, user, channel, revision string) (*RecipeR
if !namePattern.MatchString(name) { if !namePattern.MatchString(name) {
return nil, ErrValidation return nil, ErrValidation
} }
if _, err := goversion.NewSemver(version); err != nil {
v := strings.TrimSpace(version)
if v == "" {
return nil, ErrValidation return nil, ErrValidation
} }
if user != "" && !namePattern.MatchString(user) { if user != "" && !namePattern.MatchString(user) {
@ -69,7 +70,7 @@ func NewRecipeReference(name, version, user, channel, revision string) (*RecipeR
return nil, ErrValidation return nil, ErrValidation
} }
return &RecipeReference{name, version, user, channel, revision}, nil return &RecipeReference{name, v, user, channel, revision}, nil
} }
func (r *RecipeReference) RevisionOrDefault() string { func (r *RecipeReference) RevisionOrDefault() string {

View file

@ -34,6 +34,7 @@ func TestNewRecipeReference(t *testing.T) {
{"name", "1.0", "_", "_", "", true}, {"name", "1.0", "_", "_", "", true},
{"name", "1.0", "_", "_", "0", true}, {"name", "1.0", "_", "_", "0", true},
{"name", "1.0", "", "", "0", true}, {"name", "1.0", "", "", "0", true},
{"name", "1.0.0q", "", "", "0", true},
{"name", "1.0", "", "", "000000000000000000000000000000000000000000000000000000000000", false}, {"name", "1.0", "", "", "000000000000000000000000000000000000000000000000000000000000", false},
} }

View file

@ -16,6 +16,7 @@ import (
) )
const ( const (
PropertyRepository = "container.repository"
PropertyDigest = "container.digest" PropertyDigest = "container.digest"
PropertyMediaType = "container.mediatype" PropertyMediaType = "container.mediatype"
PropertyManifestTagged = "container.manifest.tagged" PropertyManifestTagged = "container.manifest.tagged"

View file

@ -80,7 +80,6 @@ type gemspec struct {
VersionRequirements requirement `yaml:"version_requirements"` VersionRequirements requirement `yaml:"version_requirements"`
} `yaml:"dependencies"` } `yaml:"dependencies"`
Description string `yaml:"description"` Description string `yaml:"description"`
Email string `yaml:"email"`
Executables []string `yaml:"executables"` Executables []string `yaml:"executables"`
Extensions []interface{} `yaml:"extensions"` Extensions []interface{} `yaml:"extensions"`
ExtraRdocFiles []string `yaml:"extra_rdoc_files"` ExtraRdocFiles []string `yaml:"extra_rdoc_files"`

View file

@ -27,7 +27,7 @@ func newAttachmentService() {
Attachment.Storage = getStorage("attachments", storageType, sec) Attachment.Storage = getStorage("attachments", storageType, sec)
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".docx,.gif,.gz,.jpeg,.jpg,.mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip") Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5) Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5)
Attachment.Enabled = sec.Key("ENABLED").MustBool(true) Attachment.Enabled = sec.Key("ENABLED").MustBool(true)

View file

@ -5,6 +5,7 @@
package setting package setting
import ( import (
"path/filepath"
"time" "time"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -13,6 +14,7 @@ import (
// Git settings // Git settings
var Git = struct { var Git = struct {
Path string Path string
HomePath string
DisableDiffHighlight bool DisableDiffHighlight bool
MaxGitDiffLines int MaxGitDiffLines int
MaxGitDiffLineCharacters int MaxGitDiffLineCharacters int
@ -67,7 +69,16 @@ var Git = struct {
} }
func newGit() { func newGit() {
if err := Cfg.Section("git").MapTo(&Git); err != nil { sec := Cfg.Section("git")
if err := sec.MapTo(&Git); err != nil {
log.Fatal("Failed to map Git settings: %v", err) log.Fatal("Failed to map Git settings: %v", err)
} }
Git.HomePath = sec.Key("HOME_PATH").MustString("home")
if !filepath.IsAbs(Git.HomePath) {
Git.HomePath = filepath.Join(AppDataPath, Git.HomePath)
} else {
Git.HomePath = filepath.Clean(Git.HomePath)
}
} }

View file

@ -170,7 +170,7 @@ var (
ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"},
KeygenPath: "ssh-keygen", KeygenPath: "ssh-keygen",
MinimumKeySizeCheck: true, MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048}, MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
PerWriteTimeout: PerWriteTimeout, PerWriteTimeout: PerWriteTimeout,
@ -843,8 +843,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.StartBuiltinServer = false SSH.StartBuiltinServer = false
} }
trustedUserCaKeys := sec.Key("SSH_TRUSTED_USER_CA_KEYS").Strings(",") SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
for _, caKey := range trustedUserCaKeys {
for _, caKey := range SSH.TrustedUserCAKeys {
pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey)) pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
if err != nil { if err != nil {
log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err) log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
@ -852,7 +853,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey) SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
} }
if len(trustedUserCaKeys) > 0 { if len(SSH.TrustedUserCAKeys) > 0 {
// Set the default as email,username otherwise we can leave it empty // Set the default as email,username otherwise we can leave it empty
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email") sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
} else { } else {
@ -861,22 +862,6 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(",")) SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
if !SSH.Disabled && !SSH.StartBuiltinServer {
if err := os.MkdirAll(SSH.RootPath, 0o700); err != nil {
log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
} else if err = os.MkdirAll(SSH.KeyTestPath, 0o644); err != nil {
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
}
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled {
fname := sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
if err := os.WriteFile(fname,
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0o600); err != nil {
log.Fatal("Failed to create '%s': %v", fname, err)
}
}
}
SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck) SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys() minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
for _, key := range minimumKeySizes { for _, key := range minimumKeySizes {

55
modules/ssh/init.go Normal file
View file

@ -0,0 +1,55 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package ssh
import (
"fmt"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
func Init() error {
if setting.SSH.Disabled {
return nil
}
if setting.SSH.StartBuiltinServer {
Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
log.Info("SSH server started on %s. Cipher list (%v), key exchange algorithms (%v), MACs (%v)",
net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort)),
setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs,
)
return nil
}
builtinUnused()
// FIXME: why 0o644 for a directory .....
if err := os.MkdirAll(setting.SSH.KeyTestPath, 0o644); err != nil {
return fmt.Errorf("failed to create directory %q for ssh key test: %w", setting.SSH.KeyTestPath, err)
}
if len(setting.SSH.TrustedUserCAKeys) > 0 && setting.SSH.AuthorizedPrincipalsEnabled {
caKeysFileName := setting.SSH.TrustedUserCAKeysFile
caKeysFileDir := filepath.Dir(caKeysFileName)
err := os.MkdirAll(caKeysFileDir, 0o700) // SSH.RootPath by default (That is `~/.ssh` in most cases)
if err != nil {
return fmt.Errorf("failed to create directory %q for ssh trusted ca keys: %w", caKeysFileDir, err)
}
if err := os.WriteFile(caKeysFileName, []byte(strings.Join(setting.SSH.TrustedUserCAKeys, "\n")), 0o600); err != nil {
return fmt.Errorf("failed to write ssh trusted ca keys to %q: %w", caKeysFileName, err)
}
}
return nil
}

View file

@ -11,6 +11,7 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -142,10 +143,14 @@ func sessionHandler(session ssh.Session) {
// Wait for the command to exit and log any errors we get // Wait for the command to exit and log any errors we get
err = cmd.Wait() err = cmd.Wait()
if err != nil { if err != nil {
log.Error("SSH: Wait: %v", err) // Cannot use errors.Is here because ExitError doesn't implement Is
// Thus errors.Is will do equality test NOT type comparison
if _, ok := err.(*exec.ExitError); !ok {
log.Error("SSH: Wait: %v", err)
}
} }
if err := session.Exit(getExitStatusFromError(err)); err != nil { if err := session.Exit(getExitStatusFromError(err)); err != nil && !errors.Is(err, io.EOF) {
log.Error("Session failed to exit. %s", err) log.Error("Session failed to exit. %s", err)
} }
} }

View file

@ -29,7 +29,7 @@ func listen(server *ssh.Server) {
log.Info("SSH Listener: %s Closed", server.Addr) log.Info("SSH Listener: %s Closed", server.Addr)
} }
// Unused informs our cleanup routine that we will not be using a ssh port // builtinUnused informs our cleanup routine that we will not be using a ssh port
func Unused() { func builtinUnused() {
graceful.GetManager().InformCleanup() graceful.GetManager().InformCleanup()
} }

View file

@ -97,6 +97,7 @@ type SubmitPullReviewOptions struct {
// DismissPullReviewOptions are options to dismiss a pull review // DismissPullReviewOptions are options to dismiss a pull review
type DismissPullReviewOptions struct { type DismissPullReviewOptions struct {
Message string `json:"message"` Message string `json:"message"`
Priors bool `json:"priors"`
} }
// PullReviewRequestOptions are options to add or remove pull review requests // PullReviewRequestOptions are options to add or remove pull review requests

View file

@ -70,6 +70,16 @@ func (ct SniffedType) IsRepresentableAsText() bool {
return ct.IsText() || ct.IsSvgImage() return ct.IsText() || ct.IsSvgImage()
} }
// IsBrowsableType returns whether a non-text type can be displayed in a browser
func (ct SniffedType) IsBrowsableBinaryType() bool {
return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio()
}
// GetMimeType returns the mime type
func (ct SniffedType) GetMimeType() string {
return strings.SplitN(ct.contentType, ";", 2)[0]
}
// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty. // DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
func DetectContentType(data []byte) SniffedType { func DetectContentType(data []byte) SniffedType {
if len(data) == 0 { if len(data) == 0 {

View file

@ -1531,7 +1531,8 @@ pulls.remove_prefix = Remove <strong>%s</strong> prefix
pulls.data_broken = This pull request is broken due to missing fork information. pulls.data_broken = This pull request is broken due to missing fork information.
pulls.files_conflicted = This pull request has changes conflicting with the target branch. pulls.files_conflicted = This pull request has changes conflicting with the target branch.
pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments." pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments."
pulls.is_empty = "This branch is equal with the target branch." pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge."
pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit."
pulls.required_status_check_failed = Some required checks were not successful. pulls.required_status_check_failed = Some required checks were not successful.
pulls.required_status_check_missing = Some required checks are missing. pulls.required_status_check_missing = Some required checks are missing.
pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. pulls.required_status_check_administrator = As an administrator, you may still merge this pull request.
@ -3037,6 +3038,7 @@ title = Packages
desc = Manage repository packages. desc = Manage repository packages.
empty = There are no packages yet. empty = There are no packages yet.
empty.documentation = For more information on the package registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">the documentation</a>. empty.documentation = For more information on the package registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">the documentation</a>.
empty.repo = Did you upload a package, but it's not shown here? Go to <a href="%[1]s">package settings</a> and link it to this repo.
filter.type = Type filter.type = Type
filter.type.all = All filter.type.all = All
filter.no_result = Your filter produced no results. filter.no_result = Your filter produced no results.

View file

@ -257,6 +257,7 @@ func ContainerRoutes() *web.Route {
r.Get("", container.ReqContainerAccess, container.DetermineSupport) r.Get("", container.ReqContainerAccess, container.DetermineSupport)
r.Get("/token", container.Authenticate) r.Get("/token", container.Authenticate)
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
r.Group("/{username}", func() { r.Group("/{username}", func() {
r.Group("/{image}", func() { r.Group("/{image}", func() {
r.Group("/blobs/uploads", func() { r.Group("/blobs/uploads", func() {

View file

@ -88,7 +88,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
for _, pd := range pds { for _, pd := range pds {
packageType := "" packageType := ""
for _, pvp := range pd.Properties { for _, pvp := range pd.VersionProperties {
if pvp.Name == composer_module.TypeProperty { if pvp.Name == composer_module.TypeProperty {
packageType = pvp.Value packageType = pvp.Value
break break

View file

@ -225,7 +225,7 @@ func UploadPackage(ctx *context.Context) {
SemverCompatible: true, SemverCompatible: true,
Creator: ctx.Doer, Creator: ctx.Doer,
Metadata: cp.Metadata, Metadata: cp.Metadata,
Properties: map[string]string{ VersionProperties: map[string]string{
composer_module.TypeProperty: cp.Type, composer_module.TypeProperty: cp.Type,
}, },
}, },

View file

@ -29,6 +29,7 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
contentStore := packages_module.NewContentStore() contentStore := packages_module.NewContentStore()
err := db.WithTx(func(ctx context.Context) error { err := db.WithTx(func(ctx context.Context) error {
created := true
p := &packages_model.Package{ p := &packages_model.Package{
OwnerID: pi.Owner.ID, OwnerID: pi.Owner.ID,
Type: packages_model.TypeContainer, Type: packages_model.TypeContainer,
@ -37,12 +38,21 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
} }
var err error var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil { if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
if err != packages_model.ErrDuplicatePackage { if err == packages_model.ErrDuplicatePackage {
created = false
} else {
log.Error("Error inserting package: %v", err) log.Error("Error inserting package: %v", err)
return err return err
} }
} }
if created {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil {
log.Error("Error setting package property: %v", err)
return err
}
}
pv := &packages_model.PackageVersion{ pv := &packages_model.PackageVersion{
PackageID: p.ID, PackageID: p.ID,
CreatorID: pi.Owner.ID, CreatorID: pi.Owner.ID,

View file

@ -112,7 +112,7 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost for anonymous access) // ReqContainerAccess is a middleware which checks the current user valid (real user or ghost for anonymous access)
func ReqContainerAccess(ctx *context.Context) { func ReqContainerAccess(ctx *context.Context) {
if ctx.Doer == nil { if ctx.Doer == nil {
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token"`) ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`)
apiErrorDefined(ctx, errUnauthorized) apiErrorDefined(ctx, errUnauthorized)
} }
} }
@ -151,6 +151,39 @@ func Authenticate(ctx *context.Context) {
}) })
} }
// https://docs.docker.com/registry/spec/api/#listing-repositories
func GetRepositoryList(ctx *context.Context) {
n := ctx.FormInt("n")
if n <= 0 || n > 100 {
n = 100
}
last := ctx.FormTrim("last")
repositories, err := container_model.GetRepositories(ctx, ctx.Doer, n, last)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
type RepositoryList struct {
Repositories []string `json:"repositories"`
}
if len(repositories) == n {
v := url.Values{}
if n > 0 {
v.Add("n", strconv.Itoa(n))
}
v.Add("last", repositories[len(repositories)-1])
ctx.Resp.Header().Set("Link", fmt.Sprintf(`</v2/_catalog?%s>; rel="next"`, v.Encode()))
}
jsonResponse(ctx, http.StatusOK, RepositoryList{
Repositories: repositories,
})
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#mounting-a-blob-from-another-repository // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#mounting-a-blob-from-another-repository
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks

View file

@ -267,6 +267,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
} }
func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) { func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) {
created := true
p := &packages_model.Package{ p := &packages_model.Package{
OwnerID: mci.Owner.ID, OwnerID: mci.Owner.ID,
Type: packages_model.TypeContainer, Type: packages_model.TypeContainer,
@ -275,12 +276,21 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
} }
var err error var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil { if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
if err != packages_model.ErrDuplicatePackage { if err == packages_model.ErrDuplicatePackage {
created = false
} else {
log.Error("Error inserting package: %v", err) log.Error("Error inserting package: %v", err)
return nil, err return nil, err
} }
} }
if created {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(mci.Owner.LowerName+"/"+mci.Image)); err != nil {
log.Error("Error setting package property: %v", err)
return nil, err
}
}
metadata.IsTagged = mci.IsTagged metadata.IsTagged = mci.IsTagged
metadataJSON, err := json.Marshal(metadata) metadataJSON, err := json.Marshal(metadata)

View file

@ -8,6 +8,7 @@ import (
"errors" "errors"
"net/http" "net/http"
"regexp" "regexp"
"strings"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -15,8 +16,6 @@ import (
packages_module "code.gitea.io/gitea/modules/packages" packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/routers/api/packages/helper" "code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages" packages_service "code.gitea.io/gitea/services/packages"
"github.com/hashicorp/go-version"
) )
var ( var (
@ -97,8 +96,7 @@ func UploadPackage(ctx *context.Context) {
Name: packageName, Name: packageName,
Version: packageVersion, Version: packageVersion,
}, },
SemverCompatible: true, Creator: ctx.Doer,
Creator: ctx.Doer,
}, },
&packages_service.PackageFileCreationInfo{ &packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{ PackageFileInfo: packages_service.PackageFileInfo{
@ -157,10 +155,10 @@ func sanitizeParameters(ctx *context.Context) (string, string, string, error) {
return "", "", "", errors.New("Invalid package name or filename") return "", "", "", errors.New("Invalid package name or filename")
} }
v, err := version.NewSemver(ctx.Params("packageversion")) packageVersion := strings.TrimSpace(ctx.Params("packageversion"))
if err != nil { if packageVersion == "" {
return "", "", "", err return "", "", "", errors.New("Invalid package version")
} }
return packageName, v.String(), filename, nil return packageName, packageVersion, filename, nil
} }

View file

@ -25,7 +25,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
for _, pd := range pds { for _, pd := range pds {
versions[pd.SemVer.String()] = createPackageMetadataVersion(registryURL, pd) versions[pd.SemVer.String()] = createPackageMetadataVersion(registryURL, pd)
for _, pvp := range pd.Properties { for _, pvp := range pd.VersionProperties {
if pvp.Name == npm_module.TagProperty { if pvp.Name == npm_module.TagProperty {
distTags[pvp.Value] = pd.Version.Version distTags[pvp.Value] = pd.Version.Version
} }

View file

@ -1010,7 +1010,7 @@ func Routes() *web.Route {
}, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
m.Group("/statuses", func() { m.Group("/statuses", func() {
m.Combo("/{sha}").Get(repo.GetCommitStatuses). m.Combo("/{sha}").Get(repo.GetCommitStatuses).
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
}, reqRepoReader(unit.TypeCode)) }, reqRepoReader(unit.TypeCode))
m.Group("/commits", func() { m.Group("/commits", func() {
m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits)

View file

@ -823,7 +823,7 @@ func DismissPullReview(ctx *context.APIContext) {
// "422": // "422":
// "$ref": "#/responses/validationError" // "$ref": "#/responses/validationError"
opts := web.GetForm(ctx).(*api.DismissPullReviewOptions) opts := web.GetForm(ctx).(*api.DismissPullReviewOptions)
dismissReview(ctx, opts.Message, true) dismissReview(ctx, opts.Message, true, opts.Priors)
} }
// UnDismissPullReview cancel to dismiss a review for a pull request // UnDismissPullReview cancel to dismiss a review for a pull request
@ -863,10 +863,10 @@ func UnDismissPullReview(ctx *context.APIContext) {
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
// "422": // "422":
// "$ref": "#/responses/validationError" // "$ref": "#/responses/validationError"
dismissReview(ctx, "", false) dismissReview(ctx, "", false, false)
} }
func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) { func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors bool) {
if !ctx.Repo.IsAdmin() { if !ctx.Repo.IsAdmin() {
ctx.Error(http.StatusForbidden, "", "Must be repo admin") ctx.Error(http.StatusForbidden, "", "Must be repo admin")
return return
@ -886,7 +886,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) {
return return
} }
_, err := pull_service.DismissReview(ctx, review.ID, msg, ctx.Doer, isDismiss) _, err := pull_service.DismissReview(ctx, review.ID, ctx.Repo.Repository.ID, msg, ctx.Doer, isDismiss, dismissPriors)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err) ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err)
return return

View file

@ -224,6 +224,7 @@ func CreateRelease(ctx *context.APIContext) {
rel.IsTag = false rel.IsTag = false
rel.Repo = ctx.Repo.Repository rel.Repo = ctx.Repo.Repository
rel.Publisher = ctx.Doer rel.Publisher = ctx.Doer
rel.Target = form.Target
if err = release_service.UpdateRelease(ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil { if err = release_service.UpdateRelease(ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) ctx.Error(http.StatusInternalServerError, "UpdateRelease", err)

View file

@ -240,6 +240,7 @@ func DeleteTopic(ctx *context.APIContext) {
if topic == nil { if topic == nil {
ctx.NotFound() ctx.NotFound()
return
} }
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusNoContent)

View file

@ -24,13 +24,13 @@ func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
} }
func listUserFollowers(ctx *context.APIContext, u *user_model.User) { func listUserFollowers(ctx *context.APIContext, u *user_model.User) {
users, err := user_model.GetUserFollowers(u, utils.GetListOptions(ctx)) users, count, err := user_model.GetUserFollowers(ctx, u, ctx.Doer, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
return return
} }
ctx.SetTotalCountHeader(int64(u.NumFollowers)) ctx.SetTotalCountHeader(count)
responseAPIUsers(ctx, users) responseAPIUsers(ctx, users)
} }
@ -86,13 +86,13 @@ func ListFollowers(ctx *context.APIContext) {
} }
func listUserFollowing(ctx *context.APIContext, u *user_model.User) { func listUserFollowing(ctx *context.APIContext, u *user_model.User) {
users, err := user_model.GetUserFollowing(u, utils.GetListOptions(ctx)) users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err) ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err)
return return
} }
ctx.SetTotalCountHeader(int64(u.NumFollowing)) ctx.SetTotalCountHeader(count)
responseAPIUsers(ctx, users) responseAPIUsers(ctx, users)
} }

View file

@ -7,12 +7,13 @@ package common
import ( import (
"fmt" "fmt"
"io" "io"
"net/url"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"code.gitea.io/gitea/modules/charset" charsetModule "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/httpcache"
@ -42,7 +43,7 @@ func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) err
} }
// ServeData download file from io.Reader // ServeData download file from io.Reader
func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { func ServeData(ctx *context.Context, filePath string, size int64, reader io.Reader) error {
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, err := util.ReadAtMost(reader, buf) n, err := util.ReadAtMost(reader, buf)
if err != nil { if err != nil {
@ -52,56 +53,73 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader)
buf = buf[:n] buf = buf[:n]
} }
ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 5*time.Minute)
if size >= 0 { if size >= 0 {
ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size))
} else { } else {
log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) log.Error("ServeData called to serve data: %s with size < 0: %d", filePath, size)
} }
name = path.Base(name)
// Google Chrome dislike commas in filenames, so let's change it to a space fileName := path.Base(filePath)
name = strings.ReplaceAll(name, ",", " ") sniffedType := typesniffer.DetectContentType(buf)
isPlain := sniffedType.IsText() || ctx.FormBool("render")
mimeType := ""
charset := ""
st := typesniffer.DetectContentType(buf)
mappedMimeType := ""
if setting.MimeTypeMap.Enabled { if setting.MimeTypeMap.Enabled {
fileExtension := strings.ToLower(filepath.Ext(name)) fileExtension := strings.ToLower(filepath.Ext(fileName))
mappedMimeType = setting.MimeTypeMap.Map[fileExtension] mimeType = setting.MimeTypeMap.Map[fileExtension]
} }
if st.IsText() || ctx.FormBool("render") {
cs, err := charset.DetectEncoding(buf) if mimeType == "" {
if err != nil { if sniffedType.IsBrowsableBinaryType() {
log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) mimeType = sniffedType.GetMimeType()
cs = "utf-8" } else if isPlain {
} mimeType = "text/plain"
if mappedMimeType == "" {
mappedMimeType = "text/plain"
}
ctx.Resp.Header().Set("Content-Type", mappedMimeType+"; charset="+strings.ToLower(cs))
} else {
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
if mappedMimeType != "" {
ctx.Resp.Header().Set("Content-Type", mappedMimeType)
}
if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) {
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
if st.IsSvgImage() || st.IsPDF() {
ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
if st.IsSvgImage() {
ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType)
} else {
ctx.Resp.Header().Set("Content-Type", typesniffer.ApplicationOctetStream)
}
}
} else { } else {
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) mimeType = typesniffer.ApplicationOctetStream
} }
} }
if isPlain {
charset, err = charsetModule.DetectEncoding(buf)
if err != nil {
log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err)
charset = "utf-8"
}
}
if charset != "" {
ctx.Resp.Header().Set("Content-Type", mimeType+"; charset="+strings.ToLower(charset))
} else {
ctx.Resp.Header().Set("Content-Type", mimeType)
}
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
isSVG := sniffedType.IsSvgImage()
// serve types that can present a security risk with CSP
if isSVG {
ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
} else if sniffedType.IsPDF() {
// no sandbox attribute for pdf as it breaks rendering in at least safari. this
// should generally be safe as scripts inside PDF can not escape the PDF document
// see https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion
ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'")
}
disposition := "inline"
if isSVG && !setting.UI.SVG.Enabled {
disposition = "attachment"
}
// encode filename per https://datatracker.ietf.org/doc/html/rfc5987
encodedFileName := `filename*=UTF-8''` + url.PathEscape(fileName)
ctx.Resp.Header().Set("Content-Disposition", disposition+"; "+encodedFileName)
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
_, err = ctx.Resp.Write(buf) _, err = ctx.Resp.Write(buf)
if err != nil { if err != nil {
return err return err

View file

@ -6,10 +6,8 @@ package routers
import ( import (
"context" "context"
"net"
"reflect" "reflect"
"runtime" "runtime"
"strconv"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
@ -143,7 +141,6 @@ func GlobalInitInstalled(ctx context.Context) {
mustInit(repo_service.Init) mustInit(repo_service.Init)
// Booting long running goroutines. // Booting long running goroutines.
cron.NewContext(ctx)
issue_indexer.InitIssueIndexer(false) issue_indexer.InitIssueIndexer(false)
code_indexer.Init() code_indexer.Init()
mustInit(stats_indexer.Init) mustInit(stats_indexer.Init)
@ -158,16 +155,13 @@ func GlobalInitInstalled(ctx context.Context) {
mustInitCtx(ctx, syncAppPathForGit) mustInitCtx(ctx, syncAppPathForGit)
if setting.SSH.StartBuiltinServer { mustInit(ssh.Init)
ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
log.Info("SSH server started on %s. Cipher list (%v), key exchange algorithms (%v), MACs (%v)",
net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort)),
setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
} else {
ssh.Unused()
}
auth.Init() auth.Init()
svg.Init() svg.Init()
// Finally start up the cron
cron.NewContext(ctx)
} }
// NormalRoutes represents non install routes // NormalRoutes represents non install routes

View file

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"path" "path"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -62,6 +63,7 @@ func installRecovery() func(next http.Handler) http.Handler {
"SignedUserName": "", "SignedUserName": "",
} }
httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform")
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
if !setting.IsProd { if !setting.IsProd {

View file

@ -5,7 +5,6 @@
package auth package auth
import ( import (
"encoding/base32"
"errors" "errors"
"net/http" "net/http"
@ -132,7 +131,7 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) {
} }
// Success! Get the credential and update the sign count with the new value we received. // Success! Get the credential and update the sign count with the new value we received.
dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, base32.HexEncoding.EncodeToString(cred.ID)) dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, cred.ID)
if err != nil { if err != nil {
ctx.ServerError("GetWebAuthnCredentialByCredID", err) ctx.ServerError("GetWebAuthnCredentialByCredID", err)
return return

View file

@ -158,6 +158,7 @@ func Recovery() func(next http.Handler) http.Handler {
store["SignedUserName"] = "" store["SignedUserName"] = ""
} }
httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform")
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
if !setting.IsProd { if !setting.IsProd {

View file

@ -24,6 +24,7 @@ import (
user_setting "code.gitea.io/gitea/routers/web/user/setting" user_setting "code.gitea.io/gitea/routers/web/user/setting"
"code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/org" "code.gitea.io/gitea/services/org"
container_service "code.gitea.io/gitea/services/packages/container"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
user_service "code.gitea.io/gitea/services/user" user_service "code.gitea.io/gitea/services/user"
) )
@ -88,6 +89,12 @@ func SettingsPost(ctx *context.Context) {
} }
return return
} }
if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil {
ctx.ServerError("UpdateRepositoryNames", err)
return
}
// reset ctx.org.OrgLink with new name // reset ctx.org.OrgLink with new name
ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name) ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name)
log.Trace("Organization name changed: %s -> %s", org.Name, form.Name) log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)

View file

@ -803,7 +803,8 @@ func NewIssue(ctx *context.Context) {
body := ctx.FormString("body") body := ctx.FormString("body")
ctx.Data["BodyQuery"] = body ctx.Data["BodyQuery"] = body
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects) isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects)
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment") upload.AddUploadContext(ctx, "comment")
@ -819,7 +820,7 @@ func NewIssue(ctx *context.Context) {
} }
projectID := ctx.FormInt64("project") projectID := ctx.FormInt64("project")
if projectID > 0 { if projectID > 0 && isProjectsEnabled {
project, err := project_model.GetProjectByID(ctx, projectID) project, err := project_model.GetProjectByID(ctx, projectID)
if err != nil { if err != nil {
log.Error("GetProjectByID: %d: %v", projectID, err) log.Error("GetProjectByID: %d: %v", projectID, err)
@ -1043,6 +1044,11 @@ func NewIssuePost(ctx *context.Context) {
} }
if projectID > 0 { if projectID > 0 {
if !ctx.Repo.CanRead(unit.TypeProjects) {
// User must also be able to see the project.
ctx.Error(http.StatusBadRequest, "user hasn't permissions to read projects")
return
}
if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil {
ctx.ServerError("ChangeProjectAssign", err) ctx.ServerError("ChangeProjectAssign", err)
return return
@ -1783,6 +1789,10 @@ func getActionIssues(ctx *context.Context) []*issues_model.Issue {
issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues) issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues)
prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests) prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
for _, issue := range issues { for _, issue := range issues {
if issue.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect"))
return nil
}
if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
return nil return nil

View file

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -60,6 +61,9 @@ func Packages(ctx *context.Context) {
ctx.Data["Query"] = query ctx.Data["Query"] = query
ctx.Data["PackageType"] = packageType ctx.Data["PackageType"] = packageType
ctx.Data["HasPackages"] = hasPackages ctx.Data["HasPackages"] = hasPackages
if ctx.Repo != nil {
ctx.Data["CanWritePackages"] = ctx.IsUserRepoWriter([]unit.Type{unit.TypePackages}) || ctx.IsUserSiteAdmin()
}
ctx.Data["PackageDescriptors"] = pds ctx.Data["PackageDescriptors"] = pds
ctx.Data["Total"] = total ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository

View file

@ -5,6 +5,7 @@
package repo package repo
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
@ -633,10 +634,17 @@ func MoveIssues(ctx *context.Context) {
} }
if len(movedIssues) != len(form.Issues) { if len(movedIssues) != len(form.Issues) {
ctx.ServerError("IssuesNotFound", err) ctx.ServerError("some issues do not exist", errors.New("some issues do not exist"))
return return
} }
for _, issue := range movedIssues {
if issue.RepoID != project.RepoID {
ctx.ServerError("Some issue's repoID is not equal to project's repoID", errors.New("Some issue's repoID is not equal to project's repoID"))
return
}
}
if err = project_model.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil { if err = project_model.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil {
ctx.ServerError("MoveIssuesOnProjectBoard", err) ctx.ServerError("MoveIssuesOnProjectBoard", err)
return return

View file

@ -5,6 +5,7 @@
package repo package repo
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
@ -118,6 +119,11 @@ func UpdateResolveConversation(ctx *context.Context) {
return return
} }
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound("comment's repoID is incorrect", errors.New("comment's repoID is incorrect"))
return
}
var permResult bool var permResult bool
if permResult, err = issues_model.CanMarkConversation(comment.Issue, ctx.Doer); err != nil { if permResult, err = issues_model.CanMarkConversation(comment.Issue, ctx.Doer); err != nil {
ctx.ServerError("CanMarkConversation", err) ctx.ServerError("CanMarkConversation", err)
@ -236,7 +242,7 @@ func SubmitReview(ctx *context.Context) {
// DismissReview dismissing stale review by repo admin // DismissReview dismissing stale review by repo admin
func DismissReview(ctx *context.Context) { func DismissReview(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.DismissReviewForm) form := web.GetForm(ctx).(*forms.DismissReviewForm)
comm, err := pull_service.DismissReview(ctx, form.ReviewID, form.Message, ctx.Doer, true) comm, err := pull_service.DismissReview(ctx, form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.Doer, true, true)
if err != nil { if err != nil {
ctx.ServerError("pull_service.DismissReview", err) ctx.ServerError("pull_service.DismissReview", err)
return return

View file

@ -474,7 +474,7 @@ func SettingsPost(ctx *context.Context) {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
} }
if form.EnablePackages && !unit_model.TypeProjects.UnitGlobalDisabled() { if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
units = append(units, repo_model.RepoUnit{ units = append(units, repo_model.RepoUnit{
RepoID: repo.ID, RepoID: repo.ID,
Type: unit_model.TypePackages, Type: unit_model.TypePackages,

View file

@ -854,15 +854,15 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
} }
ctx.Data["LatestCommitVerification"] = verification ctx.Data["LatestCommitVerification"] = verification
ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(latestCommit) ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(latestCommit)
}
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), db.ListOptions{}) statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptions{})
if err != nil { if err != nil {
log.Error("GetLatestCommitStatus: %v", err) log.Error("GetLatestCommitStatus: %v", err)
} }
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["LatestCommitStatuses"] = statuses ctx.Data["LatestCommitStatuses"] = statuses
}
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
treeLink := branchLink treeLink := branchLink
@ -902,10 +902,14 @@ func renderCode(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true ctx.Data["PageIsViewCode"] = true
if ctx.Repo.Repository.IsEmpty { if ctx.Repo.Repository.IsEmpty {
reallyEmpty, err := ctx.Repo.GitRepo.IsEmpty() reallyEmpty := true
if err != nil { var err error
ctx.ServerError("GitRepo.IsEmpty", err) if ctx.Repo.GitRepo != nil {
return reallyEmpty, err = ctx.Repo.GitRepo.IsEmpty()
if err != nil {
ctx.ServerError("GitRepo.IsEmpty", err)
return
}
} }
if reallyEmpty { if reallyEmpty {
ctx.HTML(http.StatusOK, tplRepoEMPTY) ctx.HTML(http.StatusOK, tplRepoEMPTY)

View file

@ -591,6 +591,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
LabelIDs: opts.LabelIDs, LabelIDs: opts.LabelIDs,
Org: org, Org: org,
Team: team, Team: team,
RepoCond: opts.RepoCond,
} }
issueStats, err = issues_model.GetUserIssueStats(statsOpts) issueStats, err = issues_model.GetUserIssueStats(statsOpts)

View file

@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container" container_model "code.gitea.io/gitea/models/packages/container"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
@ -91,6 +92,21 @@ func ListPackages(ctx *context.Context) {
ctx.Data["Total"] = total ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = repositoryAccessMap ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
// TODO: context/org -> HandleOrgAssignment() can not be used
if ctx.ContextUser.IsOrganization() {
org := org_model.OrgFromUser(ctx.ContextUser)
ctx.Data["Org"] = org
ctx.Data["OrgLink"] = ctx.ContextUser.OrganisationLink()
if ctx.Doer != nil {
ctx.Data["IsOrganizationMember"], _ = org_model.IsOrganizationMember(ctx, org.ID, ctx.Doer.ID)
ctx.Data["IsOrganizationOwner"], _ = org_model.IsOrganizationOwner(ctx, org.ID, ctx.Doer.ID)
} else {
ctx.Data["IsOrganizationMember"] = false
ctx.Data["IsOrganizationOwner"] = false
}
}
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParam(ctx, "q", "Query") pager.AddParam(ctx, "q", "Query")
pager.AddParam(ctx, "type", "PackageType") pager.AddParam(ctx, "type", "PackageType")

View file

@ -157,7 +157,7 @@ func Profile(ctx *context.Context) {
switch tab { switch tab {
case "followers": case "followers":
items, err := user_model.GetUserFollowers(ctx.ContextUser, db.ListOptions{ items, count, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum, PageSize: setting.UI.User.RepoPagingNum,
Page: page, Page: page,
}) })
@ -167,9 +167,9 @@ func Profile(ctx *context.Context) {
} }
ctx.Data["Cards"] = items ctx.Data["Cards"] = items
total = ctx.ContextUser.NumFollowers total = int(count)
case "following": case "following":
items, err := user_model.GetUserFollowing(ctx.ContextUser, db.ListOptions{ items, count, err := user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum, PageSize: setting.UI.User.RepoPagingNum,
Page: page, Page: page,
}) })
@ -179,7 +179,7 @@ func Profile(ctx *context.Context) {
} }
ctx.Data["Cards"] = items ctx.Data["Cards"] = items
total = ctx.ContextUser.NumFollowing total = int(count)
case "activity": case "activity":
ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{ ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
RequestedUser: ctx.ContextUser, RequestedUser: ctx.ContextUser,

View file

@ -34,6 +34,7 @@ func Account(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAccount"] = true ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.Doer.Email ctx.Data["Email"] = ctx.Doer.Email
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
loadAccountData(ctx) loadAccountData(ctx)

View file

@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/agit" "code.gitea.io/gitea/services/agit"
"code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/forms"
container_service "code.gitea.io/gitea/services/packages/container"
user_service "code.gitea.io/gitea/services/user" user_service "code.gitea.io/gitea/services/user"
) )
@ -90,6 +91,11 @@ func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName s
return err return err
} }
if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil {
ctx.ServerError("UpdateRepositoryNames", err)
return err
}
log.Trace("User name changed: %s -> %s", user.Name, newName) log.Trace("User name changed: %s -> %s", user.Name, newName)
return nil return nil
} }

View file

@ -898,7 +898,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
m.Post("/projects", reqRepoIssuesOrPullsWriter, repo.UpdateIssueProject) m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
m.Post("/dismiss_review", reqRepoAdmin, bindIgnErr(forms.DismissReviewForm{}), repo.DismissReview) m.Post("/dismiss_review", reqRepoAdmin, bindIgnErr(forms.DismissReviewForm{}), repo.DismissReview)

View file

@ -199,7 +199,7 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool {
// List all group memberships of a user // List all group memberships of a user
func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string { func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string {
var ldapGroups []string var ldapGroups []string
groupFilter := fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, uid) groupFilter := fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, ldap.EscapeFilter(uid))
result, err := l.Search(ldap.NewSearchRequest( result, err := l.Search(ldap.NewSearchRequest(
ls.GroupDN, ls.GroupDN,
ldap.ScopeWholeSubtree, ldap.ScopeWholeSubtree,

View file

@ -15,6 +15,17 @@ import (
) )
func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) error { func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) error {
// Only check if milestone exists if we don't remove it.
if issue.MilestoneID > 0 {
has, err := issues_model.HasMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID)
if err != nil {
return fmt.Errorf("HasMilestoneByRepoID: %v", err)
}
if !has {
return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist")
}
}
if err := issues_model.UpdateIssueCols(ctx, issue, "milestone_id"); err != nil { if err := issues_model.UpdateIssueCols(ctx, issue, "milestone_id"); err != nil {
return err return err
} }

Some files were not shown because too many files have changed in this diff Show more