diff --git a/.drone.yml b/.drone.yml index e766ba613424..5f26a4c287eb 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,12 +15,12 @@ trigger: steps: - name: deps-frontend pull: always - image: node:16 + image: node:14 commands: - make node_modules - name: lint-frontend - image: node:16 + image: node:14 commands: - make lint-frontend depends_on: [deps-frontend] @@ -58,7 +58,7 @@ steps: TAGS: bindata gogit sqlite sqlite_unlock_notify - name: checks-frontend - image: node:16 + image: node:14 commands: - make checks-frontend depends_on: [deps-frontend] @@ -71,20 +71,20 @@ steps: depends_on: [lint-backend] - name: test-frontend - image: node:16 + image: node:14 commands: - make test-frontend depends_on: [lint-frontend] - name: build-frontend - image: node:16 + image: node:14 commands: - make frontend depends_on: [test-frontend] - name: build-backend-no-gcc pull: always - image: golang:1.14 # this step is kept as the lowest version of golang that we support + image: golang:1.16 # this step is kept as the lowest version of golang that we support environment: GO111MODULE: on GOPROXY: off @@ -404,7 +404,7 @@ steps: - name: update pull: default - image: alpine:3.14 + image: alpine:3.13 commands: - ./build/update-locales.sh @@ -503,7 +503,7 @@ steps: pull: always image: techknowlogick/xgo:go-1.16.x commands: - - curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs + - curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs - export PATH=$PATH:$GOPATH/bin - make release environment: @@ -599,7 +599,7 @@ steps: pull: always image: techknowlogick/xgo:go-1.16.x commands: - - curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs + - curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs - export PATH=$PATH:$GOPATH/bin - make release environment: diff --git a/.eslintrc b/.eslintrc index 438fe404ffb6..3e66bd7aeb59 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,6 +2,7 @@ root: true reportUnusedDisableDirectives: true ignorePatterns: + - /web_src/js/vendor - /templates/base/head.tmpl - /templates/repo/activity.tmpl - /templates/repo/view_file.tmpl diff --git a/CHANGELOG.md b/CHANGELOG.md index 63a49e7f07dc..3a7d159b03ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 been added to each release, please refer to the [blog](https://blog.gitea.io). -## [1.15.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.15.0-rc1) - 2021-07-15 +## [1.15.0](https://github.com/go-gitea/gitea/releases/tag/v1.15.0) - 2021-08-21 * BREAKING * Make app.ini permissions more restrictive (#16266) @@ -19,9 +19,15 @@ been added to each release, please refer to the [blog](https://blog.gitea.io). * Move (custom) assets into subpath `/assets` (#15219) * Use level config in log section when sub log section not set level (#15176) * Links in markdown should be absolute to the repository not the server (#15088) + * Upgrade to the latest version of golang-jwt (#16590) (#16606) + * Set minimum supported version of go to 1.16 (#16710) * SECURITY * Encrypt LDAP bind password in db with SECRET_KEY (#15547) * Remove random password in Dockerfiles (#15362) + * Upgrade to the latest version of golang-jwt and increase minimum go to 1.15 (#16590) (#16606) + * Correctly create of git-daemon-export-ok files (#16508) (#16514) + * Don't show private user's repo in explore view (#16550) (#16554) + * Update node tar dependency to 6.1.6 (#16622) (#16623) * FEATURES * Update Go-Git to take advantage of LargeObjectThreshold (#16316) * Support custom mime type mapping for text files (#16304) @@ -42,7 +48,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io). * Add LFS Migration and Mirror (#14726) * Improve notifications for WIP draft PR's (#14663) * Disable Stars config option (#14653) - * Add option to provide signature for a token to verify key ownership (#14054) + * GPG Key Ownership verification with Signed Token (#14054) * OAuth2 auto-register (#5123) * API * Return updated repository when changing repository using API (#16420) @@ -62,6 +68,8 @@ been added to each release, please refer to the [blog](https://blog.gitea.io). * Add Active and ProhibitLogin to API (#15689) * Add Location, Website and Description to API (#15675) * Expose resolver via API (#15167) + * Swagger AccessToken fixes (#16574) (#16597) + * Set AllowedHeaders on API CORS handler (#16524) (#16618) * ENHANCEMENTS * Support HTTP/2 in Let's Encrypt (#16371) * Introduce NotifySubjectType (#16320) @@ -187,6 +195,41 @@ been added to each release, please refer to the [blog](https://blog.gitea.io). * Add NeedPostProcess for Parser interface to improve performance of csv parser and some external parser (#15153) * Add code block highlight to orgmode back (#14222) * Remove User.GetOrganizations() (#14032) + * Restore Accessibility for Dropdown (#16576) (#16617) + * Pass down SignedUserName down to AccessLogger context (#16605) (#16616) + * Fix table alignment in markdown (#16596) (#16602) + * Fix 500 on first wiki page (#16586) (#16598) + * Lock goth/gothic and Re-attempt OAuth2 registration on login if registration failed at startup (#16564) (#16570) + * Upgrade levelqueue to v0.4.0 (#16560) (#16561) + * Handle too long PR titles correctly (#16517) (#16549) + * Fix data race in bleve indexer (#16474) (#16509) + * Restore CORS on git smart http protocol (#16496) (#16506) + * Fix race in log (#16490) (#16505) + * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498) + * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480) + * Update notification table with only latest data (#16445) (#16469) + * Fix crash following ldap authentication update (#16447) (#16448) + * Fix direct creation of external users on admin page (partial #16612) (#16613) + * Prevent 500 on draft releases without tag (#16634) (#16636) + * Restore creation of git-daemon-export-ok files (#16508) (#16514) + * Fix data race in bleve indexer (#16474) (#16509) + * Restore CORS on git smart http protocol (#16496) (#16506) + * Fix race in log (#16490) (#16505) + * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498) + * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480) + * Update notification table with only latest data (#16445) (#16469) + * Fix crash following ldap authentication update (#16447) (#16448) + * Restore compatibility with SQLServer 2008 R2 in migrations (#16638) + * Fix direct creation of external users on admin page (#16613) + * Fix go-git implementation of GetNote when passed a non-existent commit (#16658) (#16659) + * Fix NPE in fuzzer (#16680) (#16682) + * Set issue_index when finishing migration (#16685) (#16687) + * Skip patch download when no patch file exists (#16356) (#16681) + * Ensure empty lines are copiable and final new line too (#16678) (#16692) + * Fix wrong user in OpenID response (#16736) (#16741) + * Do not use thin scrollbars on Firefox (#16738) (#16745) + * Recreate Tables should Recreate indexes on MySQL (#16718) (#16739) + * Keep attachments on tasklist update (#16750) (#16757) * TESTING * Bump `postgres` and `mysql` versions (#15710) * Add tests for clone from wiki (#15513) @@ -197,7 +240,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io). * Fix mirror_lfs source string in en-US locale (#15369) * BUILD * Upgrade xorm to v1.1.1 (#16339) - * Alpine 3.14 released (#16170) * Disable legal comments in esbuild (#15929) * Switch to Node 16 to build fronted (#15804) * Use esbuild to minify CSS (#15756) @@ -216,6 +258,28 @@ been added to each release, please refer to the [blog](https://blog.gitea.io). * Remove utf8 option from installation page (#16126) * Use Wants= over Requires= in systemd file (#15897) +## [1.14.6](https://github.com/go-gitea/gitea/releases/tag/v1.14.6) - 2021-08-04 + +* SECURITY + * Bump github.com/markbates/goth from v1.67.1 to v1.68.0 (#16538) (#16540) + * Switch to maintained JWT lib (#16532) (#16535) + * Upgrade to latest version of golang-jwt (as forked for 1.14) (#16590) (#16607) +* BUGFIXES + * Add basic edit ldap auth test & actually fix #16252 (#16465) (#16495) + * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16481) + +## [1.14.5](https://github.com/go-gitea/gitea/releases/tag/v1.14.5) - 2021-07-16 + +* SECURITY + * Hide mirror passwords on repo settings page (#16022) (#16355) + * Update bluemonday to v1.0.15 (#16379) (#16380) +* BUGFIXES + * Retry rename on lock induced failures (#16435) (#16439) + * Validate issue index before querying DB (#16406) (#16410) + * Fix crash following ldap authentication update (#16447) (#16449) +* ENHANCEMENTS + * Redirect on bad CSRF instead of presenting bad page (#14937) (#16378) + ## [1.14.4](https://github.com/go-gitea/gitea/releases/tag/v1.14.4) - 2021-07-06 * BUGFIXES diff --git a/Dockerfile b/Dockerfile index 43e0de40cc87..1234ba13bef5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ################################### #Build stage -FROM golang:1.16-alpine3.14 AS build-env +FROM golang:1.16-alpine3.13 AS build-env ARG GOPROXY ENV GOPROXY ${GOPROXY:-direct} @@ -26,7 +26,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \ # Begin env-to-ini build RUN go build contrib/environment-to-ini/environment-to-ini.go -FROM alpine:3.14 +FROM alpine:3.13 LABEL maintainer="maintainers@gitea.io" RUN set -x && \ diff --git a/Dockerfile.rootless b/Dockerfile.rootless index d3ba15f109da..a0cc20095c13 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,7 +1,7 @@ ################################### #Build stage -FROM golang:1.16-alpine3.14 AS build-env +FROM golang:1.16-alpine3.13 AS build-env ARG GOPROXY ENV GOPROXY ${GOPROXY:-direct} @@ -25,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \ # Begin env-to-ini build RUN go build contrib/environment-to-ini/environment-to-ini.go -FROM alpine:3.14 +FROM alpine:3.13 LABEL maintainer="maintainers@gitea.io" EXPOSE 2222 3000 diff --git a/Makefile b/Makefile index 8ee74cbecc40..0e244d66a9cd 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" ) COMMA := , XGO_VERSION := go-1.16.x -MIN_GO_VERSION := 001014000 +MIN_GO_VERSION := 001016000 MIN_NODE_VERSION := 012017000 DOCKER_IMAGE ?= aswild/gitea @@ -217,7 +217,7 @@ help: go-check: $(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');)) @if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \ - echo "Gitea requires Go 1.14 or greater to build. You can get it at https://golang.org/dl/"; \ + echo "Gitea requires Go 1.16 or greater to build. You can get it at https://golang.org/dl/"; \ exit 1; \ fi @@ -729,6 +729,7 @@ fomantic: cd $(FOMANTIC_WORK_DIR) && npm install --no-save cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/ + cp -f web_src/js/vendor/dropdown.js $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/definitions/modules cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build .PHONY: webpack diff --git a/cmd/serv.go b/cmd/serv.go index 97ae901d270e..3b180fc62680 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -23,7 +23,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/lfs" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt" jsoniter "github.com/json-iterator/go" "github.com/kballard/go-shellquote" "github.com/urfave/cli" diff --git a/docs/config.yaml b/docs/config.yaml index 451984e09d7c..a0f39987f0b5 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -18,8 +18,8 @@ params: description: Git with a cup of tea author: The Gitea Authors website: https://docs.gitea.io - version: 1.14.4 - minGoVersion: 1.14 + version: 1.14.6 + minGoVersion: 1.16 goVersion: 1.16 minNodeVersion: 12.17 diff --git a/docs/content/doc/advanced/adding-legal-pages.en-us.md b/docs/content/doc/advanced/adding-legal-pages.en-us.md index b13337a4ab00..8535a6e865a2 100644 --- a/docs/content/doc/advanced/adding-legal-pages.en-us.md +++ b/docs/content/doc/advanced/adding-legal-pages.en-us.md @@ -32,7 +32,7 @@ You absolutely must not place a general ToS or privacy statement that implies th Create or append to `/path/to/custom/templates/custom/extra_links_footer.tmpl`: ```go -Privacy Policy +Privacy Policy ``` Restart Gitea to see the changes. diff --git a/docs/content/doc/advanced/customizing-gitea.en-us.md b/docs/content/doc/advanced/customizing-gitea.en-us.md index cbb749412f03..8ea7a8d129ee 100644 --- a/docs/content/doc/advanced/customizing-gitea.en-us.md +++ b/docs/content/doc/advanced/customizing-gitea.en-us.md @@ -102,7 +102,7 @@ For instance, let's say you are in Germany and must add the famously legally-req just place it under your "$GITEA_CUSTOM/public/" directory (for instance `$GITEA_CUSTOM/public/impressum.html`) and put a link to it in either `$GITEA_CUSTOM/templates/custom/extra_links.tmpl` or `$GITEA_CUSTOM/templates/custom/extra_links_footer.tmpl`. To match the current style, the link should have the class name "item", and you can use `{{AppSubUrl}}` to get the base URL: -`Impressum` +`Impressum` For more information, see [Adding Legal Pages](https://docs.gitea.io/en-us/adding-legal-pages). @@ -174,13 +174,13 @@ You can display STL file directly in Gitea by adding: if ($('.view-raw>a[href$=".stl" i]').length) { $("body").append( - '' + '' ); Promise.all([ - lS("/Madeleine.js/src/lib/stats.js"), - lS("/Madeleine.js/src/lib/detector.js"), - lS("/Madeleine.js/src/lib/three.min.js"), - lS("/Madeleine.js/src/Madeleine.js"), + lS("/assets/Madeleine.js/src/lib/stats.js"), + lS("/assets/Madeleine.js/src/lib/detector.js"), + lS("/assets/Madeleine.js/src/lib/three.min.js"), + lS("/assets/Madeleine.js/src/Madeleine.js"), ]).then(function () { $(".view-raw") .attr("id", "view-raw") @@ -188,7 +188,7 @@ You can display STL file directly in Gitea by adding: new Madeleine({ target: "view-raw", data: $('.view-raw>a[href$=".stl" i]').attr("href"), - path: "/Madeleine.js/src", + path: "/assets/Madeleine.js/src", }); $('.view-raw>a[href$=".stl"]').remove(); }); diff --git a/docs/content/doc/advanced/customizing-gitea.zh-cn.md b/docs/content/doc/advanced/customizing-gitea.zh-cn.md index 4640f878ad0b..bb55f35e178f 100644 --- a/docs/content/doc/advanced/customizing-gitea.zh-cn.md +++ b/docs/content/doc/advanced/customizing-gitea.zh-cn.md @@ -61,7 +61,7 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板 "custom/public/"目录下(比如 `custom/public/impressum.html`)并且将它与 `custom/templates/custom/extra_links.tmpl` 链接起来即可。 这个链接应当使用一个名为“item”的 class 来匹配当前样式,您可以使用 `{{AppSubUrl}}` 来获取 base URL: -`Impressum` +`Impressum` 同理,您可以将页签添加到 `extra_tabs.tmpl` 中,使用同样的方式来添加页签。它的具体样式需要与 `templates/repo/header.tmpl` 中已有的其他选项卡的样式匹配 diff --git a/docs/content/doc/advanced/external-renderers.en-us.md b/docs/content/doc/advanced/external-renderers.en-us.md index c0109b801409..e5de7e8efd7d 100644 --- a/docs/content/doc/advanced/external-renderers.en-us.md +++ b/docs/content/doc/advanced/external-renderers.en-us.md @@ -164,5 +164,5 @@ And so you could write some CSS: Add your stylesheet to your custom directory e.g `custom/public/css/my-style-XXXXX.css` and import it using a custom header file `custom/templates/custom/header.tmpl`: ```html - + ``` diff --git a/go.mod b/go.mod index 5032acce990f..40f6ade2bd33 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee - gitea.com/lunny/levelqueue v0.3.0 + gitea.com/lunny/levelqueue v0.4.1 github.com/Microsoft/go-winio v0.5.0 // indirect github.com/NYTimes/gziphandler v1.1.1 github.com/ProtonMail/go-crypto v0.0.0-20210705153151-cc34b1f6908b // indirect @@ -28,7 +28,6 @@ require ( github.com/couchbase/gomemcached v0.1.2 // indirect github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect github.com/denisenkom/go-mssqldb v0.10.0 - github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 github.com/dustin/go-humanize v1.0.0 @@ -51,6 +50,7 @@ require ( github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/snappy v0.0.4 // indirect github.com/google/go-github/v32 v32.1.0 github.com/google/go-querystring v1.1.0 // indirect @@ -75,7 +75,7 @@ require ( github.com/lafriks/xormstore v1.4.0 github.com/lib/pq v1.10.2 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 - github.com/markbates/goth v1.67.1 + github.com/markbates/goth v1.68.0 github.com/mattn/go-isatty v0.0.13 github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-sqlite3 v1.14.7 @@ -143,3 +143,5 @@ require ( ) replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 + +replace github.com/golang-jwt/jwt v3.2.1+incompatible => github.com/golang-jwt/jwt v3.2.2+incompatible diff --git a/go.sum b/go.sum index fc6e2422c849..64d44eb34aef 100644 --- a/go.sum +++ b/go.sum @@ -49,8 +49,8 @@ gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e h1:YjaQU6XFicdhPN+Ml gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e/go.mod h1:nfA7JaGv3hbGQ1ktdhAsZhdS84qKffI8NMlHr+Opsog= gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee h1:9U6HuKUBt/cGK6T/64dEuz0r7Yp97WAAEJvXHDlY3ws= gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0= -gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I= -gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= +gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw= +gitea.com/lunny/levelqueue v0.4.1/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/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U= @@ -241,7 +241,6 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xb github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= @@ -476,6 +475,8 @@ github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQ github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -762,8 +763,8 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA= -github.com/markbates/goth v1.67.1 h1:gU5B0pzHVyhnJPwGynfFnkfvaQ39C1Sy+ewdl+bhAOw= -github.com/markbates/goth v1.67.1/go.mod h1:EyLFHGU5ySr2GXRDyJH5nu2dA7parbC8QwIYW/rGcWg= +github.com/markbates/goth v1.68.0 h1:90sKvjRAKHcl9V2uC9x/PJXeD78cFPiBsyP1xVhoQfA= +github.com/markbates/goth v1.68.0/go.mod h1:V2VcDMzDiMHW+YmqYl7i0cMiAUeCkAe4QE6jRKBhXZw= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go index 4d82c092e728..59f51951234e 100644 --- a/integrations/auth_ldap_test.go +++ b/integrations/auth_ldap_test.go @@ -144,6 +144,60 @@ func TestLDAPUserSignin(t *testing.T) { assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text()) } +func TestLDAPAuthChange(t *testing.T) { + defer prepareTestEnv(t)() + addAuthSourceLDAP(t, "") + + session := loginUser(t, "user1") + req := NewRequest(t, "GET", "/admin/auths") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + href, exists := doc.Find("table.table td a").Attr("href") + if !exists { + assert.True(t, exists, "No authentication source found") + return + } + + req = NewRequest(t, "GET", href) + resp = session.MakeRequest(t, req, http.StatusOK) + doc = NewHTMLParser(t, resp.Body) + csrf := doc.GetCSRF() + host, _ := doc.Find(`input[name="host"]`).Attr("value") + assert.Equal(t, host, getLDAPServerHost()) + binddn, _ := doc.Find(`input[name="bind_dn"]`).Attr("value") + assert.Equal(t, binddn, "uid=gitea,ou=service,dc=planetexpress,dc=com") + + req = NewRequestWithValues(t, "POST", href, map[string]string{ + "_csrf": csrf, + "type": "2", + "name": "ldap", + "host": getLDAPServerHost(), + "port": "389", + "bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com", + "bind_password": "password", + "user_base": "ou=people,dc=planetexpress,dc=com", + "filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))", + "admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)", + "restricted_filter": "(uid=leela)", + "attribute_username": "uid", + "attribute_name": "givenName", + "attribute_surname": "sn", + "attribute_mail": "mail", + "attribute_ssh_public_key": "", + "is_sync_enabled": "on", + "is_active": "on", + }) + session.MakeRequest(t, req, http.StatusFound) + + req = NewRequest(t, "GET", href) + resp = session.MakeRequest(t, req, http.StatusOK) + doc = NewHTMLParser(t, resp.Body) + host, _ = doc.Find(`input[name="host"]`).Attr("value") + assert.Equal(t, host, getLDAPServerHost()) + binddn, _ = doc.Find(`input[name="bind_dn"]`).Attr("value") + assert.Equal(t, binddn, "uid=gitea,ou=service,dc=planetexpress,dc=com") +} + func TestLDAPUserSync(t *testing.T) { if skipLDAPTests() { t.Skip() diff --git a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 new file mode 100644 index 000000000000..a46c1925ece0 Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 differ diff --git a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c new file mode 100644 index 000000000000..062641bee7da Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c differ diff --git a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master index 1b1d96a1f00b..38984b12b786 100644 --- a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master +++ b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master @@ -1 +1 @@ -423313fbd38093bb10d0c8387db9105409c6f196 +0dca5bd9b5d7ef937710e056f575e86c0184ba85 diff --git a/models/commit_status.go b/models/commit_status.go index 1105c3b17315..4193ad0d08d4 100644 --- a/models/commit_status.go +++ b/models/commit_status.go @@ -160,7 +160,7 @@ func getLatestCommitStatus(e Engine, repoID int64, sha string, listOptions ListO if len(ids) == 0 { return statuses, nil } - return statuses, x.In("id", ids).Find(&statuses) + return statuses, e.In("id", ids).Find(&statuses) } // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts diff --git a/models/fixtures/oauth2_grant.yml b/models/fixtures/oauth2_grant.yml index 105e3f22db9a..e52a2bce959e 100644 --- a/models/fixtures/oauth2_grant.yml +++ b/models/fixtures/oauth2_grant.yml @@ -5,3 +5,19 @@ scope: "openid profile" created_unix: 1546869730 updated_unix: 1546869730 + +- id: 2 + user_id: 3 + application_id: 1 + counter: 1 + scope: "openid" + created_unix: 1546869730 + updated_unix: 1546869730 + +- id: 3 + user_id: 5 + application_id: 1 + counter: 1 + scope: "openid profile email" + created_unix: 1546869730 + updated_unix: 1546869730 \ No newline at end of file diff --git a/models/issue.go b/models/issue.go index 225dfee20f09..6e63620b7f3a 100644 --- a/models/issue.go +++ b/models/issue.go @@ -982,6 +982,31 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { return opts.Issue.addCrossReferences(e, doer, false) } +// RecalculateIssueIndexForRepo create issue_index for repo if not exist and +// update it based on highest index of existing issues assigned to a repo +func RecalculateIssueIndexForRepo(repoID int64) error { + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + if err := upsertResourceIndex(sess, "issue_index", repoID); err != nil { + return err + } + + var max int64 + if _, err := sess.Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { + return err + } + + if _, err := sess.Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil { + return err + } + + return sess.Commit() +} + // NewIssue creates new issue with labels for repository. func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { idx, err := GetNextResourceIndex("issue_index", repo.ID) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index b72dcaf60c67..8cdad94fd4ae 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -9,6 +9,8 @@ import ( "time" "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" ) // Stopwatch represents a stopwatch for time tracking. @@ -61,8 +63,12 @@ func StopwatchExists(userID, issueID int64) bool { // HasUserStopwatch returns true if the user has a stopwatch func HasUserStopwatch(userID int64) (exists bool, sw *Stopwatch, err error) { + return hasUserStopwatch(x, userID) +} + +func hasUserStopwatch(e Engine, userID int64) (exists bool, sw *Stopwatch, err error) { sw = new(Stopwatch) - exists, err = x. + exists, err = e. Where("user_id = ?", userID). Get(sw) return @@ -70,11 +76,23 @@ func HasUserStopwatch(userID int64) (exists bool, sw *Stopwatch, err error) { // CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline. func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { - sw, exists, err := getStopwatch(x, user.ID, issue.ID) + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + if err := createOrStopIssueStopwatch(sess, user, issue); err != nil { + return err + } + return sess.Commit() +} + +func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error { + sw, exists, err := getStopwatch(e, user.ID, issue.ID) if err != nil { return err } - if err := issue.loadRepo(x); err != nil { + if err := issue.loadRepo(e); err != nil { return err } @@ -90,11 +108,11 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { Time: timediff, } - if _, err := x.Insert(tt); err != nil { + if _, err := e.Insert(tt); err != nil { return err } - if _, err := CreateComment(&CreateCommentOptions{ + if _, err := createComment(e, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -104,21 +122,21 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { }); err != nil { return err } - if _, err := x.Delete(sw); err != nil { + if _, err := e.Delete(sw); err != nil { return err } } else { // if another stopwatch is running: stop it - exists, sw, err := HasUserStopwatch(user.ID) + exists, sw, err := hasUserStopwatch(e, user.ID) if err != nil { return err } if exists { - issue, err := getIssueByID(x, sw.IssueID) + issue, err := getIssueByID(e, sw.IssueID) if err != nil { return err } - if err := CreateOrStopIssueStopwatch(user, issue); err != nil { + if err := createOrStopIssueStopwatch(e, user, issue); err != nil { return err } } @@ -129,11 +147,11 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { IssueID: issue.ID, } - if _, err := x.Insert(sw); err != nil { + if _, err := e.Insert(sw); err != nil { return err } - if _, err := CreateComment(&CreateCommentOptions{ + if _, err := createComment(e, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -147,21 +165,33 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { // CancelStopwatch removes the given stopwatch and logs it into issue's timeline. func CancelStopwatch(user *User, issue *Issue) error { - sw, exists, err := getStopwatch(x, user.ID, issue.ID) + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + if err := cancelStopwatch(sess, user, issue); err != nil { + return err + } + return sess.Commit() +} + +func cancelStopwatch(e *xorm.Session, user *User, issue *Issue) error { + sw, exists, err := getStopwatch(e, user.ID, issue.ID) if err != nil { return err } if exists { - if _, err := x.Delete(sw); err != nil { + if _, err := e.Delete(sw); err != nil { return err } - if err := issue.loadRepo(x); err != nil { + if err := issue.loadRepo(e); err != nil { return err } - if _, err := CreateComment(&CreateCommentOptions{ + if _, err := createComment(e, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, diff --git a/models/login_source.go b/models/login_source.go index f9bd496b3add..5674196e0c66 100644 --- a/models/login_source.go +++ b/models/login_source.go @@ -7,6 +7,7 @@ package models import ( "crypto/tls" + "encoding/binary" "errors" "fmt" "net/smtp" @@ -70,13 +71,32 @@ var ( _ convert.Conversion = &SSPIConfig{} ) -// jsonUnmarshalIgnoreErroneousBOM - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's -// possible that a Blob may gain an unwanted prefix of 0xff 0xfe. -func jsonUnmarshalIgnoreErroneousBOM(bs []byte, v interface{}) error { +// jsonUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's +// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe. +func jsonUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error { json := jsoniter.ConfigCompatibleWithStandardLibrary - err := json.Unmarshal(bs, &v) + err := json.Unmarshal(bs, v) + if err != nil { + ok := true + rs := []byte{} + temp := make([]byte, 2) + for _, rn := range string(bs) { + if rn > 0xffff { + ok = false + break + } + binary.LittleEndian.PutUint16(temp, uint16(rn)) + rs = append(rs, temp...) + } + if ok { + if rs[0] == 0xff && rs[1] == 0xfe { + rs = rs[2:] + } + err = json.Unmarshal(rs, v) + } + } if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe { - err = json.Unmarshal(bs[2:], &v) + err = json.Unmarshal(bs[2:], v) } return err } @@ -88,7 +108,7 @@ type LDAPConfig struct { // FromDB fills up a LDAPConfig from serialized format. func (cfg *LDAPConfig) FromDB(bs []byte) error { - err := jsonUnmarshalIgnoreErroneousBOM(bs, &cfg) + err := jsonUnmarshalHandleDoubleEncode(bs, &cfg) if err != nil { return err } @@ -129,7 +149,7 @@ type SMTPConfig struct { // FromDB fills up an SMTPConfig from serialized format. func (cfg *SMTPConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, cfg) + return jsonUnmarshalHandleDoubleEncode(bs, cfg) } // ToDB exports an SMTPConfig to a serialized format. @@ -146,7 +166,7 @@ type PAMConfig struct { // FromDB fills up a PAMConfig from serialized format. func (cfg *PAMConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, cfg) + return jsonUnmarshalHandleDoubleEncode(bs, cfg) } // ToDB exports a PAMConfig to a serialized format. @@ -167,7 +187,7 @@ type OAuth2Config struct { // FromDB fills up an OAuth2Config from serialized format. func (cfg *OAuth2Config) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, cfg) + return jsonUnmarshalHandleDoubleEncode(bs, cfg) } // ToDB exports an SMTPConfig to a serialized format. @@ -187,7 +207,7 @@ type SSPIConfig struct { // FromDB fills up an SSPIConfig from serialized format. func (cfg *SSPIConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, cfg) + return jsonUnmarshalHandleDoubleEncode(bs, cfg) } // ToDB exports an SSPIConfig to a serialized format. diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 7a4193199c2f..63482ea79037 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -590,11 +590,26 @@ func recreateTable(sess *xorm.Session, bean interface{}) error { return err } + if err := sess.Table(tempTableName).DropIndexes(bean); err != nil { + log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err) + return err + } + // SQLite and MySQL will move all the constraints from the temporary table to the new table if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil { log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) return err } + + if err := sess.Table(tableName).CreateIndexes(bean); err != nil { + log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err) + return err + } + + if err := sess.Table(tableName).CreateUniques(bean); err != nil { + log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err) + return err + } case setting.Database.UsePostgreSQL: var originalSequences []string type sequenceData struct { @@ -836,7 +851,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin } cols += "`" + strings.ToLower(col) + "`" } - sql := fmt.Sprintf("SELECT Name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('%[1]s') AND PARENT_COLUMN_ID IN (SELECT column_id FROM sys.columns WHERE lower(NAME) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", + sql := fmt.Sprintf("SELECT Name FROM sys.default_constraints WHERE parent_object_id = OBJECT_ID('%[1]s') AND parent_column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", tableName, strings.ReplaceAll(cols, "`", "'")) constraints := make([]string, 0) if err := sess.SQL(sql).Find(&constraints); err != nil { @@ -847,17 +862,14 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin return fmt.Errorf("Drop table `%s` default constraint `%s`: %v", tableName, constraint, err) } } - sql = fmt.Sprintf("SELECT DISTINCT Name FROM SYS.INDEXES INNER JOIN SYS.INDEX_COLUMNS ON INDEXES.INDEX_ID = INDEX_COLUMNS.INDEX_ID AND INDEXES.OBJECT_ID = INDEX_COLUMNS.OBJECT_ID WHERE INDEXES.OBJECT_ID = OBJECT_ID('%[1]s') AND INDEX_COLUMNS.COLUMN_ID IN (SELECT column_id FROM sys.columns WHERE lower(NAME) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", + sql = fmt.Sprintf("SELECT DISTINCT Name FROM sys.indexes INNER JOIN sys.index_columns ON indexes.index_id = index_columns.index_id AND indexes.object_id = index_columns.object_id WHERE indexes.object_id = OBJECT_ID('%[1]s') AND index_columns.column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", tableName, strings.ReplaceAll(cols, "`", "'")) constraints = make([]string, 0) if err := sess.SQL(sql).Find(&constraints); err != nil { return fmt.Errorf("Find constraints: %v", err) } for _, constraint := range constraints { - if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT IF EXISTS `%s`", tableName, constraint)); err != nil { - return fmt.Errorf("Drop table `%s` index constraint `%s`: %v", tableName, constraint, err) - } - if _, err := sess.Exec(fmt.Sprintf("DROP INDEX IF EXISTS `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil { + if _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil { return fmt.Errorf("Drop index `%[2]s` on `%[1]s`: %v", tableName, constraint, err) } } diff --git a/models/oauth2.go b/models/oauth2.go index 46da60e02dd6..8693726a707c 100644 --- a/models/oauth2.go +++ b/models/oauth2.go @@ -155,11 +155,6 @@ func initOAuth2LoginSources() error { err := oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping) if err != nil { log.Critical("Unable to register source: %s due to Error: %v. This source will be disabled.", source.Name, err) - source.IsActived = false - if err = UpdateSource(source); err != nil { - log.Critical("Unable to update source %s to disable it. Error: %v", err) - return err - } } } return nil diff --git a/models/oauth2_application.go b/models/oauth2_application.go index 5a924763be16..f0e51b18d49f 100644 --- a/models/oauth2_application.go +++ b/models/oauth2_application.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt" uuid "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "xorm.io/xorm" diff --git a/models/repo.go b/models/repo.go index d6abc1b5e388..93827f6a8842 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1125,7 +1125,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO // Give access to all members in teams with access to all repositories. if u.IsOrganization() { - if err := u.GetTeams(&SearchTeamOptions{}); err != nil { + if err := u.getTeams(ctx.e); err != nil { return fmt.Errorf("GetTeams: %v", err) } for _, t := range u.Teams { @@ -1152,6 +1152,16 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO return fmt.Errorf("recalculateAccesses: %v", err) } + if u.Visibility == api.VisibleTypePublic && !repo.IsPrivate { + // Create/Remove git-daemon-export-ok for git-daemon... + daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`) + if f, err := os.Create(daemonExportFile); err != nil { + log.Error("Failed to create %s: %v", daemonExportFile, err) + } else { + f.Close() + } + } + if setting.Service.AutoWatchNewRepos { if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) @@ -1310,15 +1320,16 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e // Create/Remove git-daemon-export-ok for git-daemon... daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`) isExist, err := util.IsExist(daemonExportFile) + isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic if err != nil { log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err) return err } - if repo.IsPrivate && isExist { + if !isPublic && isExist { if err = util.Remove(daemonExportFile); err != nil { log.Error("Failed to remove %s: %v", daemonExportFile, err) } - } else if !repo.IsPrivate && !isExist { + } else if isPublic && !isExist { if f, err := os.Create(daemonExportFile); err != nil { log.Error("Failed to create %s: %v", daemonExportFile, err) } else { diff --git a/models/repo_list.go b/models/repo_list.go index b988cefface8..772bd20be387 100644 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -217,16 +217,14 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { cond = cond.And(accessibleRepositoryCondition(opts.Actor)) } } else { - // Not looking at private organisations + // Not looking at private organisations and users // We should be able to see all non-private repositories that // isn't in a private or limited organisation. cond = cond.And( builder.Eq{"is_private": false}, builder.NotIn("owner_id", builder.Select("id").From("`user`").Where( - builder.And( - builder.Eq{"type": UserTypeOrganization}, - builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}), - )))) + builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}), + ))) } if opts.IsPrivate != util.OptionalBoolNone { diff --git a/models/repo_unit.go b/models/repo_unit.go index a12e056a7d5a..f430e4f7f3bf 100644 --- a/models/repo_unit.go +++ b/models/repo_unit.go @@ -28,7 +28,7 @@ type UnitConfig struct{} // FromDB fills up a UnitConfig from serialized format. func (cfg *UnitConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg) + return jsonUnmarshalHandleDoubleEncode(bs, &cfg) } // ToDB exports a UnitConfig to a serialized format. @@ -44,7 +44,7 @@ type ExternalWikiConfig struct { // FromDB fills up a ExternalWikiConfig from serialized format. func (cfg *ExternalWikiConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg) + return jsonUnmarshalHandleDoubleEncode(bs, &cfg) } // ToDB exports a ExternalWikiConfig to a serialized format. @@ -62,7 +62,7 @@ type ExternalTrackerConfig struct { // FromDB fills up a ExternalTrackerConfig from serialized format. func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg) + return jsonUnmarshalHandleDoubleEncode(bs, &cfg) } // ToDB exports a ExternalTrackerConfig to a serialized format. @@ -80,7 +80,7 @@ type IssuesConfig struct { // FromDB fills up a IssuesConfig from serialized format. func (cfg *IssuesConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg) + return jsonUnmarshalHandleDoubleEncode(bs, &cfg) } // ToDB exports a IssuesConfig to a serialized format. @@ -104,7 +104,7 @@ type PullRequestsConfig struct { // FromDB fills up a PullRequestsConfig from serialized format. func (cfg *PullRequestsConfig) FromDB(bs []byte) error { - return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg) + return jsonUnmarshalHandleDoubleEncode(bs, &cfg) } // ToDB exports a PullRequestsConfig to a serialized format. diff --git a/modules/auth/oauth2/jwtsigningkey.go b/modules/auth/oauth2/jwtsigningkey.go index 75e62a7c4303..b8f1e40e8c3c 100644 --- a/modules/auth/oauth2/jwtsigningkey.go +++ b/modules/auth/oauth2/jwtsigningkey.go @@ -25,7 +25,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt" ini "gopkg.in/ini.v1" ) diff --git a/modules/auth/oauth2/oauth2.go b/modules/auth/oauth2/oauth2.go index 5d152e0a5588..df49b1c4a3fc 100644 --- a/modules/auth/oauth2/oauth2.go +++ b/modules/auth/oauth2/oauth2.go @@ -7,6 +7,7 @@ package oauth2 import ( "net/http" "net/url" + "sync" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -34,6 +35,7 @@ import ( var ( sessionUsersStoreKey = "gitea-oauth2-sessions" providerHeaderKey = "gitea-oauth2-provider" + gothRWMutex = sync.RWMutex{} ) // CustomURLMapping describes the urls values to use when customizing OAuth2 provider URLs @@ -60,6 +62,10 @@ func Init(x *xorm.Engine) error { // Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk store.MaxLength(setting.OAuth2.MaxTokenLength) + + gothRWMutex.Lock() + defer gothRWMutex.Unlock() + gothic.Store = store gothic.SetState = func(req *http.Request) string { @@ -82,6 +88,9 @@ func Auth(provider string, request *http.Request, response http.ResponseWriter) // normally the gothic library will write some custom stuff to the response instead of our own nice error page //gothic.BeginAuthHandler(response, request) + gothRWMutex.RLock() + defer gothRWMutex.RUnlock() + url, err := gothic.GetAuthURL(response, request) if err == nil { http.Redirect(response, request, url, http.StatusTemporaryRedirect) @@ -95,6 +104,9 @@ func ProviderCallback(provider string, request *http.Request, response http.Resp // not sure if goth is thread safe (?) when using multiple providers request.Header.Set(providerHeaderKey, provider) + gothRWMutex.RLock() + defer gothRWMutex.RUnlock() + user, err := gothic.CompleteUserAuth(response, request) if err != nil { return user, err @@ -108,6 +120,9 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID provider, err := createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL, customURLMapping) if err == nil && provider != nil { + gothRWMutex.Lock() + defer gothRWMutex.Unlock() + goth.UseProviders(provider) } @@ -116,11 +131,17 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID // RemoveProvider removes the given OAuth2 provider from the goth lib func RemoveProvider(providerName string) { + gothRWMutex.Lock() + defer gothRWMutex.Unlock() + delete(goth.GetProviders(), providerName) } // ClearProviders clears all OAuth2 providers from the goth lib func ClearProviders() { + gothRWMutex.Lock() + defer gothRWMutex.Unlock() + goth.ClearProviders() } diff --git a/modules/context/access_log.go b/modules/context/access_log.go index 97bb32f4c538..1a10c4763a81 100644 --- a/modules/context/access_log.go +++ b/modules/context/access_log.go @@ -6,6 +6,7 @@ package context import ( "bytes" + "context" "html/template" "net/http" "time" @@ -22,6 +23,8 @@ type routerLoggerOptions struct { Ctx map[string]interface{} } +var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey" + // AccessLogger returns a middleware to log access logger func AccessLogger() func(http.Handler) http.Handler { logger := log.GetLogger("access") @@ -29,11 +32,10 @@ func AccessLogger() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { start := time.Now() - next.ServeHTTP(w, req) identity := "-" - if val := SignedUserName(req); val != "" { - identity = val - } + r := req.WithContext(context.WithValue(req.Context(), signedUserNameStringPointerKey, &identity)) + + next.ServeHTTP(w, r) rw := w.(ResponseWriter) buf := bytes.NewBuffer([]byte{}) diff --git a/modules/context/api.go b/modules/context/api.go index 506824674522..7fff8e83466f 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -275,6 +275,17 @@ func APIContexter() func(http.Handler) http.Handler { ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) next.ServeHTTP(ctx.Resp, ctx.Req) + + // Handle adding signedUserName to the context for the AccessLogger + usernameInterface := ctx.Data["SignedUserName"] + identityPtrInterface := ctx.Req.Context().Value(signedUserNameStringPointerKey) + if usernameInterface != nil && identityPtrInterface != nil { + username := usernameInterface.(string) + identityPtr := identityPtrInterface.(*string) + if identityPtr != nil && username != "" { + *identityPtr = username + } + } }) } } diff --git a/modules/context/context.go b/modules/context/context.go index c6829d5c26a2..1050275d1216 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -774,6 +774,17 @@ func Contexter() func(next http.Handler) http.Handler { } next.ServeHTTP(ctx.Resp, ctx.Req) + + // Handle adding signedUserName to the context for the AccessLogger + usernameInterface := ctx.Data["SignedUserName"] + identityPtrInterface := ctx.Req.Context().Value(signedUserNameStringPointerKey) + if usernameInterface != nil && identityPtrInterface != nil { + username := usernameInterface.(string) + identityPtr := identityPtrInterface.(*string) + if identityPtr != nil && username != "" { + *identityPtr = username + } + } }) } } diff --git a/modules/doctor/misc.go b/modules/doctor/misc.go index 0ca1e841eefd..47fee8f7fd9f 100644 --- a/modules/doctor/misc.go +++ b/modules/doctor/misc.go @@ -6,7 +6,9 @@ package doctor import ( "fmt" + "os" "os/exec" + "path" "strings" "code.gitea.io/gitea/models" @@ -14,6 +16,9 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + lru "github.com/hashicorp/golang-lru" "xorm.io/builder" ) @@ -75,6 +80,7 @@ func checkUserStarNum(logger log.Logger, autofix bool) error { func checkEnablePushOptions(logger log.Logger, autofix bool) error { numRepos := 0 numNeedUpdate := 0 + if err := iterateRepositories(func(repo *models.Repository) error { numRepos++ r, err := git.OpenRepository(repo.RepoPath()) @@ -114,6 +120,66 @@ func checkEnablePushOptions(logger log.Logger, autofix bool) error { return nil } +func checkDaemonExport(logger log.Logger, autofix bool) error { + numRepos := 0 + numNeedUpdate := 0 + cache, err := lru.New(512) + if err != nil { + logger.Critical("Unable to create cache: %v", err) + return err + } + if err := iterateRepositories(func(repo *models.Repository) error { + numRepos++ + + if owner, has := cache.Get(repo.OwnerID); has { + repo.Owner = owner.(*models.User) + } else { + if err := repo.GetOwner(); err != nil { + return err + } + cache.Add(repo.OwnerID, repo.Owner) + } + + // Create/Remove git-daemon-export-ok for git-daemon... + daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`) + isExist, err := util.IsExist(daemonExportFile) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err) + return err + } + isPublic := !repo.IsPrivate && repo.Owner.Visibility == structs.VisibleTypePublic + + if isPublic != isExist { + numNeedUpdate++ + if autofix { + if !isPublic && isExist { + if err = util.Remove(daemonExportFile); err != nil { + log.Error("Failed to remove %s: %v", daemonExportFile, err) + } + } else if isPublic && !isExist { + if f, err := os.Create(daemonExportFile); err != nil { + log.Error("Failed to create %s: %v", daemonExportFile, err) + } else { + f.Close() + } + } + } + } + return nil + }); err != nil { + logger.Critical("Unable to checkDaemonExport: %v", err) + return err + } + + if autofix { + logger.Info("Updated git-daemon-export-ok files for %d of %d repositories.", numNeedUpdate, numRepos) + } else { + logger.Info("Checked %d repositories, %d need updates.", numRepos, numNeedUpdate) + } + + return nil +} + func init() { Register(&Check{ Title: "Check if SCRIPT_TYPE is available", @@ -143,4 +209,11 @@ func init() { Run: checkEnablePushOptions, Priority: 7, }) + Register(&Check{ + Title: "Check git-daemon-export-ok files", + Name: "check-git-daemon-export-ok", + IsDefault: false, + Run: checkDaemonExport, + Priority: 8, + }) } diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 4ed2a503b004..ab05a9cb83cd 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -12,7 +12,8 @@ import ( "time" "code.gitea.io/gitea/modules/util" - "github.com/dgrijalva/jwt-go" + + "github.com/golang-jwt/jwt" ) // NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN. diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index bdf82bde8913..164e64381289 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -7,6 +7,7 @@ package git import ( "bufio" "bytes" + "context" "io" "math" "strconv" @@ -28,16 +29,20 @@ type WriteCloserError interface { func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) { batchStdinReader, batchStdinWriter := io.Pipe() batchStdoutReader, batchStdoutWriter := io.Pipe() + ctx, ctxCancel := context.WithCancel(DefaultContext) + closed := make(chan struct{}) cancel := func() { _ = batchStdinReader.Close() _ = batchStdinWriter.Close() _ = batchStdoutReader.Close() _ = batchStdoutWriter.Close() + ctxCancel() + <-closed } go func() { stderr := strings.Builder{} - err := NewCommand("cat-file", "--batch-check").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader) + err := NewCommandContext(ctx, "cat-file", "--batch-check").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader) if err != nil { _ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String())) @@ -45,6 +50,7 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func() _ = batchStdoutWriter.Close() _ = batchStdinReader.Close() } + close(closed) }() // For simplicities sake we'll use a buffered reader to read from the cat-file --batch-check @@ -59,16 +65,20 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) { // so let's create a batch stdin and stdout batchStdinReader, batchStdinWriter := io.Pipe() batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024)) + ctx, ctxCancel := context.WithCancel(DefaultContext) + closed := make(chan struct{}) cancel := func() { _ = batchStdinReader.Close() _ = batchStdinWriter.Close() _ = batchStdoutReader.Close() _ = batchStdoutWriter.Close() + ctxCancel() + <-closed } go func() { stderr := strings.Builder{} - err := NewCommand("cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader) + err := NewCommandContext(ctx, "cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader) if err != nil { _ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String())) @@ -76,6 +86,7 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) { _ = batchStdoutWriter.Close() _ = batchStdinReader.Close() } + close(closed) }() // For simplicities sake we'll us a buffered reader to read from the cat-file --batch diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go index 534a5d517153..702754069bd3 100644 --- a/modules/git/notes_gogit.go +++ b/modules/git/notes_gogit.go @@ -36,6 +36,9 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) remainingCommitID = remainingCommitID[2:] } if err != nil { + if err == object.ErrDirectoryNotFound { + return ErrNotExist{ID: remainingCommitID, RelPath: path} + } return err } } diff --git a/modules/git/notes_test.go b/modules/git/notes_test.go index f66a191e6ae2..fec46e5960fb 100644 --- a/modules/git/notes_test.go +++ b/modules/git/notes_test.go @@ -39,3 +39,15 @@ func TestGetNestedNotes(t *testing.T) { assert.NoError(t, err) assert.Equal(t, []byte("Note 1"), note.Message) } + +func TestGetNonExistentNotes(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") + bareRepo1, err := OpenRepository(bareRepo1Path) + assert.NoError(t, err) + defer bareRepo1.Close() + + note := Note{} + err = GetNote(context.Background(), bareRepo1, "non_existent_sha", ¬e) + assert.Error(t, err) + assert.IsType(t, ErrNotExist{}, err) +} diff --git a/modules/git/tree.go b/modules/git/tree.go index 059f0a8287d2..3671f421e97f 100644 --- a/modules/git/tree.go +++ b/modules/git/tree.go @@ -6,6 +6,7 @@ package git import ( + "bytes" "strings" ) @@ -45,3 +46,23 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) { } return g, nil } + +// LsTree checks if the given filenames are in the tree +func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) { + cmd := NewCommand("ls-tree", "-z", "--name-only", "--", ref) + for _, arg := range filenames { + if arg != "" { + cmd.AddArguments(arg) + } + } + res, err := cmd.RunInDirBytes(repo.Path) + if err != nil { + return nil, err + } + filelist := make([]string, 0, len(filenames)) + for _, line := range bytes.Split(res, []byte{'\000'}) { + filelist = append(filelist, string(line)) + } + + return filelist, err +} diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 568035fbb7fb..ed0548578a75 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -166,6 +166,11 @@ func File(numLines int, fileName string, code []byte) map[int]string { } htmlw.Flush() + finalNewLine := false + if len(code) > 0 { + finalNewLine = code[len(code)-1] == '\n' + } + m := make(map[int]string, numLines) for k, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) { line := k + 1 @@ -173,9 +178,17 @@ func File(numLines int, fileName string, code []byte) map[int]string { //need to keep lines that are only \n so copy/paste works properly in browser if content == "" { content = "\n" + } else if content == `` { + content += "\n" } + content = strings.TrimSuffix(content, ``) + content = strings.TrimPrefix(content, ``) m[line] = content } + if finalNewLine { + m[numLines+1] = "\n" + } + return m } diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go new file mode 100644 index 000000000000..7c5afaa52cc4 --- /dev/null +++ b/modules/highlight/highlight_test.go @@ -0,0 +1,103 @@ +// Copyright 2021 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 highlight + +import ( + "reflect" + "testing" + + "code.gitea.io/gitea/modules/setting" + "gopkg.in/ini.v1" +) + +func TestFile(t *testing.T) { + setting.Cfg = ini.Empty() + tests := []struct { + name string + numLines int + fileName string + code string + want map[int]string + }{ + { + name: ".drone.yml", + numLines: 12, + fileName: ".drone.yml", + code: `kind: pipeline +name: default + +steps: +- name: test + image: golang:1.13 + environment: + GOPROXY: https://goproxy.cn + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic +`, + want: map[int]string{ + 1: `kind: pipeline`, + 2: `name: default`, + 3: ` +`, + 4: `steps:`, + 5: `- name: test`, + 6: ` image: golang:1.13`, + 7: ` environment:`, + 8: ` GOPROXY: https://goproxy.cn`, + 9: ` commands:`, + 10: ` - go get -u`, + 11: ` - go build -v`, + 12: ` - go test -v -race -coverprofile=coverage.txt -covermode=atomic +`, + 13: ` +`, + }, + }, + { + name: ".drone.yml - trailing space", + numLines: 13, + fileName: ".drone.yml", + code: `kind: pipeline +name: default ` + ` + +steps: +- name: test + image: golang:1.13 + environment: + GOPROXY: https://goproxy.cn + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + `, + want: map[int]string{ + 1: `kind: pipeline`, + 2: `name: default `, + 3: ` +`, + 4: `steps:`, + 5: `- name: test`, + 6: ` image: golang:1.13`, + 7: ` environment:`, + 8: ` GOPROXY: https://goproxy.cn`, + 9: ` commands:`, + 10: ` - go get -u`, + 11: ` - go build -v`, + 12: ` - go test -v -race -coverprofile=coverage.txt -covermode=atomic`, + 13: ` `, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := File(tt.numLines, tt.fileName, []byte(tt.code)); !reflect.DeepEqual(got, tt.want) { + t.Errorf("File() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/modules/indexer/bleve/batch.go b/modules/indexer/bleve/batch.go new file mode 100644 index 000000000000..79994e6e5be3 --- /dev/null +++ b/modules/indexer/bleve/batch.go @@ -0,0 +1,59 @@ +// Copyright 2021 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 bleve + +import ( + "github.com/blevesearch/bleve/v2" +) + +// FlushingBatch is a batch of operations that automatically flushes to the +// underlying index once it reaches a certain size. +type FlushingBatch struct { + maxBatchSize int + batch *bleve.Batch + index bleve.Index +} + +// NewFlushingBatch creates a new flushing batch for the specified index. Once +// the number of operations in the batch reaches the specified limit, the batch +// automatically flushes its operations to the index. +func NewFlushingBatch(index bleve.Index, maxBatchSize int) *FlushingBatch { + return &FlushingBatch{ + maxBatchSize: maxBatchSize, + batch: index.NewBatch(), + index: index, + } +} + +// Index add a new index to batch +func (b *FlushingBatch) Index(id string, data interface{}) error { + if err := b.batch.Index(id, data); err != nil { + return err + } + return b.flushIfFull() +} + +// Delete add a delete index to batch +func (b *FlushingBatch) Delete(id string) error { + b.batch.Delete(id) + return b.flushIfFull() +} + +func (b *FlushingBatch) flushIfFull() error { + if b.batch.Size() < b.maxBatchSize { + return nil + } + return b.Flush() +} + +// Flush submit the batch and create a new one +func (b *FlushingBatch) Flush() error { + err := b.index.Batch(b.batch) + if err != nil { + return err + } + b.batch = b.index.NewBatch() + return nil +} diff --git a/modules/indexer/code/bleve.go b/modules/indexer/code/bleve.go index 600789a28409..fc5c602dbef0 100644 --- a/modules/indexer/code/bleve.go +++ b/modules/indexer/code/bleve.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -176,7 +177,8 @@ func NewBleveIndexer(indexDir string) (*BleveIndexer, bool, error) { return indexer, created, err } -func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *bufio.Reader, commitSha string, update fileUpdate, repo *models.Repository, batch rupture.FlushingBatch) error { +func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *bufio.Reader, commitSha string, + update fileUpdate, repo *models.Repository, batch *gitea_bleve.FlushingBatch) error { // Ignore vendored files in code search if setting.Indexer.ExcludeVendored && analyze.IsVendor(update.Filename) { return nil @@ -229,7 +231,7 @@ func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader * }) } -func (b *BleveIndexer) addDelete(filename string, repo *models.Repository, batch rupture.FlushingBatch) error { +func (b *BleveIndexer) addDelete(filename string, repo *models.Repository, batch *gitea_bleve.FlushingBatch) error { id := filenameIndexerID(repo.ID, filename) return batch.Delete(id) } @@ -267,7 +269,7 @@ func (b *BleveIndexer) Close() { // Index indexes the data func (b *BleveIndexer) Index(repo *models.Repository, sha string, changes *repoChanges) error { - batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize) + batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize) if len(changes.Updates) > 0 { batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath()) @@ -296,7 +298,7 @@ func (b *BleveIndexer) Delete(repoID int64) error { if err != nil { return err } - batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize) + batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize) for _, hit := range result.Hits { if err = batch.Delete(hit.ID); err != nil { return err diff --git a/modules/indexer/issues/bleve.go b/modules/indexer/issues/bleve.go index b1385eb67625..db12874e84e5 100644 --- a/modules/indexer/issues/bleve.go +++ b/modules/indexer/issues/bleve.go @@ -9,8 +9,10 @@ import ( "os" "strconv" + gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" + "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis/analyzer/custom" "github.com/blevesearch/bleve/v2/analysis/token/lowercase" @@ -197,7 +199,7 @@ func (b *BleveIndexer) Close() { // Index will save the index data func (b *BleveIndexer) Index(issues []*IndexerData) error { - batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize) + batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize) for _, issue := range issues { if err := batch.Index(indexerID(issue.ID), struct { RepoID int64 @@ -218,7 +220,7 @@ func (b *BleveIndexer) Index(issues []*IndexerData) error { // Delete deletes indexes by ids func (b *BleveIndexer) Delete(ids ...int64) error { - batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize) + batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize) for _, id := range ids { if err := batch.Delete(indexerID(id)); err != nil { return err diff --git a/modules/log/event.go b/modules/log/event.go index 6975bf749d83..00a66c306abe 100644 --- a/modules/log/event.go +++ b/modules/log/event.go @@ -143,7 +143,7 @@ type MultiChannelledLog struct { name string bufferLength int64 queue chan *Event - mutex sync.Mutex + rwmutex sync.RWMutex loggers map[string]EventLogger flush chan bool close chan bool @@ -173,10 +173,10 @@ func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog // AddLogger adds a logger to this MultiChannelledLog func (m *MultiChannelledLog) AddLogger(logger EventLogger) error { - m.mutex.Lock() + m.rwmutex.Lock() name := logger.GetName() if _, has := m.loggers[name]; has { - m.mutex.Unlock() + m.rwmutex.Unlock() return ErrDuplicateName{name} } m.loggers[name] = logger @@ -186,7 +186,7 @@ func (m *MultiChannelledLog) AddLogger(logger EventLogger) error { if logger.GetStacktraceLevel() < m.stacktraceLevel { m.stacktraceLevel = logger.GetStacktraceLevel() } - m.mutex.Unlock() + m.rwmutex.Unlock() go m.Start() return nil } @@ -195,15 +195,15 @@ func (m *MultiChannelledLog) AddLogger(logger EventLogger) error { // NB: If you delete the last sublogger this logger will simply drop // log events func (m *MultiChannelledLog) DelLogger(name string) bool { - m.mutex.Lock() + m.rwmutex.Lock() logger, has := m.loggers[name] if !has { - m.mutex.Unlock() + m.rwmutex.Unlock() return false } delete(m.loggers, name) m.internalResetLevel() - m.mutex.Unlock() + m.rwmutex.Unlock() logger.Flush() logger.Close() return true @@ -211,15 +211,15 @@ func (m *MultiChannelledLog) DelLogger(name string) bool { // GetEventLogger returns a sub logger from this MultiChannelledLog func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger { - m.mutex.Lock() - defer m.mutex.Unlock() + m.rwmutex.RLock() + defer m.rwmutex.RUnlock() return m.loggers[name] } // GetEventLoggerNames returns a list of names func (m *MultiChannelledLog) GetEventLoggerNames() []string { - m.mutex.Lock() - defer m.mutex.Unlock() + m.rwmutex.RLock() + defer m.rwmutex.RUnlock() var keys []string for k := range m.loggers { keys = append(keys, k) @@ -228,12 +228,12 @@ func (m *MultiChannelledLog) GetEventLoggerNames() []string { } func (m *MultiChannelledLog) closeLoggers() { - m.mutex.Lock() + m.rwmutex.Lock() for _, logger := range m.loggers { logger.Flush() logger.Close() } - m.mutex.Unlock() + m.rwmutex.Unlock() m.closed <- true } @@ -249,8 +249,8 @@ func (m *MultiChannelledLog) Resume() { // ReleaseReopen causes this logger to tell its subloggers to release and reopen func (m *MultiChannelledLog) ReleaseReopen() error { - m.mutex.Lock() - defer m.mutex.Unlock() + m.rwmutex.Lock() + defer m.rwmutex.Unlock() var accumulatedErr error for _, logger := range m.loggers { if err := logger.ReleaseReopen(); err != nil { @@ -266,13 +266,13 @@ func (m *MultiChannelledLog) ReleaseReopen() error { // Start processing the MultiChannelledLog func (m *MultiChannelledLog) Start() { - m.mutex.Lock() + m.rwmutex.Lock() if m.started { - m.mutex.Unlock() + m.rwmutex.Unlock() return } m.started = true - m.mutex.Unlock() + m.rwmutex.Unlock() paused := false for { if paused { @@ -286,11 +286,11 @@ func (m *MultiChannelledLog) Start() { m.closeLoggers() return } - m.mutex.Lock() + m.rwmutex.RLock() for _, logger := range m.loggers { logger.Flush() } - m.mutex.Unlock() + m.rwmutex.RUnlock() case <-m.close: m.closeLoggers() return @@ -307,24 +307,24 @@ func (m *MultiChannelledLog) Start() { m.closeLoggers() return } - m.mutex.Lock() + m.rwmutex.RLock() for _, logger := range m.loggers { err := logger.LogEvent(event) if err != nil { fmt.Println(err) } } - m.mutex.Unlock() + m.rwmutex.RUnlock() case _, ok := <-m.flush: if !ok { m.closeLoggers() return } - m.mutex.Lock() + m.rwmutex.RLock() for _, logger := range m.loggers { logger.Flush() } - m.mutex.Unlock() + m.rwmutex.RUnlock() case <-m.close: m.closeLoggers() return @@ -359,11 +359,15 @@ func (m *MultiChannelledLog) Flush() { // GetLevel gets the level of this MultiChannelledLog func (m *MultiChannelledLog) GetLevel() Level { + m.rwmutex.RLock() + defer m.rwmutex.RUnlock() return m.level } // GetStacktraceLevel gets the level of this MultiChannelledLog func (m *MultiChannelledLog) GetStacktraceLevel() Level { + m.rwmutex.RLock() + defer m.rwmutex.RUnlock() return m.stacktraceLevel } @@ -384,8 +388,8 @@ func (m *MultiChannelledLog) internalResetLevel() Level { // ResetLevel will reset the level of this MultiChannelledLog func (m *MultiChannelledLog) ResetLevel() Level { - m.mutex.Lock() - defer m.mutex.Unlock() + m.rwmutex.Lock() + defer m.rwmutex.Unlock() return m.internalResetLevel() } diff --git a/modules/markup/html.go b/modules/markup/html.go index 6d0b4fbea2f3..5ecc307a2992 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -778,7 +778,7 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { // extract repo and org name from matched link like // http://localhost:3000/gituser/myrepo/issues/1 - linkParts := strings.Split(path.Clean(link), "/") + linkParts := strings.Split(link, "/") matchOrg := linkParts[len(linkParts)-4] matchRepo := linkParts[len(linkParts)-3] diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index dff9102beddb..3eb2df00a942 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -5,6 +5,7 @@ package markup_test import ( + "io" "strings" "testing" @@ -526,3 +527,18 @@ func BenchmarkEmojiPostprocess(b *testing.B) { assert.NoError(b, err) } } + +func TestFuzz(t *testing.T) { + s := "t/l/issues/8#/../../a" + renderContext := RenderContext{ + URLPrefix: "https://example.com/go-gitea/gitea", + Metas: map[string]string{ + "user": "go-gitea", + "repo": "gitea", + }, + } + + err := PostProcess(&renderContext, strings.NewReader(s), io.Discard) + + assert.NoError(t, err) +} diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index cac2a180faee..ab026dd1b850 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -87,7 +87,9 @@ func newParserContext(ctx *markup.RenderContext) parser.Context { func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { once.Do(func() { converter = goldmark.New( - goldmark.WithExtensions(extension.Table, + goldmark.WithExtensions( + extension.NewTable( + extension.WithTableCellAlignMethod(extension.TableCellAlignAttribute)), extension.Strikethrough, extension.TaskList, extension.DefinitionList, diff --git a/modules/migrations/gitea_uploader.go b/modules/migrations/gitea_uploader.go index 2b18098b7f16..5f34353dc471 100644 --- a/modules/migrations/gitea_uploader.go +++ b/modules/migrations/gitea_uploader.go @@ -555,6 +555,9 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR // download patch file err := func() error { + if pr.PatchURL == "" { + return nil + } // pr.PatchURL maybe a local file ret, err := uri.Open(pr.PatchURL) if err != nil { @@ -871,6 +874,11 @@ func (g *GiteaLocalUploader) Finish() error { return ErrRepoNotCreated } + // update issue_index + if err := models.RecalculateIssueIndexForRepo(g.repo.ID); err != nil { + return err + } + g.repo.Status = models.RepositoryReady return models.UpdateRepositoryCols(g.repo, "status") } diff --git a/modules/queue/queue_disk_channel_test.go b/modules/queue/queue_disk_channel_test.go index 561f98ca907b..c20fe1e0a9b2 100644 --- a/modules/queue/queue_disk_channel_test.go +++ b/modules/queue/queue_disk_channel_test.go @@ -15,7 +15,6 @@ import ( func TestPersistableChannelQueue(t *testing.T) { handleChan := make(chan *testData) handle := func(data ...Data) { - assert.True(t, len(data) == 2) for _, datum := range data { testDatum := datum.(*testData) handleChan <- testDatum diff --git a/modules/util/remove.go b/modules/util/remove.go index 23104365256f..d05ee9fe4afa 100644 --- a/modules/util/remove.go +++ b/modules/util/remove.go @@ -6,10 +6,13 @@ package util import ( "os" + "runtime" "syscall" "time" ) +const windowsSharingViolationError syscall.Errno = 32 + // Remove removes the named file or (empty) directory with at most 5 attempts. func Remove(name string) error { var err error @@ -25,6 +28,12 @@ func Remove(name string) error { continue } + if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { + // try again + <-time.After(100 * time.Millisecond) + continue + } + if unwrapped == syscall.ENOENT { // it's already gone return nil @@ -48,6 +57,12 @@ func RemoveAll(name string) error { continue } + if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { + // try again + <-time.After(100 * time.Millisecond) + continue + } + if unwrapped == syscall.ENOENT { // it's already gone return nil @@ -64,13 +79,19 @@ func Rename(oldpath, newpath string) error { if err == nil { break } - unwrapped := err.(*os.PathError).Err + unwrapped := err.(*os.LinkError).Err if unwrapped == syscall.EBUSY || unwrapped == syscall.ENOTEMPTY || unwrapped == syscall.EPERM || unwrapped == syscall.EMFILE || unwrapped == syscall.ENFILE { // try again <-time.After(100 * time.Millisecond) continue } + if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { + // try again + <-time.After(100 * time.Millisecond) + continue + } + if i == 0 && os.IsNotExist(err) { return err } diff --git a/modules/util/truncate.go b/modules/util/truncate.go new file mode 100644 index 000000000000..8d0f6309732d --- /dev/null +++ b/modules/util/truncate.go @@ -0,0 +1,35 @@ +// Copyright 2021 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 util + +import "unicode/utf8" + +// SplitStringAtByteN splits a string at byte n accounting for rune boundaries. (Combining characters are not accounted for.) +func SplitStringAtByteN(input string, n int) (left, right string) { + if len(input) <= n { + left = input + return + } + + if !utf8.ValidString(input) { + left = input[:n-3] + "..." + right = "..." + input[n-3:] + return + } + + // in UTF8 "…" is 3 bytes so doesn't really gain us anything... + end := 0 + for end <= n-3 { + _, size := utf8.DecodeRuneInString(input[end:]) + if end+size > n-3 { + break + } + end += size + } + + left = input[:end] + "…" + right = "…" + input[end:] + return +} diff --git a/modules/web/route.go b/modules/web/route.go index 319d08f5981c..3c6513da626c 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -269,6 +269,26 @@ func (r *Route) Get(pattern string, h ...interface{}) { r.R.Get(r.getPattern(pattern), Wrap(middlewares...)) } +// Options delegate options method +func (r *Route) Options(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Options(r.getPattern(pattern), Wrap(middlewares...)) +} + +// GetOptions delegate get and options method +func (r *Route) GetOptions(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Get(r.getPattern(pattern), Wrap(middlewares...)) + r.R.Options(r.getPattern(pattern), Wrap(middlewares...)) +} + +// PostOptions delegate post and options method +func (r *Route) PostOptions(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Post(r.getPattern(pattern), Wrap(middlewares...)) + r.R.Options(r.getPattern(pattern), Wrap(middlewares...)) +} + // Head delegate head method func (r *Route) Head(pattern string, h ...interface{}) { var middlewares = r.getMiddlewares(h) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 0538e0f7b16b..2c9ae6db1fa7 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1248,8 +1248,8 @@ issues.dependency.remove=Odstranit issues.dependency.remove_info=Odstranit tuto závislost issues.dependency.added_dependency=`přidal(a) novou závislost %s` issues.dependency.removed_dependency=`odstranil(a) závislost %s` -issues.dependency.issue_closing_blockedby=Uzavření tohoto požadavku na natažení je blokováno následujícími úkoly -issues.dependency.pr_closing_blockedby=Uzavření tohoto úkolu je blokováno následujícími úkoly +issues.dependency.pr_closing_blockedby=Uzavření tohoto požadavku na natažení je blokováno následujícími úkoly +issues.dependency.issue_closing_blockedby=Uzavření tohoto úkolu je blokováno následujícími úkoly issues.dependency.issue_close_blocks=Tento úkol blokuje uzavření následujících úkolů issues.dependency.pr_close_blocks=Tento požadavek na natažení blokuje uzavření následujících úkolů issues.dependency.issue_close_blocked=Musíte zavřít všechny úkoly, které blokují tento úkol, aby jej bylo možné zavřít. diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index bd6067b5cf5f..25e6c2e3954c 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1326,8 +1326,8 @@ issues.dependency.remove=Entfernen issues.dependency.remove_info=Abhängigkeit löschen issues.dependency.added_dependency=`hat eine neue Abhängigkeit %s hinzugefügt` issues.dependency.removed_dependency=`hat eine Abhängigkeit %s entfernt` -issues.dependency.issue_closing_blockedby=Das Schließen dieses Pull-Requests wird von den folgenden Issues blockiert -issues.dependency.pr_closing_blockedby=Das Schließen dieses Issues wird von den folgenden Issues blockiert +issues.dependency.pr_closing_blockedby=Das Schließen dieses Pull-Requests wird von den folgenden Issues blockiert +issues.dependency.issue_closing_blockedby=Das Schließen dieses Issues wird von den folgenden Issues blockiert issues.dependency.issue_close_blocks=Dieses Issue blockiert die Schließung der folgenden Issues issues.dependency.pr_close_blocks=Dieser Pull-Request blockiert die Schließung der folgenden Issues issues.dependency.issue_close_blocked=Du musst alle Issues, die dieses Issue blockieren, schließen, bevor du es schließen kannst. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0ead1dfd6d08..5545c1a1a0ef 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1326,8 +1326,8 @@ issues.dependency.remove = Remove issues.dependency.remove_info = Remove this dependency issues.dependency.added_dependency = `added a new dependency %s` issues.dependency.removed_dependency = `removed a dependency %s` -issues.dependency.issue_closing_blockedby = Closing this pull request is blocked by the following issues -issues.dependency.pr_closing_blockedby = Closing this issue is blocked by the following issues +issues.dependency.pr_closing_blockedby = Closing this pull request is blocked by the following issues +issues.dependency.issue_closing_blockedby = Closing this issue is blocked by the following issues issues.dependency.issue_close_blocks = This issue blocks closing of the following issues issues.dependency.pr_close_blocks = This pull request blocks closing of the following issues issues.dependency.issue_close_blocked = You need to close all issues blocking this issue before you can close it. diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 2a10e289ed0b..f54368e2f611 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1326,8 +1326,8 @@ issues.dependency.remove=Eliminar issues.dependency.remove_info=Eliminar esta dependencia issues.dependency.added_dependency=`añadida una nueva dependencia %s` issues.dependency.removed_dependency=`eliminada una dependencia %s` -issues.dependency.issue_closing_blockedby=Cerrar este pull request está bloqueado por las siguientes issues -issues.dependency.pr_closing_blockedby=Cierre de esta incidencia es bloqueado por las siguientes incidencias +issues.dependency.pr_closing_blockedby=Cerrar este pull request está bloqueado por las siguientes issues +issues.dependency.issue_closing_blockedby=Cierre de esta incidencia es bloqueado por las siguientes incidencias issues.dependency.issue_close_blocks=Esta incidencia bloquea el cierre de las siguientes incidencias issues.dependency.pr_close_blocks=Este pull request bloquea el cierre de las siguientes incidencias issues.dependency.issue_close_blocked=Necesita cerrar todos las incidencias que bloquean esta incidencia antes de que se puede cerrar. diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index cf36d01996b9..a26aeb372556 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -1070,8 +1070,8 @@ issues.dependency.remove=حذف/ساقط کردن issues.dependency.remove_info=حذف این وابستگی issues.dependency.added_dependency=`%s یک مخزن جدید اضافه کرد` issues.dependency.removed_dependency=`%s یک وابستگی را حذف کرد` -issues.dependency.issue_closing_blockedby=بستن این تقاضای واکشی وسط موضوعات زیر رد/ مسدود شده است -issues.dependency.pr_closing_blockedby=بستن این موضوع وسط موضوعات زیر رد/ مسدود شده است +issues.dependency.pr_closing_blockedby=بستن این تقاضای واکشی وسط موضوعات زیر رد/ مسدود شده است +issues.dependency.issue_closing_blockedby=بستن این موضوع وسط موضوعات زیر رد/ مسدود شده است issues.dependency.issue_close_blocks=این مسئله با توجه به موضوعات مطرح شده مسدود شده است issues.dependency.pr_close_blocks=این تقاضای واکشی با توجه به موضوعات مطرح شده مسدود شده است issues.dependency.issue_close_blocked=شما نیاز به بستن تمامی مسائل مسدود شده مسئله قبل بستن آن هستید. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index b5a64da26831..d70c49a12c4c 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1277,8 +1277,8 @@ issues.dependency.remove=Supprimer issues.dependency.remove_info=Supprimer cette dépendance issues.dependency.added_dependency=`a ajouté une nouvelle dépendance %s` issues.dependency.removed_dependency=`a supprimé une dépendance %s` -issues.dependency.issue_closing_blockedby=La clôture de cette demande d'ajout est bloquée par les tickets suivants -issues.dependency.pr_closing_blockedby=La clôture de ce ticket est bloquée par les tickets suivants +issues.dependency.pr_closing_blockedby=La clôture de cette demande d'ajout est bloquée par les tickets suivants +issues.dependency.issue_closing_blockedby=La clôture de ce ticket est bloquée par les tickets suivants issues.dependency.issue_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants issues.dependency.pr_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants issues.dependency.issue_close_blocked=Vous devez fermer tous les tickets qui bloquent ce ticket avant de pouvoir le fermer. diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 2f0bff1cae8b..a4d2166e0c81 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1179,8 +1179,8 @@ issues.dependency.remove=Rimuovi issues.dependency.remove_info=Rimuovi questa dipendenza issues.dependency.added_dependency=`ha aggiunto una nuova dipendenza %s` issues.dependency.removed_dependency=`ha rimosso una dipendenza %s` -issues.dependency.issue_closing_blockedby=La chiusura di questa richiesta pull è bloccata per i seguenti problemi -issues.dependency.pr_closing_blockedby=La chiusura di questo problema è bloccata per i seguenti problemi +issues.dependency.pr_closing_blockedby=La chiusura di questa richiesta pull è bloccata per i seguenti problemi +issues.dependency.issue_closing_blockedby=La chiusura di questo problema è bloccata per i seguenti problemi issues.dependency.issue_close_blocks=Questo problema impedisce la chiusura dei seguenti problemi issues.dependency.pr_close_blocks=Questa richiesta di pull impedisce la chiusura dei seguenti problemi issues.dependency.issue_close_blocked=Devi chiudere tutte le anomalie che bloiccano questo problema prima di chiudelo. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 86c92dc93557..bf01961973e6 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1312,8 +1312,8 @@ issues.dependency.remove=削除 issues.dependency.remove_info=この依存関係を削除 issues.dependency.added_dependency=`が新しい依存関係を追加 %s` issues.dependency.removed_dependency=`が依存関係を削除 %s` -issues.dependency.issue_closing_blockedby=このプルリクエストのクローズは、これらの課題によりブロックされています -issues.dependency.pr_closing_blockedby=この課題のクローズは、これらの課題によりブロックされています +issues.dependency.pr_closing_blockedby=このプルリクエストのクローズは、これらの課題によりブロックされています +issues.dependency.issue_closing_blockedby=この課題のクローズは、これらの課題によりブロックされています issues.dependency.issue_close_blocks=この課題は、これらの課題のクローズをブロックしています issues.dependency.pr_close_blocks=このプルリクエストは、これらの課題のクローズをブロックしています issues.dependency.issue_close_blocked=この課題をクローズするには、ブロックしている課題をすべてクローズする必要があります。 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index f1e802afbcab..03a786df10e7 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1326,8 +1326,8 @@ issues.dependency.remove=Noņemt issues.dependency.remove_info=Noņemt šo atkarību issues.dependency.added_dependency=`pievienoja jaunu atkarību %s` issues.dependency.removed_dependency=`noņema atkarību %s` -issues.dependency.issue_closing_blockedby=Šī izmaiņu pieprasījuma sapludināšanu bloķē sekojošas problēmas -issues.dependency.pr_closing_blockedby=Šīs problēmas aizvēršanu bloķē sekojošas problēmas +issues.dependency.pr_closing_blockedby=Šī izmaiņu pieprasījuma sapludināšanu bloķē sekojošas problēmas +issues.dependency.issue_closing_blockedby=Šīs problēmas aizvēršanu bloķē sekojošas problēmas issues.dependency.issue_close_blocks=Šī problēma bloķē sekojošu problēmu aizvēršanu issues.dependency.pr_close_blocks=Šis izmaiņu pieprasījums bloķē sekojošu problēmu aizvēršanu issues.dependency.issue_close_blocked=Nepieciešams aizvērt visas problēmas, kas bloķē šo problēmu, lai to varētu aizērt. diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini index 4fb2bdbe8a9c..83f519de686d 100644 --- a/options/locale/locale_ml-IN.ini +++ b/options/locale/locale_ml-IN.ini @@ -738,8 +738,8 @@ issues.deleted_milestone=`(ഇല്ലാതാക്കി)` issues.filter_type.all_issues=എല്ലാ ഇഷ്യൂകളും issues.label_open_issues=%d തുറന്നനിലയിലുള്ള ഇഷ്യൂകള്‍ issues.label_deletion_desc=ഒരു ലേബൽ ഇല്ലാതാക്കിയാല്‍, അതു് നിയുകതമാക്കിയ എല്ലാ ഇഷ്യൂകളില്‍ നിന്നും നീക്കംചെയ്യും. തുടരട്ടെ? -issues.dependency.issue_closing_blockedby=ഈ ലയന അഭ്യര്‍ത്ഥന അടയ്‌ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള്‍ തടയുന്നു് -issues.dependency.pr_closing_blockedby=ഈ ഇഷ്യു അടയ്‌ക്കുന്നത് ഇനിപ്പറയുന്ന ലയന അഭ്യര്‍ത്ഥന തടയുന്നു് +issues.dependency.pr_closing_blockedby=ഈ ലയന അഭ്യര്‍ത്ഥന അടയ്‌ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള്‍ തടയുന്നു് +issues.dependency.issue_closing_blockedby=ഈ ഇഷ്യു അടയ്‌ക്കുന്നത് ഇനിപ്പറയുന്ന ലയന അഭ്യര്‍ത്ഥന തടയുന്നു് issues.dependency.issue_close_blocks=ഈ ഇഷ്യു അടയ്‌ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള്‍ തടയുന്നു് issues.dependency.pr_close_blocks=ഈ ഇഷ്യൂകള്‍ അടയ്‌ക്കുന്നത് ഈ ലയന അഭ്യര്‍ത്ഥന തടയുന്നു് issues.dependency.issue_close_blocked=ഈ ഇഷ്യൂ അടയ്‌ക്കുന്നതിന് മുമ്പ് ഇതിനെ തടയുന്ന എല്ലാ ഇഷ്യൂകളും നിങ്ങൾ അടയ്‌ക്കേണ്ടതുണ്ട്. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 551a05ed0df1..5e12e7673e20 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1168,8 +1168,8 @@ issues.dependency.remove=Verwijder issues.dependency.remove_info=Verwijder afhankelijkheid issues.dependency.added_dependency=`voegde een nieuwe afhankelijkheid %s toe ` issues.dependency.removed_dependency=`verwijderde een afhankelijkheid %s` -issues.dependency.issue_closing_blockedby=Het sluiten van deze pull-aanvraag is geblokkeerd door de volgende kwesties -issues.dependency.pr_closing_blockedby=Het sluiten van deze kwestie is geblokkeerd door de volgende kwesties +issues.dependency.pr_closing_blockedby=Het sluiten van deze pull-aanvraag is geblokkeerd door de volgende kwesties +issues.dependency.issue_closing_blockedby=Het sluiten van deze kwestie is geblokkeerd door de volgende kwesties issues.dependency.issue_close_blocks=Deze kwestie blokkeert het sluiten van de volgende kwesties issues.dependency.pr_close_blocks=Deze pull-aanvraag blokkeert het sluiten van de volgende kwesties issues.dependency.issue_close_blocked=Je moet alle kwesties die deze kwestie blokkeren sluiten voordat je deze kan sluiten. diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 4b243de3cf46..e013b985b40e 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1092,8 +1092,8 @@ issues.dependency.remove=Usuń issues.dependency.remove_info=Usuń tę zależność issues.dependency.added_dependency=`dodał nową zależność %s` issues.dependency.removed_dependency=`usunął zależność %s` -issues.dependency.issue_closing_blockedby=Zamknięcie tego Pull Requesta jest blokowane przez następujące zgłoszenia -issues.dependency.pr_closing_blockedby=Zamknięcie tego zgłoszenia jest blokowane przez następujące zgłoszenia +issues.dependency.pr_closing_blockedby=Zamknięcie tego Pull Requesta jest blokowane przez następujące zgłoszenia +issues.dependency.issue_closing_blockedby=Zamknięcie tego zgłoszenia jest blokowane przez następujące zgłoszenia issues.dependency.issue_close_blocks=To zgłoszenie blokuje zamknięcie następujących zgłoszeń issues.dependency.pr_close_blocks=Ten Pull Request blokuje zamknięcie następujących zgłoszeń issues.dependency.issue_close_blocked=Musisz zamknąć wszystkie zgłoszenia blokujące to zgłoszenie zanim je zamkniesz. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index e95b388df08e..f06422db60d6 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1186,8 +1186,8 @@ issues.dependency.add=Adicione… issues.dependency.cancel=Cancelar issues.dependency.remove=Remover issues.dependency.remove_info=Remover esta dependência -issues.dependency.issue_closing_blockedby=Fechamento deste pull request está bloqueado pelas seguintes issues -issues.dependency.pr_closing_blockedby=Fechamento desta issue está bloqueado pelas seguintes issues +issues.dependency.pr_closing_blockedby=Fechamento deste pull request está bloqueado pelas seguintes issues +issues.dependency.issue_closing_blockedby=Fechamento desta issue está bloqueado pelas seguintes issues issues.dependency.issue_close_blocks=Esta issue bloqueia o fechamento das seguintes issues issues.dependency.pr_close_blocks=Este pull request bloqueia o fechamento das seguintes issues issues.dependency.issue_close_blocked=Você precisa fechar todas as issues que bloqueiam esta issue antes de poder fechá-la. diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 24b54a918945..af699711fa6a 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1326,8 +1326,8 @@ issues.dependency.remove=Remover issues.dependency.remove_info=Remover esta dependência issues.dependency.added_dependency=`adicionou uma nova dependência %s` issues.dependency.removed_dependency=`removeu uma dependência %s` -issues.dependency.issue_closing_blockedby=O encerramento deste pedido de integração está bloqueado pelas seguintes questões -issues.dependency.pr_closing_blockedby=O encerramento desta questão está bloqueado pelas seguintes questões +issues.dependency.pr_closing_blockedby=O encerramento deste pedido de integração está bloqueado pelas seguintes questões +issues.dependency.issue_closing_blockedby=O encerramento desta questão está bloqueado pelas seguintes questões issues.dependency.issue_close_blocks=Esta questão bloqueia o encerramento das seguintes questões issues.dependency.pr_close_blocks=Este pedido de integração bloqueia o encerramento das seguintes questões issues.dependency.issue_close_blocked=Tem que encerrar todas as questões que bloqueiam esta questão antes de a poder encerrar. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 103abcf31220..c9343e8b52ae 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1324,8 +1324,8 @@ issues.dependency.remove=Удалить issues.dependency.remove_info=Удалить эту зависимость issues.dependency.added_dependency=`добавить новую зависимость %s` issues.dependency.removed_dependency=`убрал зависимость %s` -issues.dependency.issue_closing_blockedby=Закрытие этого запроса на слияние невозможно до закрытия следующих задач -issues.dependency.pr_closing_blockedby=Закрытие этой задачи блокируется следующими задачами +issues.dependency.pr_closing_blockedby=Закрытие этого запроса на слияние невозможно до закрытия следующих задач +issues.dependency.issue_closing_blockedby=Закрытие этой задачи блокируется следующими задачами issues.dependency.issue_close_blocks=Эта задача блокирует закрытие следующих задач issues.dependency.pr_close_blocks=Этот запрос на слияние блокирует закрытие следующих задач issues.dependency.issue_close_blocked=Вам необходимо закрыть все задачи, блокирующие эту задачу, прежде чем вы сможете её закрыть. diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index a1c13f2e411e..8089e8f79efa 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -1121,8 +1121,8 @@ issues.dependency.remove=Ta bort issues.dependency.remove_info=Ta bort detta beroende issues.dependency.added_dependency=`lade till ett nytt beroende %s` issues.dependency.removed_dependency=`tog bort ett beroende %s` -issues.dependency.issue_closing_blockedby=En stängning av denna pull-förfrågan blockeras av följande ärenden -issues.dependency.pr_closing_blockedby=En stängning av ärendet blockeras av följande ärenden +issues.dependency.pr_closing_blockedby=En stängning av denna pull-förfrågan blockeras av följande ärenden +issues.dependency.issue_closing_blockedby=En stängning av ärendet blockeras av följande ärenden issues.dependency.issue_close_blocks=Detta ärende blockerar en stängning av följande ärenden issues.dependency.pr_close_blocks=Denna pull-förfrågan blockerar stängning av följande ärenden issues.dependency.issue_close_blocked=Du måste stänga alla ärenden som blockerar det här ärendet innan du kan stänga det. diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 69ac3367c185..4f347e1fcb7c 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -1252,8 +1252,8 @@ issues.dependency.remove=Kaldır issues.dependency.remove_info=Bu bağımlılığı kaldır issues.dependency.added_dependency=`yeni bir %s bağımlılığı eklendi` issues.dependency.removed_dependency=`bir %s bağımlılığı kaldırıldı` -issues.dependency.issue_closing_blockedby=Bu değişiklik isteğinin kapatılması aşağıdaki konular nedeniyle engelleniyor -issues.dependency.pr_closing_blockedby=Bu konunun kapatılması aşağıdaki konular tarafından engelleniyor +issues.dependency.pr_closing_blockedby=Bu değişiklik isteğinin kapatılması aşağıdaki konular nedeniyle engelleniyor +issues.dependency.issue_closing_blockedby=Bu konunun kapatılması aşağıdaki konular tarafından engelleniyor issues.dependency.issue_close_blocks=Bu konu aşağıdaki konuların kapatılmasını engelliyor issues.dependency.pr_close_blocks=Bu değişiklik isteği aşağıdaki sorunların kapatılmasını engelliyor issues.dependency.issue_close_blocked=Kapatmadan önce bu konuyu engelleyen tüm konuları kapatmanız gerekir. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index a2a789ee77d2..ba31505f863f 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1306,8 +1306,8 @@ issues.dependency.remove=Видалити issues.dependency.remove_info=Видалити цю залежність issues.dependency.added_dependency=`додав нову залежність %s` issues.dependency.removed_dependency=`видалив залежність %s` -issues.dependency.issue_closing_blockedby=Закриття цього запиту на злиття заблокує наступні проблеми -issues.dependency.pr_closing_blockedby=Закриття цієї проблеми заблокує наступні проблеми +issues.dependency.pr_closing_blockedby=Закриття цього запиту на злиття заблокує наступні проблеми +issues.dependency.issue_closing_blockedby=Закриття цієї проблеми заблокує наступні проблеми issues.dependency.issue_close_blocks=Ця проблема блокує закриття залежних проблем issues.dependency.pr_close_blocks=Цей пулл-реквест блокує закриття залежних проблем issues.dependency.issue_close_blocked=Вам потрібно закрити всі проблеми, що блокують цю проблему, перед її закриттям. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 7c4cadb4dbd1..2a1af4bae0f6 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1322,8 +1322,8 @@ issues.dependency.remove=删除 issues.dependency.remove_info=删除此依赖项 issues.dependency.added_dependency=`添加了一个新的依赖项 %s` issues.dependency.removed_dependency=`移除了一个依赖项 %s` -issues.dependency.issue_closing_blockedby=以下工单阻止了关闭此合并请求 -issues.dependency.pr_closing_blockedby=以下工单阻止了关闭此工单 +issues.dependency.pr_closing_blockedby=以下工单阻止了关闭此合并请求 +issues.dependency.issue_closing_blockedby=以下工单阻止了关闭此工单 issues.dependency.issue_close_blocks=此工单阻止了以下工单的关闭 issues.dependency.pr_close_blocks=此合并请求阻止以下工单的关闭 issues.dependency.issue_close_blocked=您需要关闭所有阻止此工单的工单, 然后才能关闭它。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 7b49562c8ef2..0864ea154f2c 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1311,8 +1311,8 @@ issues.dependency.remove=移除 issues.dependency.remove_info=移除此先決條件 issues.dependency.added_dependency=`加入了新的先決條件 %s` issues.dependency.removed_dependency=`移除了先決條件 %s` -issues.dependency.issue_closing_blockedby=此合併請求被下列問題阻擋而無法關閉 -issues.dependency.pr_closing_blockedby=此問題被下列問題阻擋而無法關閉 +issues.dependency.pr_closing_blockedby=此合併請求被下列問題阻擋而無法關閉 +issues.dependency.issue_closing_blockedby=此問題被下列問題阻擋而無法關閉 issues.dependency.issue_close_blocks=因為此問題的阻擋,下列問題無法被關閉 issues.dependency.pr_close_blocks=因為此合併請求的阻擋,下列問題無法被關閉 issues.dependency.issue_close_blocked=在您關閉此問題以前,您必須先關閉所有阻擋它的問題。 diff --git a/package-lock.json b/package-lock.json index 43f1b6f45f89..27080929a1ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12076,9 +12076,9 @@ } }, "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", + "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -23018,9 +23018,9 @@ "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==" }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", + "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", "dev": true, "requires": { "chownr": "^2.0.0", diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 4258ea5dc300..62452f6580a6 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -569,6 +569,7 @@ func Routes() *web.Route { //setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option AllowedMethods: setting.CORSConfig.Methods, AllowCredentials: setting.CORSConfig.AllowCredentials, + AllowedHeaders: []string{"Authorization", "X-CSRFToken", "X-Gitea-OTP"}, MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), })) } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 66bcabfd38c4..6e93ac02f482 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1254,5 +1254,6 @@ func GetPullRequestCommits(ctx *context.APIContext) { ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumberOfCommits)) ctx.Header().Set("X-PageCount", strconv.Itoa(totalNumberOfPages)) ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link") ctx.JSON(http.StatusOK, &apiCommits) } diff --git a/routers/api/v1/swagger/app.go b/routers/api/v1/swagger/app.go index 8be2c855749a..9783abe1a082 100644 --- a/routers/api/v1/swagger/app.go +++ b/routers/api/v1/swagger/app.go @@ -14,3 +14,10 @@ type swaggerResponseOAuth2Application struct { // in:body Body api.OAuth2Application `json:"body"` } + +// AccessToken represents an API access token. +// swagger:response AccessToken +type swaggerResponseAccessToken struct { + // in:body + Body api.AccessToken `json:"body"` +} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 0ae96a920354..3f0c6e2d524c 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -164,6 +164,9 @@ type swaggerParameterBodies struct { // in:body CreateTagOption api.CreateTagOption + // in:body + CreateAccessTokenOption api.CreateAccessTokenOption + // in:body UserSettingsOptions api.UserSettingsOptions } diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 9f355a828950..afd209f2f079 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -76,15 +76,10 @@ func CreateAccessToken(ctx *context.APIContext) { // description: username of user // type: string // required: true - // - name: accessToken + // - name: userCreateToken // in: body // schema: - // type: object - // required: - // - name - // properties: - // name: - // type: string + // "$ref": "#/definitions/CreateAccessTokenOption" // responses: // "201": // "$ref": "#/responses/AccessToken" diff --git a/routers/private/hook.go b/routers/private/hook.go index 9f5579b6ae68..f93632515272 100644 --- a/routers/private/hook.go +++ b/routers/private/hook.go @@ -392,11 +392,6 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { }) return } - } else { - log.Error("Unexpected ref: %s", refFullName) - ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: fmt.Sprintf("Unexpected ref: %s", refFullName), - }) } } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index fddfc4a63a89..e66aa614cb0a 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/gitdiff" ) @@ -567,6 +568,18 @@ func PrepareCompareDiff( } else { title = headBranch } + if len(title) > 255 { + var trailer string + title, trailer = util.SplitStringAtByteN(title, 255) + if len(trailer) > 0 { + if ctx.Data["content"] != nil { + ctx.Data["content"] = fmt.Sprintf("%s\n\n%s", trailer, ctx.Data["content"]) + } else { + ctx.Data["content"] = trailer + "\n" + } + } + } + ctx.Data["title"] = title ctx.Data["Username"] = headUser.Name ctx.Data["Reponame"] = headRepo.Name diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 9639ea82014a..3162e0932d7c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1728,10 +1728,12 @@ func UpdateIssueContent(ctx *context.Context) { return } - files := ctx.QueryStrings("files[]") - if err := updateAttachments(issue, files); err != nil { - ctx.ServerError("UpdateAttachments", err) - return + // when update the request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates + if !ctx.QueryBool("ignore_attachments") { + if err := updateAttachments(issue, ctx.QueryStrings("files[]")); err != nil { + ctx.ServerError("UpdateAttachments", err) + return + } } content, err := markdown.RenderString(&markup.RenderContext{ @@ -2128,13 +2130,6 @@ func UpdateCommentContent(ctx *context.Context) { return } - if comment.Type == models.CommentTypeComment { - if err := comment.LoadAttachments(); err != nil { - ctx.ServerError("LoadAttachments", err) - return - } - } - if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { ctx.Error(http.StatusForbidden) return @@ -2156,10 +2151,19 @@ func UpdateCommentContent(ctx *context.Context) { return } - files := ctx.QueryStrings("files[]") - if err := updateAttachments(comment, files); err != nil { - ctx.ServerError("UpdateAttachments", err) - return + if comment.Type == models.CommentTypeComment { + if err := comment.LoadAttachments(); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } + } + + // when the update request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates + if !ctx.QueryBool("ignore_attachments") { + if err := updateAttachments(comment, ctx.QueryStrings("files[]")); err != nil { + ctx.ServerError("UpdateAttachments", err) + return + } } content, err := markdown.RenderString(&markup.RenderContext{ diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index a29979964777..703bbd837a9d 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1001,10 +1001,14 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true + ctx.Data["IsRepoToolbarCommits"] = true + ctx.Data["RequireTribute"] = true + ctx.Data["RequireSimpleMDE"] = true ctx.Data["RequireHighlightJS"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") + ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(models.UnitTypePullRequests) var ( repo = ctx.Repo.Repository @@ -1037,6 +1041,14 @@ func CompareAndPullRequestPost(ctx *context.Context) { return } + if len(form.Title) > 255 { + var trailer string + form.Title, trailer = util.SplitStringAtByteN(form.Title, 255) + + form.Content = trailer + "\n\n" + form.Content + } + middleware.AssignForm(form, ctx.Data) + ctx.HTML(http.StatusOK, tplCompareDiff) return } diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index 8934a6619f0f..bcdb8023acff 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -81,7 +81,7 @@ func TestWiki(t *testing.T) { Wiki(ctx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, "Home", ctx.Data["Title"]) - assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"]) + assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"]) } func TestWikiPages(t *testing.T) { @@ -91,7 +91,7 @@ func TestWikiPages(t *testing.T) { test.LoadRepo(t, ctx, 1) WikiPages(ctx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) - assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"]) + assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"]) } func TestNewWiki(t *testing.T) { diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 523e945db9bb..851af5d64792 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -50,6 +50,7 @@ func Notifications(c *context.Context) { return } if c.QueryBool("div-only") { + c.Data["SequenceNumber"] = c.Query("sequence-number") c.HTML(http.StatusOK, tplNotificationDiv) return } @@ -175,6 +176,7 @@ func NotificationStatusPost(c *context.Context) { return } c.Data["Link"] = setting.AppURL + "notifications" + c.Data["SequenceNumber"] = c.Req.PostFormValue("sequence-number") c.HTML(http.StatusOK, tplNotificationDiv) } diff --git a/routers/web/user/oauth.go b/routers/web/user/oauth.go index 72295b4447c2..a2810485635e 100644 --- a/routers/web/user/oauth.go +++ b/routers/web/user/oauth.go @@ -24,7 +24,7 @@ import ( "code.gitea.io/gitea/services/forms" "gitea.com/go-chi/binding" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt" jsoniter "github.com/json-iterator/go" ) @@ -187,7 +187,7 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, signingKey oauth2.JWTSign ErrorDescription: "cannot find application", } } - err = app.LoadUser() + user, err := models.GetUserByID(grant.UserID) if err != nil { if models.IsErrUserNotExist(err) { return nil, &AccessTokenError{ @@ -212,17 +212,17 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, signingKey oauth2.JWTSign Nonce: grant.Nonce, } if grant.ScopeContains("profile") { - idToken.Name = app.User.FullName - idToken.PreferredUsername = app.User.Name - idToken.Profile = app.User.HTMLURL() - idToken.Picture = app.User.AvatarLink() - idToken.Website = app.User.Website - idToken.Locale = app.User.Language - idToken.UpdatedAt = app.User.UpdatedUnix + idToken.Name = user.FullName + idToken.PreferredUsername = user.Name + idToken.Profile = user.HTMLURL() + idToken.Picture = user.AvatarLink() + idToken.Website = user.Website + idToken.Locale = user.Language + idToken.UpdatedAt = user.UpdatedUnix } if grant.ScopeContains("email") { - idToken.Email = app.User.Email - idToken.EmailVerified = app.User.IsActive + idToken.Email = user.Email + idToken.EmailVerified = user.IsActive } signedIDToken, err = idToken.SignToken(signingKey) diff --git a/routers/web/user/oauth_test.go b/routers/web/user/oauth_test.go new file mode 100644 index 000000000000..bc7e6ff209ad --- /dev/null +++ b/routers/web/user/oauth_test.go @@ -0,0 +1,75 @@ +// Copyright 2021 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 user + +import ( + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth/oauth2" + + "github.com/golang-jwt/jwt" + "github.com/stretchr/testify/assert" +) + +func createAndParseToken(t *testing.T, grant *models.OAuth2Grant) *models.OIDCToken { + signingKey, err := oauth2.CreateJWTSingingKey("HS256", make([]byte, 32)) + assert.NoError(t, err) + assert.NotNil(t, signingKey) + oauth2.DefaultSigningKey = signingKey + + response, terr := newAccessTokenResponse(grant, signingKey) + assert.Nil(t, terr) + assert.NotNil(t, response) + + parsedToken, err := jwt.ParseWithClaims(response.IDToken, &models.OIDCToken{}, func(token *jwt.Token) (interface{}, error) { + assert.NotNil(t, token.Method) + assert.Equal(t, signingKey.SigningMethod().Alg(), token.Method.Alg()) + return signingKey.VerifyKey(), nil + }) + assert.NoError(t, err) + assert.True(t, parsedToken.Valid) + + oidcToken, ok := parsedToken.Claims.(*models.OIDCToken) + assert.True(t, ok) + assert.NotNil(t, oidcToken) + + return oidcToken +} + +func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { + assert.NoError(t, models.PrepareTestDatabase()) + + grants, err := models.GetOAuth2GrantsByUserID(3) + assert.NoError(t, err) + assert.Len(t, grants, 1) + + // Scopes: openid + oidcToken := createAndParseToken(t, grants[0]) + assert.Empty(t, oidcToken.Name) + assert.Empty(t, oidcToken.PreferredUsername) + assert.Empty(t, oidcToken.Profile) + assert.Empty(t, oidcToken.Picture) + assert.Empty(t, oidcToken.Website) + assert.Empty(t, oidcToken.UpdatedAt) + assert.Empty(t, oidcToken.Email) + assert.False(t, oidcToken.EmailVerified) + + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User) + grants, err = models.GetOAuth2GrantsByUserID(user.ID) + assert.NoError(t, err) + assert.Len(t, grants, 1) + + // Scopes: openid profile email + oidcToken = createAndParseToken(t, grants[0]) + assert.Equal(t, user.FullName, oidcToken.Name) + assert.Equal(t, user.Name, oidcToken.PreferredUsername) + assert.Equal(t, user.HTMLURL(), oidcToken.Profile) + assert.Equal(t, user.AvatarLink(), oidcToken.Picture) + assert.Equal(t, user.Website, oidcToken.Website) + assert.Equal(t, user.UpdatedUnix, oidcToken.UpdatedAt) + assert.Equal(t, user.Email, oidcToken.Email) + assert.Equal(t, user.IsActive, oidcToken.EmailVerified) +} diff --git a/routers/web/web.go b/routers/web/web.go index aefcef184fc6..9368c2de2017 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -822,9 +822,14 @@ func RegisterRoutes(m *web.Route) { } ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount }) - m.Get("/attachments/{uuid}", repo.GetAttachment) + }, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader) + // to maintain compatibility with old attachments + m.Group("/{username}/{reponame}", func() { + m.Get("/attachments/{uuid}", repo.GetAttachment) + }, ignSignIn, context.RepoAssignment, context.UnitTypes()) + m.Group("/{username}/{reponame}", func() { m.Post("/topics", repo.TopicsPost) }, context.RepoAssignment, context.RepoMustNotBeArchived(), reqRepoAdmin) @@ -1006,17 +1011,17 @@ func RegisterRoutes(m *web.Route) { }, ignSignInAndCsrf, lfsServerEnabled) m.Group("", func() { - m.Post("/git-upload-pack", repo.ServiceUploadPack) - m.Post("/git-receive-pack", repo.ServiceReceivePack) - m.Get("/info/refs", repo.GetInfoRefs) - m.Get("/HEAD", repo.GetTextFile("HEAD")) - m.Get("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) - m.Get("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) - m.Get("/objects/info/packs", repo.GetInfoPacks) - m.Get("/objects/info/{file:[^/]*}", repo.GetTextFile("")) - m.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) - m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) - m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) + m.PostOptions("/git-upload-pack", repo.ServiceUploadPack) + m.PostOptions("/git-receive-pack", repo.ServiceReceivePack) + m.GetOptions("/info/refs", repo.GetInfoRefs) + m.GetOptions("/HEAD", repo.GetTextFile("HEAD")) + m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) + m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) + m.GetOptions("/objects/info/packs", repo.GetInfoPacks) + m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile("")) + m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) + m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) + m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) }, ignSignInAndCsrf) m.Head("/tasks/trigger", repo.TriggerTask) diff --git a/services/lfs/server.go b/services/lfs/server.go index 9954534b5e9a..6b79a3a36402 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -21,7 +21,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "github.com/dgrijalva/jwt-go" + "github.com/golang-jwt/jwt" jsoniter "github.com/json-iterator/go" ) diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 16301034da15..e1590f461ef2 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -88,8 +88,11 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string escaped := NameToFilename(wikiName) // Look for both files - filesInIndex, err := gitRepo.LsFiles(unescaped, escaped) + filesInIndex, err := gitRepo.LsTree("master", unescaped, escaped) if err != nil { + if strings.Contains(err.Error(), "Not a valid object name master") { + return false, escaped, nil + } log.Error("%v", err) return false, escaped, err } @@ -308,14 +311,9 @@ func DeleteWikiPage(doer *models.User, repo *models.Repository, wikiName string) return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err) } - wikiPath := NameToFilename(wikiName) - filesInIndex, err := gitRepo.LsFiles(wikiPath) - found := false - for _, file := range filesInIndex { - if file == wikiPath { - found = true - break - } + found, wikiPath, err := prepareWikiFileName(gitRepo, wikiName) + if err != nil { + return err } if found { err := gitRepo.RemoveFilesFromIndex(wikiPath) diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index b35b86d655be..6c861d556a7f 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -5,11 +5,15 @@ package wiki import ( + "io/ioutil" + "os" "path/filepath" "testing" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/util" + "github.com/stretchr/testify/assert" ) @@ -210,3 +214,79 @@ func TestRepository_DeleteWikiPage(t *testing.T) { _, err = masterTree.GetTreeEntryByPath(wikiPath) assert.Error(t, err) } + +func TestPrepareWikiFileName(t *testing.T) { + models.PrepareTestEnv(t) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + gitRepo, err := git.OpenRepository(repo.WikiPath()) + defer gitRepo.Close() + assert.NoError(t, err) + + tests := []struct { + name string + arg string + existence bool + wikiPath string + wantErr bool + }{{ + name: "add suffix", + arg: "Home", + existence: true, + wikiPath: "Home.md", + wantErr: false, + }, { + name: "test special chars", + arg: "home of and & or wiki page!", + existence: false, + wikiPath: "home-of-and-%26-or-wiki-page%21.md", + wantErr: false, + }, { + name: "fount unescaped cases", + arg: "Unescaped File", + existence: true, + wikiPath: "Unescaped File.md", + wantErr: false, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + existence, newWikiPath, err := prepareWikiFileName(gitRepo, tt.arg) + if (err != nil) != tt.wantErr { + assert.NoError(t, err) + return + } + if existence != tt.existence { + if existence { + t.Errorf("expect to find no escaped file but we detect one") + } else { + t.Errorf("expect to find an escaped file but we could not detect one") + } + } + assert.Equal(t, tt.wikiPath, newWikiPath) + }) + } +} + +func TestPrepareWikiFileName_FirstPage(t *testing.T) { + models.PrepareTestEnv(t) + + // Now create a temporaryDirectory + tmpDir, err := ioutil.TempDir("", "empty-wiki") + assert.NoError(t, err) + defer func() { + if _, err := os.Stat(tmpDir); !os.IsNotExist(err) { + _ = util.RemoveAll(tmpDir) + } + }() + + err = git.InitRepository(tmpDir, true) + assert.NoError(t, err) + + gitRepo, err := git.OpenRepository(tmpDir) + defer gitRepo.Close() + assert.NoError(t, err) + + existence, newWikiPath, err := prepareWikiFileName(gitRepo, "Home") + assert.False(t, existence) + assert.NoError(t, err) + assert.Equal(t, "Home.md", newWikiPath) +} diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index 7959ef7bb517..2beca579601f 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -176,10 +176,10 @@ {{if .IsNothingToCompare}} {{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived) }}
{{.i18n.Tr "repo.pulls.nothing_to_compare_and_allow_empty_pr"}}
-
+
- {{else}} {{if and $.IsSigned (not .Repository.IsArchived)}} -
+
{{else if .Repository.IsArchived}} @@ -201,7 +201,7 @@
{{end}} {{if $.IsSigned}} -
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 297720cec9e6..68f25e0e5e89 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -11917,18 +11917,10 @@ "required": true }, { - "name": "accessToken", + "name": "userCreateToken", "in": "body", "schema": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string" - } - } + "$ref": "#/definitions/CreateAccessTokenOption" } } ], @@ -12654,6 +12646,17 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "CreateAccessTokenOption": { + "description": "CreateAccessTokenOption options when create access token", + "type": "object", + "properties": { + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "CreateBranchProtectionOption": { "description": "CreateBranchProtectionOption options for creating a branch protection", "type": "object", @@ -17044,20 +17047,8 @@ "responses": { "AccessToken": { "description": "AccessToken represents an API access token.", - "headers": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "sha1": { - "type": "string" - }, - "token_last_eight": { - "type": "string" - } + "schema": { + "$ref": "#/definitions/AccessToken" } }, "AccessTokenList": { diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index e7327d34bbe9..8976e1fda4a0 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -1,4 +1,4 @@ -
+

{{.i18n.Tr "notification.notifications"}}