From 681c3ec7eab548d8888afcf01d57a1825b55aba4 Mon Sep 17 00:00:00 2001 From: Kyle D Date: Thu, 22 Feb 2024 13:53:03 -0500 Subject: [PATCH 01/79] Remove bountysource (#29330) [Bountysource is dead](https://github.com/bountysource/core/issues/1586). So remove them from our repo. --- .github/FUNDING.yml | 1 - README.md | 3 --- README_ZH.md | 3 --- 3 files changed, 7 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 624a2d97db..1447a6ea32 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1 @@ open_collective: gitea -custom: https://www.bountysource.com/teams/gitea diff --git a/README.md b/README.md index adba74d8bb..94d7284c7c 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,6 @@ - - -

diff --git a/README_ZH.md b/README_ZH.md index 0d9092a0fd..adfeb9a8df 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -45,9 +45,6 @@ - - -

From 532da5ed5ee3edb45d2ee63c6ab0fad53473691f Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 22 Feb 2024 22:21:43 +0100 Subject: [PATCH 02/79] Don't show third-party JS errors in production builds (#29303) So we don't get issues like https://github.com/go-gitea/gitea/issues/29080 and https://github.com/go-gitea/gitea/issues/29273 any more. Only active in [production builds](https://webpack.js.org/guides/production/#specify-the-mode), in non-production the errors will still show. --- web_src/js/bootstrap.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index f8d0c0cac0..e46c91e5e6 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -29,17 +29,26 @@ export function showGlobalErrorMessage(msg) { * @param {ErrorEvent} e */ function processWindowErrorEvent(e) { + const err = e.error ?? e.reason; + const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin)); + + // error is likely from browser extension or inline script. Do not show these in production builds. + if (!err.stack?.includes(assetBaseUrl) && window.config?.runModeIsProd) return; + + let message; if (e.type === 'unhandledrejection') { - showGlobalErrorMessage(`JavaScript promise rejection: ${e.reason}. Open browser console to see more details.`); - return; + message = `JavaScript promise rejection: ${err.message}.`; + } else { + message = `JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}).`; } + if (!e.error && e.lineno === 0 && e.colno === 0 && e.filename === '' && window.navigator.userAgent.includes('FxiOS/')) { // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240 // If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0. return; // ignore such nonsense error event } - showGlobalErrorMessage(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); + showGlobalErrorMessage(`${message} Open browser console to see more details.`); } function initGlobalErrorHandler() { From c4b0cb4d0d527793296cf801e611f77666f86551 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 23 Feb 2024 00:31:24 +0100 Subject: [PATCH 03/79] Upgrade to fabric 6 (#29334) Upgrade fabric to latest v6 beta. It works for our use case, even thought it does not fix the upstream issue https://github.com/fabricjs/fabric.js/issues/9679 that https://github.com/go-gitea/gitea/issues/29326 relates to. --- Makefile | 2 +- build/generate-images.js | 29 +++++++++++------------------ public/assets/img/favicon.svg | 2 +- public/assets/img/logo.svg | 2 +- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 7fa8193800..4ef02c6c54 100644 --- a/Makefile +++ b/Makefile @@ -969,7 +969,7 @@ generate-gitignore: .PHONY: generate-images generate-images: | node_modules - npm install --no-save --no-package-lock fabric@5 imagemin-zopfli@7 + npm install --no-save fabric@6.0.0-beta19 imagemin-zopfli@7 node build/generate-images.js $(TAGS) .PHONY: generate-manpage diff --git a/build/generate-images.js b/build/generate-images.js index 09e3e068af..db31d19e2a 100755 --- a/build/generate-images.js +++ b/build/generate-images.js @@ -1,20 +1,13 @@ #!/usr/bin/env node import imageminZopfli from 'imagemin-zopfli'; import {optimize} from 'svgo'; -import {fabric} from 'fabric'; +import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; import {readFile, writeFile} from 'node:fs/promises'; +import {argv, exit} from 'node:process'; -function exit(err) { +function doExit(err) { if (err) console.error(err); - process.exit(err ? 1 : 0); -} - -function loadSvg(svg) { - return new Promise((resolve) => { - fabric.loadSVGFromString(svg, (objects, options) => { - resolve({objects, options}); - }); - }); + exit(err ? 1 : 0); } async function generate(svg, path, {size, bg}) { @@ -35,14 +28,14 @@ async function generate(svg, path, {size, bg}) { return; } - const {objects, options} = await loadSvg(svg); - const canvas = new fabric.Canvas(); + const {objects, options} = await loadSVGFromString(svg); + const canvas = new Canvas(); canvas.setDimensions({width: size, height: size}); const ctx = canvas.getContext('2d'); ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1); if (bg) { - canvas.add(new fabric.Rect({ + canvas.add(new Rect({ left: 0, top: 0, height: size * (1 / (size / options.height)), @@ -51,7 +44,7 @@ async function generate(svg, path, {size, bg}) { })); } - canvas.add(fabric.util.groupSVGElements(objects, options)); + canvas.add(util.groupSVGElements(objects, options)); canvas.renderAll(); let png = Buffer.from([]); @@ -64,7 +57,7 @@ async function generate(svg, path, {size, bg}) { } async function main() { - const gitea = process.argv.slice(2).includes('gitea'); + const gitea = argv.slice(2).includes('gitea'); const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8'); const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8'); @@ -80,7 +73,7 @@ async function main() { } try { - exit(await main()); + doExit(await main()); } catch (err) { - exit(err); + doExit(err); } diff --git a/public/assets/img/favicon.svg b/public/assets/img/favicon.svg index afeeacb77c..43291345df 100644 --- a/public/assets/img/favicon.svg +++ b/public/assets/img/favicon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/logo.svg b/public/assets/img/logo.svg index afeeacb77c..43291345df 100644 --- a/public/assets/img/logo.svg +++ b/public/assets/img/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 5bb8d1924d77c675467694de26697b876d709a17 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Thu, 22 Feb 2024 19:08:17 -0500 Subject: [PATCH 04/79] Support SAML authentication (#25165) Closes https://github.com/go-gitea/gitea/issues/5512 This PR adds basic SAML support - Adds SAML 2.0 as an auth source - Adds SAML configuration documentation - Adds integration test: - Use bare-bones SAML IdP to test protocol flow and test account is linked successfully (only runs on Postgres by default) - Adds documentation for configuring and running SAML integration test locally Future PRs: - Support group mapping - Support auto-registration (account linking) Co-Authored-By: @jackHay22 --------- Co-authored-by: jackHay22 Co-authored-by: Lunny Xiao Co-authored-by: KN4CK3R Co-authored-by: wxiaoguang Co-authored-by: Jason Song Co-authored-by: morphelinho Co-authored-by: Zettat123 Co-authored-by: Yarden Shoham Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: silverwind --- .github/workflows/pull-db-tests.yml | 8 + assets/go-licenses.json | 25 +++ docs/content/usage/authentication.en-us.md | 69 ++++++ go.mod | 5 + go.sum | 12 ++ models/auth/oauth2.go | 20 +- models/auth/source.go | 38 ++++ options/locale/locale_en-US.ini | 14 ++ routers/init.go | 2 + routers/web/admin/auths.go | 84 ++++++++ routers/web/auth/auth.go | 35 ++- routers/web/auth/linkaccount.go | 45 ++-- routers/web/auth/oauth.go | 19 +- routers/web/auth/openid.go | 5 +- routers/web/auth/saml.go | 172 +++++++++++++++ routers/web/web.go | 5 + .../auth/source/saml/assert_interface_test.go | 22 ++ services/auth/source/saml/init.go | 29 +++ services/auth/source/saml/name_id_format.go | 38 ++++ services/auth/source/saml/providers.go | 109 ++++++++++ services/auth/source/saml/source.go | 202 ++++++++++++++++++ .../auth/source/saml/source_authenticate.go | 16 ++ services/auth/source/saml/source_callout.go | 89 ++++++++ services/auth/source/saml/source_metadata.go | 32 +++ services/auth/source/saml/source_register.go | 23 ++ services/externalaccount/link.go | 11 +- services/externalaccount/user.go | 12 +- services/forms/auth_form.go | 15 +- templates/admin/auth/edit.tmpl | 66 ++++++ templates/admin/auth/new.tmpl | 6 + templates/admin/auth/source/saml.tmpl | 62 ++++++ templates/user/auth/signin_inner.tmpl | 17 ++ tests/integration/README.md | 17 ++ tests/integration/saml_test.go | 150 +++++++++++++ web_src/js/features/admin/common.js | 8 +- web_src/js/features/user-auth.js | 21 ++ web_src/js/index.js | 6 +- 37 files changed, 1440 insertions(+), 69 deletions(-) create mode 100644 routers/web/auth/saml.go create mode 100644 services/auth/source/saml/assert_interface_test.go create mode 100644 services/auth/source/saml/init.go create mode 100644 services/auth/source/saml/name_id_format.go create mode 100644 services/auth/source/saml/providers.go create mode 100644 services/auth/source/saml/source.go create mode 100644 services/auth/source/saml/source_authenticate.go create mode 100644 services/auth/source/saml/source_callout.go create mode 100644 services/auth/source/saml/source_metadata.go create mode 100644 services/auth/source/saml/source_register.go create mode 100644 templates/admin/auth/source/saml.tmpl create mode 100644 tests/integration/saml_test.go diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml index a3886bf618..8843c6d65e 100644 --- a/.github/workflows/pull-db-tests.yml +++ b/.github/workflows/pull-db-tests.yml @@ -37,6 +37,14 @@ jobs: MINIO_ROOT_PASSWORD: 12345678 ports: - "9000:9000" + simplesaml: + image: allspice/simple-saml + ports: + - "8080:8080" + env: + SIMPLESAMLPHP_SP_ENTITY_ID: http://localhost:3002/user/saml/test-sp/metadata + SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: http://localhost:3002/user/saml/test-sp/acs + SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE: http://localhost:3002/user/saml/test-sp/acs steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 2aa60780c4..ed722b0192 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -124,6 +124,11 @@ "path": "github.com/aymerick/douceur/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Aymerick JEHANNE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, + { + "name": "github.com/beevik/etree", + "path": "github.com/beevik/etree/LICENSE", + "licenseText": "Copyright 2015-2019 Brett Vickers. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n 1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n 2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\nOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, { "name": "github.com/beorn7/perks/quantile", "path": "github.com/beorn7/perks/quantile/LICENSE", @@ -639,6 +644,11 @@ "path": "github.com/jhillyerd/enmime/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2012-2016 James Hillyerd, All Rights Reserved\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, + { + "name": "github.com/jonboulle/clockwork", + "path": "github.com/jonboulle/clockwork/LICENSE", + "licenseText": "Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, { "name": "github.com/josharian/intern", "path": "github.com/josharian/intern/license.md", @@ -719,6 +729,11 @@ "path": "github.com/markbates/goth/LICENSE.txt", "licenseText": "Copyright (c) 2014 Mark Bates\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, + { + "name": "github.com/mattermost/xml-roundtrip-validator", + "path": "github.com/mattermost/xml-roundtrip-validator/LICENSE.txt", + "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, { "name": "github.com/mattn/go-colorable", "path": "github.com/mattn/go-colorable/LICENSE", @@ -904,6 +919,16 @@ "path": "github.com/rs/xid/LICENSE", "licenseText": "Copyright (c) 2015 Olivier Poitrey \u003crs@dailymotion.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, + { + "name": "github.com/russellhaering/gosaml2", + "path": "github.com/russellhaering/gosaml2/LICENSE", + "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n" + }, + { + "name": "github.com/russellhaering/goxmldsig", + "path": "github.com/russellhaering/goxmldsig/LICENSE", + "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n" + }, { "name": "github.com/russross/blackfriday/v2", "path": "github.com/russross/blackfriday/v2/LICENSE.txt", diff --git a/docs/content/usage/authentication.en-us.md b/docs/content/usage/authentication.en-us.md index adc936dfbe..1838cfcc77 100644 --- a/docs/content/usage/authentication.en-us.md +++ b/docs/content/usage/authentication.en-us.md @@ -349,3 +349,72 @@ If set `ENABLE_REVERSE_PROXY_FULL_NAME=true`, a user full name expected in `X-WE You can also limit the reverse proxy's IP address range with `REVERSE_PROXY_TRUSTED_PROXIES` which default value is `127.0.0.0/8,::1/128`. By `REVERSE_PROXY_LIMIT`, you can limit trusted proxies level. Notice: Reverse Proxy Auth doesn't support the API. You still need an access token or basic auth to make API requests. + +## SAML + +### Configuring Gitea as a SAML 2.0 Service Provider + +- Navigate to `Site Administration > Identity & Access > Authentication Sources`. +- Click the `Add Authentication Source` button. +- Select `SAML` as the authentication type. + +#### Features Not Yet Supported + +Currently, auto-registration is not supported for SAML. During the external account linking process the user will be prompted to set a username and email address or link to an existing account. + +SAML group mapping is not supported. + +#### Settings + +- `Authentication Name` **(required)** + + - The name of this authentication source (appears in the Gitea ACS and metadata URLs) + +- `SAML NameID Format` **(required)** + + - This specifies how Identity Provider (IdP) users are mapped to Gitea users. This option will be provider specific. + +- `Icon URL` (optional) + + - URL of an icon to display on the Sign-In page for this authentication source. + +- `[Insecure] Skip Assertion Signature Validation` (optional) + + - This option is not recommended and disables integrity verification of IdP SAML assertions. + +- `Identity Provider Metadata URL` (optional if XML set) + + - The URL of the IdP metadata endpoint. + - This field must be set if `Identity Provider Metadata XML` is left blank. + +- `Identity Provider Metadata XML` (optional if URL set) + + - The XML returned by the IdP metadata endpoint. + - This field must be set if `Identity Provider Metadata URL` is left blank. + +- `Service Provider Certificate` (optional) + + - X.509-formatted certificate (with `Service Provider Private Key`) used for signing SAML requests. + - A certificate will be generated if this field is left blank. + +- `Service Provider Private Key` (optional) + + - DSA/RSA private key (with `Service Provider Certificate`) used for signing SAML requests. + - A private key will be generated if this field is left blank. + +- `Email Assertion Key` (optional) + + - The SAML assertion key used for the IdP user's email (depends on provider configuration). + +- `Name Assertion Key` (optional) + + - The SAML assertion key used for the IdP user's nickname (depends on provider configuration). + +- `Username Assertion Key` (optional) + + - The SAML assertion key used for the IdP user's username (depends on provider configuration). + +### Configuring a SAML 2.0 Identity Provider to use Gitea + +- The service provider assertion consumer service url will look like: `http(s)://[mydomain]/user/saml/[Authentication Name]/acs`. +- The service provider metadata url will look like: `http(s)://[mydomain]/user/saml/[Authentication Name]/metadata`. diff --git a/go.mod b/go.mod index 7a752ec874..012a34612f 100644 --- a/go.mod +++ b/go.mod @@ -91,6 +91,8 @@ require ( github.com/quasoft/websspi v1.1.2 github.com/redis/go-redis/v9 v9.4.0 github.com/robfig/cron/v3 v3.0.1 + github.com/russellhaering/gosaml2 v0.9.1 + github.com/russellhaering/goxmldsig v1.3.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sassoftware/go-rpmutils v0.2.1-0.20240124161140-277b154961dd github.com/sergi/go-diff v1.3.1 @@ -143,6 +145,7 @@ require ( github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/blevesearch/bleve_index_api v1.1.5 // indirect @@ -216,6 +219,7 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect + github.com/jonboulle/clockwork v0.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect @@ -225,6 +229,7 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/markbates/going v1.0.3 // indirect + github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mholt/acmez v1.2.0 // indirect diff --git a/go.sum b/go.sum index b3b8ad8ce4..393e10cfa0 100644 --- a/go.sum +++ b/go.sum @@ -130,6 +130,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= +github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE= @@ -566,6 +568,9 @@ github.com/jhillyerd/enmime v1.1.0 h1:ubaIzg68VY7CMCe2YbHe6nkRvU9vujixTkNz3EBvZO github.com/jhillyerd/enmime v1.1.0/go.mod h1:FRFuUPCLh8PByQv+8xRcLO9QHqaqTqreYhopv5eyk4I= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= +github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -634,6 +639,8 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o= github.com/markbates/goth v1.78.0 h1:7VEIFDycJp9deyVv3YraGBPdD0ZYQW93Y3Aw1eVP3BY= github.com/markbates/goth v1.78.0/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc= +github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= +github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -766,12 +773,17 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/russellhaering/gosaml2 v0.9.1 h1:H/whrl8NuSoxyW46Ww5lKPskm+5K+qYLw9afqJ/Zef0= +github.com/russellhaering/gosaml2 v0.9.1/go.mod h1:ja+qgbayxm+0mxBRLMSUuX3COqy+sb0RRhIGun/W2kc= +github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM= +github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 9d53fffc78..a252458d4e 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -8,6 +8,7 @@ import ( "crypto/sha256" "encoding/base32" "encoding/base64" + "encoding/gob" "fmt" "net" "net/url" @@ -81,6 +82,10 @@ func Init(ctx context.Context) error { builtinAllClientIDs = append(builtinAllClientIDs, clientID) } + // This is needed in order to encode and store the struct in the goth/gothic session + // during the process of linking the external user. + gob.Register(LinkAccountUser{}) + var registeredApps []*OAuth2Application if err := db.GetEngine(ctx).In("client_id", builtinAllClientIDs).Find(®isteredApps); err != nil { return err @@ -605,21 +610,6 @@ func (err ErrOAuthApplicationNotFound) Unwrap() error { return util.ErrNotExist } -// GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name -func GetActiveOAuth2SourceByName(ctx context.Context, name string) (*Source, error) { - authSource := new(Source) - has, err := db.GetEngine(ctx).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(authSource) - if err != nil { - return nil, err - } - - if !has { - return nil, fmt.Errorf("oauth2 source not found, name: %q", name) - } - - return authSource, nil -} - func DeleteOAuth2RelictsByUserID(ctx context.Context, userID int64) error { deleteCond := builder.Select("id").From("oauth2_grant").Where(builder.Eq{"oauth2_grant.user_id": userID}) diff --git a/models/auth/source.go b/models/auth/source.go index 1bdde8235c..bc564d35ba 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "github.com/markbates/goth" "xorm.io/builder" "xorm.io/xorm" "xorm.io/xorm/convert" @@ -32,6 +33,7 @@ const ( DLDAP // 5 OAuth2 // 6 SSPI // 7 + SAML // 8 ) // String returns the string name of the LoginType @@ -52,6 +54,7 @@ var Names = map[Type]string{ PAM: "PAM", OAuth2: "OAuth2", SSPI: "SPNEGO with SSPI", + SAML: "SAML", } // Config represents login config as far as the db is concerned @@ -121,6 +124,12 @@ type Source struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } +// LinkAccountUser is used to link an external user with a local user +type LinkAccountUser struct { + Type Type + GothUser goth.User +} + // TableName xorm will read the table name from this method func (Source) TableName() string { return "login_source" @@ -180,6 +189,11 @@ func (source *Source) IsSSPI() bool { return source.Type == SSPI } +// IsSAML returns true of this source is of the SAML type. +func (source *Source) IsSAML() bool { + return source.Type == SAML +} + // HasTLS returns true of this source supports TLS. func (source *Source) HasTLS() bool { hasTLSer, ok := source.Cfg.(HasTLSer) @@ -392,3 +406,27 @@ func IsErrSourceInUse(err error) bool { func (err ErrSourceInUse) Error() string { return fmt.Sprintf("login source is still used by some users [id: %d]", err.ID) } + +// GetActiveAuthProviderSources returns all activated sources +func GetActiveAuthProviderSources(ctx context.Context, authType Type) ([]*Source, error) { + sources := make([]*Source, 0, 1) + if err := db.GetEngine(ctx).Where("is_active = ? and type = ?", true, authType).Find(&sources); err != nil { + return nil, err + } + return sources, nil +} + +// GetActiveAuthSourceByName returns an AuthSource based on the given name and type +func GetActiveAuthSourceByName(ctx context.Context, name string, authType Type) (*Source, error) { + authSource := new(Source) + has, err := db.GetEngine(ctx).Where("name = ? and type = ? and is_active = ?", name, authType, true).Get(authSource) + if err != nil { + return nil, err + } + + if !has { + return nil, fmt.Errorf("auth source not found, name: %q", name) + } + + return authSource, nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 574e99e654..ae34d72e41 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -522,6 +522,9 @@ Content = Content SSPISeparatorReplacement = Separator SSPIDefaultLanguage = Default Language +SAMLMetadata = Either SAML Identity Provider metadata URL or XML +SAMLMetadataURL = SAML Identity Provider metadata URL is invalid + require_error = ` cannot be empty.` alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` @@ -3026,7 +3029,18 @@ auths.sspi_separator_replacement = Separator to use instead of \, / and @ auths.sspi_separator_replacement_helper = The character to use to replace the separators of down-level logon names (eg. the \ in "DOMAIN\user") and user principal names (eg. the @ in "user@example.org"). auths.sspi_default_language = Default user language auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected. +auths.saml_nameidformat = SAML NameID Format +auths.saml_identity_provider_metadata_url = Identity Provider Metadata URL +auths.saml_identity_provider_metadata = Identity Provider Metadata XML +auths.saml_insecure_skip_assertion_signature_validation = [Insecure] Skip Assertion Signature Validation +auths.saml_service_provider_certificate = Service Provider Certificate +auths.saml_service_provider_private_key = Service Provider Private Key +auths.saml_identity_provider_email_assertion_key = Email Assertion Key +auths.saml_identity_provider_name_assertion_key = Name Assertion Key +auths.saml_identity_provider_username_assertion_key = Username Assertion Key +auths.saml_icon_url = Icon URL auths.tips = Tips +auths.tips.saml = Documentation can be found at https://docs.gitea.com/usage/authentication#saml auths.tips.oauth2.general = OAuth2 Authentication auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be: auths.tip.oauth2_provider = OAuth2 Provider diff --git a/routers/init.go b/routers/init.go index e0a7150ba3..9ae8c368a2 100644 --- a/routers/init.go +++ b/routers/init.go @@ -35,6 +35,7 @@ import ( actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/auth/source/saml" "code.gitea.io/gitea/services/automerge" "code.gitea.io/gitea/services/cron" feed_service "code.gitea.io/gitea/services/feed" @@ -138,6 +139,7 @@ func InitWebInstalled(ctx context.Context) { log.Info("ORM engine initialization successful!") mustInit(system.Init) mustInitCtx(ctx, oauth2.Init) + mustInitCtx(ctx, saml.Init) mustInit(release_service.Init) diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 7fdd18dfae..187b569d39 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -1,9 +1,12 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package admin import ( + "crypto/tls" + "crypto/x509" "errors" "fmt" "net/http" @@ -25,6 +28,7 @@ import ( "code.gitea.io/gitea/services/auth/source/ldap" "code.gitea.io/gitea/services/auth/source/oauth2" pam_service "code.gitea.io/gitea/services/auth/source/pam" + "code.gitea.io/gitea/services/auth/source/saml" "code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/auth/source/sspi" "code.gitea.io/gitea/services/forms" @@ -71,6 +75,7 @@ var ( {auth.SMTP.String(), auth.SMTP}, {auth.OAuth2.String(), auth.OAuth2}, {auth.SSPI.String(), auth.SSPI}, + {auth.SAML.String(), auth.SAML}, } if pam.Supported { items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM}) @@ -83,6 +88,16 @@ var ( {ldap.SecurityProtocolNames[ldap.SecurityProtocolLDAPS], ldap.SecurityProtocolLDAPS}, {ldap.SecurityProtocolNames[ldap.SecurityProtocolStartTLS], ldap.SecurityProtocolStartTLS}, } + + nameIDFormats = []dropdownItem{ + {saml.NameIDFormatNames[saml.SAML20Persistent], saml.SAML20Persistent}, // use this as default value + {saml.NameIDFormatNames[saml.SAML11Email], saml.SAML11Email}, + {saml.NameIDFormatNames[saml.SAML11Persistent], saml.SAML11Persistent}, + {saml.NameIDFormatNames[saml.SAML11Unspecified], saml.SAML11Unspecified}, + {saml.NameIDFormatNames[saml.SAML20Email], saml.SAML20Email}, + {saml.NameIDFormatNames[saml.SAML20Transient], saml.SAML20Transient}, + {saml.NameIDFormatNames[saml.SAML20Unspecified], saml.SAML20Unspecified}, + } ) // NewAuthSource render adding a new auth source page @@ -98,6 +113,8 @@ func NewAuthSource(ctx *context.Context) { ctx.Data["is_sync_enabled"] = true ctx.Data["AuthSources"] = authSources ctx.Data["SecurityProtocols"] = securityProtocols + ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[saml.SAML20Persistent] + ctx.Data["NameIDFormats"] = nameIDFormats ctx.Data["SMTPAuths"] = smtp.Authenticators oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers @@ -231,6 +248,52 @@ func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi }, nil } +func parseSAMLConfig(ctx *context.Context, form forms.AuthenticationForm) (*saml.Source, error) { + if util.IsEmptyString(form.IdentityProviderMetadata) && util.IsEmptyString(form.IdentityProviderMetadataURL) { + return nil, fmt.Errorf("%s %s", ctx.Tr("form.SAMLMetadata"), ctx.Tr("form.require_error")) + } + + if !util.IsEmptyString(form.IdentityProviderMetadataURL) { + _, err := url.Parse(form.IdentityProviderMetadataURL) + if err != nil { + return nil, fmt.Errorf("%s", ctx.Tr("form.SAMLMetadataURL")) + } + } + + // check the integrity of the certificate and private key (autogenerated if these form fields are blank) + if !util.IsEmptyString(form.ServiceProviderCertificate) && !util.IsEmptyString(form.ServiceProviderPrivateKey) { + keyPair, err := tls.X509KeyPair([]byte(form.ServiceProviderCertificate), []byte(form.ServiceProviderPrivateKey)) + if err != nil { + return nil, err + } + keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) + if err != nil { + return nil, err + } + } else { + privateKey, cert, err := saml.GenerateSAMLSPKeypair() + if err != nil { + return nil, err + } + + form.ServiceProviderPrivateKey = privateKey + form.ServiceProviderCertificate = cert + } + + return &saml.Source{ + IdentityProviderMetadata: form.IdentityProviderMetadata, + IdentityProviderMetadataURL: form.IdentityProviderMetadataURL, + InsecureSkipAssertionSignatureValidation: form.InsecureSkipAssertionSignatureValidation, + NameIDFormat: saml.NameIDFormat(form.NameIDFormat), + ServiceProviderCertificate: form.ServiceProviderCertificate, + ServiceProviderPrivateKey: form.ServiceProviderPrivateKey, + EmailAssertionKey: form.EmailAssertionKey, + NameAssertionKey: form.NameAssertionKey, + UsernameAssertionKey: form.UsernameAssertionKey, + IconURL: form.SAMLIconURL, + }, nil +} + // NewAuthSourcePost response for adding an auth source func NewAuthSourcePost(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.AuthenticationForm) @@ -244,6 +307,8 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.Data["SMTPAuths"] = smtp.Authenticators oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers + ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[saml.NameIDFormat(form.NameIDFormat)] + ctx.Data["NameIDFormats"] = nameIDFormats ctx.Data["SSPIAutoCreateUsers"] = true ctx.Data["SSPIAutoActivateUsers"] = true @@ -290,6 +355,13 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) return } + case auth.SAML: + var err error + config, err = parseSAMLConfig(ctx, form) + if err != nil { + ctx.RenderWithErr(err.Error(), tplAuthNew, form) + return + } default: ctx.Error(http.StatusBadRequest) return @@ -336,6 +408,7 @@ func EditAuthSource(ctx *context.Context) { ctx.Data["SMTPAuths"] = smtp.Authenticators oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers + ctx.Data["NameIDFormats"] = nameIDFormats source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid")) if err != nil { @@ -344,6 +417,9 @@ func EditAuthSource(ctx *context.Context) { } ctx.Data["Source"] = source ctx.Data["HasTLS"] = source.HasTLS() + if source.IsSAML() { + ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[source.Cfg.(*saml.Source).NameIDFormat] + } if source.IsOAuth2() { type Named interface { @@ -378,6 +454,8 @@ func EditAuthSourcePost(ctx *context.Context) { } ctx.Data["Source"] = source ctx.Data["HasTLS"] = source.HasTLS() + ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[saml.SAML20Persistent] + ctx.Data["NameIDFormats"] = nameIDFormats if ctx.HasError() { ctx.HTML(http.StatusOK, tplAuthEdit) @@ -412,6 +490,12 @@ func EditAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(err.Error(), tplAuthEdit, form) return } + case auth.SAML: + config, err = parseSAMLConfig(ctx, form) + if err != nil { + ctx.RenderWithErr(err.Error(), tplAuthEdit, form) + return + } default: ctx.Error(http.StatusBadRequest) return diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 3de1f3373d..f5955ec5ff 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -28,6 +28,7 @@ import ( "code.gitea.io/gitea/routers/utils" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/auth/source/saml" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" @@ -170,6 +171,14 @@ func SignIn(ctx *context.Context) { return } ctx.Data["OAuth2Providers"] = oauth2Providers + + samlProviders, err := saml.GetSAMLProviders(ctx, util.OptionalBoolTrue) + if err != nil { + ctx.ServerError("UserSignIn", err) + return + } + ctx.Data["SAMLProviders"] = samlProviders + ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true @@ -193,6 +202,14 @@ func SignInPost(ctx *context.Context) { return } ctx.Data["OAuth2Providers"] = oauth2Providers + + samlProviders, err := saml.GetSAMLProviders(ctx, util.OptionalBoolTrue) + if err != nil { + ctx.ServerError("UserSignIn", err) + return + } + ctx.Data["SAMLProviders"] = samlProviders + ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true @@ -504,7 +521,7 @@ func SignUpPost(ctx *context.Context) { Passwd: form.Password, } - if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false) { + if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false, auth.NoType) { // error already handled return } @@ -515,16 +532,16 @@ func SignUpPost(ctx *context.Context) { // createAndHandleCreatedUser calls createUserInContext and // then handleUserCreated. -func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool { - if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink) { +func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool, authType auth.Type) bool { + if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink, authType) { return false } - return handleUserCreated(ctx, u, gothUser) + return handleUserCreated(ctx, u, gothUser, authType) } // createUserInContext creates a user and handles errors within a given context. // Optionally a template can be specified. -func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) { +func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool, authType auth.Type) (ok bool) { if err := user_model.CreateUser(ctx, u, overwrites); err != nil { if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) { if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { @@ -541,10 +558,10 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us } // TODO: probably we should respect 'remember' user's choice... - linkAccount(ctx, user, *gothUser, true) + linkAccount(ctx, user, *gothUser, true, authType) return false // user is already created here, all redirects are handled } else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin { - showLinkingLogin(ctx, *gothUser) + showLinkingLogin(ctx, *gothUser, authType) return false // user will be created only after linking login } } @@ -590,7 +607,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us // handleUserCreated does additional steps after a new user is created. // It auto-sets admin for the only user, updates the optional external user and // sends a confirmation email if required. -func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) { +func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User, authType auth.Type) (ok bool) { // Auto-set admin for the only user. if user_model.CountUsers(ctx, nil) == 1 { opts := &user_service.UpdateOptions{ @@ -606,7 +623,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. // update external user information if gothUser != nil { - if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser); err != nil { + if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser, authType); err != nil { if !errors.Is(err, util.ErrNotExist) { log.Error("UpdateExternalUser failed: %v", err) } diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index 1d94e52fe3..c62ae84083 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -48,13 +48,13 @@ func LinkAccount(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" - gothUser := ctx.Session.Get("linkAccountGothUser") - if gothUser == nil { + externalLinkUser := ctx.Session.Get("linkAccountUser") + if externalLinkUser == nil { ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) return } - gu, _ := gothUser.(goth.User) + gu := externalLinkUser.(auth.LinkAccountUser).GothUser uname, err := getUserName(&gu) if err != nil { ctx.ServerError("UserSignIn", err) @@ -135,12 +135,14 @@ func LinkAccountPostSignIn(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" - gothUser := ctx.Session.Get("linkAccountGothUser") - if gothUser == nil { + externalLinkUserInterface := ctx.Session.Get("linkAccountUser") + if externalLinkUserInterface == nil { ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) return } + externalLinkUser := externalLinkUserInterface.(auth.LinkAccountUser) + if ctx.HasError() { ctx.HTML(http.StatusOK, tplLinkAccount) return @@ -152,10 +154,10 @@ func LinkAccountPostSignIn(ctx *context.Context) { return } - linkAccount(ctx, u, gothUser.(goth.User), signInForm.Remember) + linkAccount(ctx, u, externalLinkUser.GothUser, signInForm.Remember, externalLinkUser.Type) } -func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool) { +func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool, authType auth.Type) { updateAvatarIfNeed(ctx, gothUser.AvatarURL, u) // If this user is enrolled in 2FA, we can't sign the user in just yet. @@ -168,7 +170,7 @@ func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, r return } - err = externalaccount.LinkAccountToUser(ctx, u, gothUser) + err = externalaccount.LinkAccountToUser(ctx, u, gothUser, authType) if err != nil { ctx.ServerError("UserLinkAccount", err) return @@ -222,14 +224,14 @@ func LinkAccountPostRegister(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" - gothUserInterface := ctx.Session.Get("linkAccountGothUser") - if gothUserInterface == nil { + externalLinkUser := ctx.Session.Get("linkAccountUser") + if externalLinkUser == nil { ctx.ServerError("UserSignUp", errors.New("not in LinkAccount session")) return } - gothUser, ok := gothUserInterface.(goth.User) + linkUser, ok := externalLinkUser.(auth.LinkAccountUser) if !ok { - ctx.ServerError("UserSignUp", fmt.Errorf("session linkAccountGothUser type is %t but not goth.User", gothUserInterface)) + ctx.ServerError("UserSignUp", fmt.Errorf("session linkAccountUser type is %t but not goth.User", externalLinkUser)) return } @@ -275,7 +277,7 @@ func LinkAccountPostRegister(ctx *context.Context) { } } - authSource, err := auth.GetActiveOAuth2SourceByName(ctx, gothUser.Provider) + authSource, err := auth.GetActiveAuthSourceByName(ctx, linkUser.GothUser.Provider, linkUser.Type) if err != nil { ctx.ServerError("CreateUser", err) return @@ -285,21 +287,24 @@ func LinkAccountPostRegister(ctx *context.Context) { Name: form.UserName, Email: form.Email, Passwd: form.Password, - LoginType: auth.OAuth2, + LoginType: authSource.Type, LoginSource: authSource.ID, - LoginName: gothUser.UserID, + LoginName: linkUser.GothUser.UserID, } - if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, &gothUser, false) { + if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, &linkUser.GothUser, false, linkUser.Type) { // error already handled return } - source := authSource.Cfg.(*oauth2.Source) - if err := syncGroupsToTeams(ctx, source, &gothUser, u); err != nil { - ctx.ServerError("SyncGroupsToTeams", err) - return + if linkUser.Type == auth.OAuth2 { + source := authSource.Cfg.(*oauth2.Source) + if err := syncGroupsToTeams(ctx, source, &linkUser.GothUser, u); err != nil { + ctx.ServerError("SyncGroupsToTeams", err) + return + } } + // TODO we will support some form of group mapping for SAML handleSignIn(ctx, u, false) } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index ee0770ef37..d00644dd5f 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -841,7 +841,7 @@ func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirect func SignInOAuth(ctx *context.Context) { provider := ctx.Params(":provider") - authSource, err := auth.GetActiveOAuth2SourceByName(ctx, provider) + authSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.OAuth2) if err != nil { ctx.ServerError("SignIn", err) return @@ -892,7 +892,7 @@ func SignInOAuthCallback(ctx *context.Context) { } // first look if the provider is still active - authSource, err := auth.GetActiveOAuth2SourceByName(ctx, provider) + authSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.OAuth2) if err != nil { ctx.ServerError("SignIn", err) return @@ -935,7 +935,7 @@ func SignInOAuthCallback(ctx *context.Context) { if u == nil { if ctx.Doer != nil { // attach user to already logged in user - err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser) + err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser, auth.OAuth2) if err != nil { ctx.ServerError("UserLinkAccount", err) return @@ -988,7 +988,7 @@ func SignInOAuthCallback(ctx *context.Context) { u.IsAdmin = isAdmin.ValueOrDefault(false) u.IsRestricted = isRestricted.ValueOrDefault(false) - if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) { + if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled, auth.OAuth2) { // error already handled return } @@ -999,7 +999,7 @@ func SignInOAuthCallback(ctx *context.Context) { } } else { // no existing user is found, request attach or new account - showLinkingLogin(ctx, gothUser) + showLinkingLogin(ctx, gothUser, auth.OAuth2) return } } @@ -1063,9 +1063,12 @@ func getUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, gothUser *g return isAdmin, isRestricted } -func showLinkingLogin(ctx *context.Context, gothUser goth.User) { +func showLinkingLogin(ctx *context.Context, gothUser goth.User, authType auth.Type) { if err := updateSession(ctx, nil, map[string]any{ - "linkAccountGothUser": gothUser, + "linkAccountUser": auth.LinkAccountUser{ + Type: authType, + GothUser: gothUser, + }, }); err != nil { ctx.ServerError("updateSession", err) return @@ -1144,7 +1147,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model } // update external user information - if err := externalaccount.UpdateExternalUser(ctx, u, gothUser); err != nil { + if err := externalaccount.UpdateExternalUser(ctx, u, gothUser, auth.OAuth2); err != nil { if !errors.Is(err, util.ErrNotExist) { log.Error("UpdateExternalUser failed: %v", err) } diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index 29ef772b1c..bf377b4496 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" + auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/base" @@ -363,7 +364,7 @@ func RegisterOpenIDPost(ctx *context.Context) { Email: form.Email, Passwd: password, } - if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil, false) { + if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil, false, auth_model.NoType) { // error already handled return } @@ -379,7 +380,7 @@ func RegisterOpenIDPost(ctx *context.Context) { return } - if !handleUserCreated(ctx, u, nil) { + if !handleUserCreated(ctx, u, nil, auth_model.NoType) { // error already handled return } diff --git a/routers/web/auth/saml.go b/routers/web/auth/saml.go new file mode 100644 index 0000000000..29d689d2e9 --- /dev/null +++ b/routers/web/auth/saml.go @@ -0,0 +1,172 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "errors" + "fmt" + "net/http" + "strings" + + "code.gitea.io/gitea/models/auth" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/auth/source/saml" + "code.gitea.io/gitea/services/externalaccount" + + "github.com/markbates/goth" +) + +func SignInSAML(ctx *context.Context) { + provider := ctx.Params(":provider") + + loginSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.SAML) + if err != nil || loginSource == nil { + ctx.NotFound("SAMLMetadata", err) + return + } + + if err = loginSource.Cfg.(*saml.Source).Callout(ctx.Req, ctx.Resp); err != nil { + if strings.Contains(err.Error(), "no provider for ") { + ctx.Error(http.StatusNotFound) + return + } + ctx.ServerError("SignIn", err) + } +} + +func SignInSAMLCallback(ctx *context.Context) { + provider := ctx.Params(":provider") + loginSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.SAML) + if err != nil || loginSource == nil { + ctx.NotFound("SignInSAMLCallback", err) + return + } + + if loginSource == nil { + ctx.ServerError("SignIn", fmt.Errorf("no valid provider found, check configured callback url in provider")) + return + } + + u, gothUser, err := samlUserLoginCallback(*ctx, loginSource, ctx.Req, ctx.Resp) + if err != nil { + ctx.ServerError("SignInSAMLCallback", err) + return + } + + if u == nil { + if ctx.Doer != nil { + // attach user to already logged in user + err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser, auth.SAML) + if err != nil { + ctx.ServerError("LinkAccountToUser", err) + return + } + + ctx.Redirect(setting.AppSubURL + "/user/settings/security") + return + } else if !setting.Service.AllowOnlyInternalRegistration && false { + // TODO: allow auto registration from saml users (OAuth2 uses the following setting.OAuth2Client.EnableAutoRegistration) + } else { + // no existing user is found, request attach or new account + showLinkingLogin(ctx, gothUser, auth.SAML) + return + } + } + + handleSamlSignIn(ctx, loginSource, u, gothUser) +} + +func handleSamlSignIn(ctx *context.Context, source *auth.Source, u *user_model.User, gothUser goth.User) { + if err := updateSession(ctx, nil, map[string]any{ + "uid": u.ID, + "uname": u.Name, + }); err != nil { + ctx.ServerError("updateSession", err) + return + } + + // Clear whatever CSRF cookie has right now, force to generate a new one + ctx.Csrf.DeleteCookie(ctx) + + // Register last login + u.SetLastLogin() + + // update external user information + if err := externalaccount.UpdateExternalUser(ctx, u, gothUser, auth.SAML); err != nil { + if !errors.Is(err, util.ErrNotExist) { + log.Error("UpdateExternalUser failed: %v", err) + } + } + + if err := resetLocale(ctx, u); err != nil { + ctx.ServerError("resetLocale", err) + return + } + + if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 { + middleware.DeleteRedirectToCookie(ctx.Resp) + ctx.RedirectToFirst(redirectTo) + return + } + + ctx.Redirect(setting.AppSubURL + "/") +} + +func samlUserLoginCallback(ctx context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { + samlSource := authSource.Cfg.(*saml.Source) + + gothUser, err := samlSource.Callback(request, response) + if err != nil { + return nil, gothUser, err + } + + user := &user_model.User{ + LoginName: gothUser.UserID, + LoginType: auth.SAML, + LoginSource: authSource.ID, + } + + hasUser, err := user_model.GetUser(ctx, user) + if err != nil { + return nil, goth.User{}, err + } + + if hasUser { + return user, gothUser, nil + } + + // search in external linked users + externalLoginUser := &user_model.ExternalLoginUser{ + ExternalID: gothUser.UserID, + LoginSourceID: authSource.ID, + } + hasUser, err = user_model.GetExternalLogin(ctx, externalLoginUser) + if err != nil { + return nil, goth.User{}, err + } + if hasUser { + user, err = user_model.GetUserByID(request.Context(), externalLoginUser.UserID) + return user, gothUser, err + } + + // no user found to login + return nil, gothUser, nil +} + +func SAMLMetadata(ctx *context.Context) { + provider := ctx.Params(":provider") + loginSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.SAML) + if err != nil || loginSource == nil { + ctx.NotFound("SAMLMetadata", err) + return + } + if err = loginSource.Cfg.(*saml.Source).Metadata(ctx.Req, ctx.Resp); err != nil { + ctx.ServerError("SAMLMetadata", err) + } +} diff --git a/routers/web/web.go b/routers/web/web.go index 864164972e..77c8319f06 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -667,6 +667,11 @@ func registerRoutes(m *web.Route) { m.Get("/{provider}", auth.SignInOAuth) m.Get("/{provider}/callback", auth.SignInOAuthCallback) }) + m.Group("/saml", func() { + m.Get("/{provider}", auth.SignInSAML) // redir to SAML IDP + m.Post("/{provider}/acs", auth.SignInSAMLCallback) + m.Get("/{provider}/metadata", auth.SAMLMetadata) + }) }) // ***** END: User ***** diff --git a/services/auth/source/saml/assert_interface_test.go b/services/auth/source/saml/assert_interface_test.go new file mode 100644 index 0000000000..2ca7057b8a --- /dev/null +++ b/services/auth/source/saml/assert_interface_test.go @@ -0,0 +1,22 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml_test + +import ( + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/auth/source/saml" +) + +// This test file exists to assert that our Source exposes the interfaces that we expect +// It tightly binds the interfaces and implementation without breaking go import cycles + +type sourceInterface interface { + auth_model.Config + auth_model.SourceSettable + auth_model.RegisterableSource + auth.PasswordAuthenticator +} + +var _ (sourceInterface) = &saml.Source{} diff --git a/services/auth/source/saml/init.go b/services/auth/source/saml/init.go new file mode 100644 index 0000000000..f1d6d9fa4b --- /dev/null +++ b/services/auth/source/saml/init.go @@ -0,0 +1,29 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +import ( + "context" + "sync" + + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/modules/log" +) + +var samlRWMutex = sync.RWMutex{} + +func Init(ctx context.Context) error { + loginSources, _ := auth.GetActiveAuthProviderSources(ctx, auth.SAML) + for _, source := range loginSources { + samlSource, ok := source.Cfg.(*Source) + if !ok { + continue + } + err := samlSource.RegisterSource() + if err != nil { + log.Error("Unable to register source: %s due to Error: %v.", source.Name, err) + } + } + return nil +} diff --git a/services/auth/source/saml/name_id_format.go b/services/auth/source/saml/name_id_format.go new file mode 100644 index 0000000000..1ddf047729 --- /dev/null +++ b/services/auth/source/saml/name_id_format.go @@ -0,0 +1,38 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +type NameIDFormat int + +const ( + SAML11Email NameIDFormat = iota + 1 + SAML11Persistent + SAML11Unspecified + SAML20Email + SAML20Persistent + SAML20Transient + SAML20Unspecified +) + +const DefaultNameIDFormat NameIDFormat = SAML20Persistent + +var NameIDFormatNames = map[NameIDFormat]string{ + SAML11Email: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + SAML11Persistent: "urn:oasis:names:tc:SAML:1.1:nameid-format:persistent", + SAML11Unspecified: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + SAML20Email: "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress", + SAML20Persistent: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + SAML20Transient: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + SAML20Unspecified: "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", +} + +// String returns the name of the NameIDFormat +func (n NameIDFormat) String() string { + return NameIDFormatNames[n] +} + +// Int returns the int value of the NameIDFormat +func (n NameIDFormat) Int() int { + return int(n) +} diff --git a/services/auth/source/saml/providers.go b/services/auth/source/saml/providers.go new file mode 100644 index 0000000000..d0b36ff44d --- /dev/null +++ b/services/auth/source/saml/providers.go @@ -0,0 +1,109 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +import ( + "context" + "fmt" + "html" + "html/template" + "io" + "net/http" + "sort" + "time" + + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/httplib" + "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/util" +) + +// Providers is list of known/available providers. +type Providers map[string]Source + +var providers = Providers{} + +// Provider is an interface for describing a single SAML provider +type Provider interface { + Name() string + IconHTML(size int) template.HTML +} + +// AuthSourceProvider is a SAML provider +type AuthSourceProvider struct { + sourceName, iconURL string +} + +func (p *AuthSourceProvider) Name() string { + return p.sourceName +} + +func (p *AuthSourceProvider) IconHTML(size int) template.HTML { + if p.iconURL != "" { + return template.HTML(fmt.Sprintf(`%s`, + size, + size, + html.EscapeString(p.iconURL), html.EscapeString(p.Name()), + )) + } + return svg.RenderHTML("gitea-lock-cog", size, "gt-mr-3") +} + +func readIdentityProviderMetadata(ctx context.Context, source *Source) ([]byte, error) { + if source.IdentityProviderMetadata != "" { + return []byte(source.IdentityProviderMetadata), nil + } + + req := httplib.NewRequest(source.IdentityProviderMetadataURL, "GET") + req.SetTimeout(20*time.Second, time.Minute) + resp, err := req.Response() + if err != nil { + return nil, fmt.Errorf("Unable to contact gitea: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, err + } + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return data, nil +} + +func createProviderFromSource(source *auth.Source) (Provider, error) { + samlCfg, ok := source.Cfg.(*Source) + if !ok { + return nil, fmt.Errorf("invalid SAML source config: %v", samlCfg) + } + return &AuthSourceProvider{sourceName: source.Name, iconURL: samlCfg.IconURL}, nil +} + +// GetSAMLProviders returns the list of configured SAML providers +func GetSAMLProviders(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) { + authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ + IsActive: isActive, + LoginType: auth.SAML, + }) + if err != nil { + return nil, err + } + + samlProviders := make([]Provider, 0, len(authSources)) + for _, source := range authSources { + p, err := createProviderFromSource(source) + if err != nil { + return nil, err + } + samlProviders = append(samlProviders, p) + } + + sort.Slice(samlProviders, func(i, j int) bool { + return samlProviders[i].Name() < samlProviders[j].Name() + }) + + return samlProviders, nil +} diff --git a/services/auth/source/saml/source.go b/services/auth/source/saml/source.go new file mode 100644 index 0000000000..52388646b5 --- /dev/null +++ b/services/auth/source/saml/source.go @@ -0,0 +1,202 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "encoding/xml" + "errors" + "fmt" + "math/big" + "net/url" + "time" + + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + saml2 "github.com/russellhaering/gosaml2" + "github.com/russellhaering/gosaml2/types" + dsig "github.com/russellhaering/goxmldsig" +) + +// Source holds configuration for the SAML login source. +type Source struct { + // IdentityProviderMetadata description: The SAML Identity Provider metadata XML contents (for static configuration of the SAML Service Provider). The value of this field should be an XML document whose root element is `` or ``. To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. + IdentityProviderMetadata string + // IdentityProviderMetadataURL description: The SAML Identity Provider metadata URL (for dynamic configuration of the SAML Service Provider). + IdentityProviderMetadataURL string + // InsecureSkipAssertionSignatureValidation description: Whether the Service Provider should (insecurely) accept assertions from the Identity Provider without a valid signature. + InsecureSkipAssertionSignatureValidation bool + // NameIDFormat description: The SAML NameID format to use when performing user authentication. + NameIDFormat NameIDFormat + // ServiceProviderCertificate description: The SAML Service Provider certificate in X.509 encoding (begins with "-----BEGIN CERTIFICATE-----"). This certificate is used by the Identity Provider to validate the Service Provider's AuthnRequests and LogoutRequests. It corresponds to the Service Provider's private key (`serviceProviderPrivateKey`). To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. + ServiceProviderCertificate string + // ServiceProviderIssuer description: The SAML Service Provider name, used to identify this Service Provider. This is required if the "externalURL" field is not set (as the SAML metadata endpoint is computed as ".auth/saml/metadata"), or when using multiple SAML authentication providers. + ServiceProviderIssuer string + // ServiceProviderPrivateKey description: The SAML Service Provider private key in PKCS#8 encoding (begins with "-----BEGIN PRIVATE KEY-----"). This private key is used to sign AuthnRequests and LogoutRequests. It corresponds to the Service Provider's certificate (`serviceProviderCertificate`). To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. + ServiceProviderPrivateKey string + + CallbackURL string + IconURL string + + // EmailAssertionKey description: Assertion key for user.Email + EmailAssertionKey string + // NameAssertionKey description: Assertion key for user.NickName + NameAssertionKey string + // UsernameAssertionKey description: Assertion key for user.Name + UsernameAssertionKey string + + // reference to the authSource + authSource *auth.Source + + samlSP *saml2.SAMLServiceProvider +} + +func GenerateSAMLSPKeypair() (string, string, error) { + key, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return "", "", err + } + + keyBytes := x509.MarshalPKCS1PrivateKey(key) + keyPem := pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: keyBytes, + }, + ) + + now := time.Now() + + template := &x509.Certificate{ + SerialNumber: big.NewInt(0), + NotBefore: now.Add(-5 * time.Minute), + NotAfter: now.Add(365 * 24 * time.Hour), + + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{}, + BasicConstraintsValid: true, + } + + certificate, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + return "", "", err + } + + certPem := pem.EncodeToMemory( + &pem.Block{ + Type: "CERTIFICATE", + Bytes: certificate, + }, + ) + + return string(keyPem), string(certPem), nil +} + +func (source *Source) initSAMLSp() error { + source.CallbackURL = setting.AppURL + "user/saml/" + url.PathEscape(source.authSource.Name) + "/acs" + + idpMetadata, err := readIdentityProviderMetadata(context.Background(), source) + if err != nil { + return err + } + { + if source.IdentityProviderMetadataURL != "" { + log.Trace(fmt.Sprintf("Identity Provider metadata: %s", source.IdentityProviderMetadataURL), string(idpMetadata)) + } + } + + metadata := &types.EntityDescriptor{} + err = xml.Unmarshal(idpMetadata, metadata) + if err != nil { + return err + } + + certStore := dsig.MemoryX509CertificateStore{ + Roots: []*x509.Certificate{}, + } + + if metadata.IDPSSODescriptor == nil { + return errors.New("saml idp metadata missing IDPSSODescriptor") + } + + for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors { + for idx, xcert := range kd.KeyInfo.X509Data.X509Certificates { + if xcert.Data == "" { + return fmt.Errorf("metadata certificate(%d) must not be empty", idx) + } + certData, err := base64.StdEncoding.DecodeString(xcert.Data) + if err != nil { + return err + } + + idpCert, err := x509.ParseCertificate(certData) + if err != nil { + return err + } + + certStore.Roots = append(certStore.Roots, idpCert) + } + } + + var keyStore dsig.X509KeyStore + + if source.ServiceProviderCertificate != "" && source.ServiceProviderPrivateKey != "" { + keyPair, err := tls.X509KeyPair([]byte(source.ServiceProviderCertificate), []byte(source.ServiceProviderPrivateKey)) + if err != nil { + return err + } + keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) + if err != nil { + return err + } + keyStore = dsig.TLSCertKeyStore(keyPair) + } + + source.samlSP = &saml2.SAMLServiceProvider{ + IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location, + IdentityProviderIssuer: metadata.EntityID, + AudienceURI: setting.AppURL + "user/saml/" + url.PathEscape(source.authSource.Name) + "/metadata", + AssertionConsumerServiceURL: source.CallbackURL, + SkipSignatureValidation: source.InsecureSkipAssertionSignatureValidation, + NameIdFormat: source.NameIDFormat.String(), + IDPCertificateStore: &certStore, + SignAuthnRequests: source.ServiceProviderCertificate != "" && source.ServiceProviderPrivateKey != "", + SPKeyStore: keyStore, + ServiceProviderIssuer: setting.AppURL + "user/saml/" + url.PathEscape(source.authSource.Name) + "/metadata", + } + + return nil +} + +// FromDB fills up a SAML from serialized format. +func (source *Source) FromDB(bs []byte) error { + if err := json.UnmarshalHandleDoubleEncode(bs, &source); err != nil { + return err + } + + return source.initSAMLSp() +} + +// ToDB exports a SAML to a serialized format. +func (source *Source) ToDB() ([]byte, error) { + return json.Marshal(source) +} + +// SetAuthSource sets the related AuthSource +func (source *Source) SetAuthSource(authSource *auth.Source) { + source.authSource = authSource +} + +func init() { + auth.RegisterTypeConfig(auth.SAML, &Source{}) +} diff --git a/services/auth/source/saml/source_authenticate.go b/services/auth/source/saml/source_authenticate.go new file mode 100644 index 0000000000..d118917f87 --- /dev/null +++ b/services/auth/source/saml/source_authenticate.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +import ( + "context" + + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/services/auth/source/db" +) + +// Authenticate falls back to the db authenticator +func (source *Source) Authenticate(ctx context.Context, user *user_model.User, login, password string) (*user_model.User, error) { + return db.Authenticate(ctx, user, login, password) +} diff --git a/services/auth/source/saml/source_callout.go b/services/auth/source/saml/source_callout.go new file mode 100644 index 0000000000..5366f8a527 --- /dev/null +++ b/services/auth/source/saml/source_callout.go @@ -0,0 +1,89 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +import ( + "fmt" + "net/http" + "strings" + + "github.com/markbates/goth" +) + +// Callout redirects request/response pair to authenticate against the provider +func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error { + samlRWMutex.RLock() + defer samlRWMutex.RUnlock() + if _, ok := providers[source.authSource.Name]; !ok { + return fmt.Errorf("no provider for this saml") + } + + authURL, err := providers[source.authSource.Name].samlSP.BuildAuthURL("") + if err == nil { + http.Redirect(response, request, authURL, http.StatusTemporaryRedirect) + } + return err +} + +// Callback handles SAML callback, resolve to a goth user and send back to original url +// this will trigger a new authentication request, but because we save it in the session we can use that +func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) { + samlRWMutex.RLock() + defer samlRWMutex.RUnlock() + + user := goth.User{ + Provider: source.authSource.Name, + } + samlResponse := request.FormValue("SAMLResponse") + assertions, err := source.samlSP.RetrieveAssertionInfo(samlResponse) + if err != nil { + return user, err + } + + if assertions.WarningInfo.OneTimeUse { + return user, fmt.Errorf("SAML response contains one time use warning") + } + + if assertions.WarningInfo.ProxyRestriction != nil { + return user, fmt.Errorf("SAML response contains proxy restriction warning: %v", assertions.WarningInfo.ProxyRestriction) + } + + if assertions.WarningInfo.NotInAudience { + return user, fmt.Errorf("SAML response contains audience warning") + } + + if assertions.WarningInfo.InvalidTime { + return user, fmt.Errorf("SAML response contains invalid time warning") + } + + samlMap := make(map[string]string) + for key, value := range assertions.Values { + keyParsed := strings.ToLower(key[strings.LastIndex(key, "/")+1:]) // Uses the trailing slug as the key name. + valueParsed := value.Values[0].Value + samlMap[keyParsed] = valueParsed + + } + + user.UserID = assertions.NameID + if user.UserID == "" { + return user, fmt.Errorf("no nameID found in SAML response") + } + + // email + if _, ok := samlMap[source.EmailAssertionKey]; !ok { + user.Email = samlMap[source.EmailAssertionKey] + } + // name + if _, ok := samlMap[source.NameAssertionKey]; !ok { + user.NickName = samlMap[source.NameAssertionKey] + } + // username + if _, ok := samlMap[source.UsernameAssertionKey]; !ok { + user.Name = samlMap[source.UsernameAssertionKey] + } + + // TODO: utilize groups once mapping is supported + + return user, nil +} diff --git a/services/auth/source/saml/source_metadata.go b/services/auth/source/saml/source_metadata.go new file mode 100644 index 0000000000..9fb8c758e3 --- /dev/null +++ b/services/auth/source/saml/source_metadata.go @@ -0,0 +1,32 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +import ( + "encoding/xml" + "fmt" + "net/http" +) + +// Metadata redirects request/response pair to authenticate against the provider +func (source *Source) Metadata(request *http.Request, response http.ResponseWriter) error { + samlRWMutex.RLock() + defer samlRWMutex.RUnlock() + if _, ok := providers[source.authSource.Name]; !ok { + return fmt.Errorf("provider does not exist") + } + + metadata, err := providers[source.authSource.Name].samlSP.Metadata() + if err != nil { + return err + } + buf, err := xml.Marshal(metadata) + if err != nil { + return err + } + + response.Header().Set("Content-Type", "application/samlmetadata+xml; charset=utf-8") + _, _ = response.Write(buf) + return nil +} diff --git a/services/auth/source/saml/source_register.go b/services/auth/source/saml/source_register.go new file mode 100644 index 0000000000..93eaaa88b6 --- /dev/null +++ b/services/auth/source/saml/source_register.go @@ -0,0 +1,23 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package saml + +// RegisterSource causes an OAuth2 configuration to be registered +func (source *Source) RegisterSource() error { + samlRWMutex.Lock() + defer samlRWMutex.Unlock() + if err := source.initSAMLSp(); err != nil { + return err + } + providers[source.authSource.Name] = *source + return nil +} + +// UnregisterSource causes an SAML configuration to be unregistered +func (source *Source) UnregisterSource() error { + samlRWMutex.Lock() + defer samlRWMutex.Unlock() + delete(providers, source.authSource.Name) + return nil +} diff --git a/services/externalaccount/link.go b/services/externalaccount/link.go index d6e2ea7e94..1f4c6728b8 100644 --- a/services/externalaccount/link.go +++ b/services/externalaccount/link.go @@ -7,9 +7,8 @@ import ( "context" "fmt" + "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" - - "github.com/markbates/goth" ) // Store represents a thing that stores things @@ -21,10 +20,12 @@ type Store interface { // LinkAccountFromStore links the provided user with a stored external user func LinkAccountFromStore(ctx context.Context, store Store, user *user_model.User) error { - gothUser := store.Get("linkAccountGothUser") - if gothUser == nil { + externalLinkUserInterface := store.Get("linkAccountUser") + if externalLinkUserInterface == nil { return fmt.Errorf("not in LinkAccount session") } - return LinkAccountToUser(ctx, user, gothUser.(goth.User)) + externalLinkUser := externalLinkUserInterface.(auth.LinkAccountUser) + + return LinkAccountToUser(ctx, user, externalLinkUser.GothUser, externalLinkUser.Type) } diff --git a/services/externalaccount/user.go b/services/externalaccount/user.go index e2de41da18..fa85a65669 100644 --- a/services/externalaccount/user.go +++ b/services/externalaccount/user.go @@ -16,8 +16,8 @@ import ( "github.com/markbates/goth" ) -func toExternalLoginUser(ctx context.Context, user *user_model.User, gothUser goth.User) (*user_model.ExternalLoginUser, error) { - authSource, err := auth.GetActiveOAuth2SourceByName(ctx, gothUser.Provider) +func toExternalLoginUser(ctx context.Context, user *user_model.User, gothUser goth.User, authType auth.Type) (*user_model.ExternalLoginUser, error) { + authSource, err := auth.GetActiveAuthSourceByName(ctx, gothUser.Provider, authType) if err != nil { return nil, err } @@ -43,8 +43,8 @@ func toExternalLoginUser(ctx context.Context, user *user_model.User, gothUser go } // LinkAccountToUser link the gothUser to the user -func LinkAccountToUser(ctx context.Context, user *user_model.User, gothUser goth.User) error { - externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser) +func LinkAccountToUser(ctx context.Context, user *user_model.User, gothUser goth.User, authType auth.Type) error { + externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser, authType) if err != nil { return err } @@ -71,8 +71,8 @@ func LinkAccountToUser(ctx context.Context, user *user_model.User, gothUser goth } // UpdateExternalUser updates external user's information -func UpdateExternalUser(ctx context.Context, user *user_model.User, gothUser goth.User) error { - externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser) +func UpdateExternalUser(ctx context.Context, user *user_model.User, gothUser goth.User, authType auth.Type) error { + externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser, authType) if err != nil { return err } diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 25acbbb99e..85be38b403 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -1,3 +1,4 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved. // SPDX-License-Identifier: MIT @@ -15,7 +16,7 @@ import ( // AuthenticationForm form for authentication type AuthenticationForm struct { ID int64 - Type int `binding:"Range(2,7)"` + Type int `binding:"Range(2,9)"` Name string `binding:"Required;MaxSize(30)"` Host string Port int @@ -82,6 +83,18 @@ type AuthenticationForm struct { SSPIDefaultLanguage string GroupTeamMap string `binding:"ValidGroupTeamMap"` GroupTeamMapRemoval bool + + // SAML Settings + NameIDFormat int + IdentityProviderMetadata string + IdentityProviderMetadataURL string + InsecureSkipAssertionSignatureValidation bool + ServiceProviderCertificate string + ServiceProviderPrivateKey string + EmailAssertionKey string + NameAssertionKey string + UsernameAssertionKey string + SAMLIconURL string } // Validate validates fields diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 25abefae00..2182d011e9 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -367,6 +367,69 @@ {{end}} + + {{if .Source.IsSAML}} + {{$cfg:=.Source.Cfg}} +

+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+ +
+
+ + +
+
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ {{end}} + {{if .Source.IsSSPI}} {{$cfg:=.Source.Cfg}} @@ -441,6 +504,9 @@
GMail Settings:

Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true

+
SAML Settings:
+

{{ctx.Locale.Tr "admin.auths.tips.saml"}}

+
{{ctx.Locale.Tr "admin.auths.tips.oauth2.general"}}:

{{ctx.Locale.Tr "admin.auths.tips.oauth2.general.tip"}}

diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index f32f77d5dc..665b0e3086 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -53,6 +53,9 @@ {{template "admin/auth/source/sspi" .}} + + {{template "admin/auth/source/saml" .}} +
@@ -85,6 +88,9 @@
GMail Settings:

Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true

+
SAML Settings:
+

{{ctx.Locale.Tr "admin.auths.tips.saml"}}

+
{{ctx.Locale.Tr "admin.auths.tips.oauth2.general"}}:

{{ctx.Locale.Tr "admin.auths.tips.oauth2.general.tip"}}

diff --git a/templates/admin/auth/source/saml.tmpl b/templates/admin/auth/source/saml.tmpl new file mode 100644 index 0000000000..050e22ddcc --- /dev/null +++ b/templates/admin/auth/source/saml.tmpl @@ -0,0 +1,62 @@ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+ +
+
+ + +
+
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index 0d0064b02a..1b4e2b25f9 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -69,5 +69,22 @@
{{end}} + {{if .SAMLProviders}} +
+ {{.locale.Tr "sign_in_or"}} +
+
+
+
+ {{range $provider := .SAMLProviders}} + + {{end}} +
+
+
+ {{end}} diff --git a/tests/integration/README.md b/tests/integration/README.md index f6f74ca21f..c691483511 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -110,3 +110,20 @@ SLOW_FLUSH = 5S ; 5s is the default value ```bash GITEA_SLOW_TEST_TIME="10s" GITEA_SLOW_FLUSH_TIME="5s" make test-sqlite ``` + +## Running SimpleSAML for testing SAML locally + +```shell +docker run \ +-p 8080:8080 \ +-p 8443:8443 \ +-e SIMPLESAMLPHP_SP_ENTITY_ID=http://localhost:3003/user/saml/test-sp/metadata \ +-e SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE=http://localhost:3003/user/saml/test-sp/acs \ +-e SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE=http://localhost:3003/user/saml/test-sp/acs \ +--add-host=localhost:192.168.65.2 \ +-d allspice/simple-saml +``` + +```shell +TEST_SIMPLESAML_URL=localhost:8080 make test-sqlite#TestSAMLRegistration +``` diff --git a/tests/integration/saml_test.go b/tests/integration/saml_test.go new file mode 100644 index 0000000000..585fd35c5f --- /dev/null +++ b/tests/integration/saml_test.go @@ -0,0 +1,150 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/cookiejar" + "net/url" + "os" + "regexp" + "strings" + "testing" + "time" + + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/services/auth/source/saml" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestSAMLRegistration(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + samlURL := "localhost:8080" + + if os.Getenv("CI") == "" || !setting.Database.Type.IsPostgreSQL() { + // Make it possible to run tests against a local simplesaml instance + samlURL = os.Getenv("TEST_SIMPLESAML_URL") + if samlURL == "" { + t.Skip("TEST_SIMPLESAML_URL not set and not running in CI") + return + } + } + + privateKey, cert, err := saml.GenerateSAMLSPKeypair() + assert.NoError(t, err) + + // verify that the keypair can be parsed + keyPair, err := tls.X509KeyPair([]byte(cert), []byte(privateKey)) + assert.NoError(t, err) + keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) + assert.NoError(t, err) + + assert.NoError(t, auth.CreateSource(db.DefaultContext, &auth.Source{ + Type: auth.SAML, + Name: "test-sp", + IsActive: true, + IsSyncEnabled: false, + Cfg: &saml.Source{ + IdentityProviderMetadata: "", + IdentityProviderMetadataURL: fmt.Sprintf("http://%s/simplesaml/saml2/idp/metadata.php", samlURL), + InsecureSkipAssertionSignatureValidation: false, + NameIDFormat: 4, + ServiceProviderCertificate: "", // SimpleSAMLPhp requires that the SP certificate be specified in the server configuration rather than SP metadata + ServiceProviderPrivateKey: "", + EmailAssertionKey: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + NameAssertionKey: "http://schemas.xmlsoap.org/claims/CommonName", + UsernameAssertionKey: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + IconURL: "", + }, + })) + + // check the saml metadata url + req := NewRequest(t, "GET", "/user/saml/test-sp/metadata") + MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", "/user/saml/test-sp") + resp := MakeRequest(t, req, http.StatusTemporaryRedirect) + + jar, err := cookiejar.New(nil) + assert.NoError(t, err) + + client := http.Client{ + Timeout: 30 * time.Second, + Jar: jar, + } + + httpReq, err := http.NewRequest("GET", test.RedirectURL(resp), nil) + assert.NoError(t, err) + + var formRedirectURL *url.URL + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + // capture the redirected destination to use in POST request + formRedirectURL = req.URL + return nil + } + + res, err := client.Do(httpReq) + client.CheckRedirect = nil + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.NotNil(t, formRedirectURL) + + form := url.Values{ + "username": {"user1"}, + "password": {"user1pass"}, + } + + httpReq, err = http.NewRequest("POST", formRedirectURL.String(), strings.NewReader(form.Encode())) + assert.NoError(t, err) + httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + res, err = client.Do(httpReq) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, res.StatusCode) + + body, err := io.ReadAll(res.Body) + assert.NoError(t, err) + + samlResMatcher := regexp.MustCompile(``) + matches := samlResMatcher.FindStringSubmatch(string(body)) + assert.Len(t, matches, 2) + assert.NoError(t, res.Body.Close()) + + session := emptyTestSession(t) + + req = NewRequestWithValues(t, "POST", "/user/saml/test-sp/acs", map[string]string{ + "SAMLResponse": matches[1], + }) + resp = session.MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, test.RedirectURL(resp), "/user/link_account") + + csrf := GetCSRF(t, session, test.RedirectURL(resp)) + + // link the account + req = NewRequestWithValues(t, "POST", "/user/link_account_signup", map[string]string{ + "_csrf": csrf, + "user_name": "samluser", + "email": "saml@example.com", + }) + + resp = session.MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, test.RedirectURL(resp), "/") + + // verify that the user was created + u, err := user_model.GetUserByEmail(db.DefaultContext, "saml@example.com") + assert.NoError(t, err) + assert.NotNil(t, u) + assert.Equal(t, "samluser", u.Name) +} diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index 044976ea7b..4804163971 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -103,9 +103,9 @@ export function initAdminCommon() { // New authentication if ($('.admin.new.authentication').length > 0) { $('#auth_type').on('change', function () { - hideElem($('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi')); + hideElem($('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi, .saml')); - $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]').removeAttr('required'); + $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required], .saml input[required]').removeAttr('required'); $('.binddnrequired').removeClass('required'); const authType = $(this).val(); @@ -137,6 +137,10 @@ export function initAdminCommon() { showElem($('.sspi')); $('.sspi div.required input').attr('required', 'required'); break; + case '8': // SAML + showElem($('.saml')); + $('.saml div.required input').attr('required', 'required'); + break; } if (authType === '2' || authType === '5') { onSecurityProtocolChange(); diff --git a/web_src/js/features/user-auth.js b/web_src/js/features/user-auth.js index 60d186e699..3bf84e31df 100644 --- a/web_src/js/features/user-auth.js +++ b/web_src/js/features/user-auth.js @@ -20,3 +20,24 @@ export function initUserAuthOauth2() { }); } } + +export function initUserAuthSAML() { + const outer = document.getElementById('saml-login-navigator'); + if (!outer) return; + const inner = document.getElementById('saml-login-navigator-inner'); + + checkAppUrl(); + + for (const link of outer.querySelectorAll('.saml-login-link')) { + link.addEventListener('click', () => { + inner.classList.add('gt-invisible'); + outer.classList.add('is-loading'); + setTimeout(() => { + // recover previous content to let user try again + // usually redirection will be performed before this action + outer.classList.remove('is-loading'); + inner.classList.remove('gt-invisible'); + }, 5000); + }); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index 117279c3c4..ddd435f05e 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -23,7 +23,10 @@ import {initFindFileInRepo} from './features/repo-findfile.js'; import {initCommentContent, initMarkupContent} from './markup/content.js'; import {initPdfViewer} from './render/pdf.js'; -import {initUserAuthOauth2} from './features/user-auth.js'; +import { + initUserAuthOauth2, + initUserAuthSAML +} from './features/user-auth.js'; import { initRepoIssueDue, initRepoIssueReferenceRepositorySearch, @@ -179,6 +182,7 @@ onDomReady(() => { initCaptcha(); initUserAuthOauth2(); + initUserAuthSAML(); initUserAuthWebAuthn(); initUserAuthWebAuthnRegister(); initUserSettings(); From e0445042410da1fb3e5b44edebd0f5d2b7ae06b2 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 23 Feb 2024 01:24:07 +0100 Subject: [PATCH 05/79] Frontport changelogs of minor releases (#29337) as title --- CHANGELOG.md | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae87638f1c..9f2c69888a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,242 @@ 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.com). +## [1.21.6](https://github.com/go-gitea/gitea/releases/tag/v1.21.6) - 2024-02-22 + +* SECURITY + * Fix XSS vulnerabilities (#29336) + * Use general token signing secret (#29205) (#29325) +* API + * Refactor issue template parsing and fix API endpoint (#29069) (#29140) + * Fix swift packages not resolving (#29095) (#29102) +* ENHANCEMENTS + * Refactor git version functions and check compatibility (#29155) (#29157) + * Improve user experience for outdated comments (#29050) (#29086) + * Hide code links on release page if user cannot read code (#29064) (#29066) + * Wrap contained tags and branches again (#29021) (#29026) + * Fix incorrect button CSS usages (#29015) (#29023) + * Strip trailing newline in markdown code copy (#29019) (#29022) +* BUGFIXES + * Remove SSH workaround (#27893) (#29332) + * Only log error when tag sync fails (#29295) (#29327) + * Fix SSPI user creation (#28948) (#29323) + * Improve the `issue_comment` workflow trigger event (#29277) (#29322) + * Discard unread data of `git cat-file` (#29297) (#29310) + * Fix error display when merging PRs (#29288) (#29309) + * Prevent double use of `git cat-file` session. (#29298) (#29301) + * Fix missing link on outgoing new release notifications (#29079) (#29300) + * Fix debian InRelease Acquire-By-Hash newline (#29204) (#29299) + * Always write proc-receive hook for all git versions (#29287) (#29291) + * Do not show delete button when time tracker is disabled (#29257) (#29279) + * Workaround to clean up old reviews on creating a new one (#28554) (#29264) + * Fix bug when the linked account was disactived and list the linked accounts (#29263) + * Do not use lower tag names to find releases/tags (#29261) (#29262) + * Fix missed edit issues event for actions (#29237) (#29251) + * Only delete scheduled workflows when needed (#29091) (#29235) + * Make submit event code work with both jQuery event and native event (#29223) (#29234) + * Fix push to create with capitalize repo name (#29090) (#29206) + * Use ghost user if user was not found (#29161) (#29169) + * Dont load Review if Comment is CommentTypeReviewRequest (#28551) (#29160) + * Refactor parseSignatureFromCommitLine (#29054) (#29108) + * Avoid showing unnecessary JS errors when there are elements with different origin on the page (#29081) (#29089) + * Fix gitea-origin-url with default ports (#29085) (#29088) + * Fix orgmode link resolving (#29024) (#29076) + * Fix: Elasticsearch: Request Entity Too Large #28117 (#29062) (#29075) + * Do not render empty comments (#29039) (#29049) + * Avoid sending update/delete release notice when it is draft (#29008) (#29025) +* DOCS + * Rm outdated docs from some languages (#27530) (#29208) +* MISC + * Implement some action notifier functions (#29173) (#29308) + * Fix gitea-action user avatar broken on edited menu (#29190) (#29307) + * Disallow merge when required checked are missing (#29143) (#29268) + * Convert visibility to number (#29226) (#29244) + * Load outdated comments when (un)resolving conversation on PR timeline (#29203) (#29221) + * Fix incorrect link to swift doc and swift package-registry login command (#29096) (#29103) + * Fix typos in the documentation (#29048) (#29056) + * Explained where create issue/PR template (#29035) + +## [1.21.5](https://github.com/go-gitea/gitea/releases/tag/v1.21.5) - 2024-01-31 + +* SECURITY + * Prevent anonymous container access if `RequireSignInView` is enabled (#28877) (#28882) + * Update go dependencies and fix go-git (#28893) (#28934) +* BUGFIXES + * Revert "Speed up loading the dashboard on mysql/mariadb (#28546)" (#29006) (#29007) + * Fix an actions schedule bug (#28942) (#28999) + * Fix update enable_prune even if mirror_interval is not provided (#28905) (#28929) + * Fix uploaded artifacts should be overwritten (#28726) backport v1.21 (#28832) + * Preserve BOM in web editor (#28935) (#28959) + * Strip `/` from relative links (#28932) (#28952) + * Don't remove all mirror repository's releases when mirroring (#28817) (#28939) + * Implement `MigrateRepository` for the actions notifier (#28920) (#28923) + * Respect branch info for relative links (#28909) (#28922) + * Don't reload timeline page when (un)resolving or replying conversation (#28654) (#28917) + * Only migrate the first 255 chars of a Github issue title (#28902) (#28912) + * Fix sort bug on repository issues list (#28897) (#28901) + * Fix `DeleteCollaboration` transaction behaviour (#28886) (#28889) + * Fix schedule not trigger bug because matching full ref name with short ref name (#28874) (#28888) + * Fix migrate storage bug (#28830) (#28867) + * Fix archive creating LFS hooks and breaking pull requests (#28848) (#28851) + * Fix reverting a merge commit failing (#28794) (#28825) + * Upgrade xorm to v1.3.7 to fix a resource leak problem caused by Iterate (#28891) (#28895) + * Fix incorrect PostgreSQL connection string for Unix sockets (#28865) (#28870) +* ENHANCEMENTS + * Make loading animation less aggressive (#28955) (#28956) + * Avoid duplicate JS error messages on UI (#28873) (#28881) + * Bump `@github/relative-time-element` to 4.3.1 (#28819) (#28826) +* MISC + * Warn that `DISABLE_QUERY_AUTH_TOKEN` is false only if it's explicitly defined (#28783) (#28868) + * Remove duplicated checkinit on git module (#28824) (#28831) + +## [1.21.4](https://github.com/go-gitea/gitea/releases/tag/v1.21.4) - 2024-01-16 + +* SECURITY + * Update github.com/cloudflare/circl (#28789) (#28790) + * Require token for GET subscription endpoint (#28765) (#28768) +* BUGFIXES + * Use refname:strip-2 instead of refname:short when syncing tags (#28797) (#28811) + * Fix links in issue card (#28806) (#28807) + * Fix nil pointer panic when exec some gitea cli command (#28791) (#28795) + * Require token for GET subscription endpoint (#28765) (#28778) + * Fix button size in "attached header right" (#28770) (#28774) + * Fix `convert.ToTeams` on empty input (#28426) (#28767) + * Hide code related setting options in repository when code unit is disabled (#28631) (#28749) + * Fix incorrect URL for "Reference in New Issue" (#28716) (#28723) + * Fix panic when parsing empty pgsql host (#28708) (#28709) + * Upgrade xorm to new version which supported update join for all supported databases (#28590) (#28668) + * Fix alpine package files are not rebuilt (#28638) (#28665) + * Avoid cycle-redirecting user/login page (#28636) (#28658) + * Fix empty ref for cron workflow runs (#28640) (#28647) + * Remove unnecessary syncbranchToDB with tests (#28624) (#28629) + * Use known issue IID to generate new PR index number when migrating from GitLab (#28616) (#28618) + * Fix flex container width (#28603) (#28605) + * Fix the scroll behavior for emoji/mention list (#28597) (#28601) + * Fix wrong due date rendering in issue list page (#28588) (#28591) + * Fix `status_check_contexts` matching bug (#28582) (#28589) + * Fix 500 error of searching commits (#28576) (#28579) + * Use information from previous blame parts (#28572) (#28577) + * Update mermaid for 1.21 (#28571) + * Fix 405 method not allowed CORS / OIDC (#28583) (#28586) (#28587) (#28611) + * Fix `GetCommitStatuses` (#28787) (#28804) + * Forbid removing the last admin user (#28337) (#28793) + * Fix schedule tasks bugs (#28691) (#28780) + * Fix issue dependencies (#27736) (#28776) + * Fix system webhooks API bug (#28531) (#28666) + * Fix when private user following user, private user will not be counted in his own view (#28037) (#28792) + * Render code block in activity tab (#28816) (#28818) +* ENHANCEMENTS + * Rework markup link rendering (#26745) (#28803) + * Modernize merge button (#28140) (#28786) + * Speed up loading the dashboard on mysql/mariadb (#28546) (#28784) + * Assign pull request to project during creation (#28227) (#28775) + * Show description as tooltip instead of title for labels (#28754) (#28766) + * Make template `DateTime` show proper tooltip (#28677) (#28683) + * Switch destination directory for apt signing keys (#28639) (#28642) + * Include heap pprof in diagnosis report to help debugging memory leaks (#28596) (#28599) +* DOCS + * Suggest to use Type=simple for systemd service (#28717) (#28722) + * Extend description for ARTIFACT_RETENTION_DAYS (#28626) (#28630) +* MISC + * Add -F to commit search to treat keywords as strings (#28744) (#28748) + * Add download attribute to release attachments (#28739) (#28740) + * Concatenate error in `checkIfPRContentChanged` (#28731) (#28737) + * Improve 1.21 document for Database Preparation (#28643) (#28644) + +## [1.21.3](https://github.com/go-gitea/gitea/releases/tag/v1.21.3) - 2023-12-21 + +* SECURITY + * Update golang.org/x/crypto (#28519) +* API + * chore(api): support ignore password if login source type is LDAP for creating user API (#28491) (#28525) + * Add endpoint for not implemented Docker auth (#28457) (#28462) +* ENHANCEMENTS + * Add option to disable ambiguous unicode characters detection (#28454) (#28499) + * Refactor SSH clone URL generation code (#28421) (#28480) + * Polyfill SubmitEvent for PaleMoon (#28441) (#28478) +* BUGFIXES + * Fix the issue ref rendering for wiki (#28556) (#28559) + * Fix duplicate ID when deleting repo (#28520) (#28528) + * Only check online runner when detecting matching runners in workflows (#28286) (#28512) + * Initalize stroage for orphaned repository doctor (#28487) (#28490) + * Fix possible nil pointer access (#28428) (#28440) + * Don't show unnecessary citation JS error on UI (#28433) (#28437) +* DOCS + * Update actions document about comparsion as Github Actions (#28560) (#28564) + * Fix documents for "custom/public/assets/" (#28465) (#28467) +* MISC + * Fix inperformant query on retrifing review from database. (#28552) (#28562) + * Improve the prompt for "ssh-keygen sign" (#28509) (#28510) + * Update docs for DISABLE_QUERY_AUTH_TOKEN (#28485) (#28488) + * Fix Chinese translation of config cheat sheet[API] (#28472) (#28473) + * Retry SSH key verification with additional CRLF if it failed (#28392) (#28464) + +## [1.21.2](https://github.com/go-gitea/gitea/releases/tag/1.21.2) - 2023-12-12 + +* SECURITY + * Rebuild with recently released golang version + * Fix missing check (#28406) (#28411) + * Do some missing checks (#28423) (#28432) +* BUGFIXES + * Fix margin in server signed signature verification view (#28379) (#28381) + * Fix object does not exist error when checking citation file (#28314) (#28369) + * Use `filepath` instead of `path` to create SQLite3 database file (#28374) (#28378) + * Fix the runs will not be displayed bug when the main branch have no workflows but other branches have (#28359) (#28365) + * Handle repository.size column being NULL in migration v263 (#28336) (#28363) + * Convert git commit summary to valid UTF8. (#28356) (#28358) + * Fix migration panic due to an empty review comment diff (#28334) (#28362) + * Add `HEAD` support for rpm repo files (#28309) (#28360) + * Fix RPM/Debian signature key creation (#28352) (#28353) + * Keep profile tab when clicking on Language (#28320) (#28331) + * Fix missing issue search index update when changing status (#28325) (#28330) + * Fix wrong link in `protect_branch_name_pattern_desc` (#28313) (#28315) + * Read `previous` info from git blame (#28306) (#28310) + * Ignore "non-existing" errors when getDirectorySize calculates the size (#28276) (#28285) + * Use appSubUrl for OAuth2 callback URL tip (#28266) (#28275) + * Meilisearch: require all query terms to be matched (#28293) (#28296) + * Fix required error for token name (#28267) (#28284) + * Fix issue will be detected as pull request when checking `First-time contributor` (#28237) (#28271) + * Use full width for project boards (#28225) (#28245) + * Increase "version" when update the setting value to a same value as before (#28243) (#28244) + * Also sync DB branches on push if necessary (#28361) (#28403) + * Make gogit Repository.GetBranchNames consistent (#28348) (#28386) + * Recover from panic in cron task (#28409) (#28425) + * Deprecate query string auth tokens (#28390) (#28430) +* ENHANCEMENTS + * Improve doctor cli behavior (#28422) (#28424) + * Fix margin in server signed signature verification view (#28379) (#28381) + * Refactor template empty checks (#28351) (#28354) + * Read `previous` info from git blame (#28306) (#28310) + * Use full width for project boards (#28225) (#28245) + * Enable system users search via the API (#28013) (#28018) + +## [1.21.1](https://github.com/go-gitea/gitea/releases/tag/1.21.1) - 2023-11-26 + +* SECURITY + * Fix comment permissions (#28213) (#28216) +* BUGFIXES + * Fix delete-orphaned-repos (#28200) (#28202) + * Make CORS work for oauth2 handlers (#28184) (#28185) + * Fix missing buttons (#28179) (#28181) + * Fix no ActionTaskOutput table waring (#28149) (#28152) + * Fix empty action run title (#28113) (#28148) + * Use "is-loading" to avoid duplicate form submit for code comment (#28143) (#28147) + * Fix Matrix and MSTeams nil dereference (#28089) (#28105) + * Fix incorrect pgsql conn builder behavior (#28085) (#28098) + * Fix system config cache expiration timing (#28072) (#28090) + * Restricted users only see repos in orgs which their team was assigned to (#28025) (#28051) +* API + * Fix permissions for Token DELETE endpoint to match GET and POST (#27610) (#28099) +* ENHANCEMENTS + * Do not display search box when there's no packages yet (#28146) (#28159) + * Add missing `packages.cleanup.success` (#28129) (#28132) +* DOCS + * Docs: Replace deprecated IS_TLS_ENABLED mailer setting in email setup (#28205) (#28208) + * Fix the description about the default setting for action in quick start document (#28160) (#28168) + * Add guide page to actions when there's no workflows (#28145) (#28153) +* MISC + * Use full width for PR comparison (#28182) (#28186) + ## [1.21.0](https://github.com/go-gitea/gitea/releases/tag/v1.21.0) - 2023-11-14 * BREAKING From 532e422027c88a4a3dc0c2968857f8d5f94d861f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Fri, 23 Feb 2024 01:24:57 +0100 Subject: [PATCH 06/79] Unify organizations header (#29248) Unify organizations header before: ![image](https://github.com/go-gitea/gitea/assets/72873130/74474e0d-33c3-4bbf-9324-d130ea2c62f8) after: ![image](https://github.com/go-gitea/gitea/assets/72873130/1c65de0d-fa0f-4b17-ab8d-067de8c7113b) --------- Co-authored-by: silverwind --- modules/context/org.go | 15 +++++ routers/web/org/home.go | 18 ------ routers/web/shared/user/header.go | 15 ----- routers/web/user/profile.go | 1 + templates/org/header.tmpl | 42 +++++++++----- templates/org/home.tmpl | 33 +---------- templates/org/member/members.tmpl | 2 +- templates/org/menu.tmpl | 10 ++-- templates/org/projects/list.tmpl | 7 +-- templates/package/settings.tmpl | 12 +++- templates/user/code.tmpl | 5 +- templates/user/overview/header.tmpl | 57 ++++++------------- templates/user/overview/package_versions.tmpl | 7 +-- templates/user/overview/packages.tmpl | 7 +-- web_src/css/org.css | 18 +++--- 15 files changed, 95 insertions(+), 154 deletions(-) diff --git a/modules/context/org.go b/modules/context/org.go index d068646577..018b76de43 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -11,6 +11,8 @@ import ( "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" ) @@ -255,6 +257,19 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects) ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages) ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode) + + ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) + if len(ctx.ContextUser.Description) != 0 { + content, err := markdown.RenderString(&markup.RenderContext{ + Metas: map[string]string{"mode": "document"}, + Ctx: ctx, + }, ctx.ContextUser.Description) + if err != nil { + ctx.ServerError("RenderString", err) + return + } + ctx.Data["RenderedDescription"] = content + } } // OrgAssignment returns a middleware to handle organization assignment diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 8bf02b2c42..36f543dc45 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" @@ -46,17 +45,6 @@ func Home(ctx *context.Context) { ctx.Data["PageIsUserProfile"] = true ctx.Data["Title"] = org.DisplayName() - if len(org.Description) != 0 { - desc, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - Metas: map[string]string{"mode": "document"}, - }, org.Description) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - ctx.Data["RenderedDescription"] = desc - } var orderBy db.SearchOrderBy ctx.Data["SortType"] = ctx.FormString("sort") @@ -131,18 +119,12 @@ func Home(ctx *context.Context) { return } - var isFollowing bool - if ctx.Doer != nil { - isFollowing = user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) - } - ctx.Data["Repos"] = repos ctx.Data["Total"] = count ctx.Data["Members"] = members ctx.Data["Teams"] = ctx.Org.Teams ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull ctx.Data["PageIsViewRepositories"] = true - ctx.Data["IsFollowing"] = isFollowing err = shared_user.LoadHeaderCount(ctx) if err != nil { diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index a6c66a2c70..99b701b439 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -17,8 +17,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" ) @@ -36,7 +34,6 @@ func prepareContextForCommonProfile(ctx *context.Context) { func PrepareContextForProfileBigAvatar(ctx *context.Context) { prepareContextForCommonProfile(ctx) - ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) @@ -48,18 +45,6 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { } ctx.Data["OpenIDs"] = openIDs - if len(ctx.ContextUser.Description) != 0 { - content, err := markdown.RenderString(&markup.RenderContext{ - Metas: map[string]string{"mode": "document"}, - Ctx: ctx, - }, ctx.ContextUser.Description) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - ctx.Data["RenderedDescription"] = content - } - showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ UserID: ctx.ContextUser.ID, diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 37ce450530..4d0ad06cba 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -324,6 +324,7 @@ func Action(ctx *context.Context) { ctx.HTML(http.StatusOK, tplProfileBigAvatar) return } else if ctx.ContextUser.IsOrganization() { + ctx.Data["Org"] = ctx.ContextUser ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.HTML(http.StatusOK, tplFollowUnfollow) return diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index 7b912c1c56..8423fd7d3b 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -1,18 +1,32 @@ -{{with .Org}} -
-
-
-
- {{ctx.AvatarUtils.Avatar . 100}} - {{.DisplayName}} - - {{if .Visibility.IsLimited}}
{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}
{{end}} - {{if .Visibility.IsPrivate}}
{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}
{{end}} -
-
-
+
+ {{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}} +
+
+ {{.Org.DisplayName}} + + {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} + {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} + + + {{if .EnableFeed}} + + {{svg "octicon-rss" 24}} + + {{end}} + {{if .IsSigned}} + {{template "org/follow_unfollow" .}} + {{end}} + +
+ {{if .RenderedDescription}}
{{.RenderedDescription | Str2html}}
{{end}} +
+ {{if .Org.Location}}
{{svg "octicon-location"}} {{.Org.Location}}
{{end}} + {{if .Org.Website}}
{{svg "octicon-link"}} {{.Org.Website}}
{{end}} + {{if .IsSigned}} + {{if .Org.Email}}
{{svg "octicon-mail"}} {{.Org.Email}}
{{end}} + {{end}}
-{{end}} +
{{template "org/menu" .}} diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 81a76d3b4d..4e33b1af72 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -1,37 +1,6 @@ {{template "base/head" .}}
-
- {{ctx.AvatarUtils.Avatar .Org 140 "org-avatar"}} -
-
- {{.Org.DisplayName}} - - {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} - {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} - -
- {{if $.RenderedDescription}}
{{$.RenderedDescription|Str2html}}
{{end}} -
- {{if .Org.Location}}
{{svg "octicon-location"}} {{.Org.Location}}
{{end}} - {{if .Org.Website}}
{{svg "octicon-link"}} {{.Org.Website}}
{{end}} - {{if $.IsSigned}} - {{if .Org.Email}}
{{svg "octicon-mail"}} {{.Org.Email}}
{{end}} - {{end}} -
-
- -
- - {{template "org/menu" .}} + {{template "org/header" .}}
diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl index 03509ec93e..64f1aaa7d2 100644 --- a/templates/org/member/members.tmpl +++ b/templates/org/member/members.tmpl @@ -1,5 +1,5 @@ {{template "base/head" .}} -
+
{{template "org/header" .}}
{{template "base/alert" .}} diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl index 8a97711ce2..f07b26865a 100644 --- a/templates/org/menu.tmpl +++ b/templates/org/menu.tmpl @@ -15,24 +15,24 @@ {{end}} {{if and .IsPackageEnabled .CanReadPackages}} - + {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} {{end}} {{if and .IsRepoIndexerEnabled .CanReadCode}} - - {{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}} + + {{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}} {{end}} {{if .NumMembers}} - {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}} + {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}}
{{.NumMembers}}
{{end}} {{if .IsOrganizationMember}} - {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} + {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} {{if .NumTeams}}
{{.NumTeams}}
{{end}} diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl index 689091e5e0..97cc6cf66c 100644 --- a/templates/org/projects/list.tmpl +++ b/templates/org/projects/list.tmpl @@ -1,10 +1,9 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
- {{template "shared/user/org_profile_avatar" .}} +
+ {{template "org/header" .}}
- {{template "user/overview/header" .}} - {{template "projects/list" .}} + {{template "projects/list" .}}
{{else}} diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index 6ef62753e2..10e26c7010 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -1,8 +1,14 @@ {{template "base/head" .}} -
- {{template "shared/user/org_profile_avatar" .}} +
+ {{if .ContextUser.IsOrganization}} + {{template "org/header" .}} + {{else}} + {{template "shared/user/org_profile_avatar" .}} + {{end}}
- {{template "user/overview/header" .}} + {{if not .ContextUser.IsOrganization}} + {{template "user/overview/header" .}} + {{end}} {{template "base/alert" .}}

{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}}) / {{ctx.Locale.Tr "repo.settings"}}

diff --git a/templates/user/code.tmpl b/templates/user/code.tmpl index da9a3c3a24..f71f55c474 100644 --- a/templates/user/code.tmpl +++ b/templates/user/code.tmpl @@ -1,9 +1,8 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
- {{template "shared/user/org_profile_avatar" .}} +
+ {{template "org/header" .}}
- {{template "user/overview/header" .}} {{template "code/searchcombo" .}}
diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl index c0cbe2561c..4fdaa70d87 100644 --- a/templates/user/overview/header.tmpl +++ b/templates/user/overview/header.tmpl @@ -10,7 +10,7 @@
{{.RepoCount}}
{{end}} - {{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}} + {{if or .ContextUser.IsIndividual .CanReadProjects}} {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} {{if .ProjectCount}} @@ -18,55 +18,30 @@ {{end}} {{end}} - {{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}} + {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}} {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} {{end}} - {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}} + {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}} {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}} {{end}} - {{if .ContextUser.IsOrganization}} - {{if .NumMembers}} - - {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}} -
{{.NumMembers}}
-
- {{end}} - {{if .IsOrganizationMember}} - - {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} - {{if .NumTeams}} -
{{.NumTeams}}
- {{end}} -
- {{end}} - - {{if .IsOrganizationOwner}} - - {{end}} - {{else}} - - {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} + + {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} + + {{if not .DisableStars}} + + {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}} + {{if .ContextUser.NumStars}} +
{{.ContextUser.NumStars}}
+ {{end}} +
+ {{else}} + + {{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}} - {{if not .DisableStars}} - - {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}} - {{if .ContextUser.NumStars}} -
{{.ContextUser.NumStars}}
- {{end}} -
- {{else}} - - {{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}} - - {{end}} {{end}}
diff --git a/templates/user/overview/package_versions.tmpl b/templates/user/overview/package_versions.tmpl index 6f740e0e7c..f6f963aecb 100644 --- a/templates/user/overview/package_versions.tmpl +++ b/templates/user/overview/package_versions.tmpl @@ -1,10 +1,9 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
- {{template "shared/user/org_profile_avatar" .}} +
+ {{template "org/header" .}}
- {{template "user/overview/header" .}} - {{template "package/shared/versionlist" .}} + {{template "package/shared/versionlist" .}}
{{else}} diff --git a/templates/user/overview/packages.tmpl b/templates/user/overview/packages.tmpl index 4fd17696d1..30ff871cb2 100644 --- a/templates/user/overview/packages.tmpl +++ b/templates/user/overview/packages.tmpl @@ -1,10 +1,9 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
- {{template "shared/user/org_profile_avatar" .}} +
+ {{template "org/header" .}}
- {{template "user/overview/header" .}} - {{template "package/shared/list" .}} + {{template "package/shared/list" .}}
{{else}} diff --git a/web_src/css/org.css b/web_src/css/org.css index d2bf0ff606..8b3684d0c0 100644 --- a/web_src/css/org.css +++ b/web_src/css/org.css @@ -93,46 +93,44 @@ min-width: 300px; } -.organization.profile .org-avatar { - width: 100px; - height: 100px; +.page-content.organization .org-avatar { margin-right: 15px; } -.organization.profile #org-info { +.page-content.organization #org-info { overflow-wrap: anywhere; flex: 1; word-break: break-all; } -.organization.profile #org-info .ui.header { +.page-content.organization #org-info .ui.header { display: flex; align-items: center; font-size: 36px; margin-bottom: 0; } -.organization.profile #org-info .desc { +.page-content.organization #org-info .desc { font-size: 16px; margin-bottom: 10px; } -.organization.profile #org-info .meta { +.page-content.organization #org-info .meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } -.organization.profile .ui.top.header .ui.right { +.page-content.organization .ui.top.header .ui.right { margin-top: 0; } -.organization.profile .teams .item { +.page-content.organization .teams .item { padding: 10px 15px; } -.organization.profile .members .ui.avatar { +.page-content.organization .members .ui.avatar { width: 48px; height: 48px; margin-right: 5px; From b748d62b461f9f23823f8772bc708b44b15a23a7 Mon Sep 17 00:00:00 2001 From: Earl Warren <109468362+earl-warren@users.noreply.github.com> Date: Fri, 23 Feb 2024 01:57:24 +0100 Subject: [PATCH 07/79] Add slow SQL query warning (#27545) - Databases are one of the most important parts of Forgejo, every interaction uses the database in one way or another. Therefore, it is important to maintain the database and recognize when the server is not doing well with the database. There already is the option to log *every* SQL query along with its execution time, but monitoring becomes impractical for larger instances and takes up unnecessary storage in the logs. - Add a QoL enhancement that allows instance administrators to specify a threshold value beyond which query execution time is logged as a warning in the xorm logger. The default value is a conservative five seconds to avoid this becoming a source of spam in the logs. - The use case for this patch is that with an instance the size of Codeberg, monitoring SQL logs is not very fruitful and most of them are uninteresting. Recently, in the context of persistent deadlock issues (https://codeberg.org/forgejo/forgejo/issues/220), I have noticed that certain queries hold locks on tables like comment and issue for several seconds. This patch helps to identify which queries these are and when they happen. - Added unit test. (cherry picked from commit 9cf501f1af4cd870221cef6af489618785b71186) --------- Co-authored-by: Gusted Co-authored-by: Giteabot Co-authored-by: 6543 <6543@obermui.de> --- custom/conf/app.example.ini | 4 ++ .../config-cheat-sheet.en-us.md | 1 + models/db/engine.go | 31 ++++++++++++++ modules/setting/database.go | 42 ++++++++++--------- 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 4aae1c497f..a360970593 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -412,6 +412,10 @@ USER = root ;; ;; Whether execute database models migrations automatically ;AUTO_MIGRATION = true +;; +;; Threshold value (in seconds) beyond which query execution time is logged as a warning in the xorm logger +;; +;SLOW_QUERY_THRESHOLD = 5s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 415176d4ff..838e26b0f6 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -458,6 +458,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `MAX_IDLE_CONNS` **2**: Max idle database connections on connection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`. - `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071). - `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically. +- `SLOW_QUERY_THRESHOLD` **5s**: Threshold value in seconds beyond which query execution time is logged as a warning in the xorm logger. [^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details. diff --git a/models/db/engine.go b/models/db/engine.go index 2cd1c36c58..2a2743e927 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -11,10 +11,13 @@ import ( "io" "reflect" "strings" + "time" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "xorm.io/xorm" + "xorm.io/xorm/contexts" "xorm.io/xorm/names" "xorm.io/xorm/schemas" @@ -143,6 +146,13 @@ func InitEngine(ctx context.Context) error { xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) xormEngine.SetDefaultContext(ctx) + if setting.Database.SlowQueryThreshold > 0 { + xormEngine.AddHook(&SlowQueryHook{ + Threshold: setting.Database.SlowQueryThreshold, + Logger: log.GetLogger("xorm"), + }) + } + SetDefaultEngine(ctx, xormEngine) return nil } @@ -298,3 +308,24 @@ func SetLogSQL(ctx context.Context, on bool) { sess.Engine().ShowSQL(on) } } + +type SlowQueryHook struct { + Threshold time.Duration + Logger log.Logger +} + +var _ contexts.Hook = &SlowQueryHook{} + +func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { + return c.Ctx, nil +} + +func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error { + if c.ExecuteTime >= h.Threshold { + // 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function + // is being displayed (the function that ultimately wants to execute the query in the code) + // instead of the function of the slow query hook being called. + h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) + } + return nil +} diff --git a/modules/setting/database.go b/modules/setting/database.go index e200b15b2e..1a4bf64805 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -25,26 +25,27 @@ var ( // Database holds the database settings Database = struct { - Type DatabaseType - Host string - Name string - User string - Passwd string - Schema string - SSLMode string - Path string - LogSQL bool - MysqlCharset string - CharsetCollation string - Timeout int // seconds - SQLiteJournalMode string - DBConnectRetries int - DBConnectBackoff time.Duration - MaxIdleConns int - MaxOpenConns int - ConnMaxLifetime time.Duration - IterateBufferSize int - AutoMigration bool + Type DatabaseType + Host string + Name string + User string + Passwd string + Schema string + SSLMode string + Path string + LogSQL bool + MysqlCharset string + CharsetCollation string + Timeout int // seconds + SQLiteJournalMode string + DBConnectRetries int + DBConnectBackoff time.Duration + MaxIdleConns int + MaxOpenConns int + ConnMaxLifetime time.Duration + IterateBufferSize int + AutoMigration bool + SlowQueryThreshold time.Duration }{ Timeout: 500, IterateBufferSize: 50, @@ -87,6 +88,7 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10) Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second) Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true) + Database.SlowQueryThreshold = sec.Key("SLOW_QUERY_THRESHOLD").MustDuration(5 * time.Second) } // DBConnStr returns database connection string From 7fbdb60fc1152acc9a040dc04b1b0f5a3475b081 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 23 Feb 2024 03:18:33 +0100 Subject: [PATCH 08/79] Start to migrate from `util.OptionalBool` to `optional.Option[bool]` (#29329) just create transition helper and migrate two structs --- cmd/admin_user_create.go | 8 +++--- models/git/branch_list.go | 10 ++++---- models/git/branch_test.go | 4 +-- models/git/protected_branch_list.go | 4 +-- models/user/user.go | 25 ++++++++++--------- modules/context/repo.go | 3 ++- modules/optional/option_test.go | 5 ++-- modules/util/util.go | 18 +++++++++++++ routers/api/v1/admin/user.go | 8 ++---- routers/api/v1/repo/branch.go | 6 ++--- routers/install/install.go | 6 ++--- routers/web/admin/users.go | 2 +- routers/web/auth/oauth.go | 2 +- routers/web/repo/compare.go | 5 ++-- routers/web/repo/pull.go | 3 ++- routers/web/repo/repo.go | 5 ++-- services/auth/reverseproxy.go | 4 +-- .../auth/source/ldap/source_authenticate.go | 5 ++-- services/auth/source/ldap/source_sync.go | 5 ++-- .../auth/source/pam/source_authenticate.go | 4 +-- .../auth/source/smtp/source_authenticate.go | 3 ++- services/auth/sspi.go | 5 ++-- services/repository/adopt.go | 3 ++- services/repository/branch.go | 5 ++-- 24 files changed, 84 insertions(+), 64 deletions(-) diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index fefe18d39c..a257ce21c8 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -10,8 +10,8 @@ import ( auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" pwd "code.gitea.io/gitea/modules/auth/password" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/urfave/cli/v2" ) @@ -123,10 +123,10 @@ func runCreateUser(c *cli.Context) error { changePassword = c.Bool("must-change-password") } - restricted := util.OptionalBoolNone + restricted := optional.None[bool]() if c.IsSet("restricted") { - restricted = util.OptionalBoolOf(c.Bool("restricted")) + restricted = optional.Some(c.Bool("restricted")) } // default user visibility in app.ini @@ -142,7 +142,7 @@ func runCreateUser(c *cli.Context) error { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), IsRestricted: restricted, } diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 0e8d28038a..8319e5ecd0 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "xorm.io/builder" ) @@ -67,7 +67,7 @@ type FindBranchOptions struct { db.ListOptions RepoID int64 ExcludeBranchNames []string - IsDeletedBranch util.OptionalBool + IsDeletedBranch optional.Option[bool] OrderBy string Keyword string } @@ -81,8 +81,8 @@ func (opts FindBranchOptions) ToConds() builder.Cond { if len(opts.ExcludeBranchNames) > 0 { cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames)) } - if !opts.IsDeletedBranch.IsNone() { - cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.IsTrue()}) + if opts.IsDeletedBranch.Has() { + cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.Value()}) } if opts.Keyword != "" { cond = cond.And(builder.Like{"name", opts.Keyword}) @@ -92,7 +92,7 @@ func (opts FindBranchOptions) ToConds() builder.Cond { func (opts FindBranchOptions) ToOrders() string { orderBy := opts.OrderBy - if !opts.IsDeletedBranch.IsFalse() { // if deleted branch included, put them at the end + if opts.IsDeletedBranch.ValueOrDefault(true) { // if deleted branch included, put them at the end if orderBy != "" { orderBy += ", " } diff --git a/models/git/branch_test.go b/models/git/branch_test.go index fd5d6519e9..b8ea663e81 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -13,7 +13,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" ) @@ -50,7 +50,7 @@ func TestGetDeletedBranches(t *testing.T) { branches, err := db.Find[git_model.Branch](db.DefaultContext, git_model.FindBranchOptions{ ListOptions: db.ListOptionsAll, RepoID: repo.ID, - IsDeletedBranch: util.OptionalBoolTrue, + IsDeletedBranch: optional.Some(true), }) assert.NoError(t, err) assert.Len(t, branches, 2) diff --git a/models/git/protected_branch_list.go b/models/git/protected_branch_list.go index eeb307e245..613333a5a2 100644 --- a/models/git/protected_branch_list.go +++ b/models/git/protected_branch_list.go @@ -8,7 +8,7 @@ import ( "sort" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/gobwas/glob" ) @@ -56,7 +56,7 @@ func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) Page: page, }, RepoID: repoID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { return nil, err diff --git a/models/user/user.go b/models/user/user.go index f31dfb76bb..e92bbd4d0b 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -573,14 +574,14 @@ func IsUsableUsername(name string) error { // CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation type CreateUserOverwriteOptions struct { - KeepEmailPrivate util.OptionalBool + KeepEmailPrivate optional.Option[bool] Visibility *structs.VisibleType - AllowCreateOrganization util.OptionalBool + AllowCreateOrganization optional.Option[bool] EmailNotificationsPreference *string MaxRepoCreation *int Theme *string - IsRestricted util.OptionalBool - IsActive util.OptionalBool + IsRestricted optional.Option[bool] + IsActive optional.Option[bool] } // CreateUser creates record of a new user. @@ -607,14 +608,14 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve // overwrite defaults if set if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { overwrite := overwriteDefault[0] - if !overwrite.KeepEmailPrivate.IsNone() { - u.KeepEmailPrivate = overwrite.KeepEmailPrivate.IsTrue() + if overwrite.KeepEmailPrivate.Has() { + u.KeepEmailPrivate = overwrite.KeepEmailPrivate.Value() } if overwrite.Visibility != nil { u.Visibility = *overwrite.Visibility } - if !overwrite.AllowCreateOrganization.IsNone() { - u.AllowCreateOrganization = overwrite.AllowCreateOrganization.IsTrue() + if overwrite.AllowCreateOrganization.Has() { + u.AllowCreateOrganization = overwrite.AllowCreateOrganization.Value() } if overwrite.EmailNotificationsPreference != nil { u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference @@ -625,11 +626,11 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve if overwrite.Theme != nil { u.Theme = *overwrite.Theme } - if !overwrite.IsRestricted.IsNone() { - u.IsRestricted = overwrite.IsRestricted.IsTrue() + if overwrite.IsRestricted.Has() { + u.IsRestricted = overwrite.IsRestricted.Value() } - if !overwrite.IsActive.IsNone() { - u.IsActive = overwrite.IsActive.IsTrue() + if overwrite.IsActive.Has() { + u.IsActive = overwrite.IsActive.Value() } } diff --git a/modules/context/repo.go b/modules/context/repo.go index 3ff7209c4c..8508d46cf4 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -27,6 +27,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -671,7 +672,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), ListOptions: db.ListOptionsAll, } branchesTotal, err := db.Count[git_model.Branch](ctx, branchOpts) diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index 7ec345b6ba..bfc4577dbe 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -6,8 +6,6 @@ package optional import ( "testing" - "code.gitea.io/gitea/modules/util" - "github.com/stretchr/testify/assert" ) @@ -30,7 +28,8 @@ func TestOption(t *testing.T) { var ptr *int assert.False(t, FromPtr(ptr).Has()) - opt1 := FromPtr(util.ToPointer(1)) + int1 := 1 + opt1 := FromPtr(&int1) assert.True(t, opt1.Has()) assert.Equal(t, int(1), opt1.Value()) diff --git a/modules/util/util.go b/modules/util/util.go index 0e5c6a4e64..28b549f405 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -11,6 +11,8 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/optional" + "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -42,6 +44,22 @@ func (o OptionalBool) IsNone() bool { return o == OptionalBoolNone } +// ToGeneric converts OptionalBool to optional.Option[bool] +func (o OptionalBool) ToGeneric() optional.Option[bool] { + if o.IsNone() { + return optional.None[bool]() + } + return optional.Some[bool](o.IsTrue()) +} + +// OptionalBoolFromGeneric converts optional.Option[bool] to OptionalBool +func OptionalBoolFromGeneric(o optional.Option[bool]) OptionalBool { + if o.Has() { + return OptionalBoolOf(o.Value()) + } + return OptionalBoolNone +} + // OptionalBoolOf get the corresponding OptionalBool of a bool func OptionalBoolOf(b bool) OptionalBool { if b { diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 272996f43d..2ce7651a09 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -21,7 +21,6 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" @@ -117,11 +116,8 @@ func CreateUser(ctx *context.APIContext) { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, - } - - if form.Restricted != nil { - overwriteDefault.IsRestricted = util.OptionalBoolOf(*form.Restricted) + IsActive: optional.Some(true), + IsRestricted: optional.FromPtr(form.Restricted), } if form.Visibility != "" { diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index bd02a8afc4..2cdbcd25a2 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -17,9 +17,9 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/convert" @@ -141,7 +141,7 @@ func DeleteBranch(ctx *context.APIContext) { // check whether branches of this repository has been synced totalNumOfBranches, err := db.Count[git_model.Branch](ctx, git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { ctx.Error(http.StatusInternalServerError, "CountBranches", err) @@ -340,7 +340,7 @@ func ListBranches(ctx *context.APIContext) { branchOpts := git_model.FindBranchOptions{ ListOptions: listOptions, RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), } var err error totalNumOfBranches, err = db.Count[git_model.Branch](ctx, branchOpts) diff --git a/routers/install/install.go b/routers/install/install.go index 064575d34c..78372669f4 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -25,12 +25,12 @@ import ( "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/user" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/common" @@ -533,8 +533,8 @@ func SubmitInstall(ctx *context.Context) { IsAdmin: true, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsRestricted: util.OptionalBoolFalse, - IsActive: util.OptionalBoolTrue, + IsRestricted: optional.Some(false), + IsActive: optional.Some(true), } if err = user_model.CreateUser(ctx, u, overwriteDefault); err != nil { diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index af184fa9eb..adb9799c01 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -140,7 +140,7 @@ func NewUserPost(ctx *context.Context) { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), Visibility: &form.Visibility, } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index d00644dd5f..5e7368eb9a 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -979,7 +979,7 @@ func SignInOAuthCallback(ctx *context.Context) { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm), + IsActive: optional.Some(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm), } source := authSource.Cfg.(*oauth2.Source) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 67d41cf807..df41c750de 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -31,6 +31,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" @@ -700,7 +701,7 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { return nil, nil, err @@ -757,7 +758,7 @@ func CompareDiff(ctx *context.Context) { ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { ctx.ServerError("GetBranches", err) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index b9a4aff02e..14f1eb3102 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -32,6 +32,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" @@ -186,7 +187,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), // Add it as the first option ExcludeBranchNames: []string{ctx.Repo.Repository.DefaultBranch}, }) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index bede21be17..323413d976 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" @@ -685,7 +686,7 @@ type branchTagSearchResponse struct { func GetBranchesList(ctx *context.Context) { branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), ListOptions: db.ListOptions{ ListAll: true, }, @@ -720,7 +721,7 @@ func GetTagList(ctx *context.Context) { func PrepareBranchList(ctx *context.Context) { branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), ListOptions: db.ListOptions{ ListAll: true, }, diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go index 359c1f2473..b6aeb0aed2 100644 --- a/services/auth/reverseproxy.go +++ b/services/auth/reverseproxy.go @@ -10,8 +10,8 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" gouuid "github.com/google/uuid" @@ -161,7 +161,7 @@ func (r *ReverseProxy) newUser(req *http.Request) *user_model.User { } overwriteDefault := user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), } if err := user_model.CreateUser(req.Context(), user, &overwriteDefault); err != nil { diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index 8f641ed541..68ecd16342 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -13,7 +13,6 @@ import ( user_model "code.gitea.io/gitea/models/user" auth_module "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/util" source_service "code.gitea.io/gitea/services/auth/source" user_service "code.gitea.io/gitea/services/user" ) @@ -85,8 +84,8 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u IsAdmin: sr.IsAdmin, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsRestricted: util.OptionalBoolOf(sr.IsRestricted), - IsActive: util.OptionalBoolTrue, + IsRestricted: optional.Some(sr.IsRestricted), + IsActive: optional.Some(true), } err := user_model.CreateUser(ctx, user, overwriteDefault) diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index eee7bb585a..62f052d68c 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -16,7 +16,6 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/util" source_service "code.gitea.io/gitea/services/auth/source" user_service "code.gitea.io/gitea/services/user" ) @@ -125,8 +124,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { IsAdmin: su.IsAdmin, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsRestricted: util.OptionalBoolOf(su.IsRestricted), - IsActive: util.OptionalBoolTrue, + IsRestricted: optional.Some(su.IsRestricted), + IsActive: optional.Some(true), } err = user_model.CreateUser(ctx, usr, overwriteDefault) diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go index 0891a86392..addd1bd2c9 100644 --- a/services/auth/source/pam/source_authenticate.go +++ b/services/auth/source/pam/source_authenticate.go @@ -11,8 +11,8 @@ import ( "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/pam" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/google/uuid" ) @@ -60,7 +60,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u LoginName: userName, // This is what the user typed in } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), } if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go index b244fc7d40..1f0a61c789 100644 --- a/services/auth/source/smtp/source_authenticate.go +++ b/services/auth/source/smtp/source_authenticate.go @@ -12,6 +12,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/util" ) @@ -75,7 +76,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u LoginName: userName, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), } if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { diff --git a/services/auth/sspi.go b/services/auth/sspi.go index 0e974fde8f..8c0fc77a96 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/base" gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" @@ -172,8 +173,8 @@ func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) ( } emailNotificationPreference := user_model.EmailNotificationsDisabled overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolOf(cfg.AutoActivateUsers), - KeepEmailPrivate: util.OptionalBoolTrue, + IsActive: optional.Some(cfg.AutoActivateUsers), + KeepEmailPrivate: optional.Some(true), EmailNotificationsPreference: &emailNotificationPreference, } if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { diff --git a/services/repository/adopt.go b/services/repository/adopt.go index bfb965063f..7ca68776b5 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -154,7 +155,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) found := false diff --git a/services/repository/branch.go b/services/repository/branch.go index e2e50297af..38781acb58 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/queue" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/timeutil" @@ -61,7 +62,7 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git branchOpts := git_model.FindBranchOptions{ RepoID: repo.ID, - IsDeletedBranch: isDeletedBranch, + IsDeletedBranch: isDeletedBranch.ToGeneric(), ListOptions: db.ListOptions{ Page: page, PageSize: pageSize, @@ -239,7 +240,7 @@ func syncBranchToDB(ctx context.Context, repoID, pusherID int64, branchName stri // we cannot simply insert the branch but need to check we have branches or not hasBranch, err := db.Exist[git_model.Branch](ctx, git_model.FindBranchOptions{ RepoID: repoID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }.ToConds()) if err != nil { return err From 3ef6252e06a1f3981f8b7d1717bfc581418b1dc5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 23 Feb 2024 15:24:04 +0800 Subject: [PATCH 09/79] Allow options to disable user deletion from the interface on app.ini (#29275) Extract from #20549 This PR added a new option on app.ini `[admin]USER_DISABLED_FEATURES` to allow the site administrator to disable users visiting deletion user interface or allow. This options are also potentially allowed to define more features in future PRs. --------- Co-authored-by: wxiaoguang --- custom/conf/app.example.ini | 3 +++ .../config-cheat-sheet.en-us.md | 2 ++ .../config-cheat-sheet.zh-cn.md | 2 ++ modules/setting/admin.go | 10 +++++++- routers/web/user/setting/account.go | 6 +++++ templates/user/settings/account.tmpl | 23 ++++++++++--------- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index a360970593..5451537d02 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1474,6 +1474,9 @@ LEVEL = Info ;; ;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled ;DEFAULT_EMAIL_NOTIFICATIONS = enabled +;; Disabled features for users, could be "deletion", more features can be disabled in future +;; - deletion: a user cannot delete their own account +;USER_DISABLED_FEATURES = ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 838e26b0f6..643932de6c 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -518,6 +518,8 @@ And the following unique queues: - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled - `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations. +- `USER_DISABLED_FEATURES`: **_empty_** Disabled features for users, could be `deletion` and more features can be added in future. + - `deletion`: User cannot delete their own account. ## Security (`security`) diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index 01906930cb..5fe0a62215 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -497,6 +497,8 @@ Gitea 创建以下非唯一队列: - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**:用户电子邮件通知的默认配置(用户可配置)。选项:enabled、onmention、disabled - `DISABLE_REGULAR_ORG_CREATION`: **false**:禁止普通(非管理员)用户创建组织。 +- `USER_DISABLED_FEATURES`:**_empty_** 禁用的用户特性,当前允许为空或者 `deletion`, 未来可以增加更多设置。 + - `deletion`: 用户不能通过界面或者API删除他自己。 ## 安全性 (`security`) diff --git a/modules/setting/admin.go b/modules/setting/admin.go index 2d2dd26de9..48a2ea9744 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -3,14 +3,22 @@ package setting +import "code.gitea.io/gitea/modules/container" + // Admin settings var Admin struct { DisableRegularOrgCreation bool DefaultEmailNotification string + UserDisabledFeatures container.Set[string] } func loadAdminFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "admin", &Admin) sec := rootCfg.Section("admin") + Admin.DisableRegularOrgCreation = sec.Key("DISABLE_REGULAR_ORG_CREATION").MustBool(false) Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") + Admin.UserDisabledFeatures = container.SetOf(sec.Key("USER_DISABLED_FEATURES").Strings(",")...) } + +const ( + UserFeatureDeletion = "deletion" +) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index c7f194a3b5..659c3e29c1 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -233,6 +233,11 @@ func DeleteEmail(ctx *context.Context) { // DeleteAccount render user suicide page and response for delete user himself func DeleteAccount(ctx *context.Context) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureDeletion) { + ctx.Error(http.StatusNotFound) + return + } + ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true @@ -299,6 +304,7 @@ func loadAccountData(ctx *context.Context) { ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference ctx.Data["ActivationsPending"] = pendingActivation ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm + ctx.Data["UserDisabledFeatures"] = &setting.Admin.UserDisabledFeatures if setting.Service.UserDeleteWithCommentsMaxTime != 0 { ctx.Data["UserDeleteWithCommentsMaxTime"] = setting.Service.UserDeleteWithCommentsMaxTime.String() diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index bfcf423d67..515e79d739 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -128,6 +128,7 @@ {{end}}
+ {{if not ($.UserDisabledFeatures.Contains "deletion")}}

{{ctx.Locale.Tr "settings.delete_account"}}

@@ -151,7 +152,18 @@
+

+ {{end}}
- - {{template "user/settings/layout_footer" .}} From 7d0903bf90bce6d0ed2fa131ab028a55b8729b73 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 23 Feb 2024 19:09:18 +0800 Subject: [PATCH 10/79] Adjust changelog for v1.21.6 to move prs to correct labels (#29339) (#29343) When releasing, the releaser should read all the pull requests carefully and do some adjustments because some of pull requests' labels are not right when it's merged. And the changelog tool needs to be adjusted. If one pull request has both `bug` and `API`, it should mark it as `bug` but not `API`. Backport #29339 --- CHANGELOG.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2c69888a..e119d0bec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.com). * SECURITY * Fix XSS vulnerabilities (#29336) * Use general token signing secret (#29205) (#29325) -* API - * Refactor issue template parsing and fix API endpoint (#29069) (#29140) - * Fix swift packages not resolving (#29095) (#29102) * ENHANCEMENTS * Refactor git version functions and check compatibility (#29155) (#29157) * Improve user experience for outdated comments (#29050) (#29086) @@ -19,7 +16,11 @@ been added to each release, please refer to the [blog](https://blog.gitea.com). * Wrap contained tags and branches again (#29021) (#29026) * Fix incorrect button CSS usages (#29015) (#29023) * Strip trailing newline in markdown code copy (#29019) (#29022) + * Implement some action notifier functions (#29173) (#29308) + * Load outdated comments when (un)resolving conversation on PR timeline (#29203) (#29221) * BUGFIXES + * Refactor issue template parsing and fix API endpoint (#29069) (#29140) + * Fix swift packages not resolving (#29095) (#29102) * Remove SSH workaround (#27893) (#29332) * Only log error when tag sync fails (#29295) (#29327) * Fix SSPI user creation (#28948) (#29323) @@ -44,18 +45,15 @@ been added to each release, please refer to the [blog](https://blog.gitea.com). * Avoid showing unnecessary JS errors when there are elements with different origin on the page (#29081) (#29089) * Fix gitea-origin-url with default ports (#29085) (#29088) * Fix orgmode link resolving (#29024) (#29076) - * Fix: Elasticsearch: Request Entity Too Large #28117 (#29062) (#29075) + * Fix Elasticsearh Request Entity Too Large #28117 (#29062) (#29075) * Do not render empty comments (#29039) (#29049) * Avoid sending update/delete release notice when it is draft (#29008) (#29025) -* DOCS - * Rm outdated docs from some languages (#27530) (#29208) -* MISC - * Implement some action notifier functions (#29173) (#29308) * Fix gitea-action user avatar broken on edited menu (#29190) (#29307) * Disallow merge when required checked are missing (#29143) (#29268) - * Convert visibility to number (#29226) (#29244) - * Load outdated comments when (un)resolving conversation on PR timeline (#29203) (#29221) * Fix incorrect link to swift doc and swift package-registry login command (#29096) (#29103) + * Convert visibility to number (#29226) (#29244) +* DOCS + * Remove outdated docs from some languages (#27530) (#29208) * Fix typos in the documentation (#29048) (#29056) * Explained where create issue/PR template (#29035) @@ -174,7 +172,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com). * Fix Chinese translation of config cheat sheet[API] (#28472) (#28473) * Retry SSH key verification with additional CRLF if it failed (#28392) (#28464) -## [1.21.2](https://github.com/go-gitea/gitea/releases/tag/1.21.2) - 2023-12-12 +## [1.21.2](https://github.com/go-gitea/gitea/releases/tag/v1.21.2) - 2023-12-12 * SECURITY * Rebuild with recently released golang version @@ -213,7 +211,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com). * Use full width for project boards (#28225) (#28245) * Enable system users search via the API (#28013) (#28018) -## [1.21.1](https://github.com/go-gitea/gitea/releases/tag/1.21.1) - 2023-11-26 +## [1.21.1](https://github.com/go-gitea/gitea/releases/tag/v1.21.1) - 2023-11-26 * SECURITY * Fix comment permissions (#28213) (#28216) From 2a278b996fd6608973c3ab2a2cfb584e67d5bd8b Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Fri, 23 Feb 2024 18:24:27 +0100 Subject: [PATCH 11/79] Add support for `linguist-detectable` and `linguist-documentation` (#29267) Add support for `linguist-detectable` and `linguist-documentation` Add tests for the attributes https://github.com/github-linguist/linguist/blob/master/docs/overrides.md#detectable https://github.com/github-linguist/linguist/blob/master/docs/overrides.md#documentation --- modules/git/repo_attribute.go | 23 +- modules/git/repo_language_stats_gogit.go | 77 +++--- modules/git/repo_language_stats_nogogit.go | 77 +++--- tests/integration/linguist_test.go | 259 +++++++++++++++++++++ 4 files changed, 365 insertions(+), 71 deletions(-) create mode 100644 tests/integration/linguist_test.go diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 2b34f117f7..44f13ddc2d 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -11,6 +11,7 @@ import ( "os" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" ) // CheckAttributeOpts represents the possible options to CheckAttribute @@ -291,7 +292,7 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe } checker := &CheckAttributeReader{ - Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, + Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language", "linguist-documentation", "linguist-detectable"}, Repo: repo, IndexFile: indexFilename, WorkTree: worktree, @@ -316,3 +317,23 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe return checker, deferable } + +// true if "set"/"true", false if "unset"/"false", none otherwise +func attributeToBool(attr map[string]string, name string) optional.Option[bool] { + if value, has := attr[name]; has && value != "unspecified" { + switch value { + case "set", "true": + return optional.Some(true) + case "unset", "false": + return optional.Some(false) + } + } + return optional.None[bool]() +} + +func attributeToString(attr map[string]string, name string) optional.Option[string] { + if value, has := attr[name]; has && value != "unspecified" { + return optional.Some(value) + } + return optional.None[string]() +} diff --git a/modules/git/repo_language_stats_gogit.go b/modules/git/repo_language_stats_gogit.go index 4c6fbd6c7e..99c7a894d5 100644 --- a/modules/git/repo_language_stats_gogit.go +++ b/modules/git/repo_language_stats_gogit.go @@ -11,6 +11,7 @@ import ( "strings" "code.gitea.io/gitea/modules/analyze" + "code.gitea.io/gitea/modules/optional" "github.com/go-enry/go-enry/v2" "github.com/go-git/go-git/v5" @@ -57,25 +58,47 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err return nil } - notVendored := false - notGenerated := false + isVendored := optional.None[bool]() + isGenerated := optional.None[bool]() + isDocumentation := optional.None[bool]() + isDetectable := optional.None[bool]() if checker != nil { attrs, err := checker.CheckPath(f.Name) if err == nil { - if vendored, has := attrs["linguist-vendored"]; has { - if vendored == "set" || vendored == "true" { - return nil - } - notVendored = vendored == "false" + isVendored = attributeToBool(attrs, "linguist-vendored") + if isVendored.ValueOrDefault(false) { + return nil } - if generated, has := attrs["linguist-generated"]; has { - if generated == "set" || generated == "true" { - return nil - } - notGenerated = generated == "false" + + isGenerated = attributeToBool(attrs, "linguist-generated") + if isGenerated.ValueOrDefault(false) { + return nil } - if language, has := attrs["linguist-language"]; has && language != "unspecified" && language != "" { + + isDocumentation = attributeToBool(attrs, "linguist-documentation") + if isDocumentation.ValueOrDefault(false) { + return nil + } + + isDetectable = attributeToBool(attrs, "linguist-detectable") + if !isDetectable.ValueOrDefault(true) { + return nil + } + + hasLanguage := attributeToString(attrs, "linguist-language") + if hasLanguage.Value() == "" { + hasLanguage = attributeToString(attrs, "gitlab-language") + if hasLanguage.Has() { + language := hasLanguage.Value() + if idx := strings.IndexByte(language, '?'); idx >= 0 { + hasLanguage = optional.Some(language[:idx]) + } + } + } + if hasLanguage.Value() != "" { + language := hasLanguage.Value() + // group languages, such as Pug -> HTML; SCSS -> CSS group := enry.GetLanguageGroup(language) if len(group) != 0 { @@ -85,28 +108,14 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err // this language will always be added to the size sizes[language] += f.Size return nil - } else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" { - // strip off a ? if present - if idx := strings.IndexByte(language, '?'); idx >= 0 { - language = language[:idx] - } - if len(language) != 0 { - // group languages, such as Pug -> HTML; SCSS -> CSS - group := enry.GetLanguageGroup(language) - if len(group) != 0 { - language = group - } - - // this language will always be added to the size - sizes[language] += f.Size - return nil - } } } } - if (!notVendored && analyze.IsVendor(f.Name)) || enry.IsDotFile(f.Name) || - enry.IsDocumentation(f.Name) || enry.IsConfiguration(f.Name) { + if (!isVendored.Has() && analyze.IsVendor(f.Name)) || + enry.IsDotFile(f.Name) || + (!isDocumentation.Has() && enry.IsDocumentation(f.Name)) || + enry.IsConfiguration(f.Name) { return nil } @@ -115,12 +124,10 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err if f.Size <= bigFileSize { content, _ = readFile(f, fileSizeLimit) } - if !notGenerated && enry.IsGenerated(f.Name, content) { + if !isGenerated.Has() && enry.IsGenerated(f.Name, content) { return nil } - // TODO: Use .gitattributes file for linguist overrides - language := analyze.GetCodeLanguage(f.Name, content) if language == enry.OtherLanguage || language == "" { return nil @@ -138,7 +145,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err included = langtype == enry.Programming || langtype == enry.Markup includedLanguage[language] = included } - if included { + if included || isDetectable.ValueOrDefault(false) { sizes[language] += f.Size } else if len(sizes) == 0 && (firstExcludedLanguage == "" || firstExcludedLanguage == language) { firstExcludedLanguage = language diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go index d68d7d210a..16669924d6 100644 --- a/modules/git/repo_language_stats_nogogit.go +++ b/modules/git/repo_language_stats_nogogit.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "github.com/go-enry/go-enry/v2" ) @@ -88,25 +89,47 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err continue } - notVendored := false - notGenerated := false + isVendored := optional.None[bool]() + isGenerated := optional.None[bool]() + isDocumentation := optional.None[bool]() + isDetectable := optional.None[bool]() if checker != nil { attrs, err := checker.CheckPath(f.Name()) if err == nil { - if vendored, has := attrs["linguist-vendored"]; has { - if vendored == "set" || vendored == "true" { - continue - } - notVendored = vendored == "false" + isVendored = attributeToBool(attrs, "linguist-vendored") + if isVendored.ValueOrDefault(false) { + continue } - if generated, has := attrs["linguist-generated"]; has { - if generated == "set" || generated == "true" { - continue - } - notGenerated = generated == "false" + + isGenerated = attributeToBool(attrs, "linguist-generated") + if isGenerated.ValueOrDefault(false) { + continue } - if language, has := attrs["linguist-language"]; has && language != "unspecified" && language != "" { + + isDocumentation = attributeToBool(attrs, "linguist-documentation") + if isDocumentation.ValueOrDefault(false) { + continue + } + + isDetectable = attributeToBool(attrs, "linguist-detectable") + if !isDetectable.ValueOrDefault(true) { + continue + } + + hasLanguage := attributeToString(attrs, "linguist-language") + if hasLanguage.Value() == "" { + hasLanguage = attributeToString(attrs, "gitlab-language") + if hasLanguage.Has() { + language := hasLanguage.Value() + if idx := strings.IndexByte(language, '?'); idx >= 0 { + hasLanguage = optional.Some(language[:idx]) + } + } + } + if hasLanguage.Value() != "" { + language := hasLanguage.Value() + // group languages, such as Pug -> HTML; SCSS -> CSS group := enry.GetLanguageGroup(language) if len(group) != 0 { @@ -116,29 +139,14 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err // this language will always be added to the size sizes[language] += f.Size() continue - } else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" { - // strip off a ? if present - if idx := strings.IndexByte(language, '?'); idx >= 0 { - language = language[:idx] - } - if len(language) != 0 { - // group languages, such as Pug -> HTML; SCSS -> CSS - group := enry.GetLanguageGroup(language) - if len(group) != 0 { - language = group - } - - // this language will always be added to the size - sizes[language] += f.Size() - continue - } } - } } - if (!notVendored && analyze.IsVendor(f.Name())) || enry.IsDotFile(f.Name()) || - enry.IsDocumentation(f.Name()) || enry.IsConfiguration(f.Name()) { + if (!isVendored.Has() && analyze.IsVendor(f.Name())) || + enry.IsDotFile(f.Name()) || + (!isDocumentation.Has() && enry.IsDocumentation(f.Name())) || + enry.IsConfiguration(f.Name()) { continue } @@ -170,7 +178,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err return nil, err } } - if !notGenerated && enry.IsGenerated(f.Name(), content) { + if !isGenerated.Has() && enry.IsGenerated(f.Name(), content) { continue } @@ -193,13 +201,12 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err included = langType == enry.Programming || langType == enry.Markup includedLanguage[language] = included } - if included { + if included || isDetectable.ValueOrDefault(false) { sizes[language] += f.Size() } else if len(sizes) == 0 && (firstExcludedLanguage == "" || firstExcludedLanguage == language) { firstExcludedLanguage = language firstExcludedLanguageSize += f.Size() } - continue } // If there are no included languages add the first excluded language diff --git a/tests/integration/linguist_test.go b/tests/integration/linguist_test.go new file mode 100644 index 0000000000..e569de93a8 --- /dev/null +++ b/tests/integration/linguist_test.go @@ -0,0 +1,259 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "context" + "net/url" + "strings" + "testing" + "time" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/indexer/stats" + "code.gitea.io/gitea/modules/queue" + repo_service "code.gitea.io/gitea/services/repository" + files_service "code.gitea.io/gitea/services/repository/files" + + "github.com/stretchr/testify/assert" +) + +func TestLinguist(t *testing.T) { + onGiteaRun(t, func(t *testing.T, _ *url.URL) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + cppContent := "#include \nint main() {\nstd::cout << \"Hello Gitea!\";\nreturn 0;\n}" + pyContent := "print(\"Hello Gitea!\")" + phpContent := "" + lockContent := "# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand." + mdContent := "markdown" + + cases := []struct { + GitAttributesContent string + FilesToAdd []*files_service.ChangeRepoFile + ExpectedLanguageOrder []string + }{ + // case 0 + { + ExpectedLanguageOrder: []string{}, + }, + // case 1 + { + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + { + TreePath: "python.py", + ContentReader: strings.NewReader(pyContent), + }, + { + TreePath: "php.php", + ContentReader: strings.NewReader(phpContent), + }, + }, + ExpectedLanguageOrder: []string{"C++", "PHP", "Python"}, + }, + // case 2 + { + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: ".cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + { + TreePath: "python.py", + ContentReader: strings.NewReader(pyContent), + }, + { + TreePath: "vendor/php.php", + ContentReader: strings.NewReader(phpContent), + }, + }, + ExpectedLanguageOrder: []string{"Python"}, + }, + // case 3 + { + GitAttributesContent: "*.cpp linguist-language=Go", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + }, + ExpectedLanguageOrder: []string{"Go"}, + }, + // case 4 + { + GitAttributesContent: "*.cpp gitlab-language=Go?parent=json", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + }, + ExpectedLanguageOrder: []string{"Go"}, + }, + // case 5 + { + GitAttributesContent: "*.cpp linguist-language=HTML gitlab-language=Go?parent=json", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + }, + ExpectedLanguageOrder: []string{"HTML"}, + }, + // case 6 + { + GitAttributesContent: "vendor/** linguist-vendored=false", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "vendor/php.php", + ContentReader: strings.NewReader(phpContent), + }, + }, + ExpectedLanguageOrder: []string{"PHP"}, + }, + // case 7 + { + GitAttributesContent: "*.cpp linguist-vendored=true\n*.py linguist-vendored\nvendor/** -linguist-vendored", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + { + TreePath: "python.py", + ContentReader: strings.NewReader(pyContent), + }, + { + TreePath: "vendor/php.php", + ContentReader: strings.NewReader(phpContent), + }, + }, + ExpectedLanguageOrder: []string{"PHP"}, + }, + // case 8 + { + GitAttributesContent: "poetry.lock linguist-language=Go", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "poetry.lock", + ContentReader: strings.NewReader(lockContent), + }, + }, + ExpectedLanguageOrder: []string{"Go"}, + }, + // case 9 + { + GitAttributesContent: "poetry.lock linguist-generated=false", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "poetry.lock", + ContentReader: strings.NewReader(lockContent), + }, + }, + ExpectedLanguageOrder: []string{"TOML"}, + }, + // case 10 + { + GitAttributesContent: "*.cpp -linguist-detectable", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + }, + ExpectedLanguageOrder: []string{}, + }, + // case 11 + { + GitAttributesContent: "*.md linguist-detectable", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "test.md", + ContentReader: strings.NewReader(mdContent), + }, + }, + ExpectedLanguageOrder: []string{"Markdown"}, + }, + // case 12 + { + GitAttributesContent: "test2.md linguist-detectable", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "cplusplus.cpp", + ContentReader: strings.NewReader(cppContent), + }, + { + TreePath: "test.md", + ContentReader: strings.NewReader(mdContent), + }, + { + TreePath: "test2.md", + ContentReader: strings.NewReader(mdContent), + }, + }, + ExpectedLanguageOrder: []string{"C++", "Markdown"}, + }, + // case 13 + { + GitAttributesContent: "README.md linguist-documentation=false", + FilesToAdd: []*files_service.ChangeRepoFile{ + { + TreePath: "README.md", + ContentReader: strings.NewReader(mdContent), + }, + }, + ExpectedLanguageOrder: []string{"Markdown"}, + }, + } + + for i, c := range cases { + repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{ + Name: "linguist-test", + }) + assert.NoError(t, err) + + files := []*files_service.ChangeRepoFile{ + { + TreePath: ".gitattributes", + ContentReader: strings.NewReader(c.GitAttributesContent), + }, + } + files = append(files, c.FilesToAdd...) + for _, f := range files { + f.Operation = "create" + } + + _, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user, &files_service.ChangeRepoFilesOptions{ + Files: files, + OldBranch: repo.DefaultBranch, + NewBranch: repo.DefaultBranch, + }) + assert.NoError(t, err) + + assert.NoError(t, stats.UpdateRepoIndexer(repo)) + assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 10*time.Second)) + + stats, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, len(c.FilesToAdd)) + assert.NoError(t, err) + + languages := make([]string, 0, len(stats)) + for _, s := range stats { + languages = append(languages, s.Language) + } + assert.Equal(t, c.ExpectedLanguageOrder, languages, "case %d: unexpected language stats", i) + + assert.NoError(t, repo_service.DeleteRepository(db.DefaultContext, user, repo, false)) + } + }) +} From b762a1f1b1f7941a7db2207552d7b441d868cbe9 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Sat, 24 Feb 2024 01:49:46 +0800 Subject: [PATCH 12/79] Fix tarball/zipball download bug (#29342) Fix #29249 ~~Use the `/repos/{owner}/{repo}/archive/{archive}` API to download.~~ Apply #26430 to archive download URLs. --- services/auth/auth.go | 5 +++++ services/auth/oauth2.go | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/services/auth/auth.go b/services/auth/auth.go index 6746dc2a54..7c07dc438e 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -40,6 +40,7 @@ func isContainerPath(req *http.Request) bool { var ( gitRawOrAttachPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`) lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`) + archivePathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/archive/`) ) func isGitRawOrAttachPath(req *http.Request) bool { @@ -56,6 +57,10 @@ func isGitRawOrAttachOrLFSPath(req *http.Request) bool { return false } +func isArchivePath(req *http.Request) bool { + return archivePathRe.MatchString(req.URL.Path) +} + // handleSignIn clears existing session variables and stores new ones for the specified user object func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *user_model.User) { // We need to regenerate the session... diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index f2f7858a85..46d8510143 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -133,7 +133,7 @@ func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store Dat func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { // These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) && - !isGitRawOrAttachPath(req) { + !isGitRawOrAttachPath(req) && !isArchivePath(req) { return nil, nil } From 12d233faf786a54579a33b99b3cd56586c279f56 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 23 Feb 2024 23:19:54 +0200 Subject: [PATCH 13/79] Remove jQuery from the stopwatch (#29351) - Switched to plain JavaScript - Tested the stopwatch functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/c8e9a401-45e5-4a1d-a683-0d655f1d570e) Signed-off-by: Yarden Shoham --- web_src/js/features/stopwatch.js | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js index f43014fec5..e7e20e5212 100644 --- a/web_src/js/features/stopwatch.js +++ b/web_src/js/features/stopwatch.js @@ -1,8 +1,9 @@ -import $ from 'jquery'; import prettyMilliseconds from 'pretty-ms'; import {createTippy} from '../modules/tippy.js'; +import {GET} from '../modules/fetch.js'; +import {hideElem, showElem} from '../utils/dom.js'; -const {appSubUrl, csrfToken, notificationSettings, enableTimeTracking, assetVersionEncoded} = window.config; +const {appSubUrl, notificationSettings, enableTimeTracking, assetVersionEncoded} = window.config; export function initStopwatch() { if (!enableTimeTracking) { @@ -28,7 +29,7 @@ export function initStopwatch() { }); // global stop watch (in the head_navbar), it should always work in any case either the EventSource or the PeriodicPoller is used. - const currSeconds = $('.stopwatch-time').attr('data-seconds'); + const currSeconds = document.querySelector('.stopwatch-time')?.getAttribute('data-seconds'); if (currSeconds) { updateStopwatchTime(currSeconds); } @@ -112,29 +113,31 @@ async function updateStopwatchWithCallback(callback, timeout) { } async function updateStopwatch() { - const data = await $.ajax({ - type: 'GET', - url: `${appSubUrl}/user/stopwatches`, - headers: {'X-Csrf-Token': csrfToken}, - }); + const response = await GET(`${appSubUrl}/user/stopwatches`); + if (!response.ok) { + console.error('Failed to fetch stopwatch data'); + return false; + } + const data = await response.json(); return updateStopwatchData(data); } function updateStopwatchData(data) { const watch = data[0]; - const btnEl = $('.active-stopwatch-trigger'); + const btnEl = document.querySelector('.active-stopwatch-trigger'); if (!watch) { clearStopwatchTimer(); - btnEl.addClass('gt-hidden'); + hideElem(btnEl); } else { const {repo_owner_name, repo_name, issue_index, seconds} = watch; const issueUrl = `${appSubUrl}/${repo_owner_name}/${repo_name}/issues/${issue_index}`; - $('.stopwatch-link').attr('href', issueUrl); - $('.stopwatch-commit').attr('action', `${issueUrl}/times/stopwatch/toggle`); - $('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`); - $('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`); + document.querySelector('.stopwatch-link')?.setAttribute('href', issueUrl); + document.querySelector('.stopwatch-commit')?.setAttribute('action', `${issueUrl}/times/stopwatch/toggle`); + document.querySelector('.stopwatch-cancel')?.setAttribute('action', `${issueUrl}/times/stopwatch/cancel`); + const stopwatchIssue = document.querySelector('.stopwatch-issue'); + if (stopwatchIssue) stopwatchIssue.textContent = `${repo_owner_name}/${repo_name}#${issue_index}`; updateStopwatchTime(seconds); - btnEl.removeClass('gt-hidden'); + showElem(btnEl); } return Boolean(data.length); } @@ -151,12 +154,13 @@ function updateStopwatchTime(seconds) { if (!Number.isFinite(secs)) return; clearStopwatchTimer(); - const $stopwatch = $('.stopwatch-time'); + const stopwatch = document.querySelector('.stopwatch-time'); + // TODO: replace with similar to how system status up time is shown const start = Date.now(); const updateUi = () => { const delta = Date.now() - start; const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true}); - $stopwatch.text(dur); + if (stopwatch) stopwatch.textContent = dur; }; updateUi(); updateTimeIntervalId = setInterval(updateUi, 1000); From 53c7d8908e5ef35818b72b8c3d873b509269bc1a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 23 Feb 2024 22:51:46 +0100 Subject: [PATCH 14/79] Make optional.Option[T] type serializable (#29282) make the generic `Option` type de-/serializable for json and yaml --------- Co-authored-by: KN4CK3R --- modules/optional/option_test.go | 22 +-- modules/optional/serialization.go | 46 ++++++ modules/optional/serialization_test.go | 190 +++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 10 deletions(-) create mode 100644 modules/optional/serialization.go create mode 100644 modules/optional/serialization_test.go diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index bfc4577dbe..410fd73577 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -1,47 +1,49 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package optional +package optional_test import ( "testing" + "code.gitea.io/gitea/modules/optional" + "github.com/stretchr/testify/assert" ) func TestOption(t *testing.T) { - var uninitialized Option[int] + var uninitialized optional.Option[int] assert.False(t, uninitialized.Has()) assert.Equal(t, int(0), uninitialized.Value()) assert.Equal(t, int(1), uninitialized.ValueOrDefault(1)) - none := None[int]() + none := optional.None[int]() assert.False(t, none.Has()) assert.Equal(t, int(0), none.Value()) assert.Equal(t, int(1), none.ValueOrDefault(1)) - some := Some[int](1) + some := optional.Some[int](1) assert.True(t, some.Has()) assert.Equal(t, int(1), some.Value()) assert.Equal(t, int(1), some.ValueOrDefault(2)) var ptr *int - assert.False(t, FromPtr(ptr).Has()) + assert.False(t, optional.FromPtr(ptr).Has()) int1 := 1 - opt1 := FromPtr(&int1) + opt1 := optional.FromPtr(&int1) assert.True(t, opt1.Has()) assert.Equal(t, int(1), opt1.Value()) - assert.False(t, FromNonDefault("").Has()) + assert.False(t, optional.FromNonDefault("").Has()) - opt2 := FromNonDefault("test") + opt2 := optional.FromNonDefault("test") assert.True(t, opt2.Has()) assert.Equal(t, "test", opt2.Value()) - assert.False(t, FromNonDefault(0).Has()) + assert.False(t, optional.FromNonDefault(0).Has()) - opt3 := FromNonDefault(1) + opt3 := optional.FromNonDefault(1) assert.True(t, opt3.Has()) assert.Equal(t, int(1), opt3.Value()) } diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go new file mode 100644 index 0000000000..6688e78cd1 --- /dev/null +++ b/modules/optional/serialization.go @@ -0,0 +1,46 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package optional + +import ( + "code.gitea.io/gitea/modules/json" + + "gopkg.in/yaml.v3" +) + +func (o *Option[T]) UnmarshalJSON(data []byte) error { + var v *T + if err := json.Unmarshal(data, &v); err != nil { + return err + } + *o = FromPtr(v) + return nil +} + +func (o Option[T]) MarshalJSON() ([]byte, error) { + if !o.Has() { + return []byte("null"), nil + } + + return json.Marshal(o.Value()) +} + +func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { + var v *T + if err := value.Decode(&v); err != nil { + return err + } + *o = FromPtr(v) + return nil +} + +func (o Option[T]) MarshalYAML() (interface{}, error) { + if !o.Has() { + return nil, nil + } + + value := new(yaml.Node) + err := value.Encode(o.Value()) + return value, err +} diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go new file mode 100644 index 0000000000..09a4bddea0 --- /dev/null +++ b/modules/optional/serialization_test.go @@ -0,0 +1,190 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package optional_test + +import ( + std_json "encoding/json" //nolint:depguard + "testing" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/optional" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +type testSerializationStruct struct { + NormalString string `json:"normal_string" yaml:"normal_string"` + NormalBool bool `json:"normal_bool" yaml:"normal_bool"` + OptBool optional.Option[bool] `json:"optional_bool,omitempty" yaml:"optional_bool,omitempty"` + OptString optional.Option[string] `json:"optional_string,omitempty" yaml:"optional_string,omitempty"` + OptTwoBool optional.Option[bool] `json:"optional_two_bool" yaml:"optional_two_bool"` + OptTwoString optional.Option[string] `json:"optional_twostring" yaml:"optional_two_string"` +} + +func TestOptionalToJson(t *testing.T) { + tests := []struct { + name string + obj *testSerializationStruct + want string + }{ + { + name: "empty", + obj: new(testSerializationStruct), + want: `{"normal_string":"","normal_bool":false,"optional_two_bool":null,"optional_twostring":null}`, + }, + { + name: "some", + obj: &testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + OptTwoBool: optional.None[bool](), + OptTwoString: optional.None[string](), + }, + want: `{"normal_string":"a string","normal_bool":true,"optional_bool":false,"optional_string":"","optional_two_bool":null,"optional_twostring":null}`, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + b, err := json.Marshal(tc.obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, string(b), "gitea json module returned unexpected") + + b, err = std_json.Marshal(tc.obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, string(b), "std json module returned unexpected") + }) + } +} + +func TestOptionalFromJson(t *testing.T) { + tests := []struct { + name string + data string + want testSerializationStruct + }{ + { + name: "empty", + data: `{}`, + want: testSerializationStruct{ + NormalString: "", + }, + }, + { + name: "some", + data: `{"normal_string":"a string","normal_bool":true,"optional_bool":false,"optional_string":"","optional_two_bool":null,"optional_twostring":null}`, + want: testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var obj1 testSerializationStruct + err := json.Unmarshal([]byte(tc.data), &obj1) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, obj1, "gitea json module returned unexpected") + + var obj2 testSerializationStruct + err = std_json.Unmarshal([]byte(tc.data), &obj2) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, obj2, "std json module returned unexpected") + }) + } +} + +func TestOptionalToYaml(t *testing.T) { + tests := []struct { + name string + obj *testSerializationStruct + want string + }{ + { + name: "empty", + obj: new(testSerializationStruct), + want: `normal_string: "" +normal_bool: false +optional_two_bool: null +optional_two_string: null +`, + }, + { + name: "some", + obj: &testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + }, + want: `normal_string: a string +normal_bool: true +optional_bool: false +optional_string: "" +optional_two_bool: null +optional_two_string: null +`, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + b, err := yaml.Marshal(tc.obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, string(b), "yaml module returned unexpected") + }) + } +} + +func TestOptionalFromYaml(t *testing.T) { + tests := []struct { + name string + data string + want testSerializationStruct + }{ + { + name: "empty", + data: ``, + want: testSerializationStruct{}, + }, + { + name: "empty but init", + data: `normal_string: "" +normal_bool: false +optional_bool: +optional_two_bool: +optional_two_string: +`, + want: testSerializationStruct{}, + }, + { + name: "some", + data: ` +normal_string: a string +normal_bool: true +optional_bool: false +optional_string: "" +optional_two_bool: null +optional_twostring: null +`, + want: testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var obj testSerializationStruct + err := yaml.Unmarshal([]byte(tc.data), &obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, obj, "yaml module returned unexpected") + }) + } +} From 08c1926e1c3e2487f207b5f225d8b0f2831d0708 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 23 Feb 2024 23:07:27 +0100 Subject: [PATCH 15/79] Refactor generate-svg.js (#29348) Small refactor to avoid `process` global and to sync it with `generate-images`. --- build/generate-svg.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/generate-svg.js b/build/generate-svg.js index 2c0a5e37ba..f26b60d960 100755 --- a/build/generate-svg.js +++ b/build/generate-svg.js @@ -4,15 +4,16 @@ import {optimize} from 'svgo'; import {parse} from 'node:path'; import {readFile, writeFile, mkdir} from 'node:fs/promises'; import {fileURLToPath} from 'node:url'; +import {exit} from 'node:process'; const glob = (pattern) => fastGlob.sync(pattern, { cwd: fileURLToPath(new URL('..', import.meta.url)), absolute: true, }); -function exit(err) { +function doExit(err) { if (err) console.error(err); - process.exit(err ? 1 : 0); + exit(err ? 1 : 0); } async function processFile(file, {prefix, fullName} = {}) { @@ -64,7 +65,7 @@ async function main() { } try { - exit(await main()); + doExit(await main()); } catch (err) { - exit(err); + doExit(err); } From 6f6120dfa8d549d0b866eeb9317054fea831c844 Mon Sep 17 00:00:00 2001 From: Carlos Felgueiras Date: Sat, 24 Feb 2024 00:02:14 +0100 Subject: [PATCH 16/79] Fix validity of the FROM email address not being checked (#29347) Fixes #27188. Introduces a check on the installation that tries to parse the FROM address. If it fails, shows a new error message to the user. --------- Co-authored-by: KN4CK3R --- options/locale/locale_en-US.ini | 1 + routers/install/install.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ae34d72e41..31dbabe874 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -247,6 +247,7 @@ email_title = Email Settings smtp_addr = SMTP Host smtp_port = SMTP Port smtp_from = Send Email As +smtp_from_invalid = The "Send Email As" address is invalid smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" format. mailer_user = SMTP Username mailer_password = SMTP Password diff --git a/routers/install/install.go b/routers/install/install.go index 78372669f4..decf74cecb 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -7,6 +7,7 @@ package install import ( "fmt" "net/http" + "net/mail" "os" "os/exec" "path/filepath" @@ -419,6 +420,11 @@ func SubmitInstall(ctx *context.Context) { } if len(strings.TrimSpace(form.SMTPAddr)) > 0 { + if _, err := mail.ParseAddress(form.SMTPFrom); err != nil { + ctx.RenderWithErr(ctx.Tr("install.smtp_from_invalid"), tplInstall, &form) + return + } + cfg.Section("mailer").Key("ENABLED").SetValue("true") cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr) cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) From 875f5ea6d83c8371f309df99654ca3556623004c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eahin=20Akkaya?= Date: Sat, 24 Feb 2024 02:41:24 +0300 Subject: [PATCH 17/79] Implement code frequency graph (#29191) ### Overview This is the implementation of Code Frequency page. This feature was mentioned on these issues: #18262, #7392. It adds another tab to Activity page called Code Frequency. Code Frequency tab shows additions and deletions over time since the repository existed. Before: image After: image --- #### Features - See additions deletions over time since repository existed - Click on "Additions" or "Deletions" legend to show only one type of contribution - Use the same cache from Contributors page so that the loading of data will be fast once it is cached by visiting either one of the pages --------- Co-authored-by: Giteabot --- options/locale/locale_en-US.ini | 2 + routers/web/repo/code_frequency.go | 41 +++++ routers/web/web.go | 4 + services/repository/contributors_graph.go | 2 - templates/repo/activity.tmpl | 1 + templates/repo/code_frequency.tmpl | 9 + templates/repo/navbar.tmpl | 3 + web_src/js/components/RepoCodeFrequency.vue | 172 ++++++++++++++++++++ web_src/js/components/RepoContributors.vue | 36 +--- web_src/js/features/code-frequency.js | 21 +++ web_src/js/index.js | 2 + web_src/js/utils.js | 2 + web_src/js/utils/color.js | 14 ++ 13 files changed, 277 insertions(+), 32 deletions(-) create mode 100644 routers/web/repo/code_frequency.go create mode 100644 templates/repo/code_frequency.tmpl create mode 100644 web_src/js/components/RepoCodeFrequency.vue create mode 100644 web_src/js/features/code-frequency.js diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 31dbabe874..b35672eac2 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1919,6 +1919,7 @@ wiki.original_git_entry_tooltip = View original Git file instead of using friend activity = Activity activity.navbar.pulse = Pulse activity.navbar.contributors = Contributors +activity.navbar.code_frequency = Code Frequency activity.period.filter_label = Period: activity.period.daily = 1 day activity.period.halfweekly = 3 days @@ -2597,6 +2598,7 @@ component_loading = Loading %s... component_loading_failed = Could not load %s component_loading_info = This might take a bit… component_failed_to_load = An unexpected error happened. +code_frequency.what = code frequency contributors.what = contributions [org] diff --git a/routers/web/repo/code_frequency.go b/routers/web/repo/code_frequency.go new file mode 100644 index 0000000000..48ade655b7 --- /dev/null +++ b/routers/web/repo/code_frequency.go @@ -0,0 +1,41 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "errors" + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + contributors_service "code.gitea.io/gitea/services/repository" +) + +const ( + tplCodeFrequency base.TplName = "repo/activity" +) + +// CodeFrequency renders the page to show repository code frequency +func CodeFrequency(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.activity.navbar.code_frequency") + + ctx.Data["PageIsActivity"] = true + ctx.Data["PageIsCodeFrequency"] = true + ctx.PageData["repoLink"] = ctx.Repo.RepoLink + + ctx.HTML(http.StatusOK, tplCodeFrequency) +} + +// CodeFrequencyData returns JSON of code frequency data +func CodeFrequencyData(ctx *context.Context) { + if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { + if errors.Is(err, contributors_service.ErrAwaitGeneration) { + ctx.Status(http.StatusAccepted) + return + } + ctx.ServerError("GetCodeFrequencyData", err) + } else { + ctx.JSON(http.StatusOK, contributorStats["total"].Weeks) + } +} diff --git a/routers/web/web.go b/routers/web/web.go index 77c8319f06..5e18aac67d 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1403,6 +1403,10 @@ func registerRoutes(m *web.Route) { m.Get("", repo.Contributors) m.Get("/data", repo.ContributorsData) }) + m.Group("/code-frequency", func() { + m.Get("", repo.CodeFrequency) + m.Get("/data", repo.CodeFrequencyData) + }) }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases)) m.Group("/activity_author_data", func() { diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index 8421df8e3a..7c9f535ae0 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -143,7 +143,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { _ = stdoutWriter.Close() scanner := bufio.NewScanner(stdoutReader) - scanner.Split(bufio.ScanLines) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) @@ -180,7 +179,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int } } commitStats.Total = commitStats.Additions + commitStats.Deletions - scanner.Scan() scanner.Text() // empty line at the end res := &ExtendedCommitStats{ diff --git a/templates/repo/activity.tmpl b/templates/repo/activity.tmpl index 960083d2fb..94f52b0e26 100644 --- a/templates/repo/activity.tmpl +++ b/templates/repo/activity.tmpl @@ -8,6 +8,7 @@
{{if .PageIsPulse}}{{template "repo/pulse" .}}{{end}} {{if .PageIsContributors}}{{template "repo/contributors" .}}{{end}} + {{if .PageIsCodeFrequency}}{{template "repo/code_frequency" .}}{{end}}
diff --git a/templates/repo/code_frequency.tmpl b/templates/repo/code_frequency.tmpl new file mode 100644 index 0000000000..50ec1beb6b --- /dev/null +++ b/templates/repo/code_frequency.tmpl @@ -0,0 +1,9 @@ +{{if .Permission.CanRead $.UnitTypeCode}} +
+
+{{end}} diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl index a9042ee30d..aa5021e73a 100644 --- a/templates/repo/navbar.tmpl +++ b/templates/repo/navbar.tmpl @@ -5,4 +5,7 @@ {{ctx.Locale.Tr "repo.activity.navbar.contributors"}} + + {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}} +
diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue new file mode 100644 index 0000000000..ad607a041a --- /dev/null +++ b/web_src/js/components/RepoCodeFrequency.vue @@ -0,0 +1,172 @@ + + + diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index fa1545b3df..84fdcae1f6 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -3,10 +3,7 @@ import {SvgIcon} from '../svg.js'; import { Chart, Title, - Tooltip, - Legend, BarElement, - CategoryScale, LinearScale, TimeScale, PointElement, @@ -21,27 +18,13 @@ import { firstStartDateAfterDate, fillEmptyStartDaysWithZeroes, } from '../utils/time.js'; +import {chartJsColors} from '../utils/color.js'; +import {sleep} from '../utils.js'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import $ from 'jquery'; const {pageData} = window.config; -const colors = { - text: '--color-text', - border: '--color-secondary-alpha-60', - commits: '--color-primary-alpha-60', - additions: '--color-green', - deletions: '--color-red', - title: '--color-secondary-dark-4', -}; - -const styles = window.getComputedStyle(document.documentElement); -const getColor = (name) => styles.getPropertyValue(name).trim(); - -for (const [key, value] of Object.entries(colors)) { - colors[key] = getColor(value); -} - const customEventListener = { id: 'customEventListener', afterEvent: (chart, args, opts) => { @@ -54,17 +37,14 @@ const customEventListener = { } }; -Chart.defaults.color = colors.text; -Chart.defaults.borderColor = colors.border; +Chart.defaults.color = chartJsColors.text; +Chart.defaults.borderColor = chartJsColors.border; Chart.register( TimeScale, - CategoryScale, LinearScale, BarElement, Title, - Tooltip, - Legend, PointElement, LineElement, Filler, @@ -122,7 +102,7 @@ export default { do { response = await GET(`${this.repoLink}/activity/contributors/data`); if (response.status === 202) { - await new Promise((resolve) => setTimeout(resolve, 1000)); // wait for 1 second before retrying + await sleep(1000); // wait for 1 second before retrying } } while (response.status === 202); if (response.ok) { @@ -222,7 +202,7 @@ export default { pointRadius: 0, pointHitRadius: 0, fill: 'start', - backgroundColor: colors[this.type], + backgroundColor: chartJsColors[this.type], borderWidth: 0, tension: 0.3, }, @@ -254,7 +234,6 @@ export default { title: { display: type === 'main', text: 'drag: zoom, shift+drag: pan, double click: reset zoom', - color: colors.title, position: 'top', align: 'center', }, @@ -262,9 +241,6 @@ export default { chartType: type, instance: this, }, - legend: { - display: false, - }, zoom: { pan: { enabled: true, diff --git a/web_src/js/features/code-frequency.js b/web_src/js/features/code-frequency.js new file mode 100644 index 0000000000..103d82f6e3 --- /dev/null +++ b/web_src/js/features/code-frequency.js @@ -0,0 +1,21 @@ +import {createApp} from 'vue'; + +export async function initRepoCodeFrequency() { + const el = document.getElementById('repo-code-frequency-chart'); + if (!el) return; + + const {default: RepoCodeFrequency} = await import(/* webpackChunkName: "code-frequency-graph" */'../components/RepoCodeFrequency.vue'); + try { + const View = createApp(RepoCodeFrequency, { + locale: { + loadingTitle: el.getAttribute('data-locale-loading-title'), + loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'), + loadingInfo: el.getAttribute('data-locale-loading-info'), + } + }); + View.mount(el); + } catch (err) { + console.error('RepoCodeFrequency failed to load', err); + el.textContent = el.getAttribute('data-locale-component-failed-to-load'); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index ddd435f05e..876e4291ee 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -87,6 +87,7 @@ import {onDomReady} from './utils/dom.js'; import {initRepoIssueList} from './features/repo-issue-list.js'; import {initCommonIssueListQuickGoto} from './features/common-issue-list.js'; import {initRepoContributors} from './features/contributors.js'; +import {initRepoCodeFrequency} from './features/code-frequency.js'; import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'; import {initDirAuto} from './modules/dirauto.js'; @@ -177,6 +178,7 @@ onDomReady(() => { initRepository(); initRepositoryActionView(); initRepoContributors(); + initRepoCodeFrequency(); initCommitStatuses(); initCaptcha(); diff --git a/web_src/js/utils.js b/web_src/js/utils.js index c82e42d349..3a2694335f 100644 --- a/web_src/js/utils.js +++ b/web_src/js/utils.js @@ -139,3 +139,5 @@ export function parseDom(text, contentType) { export function serializeXml(node) { return xmlSerializer.serializeToString(node); } + +export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/web_src/js/utils/color.js b/web_src/js/utils/color.js index 5d9c4ca45d..0ba6af49ee 100644 --- a/web_src/js/utils/color.js +++ b/web_src/js/utils/color.js @@ -19,3 +19,17 @@ function getLuminance(r, g, b) { export function useLightTextOnBackground(r, g, b) { return getLuminance(r, g, b) < 0.453; } + +function resolveColors(obj) { + const styles = window.getComputedStyle(document.documentElement); + const getColor = (name) => styles.getPropertyValue(name).trim(); + return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, getColor(value)])); +} + +export const chartJsColors = resolveColors({ + text: '--color-text', + border: '--color-secondary-alpha-60', + commits: '--color-primary-alpha-60', + additions: '--color-green', + deletions: '--color-red', +}); From 4ba642d07d50d7eb42ae33cd6f1f7f2c82c02a40 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 24 Feb 2024 05:18:49 +0100 Subject: [PATCH 18/79] Revert "Support SAML authentication (#25165)" (#29358) This reverts #25165 (5bb8d1924d77c675467694de26697b876d709a17), as there was a chance some important reviews got missed. so after reverting this patch it will be resubmitted for reviewing again https://github.com/go-gitea/gitea/pull/25165#issuecomment-1960670242 temporary Open #5512 again --- .github/workflows/pull-db-tests.yml | 8 - assets/go-licenses.json | 25 --- docs/content/usage/authentication.en-us.md | 69 ------ go.mod | 5 - go.sum | 12 -- models/auth/oauth2.go | 20 +- models/auth/source.go | 38 ---- options/locale/locale_en-US.ini | 14 -- routers/init.go | 2 - routers/web/admin/auths.go | 84 -------- routers/web/auth/auth.go | 35 +-- routers/web/auth/linkaccount.go | 45 ++-- routers/web/auth/oauth.go | 19 +- routers/web/auth/openid.go | 5 +- routers/web/auth/saml.go | 172 --------------- routers/web/web.go | 5 - .../auth/source/saml/assert_interface_test.go | 22 -- services/auth/source/saml/init.go | 29 --- services/auth/source/saml/name_id_format.go | 38 ---- services/auth/source/saml/providers.go | 109 ---------- services/auth/source/saml/source.go | 202 ------------------ .../auth/source/saml/source_authenticate.go | 16 -- services/auth/source/saml/source_callout.go | 89 -------- services/auth/source/saml/source_metadata.go | 32 --- services/auth/source/saml/source_register.go | 23 -- services/externalaccount/link.go | 11 +- services/externalaccount/user.go | 12 +- services/forms/auth_form.go | 15 +- templates/admin/auth/edit.tmpl | 66 ------ templates/admin/auth/new.tmpl | 6 - templates/admin/auth/source/saml.tmpl | 62 ------ templates/user/auth/signin_inner.tmpl | 17 -- tests/integration/README.md | 17 -- tests/integration/saml_test.go | 150 ------------- web_src/js/features/admin/common.js | 8 +- web_src/js/features/user-auth.js | 21 -- web_src/js/index.js | 6 +- 37 files changed, 69 insertions(+), 1440 deletions(-) delete mode 100644 routers/web/auth/saml.go delete mode 100644 services/auth/source/saml/assert_interface_test.go delete mode 100644 services/auth/source/saml/init.go delete mode 100644 services/auth/source/saml/name_id_format.go delete mode 100644 services/auth/source/saml/providers.go delete mode 100644 services/auth/source/saml/source.go delete mode 100644 services/auth/source/saml/source_authenticate.go delete mode 100644 services/auth/source/saml/source_callout.go delete mode 100644 services/auth/source/saml/source_metadata.go delete mode 100644 services/auth/source/saml/source_register.go delete mode 100644 templates/admin/auth/source/saml.tmpl delete mode 100644 tests/integration/saml_test.go diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml index 8843c6d65e..a3886bf618 100644 --- a/.github/workflows/pull-db-tests.yml +++ b/.github/workflows/pull-db-tests.yml @@ -37,14 +37,6 @@ jobs: MINIO_ROOT_PASSWORD: 12345678 ports: - "9000:9000" - simplesaml: - image: allspice/simple-saml - ports: - - "8080:8080" - env: - SIMPLESAMLPHP_SP_ENTITY_ID: http://localhost:3002/user/saml/test-sp/metadata - SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: http://localhost:3002/user/saml/test-sp/acs - SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE: http://localhost:3002/user/saml/test-sp/acs steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/assets/go-licenses.json b/assets/go-licenses.json index ed722b0192..2aa60780c4 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -124,11 +124,6 @@ "path": "github.com/aymerick/douceur/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Aymerick JEHANNE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, - { - "name": "github.com/beevik/etree", - "path": "github.com/beevik/etree/LICENSE", - "licenseText": "Copyright 2015-2019 Brett Vickers. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n 1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n 2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\nOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "github.com/beorn7/perks/quantile", "path": "github.com/beorn7/perks/quantile/LICENSE", @@ -644,11 +639,6 @@ "path": "github.com/jhillyerd/enmime/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2012-2016 James Hillyerd, All Rights Reserved\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, - { - "name": "github.com/jonboulle/clockwork", - "path": "github.com/jonboulle/clockwork/LICENSE", - "licenseText": "Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, { "name": "github.com/josharian/intern", "path": "github.com/josharian/intern/license.md", @@ -729,11 +719,6 @@ "path": "github.com/markbates/goth/LICENSE.txt", "licenseText": "Copyright (c) 2014 Mark Bates\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, - { - "name": "github.com/mattermost/xml-roundtrip-validator", - "path": "github.com/mattermost/xml-roundtrip-validator/LICENSE.txt", - "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, { "name": "github.com/mattn/go-colorable", "path": "github.com/mattn/go-colorable/LICENSE", @@ -919,16 +904,6 @@ "path": "github.com/rs/xid/LICENSE", "licenseText": "Copyright (c) 2015 Olivier Poitrey \u003crs@dailymotion.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, - { - "name": "github.com/russellhaering/gosaml2", - "path": "github.com/russellhaering/gosaml2/LICENSE", - "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n" - }, - { - "name": "github.com/russellhaering/goxmldsig", - "path": "github.com/russellhaering/goxmldsig/LICENSE", - "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n" - }, { "name": "github.com/russross/blackfriday/v2", "path": "github.com/russross/blackfriday/v2/LICENSE.txt", diff --git a/docs/content/usage/authentication.en-us.md b/docs/content/usage/authentication.en-us.md index 1838cfcc77..adc936dfbe 100644 --- a/docs/content/usage/authentication.en-us.md +++ b/docs/content/usage/authentication.en-us.md @@ -349,72 +349,3 @@ If set `ENABLE_REVERSE_PROXY_FULL_NAME=true`, a user full name expected in `X-WE You can also limit the reverse proxy's IP address range with `REVERSE_PROXY_TRUSTED_PROXIES` which default value is `127.0.0.0/8,::1/128`. By `REVERSE_PROXY_LIMIT`, you can limit trusted proxies level. Notice: Reverse Proxy Auth doesn't support the API. You still need an access token or basic auth to make API requests. - -## SAML - -### Configuring Gitea as a SAML 2.0 Service Provider - -- Navigate to `Site Administration > Identity & Access > Authentication Sources`. -- Click the `Add Authentication Source` button. -- Select `SAML` as the authentication type. - -#### Features Not Yet Supported - -Currently, auto-registration is not supported for SAML. During the external account linking process the user will be prompted to set a username and email address or link to an existing account. - -SAML group mapping is not supported. - -#### Settings - -- `Authentication Name` **(required)** - - - The name of this authentication source (appears in the Gitea ACS and metadata URLs) - -- `SAML NameID Format` **(required)** - - - This specifies how Identity Provider (IdP) users are mapped to Gitea users. This option will be provider specific. - -- `Icon URL` (optional) - - - URL of an icon to display on the Sign-In page for this authentication source. - -- `[Insecure] Skip Assertion Signature Validation` (optional) - - - This option is not recommended and disables integrity verification of IdP SAML assertions. - -- `Identity Provider Metadata URL` (optional if XML set) - - - The URL of the IdP metadata endpoint. - - This field must be set if `Identity Provider Metadata XML` is left blank. - -- `Identity Provider Metadata XML` (optional if URL set) - - - The XML returned by the IdP metadata endpoint. - - This field must be set if `Identity Provider Metadata URL` is left blank. - -- `Service Provider Certificate` (optional) - - - X.509-formatted certificate (with `Service Provider Private Key`) used for signing SAML requests. - - A certificate will be generated if this field is left blank. - -- `Service Provider Private Key` (optional) - - - DSA/RSA private key (with `Service Provider Certificate`) used for signing SAML requests. - - A private key will be generated if this field is left blank. - -- `Email Assertion Key` (optional) - - - The SAML assertion key used for the IdP user's email (depends on provider configuration). - -- `Name Assertion Key` (optional) - - - The SAML assertion key used for the IdP user's nickname (depends on provider configuration). - -- `Username Assertion Key` (optional) - - - The SAML assertion key used for the IdP user's username (depends on provider configuration). - -### Configuring a SAML 2.0 Identity Provider to use Gitea - -- The service provider assertion consumer service url will look like: `http(s)://[mydomain]/user/saml/[Authentication Name]/acs`. -- The service provider metadata url will look like: `http(s)://[mydomain]/user/saml/[Authentication Name]/metadata`. diff --git a/go.mod b/go.mod index 012a34612f..7a752ec874 100644 --- a/go.mod +++ b/go.mod @@ -91,8 +91,6 @@ require ( github.com/quasoft/websspi v1.1.2 github.com/redis/go-redis/v9 v9.4.0 github.com/robfig/cron/v3 v3.0.1 - github.com/russellhaering/gosaml2 v0.9.1 - github.com/russellhaering/goxmldsig v1.3.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sassoftware/go-rpmutils v0.2.1-0.20240124161140-277b154961dd github.com/sergi/go-diff v1.3.1 @@ -145,7 +143,6 @@ require ( github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/blevesearch/bleve_index_api v1.1.5 // indirect @@ -219,7 +216,6 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect - github.com/jonboulle/clockwork v0.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect @@ -229,7 +225,6 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/markbates/going v1.0.3 // indirect - github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mholt/acmez v1.2.0 // indirect diff --git a/go.sum b/go.sum index 393e10cfa0..b3b8ad8ce4 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,6 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= -github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE= @@ -568,9 +566,6 @@ github.com/jhillyerd/enmime v1.1.0 h1:ubaIzg68VY7CMCe2YbHe6nkRvU9vujixTkNz3EBvZO github.com/jhillyerd/enmime v1.1.0/go.mod h1:FRFuUPCLh8PByQv+8xRcLO9QHqaqTqreYhopv5eyk4I= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= -github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -639,8 +634,6 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o= github.com/markbates/goth v1.78.0 h1:7VEIFDycJp9deyVv3YraGBPdD0ZYQW93Y3Aw1eVP3BY= github.com/markbates/goth v1.78.0/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc= -github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= -github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -773,17 +766,12 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/russellhaering/gosaml2 v0.9.1 h1:H/whrl8NuSoxyW46Ww5lKPskm+5K+qYLw9afqJ/Zef0= -github.com/russellhaering/gosaml2 v0.9.1/go.mod h1:ja+qgbayxm+0mxBRLMSUuX3COqy+sb0RRhIGun/W2kc= -github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM= -github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index a252458d4e..9d53fffc78 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -8,7 +8,6 @@ import ( "crypto/sha256" "encoding/base32" "encoding/base64" - "encoding/gob" "fmt" "net" "net/url" @@ -82,10 +81,6 @@ func Init(ctx context.Context) error { builtinAllClientIDs = append(builtinAllClientIDs, clientID) } - // This is needed in order to encode and store the struct in the goth/gothic session - // during the process of linking the external user. - gob.Register(LinkAccountUser{}) - var registeredApps []*OAuth2Application if err := db.GetEngine(ctx).In("client_id", builtinAllClientIDs).Find(®isteredApps); err != nil { return err @@ -610,6 +605,21 @@ func (err ErrOAuthApplicationNotFound) Unwrap() error { return util.ErrNotExist } +// GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name +func GetActiveOAuth2SourceByName(ctx context.Context, name string) (*Source, error) { + authSource := new(Source) + has, err := db.GetEngine(ctx).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(authSource) + if err != nil { + return nil, err + } + + if !has { + return nil, fmt.Errorf("oauth2 source not found, name: %q", name) + } + + return authSource, nil +} + func DeleteOAuth2RelictsByUserID(ctx context.Context, userID int64) error { deleteCond := builder.Select("id").From("oauth2_grant").Where(builder.Eq{"oauth2_grant.user_id": userID}) diff --git a/models/auth/source.go b/models/auth/source.go index bc564d35ba..1bdde8235c 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -14,7 +14,6 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" - "github.com/markbates/goth" "xorm.io/builder" "xorm.io/xorm" "xorm.io/xorm/convert" @@ -33,7 +32,6 @@ const ( DLDAP // 5 OAuth2 // 6 SSPI // 7 - SAML // 8 ) // String returns the string name of the LoginType @@ -54,7 +52,6 @@ var Names = map[Type]string{ PAM: "PAM", OAuth2: "OAuth2", SSPI: "SPNEGO with SSPI", - SAML: "SAML", } // Config represents login config as far as the db is concerned @@ -124,12 +121,6 @@ type Source struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } -// LinkAccountUser is used to link an external user with a local user -type LinkAccountUser struct { - Type Type - GothUser goth.User -} - // TableName xorm will read the table name from this method func (Source) TableName() string { return "login_source" @@ -189,11 +180,6 @@ func (source *Source) IsSSPI() bool { return source.Type == SSPI } -// IsSAML returns true of this source is of the SAML type. -func (source *Source) IsSAML() bool { - return source.Type == SAML -} - // HasTLS returns true of this source supports TLS. func (source *Source) HasTLS() bool { hasTLSer, ok := source.Cfg.(HasTLSer) @@ -406,27 +392,3 @@ func IsErrSourceInUse(err error) bool { func (err ErrSourceInUse) Error() string { return fmt.Sprintf("login source is still used by some users [id: %d]", err.ID) } - -// GetActiveAuthProviderSources returns all activated sources -func GetActiveAuthProviderSources(ctx context.Context, authType Type) ([]*Source, error) { - sources := make([]*Source, 0, 1) - if err := db.GetEngine(ctx).Where("is_active = ? and type = ?", true, authType).Find(&sources); err != nil { - return nil, err - } - return sources, nil -} - -// GetActiveAuthSourceByName returns an AuthSource based on the given name and type -func GetActiveAuthSourceByName(ctx context.Context, name string, authType Type) (*Source, error) { - authSource := new(Source) - has, err := db.GetEngine(ctx).Where("name = ? and type = ? and is_active = ?", name, authType, true).Get(authSource) - if err != nil { - return nil, err - } - - if !has { - return nil, fmt.Errorf("auth source not found, name: %q", name) - } - - return authSource, nil -} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index b35672eac2..2c92f40a17 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -523,9 +523,6 @@ Content = Content SSPISeparatorReplacement = Separator SSPIDefaultLanguage = Default Language -SAMLMetadata = Either SAML Identity Provider metadata URL or XML -SAMLMetadataURL = SAML Identity Provider metadata URL is invalid - require_error = ` cannot be empty.` alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` @@ -3032,18 +3029,7 @@ auths.sspi_separator_replacement = Separator to use instead of \, / and @ auths.sspi_separator_replacement_helper = The character to use to replace the separators of down-level logon names (eg. the \ in "DOMAIN\user") and user principal names (eg. the @ in "user@example.org"). auths.sspi_default_language = Default user language auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected. -auths.saml_nameidformat = SAML NameID Format -auths.saml_identity_provider_metadata_url = Identity Provider Metadata URL -auths.saml_identity_provider_metadata = Identity Provider Metadata XML -auths.saml_insecure_skip_assertion_signature_validation = [Insecure] Skip Assertion Signature Validation -auths.saml_service_provider_certificate = Service Provider Certificate -auths.saml_service_provider_private_key = Service Provider Private Key -auths.saml_identity_provider_email_assertion_key = Email Assertion Key -auths.saml_identity_provider_name_assertion_key = Name Assertion Key -auths.saml_identity_provider_username_assertion_key = Username Assertion Key -auths.saml_icon_url = Icon URL auths.tips = Tips -auths.tips.saml = Documentation can be found at https://docs.gitea.com/usage/authentication#saml auths.tips.oauth2.general = OAuth2 Authentication auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be: auths.tip.oauth2_provider = OAuth2 Provider diff --git a/routers/init.go b/routers/init.go index 9ae8c368a2..e0a7150ba3 100644 --- a/routers/init.go +++ b/routers/init.go @@ -35,7 +35,6 @@ import ( actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" - "code.gitea.io/gitea/services/auth/source/saml" "code.gitea.io/gitea/services/automerge" "code.gitea.io/gitea/services/cron" feed_service "code.gitea.io/gitea/services/feed" @@ -139,7 +138,6 @@ func InitWebInstalled(ctx context.Context) { log.Info("ORM engine initialization successful!") mustInit(system.Init) mustInitCtx(ctx, oauth2.Init) - mustInitCtx(ctx, saml.Init) mustInit(release_service.Init) diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 187b569d39..7fdd18dfae 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -1,12 +1,9 @@ // Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package admin import ( - "crypto/tls" - "crypto/x509" "errors" "fmt" "net/http" @@ -28,7 +25,6 @@ import ( "code.gitea.io/gitea/services/auth/source/ldap" "code.gitea.io/gitea/services/auth/source/oauth2" pam_service "code.gitea.io/gitea/services/auth/source/pam" - "code.gitea.io/gitea/services/auth/source/saml" "code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/auth/source/sspi" "code.gitea.io/gitea/services/forms" @@ -75,7 +71,6 @@ var ( {auth.SMTP.String(), auth.SMTP}, {auth.OAuth2.String(), auth.OAuth2}, {auth.SSPI.String(), auth.SSPI}, - {auth.SAML.String(), auth.SAML}, } if pam.Supported { items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM}) @@ -88,16 +83,6 @@ var ( {ldap.SecurityProtocolNames[ldap.SecurityProtocolLDAPS], ldap.SecurityProtocolLDAPS}, {ldap.SecurityProtocolNames[ldap.SecurityProtocolStartTLS], ldap.SecurityProtocolStartTLS}, } - - nameIDFormats = []dropdownItem{ - {saml.NameIDFormatNames[saml.SAML20Persistent], saml.SAML20Persistent}, // use this as default value - {saml.NameIDFormatNames[saml.SAML11Email], saml.SAML11Email}, - {saml.NameIDFormatNames[saml.SAML11Persistent], saml.SAML11Persistent}, - {saml.NameIDFormatNames[saml.SAML11Unspecified], saml.SAML11Unspecified}, - {saml.NameIDFormatNames[saml.SAML20Email], saml.SAML20Email}, - {saml.NameIDFormatNames[saml.SAML20Transient], saml.SAML20Transient}, - {saml.NameIDFormatNames[saml.SAML20Unspecified], saml.SAML20Unspecified}, - } ) // NewAuthSource render adding a new auth source page @@ -113,8 +98,6 @@ func NewAuthSource(ctx *context.Context) { ctx.Data["is_sync_enabled"] = true ctx.Data["AuthSources"] = authSources ctx.Data["SecurityProtocols"] = securityProtocols - ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[saml.SAML20Persistent] - ctx.Data["NameIDFormats"] = nameIDFormats ctx.Data["SMTPAuths"] = smtp.Authenticators oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers @@ -248,52 +231,6 @@ func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi }, nil } -func parseSAMLConfig(ctx *context.Context, form forms.AuthenticationForm) (*saml.Source, error) { - if util.IsEmptyString(form.IdentityProviderMetadata) && util.IsEmptyString(form.IdentityProviderMetadataURL) { - return nil, fmt.Errorf("%s %s", ctx.Tr("form.SAMLMetadata"), ctx.Tr("form.require_error")) - } - - if !util.IsEmptyString(form.IdentityProviderMetadataURL) { - _, err := url.Parse(form.IdentityProviderMetadataURL) - if err != nil { - return nil, fmt.Errorf("%s", ctx.Tr("form.SAMLMetadataURL")) - } - } - - // check the integrity of the certificate and private key (autogenerated if these form fields are blank) - if !util.IsEmptyString(form.ServiceProviderCertificate) && !util.IsEmptyString(form.ServiceProviderPrivateKey) { - keyPair, err := tls.X509KeyPair([]byte(form.ServiceProviderCertificate), []byte(form.ServiceProviderPrivateKey)) - if err != nil { - return nil, err - } - keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) - if err != nil { - return nil, err - } - } else { - privateKey, cert, err := saml.GenerateSAMLSPKeypair() - if err != nil { - return nil, err - } - - form.ServiceProviderPrivateKey = privateKey - form.ServiceProviderCertificate = cert - } - - return &saml.Source{ - IdentityProviderMetadata: form.IdentityProviderMetadata, - IdentityProviderMetadataURL: form.IdentityProviderMetadataURL, - InsecureSkipAssertionSignatureValidation: form.InsecureSkipAssertionSignatureValidation, - NameIDFormat: saml.NameIDFormat(form.NameIDFormat), - ServiceProviderCertificate: form.ServiceProviderCertificate, - ServiceProviderPrivateKey: form.ServiceProviderPrivateKey, - EmailAssertionKey: form.EmailAssertionKey, - NameAssertionKey: form.NameAssertionKey, - UsernameAssertionKey: form.UsernameAssertionKey, - IconURL: form.SAMLIconURL, - }, nil -} - // NewAuthSourcePost response for adding an auth source func NewAuthSourcePost(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.AuthenticationForm) @@ -307,8 +244,6 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.Data["SMTPAuths"] = smtp.Authenticators oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers - ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[saml.NameIDFormat(form.NameIDFormat)] - ctx.Data["NameIDFormats"] = nameIDFormats ctx.Data["SSPIAutoCreateUsers"] = true ctx.Data["SSPIAutoActivateUsers"] = true @@ -355,13 +290,6 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) return } - case auth.SAML: - var err error - config, err = parseSAMLConfig(ctx, form) - if err != nil { - ctx.RenderWithErr(err.Error(), tplAuthNew, form) - return - } default: ctx.Error(http.StatusBadRequest) return @@ -408,7 +336,6 @@ func EditAuthSource(ctx *context.Context) { ctx.Data["SMTPAuths"] = smtp.Authenticators oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers - ctx.Data["NameIDFormats"] = nameIDFormats source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid")) if err != nil { @@ -417,9 +344,6 @@ func EditAuthSource(ctx *context.Context) { } ctx.Data["Source"] = source ctx.Data["HasTLS"] = source.HasTLS() - if source.IsSAML() { - ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[source.Cfg.(*saml.Source).NameIDFormat] - } if source.IsOAuth2() { type Named interface { @@ -454,8 +378,6 @@ func EditAuthSourcePost(ctx *context.Context) { } ctx.Data["Source"] = source ctx.Data["HasTLS"] = source.HasTLS() - ctx.Data["CurrentNameIDFormat"] = saml.NameIDFormatNames[saml.SAML20Persistent] - ctx.Data["NameIDFormats"] = nameIDFormats if ctx.HasError() { ctx.HTML(http.StatusOK, tplAuthEdit) @@ -490,12 +412,6 @@ func EditAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(err.Error(), tplAuthEdit, form) return } - case auth.SAML: - config, err = parseSAMLConfig(ctx, form) - if err != nil { - ctx.RenderWithErr(err.Error(), tplAuthEdit, form) - return - } default: ctx.Error(http.StatusBadRequest) return diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index f5955ec5ff..3de1f3373d 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -28,7 +28,6 @@ import ( "code.gitea.io/gitea/routers/utils" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" - "code.gitea.io/gitea/services/auth/source/saml" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" @@ -171,14 +170,6 @@ func SignIn(ctx *context.Context) { return } ctx.Data["OAuth2Providers"] = oauth2Providers - - samlProviders, err := saml.GetSAMLProviders(ctx, util.OptionalBoolTrue) - if err != nil { - ctx.ServerError("UserSignIn", err) - return - } - ctx.Data["SAMLProviders"] = samlProviders - ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true @@ -202,14 +193,6 @@ func SignInPost(ctx *context.Context) { return } ctx.Data["OAuth2Providers"] = oauth2Providers - - samlProviders, err := saml.GetSAMLProviders(ctx, util.OptionalBoolTrue) - if err != nil { - ctx.ServerError("UserSignIn", err) - return - } - ctx.Data["SAMLProviders"] = samlProviders - ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true @@ -521,7 +504,7 @@ func SignUpPost(ctx *context.Context) { Passwd: form.Password, } - if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false, auth.NoType) { + if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false) { // error already handled return } @@ -532,16 +515,16 @@ func SignUpPost(ctx *context.Context) { // createAndHandleCreatedUser calls createUserInContext and // then handleUserCreated. -func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool, authType auth.Type) bool { - if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink, authType) { +func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool { + if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink) { return false } - return handleUserCreated(ctx, u, gothUser, authType) + return handleUserCreated(ctx, u, gothUser) } // createUserInContext creates a user and handles errors within a given context. // Optionally a template can be specified. -func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool, authType auth.Type) (ok bool) { +func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) { if err := user_model.CreateUser(ctx, u, overwrites); err != nil { if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) { if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { @@ -558,10 +541,10 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us } // TODO: probably we should respect 'remember' user's choice... - linkAccount(ctx, user, *gothUser, true, authType) + linkAccount(ctx, user, *gothUser, true) return false // user is already created here, all redirects are handled } else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin { - showLinkingLogin(ctx, *gothUser, authType) + showLinkingLogin(ctx, *gothUser) return false // user will be created only after linking login } } @@ -607,7 +590,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us // handleUserCreated does additional steps after a new user is created. // It auto-sets admin for the only user, updates the optional external user and // sends a confirmation email if required. -func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User, authType auth.Type) (ok bool) { +func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) { // Auto-set admin for the only user. if user_model.CountUsers(ctx, nil) == 1 { opts := &user_service.UpdateOptions{ @@ -623,7 +606,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. // update external user information if gothUser != nil { - if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser, authType); err != nil { + if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser); err != nil { if !errors.Is(err, util.ErrNotExist) { log.Error("UpdateExternalUser failed: %v", err) } diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index c62ae84083..1d94e52fe3 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -48,13 +48,13 @@ func LinkAccount(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" - externalLinkUser := ctx.Session.Get("linkAccountUser") - if externalLinkUser == nil { + gothUser := ctx.Session.Get("linkAccountGothUser") + if gothUser == nil { ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) return } - gu := externalLinkUser.(auth.LinkAccountUser).GothUser + gu, _ := gothUser.(goth.User) uname, err := getUserName(&gu) if err != nil { ctx.ServerError("UserSignIn", err) @@ -135,14 +135,12 @@ func LinkAccountPostSignIn(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" - externalLinkUserInterface := ctx.Session.Get("linkAccountUser") - if externalLinkUserInterface == nil { + gothUser := ctx.Session.Get("linkAccountGothUser") + if gothUser == nil { ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session")) return } - externalLinkUser := externalLinkUserInterface.(auth.LinkAccountUser) - if ctx.HasError() { ctx.HTML(http.StatusOK, tplLinkAccount) return @@ -154,10 +152,10 @@ func LinkAccountPostSignIn(ctx *context.Context) { return } - linkAccount(ctx, u, externalLinkUser.GothUser, signInForm.Remember, externalLinkUser.Type) + linkAccount(ctx, u, gothUser.(goth.User), signInForm.Remember) } -func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool, authType auth.Type) { +func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool) { updateAvatarIfNeed(ctx, gothUser.AvatarURL, u) // If this user is enrolled in 2FA, we can't sign the user in just yet. @@ -170,7 +168,7 @@ func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, r return } - err = externalaccount.LinkAccountToUser(ctx, u, gothUser, authType) + err = externalaccount.LinkAccountToUser(ctx, u, gothUser) if err != nil { ctx.ServerError("UserLinkAccount", err) return @@ -224,14 +222,14 @@ func LinkAccountPostRegister(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" - externalLinkUser := ctx.Session.Get("linkAccountUser") - if externalLinkUser == nil { + gothUserInterface := ctx.Session.Get("linkAccountGothUser") + if gothUserInterface == nil { ctx.ServerError("UserSignUp", errors.New("not in LinkAccount session")) return } - linkUser, ok := externalLinkUser.(auth.LinkAccountUser) + gothUser, ok := gothUserInterface.(goth.User) if !ok { - ctx.ServerError("UserSignUp", fmt.Errorf("session linkAccountUser type is %t but not goth.User", externalLinkUser)) + ctx.ServerError("UserSignUp", fmt.Errorf("session linkAccountGothUser type is %t but not goth.User", gothUserInterface)) return } @@ -277,7 +275,7 @@ func LinkAccountPostRegister(ctx *context.Context) { } } - authSource, err := auth.GetActiveAuthSourceByName(ctx, linkUser.GothUser.Provider, linkUser.Type) + authSource, err := auth.GetActiveOAuth2SourceByName(ctx, gothUser.Provider) if err != nil { ctx.ServerError("CreateUser", err) return @@ -287,24 +285,21 @@ func LinkAccountPostRegister(ctx *context.Context) { Name: form.UserName, Email: form.Email, Passwd: form.Password, - LoginType: authSource.Type, + LoginType: auth.OAuth2, LoginSource: authSource.ID, - LoginName: linkUser.GothUser.UserID, + LoginName: gothUser.UserID, } - if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, &linkUser.GothUser, false, linkUser.Type) { + if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, &gothUser, false) { // error already handled return } - if linkUser.Type == auth.OAuth2 { - source := authSource.Cfg.(*oauth2.Source) - if err := syncGroupsToTeams(ctx, source, &linkUser.GothUser, u); err != nil { - ctx.ServerError("SyncGroupsToTeams", err) - return - } + source := authSource.Cfg.(*oauth2.Source) + if err := syncGroupsToTeams(ctx, source, &gothUser, u); err != nil { + ctx.ServerError("SyncGroupsToTeams", err) + return } - // TODO we will support some form of group mapping for SAML handleSignIn(ctx, u, false) } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 5e7368eb9a..33a4ae9192 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -841,7 +841,7 @@ func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirect func SignInOAuth(ctx *context.Context) { provider := ctx.Params(":provider") - authSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.OAuth2) + authSource, err := auth.GetActiveOAuth2SourceByName(ctx, provider) if err != nil { ctx.ServerError("SignIn", err) return @@ -892,7 +892,7 @@ func SignInOAuthCallback(ctx *context.Context) { } // first look if the provider is still active - authSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.OAuth2) + authSource, err := auth.GetActiveOAuth2SourceByName(ctx, provider) if err != nil { ctx.ServerError("SignIn", err) return @@ -935,7 +935,7 @@ func SignInOAuthCallback(ctx *context.Context) { if u == nil { if ctx.Doer != nil { // attach user to already logged in user - err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser, auth.OAuth2) + err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser) if err != nil { ctx.ServerError("UserLinkAccount", err) return @@ -988,7 +988,7 @@ func SignInOAuthCallback(ctx *context.Context) { u.IsAdmin = isAdmin.ValueOrDefault(false) u.IsRestricted = isRestricted.ValueOrDefault(false) - if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled, auth.OAuth2) { + if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) { // error already handled return } @@ -999,7 +999,7 @@ func SignInOAuthCallback(ctx *context.Context) { } } else { // no existing user is found, request attach or new account - showLinkingLogin(ctx, gothUser, auth.OAuth2) + showLinkingLogin(ctx, gothUser) return } } @@ -1063,12 +1063,9 @@ func getUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, gothUser *g return isAdmin, isRestricted } -func showLinkingLogin(ctx *context.Context, gothUser goth.User, authType auth.Type) { +func showLinkingLogin(ctx *context.Context, gothUser goth.User) { if err := updateSession(ctx, nil, map[string]any{ - "linkAccountUser": auth.LinkAccountUser{ - Type: authType, - GothUser: gothUser, - }, + "linkAccountGothUser": gothUser, }); err != nil { ctx.ServerError("updateSession", err) return @@ -1147,7 +1144,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model } // update external user information - if err := externalaccount.UpdateExternalUser(ctx, u, gothUser, auth.OAuth2); err != nil { + if err := externalaccount.UpdateExternalUser(ctx, u, gothUser); err != nil { if !errors.Is(err, util.ErrNotExist) { log.Error("UpdateExternalUser failed: %v", err) } diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index bf377b4496..29ef772b1c 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -8,7 +8,6 @@ import ( "net/http" "net/url" - auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/base" @@ -364,7 +363,7 @@ func RegisterOpenIDPost(ctx *context.Context) { Email: form.Email, Passwd: password, } - if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil, false, auth_model.NoType) { + if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil, false) { // error already handled return } @@ -380,7 +379,7 @@ func RegisterOpenIDPost(ctx *context.Context) { return } - if !handleUserCreated(ctx, u, nil, auth_model.NoType) { + if !handleUserCreated(ctx, u, nil) { // error already handled return } diff --git a/routers/web/auth/saml.go b/routers/web/auth/saml.go deleted file mode 100644 index 29d689d2e9..0000000000 --- a/routers/web/auth/saml.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package auth - -import ( - "errors" - "fmt" - "net/http" - "strings" - - "code.gitea.io/gitea/models/auth" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/services/auth/source/saml" - "code.gitea.io/gitea/services/externalaccount" - - "github.com/markbates/goth" -) - -func SignInSAML(ctx *context.Context) { - provider := ctx.Params(":provider") - - loginSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.SAML) - if err != nil || loginSource == nil { - ctx.NotFound("SAMLMetadata", err) - return - } - - if err = loginSource.Cfg.(*saml.Source).Callout(ctx.Req, ctx.Resp); err != nil { - if strings.Contains(err.Error(), "no provider for ") { - ctx.Error(http.StatusNotFound) - return - } - ctx.ServerError("SignIn", err) - } -} - -func SignInSAMLCallback(ctx *context.Context) { - provider := ctx.Params(":provider") - loginSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.SAML) - if err != nil || loginSource == nil { - ctx.NotFound("SignInSAMLCallback", err) - return - } - - if loginSource == nil { - ctx.ServerError("SignIn", fmt.Errorf("no valid provider found, check configured callback url in provider")) - return - } - - u, gothUser, err := samlUserLoginCallback(*ctx, loginSource, ctx.Req, ctx.Resp) - if err != nil { - ctx.ServerError("SignInSAMLCallback", err) - return - } - - if u == nil { - if ctx.Doer != nil { - // attach user to already logged in user - err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser, auth.SAML) - if err != nil { - ctx.ServerError("LinkAccountToUser", err) - return - } - - ctx.Redirect(setting.AppSubURL + "/user/settings/security") - return - } else if !setting.Service.AllowOnlyInternalRegistration && false { - // TODO: allow auto registration from saml users (OAuth2 uses the following setting.OAuth2Client.EnableAutoRegistration) - } else { - // no existing user is found, request attach or new account - showLinkingLogin(ctx, gothUser, auth.SAML) - return - } - } - - handleSamlSignIn(ctx, loginSource, u, gothUser) -} - -func handleSamlSignIn(ctx *context.Context, source *auth.Source, u *user_model.User, gothUser goth.User) { - if err := updateSession(ctx, nil, map[string]any{ - "uid": u.ID, - "uname": u.Name, - }); err != nil { - ctx.ServerError("updateSession", err) - return - } - - // Clear whatever CSRF cookie has right now, force to generate a new one - ctx.Csrf.DeleteCookie(ctx) - - // Register last login - u.SetLastLogin() - - // update external user information - if err := externalaccount.UpdateExternalUser(ctx, u, gothUser, auth.SAML); err != nil { - if !errors.Is(err, util.ErrNotExist) { - log.Error("UpdateExternalUser failed: %v", err) - } - } - - if err := resetLocale(ctx, u); err != nil { - ctx.ServerError("resetLocale", err) - return - } - - if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 { - middleware.DeleteRedirectToCookie(ctx.Resp) - ctx.RedirectToFirst(redirectTo) - return - } - - ctx.Redirect(setting.AppSubURL + "/") -} - -func samlUserLoginCallback(ctx context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { - samlSource := authSource.Cfg.(*saml.Source) - - gothUser, err := samlSource.Callback(request, response) - if err != nil { - return nil, gothUser, err - } - - user := &user_model.User{ - LoginName: gothUser.UserID, - LoginType: auth.SAML, - LoginSource: authSource.ID, - } - - hasUser, err := user_model.GetUser(ctx, user) - if err != nil { - return nil, goth.User{}, err - } - - if hasUser { - return user, gothUser, nil - } - - // search in external linked users - externalLoginUser := &user_model.ExternalLoginUser{ - ExternalID: gothUser.UserID, - LoginSourceID: authSource.ID, - } - hasUser, err = user_model.GetExternalLogin(ctx, externalLoginUser) - if err != nil { - return nil, goth.User{}, err - } - if hasUser { - user, err = user_model.GetUserByID(request.Context(), externalLoginUser.UserID) - return user, gothUser, err - } - - // no user found to login - return nil, gothUser, nil -} - -func SAMLMetadata(ctx *context.Context) { - provider := ctx.Params(":provider") - loginSource, err := auth.GetActiveAuthSourceByName(ctx, provider, auth.SAML) - if err != nil || loginSource == nil { - ctx.NotFound("SAMLMetadata", err) - return - } - if err = loginSource.Cfg.(*saml.Source).Metadata(ctx.Req, ctx.Resp); err != nil { - ctx.ServerError("SAMLMetadata", err) - } -} diff --git a/routers/web/web.go b/routers/web/web.go index 5e18aac67d..a76b444e4f 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -667,11 +667,6 @@ func registerRoutes(m *web.Route) { m.Get("/{provider}", auth.SignInOAuth) m.Get("/{provider}/callback", auth.SignInOAuthCallback) }) - m.Group("/saml", func() { - m.Get("/{provider}", auth.SignInSAML) // redir to SAML IDP - m.Post("/{provider}/acs", auth.SignInSAMLCallback) - m.Get("/{provider}/metadata", auth.SAMLMetadata) - }) }) // ***** END: User ***** diff --git a/services/auth/source/saml/assert_interface_test.go b/services/auth/source/saml/assert_interface_test.go deleted file mode 100644 index 2ca7057b8a..0000000000 --- a/services/auth/source/saml/assert_interface_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml_test - -import ( - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/services/auth" - "code.gitea.io/gitea/services/auth/source/saml" -) - -// This test file exists to assert that our Source exposes the interfaces that we expect -// It tightly binds the interfaces and implementation without breaking go import cycles - -type sourceInterface interface { - auth_model.Config - auth_model.SourceSettable - auth_model.RegisterableSource - auth.PasswordAuthenticator -} - -var _ (sourceInterface) = &saml.Source{} diff --git a/services/auth/source/saml/init.go b/services/auth/source/saml/init.go deleted file mode 100644 index f1d6d9fa4b..0000000000 --- a/services/auth/source/saml/init.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -import ( - "context" - "sync" - - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/log" -) - -var samlRWMutex = sync.RWMutex{} - -func Init(ctx context.Context) error { - loginSources, _ := auth.GetActiveAuthProviderSources(ctx, auth.SAML) - for _, source := range loginSources { - samlSource, ok := source.Cfg.(*Source) - if !ok { - continue - } - err := samlSource.RegisterSource() - if err != nil { - log.Error("Unable to register source: %s due to Error: %v.", source.Name, err) - } - } - return nil -} diff --git a/services/auth/source/saml/name_id_format.go b/services/auth/source/saml/name_id_format.go deleted file mode 100644 index 1ddf047729..0000000000 --- a/services/auth/source/saml/name_id_format.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -type NameIDFormat int - -const ( - SAML11Email NameIDFormat = iota + 1 - SAML11Persistent - SAML11Unspecified - SAML20Email - SAML20Persistent - SAML20Transient - SAML20Unspecified -) - -const DefaultNameIDFormat NameIDFormat = SAML20Persistent - -var NameIDFormatNames = map[NameIDFormat]string{ - SAML11Email: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", - SAML11Persistent: "urn:oasis:names:tc:SAML:1.1:nameid-format:persistent", - SAML11Unspecified: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - SAML20Email: "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress", - SAML20Persistent: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", - SAML20Transient: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", - SAML20Unspecified: "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", -} - -// String returns the name of the NameIDFormat -func (n NameIDFormat) String() string { - return NameIDFormatNames[n] -} - -// Int returns the int value of the NameIDFormat -func (n NameIDFormat) Int() int { - return int(n) -} diff --git a/services/auth/source/saml/providers.go b/services/auth/source/saml/providers.go deleted file mode 100644 index d0b36ff44d..0000000000 --- a/services/auth/source/saml/providers.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -import ( - "context" - "fmt" - "html" - "html/template" - "io" - "net/http" - "sort" - "time" - - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/httplib" - "code.gitea.io/gitea/modules/svg" - "code.gitea.io/gitea/modules/util" -) - -// Providers is list of known/available providers. -type Providers map[string]Source - -var providers = Providers{} - -// Provider is an interface for describing a single SAML provider -type Provider interface { - Name() string - IconHTML(size int) template.HTML -} - -// AuthSourceProvider is a SAML provider -type AuthSourceProvider struct { - sourceName, iconURL string -} - -func (p *AuthSourceProvider) Name() string { - return p.sourceName -} - -func (p *AuthSourceProvider) IconHTML(size int) template.HTML { - if p.iconURL != "" { - return template.HTML(fmt.Sprintf(`%s`, - size, - size, - html.EscapeString(p.iconURL), html.EscapeString(p.Name()), - )) - } - return svg.RenderHTML("gitea-lock-cog", size, "gt-mr-3") -} - -func readIdentityProviderMetadata(ctx context.Context, source *Source) ([]byte, error) { - if source.IdentityProviderMetadata != "" { - return []byte(source.IdentityProviderMetadata), nil - } - - req := httplib.NewRequest(source.IdentityProviderMetadataURL, "GET") - req.SetTimeout(20*time.Second, time.Minute) - resp, err := req.Response() - if err != nil { - return nil, fmt.Errorf("Unable to contact gitea: %v", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, err - } - - data, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - return data, nil -} - -func createProviderFromSource(source *auth.Source) (Provider, error) { - samlCfg, ok := source.Cfg.(*Source) - if !ok { - return nil, fmt.Errorf("invalid SAML source config: %v", samlCfg) - } - return &AuthSourceProvider{sourceName: source.Name, iconURL: samlCfg.IconURL}, nil -} - -// GetSAMLProviders returns the list of configured SAML providers -func GetSAMLProviders(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) { - authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: isActive, - LoginType: auth.SAML, - }) - if err != nil { - return nil, err - } - - samlProviders := make([]Provider, 0, len(authSources)) - for _, source := range authSources { - p, err := createProviderFromSource(source) - if err != nil { - return nil, err - } - samlProviders = append(samlProviders, p) - } - - sort.Slice(samlProviders, func(i, j int) bool { - return samlProviders[i].Name() < samlProviders[j].Name() - }) - - return samlProviders, nil -} diff --git a/services/auth/source/saml/source.go b/services/auth/source/saml/source.go deleted file mode 100644 index 52388646b5..0000000000 --- a/services/auth/source/saml/source.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "encoding/xml" - "errors" - "fmt" - "math/big" - "net/url" - "time" - - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - - saml2 "github.com/russellhaering/gosaml2" - "github.com/russellhaering/gosaml2/types" - dsig "github.com/russellhaering/goxmldsig" -) - -// Source holds configuration for the SAML login source. -type Source struct { - // IdentityProviderMetadata description: The SAML Identity Provider metadata XML contents (for static configuration of the SAML Service Provider). The value of this field should be an XML document whose root element is `` or ``. To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. - IdentityProviderMetadata string - // IdentityProviderMetadataURL description: The SAML Identity Provider metadata URL (for dynamic configuration of the SAML Service Provider). - IdentityProviderMetadataURL string - // InsecureSkipAssertionSignatureValidation description: Whether the Service Provider should (insecurely) accept assertions from the Identity Provider without a valid signature. - InsecureSkipAssertionSignatureValidation bool - // NameIDFormat description: The SAML NameID format to use when performing user authentication. - NameIDFormat NameIDFormat - // ServiceProviderCertificate description: The SAML Service Provider certificate in X.509 encoding (begins with "-----BEGIN CERTIFICATE-----"). This certificate is used by the Identity Provider to validate the Service Provider's AuthnRequests and LogoutRequests. It corresponds to the Service Provider's private key (`serviceProviderPrivateKey`). To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. - ServiceProviderCertificate string - // ServiceProviderIssuer description: The SAML Service Provider name, used to identify this Service Provider. This is required if the "externalURL" field is not set (as the SAML metadata endpoint is computed as ".auth/saml/metadata"), or when using multiple SAML authentication providers. - ServiceProviderIssuer string - // ServiceProviderPrivateKey description: The SAML Service Provider private key in PKCS#8 encoding (begins with "-----BEGIN PRIVATE KEY-----"). This private key is used to sign AuthnRequests and LogoutRequests. It corresponds to the Service Provider's certificate (`serviceProviderCertificate`). To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. - ServiceProviderPrivateKey string - - CallbackURL string - IconURL string - - // EmailAssertionKey description: Assertion key for user.Email - EmailAssertionKey string - // NameAssertionKey description: Assertion key for user.NickName - NameAssertionKey string - // UsernameAssertionKey description: Assertion key for user.Name - UsernameAssertionKey string - - // reference to the authSource - authSource *auth.Source - - samlSP *saml2.SAMLServiceProvider -} - -func GenerateSAMLSPKeypair() (string, string, error) { - key, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - return "", "", err - } - - keyBytes := x509.MarshalPKCS1PrivateKey(key) - keyPem := pem.EncodeToMemory( - &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: keyBytes, - }, - ) - - now := time.Now() - - template := &x509.Certificate{ - SerialNumber: big.NewInt(0), - NotBefore: now.Add(-5 * time.Minute), - NotAfter: now.Add(365 * 24 * time.Hour), - - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{}, - BasicConstraintsValid: true, - } - - certificate, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) - if err != nil { - return "", "", err - } - - certPem := pem.EncodeToMemory( - &pem.Block{ - Type: "CERTIFICATE", - Bytes: certificate, - }, - ) - - return string(keyPem), string(certPem), nil -} - -func (source *Source) initSAMLSp() error { - source.CallbackURL = setting.AppURL + "user/saml/" + url.PathEscape(source.authSource.Name) + "/acs" - - idpMetadata, err := readIdentityProviderMetadata(context.Background(), source) - if err != nil { - return err - } - { - if source.IdentityProviderMetadataURL != "" { - log.Trace(fmt.Sprintf("Identity Provider metadata: %s", source.IdentityProviderMetadataURL), string(idpMetadata)) - } - } - - metadata := &types.EntityDescriptor{} - err = xml.Unmarshal(idpMetadata, metadata) - if err != nil { - return err - } - - certStore := dsig.MemoryX509CertificateStore{ - Roots: []*x509.Certificate{}, - } - - if metadata.IDPSSODescriptor == nil { - return errors.New("saml idp metadata missing IDPSSODescriptor") - } - - for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors { - for idx, xcert := range kd.KeyInfo.X509Data.X509Certificates { - if xcert.Data == "" { - return fmt.Errorf("metadata certificate(%d) must not be empty", idx) - } - certData, err := base64.StdEncoding.DecodeString(xcert.Data) - if err != nil { - return err - } - - idpCert, err := x509.ParseCertificate(certData) - if err != nil { - return err - } - - certStore.Roots = append(certStore.Roots, idpCert) - } - } - - var keyStore dsig.X509KeyStore - - if source.ServiceProviderCertificate != "" && source.ServiceProviderPrivateKey != "" { - keyPair, err := tls.X509KeyPair([]byte(source.ServiceProviderCertificate), []byte(source.ServiceProviderPrivateKey)) - if err != nil { - return err - } - keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) - if err != nil { - return err - } - keyStore = dsig.TLSCertKeyStore(keyPair) - } - - source.samlSP = &saml2.SAMLServiceProvider{ - IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location, - IdentityProviderIssuer: metadata.EntityID, - AudienceURI: setting.AppURL + "user/saml/" + url.PathEscape(source.authSource.Name) + "/metadata", - AssertionConsumerServiceURL: source.CallbackURL, - SkipSignatureValidation: source.InsecureSkipAssertionSignatureValidation, - NameIdFormat: source.NameIDFormat.String(), - IDPCertificateStore: &certStore, - SignAuthnRequests: source.ServiceProviderCertificate != "" && source.ServiceProviderPrivateKey != "", - SPKeyStore: keyStore, - ServiceProviderIssuer: setting.AppURL + "user/saml/" + url.PathEscape(source.authSource.Name) + "/metadata", - } - - return nil -} - -// FromDB fills up a SAML from serialized format. -func (source *Source) FromDB(bs []byte) error { - if err := json.UnmarshalHandleDoubleEncode(bs, &source); err != nil { - return err - } - - return source.initSAMLSp() -} - -// ToDB exports a SAML to a serialized format. -func (source *Source) ToDB() ([]byte, error) { - return json.Marshal(source) -} - -// SetAuthSource sets the related AuthSource -func (source *Source) SetAuthSource(authSource *auth.Source) { - source.authSource = authSource -} - -func init() { - auth.RegisterTypeConfig(auth.SAML, &Source{}) -} diff --git a/services/auth/source/saml/source_authenticate.go b/services/auth/source/saml/source_authenticate.go deleted file mode 100644 index d118917f87..0000000000 --- a/services/auth/source/saml/source_authenticate.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -import ( - "context" - - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/services/auth/source/db" -) - -// Authenticate falls back to the db authenticator -func (source *Source) Authenticate(ctx context.Context, user *user_model.User, login, password string) (*user_model.User, error) { - return db.Authenticate(ctx, user, login, password) -} diff --git a/services/auth/source/saml/source_callout.go b/services/auth/source/saml/source_callout.go deleted file mode 100644 index 5366f8a527..0000000000 --- a/services/auth/source/saml/source_callout.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -import ( - "fmt" - "net/http" - "strings" - - "github.com/markbates/goth" -) - -// Callout redirects request/response pair to authenticate against the provider -func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error { - samlRWMutex.RLock() - defer samlRWMutex.RUnlock() - if _, ok := providers[source.authSource.Name]; !ok { - return fmt.Errorf("no provider for this saml") - } - - authURL, err := providers[source.authSource.Name].samlSP.BuildAuthURL("") - if err == nil { - http.Redirect(response, request, authURL, http.StatusTemporaryRedirect) - } - return err -} - -// Callback handles SAML callback, resolve to a goth user and send back to original url -// this will trigger a new authentication request, but because we save it in the session we can use that -func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) { - samlRWMutex.RLock() - defer samlRWMutex.RUnlock() - - user := goth.User{ - Provider: source.authSource.Name, - } - samlResponse := request.FormValue("SAMLResponse") - assertions, err := source.samlSP.RetrieveAssertionInfo(samlResponse) - if err != nil { - return user, err - } - - if assertions.WarningInfo.OneTimeUse { - return user, fmt.Errorf("SAML response contains one time use warning") - } - - if assertions.WarningInfo.ProxyRestriction != nil { - return user, fmt.Errorf("SAML response contains proxy restriction warning: %v", assertions.WarningInfo.ProxyRestriction) - } - - if assertions.WarningInfo.NotInAudience { - return user, fmt.Errorf("SAML response contains audience warning") - } - - if assertions.WarningInfo.InvalidTime { - return user, fmt.Errorf("SAML response contains invalid time warning") - } - - samlMap := make(map[string]string) - for key, value := range assertions.Values { - keyParsed := strings.ToLower(key[strings.LastIndex(key, "/")+1:]) // Uses the trailing slug as the key name. - valueParsed := value.Values[0].Value - samlMap[keyParsed] = valueParsed - - } - - user.UserID = assertions.NameID - if user.UserID == "" { - return user, fmt.Errorf("no nameID found in SAML response") - } - - // email - if _, ok := samlMap[source.EmailAssertionKey]; !ok { - user.Email = samlMap[source.EmailAssertionKey] - } - // name - if _, ok := samlMap[source.NameAssertionKey]; !ok { - user.NickName = samlMap[source.NameAssertionKey] - } - // username - if _, ok := samlMap[source.UsernameAssertionKey]; !ok { - user.Name = samlMap[source.UsernameAssertionKey] - } - - // TODO: utilize groups once mapping is supported - - return user, nil -} diff --git a/services/auth/source/saml/source_metadata.go b/services/auth/source/saml/source_metadata.go deleted file mode 100644 index 9fb8c758e3..0000000000 --- a/services/auth/source/saml/source_metadata.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -import ( - "encoding/xml" - "fmt" - "net/http" -) - -// Metadata redirects request/response pair to authenticate against the provider -func (source *Source) Metadata(request *http.Request, response http.ResponseWriter) error { - samlRWMutex.RLock() - defer samlRWMutex.RUnlock() - if _, ok := providers[source.authSource.Name]; !ok { - return fmt.Errorf("provider does not exist") - } - - metadata, err := providers[source.authSource.Name].samlSP.Metadata() - if err != nil { - return err - } - buf, err := xml.Marshal(metadata) - if err != nil { - return err - } - - response.Header().Set("Content-Type", "application/samlmetadata+xml; charset=utf-8") - _, _ = response.Write(buf) - return nil -} diff --git a/services/auth/source/saml/source_register.go b/services/auth/source/saml/source_register.go deleted file mode 100644 index 93eaaa88b6..0000000000 --- a/services/auth/source/saml/source_register.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package saml - -// RegisterSource causes an OAuth2 configuration to be registered -func (source *Source) RegisterSource() error { - samlRWMutex.Lock() - defer samlRWMutex.Unlock() - if err := source.initSAMLSp(); err != nil { - return err - } - providers[source.authSource.Name] = *source - return nil -} - -// UnregisterSource causes an SAML configuration to be unregistered -func (source *Source) UnregisterSource() error { - samlRWMutex.Lock() - defer samlRWMutex.Unlock() - delete(providers, source.authSource.Name) - return nil -} diff --git a/services/externalaccount/link.go b/services/externalaccount/link.go index 1f4c6728b8..d6e2ea7e94 100644 --- a/services/externalaccount/link.go +++ b/services/externalaccount/link.go @@ -7,8 +7,9 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" + + "github.com/markbates/goth" ) // Store represents a thing that stores things @@ -20,12 +21,10 @@ type Store interface { // LinkAccountFromStore links the provided user with a stored external user func LinkAccountFromStore(ctx context.Context, store Store, user *user_model.User) error { - externalLinkUserInterface := store.Get("linkAccountUser") - if externalLinkUserInterface == nil { + gothUser := store.Get("linkAccountGothUser") + if gothUser == nil { return fmt.Errorf("not in LinkAccount session") } - externalLinkUser := externalLinkUserInterface.(auth.LinkAccountUser) - - return LinkAccountToUser(ctx, user, externalLinkUser.GothUser, externalLinkUser.Type) + return LinkAccountToUser(ctx, user, gothUser.(goth.User)) } diff --git a/services/externalaccount/user.go b/services/externalaccount/user.go index fa85a65669..e2de41da18 100644 --- a/services/externalaccount/user.go +++ b/services/externalaccount/user.go @@ -16,8 +16,8 @@ import ( "github.com/markbates/goth" ) -func toExternalLoginUser(ctx context.Context, user *user_model.User, gothUser goth.User, authType auth.Type) (*user_model.ExternalLoginUser, error) { - authSource, err := auth.GetActiveAuthSourceByName(ctx, gothUser.Provider, authType) +func toExternalLoginUser(ctx context.Context, user *user_model.User, gothUser goth.User) (*user_model.ExternalLoginUser, error) { + authSource, err := auth.GetActiveOAuth2SourceByName(ctx, gothUser.Provider) if err != nil { return nil, err } @@ -43,8 +43,8 @@ func toExternalLoginUser(ctx context.Context, user *user_model.User, gothUser go } // LinkAccountToUser link the gothUser to the user -func LinkAccountToUser(ctx context.Context, user *user_model.User, gothUser goth.User, authType auth.Type) error { - externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser, authType) +func LinkAccountToUser(ctx context.Context, user *user_model.User, gothUser goth.User) error { + externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser) if err != nil { return err } @@ -71,8 +71,8 @@ func LinkAccountToUser(ctx context.Context, user *user_model.User, gothUser goth } // UpdateExternalUser updates external user's information -func UpdateExternalUser(ctx context.Context, user *user_model.User, gothUser goth.User, authType auth.Type) error { - externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser, authType) +func UpdateExternalUser(ctx context.Context, user *user_model.User, gothUser goth.User) error { + externalLoginUser, err := toExternalLoginUser(ctx, user, gothUser) if err != nil { return err } diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 85be38b403..25acbbb99e 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -1,4 +1,3 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved. // SPDX-License-Identifier: MIT @@ -16,7 +15,7 @@ import ( // AuthenticationForm form for authentication type AuthenticationForm struct { ID int64 - Type int `binding:"Range(2,9)"` + Type int `binding:"Range(2,7)"` Name string `binding:"Required;MaxSize(30)"` Host string Port int @@ -83,18 +82,6 @@ type AuthenticationForm struct { SSPIDefaultLanguage string GroupTeamMap string `binding:"ValidGroupTeamMap"` GroupTeamMapRemoval bool - - // SAML Settings - NameIDFormat int - IdentityProviderMetadata string - IdentityProviderMetadataURL string - InsecureSkipAssertionSignatureValidation bool - ServiceProviderCertificate string - ServiceProviderPrivateKey string - EmailAssertionKey string - NameAssertionKey string - UsernameAssertionKey string - SAMLIconURL string } // Validate validates fields diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 2182d011e9..25abefae00 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -367,69 +367,6 @@
{{end}} - - {{if .Source.IsSAML}} - {{$cfg:=.Source.Cfg}} -
- - -
- -
- - -
- -
- - -
-
- - -
- -
-
- - -
-
- -
- - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- {{end}} - {{if .Source.IsSSPI}} {{$cfg:=.Source.Cfg}} @@ -504,9 +441,6 @@
GMail Settings:

Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true

-
SAML Settings:
-

{{ctx.Locale.Tr "admin.auths.tips.saml"}}

-
{{ctx.Locale.Tr "admin.auths.tips.oauth2.general"}}:

{{ctx.Locale.Tr "admin.auths.tips.oauth2.general.tip"}}

diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index 665b0e3086..f32f77d5dc 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -53,9 +53,6 @@ {{template "admin/auth/source/sspi" .}} - - {{template "admin/auth/source/saml" .}} -
@@ -88,9 +85,6 @@
GMail Settings:

Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true

-
SAML Settings:
-

{{ctx.Locale.Tr "admin.auths.tips.saml"}}

-
{{ctx.Locale.Tr "admin.auths.tips.oauth2.general"}}:

{{ctx.Locale.Tr "admin.auths.tips.oauth2.general.tip"}}

diff --git a/templates/admin/auth/source/saml.tmpl b/templates/admin/auth/source/saml.tmpl deleted file mode 100644 index 050e22ddcc..0000000000 --- a/templates/admin/auth/source/saml.tmpl +++ /dev/null @@ -1,62 +0,0 @@ -
- -
- - -
- -
- - -
- -
- - -
-
- - -
- -
-
- - -
-
- -
- - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index 1b4e2b25f9..0d0064b02a 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -69,22 +69,5 @@
{{end}} - {{if .SAMLProviders}} -
- {{.locale.Tr "sign_in_or"}} -
-
-
-
- {{range $provider := .SAMLProviders}} - - {{end}} -
-
-
- {{end}}
diff --git a/tests/integration/README.md b/tests/integration/README.md index c691483511..f6f74ca21f 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -110,20 +110,3 @@ SLOW_FLUSH = 5S ; 5s is the default value ```bash GITEA_SLOW_TEST_TIME="10s" GITEA_SLOW_FLUSH_TIME="5s" make test-sqlite ``` - -## Running SimpleSAML for testing SAML locally - -```shell -docker run \ --p 8080:8080 \ --p 8443:8443 \ --e SIMPLESAMLPHP_SP_ENTITY_ID=http://localhost:3003/user/saml/test-sp/metadata \ --e SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE=http://localhost:3003/user/saml/test-sp/acs \ --e SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE=http://localhost:3003/user/saml/test-sp/acs \ ---add-host=localhost:192.168.65.2 \ --d allspice/simple-saml -``` - -```shell -TEST_SIMPLESAML_URL=localhost:8080 make test-sqlite#TestSAMLRegistration -``` diff --git a/tests/integration/saml_test.go b/tests/integration/saml_test.go deleted file mode 100644 index 585fd35c5f..0000000000 --- a/tests/integration/saml_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "net/http" - "net/http/cookiejar" - "net/url" - "os" - "regexp" - "strings" - "testing" - "time" - - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/services/auth/source/saml" - "code.gitea.io/gitea/tests" - - "github.com/stretchr/testify/assert" -) - -func TestSAMLRegistration(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - samlURL := "localhost:8080" - - if os.Getenv("CI") == "" || !setting.Database.Type.IsPostgreSQL() { - // Make it possible to run tests against a local simplesaml instance - samlURL = os.Getenv("TEST_SIMPLESAML_URL") - if samlURL == "" { - t.Skip("TEST_SIMPLESAML_URL not set and not running in CI") - return - } - } - - privateKey, cert, err := saml.GenerateSAMLSPKeypair() - assert.NoError(t, err) - - // verify that the keypair can be parsed - keyPair, err := tls.X509KeyPair([]byte(cert), []byte(privateKey)) - assert.NoError(t, err) - keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) - assert.NoError(t, err) - - assert.NoError(t, auth.CreateSource(db.DefaultContext, &auth.Source{ - Type: auth.SAML, - Name: "test-sp", - IsActive: true, - IsSyncEnabled: false, - Cfg: &saml.Source{ - IdentityProviderMetadata: "", - IdentityProviderMetadataURL: fmt.Sprintf("http://%s/simplesaml/saml2/idp/metadata.php", samlURL), - InsecureSkipAssertionSignatureValidation: false, - NameIDFormat: 4, - ServiceProviderCertificate: "", // SimpleSAMLPhp requires that the SP certificate be specified in the server configuration rather than SP metadata - ServiceProviderPrivateKey: "", - EmailAssertionKey: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", - NameAssertionKey: "http://schemas.xmlsoap.org/claims/CommonName", - UsernameAssertionKey: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", - IconURL: "", - }, - })) - - // check the saml metadata url - req := NewRequest(t, "GET", "/user/saml/test-sp/metadata") - MakeRequest(t, req, http.StatusOK) - - req = NewRequest(t, "GET", "/user/saml/test-sp") - resp := MakeRequest(t, req, http.StatusTemporaryRedirect) - - jar, err := cookiejar.New(nil) - assert.NoError(t, err) - - client := http.Client{ - Timeout: 30 * time.Second, - Jar: jar, - } - - httpReq, err := http.NewRequest("GET", test.RedirectURL(resp), nil) - assert.NoError(t, err) - - var formRedirectURL *url.URL - client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - // capture the redirected destination to use in POST request - formRedirectURL = req.URL - return nil - } - - res, err := client.Do(httpReq) - client.CheckRedirect = nil - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, res.StatusCode) - assert.NotNil(t, formRedirectURL) - - form := url.Values{ - "username": {"user1"}, - "password": {"user1pass"}, - } - - httpReq, err = http.NewRequest("POST", formRedirectURL.String(), strings.NewReader(form.Encode())) - assert.NoError(t, err) - httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - res, err = client.Do(httpReq) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, res.StatusCode) - - body, err := io.ReadAll(res.Body) - assert.NoError(t, err) - - samlResMatcher := regexp.MustCompile(``) - matches := samlResMatcher.FindStringSubmatch(string(body)) - assert.Len(t, matches, 2) - assert.NoError(t, res.Body.Close()) - - session := emptyTestSession(t) - - req = NewRequestWithValues(t, "POST", "/user/saml/test-sp/acs", map[string]string{ - "SAMLResponse": matches[1], - }) - resp = session.MakeRequest(t, req, http.StatusSeeOther) - assert.Equal(t, test.RedirectURL(resp), "/user/link_account") - - csrf := GetCSRF(t, session, test.RedirectURL(resp)) - - // link the account - req = NewRequestWithValues(t, "POST", "/user/link_account_signup", map[string]string{ - "_csrf": csrf, - "user_name": "samluser", - "email": "saml@example.com", - }) - - resp = session.MakeRequest(t, req, http.StatusSeeOther) - assert.Equal(t, test.RedirectURL(resp), "/") - - // verify that the user was created - u, err := user_model.GetUserByEmail(db.DefaultContext, "saml@example.com") - assert.NoError(t, err) - assert.NotNil(t, u) - assert.Equal(t, "samluser", u.Name) -} diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index 4804163971..044976ea7b 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -103,9 +103,9 @@ export function initAdminCommon() { // New authentication if ($('.admin.new.authentication').length > 0) { $('#auth_type').on('change', function () { - hideElem($('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi, .saml')); + hideElem($('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi')); - $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required], .saml input[required]').removeAttr('required'); + $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]').removeAttr('required'); $('.binddnrequired').removeClass('required'); const authType = $(this).val(); @@ -137,10 +137,6 @@ export function initAdminCommon() { showElem($('.sspi')); $('.sspi div.required input').attr('required', 'required'); break; - case '8': // SAML - showElem($('.saml')); - $('.saml div.required input').attr('required', 'required'); - break; } if (authType === '2' || authType === '5') { onSecurityProtocolChange(); diff --git a/web_src/js/features/user-auth.js b/web_src/js/features/user-auth.js index 3bf84e31df..60d186e699 100644 --- a/web_src/js/features/user-auth.js +++ b/web_src/js/features/user-auth.js @@ -20,24 +20,3 @@ export function initUserAuthOauth2() { }); } } - -export function initUserAuthSAML() { - const outer = document.getElementById('saml-login-navigator'); - if (!outer) return; - const inner = document.getElementById('saml-login-navigator-inner'); - - checkAppUrl(); - - for (const link of outer.querySelectorAll('.saml-login-link')) { - link.addEventListener('click', () => { - inner.classList.add('gt-invisible'); - outer.classList.add('is-loading'); - setTimeout(() => { - // recover previous content to let user try again - // usually redirection will be performed before this action - outer.classList.remove('is-loading'); - inner.classList.remove('gt-invisible'); - }, 5000); - }); - } -} diff --git a/web_src/js/index.js b/web_src/js/index.js index 876e4291ee..d9cfff4084 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -23,10 +23,7 @@ import {initFindFileInRepo} from './features/repo-findfile.js'; import {initCommentContent, initMarkupContent} from './markup/content.js'; import {initPdfViewer} from './render/pdf.js'; -import { - initUserAuthOauth2, - initUserAuthSAML -} from './features/user-auth.js'; +import {initUserAuthOauth2} from './features/user-auth.js'; import { initRepoIssueDue, initRepoIssueReferenceRepositorySearch, @@ -184,7 +181,6 @@ onDomReady(() => { initCaptcha(); initUserAuthOauth2(); - initUserAuthSAML(); initUserAuthWebAuthn(); initUserAuthWebAuthnRegister(); initUserSettings(); From b79c30435f439af8243ee281310258cdf141e27b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 24 Feb 2024 14:55:19 +0800 Subject: [PATCH 19/79] Use the database object format name but not read from git repoisitory everytime and fix possible migration wrong objectformat when migrating a sha256 repository (#29294) Now we can get object format name from git command line or from the database repository table. Assume the column is right, we don't need to read from git command line every time. This also fixed a possible bug that the object format is wrong when migrating a sha256 repository from external. image --- modules/context/api.go | 9 ++------- modules/context/repo.go | 20 ++++++++------------ routers/api/v1/utils/git.go | 2 +- routers/private/hook_pre_receive.go | 2 +- routers/web/repo/blame.go | 7 ++----- routers/web/repo/compare.go | 4 ++-- routers/web/repo/setting/lfs.go | 2 +- services/agit/agit.go | 3 +-- services/migrations/gitea_uploader.go | 16 +++++++++++++--- services/pull/check.go | 5 +---- services/pull/merge.go | 2 +- services/release/release.go | 2 +- services/repository/branch.go | 7 ++----- services/repository/files/commit.go | 6 ++---- services/repository/files/tree.go | 2 +- services/repository/lfs.go | 2 +- services/repository/push.go | 6 +----- 17 files changed, 41 insertions(+), 56 deletions(-) diff --git a/modules/context/api.go b/modules/context/api.go index f8bc682fed..b18a206b5e 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -307,12 +307,6 @@ func RepoRefForAPI(next http.Handler) http.Handler { return } - objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat() - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetCommit", err) - return - } - if ref := ctx.FormTrim("ref"); len(ref) > 0 { commit, err := ctx.Repo.GitRepo.GetCommit(ref) if err != nil { @@ -331,6 +325,7 @@ func RepoRefForAPI(next http.Handler) http.Handler { } refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny) + var err error if ctx.Repo.GitRepo.IsBranchExist(refName) { ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) @@ -346,7 +341,7 @@ func RepoRefForAPI(next http.Handler) http.Handler { return } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if len(refName) == objectFormat.FullLength() { + } else if len(refName) == ctx.Repo.GetObjectFormat().FullLength() { ctx.Repo.CommitID = refName ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) if err != nil { diff --git a/modules/context/repo.go b/modules/context/repo.go index 8508d46cf4..a73d09ee21 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -83,6 +83,10 @@ func (r *Repository) CanCreateBranch() bool { return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch() } +func (r *Repository) GetObjectFormat() git.ObjectFormat { + return git.ObjectFormatFromName(r.Repository.ObjectFormatName) +} + // RepoMustNotBeArchived checks if a repo is archived func RepoMustNotBeArchived() func(ctx *Context) { return func(ctx *Context) { @@ -830,9 +834,8 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { } // For legacy and API support only full commit sha parts := strings.Split(path, "/") - objectFormat, _ := repo.GitRepo.GetObjectFormat() - if len(parts) > 0 && len(parts[0]) == objectFormat.FullLength() { + if len(parts) > 0 && len(parts[0]) == git.ObjectFormatFromName(repo.Repository.ObjectFormatName).FullLength() { repo.TreePath = strings.Join(parts[1:], "/") return parts[0] } @@ -876,9 +879,8 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist) case RepoRefCommit: parts := strings.Split(path, "/") - objectFormat, _ := repo.GitRepo.GetObjectFormat() - if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= objectFormat.FullLength() { + if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= repo.GetObjectFormat().FullLength() { repo.TreePath = strings.Join(parts[1:], "/") return parts[0] } @@ -937,12 +939,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context } } - objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat() - if err != nil { - log.Error("Cannot determine objectFormat for repository: %w", err) - ctx.Repo.Repository.MarkAsBrokenEmpty() - } - // Get default branch. if len(ctx.Params("*")) == 0 { refName = ctx.Repo.Repository.DefaultBranch @@ -1009,7 +1005,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context return cancel } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if len(refName) >= 7 && len(refName) <= objectFormat.FullLength() { + } else if len(refName) >= 7 && len(refName) <= ctx.Repo.GetObjectFormat().FullLength() { ctx.Repo.IsViewCommit = true ctx.Repo.CommitID = refName @@ -1019,7 +1015,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context return cancel } // If short commit ID add canonical link header - if len(refName) < objectFormat.FullLength() { + if len(refName) < ctx.Repo.GetObjectFormat().FullLength() { ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"", util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1)))) } diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index 2299cdc247..5e80190017 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -72,7 +72,7 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str // ConvertToObjectID returns a full-length SHA1 from a potential ID string func ConvertToObjectID(ctx gocontext.Context, repo *context.Repository, commitID string) (git.ObjectID, error) { - objectFormat, _ := repo.GitRepo.GetObjectFormat() + objectFormat := repo.GetObjectFormat() if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) { sha, err := git.NewIDFromString(commitID) if err == nil { diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 90d8287f06..f28ae4c0eb 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -145,7 +145,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r repo := ctx.Repo.Repository gitRepo := ctx.Repo.GitRepo - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := ctx.Repo.GetObjectFormat() if branchName == repo.DefaultBranch && newCommitID == objectFormat.EmptyObjectID().String() { log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo) diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index c7875ea0cb..7602b30d2b 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -132,11 +132,8 @@ type blameResult struct { } func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) { - objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat() - if err != nil { - ctx.NotFound("CreateBlameReader", err) - return nil, err - } + objectFormat := ctx.Repo.GetObjectFormat() + blameReader, err := git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, bypassBlameIgnore) if err != nil { return nil, err diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index df41c750de..535487d5fd 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -312,14 +312,14 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch) baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch) baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch) - objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat() + if !baseIsCommit && !baseIsBranch && !baseIsTag { // Check if baseBranch is short sha commit hash if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil { ci.BaseBranch = baseCommit.ID.String() ctx.Data["BaseBranch"] = ci.BaseBranch baseIsCommit = true - } else if ci.BaseBranch == objectFormat.EmptyObjectID().String() { + } else if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() { if isSameRepo { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch)) } else { diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index cd0f11d548..76a90a4ac5 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -388,7 +388,7 @@ func LFSFileFind(ctx *context.Context) { sha := ctx.FormString("sha") ctx.Data["Title"] = oid ctx.Data["PageIsSettingsLFS"] = true - objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat() + objectFormat := ctx.Repo.GetObjectFormat() var objectID git.ObjectID if len(sha) == 0 { pointer := lfs.Pointer{Oid: oid, Size: size} diff --git a/services/agit/agit.go b/services/agit/agit.go index 75b561581d..2233fe8547 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -36,7 +36,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. topicBranch = opts.GitPushOptions["topic"] _, forcePush = opts.GitPushOptions["force-push"] - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) pusher, err := user_model.GetUserByID(ctx, opts.UserID) if err != nil { @@ -149,7 +149,6 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID) - objectFormat, _ := gitRepo.GetObjectFormat() results = append(results, private.HookProcReceiveRefResult{ Ref: pr.GetGitRefName(), OriginalRef: opts.RefFullNames[i], diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 2891977c7c..468be6c9df 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -140,8 +140,18 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate if err != nil { return err } - g.gitRepo, err = gitrepo.OpenRepository(g.ctx, r) - return err + g.gitRepo, err = gitrepo.OpenRepository(g.ctx, g.repo) + if err != nil { + return err + } + + // detect object format from git repository and update to database + objectFormat, err := g.gitRepo.GetObjectFormat() + if err != nil { + return err + } + g.repo.ObjectFormatName = objectFormat.Name() + return repo_model.UpdateRepositoryCols(g.ctx, g.repo, "object_format_name") } // Close closes this uploader @@ -896,7 +906,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error { comment.UpdatedAt = comment.CreatedAt } - objectFormat, _ := g.gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(g.repo.ObjectFormatName) if !objectFormat.IsValid(comment.CommitID) { log.Warn("Invalid comment CommitID[%s] on comment[%d] in PR #%d of %s/%s replaced with %s", comment.CommitID, pr.Index, g.repoOwner, g.repoName, headCommitID) comment.CommitID = headCommitID diff --git a/services/pull/check.go b/services/pull/check.go index dd6c3ed230..f4dd332b14 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -222,10 +222,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com } defer gitRepo.Close() - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return nil, fmt.Errorf("%-v GetObjectFormat: %w", pr.BaseRepo, err) - } + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) // Get the commit from BaseBranch where the pull request got merged mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse"). diff --git a/services/pull/merge.go b/services/pull/merge.go index d4c0c821d6..e37540a96f 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -497,7 +497,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged} } - objectFormat, _ := baseGitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) if len(commitID) != objectFormat.FullLength() { return fmt.Errorf("Wrong commit ID") } diff --git a/services/release/release.go b/services/release/release.go index 4c522c18be..a359e5078e 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -88,7 +88,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel created = true rel.LowerTagName = strings.ToLower(rel.TagName) - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(rel.Repo.ObjectFormatName) commits := repository.NewPushCommits() commits.HeadCommit = repository.CommitToPushCommit(commit) commits.CompareURL = rel.Repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), commit.ID.String()) diff --git a/services/repository/branch.go b/services/repository/branch.go index 38781acb58..ec41173da8 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -380,11 +380,6 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R return fmt.Errorf("GetBranch: %vc", err) } - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return err - } - if rawBranch.IsDeleted { return nil } @@ -406,6 +401,8 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R return err } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + // Don't return error below this if err := PushUpdate( &repo_module.PushUpdateOptions{ diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 16a15e06a7..512aec7c81 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -30,10 +30,8 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato } defer closer.Close() - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return fmt.Errorf("GetObjectFormat[%s]: %w", repoPath, err) - } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + commit, err := gitRepo.GetCommit(sha) if err != nil { gitRepo.Close() diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index 9d3185c3fc..e3a7f3b8b0 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -37,7 +37,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git } apiURL := repo.APIURL() apiURLLen := len(apiURL) - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) hashLen := objectFormat.FullLength() const gitBlobsPath = "/git/blobs/" diff --git a/services/repository/lfs.go b/services/repository/lfs.go index 4504f796bd..4d48881b87 100644 --- a/services/repository/lfs.go +++ b/services/repository/lfs.go @@ -79,7 +79,7 @@ func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.R store := lfs.NewContentStore() errStop := errors.New("STOPERR") - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) err = git_model.IterateLFSMetaObjectsForRepo(ctx, repo.ID, func(ctx context.Context, metaObject *git_model.LFSMetaObject, count int64) error { if opts.NumberToCheckPerRepo > 0 && total > opts.NumberToCheckPerRepo { diff --git a/services/repository/push.go b/services/repository/push.go index c76025b6a7..9aaf0e1c9b 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -93,11 +93,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } defer gitRepo.Close() - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return fmt.Errorf("unknown repository ObjectFormat [%s]: %w", repo.FullName(), err) - } - if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { return fmt.Errorf("Failed to update size for repository: %v", err) } @@ -105,6 +100,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { addTags := make([]string, 0, len(optsList)) delTags := make([]string, 0, len(optsList)) var pusher *user_model.User + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) for _, opts := range optsList { log.Trace("pushUpdates: %-v %s %s %s", repo, opts.OldCommitID, opts.NewCommitID, opts.RefFullName) From 6e5966597c2d498d1a8540dad965461d44ff8e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Br=C3=BCckner?= Date: Sat, 24 Feb 2024 07:49:16 +0000 Subject: [PATCH 20/79] Properly migrate target branch change GitLab comment (#29340) GitLab generates "system notes" whenever an event happens within the platform. Unlike Gitea, those events are stored and retrieved as text comments with no semantic details. The only way to tell whether a comment was generated in this manner is the `system` flag on the note type. This PR adds detection for a new specific kind of event: Changing the target branch of a PR. When detected, it is downloaded using Gitea's type for this event, and eventually uploaded into Gitea in the expected format, i.e. with no text content in the comment. This PR also updates the template used to render comments to add support for migrated comments of this type. ref: https://gitlab.com/gitlab-org/gitlab/-/blob/11bd6dc826e0bea2832324a1d7356949a9398884/app/services/system_notes/merge_requests_service.rb#L102 --- services/migrations/gitea_uploader.go | 10 ++++++++-- services/migrations/gitlab.go | 10 +++++++++- services/migrations/gitlab_test.go | 19 ++++++++++++++++++- .../repo/issue/view_content/comments.tmpl | 19 +++++++++++++++---- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 468be6c9df..8bcf483947 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -492,10 +492,16 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { } case issues_model.CommentTypeChangeTitle: if comment.Meta["OldTitle"] != nil { - cm.OldTitle = fmt.Sprintf("%s", comment.Meta["OldTitle"]) + cm.OldTitle = fmt.Sprint(comment.Meta["OldTitle"]) } if comment.Meta["NewTitle"] != nil { - cm.NewTitle = fmt.Sprintf("%s", comment.Meta["NewTitle"]) + cm.NewTitle = fmt.Sprint(comment.Meta["NewTitle"]) + } + case issues_model.CommentTypeChangeTargetBranch: + if comment.Meta["OldRef"] != nil && comment.Meta["NewRef"] != nil { + cm.OldRef = fmt.Sprint(comment.Meta["OldRef"]) + cm.NewRef = fmt.Sprint(comment.Meta["NewRef"]) + cm.Content = "" } case issues_model.CommentTypePRScheduledToAutoMerge, issues_model.CommentTypePRUnScheduledToAutoMerge: cm.Content = "" diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index d08eaf0f84..5e49ae6d57 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -11,6 +11,7 @@ import ( "net/http" "net/url" "path" + "regexp" "strings" "time" @@ -519,6 +520,8 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co return allComments, true, nil } +var targetBranchChangeRegexp = regexp.MustCompile("^changed target branch from `(.*?)` to `(.*?)`$") + func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.Note) *base.Comment { comment := &base.Comment{ IssueIndex: localIndex, @@ -528,11 +531,16 @@ func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.N PosterEmail: note.Author.Email, Content: note.Body, Created: *note.CreatedAt, + Meta: map[string]any{}, } // Try to find the underlying event of system notes. if note.System { - if strings.HasPrefix(note.Body, "enabled an automatic merge") { + if match := targetBranchChangeRegexp.FindStringSubmatch(note.Body); match != nil { + comment.CommentType = issues_model.CommentTypeChangeTargetBranch.String() + comment.Meta["OldRef"] = match[1] + comment.Meta["NewRef"] = match[2] + } else if strings.HasPrefix(note.Body, "enabled an automatic merge") { comment.CommentType = issues_model.CommentTypePRScheduledToAutoMerge.String() } else if note.Body == "canceled the automatic merge" { comment.CommentType = issues_model.CommentTypePRUnScheduledToAutoMerge.String() diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go index 2b87a1dfe6..0b9eeaed54 100644 --- a/services/migrations/gitlab_test.go +++ b/services/migrations/gitlab_test.go @@ -545,7 +545,8 @@ func TestNoteToComment(t *testing.T) { notes := []gitlab.Note{ makeTestNote(1, "This is a regular comment", false), makeTestNote(2, "enabled an automatic merge for abcd1234", true), - makeTestNote(3, "canceled the automatic merge", true), + makeTestNote(3, "changed target branch from `master` to `main`", true), + makeTestNote(4, "canceled the automatic merge", true), } comments := []base.Comment{{ IssueIndex: 17, @@ -556,6 +557,7 @@ func TestNoteToComment(t *testing.T) { CommentType: "", Content: "This is a regular comment", Created: now, + Meta: map[string]any{}, }, { IssueIndex: 17, Index: 2, @@ -565,15 +567,30 @@ func TestNoteToComment(t *testing.T) { CommentType: "pull_scheduled_merge", Content: "enabled an automatic merge for abcd1234", Created: now, + Meta: map[string]any{}, }, { IssueIndex: 17, Index: 3, PosterID: 72, PosterName: "test", PosterEmail: "test@example.com", + CommentType: "change_target_branch", + Content: "changed target branch from `master` to `main`", + Created: now, + Meta: map[string]any{ + "OldRef": "master", + "NewRef": "main", + }, + }, { + IssueIndex: 17, + Index: 4, + PosterID: 72, + PosterName: "test", + PosterEmail: "test@example.com", CommentType: "pull_cancel_scheduled_merge", Content: "canceled the automatic merge", Created: now, + Meta: map[string]any{}, }} for i, note := range notes { diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 597f025470..7bd7e8c35d 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -365,8 +365,7 @@ {{else if eq .Type 22}}
- {{if .OriginalAuthor}} - {{else}} + {{if not .OriginalAuthor}} {{/* Some timeline avatars need a offset to correctly align with their speech bubble. The condition depends on review type and for positive reviews whether there is a comment element or not */}} @@ -495,9 +494,21 @@ {{else if eq .Type 25}}
{{svg "octicon-git-branch"}} - {{template "shared/user/avatarlink" dict "user" .Poster}} + {{if not .OriginalAuthor}} + {{template "shared/user/avatarlink" dict "user" .Poster}} + {{end}} - {{.Poster.Name}} + {{if .OriginalAuthor}} + + {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} + {{.OriginalAuthor}} + + {{if $.Repository.OriginalURL}} + ({{ctx.Locale.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname}}) + {{end}} + {{else}} + {{template "shared/user/authorlink" .Poster}} + {{end}} {{ctx.Locale.Tr "repo.pulls.change_target_branch_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr}}
From 0a426cc575734e5eff410d6a790f40473117f753 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Sat, 24 Feb 2024 09:18:39 +0100 Subject: [PATCH 21/79] Add API to get merged PR of a commit (#29243) Adds a new API `/repos/{owner}/{repo}/commits/{sha}/pull` that allows you to get the merged PR associated to a commit. --------- Co-authored-by: 6543 <6543@obermui.de> --- models/fixtures/pull_request.yml | 1 + models/issues/pull.go | 20 +++++++++++++ models/issues/pull_test.go | 12 ++++++++ routers/api/v1/api.go | 1 + routers/api/v1/repo/commits.go | 51 ++++++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 43 +++++++++++++++++++++++++++ 6 files changed, 128 insertions(+) diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml index 560674c370..54590fb830 100644 --- a/models/fixtures/pull_request.yml +++ b/models/fixtures/pull_request.yml @@ -9,6 +9,7 @@ head_branch: branch1 base_branch: master merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 + merged_commit_id: 1a8823cd1a9549fde083f992f6b9b87a7ab74fb3 has_merged: true merger_id: 2 diff --git a/models/issues/pull.go b/models/issues/pull.go index 2cb1e1b971..18e6b2776d 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -1093,3 +1093,23 @@ func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error { } return committer.Commit() } + +// GetPullRequestByMergedCommit returns a merged pull request by the given commit +func GetPullRequestByMergedCommit(ctx context.Context, repoID int64, sha string) (*PullRequest, error) { + pr := new(PullRequest) + has, err := db.GetEngine(ctx).Where("base_repo_id = ? AND merged_commit_id = ?", repoID, sha).Get(pr) + if err != nil { + return nil, err + } else if !has { + return nil, ErrPullRequestNotExist{0, 0, 0, repoID, "", ""} + } + + if err = pr.LoadAttributes(ctx); err != nil { + return nil, err + } + if err = pr.LoadIssue(ctx); err != nil { + return nil, err + } + + return pr, nil +} diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index 173417136c..3a30b2f3de 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -339,6 +339,18 @@ func TestGetApprovers(t *testing.T) { assert.EqualValues(t, expected, approvers) } +func TestGetPullRequestByMergedCommit(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + pr, err := issues_model.GetPullRequestByMergedCommit(db.DefaultContext, 1, "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3") + assert.NoError(t, err) + assert.EqualValues(t, 1, pr.ID) + + _, err = issues_model.GetPullRequestByMergedCommit(db.DefaultContext, 0, "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3") + assert.ErrorAs(t, err, &issues_model.ErrPullRequestNotExist{}) + _, err = issues_model.GetPullRequestByMergedCommit(db.DefaultContext, 1, "") + assert.ErrorAs(t, err, &issues_model.ErrPullRequestNotExist{}) +} + func TestMigrate_InsertPullRequests(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) reponame := "repo1" diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 3fafb96b8e..e7bdef1489 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1235,6 +1235,7 @@ func Routes() *web.Route { m.Group("/{ref}", func() { m.Get("/status", repo.GetCombinedCommitStatusByRef) m.Get("/statuses", repo.GetCommitStatusesByRef) + m.Get("/pull", repo.GetCommitPullRequest) }, context.ReferencesGitRepo()) }, reqRepoReader(unit.TypeCode)) m.Group("/git", func() { diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 43b6400009..d01cf6b8bc 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -10,6 +10,7 @@ import ( "net/http" "strconv" + issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" @@ -323,3 +324,53 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) { return } } + +// GetCommitPullRequest returns the pull request of the commit +func GetCommitPullRequest(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/commits/{sha}/pull repository repoGetCommitPullRequest + // --- + // summary: Get the pull request of the commit + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: sha + // in: path + // description: SHA of the commit to get + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/PullRequest" + // "404": + // "$ref": "#/responses/notFound" + + pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.Params(":sha")) + if err != nil { + if issues_model.IsErrPullRequestNotExist(err) { + ctx.Error(http.StatusNotFound, "GetPullRequestByMergedCommit", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err) + } + return + } + + if err = pr.LoadBaseRepo(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err) + return + } + if err = pr.LoadHeadRepo(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) + return + } + ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer)) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d26bed53aa..eaa1448b2b 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -4565,6 +4565,49 @@ } } }, + "/repos/{owner}/{repo}/commits/{sha}/pull": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get the pull request of the commit", + "operationId": "repoGetCommitPullRequest", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "SHA of the commit to get", + "name": "sha", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/PullRequest" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/contents": { "get": { "produces": [ From d3982bcd814bac93e3cbce1c7eb749b17e413fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eahin=20Akkaya?= Date: Sat, 24 Feb 2024 13:22:51 +0300 Subject: [PATCH 22/79] Implement recent commits graph (#29210) This is the implementation of Recent Commits page. This feature was mentioned on #18262. It adds another tab to Activity page called Recent Commits. Recent Commits tab shows number of commits since last year for the repository. --- options/locale/locale_en-US.ini | 4 +- routers/web/repo/recent_commits.go | 41 ++++++ routers/web/web.go | 4 + templates/repo/activity.tmpl | 1 + templates/repo/navbar.tmpl | 3 + templates/repo/recent_commits.tmpl | 9 ++ web_src/js/components/RepoRecentCommits.vue | 149 ++++++++++++++++++++ web_src/js/features/recent-commits.js | 21 +++ web_src/js/index.js | 2 + 9 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 routers/web/repo/recent_commits.go create mode 100644 templates/repo/recent_commits.tmpl create mode 100644 web_src/js/components/RepoRecentCommits.vue create mode 100644 web_src/js/features/recent-commits.js diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2c92f40a17..ff6a3f1b8e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1915,8 +1915,9 @@ wiki.original_git_entry_tooltip = View original Git file instead of using friend activity = Activity activity.navbar.pulse = Pulse -activity.navbar.contributors = Contributors activity.navbar.code_frequency = Code Frequency +activity.navbar.contributors = Contributors +activity.navbar.recent_commits = Recent Commits activity.period.filter_label = Period: activity.period.daily = 1 day activity.period.halfweekly = 3 days @@ -2597,6 +2598,7 @@ component_loading_info = This might take a bit… component_failed_to_load = An unexpected error happened. code_frequency.what = code frequency contributors.what = contributions +recent_commits.what = recent commits [org] org_name_holder = Organization Name diff --git a/routers/web/repo/recent_commits.go b/routers/web/repo/recent_commits.go new file mode 100644 index 0000000000..3507cb8752 --- /dev/null +++ b/routers/web/repo/recent_commits.go @@ -0,0 +1,41 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "errors" + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + contributors_service "code.gitea.io/gitea/services/repository" +) + +const ( + tplRecentCommits base.TplName = "repo/activity" +) + +// RecentCommits renders the page to show recent commit frequency on repository +func RecentCommits(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.activity.navbar.recent_commits") + + ctx.Data["PageIsActivity"] = true + ctx.Data["PageIsRecentCommits"] = true + ctx.PageData["repoLink"] = ctx.Repo.RepoLink + + ctx.HTML(http.StatusOK, tplRecentCommits) +} + +// RecentCommitsData returns JSON of recent commits data +func RecentCommitsData(ctx *context.Context) { + if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { + if errors.Is(err, contributors_service.ErrAwaitGeneration) { + ctx.Status(http.StatusAccepted) + return + } + ctx.ServerError("RecentCommitsData", err) + } else { + ctx.JSON(http.StatusOK, contributorStats["total"].Weeks) + } +} diff --git a/routers/web/web.go b/routers/web/web.go index a76b444e4f..8505417c88 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1402,6 +1402,10 @@ func registerRoutes(m *web.Route) { m.Get("", repo.CodeFrequency) m.Get("/data", repo.CodeFrequencyData) }) + m.Group("/recent-commits", func() { + m.Get("", repo.RecentCommits) + m.Get("/data", repo.RecentCommitsData) + }) }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases)) m.Group("/activity_author_data", func() { diff --git a/templates/repo/activity.tmpl b/templates/repo/activity.tmpl index 94f52b0e26..a19fb66261 100644 --- a/templates/repo/activity.tmpl +++ b/templates/repo/activity.tmpl @@ -9,6 +9,7 @@ {{if .PageIsPulse}}{{template "repo/pulse" .}}{{end}} {{if .PageIsContributors}}{{template "repo/contributors" .}}{{end}} {{if .PageIsCodeFrequency}}{{template "repo/code_frequency" .}}{{end}} + {{if .PageIsRecentCommits}}{{template "repo/recent_commits" .}}{{end}}
diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl index aa5021e73a..b2471dc17e 100644 --- a/templates/repo/navbar.tmpl +++ b/templates/repo/navbar.tmpl @@ -8,4 +8,7 @@ {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}} + + {{ctx.Locale.Tr "repo.activity.navbar.recent_commits"}} +
diff --git a/templates/repo/recent_commits.tmpl b/templates/repo/recent_commits.tmpl new file mode 100644 index 0000000000..5c241d635c --- /dev/null +++ b/templates/repo/recent_commits.tmpl @@ -0,0 +1,9 @@ +{{if .Permission.CanRead $.UnitTypeCode}} +
+
+{{end}} diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue new file mode 100644 index 0000000000..77697cd413 --- /dev/null +++ b/web_src/js/components/RepoRecentCommits.vue @@ -0,0 +1,149 @@ + + + diff --git a/web_src/js/features/recent-commits.js b/web_src/js/features/recent-commits.js new file mode 100644 index 0000000000..ded10d39be --- /dev/null +++ b/web_src/js/features/recent-commits.js @@ -0,0 +1,21 @@ +import {createApp} from 'vue'; + +export async function initRepoRecentCommits() { + const el = document.getElementById('repo-recent-commits-chart'); + if (!el) return; + + const {default: RepoRecentCommits} = await import(/* webpackChunkName: "recent-commits-graph" */'../components/RepoRecentCommits.vue'); + try { + const View = createApp(RepoRecentCommits, { + locale: { + loadingTitle: el.getAttribute('data-locale-loading-title'), + loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'), + loadingInfo: el.getAttribute('data-locale-loading-info'), + } + }); + View.mount(el); + } catch (err) { + console.error('RepoRecentCommits failed to load', err); + el.textContent = el.getAttribute('data-locale-component-failed-to-load'); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index d9cfff4084..b7f3ba99a0 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -85,6 +85,7 @@ import {initRepoIssueList} from './features/repo-issue-list.js'; import {initCommonIssueListQuickGoto} from './features/common-issue-list.js'; import {initRepoContributors} from './features/contributors.js'; import {initRepoCodeFrequency} from './features/code-frequency.js'; +import {initRepoRecentCommits} from './features/recent-commits.js'; import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'; import {initDirAuto} from './modules/dirauto.js'; @@ -176,6 +177,7 @@ onDomReady(() => { initRepositoryActionView(); initRepoContributors(); initRepoCodeFrequency(); + initRepoRecentCommits(); initCommitStatuses(); initCaptcha(); From 553d46e6f6a144905266d58315a2b0ff2e976380 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sat, 24 Feb 2024 12:45:59 +0100 Subject: [PATCH 23/79] Do not double close reader (#29354) Fixes #29346 --------- Co-authored-by: wxiaoguang --- modules/git/blob_nogogit.go | 12 +++++++++++- routers/web/repo/editor.go | 3 --- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/git/blob_nogogit.go b/modules/git/blob_nogogit.go index 9e1c2a0376..945a6bc432 100644 --- a/modules/git/blob_nogogit.go +++ b/modules/git/blob_nogogit.go @@ -102,7 +102,17 @@ func (b *blobReader) Read(p []byte) (n int, err error) { // Close implements io.Closer func (b *blobReader) Close() error { + if b.rd == nil { + return nil + } + defer b.cancel() - return DiscardFull(b.rd, b.n+1) + if err := DiscardFull(b.rd, b.n+1); err != nil { + return err + } + + b.rd = nil + + return nil } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index bc3cb8801d..28644fbe3d 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -161,9 +161,6 @@ func editFile(ctx *context.Context, isNewFile bool) { } d, _ := io.ReadAll(dataRc) - if err := dataRc.Close(); err != nil { - log.Error("Error whilst closing blob data: %v", err) - } buf = append(buf, d...) if content, err := charset.ToUTF8(buf, charset.ConvertOpts{KeepBOM: true}); err != nil { From 267dbb4e938cc42dc09a4a893cca631b2f755557 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 24 Feb 2024 14:03:53 +0200 Subject: [PATCH 24/79] Remove jQuery from the issue reference context popup (#29367) - Removed all jQuery calls - Tested the context popup functionality and it works as before # Demo without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/90b53de5-a8e9-4ed7-9236-1c9dfc324f38) --------- Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- web_src/js/components/ContextPopup.vue | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index d9e6da316c..3a1b828cca 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -1,8 +1,8 @@ diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index d4b19978d3..f7c74c9aba 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -139,7 +139,9 @@ {{svg "octicon-cross-reference" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.cite_this_repo"}} {{end}} {{end}} - {{svg "gitea-vscode" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.clone_in_vsc"}} + {{range .OpenWithEditorApps}} + {{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}} + {{end}}
{{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}} diff --git a/web_src/svg/gitea-open-with-jetbrains.svg b/web_src/svg/gitea-open-with-jetbrains.svg new file mode 100644 index 0000000000..a7884c4289 --- /dev/null +++ b/web_src/svg/gitea-open-with-jetbrains.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web_src/svg/gitea-vscode.svg b/web_src/svg/gitea-open-with-vscode.svg similarity index 100% rename from web_src/svg/gitea-vscode.svg rename to web_src/svg/gitea-open-with-vscode.svg diff --git a/web_src/svg/gitea-open-with-vscodium.svg b/web_src/svg/gitea-open-with-vscodium.svg new file mode 100644 index 0000000000..483676fe71 --- /dev/null +++ b/web_src/svg/gitea-open-with-vscodium.svg @@ -0,0 +1 @@ + \ No newline at end of file From 98ab9445d1020c515c3c789f0b27d952903a2978 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 24 Feb 2024 22:14:48 +0800 Subject: [PATCH 27/79] Users with `read` permission of pull requests can be assigned too (#27263) This PR will also keep the consistent between list assigned users and check assigned users. --- models/perm/access/repo_permission.go | 4 ++-- models/repo/user_repo.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 395ecdf1a5..4175cb9b92 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -332,7 +332,6 @@ func HasAccessUnit(ctx context.Context, user *user_model.User, repo *repo_model. // CanBeAssigned return true if user can be assigned to issue or pull requests in repo // Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface. -// FIXME: user could send PullRequest also could be assigned??? func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.Repository, _ bool) (bool, error) { if user.IsOrganization() { return false, fmt.Errorf("Organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID) @@ -341,7 +340,8 @@ func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model. if err != nil { return false, err } - return perm.CanAccessAny(perm_model.AccessModeWrite, unit.TypeCode, unit.TypeIssues, unit.TypePullRequests), nil + return perm.CanAccessAny(perm_model.AccessModeWrite, unit.AllRepoUnitTypes...) || + perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil } // HasAccess returns true if user has access to repo diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index dd2ef62201..30c9db7474 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -8,6 +8,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" api "code.gitea.io/gitea/modules/structs" @@ -78,7 +79,8 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us if err = e.Table("team_user"). Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id"). Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id"). - Where("`team_repo`.repo_id = ? AND `team_unit`.access_mode >= ?", repo.ID, perm.AccessModeWrite). + Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))", + repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests). Distinct("`team_user`.uid"). Select("`team_user`.uid"). Find(&additionalUserIDs); err != nil { From 4197e2810081025a6614624e7b1731af91c8db72 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sat, 24 Feb 2024 19:46:49 +0100 Subject: [PATCH 28/79] Refactor git attributes (#29356) --- modules/git/attribute.go | 35 ++++++++++++++++++ modules/git/repo_attribute.go | 36 ++++++------------- modules/git/repo_attribute_test.go | 10 +++--- modules/git/repo_language_stats.go | 19 ++++++++++ modules/git/repo_language_stats_gogit.go | 20 +++-------- modules/git/repo_language_stats_nogogit.go | 20 +++-------- routers/web/repo/view.go | 7 ++-- services/gitdiff/gitdiff.go | 42 +++++++++------------- services/repository/files/content.go | 12 ++----- 9 files changed, 101 insertions(+), 100 deletions(-) create mode 100644 modules/git/attribute.go diff --git a/modules/git/attribute.go b/modules/git/attribute.go new file mode 100644 index 0000000000..4dfa510369 --- /dev/null +++ b/modules/git/attribute.go @@ -0,0 +1,35 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "code.gitea.io/gitea/modules/optional" +) + +const ( + AttributeLinguistVendored = "linguist-vendored" + AttributeLinguistGenerated = "linguist-generated" + AttributeLinguistDocumentation = "linguist-documentation" + AttributeLinguistDetectable = "linguist-detectable" + AttributeLinguistLanguage = "linguist-language" + AttributeGitlabLanguage = "gitlab-language" +) + +// true if "set"/"true", false if "unset"/"false", none otherwise +func AttributeToBool(attr map[string]string, name string) optional.Option[bool] { + switch attr[name] { + case "set", "true": + return optional.Some(true) + case "unset", "false": + return optional.Some(false) + } + return optional.None[bool]() +} + +func AttributeToString(attr map[string]string, name string) optional.Option[string] { + if value, has := attr[name]; has && value != "unspecified" { + return optional.Some(value) + } + return optional.None[string]() +} diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 44f13ddc2d..84f85d1b1a 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -11,7 +11,6 @@ import ( "os" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/optional" ) // CheckAttributeOpts represents the possible options to CheckAttribute @@ -292,10 +291,17 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe } checker := &CheckAttributeReader{ - Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language", "linguist-documentation", "linguist-detectable"}, - Repo: repo, - IndexFile: indexFilename, - WorkTree: worktree, + Attributes: []string{ + AttributeLinguistVendored, + AttributeLinguistGenerated, + AttributeLinguistDocumentation, + AttributeLinguistDetectable, + AttributeLinguistLanguage, + AttributeGitlabLanguage, + }, + Repo: repo, + IndexFile: indexFilename, + WorkTree: worktree, } ctx, cancel := context.WithCancel(repo.Ctx) if err := checker.Init(ctx); err != nil { @@ -317,23 +323,3 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe return checker, deferable } - -// true if "set"/"true", false if "unset"/"false", none otherwise -func attributeToBool(attr map[string]string, name string) optional.Option[bool] { - if value, has := attr[name]; has && value != "unspecified" { - switch value { - case "set", "true": - return optional.Some(true) - case "unset", "false": - return optional.Some(false) - } - } - return optional.None[bool]() -} - -func attributeToString(attr map[string]string, name string) optional.Option[string] { - if value, has := attr[name]; has && value != "unspecified" { - return optional.Some(value) - } - return optional.None[string]() -} diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index ed16dccbe4..0fcd94b4c7 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -24,7 +24,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) { select { case attr := <-wr.ReadAttribute(): assert.Equal(t, ".gitignore\"\n", attr.Filename) - assert.Equal(t, "linguist-vendored", attr.Attribute) + assert.Equal(t, AttributeLinguistVendored, attr.Attribute) assert.Equal(t, "unspecified", attr.Value) case <-time.After(100 * time.Millisecond): assert.FailNow(t, "took too long to read an attribute from the list") @@ -38,7 +38,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) { select { case attr := <-wr.ReadAttribute(): assert.Equal(t, ".gitignore\"\n", attr.Filename) - assert.Equal(t, "linguist-vendored", attr.Attribute) + assert.Equal(t, AttributeLinguistVendored, attr.Attribute) assert.Equal(t, "unspecified", attr.Value) case <-time.After(100 * time.Millisecond): assert.FailNow(t, "took too long to read an attribute from the list") @@ -77,21 +77,21 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, attributeTriple{ Filename: "shouldbe.vendor", - Attribute: "linguist-vendored", + Attribute: AttributeLinguistVendored, Value: "set", }, attr) attr = <-wr.ReadAttribute() assert.NoError(t, err) assert.EqualValues(t, attributeTriple{ Filename: "shouldbe.vendor", - Attribute: "linguist-generated", + Attribute: AttributeLinguistGenerated, Value: "unspecified", }, attr) attr = <-wr.ReadAttribute() assert.NoError(t, err) assert.EqualValues(t, attributeTriple{ Filename: "shouldbe.vendor", - Attribute: "linguist-language", + Attribute: AttributeLinguistLanguage, Value: "unspecified", }, attr) } diff --git a/modules/git/repo_language_stats.go b/modules/git/repo_language_stats.go index c40d6937b5..8551ea9d24 100644 --- a/modules/git/repo_language_stats.go +++ b/modules/git/repo_language_stats.go @@ -6,6 +6,8 @@ package git import ( "strings" "unicode" + + "code.gitea.io/gitea/modules/optional" ) const ( @@ -46,3 +48,20 @@ func mergeLanguageStats(stats map[string]int64) map[string]int64 { } return res } + +func TryReadLanguageAttribute(attrs map[string]string) optional.Option[string] { + language := AttributeToString(attrs, AttributeLinguistLanguage) + if language.Value() == "" { + language = AttributeToString(attrs, AttributeGitlabLanguage) + if language.Has() { + raw := language.Value() + // gitlab-language may have additional parameters after the language + // ignore them and just use the main language + // https://docs.gitlab.com/ee/user/project/highlighting.html#override-syntax-highlighting-for-a-file-type + if idx := strings.IndexByte(raw, '?'); idx >= 0 { + language = optional.Some(raw[:idx]) + } + } + } + return language +} diff --git a/modules/git/repo_language_stats_gogit.go b/modules/git/repo_language_stats_gogit.go index 99c7a894d5..a34c03c781 100644 --- a/modules/git/repo_language_stats_gogit.go +++ b/modules/git/repo_language_stats_gogit.go @@ -8,7 +8,6 @@ package git import ( "bytes" "io" - "strings" "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/optional" @@ -66,36 +65,27 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err if checker != nil { attrs, err := checker.CheckPath(f.Name) if err == nil { - isVendored = attributeToBool(attrs, "linguist-vendored") + isVendored = AttributeToBool(attrs, AttributeLinguistVendored) if isVendored.ValueOrDefault(false) { return nil } - isGenerated = attributeToBool(attrs, "linguist-generated") + isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated) if isGenerated.ValueOrDefault(false) { return nil } - isDocumentation = attributeToBool(attrs, "linguist-documentation") + isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation) if isDocumentation.ValueOrDefault(false) { return nil } - isDetectable = attributeToBool(attrs, "linguist-detectable") + isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable) if !isDetectable.ValueOrDefault(true) { return nil } - hasLanguage := attributeToString(attrs, "linguist-language") - if hasLanguage.Value() == "" { - hasLanguage = attributeToString(attrs, "gitlab-language") - if hasLanguage.Has() { - language := hasLanguage.Value() - if idx := strings.IndexByte(language, '?'); idx >= 0 { - hasLanguage = optional.Some(language[:idx]) - } - } - } + hasLanguage := TryReadLanguageAttribute(attrs) if hasLanguage.Value() != "" { language := hasLanguage.Value() diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go index 16669924d6..318fc091ce 100644 --- a/modules/git/repo_language_stats_nogogit.go +++ b/modules/git/repo_language_stats_nogogit.go @@ -8,7 +8,6 @@ package git import ( "bytes" "io" - "strings" "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/log" @@ -97,36 +96,27 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err if checker != nil { attrs, err := checker.CheckPath(f.Name()) if err == nil { - isVendored = attributeToBool(attrs, "linguist-vendored") + isVendored = AttributeToBool(attrs, AttributeLinguistVendored) if isVendored.ValueOrDefault(false) { continue } - isGenerated = attributeToBool(attrs, "linguist-generated") + isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated) if isGenerated.ValueOrDefault(false) { continue } - isDocumentation = attributeToBool(attrs, "linguist-documentation") + isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation) if isDocumentation.ValueOrDefault(false) { continue } - isDetectable = attributeToBool(attrs, "linguist-detectable") + isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable) if !isDetectable.ValueOrDefault(true) { continue } - hasLanguage := attributeToString(attrs, "linguist-language") - if hasLanguage.Value() == "" { - hasLanguage = attributeToString(attrs, "gitlab-language") - if hasLanguage.Has() { - language := hasLanguage.Value() - if idx := strings.IndexByte(language, '?'); idx >= 0 { - hasLanguage = optional.Some(language[:idx]) - } - } - } + hasLanguage := TryReadLanguageAttribute(attrs) if hasLanguage.Value() != "" { language := hasLanguage.Value() diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 33a5941d36..48a35dd060 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -635,11 +635,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { defer deferable() attrs, err := checker.CheckPath(ctx.Repo.TreePath) if err == nil { - vendored, has := attrs["linguist-vendored"] - ctx.Data["IsVendored"] = has && (vendored == "set" || vendored == "true") - - generated, has := attrs["linguist-generated"] - ctx.Data["IsGenerated"] = has && (generated == "set" || generated == "true") + ctx.Data["IsVendored"] = git.AttributeToBool(attrs, git.AttributeLinguistVendored).Value() + ctx.Data["IsGenerated"] = git.AttributeToBool(attrs, git.AttributeLinguistGenerated).Value() } } } diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 0f6e2b6c17..740c748347 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -29,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/translation" @@ -1181,41 +1182,30 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi for _, diffFile := range diff.Files { - gotVendor := false - gotGenerated := false + isVendored := optional.None[bool]() + isGenerated := optional.None[bool]() if checker != nil { attrs, err := checker.CheckPath(diffFile.Name) if err == nil { - if vendored, has := attrs["linguist-vendored"]; has { - if vendored == "set" || vendored == "true" { - diffFile.IsVendored = true - gotVendor = true - } else { - gotVendor = vendored == "false" - } - } - if generated, has := attrs["linguist-generated"]; has { - if generated == "set" || generated == "true" { - diffFile.IsGenerated = true - gotGenerated = true - } else { - gotGenerated = generated == "false" - } - } - if language, has := attrs["linguist-language"]; has && language != "unspecified" && language != "" { - diffFile.Language = language - } else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" { - diffFile.Language = language + isVendored = git.AttributeToBool(attrs, git.AttributeLinguistVendored) + isGenerated = git.AttributeToBool(attrs, git.AttributeLinguistGenerated) + + language := git.TryReadLanguageAttribute(attrs) + if language.Has() { + diffFile.Language = language.Value() } } } - if !gotVendor { - diffFile.IsVendored = analyze.IsVendor(diffFile.Name) + if !isVendored.Has() { + isVendored = optional.Some(analyze.IsVendor(diffFile.Name)) } - if !gotGenerated { - diffFile.IsGenerated = analyze.IsGenerated(diffFile.Name) + diffFile.IsVendored = isVendored.Value() + + if !isGenerated.Has() { + isGenerated = optional.Some(analyze.IsGenerated(diffFile.Name)) } + diffFile.IsGenerated = isGenerated.Value() tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID) if tailSection != nil { diff --git a/services/repository/files/content.go b/services/repository/files/content.go index f2a7677688..9500b8f46d 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -282,7 +282,7 @@ func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) ( filename2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{ CachedOnly: true, - Attributes: []string{"linguist-language", "gitlab-language"}, + Attributes: []string{git.AttributeLinguistLanguage, git.AttributeGitlabLanguage}, Filenames: []string{treePath}, IndexFile: indexFilename, WorkTree: worktree, @@ -291,13 +291,7 @@ func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) ( return "", err } - language := filename2attribute2info[treePath]["linguist-language"] - if language == "" || language == "unspecified" { - language = filename2attribute2info[treePath]["gitlab-language"] - } - if language == "unspecified" { - language = "" - } + language := git.TryReadLanguageAttribute(filename2attribute2info[treePath]) - return language, nil + return language.Value(), nil } From c86d033a3ec0514efcd9524d03dce1b6551e487f Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 24 Feb 2024 21:11:51 +0200 Subject: [PATCH 29/79] Remove jQuery from the Unicode escape button (#29369) - Switched to plain JavaScript - Tested the Unicode escape button functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/664f0ced-876b-4cb7-a668-bd62169fc843) --------- Signed-off-by: Yarden Shoham Co-authored-by: wxiaoguang --- web_src/js/features/repo-unicode-escape.js | 46 ++++++++++------------ web_src/js/utils/dom.js | 4 ++ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/web_src/js/features/repo-unicode-escape.js b/web_src/js/features/repo-unicode-escape.js index 6a201ec4d1..d878532001 100644 --- a/web_src/js/features/repo-unicode-escape.js +++ b/web_src/js/features/repo-unicode-escape.js @@ -1,31 +1,27 @@ -import $ from 'jquery'; -import {hideElem, showElem} from '../utils/dom.js'; +import {hideElem, queryElemSiblings, showElem, toggleElem} from '../utils/dom.js'; export function initUnicodeEscapeButton() { - $(document).on('click', '.escape-button', (e) => { + document.addEventListener('click', (e) => { + const btn = e.target.closest('.escape-button, .unescape-button, .toggle-escape-button'); + if (!btn) return; + e.preventDefault(); - $(e.target).parents('.file-content, .non-diff-file-content').find('.file-code, .file-view').addClass('unicode-escaped'); - hideElem($(e.target)); - showElem($(e.target).siblings('.unescape-button')); - }); - $(document).on('click', '.unescape-button', (e) => { - e.preventDefault(); - $(e.target).parents('.file-content, .non-diff-file-content').find('.file-code, .file-view').removeClass('unicode-escaped'); - hideElem($(e.target)); - showElem($(e.target).siblings('.escape-button')); - }); - $(document).on('click', '.toggle-escape-button', (e) => { - e.preventDefault(); - const fileContent = $(e.target).parents('.file-content, .non-diff-file-content'); - const fileView = fileContent.find('.file-code, .file-view'); - if (fileView.hasClass('unicode-escaped')) { - fileView.removeClass('unicode-escaped'); - hideElem(fileContent.find('.unescape-button')); - showElem(fileContent.find('.escape-button')); - } else { - fileView.addClass('unicode-escaped'); - showElem(fileContent.find('.unescape-button')); - hideElem(fileContent.find('.escape-button')); + + const fileContent = btn.closest('.file-content, .non-diff-file-content'); + const fileView = fileContent?.querySelectorAll('.file-code, .file-view'); + if (btn.matches('.escape-button')) { + for (const el of fileView) el.classList.add('unicode-escaped'); + hideElem(btn); + showElem(queryElemSiblings(btn, '.unescape-button')); + } else if (btn.matches('.unescape-button')) { + for (const el of fileView) el.classList.remove('unicode-escaped'); + hideElem(btn); + showElem(queryElemSiblings(btn, '.escape-button')); + } else if (btn.matches('.toggle-escape-button')) { + const isEscaped = fileView[0]?.classList.contains('unicode-escaped'); + for (const el of fileView) el.classList.toggle('unicode-escaped', !isEscaped); + toggleElem(fileContent.querySelectorAll('.unescape-button'), !isEscaped); + toggleElem(fileContent.querySelectorAll('.escape-button'), isEscaped); } }); } diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js index ca24650f76..91535dc187 100644 --- a/web_src/js/utils/dom.js +++ b/web_src/js/utils/dom.js @@ -51,6 +51,10 @@ export function isElemHidden(el) { return res[0]; } +export function queryElemSiblings(el, selector) { + return Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector)); +} + export function onDomReady(cb) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', cb); From ff9dc512438f1a3bc36cc8c419d8450f808517f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:19:49 +0100 Subject: [PATCH 30/79] Apply to become a maintainer (zokkis) (#29383) --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 72171f80ed..2f95fdca50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -59,3 +59,4 @@ Rui Chen (@chenrui333) Nanguan Lin (@lng2020) kerwin612 (@kerwin612) Gary Wang (@BLumia) +Tim-Niclas Oelschläger (@zokkis) From 10c7996b5a5c705964fc6cc9c1817eea1fc436ef Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 25 Feb 2024 06:34:51 +0800 Subject: [PATCH 31/79] Remove RenderEmojiPlain from template helper (#29375) RenderEmojiPlain(emoji.ReplaceAliases) should be called explicitly for some contents, but not for everything. Actually in modern days, in most cases it doesn't need such "ReplaceAliases". So only keep it for issue/PR titles. If anyone really needs to do ReplaceAliases for some contents, I will propose a following fix. --- modules/templates/helper.go | 12 ------------ routers/web/repo/issue.go | 3 ++- routers/web/repo/pull.go | 3 ++- templates/base/head.tmpl | 2 +- templates/repo/issue/choose.tmpl | 8 ++++---- templates/repo/settings/lfs_file_find.tmpl | 2 +- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 6e42594b0b..691f754748 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -14,7 +14,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/svg" @@ -159,7 +158,6 @@ func NewFuncMap() template.FuncMap { "RenderCodeBlock": RenderCodeBlock, "RenderIssueTitle": RenderIssueTitle, "RenderEmoji": RenderEmoji, - "RenderEmojiPlain": RenderEmojiPlain, "ReactionToEmoji": ReactionToEmoji, "RenderMarkdownToHtml": RenderMarkdownToHtml, @@ -215,16 +213,6 @@ func JSEscapeSafe(s string) template.HTML { return template.HTML(template.JSEscapeString(s)) } -func RenderEmojiPlain(s any) any { - switch v := s.(type) { - case string: - return emoji.ReplaceAliases(v) - case template.HTML: - return template.HTML(emoji.ReplaceAliases(string(v))) - } - panic(fmt.Sprintf("unexpected type %T", s)) -} - // DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls func DotEscape(raw string) string { return strings.ReplaceAll(raw, ".", "\u200d.\u200d") diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 9f08607642..245ed2b2f2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -32,6 +32,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" issue_template "code.gitea.io/gitea/modules/issue/template" @@ -1435,7 +1436,7 @@ func ViewIssue(ctx *context.Context) { return } - ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title) + ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, emoji.ReplaceAliases(issue.Title)) iw := new(issues_model.IssueWatch) if ctx.Doer != nil { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 14f1eb3102..7ab21f22b9 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -28,6 +28,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" issue_template "code.gitea.io/gitea/modules/issue/template" @@ -335,7 +336,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) { ctx.ServerError("LoadRepo", err) return nil, false } - ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title) + ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, emoji.ReplaceAliases(issue.Title)) ctx.Data["Issue"] = issue if !issue.IsPull { diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index d7e28474e7..2de8f58235 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -2,7 +2,7 @@ - {{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} + {{if .Title}}{{.Title}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} {{if .ManifestData}}{{end}} diff --git a/templates/repo/issue/choose.tmpl b/templates/repo/issue/choose.tmpl index 127b9d7d87..a8037482be 100644 --- a/templates/repo/issue/choose.tmpl +++ b/templates/repo/issue/choose.tmpl @@ -11,8 +11,8 @@
- {{.Name | RenderEmojiPlain}} -
{{.About | RenderEmojiPlain}} + {{.Name}} +
{{.About}}
{{ctx.Locale.Tr "repo.issues.choose.get_started"}} @@ -24,8 +24,8 @@
- {{.Name | RenderEmojiPlain}} -
{{.About | RenderEmojiPlain}} + {{.Name}} +
{{.About}}
{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.issues.choose.open_external_link"}} diff --git a/templates/repo/settings/lfs_file_find.tmpl b/templates/repo/settings/lfs_file_find.tmpl index fea9aa323f..809a028b2c 100644 --- a/templates/repo/settings/lfs_file_find.tmpl +++ b/templates/repo/settings/lfs_file_find.tmpl @@ -14,7 +14,7 @@ - + {{.Summary | RenderEmoji $.Context}} From 15d071f4f81a0ad09f260de83cb6402875b4de27 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sun, 25 Feb 2024 01:08:51 +0200 Subject: [PATCH 32/79] Remove jQuery AJAX from repo collaborator mode dropdown (#29371) - Removed all jQuery AJAX calls and replaced with our fetch wrapper - Tested the repo collaborator mode dropdown functionality and it works as before # Demo using `fetch` instead of jQuery AJAX ![action](https://github.com/go-gitea/gitea/assets/20454870/04466629-19b2-4469-9231-38820ee13c36) --------- Signed-off-by: Yarden Shoham Co-authored-by: delvh Co-authored-by: Giteabot --- web_src/js/features/repo-settings.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 75e624a6a7..0418f3a14a 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; import {onInputDebounce, toggleElem} from '../utils/dom.js'; +import {POST} from '../modules/fetch.js'; const {appSubUrl, csrfToken} = window.config; @@ -11,18 +12,19 @@ export function initRepoSettingsCollaboration() { const $dropdown = $(e); const $text = $dropdown.find('> .text'); $dropdown.dropdown({ - action(_text, value) { + async action(_text, value) { const lastValue = $dropdown.attr('data-last-value'); - $.post($dropdown.attr('data-url'), { - _csrf: csrfToken, - uid: $dropdown.attr('data-uid'), - mode: value, - }).fail(() => { + try { + $dropdown.attr('data-last-value', value); + $dropdown.dropdown('hide'); + const data = new FormData(); + data.append('uid', $dropdown.attr('data-uid')); + data.append('mode', value); + await POST($dropdown.attr('data-url'), {data}); + } catch { $text.text('(error)'); // prevent from misleading users when error occurs $dropdown.attr('data-last-value', lastValue); - }); - $dropdown.attr('data-last-value', value); - $dropdown.dropdown('hide'); + } }, onChange(_value, text, _$choice) { $text.text(text); // update the text when using keyboard navigating From 328d908b4fb67da0d9e5a031ee0fcd78927baaa3 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 25 Feb 2024 08:13:04 +0800 Subject: [PATCH 33/79] Move citiation button to proper place (#29374) The citiation button shouldn't be controlled by DisableDownloadSourceArchives (line 134) So move it out of that "if" block. Co-authored-by: Giteabot --- templates/repo/home.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index f7c74c9aba..2c08fb02d5 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -135,9 +135,9 @@ {{svg "octicon-file-zip" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_zip"}} {{svg "octicon-file-zip" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_tar"}} {{svg "octicon-package" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.download_bundle"}} - {{if .CitiationExist}} - {{svg "octicon-cross-reference" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.cite_this_repo"}} - {{end}} + {{end}} + {{if .CitiationExist}} + {{svg "octicon-cross-reference" 16 "gt-mr-3"}}{{ctx.Locale.Tr "repo.cite_this_repo"}} {{end}} {{range .OpenWithEditorApps}} {{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}} From b616f666b89f57f3c285b70c11693f50ba38bcaa Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sun, 25 Feb 2024 06:09:55 +0200 Subject: [PATCH 34/79] Remove jQuery AJAX from the repo commit graph (#29373) - Removed all jQuery AJAX calls and replaced with our fetch wrapper - Tested the repo collaborator mode dropdown functionality and it works as before # Demo using `fetch` instead of jQuery AJAX ![action](https://github.com/go-gitea/gitea/assets/20454870/7e2f166e-9941-4f26-9666-d00cdf3d9f60) Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- web_src/js/features/repo-graph.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js index e445ae1103..c83f448b76 100644 --- a/web_src/js/features/repo-graph.js +++ b/web_src/js/features/repo-graph.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import {GET} from '../modules/fetch.js'; export function initRepoGraphGit() { const graphContainer = document.getElementById('git-graph-container'); @@ -60,7 +61,9 @@ export function initRepoGraphGit() { $('#rev-container').addClass('gt-hidden'); $('#loading-indicator').removeClass('gt-hidden'); (async () => { - const div = $(await $.ajax(String(ajaxUrl))); + const response = await GET(String(ajaxUrl)); + const html = await response.text(); + const div = $(html); $('#pagination').html(div.find('#pagination').html()); $('#rel-container').html(div.find('#rel-container').html()); $('#rev-container').html(div.find('#rev-container').html()); From 736c98be5c2bed26cef9f7f679c49a95af8161ef Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 25 Feb 2024 12:17:11 +0800 Subject: [PATCH 35/79] Refactor `copy` button event handler (#29379) Use "closest" instead of "for-loop" --- web_src/js/features/clipboard.js | 41 +++++++++++++------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 224628658e..8be5505c8b 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -5,37 +5,30 @@ import {clippie} from 'clippie'; const {copy_success, copy_error} = window.config.i18n; // Enable clipboard copy from HTML attributes. These properties are supported: -// - data-clipboard-text: Direct text to copy, has highest precedence +// - data-clipboard-text: Direct text to copy // - data-clipboard-target: Holds a selector for a or @@ -60,7 +60,7 @@
{{svg "octicon-git-branch"}} - +
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index cfc266731b..a6dce81c08 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -15,7 +15,7 @@ {{range $i, $v := .TreeNames}} {{if eq $i $l}} - + {{svg "octicon-info"}} {{else}} {{$v}} diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index 44c30bd5f9..c9a78cc35f 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -15,7 +15,7 @@ {{.BranchName}} {{ctx.Locale.Tr "repo.editor.or"}} {{ctx.Locale.Tr "repo.editor.cancel_lower"}} - +
diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl index d362a5602a..0a7c49dae3 100644 --- a/templates/repo/editor/upload.tmpl +++ b/templates/repo/editor/upload.tmpl @@ -13,7 +13,7 @@ {{range $i, $v := .TreeNames}} {{if eq $i $l}} - + {{svg "octicon-info"}} {{else}} {{$v}} diff --git a/web_src/js/utils.js b/web_src/js/utils.js index 3a2694335f..ce0fb66343 100644 --- a/web_src/js/utils.js +++ b/web_src/js/utils.js @@ -2,13 +2,14 @@ import {encode, decode} from 'uint8-to-base64'; // transform /path/to/file.ext to file.ext export function basename(path = '') { - return path ? path.replace(/^.*\//, '') : ''; + const lastSlashIndex = path.lastIndexOf('/'); + return lastSlashIndex < 0 ? path : path.substring(lastSlashIndex + 1); } // transform /path/to/file.ext to .ext export function extname(path = '') { - const [_, ext] = /.+(\.[^.]+)$/.exec(path) || []; - return ext || ''; + const lastPointIndex = path.lastIndexOf('.'); + return lastPointIndex < 0 ? '' : path.substring(lastPointIndex); } // test whether a variable is an object From 1c6858543ca976933004c21b3056a7301e1729d6 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sun, 25 Feb 2024 16:10:55 +0100 Subject: [PATCH 47/79] Integrate alpine `noarch` packages into other architectures index (#29137) Fixes #26691 Revert #24972 The alpine package manager expects `noarch` packages in the index of other architectures too. --------- Co-authored-by: Lauris BH Co-authored-by: Lunny Xiao --- modules/packages/alpine/metadata.go | 2 + routers/api/packages/alpine/alpine.go | 33 +++- services/packages/alpine/repository.go | 56 +++++- tests/integration/api_packages_alpine_test.go | 163 ++++++++++++------ 4 files changed, 190 insertions(+), 64 deletions(-) diff --git a/modules/packages/alpine/metadata.go b/modules/packages/alpine/metadata.go index 582c42610d..c492811744 100644 --- a/modules/packages/alpine/metadata.go +++ b/modules/packages/alpine/metadata.go @@ -34,6 +34,8 @@ const ( RepositoryPackage = "_alpine" RepositoryVersion = "_repository" + + NoArch = "noarch" ) // https://wiki.alpinelinux.org/wiki/Apk_spec diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index bb14c5163a..3fd8288c01 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -72,7 +72,7 @@ func GetRepositoryFile(ctx *context.Context) { ctx, pv, &packages_service.PackageFileInfo{ - Filename: alpine_service.IndexFilename, + Filename: alpine_service.IndexArchiveFilename, CompositeKey: fmt.Sprintf("%s|%s|%s", ctx.Params("branch"), ctx.Params("repository"), ctx.Params("architecture")), }, ) @@ -182,19 +182,38 @@ func UploadPackageFile(ctx *context.Context) { } func DownloadPackageFile(ctx *context.Context) { - pfs, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{ + branch := ctx.Params("branch") + repository := ctx.Params("repository") + architecture := ctx.Params("architecture") + + opts := &packages_model.PackageFileSearchOptions{ OwnerID: ctx.Package.Owner.ID, PackageType: packages_model.TypeAlpine, Query: ctx.Params("filename"), - CompositeKey: fmt.Sprintf("%s|%s|%s", ctx.Params("branch"), ctx.Params("repository"), ctx.Params("architecture")), - }) + CompositeKey: fmt.Sprintf("%s|%s|%s", branch, repository, architecture), + } + pfs, _, err := packages_model.SearchFiles(ctx, opts) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if len(pfs) != 1 { - apiError(ctx, http.StatusNotFound, nil) - return + if len(pfs) == 0 { + // Try again with architecture 'noarch' + if architecture == alpine_module.NoArch { + apiError(ctx, http.StatusNotFound, nil) + return + } + + opts.CompositeKey = fmt.Sprintf("%s|%s|%s", branch, repository, alpine_module.NoArch) + if pfs, _, err = packages_model.SearchFiles(ctx, opts); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + if len(pfs) == 0 { + apiError(ctx, http.StatusNotFound, nil) + return + } } s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) diff --git a/services/packages/alpine/repository.go b/services/packages/alpine/repository.go index 104548b421..664ab34559 100644 --- a/services/packages/alpine/repository.go +++ b/services/packages/alpine/repository.go @@ -23,6 +23,7 @@ import ( packages_model "code.gitea.io/gitea/models/packages" alpine_model "code.gitea.io/gitea/models/packages/alpine" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/json" packages_module "code.gitea.io/gitea/modules/packages" alpine_module "code.gitea.io/gitea/modules/packages/alpine" @@ -30,7 +31,10 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -const IndexFilename = "APKINDEX.tar.gz" +const ( + IndexFilename = "APKINDEX" + IndexArchiveFilename = IndexFilename + ".tar.gz" +) // GetOrCreateRepositoryVersion gets or creates the internal repository package // The Alpine registry needs multiple index files which are stored in this package. @@ -120,7 +124,22 @@ func BuildSpecificRepositoryFiles(ctx context.Context, ownerID int64, branch, re return err } - return buildPackagesIndex(ctx, ownerID, pv, branch, repository, architecture) + architectures := container.SetOf(architecture) + if architecture == alpine_module.NoArch { + // Update all other architectures too when updating the noarch index + additionalArchitectures, err := alpine_model.GetArchitectures(ctx, ownerID, repository) + if err != nil { + return err + } + architectures.AddMultiple(additionalArchitectures...) + } + + for architecture := range architectures { + if err := buildPackagesIndex(ctx, ownerID, pv, branch, repository, architecture); err != nil { + return err + } + } + return nil } type packageData struct { @@ -133,8 +152,7 @@ type packageData struct { type packageCache = map[*packages_model.PackageFile]*packageData -// https://wiki.alpinelinux.org/wiki/Apk_spec#APKINDEX_Format -func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *packages_model.PackageVersion, branch, repository, architecture string) error { +func searchPackageFiles(ctx context.Context, ownerID int64, branch, repository, architecture string) ([]*packages_model.PackageFile, error) { pfs, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{ OwnerID: ownerID, PackageType: packages_model.TypeAlpine, @@ -145,13 +163,30 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package alpine_module.PropertyArchitecture: architecture, }, }) + if err != nil { + return nil, err + } + return pfs, nil +} + +// https://wiki.alpinelinux.org/wiki/Apk_spec#APKINDEX_Format +func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *packages_model.PackageVersion, branch, repository, architecture string) error { + pfs, err := searchPackageFiles(ctx, ownerID, branch, repository, architecture) if err != nil { return err } + if architecture != alpine_module.NoArch { + // Add all noarch packages too + noarchFiles, err := searchPackageFiles(ctx, ownerID, branch, repository, alpine_module.NoArch) + if err != nil { + return err + } + pfs = append(pfs, noarchFiles...) + } // Delete the package indices if there are no packages if len(pfs) == 0 { - pf, err := packages_model.GetFileForVersionByName(ctx, repoVersion.ID, IndexFilename, fmt.Sprintf("%s|%s|%s", branch, repository, architecture)) + pf, err := packages_model.GetFileForVersionByName(ctx, repoVersion.ID, IndexArchiveFilename, fmt.Sprintf("%s|%s|%s", branch, repository, architecture)) if err != nil && !errors.Is(err, util.ErrNotExist) { return err } else if pf == nil { @@ -206,7 +241,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package fmt.Fprintf(&buf, "C:%s\n", pd.FileMetadata.Checksum) fmt.Fprintf(&buf, "P:%s\n", pd.Package.Name) fmt.Fprintf(&buf, "V:%s\n", pd.Version.Version) - fmt.Fprintf(&buf, "A:%s\n", pd.FileMetadata.Architecture) + fmt.Fprintf(&buf, "A:%s\n", architecture) if pd.VersionMetadata.Description != "" { fmt.Fprintf(&buf, "T:%s\n", pd.VersionMetadata.Description) } @@ -244,7 +279,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package h := sha1.New() - if err := writeGzipStream(io.MultiWriter(unsignedIndexContent, h), "APKINDEX", buf.Bytes(), true); err != nil { + if err := writeGzipStream(io.MultiWriter(unsignedIndexContent, h), IndexFilename, buf.Bytes(), true); err != nil { return err } @@ -299,13 +334,18 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package repoVersion, &packages_service.PackageFileCreationInfo{ PackageFileInfo: packages_service.PackageFileInfo{ - Filename: IndexFilename, + Filename: IndexArchiveFilename, CompositeKey: fmt.Sprintf("%s|%s|%s", branch, repository, architecture), }, Creator: user_model.NewGhostUser(), Data: signedIndexContent, IsLead: false, OverwriteExisting: true, + Properties: map[string]string{ + alpine_module.PropertyBranch: branch, + alpine_module.PropertyRepository: repository, + alpine_module.PropertyArchitecture: architecture, + }, }, ) return err diff --git a/tests/integration/api_packages_alpine_test.go b/tests/integration/api_packages_alpine_test.go index 3cc7178e02..228f497127 100644 --- a/tests/integration/api_packages_alpine_test.go +++ b/tests/integration/api_packages_alpine_test.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" alpine_module "code.gitea.io/gitea/modules/packages/alpine" + alpine_service "code.gitea.io/gitea/services/packages/alpine" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -59,7 +60,34 @@ Djfa/2q5bH4699v++uMAAAAAAAAAAAAAAAAAAAAAAHbgA/eXQh8AKAAA` content, err := base64.StdEncoding.DecodeString(base64AlpinePackageContent) assert.NoError(t, err) - branches := []string{"v3.16", "v3.17", "v3.18"} + base64AlpinePackageNoArchContent := `H4sIAAAAAAACA9ML9nT30wsKdtQrLU4t0jUzTUo1NDVP0ysqTtQrKE1ioAYwAAIzExMwDQTotCGI +bWhiampuYmRiaGrMYGBoZGZkxKBgwEAHUFpcklikoMAwQkHLB7eoE40P9n5jvx32t7Dy9rq7x19k +66cJPV38t/h+vWe2jdXy+/PzPT0YTF5z39i4cPFptcLa1C1lD0z/XvrNp6In/7nP4PPCF2pZu8uV +z74QXLxpY1XWJuVFysqVf+PdizccFbD6ZL/QPGXd1Ri1fec2XBNuYfK/rFa6wF/h3dK/W12f8mxP +04iP3aCy+vPx7h9S+5M1LLkWr5M/4ezGt3bDW/FjBp/S9hiKP72s/XrJ0vWtO0zr5wa+D/X8XluW +d7BLP7XS3YUhd8WbPPF/NW3691ONJbXsRb69O7BIMZC96uTri+utC/fbie5J+n7zhCxD4Aep/qet +QnlCZyN8MhNdVNlNl7965R1nExrrGvfI/YQZFx8Dg+d9122hZsYd/24WL/L69OWrDAN/y//nS7im +XEive3v7QeTe433TPj/X71+9yHiV6+E9k++3TL8V0Xoq9panhNt23fLgau/pTOvmKx6bV/pS26+Y +5UP4viyuklYeu4/BZl6rLINe1L/uWuUXcH5z7pa2b9+/rp/v/8dFgc1PL3bO3/iVcrI//J/LMU2X +Nzu1IaMmWXnGp7CmyQIR39d0Nai9/+tdPbfjvmsNH88Tu7uVrvNuJE0wjxfePXGv/KHNXD+mnG0t +yTPu+Na0b5WR9O4t0yMd9T5k6ui7hOyU/jL/4dOn6neLwhdrZIZfcl1ectnGvUTurWDo1vY5Gw9k +PTQLVgcA61F+7gAEAAAfiwgAAAAAAAID7VVNa9wwEPXZv2Ig53hHlizbCzkVkobQJtDkB4wl2SvW +lhdbTpP++oyXQGEPLYU2paTvIs3X05PQSNnmjp4+OrJumjfZ3c3V9efL2+T3AhlaqePIOB0Rc50I +VRSlypUoZIJCKJQJPCVvgGWONLGU5H1CCDDRD+4CU57S6zT5j3eCP9Tyv9T/GsuT/scyLxPAt+z/ +aRzjj/J+Fv9HcQZXLriJorPQPAM1i+8tyEzkGZ5PmJ7BMvvQQUt7tx4BPPJH4ccAIpN5Jjj+hSJc +ugZAghDbArco4eH+A+SYq/Sw7wINDi6g89HReRhpMrvVzTzsFZlaV2Hbutmw4zVhmXo2djEe5u1m +c6zNzDikR3mW1a61JepaC0SZHsjsqTsyPoR9GL+GdPbf1iSFtU5Xyu/c4+Q7H04lMfvgI3vT3hsX +5rX40/U9b5CWOA78Mhrq+2ewLjrDp7VNWQbtaF6ZXVWZIhdV09RWOIvU6BqNboSxLSEpkrpQq80x +W1Nla6NavuqtrJQ0sv17D+4L2oD1lwAIAAAfiwgAAAAAAAID7dM/SgNBFAbw6cSAnYXlXsDNm50/ +u1METBeIkEBMK87uzKKEJbB/IN7CxhN4AI/gNcRD6BWciI0WSiBGxO/XvA9mile8L+5P7WrkrfN1 +049dV1XXbNso0FK+zeDzJC4SxqVSqUwkV4IR51KkLFqxHeia1tZhFfY/cR4V7VXlB9QL0b5HnUXD +6fj4bDI5ncXFpS8WTVfFs9GQD5wVxgrvlde5zMmJRKm89KVRmnhmyJYuo5RMj8Ef8EOV36j/6/yx +/5qnxKJ1J8MZJifskD2Zu+fzxfggmT+83F4c3dw/7u1vtf/1ctl+9e+7dwAAAAAAAAAAAAAAAAAA +AACAX/AKARNTyAAoAAA=` + noarchContent, err := base64.StdEncoding.DecodeString(base64AlpinePackageNoArchContent) + assert.NoError(t, err) + + branches := []string{"v3.16", "v3.17"} repositories := []string{"main", "testing"} rootURL := fmt.Sprintf("/api/packages/%s/alpine", user.Name) @@ -139,63 +167,71 @@ Djfa/2q5bH4699v++uMAAAAAAAAAAAAAAAAAAAAAAHbgA/eXQh8AKAAA` }) }) - t.Run("Index", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + readIndexContent := func(r io.Reader) (string, error) { + br := bufio.NewReader(r) - url := fmt.Sprintf("%s/%s/%s/x86_64/APKINDEX.tar.gz", rootURL, branch, repository) + gzr, err := gzip.NewReader(br) + if err != nil { + return "", err + } - req := NewRequest(t, "GET", url) - resp := MakeRequest(t, req, http.StatusOK) - - assert.Condition(t, func() bool { - br := bufio.NewReader(resp.Body) - - gzr, err := gzip.NewReader(br) - assert.NoError(t, err) + for { + gzr.Multistream(false) + tr := tar.NewReader(gzr) for { - gzr.Multistream(false) - - tr := tar.NewReader(gzr) - for { - hd, err := tr.Next() - if err == io.EOF { - break - } - assert.NoError(t, err) - - if hd.Name == "APKINDEX" { - buf, err := io.ReadAll(tr) - assert.NoError(t, err) - - s := string(buf) - - assert.Contains(t, s, "C:Q1/se1PjO94hYXbfpNR1/61hVORIc=\n") - assert.Contains(t, s, "P:"+packageName+"\n") - assert.Contains(t, s, "V:"+packageVersion+"\n") - assert.Contains(t, s, "A:x86_64\n") - assert.Contains(t, s, "T:Gitea Test Package\n") - assert.Contains(t, s, "U:https://gitea.io/\n") - assert.Contains(t, s, "L:MIT\n") - assert.Contains(t, s, "S:1353\n") - assert.Contains(t, s, "I:4096\n") - assert.Contains(t, s, "o:gitea-test\n") - assert.Contains(t, s, "m:KN4CK3R \n") - assert.Contains(t, s, "t:1679498030\n") - - return true - } - } - - err = gzr.Reset(br) + hd, err := tr.Next() if err == io.EOF { break } - assert.NoError(t, err) + if err != nil { + return "", err + } + + if hd.Name == alpine_service.IndexFilename { + buf, err := io.ReadAll(tr) + if err != nil { + return "", err + } + + return string(buf), nil + } } - return false - }) + err = gzr.Reset(br) + if err == io.EOF { + break + } + if err != nil { + return "", err + } + } + + return "", io.EOF + } + + t.Run("Index", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/x86_64/APKINDEX.tar.gz", rootURL, branch, repository)) + resp := MakeRequest(t, req, http.StatusOK) + + content, err := readIndexContent(resp.Body) + assert.NoError(t, err) + + assert.Contains(t, content, "C:Q1/se1PjO94hYXbfpNR1/61hVORIc=\n") + assert.Contains(t, content, "P:"+packageName+"\n") + assert.Contains(t, content, "V:"+packageVersion+"\n") + assert.Contains(t, content, "A:x86_64\n") + assert.NotContains(t, content, "A:noarch\n") + assert.Contains(t, content, "T:Gitea Test Package\n") + assert.Contains(t, content, "U:https://gitea.io/\n") + assert.Contains(t, content, "L:MIT\n") + assert.Contains(t, content, "S:1353\n") + assert.Contains(t, content, "I:4096\n") + assert.Contains(t, content, "o:gitea-test\n") + assert.Contains(t, content, "m:KN4CK3R \n") + assert.Contains(t, content, "t:1679498030\n") }) t.Run("Download", func(t *testing.T) { @@ -204,6 +240,35 @@ Djfa/2q5bH4699v++uMAAAAAAAAAAAAAAAAAAAAAAHbgA/eXQh8AKAAA` req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/x86_64/%s-%s.apk", rootURL, branch, repository, packageName, packageVersion)) MakeRequest(t, req, http.StatusOK) }) + + t.Run("NoArch", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/%s/%s", rootURL, branch, repository), bytes.NewReader(noarchContent)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusCreated) + + req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/x86_64/APKINDEX.tar.gz", rootURL, branch, repository)) + resp := MakeRequest(t, req, http.StatusOK) + + content, err := readIndexContent(resp.Body) + assert.NoError(t, err) + + assert.Contains(t, content, "C:Q1/se1PjO94hYXbfpNR1/61hVORIc=\n") + assert.Contains(t, content, "A:x86_64\n") + assert.Contains(t, content, "C:Q1kbH5WoIPFccQYyATanaKXd2cJcc=\n") + assert.NotContains(t, content, "A:noarch\n") + + // noarch package should be available with every architecture requested + for _, arch := range []string{alpine_module.NoArch, "x86_64", "my_arch"} { + req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s/gitea-noarch-1.4-r0.apk", rootURL, branch, repository, arch)) + MakeRequest(t, req, http.StatusOK) + } + + req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/noarch/gitea-noarch-1.4-r0.apk", rootURL, branch, repository)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusNoContent) + }) }) } } From 4ccf5ab330c9ce8959aa6734c2e6fee282619ba5 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sun, 25 Feb 2024 16:42:36 +0100 Subject: [PATCH 48/79] Add missing space (#29393) --- templates/repo/diff/options_dropdown.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/diff/options_dropdown.tmpl b/templates/repo/diff/options_dropdown.tmpl index 3bcb877cc6..b7c46dd846 100644 --- a/templates/repo/diff/options_dropdown.tmpl +++ b/templates/repo/diff/options_dropdown.tmpl @@ -13,7 +13,7 @@ {{ctx.Locale.Tr "repo.diff.download_diff"}} {{end}} {{ctx.Locale.Tr "repo.pulls.expand_files"}} - {{ctx.Locale.Tr "repo.pulls.collapse_files"}} + {{ctx.Locale.Tr "repo.pulls.collapse_files"}} {{if .Issue.Index}} {{if .ShowOutdatedComments}} From f4b92578b4601bc6e9b631b9a5a5f3766c27b0cb Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 25 Feb 2024 17:46:46 +0100 Subject: [PATCH 49/79] Add tailwindcss (#29357) This will get tailwindcss working on a basic level. It provides only the utility classes, e.g. no tailwind base which we don't need because we have our own CSS reset. Without the base, we also do not have their CSS variables so a small amount of features do not work and I removed the generated classes for them. ***Note for future developers: This currently uses a `tw-` prefix, so we use it like `tw-p-3`.***
Currently added CSS, all false-positives ``` .\!visible{ visibility: visible !important } .visible{ visibility: visible } .invisible{ visibility: hidden } .collapse{ visibility: collapse } .static{ position: static } .\!fixed{ position: fixed !important } .absolute{ position: absolute } .relative{ position: relative } .sticky{ position: sticky } .left-10{ left: 2.5rem } .isolate{ isolation: isolate } .float-right{ float: right } .float-left{ float: left } .mr-2{ margin-right: 0.5rem } .mr-3{ margin-right: 0.75rem } .\!block{ display: block !important } .block{ display: block } .inline-block{ display: inline-block } .inline{ display: inline } .flex{ display: flex } .inline-flex{ display: inline-flex } .\!table{ display: table !important } .inline-table{ display: inline-table } .table-caption{ display: table-caption } .table-cell{ display: table-cell } .table-column{ display: table-column } .table-column-group{ display: table-column-group } .table-footer-group{ display: table-footer-group } .table-header-group{ display: table-header-group } .table-row-group{ display: table-row-group } .table-row{ display: table-row } .flow-root{ display: flow-root } .inline-grid{ display: inline-grid } .contents{ display: contents } .list-item{ display: list-item } .\!hidden{ display: none !important } .hidden{ display: none } .flex-shrink{ flex-shrink: 1 } .shrink{ flex-shrink: 1 } .flex-grow{ flex-grow: 1 } .grow{ flex-grow: 1 } .border-collapse{ border-collapse: collapse } .select-all{ user-select: all } .resize{ resize: both } .flex-wrap{ flex-wrap: wrap } .overflow-visible{ overflow: visible } .rounded{ border-radius: 0.25rem } .border{ border-width: 1px } .text-justify{ text-align: justify } .uppercase{ text-transform: uppercase } .lowercase{ text-transform: lowercase } .capitalize{ text-transform: capitalize } .italic{ font-style: italic } .text-red{ color: var(--color-red) } .text-shadow{ color: var(--color-shadow) } .underline{ text-decoration-line: underline } .overline{ text-decoration-line: overline } .line-through{ text-decoration-line: line-through } .outline{ outline-style: solid } .ease-in{ transition-timing-function: cubic-bezier(0.4, 0, 1, 1) } .ease-in-out{ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) } .ease-out{ transition-timing-function: cubic-bezier(0, 0, 0.2, 1) } ```
--------- Co-authored-by: Giteabot --- .stylelintrc.yaml | 2 +- Makefile | 2 +- package-lock.json | 514 ++++++++++++++++++++++++++++---- package.json | 4 + tailwind.config.js | 39 +++ templates/devtest/gitea-ui.tmpl | 6 + web_src/css/base.css | 8 + web_src/css/index.css | 2 + webpack.config.js | 11 + 9 files changed, 530 insertions(+), 58 deletions(-) create mode 100644 tailwind.config.js diff --git a/.stylelintrc.yaml b/.stylelintrc.yaml index a44294ee76..7dd0a566f2 100644 --- a/.stylelintrc.yaml +++ b/.stylelintrc.yaml @@ -98,7 +98,7 @@ rules: at-rule-allowed-list: null at-rule-disallowed-list: null at-rule-empty-line-before: null - at-rule-no-unknown: true + at-rule-no-unknown: [true, {ignoreAtRules: [tailwind]}] at-rule-no-vendor-prefix: true at-rule-property-required-list: null block-no-empty: true diff --git a/Makefile b/Makefile index 4ef02c6c54..0e9e792053 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,7 @@ GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/m FOMANTIC_WORK_DIR := web_src/fomantic WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f) -WEBPACK_CONFIGS := webpack.config.js +WEBPACK_CONFIGS := webpack.config.js tailwind.config.js WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/img/webpack diff --git a/package-lock.json b/package-lock.json index f1f8cc4705..8f641edb5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.6", "css-loader": "6.10.0", + "css-variables-parser": "1.0.1", "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", @@ -41,9 +42,12 @@ "monaco-editor": "0.46.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", + "postcss": "8.4.35", + "postcss-loader": "8.1.0", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", "swagger-ui-dist": "5.11.6", + "tailwindcss": "3.4.1", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", @@ -105,6 +109,17 @@ "node": ">=0.10.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@asyncapi/specs": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.3.1.tgz", @@ -118,7 +133,6 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -131,7 +145,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -143,7 +156,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -157,7 +169,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -165,14 +176,12 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -181,7 +190,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -190,7 +198,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -202,7 +209,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -211,7 +217,6 @@ "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -225,7 +230,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -237,7 +241,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -251,7 +254,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -259,14 +261,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -275,7 +275,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -283,14 +282,12 @@ "node_modules/@babel/highlight/node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1111,7 +1108,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1128,7 +1124,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1140,7 +1135,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -1151,14 +1145,12 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1175,7 +1167,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1190,7 +1181,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1381,7 +1371,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -3086,6 +3075,28 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3400,6 +3411,14 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -3534,11 +3553,18 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001587", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz", @@ -3646,6 +3672,40 @@ "node": "*" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -3857,7 +3917,6 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -3975,6 +4034,35 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, + "node_modules/css-variables-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-variables-parser/-/css-variables-parser-1.0.1.tgz", + "integrity": "sha512-GWaqrwGtAWVr/yjjE17iyvbcy+W3voe0vko1/xLCwFeYd3kTLstzUdVH+g5TTXejrtlsb1FS4L9rP6PmeTa8wQ==", + "dependencies": { + "postcss": "^7.0.36" + } + }, + "node_modules/css-variables-parser/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/css-variables-parser/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -4661,6 +4749,11 @@ "node": ">=6" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", @@ -4690,6 +4783,11 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4774,8 +4872,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/easymde": { "version": "2.18.0", @@ -4839,7 +4936,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, "engines": { "node": ">=6" } @@ -4859,7 +4955,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -6154,7 +6249,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -6203,7 +6297,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -6390,7 +6483,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -6815,7 +6907,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6935,8 +7026,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { "version": "2.0.0", @@ -6965,6 +7055,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -7377,7 +7478,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -7418,6 +7518,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -7779,11 +7887,18 @@ "node": ">=8" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/linkify-it": { "version": "5.0.0", @@ -8707,7 +8822,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -8759,6 +8873,16 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -8902,7 +9026,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8969,6 +9092,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -9148,7 +9279,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -9160,7 +9290,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -9230,7 +9359,6 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -9246,7 +9374,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, "engines": { "node": "14 || >=16.14" } @@ -9296,6 +9423,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -9462,6 +9605,70 @@ "node": "^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.0.tgz", + "integrity": "sha512-AbperNcX3rlob7Ay7A/HQcrofug1caABBkopoFeOQMspZBqcqj6giYn1Bwey/0uiOPAcR+NQD0I2HC7rXzk91w==", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, "node_modules/postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", @@ -9517,6 +9724,24 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, "node_modules/postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", @@ -9754,6 +9979,14 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -9856,6 +10089,17 @@ "node": ">=8" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -10037,7 +10281,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -10398,7 +10641,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -10609,7 +10851,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10680,7 +10921,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10999,6 +11239,56 @@ "node": ">= 8" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/superstruct": { "version": "0.10.13", "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.10.13.tgz", @@ -11144,6 +11434,87 @@ "node": ">=10.0.0" } }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -11258,6 +11629,25 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/throttle-debounce": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.0.tgz", @@ -11380,6 +11770,11 @@ "node": ">=6.10" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -12431,7 +12826,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -12569,6 +12963,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", diff --git a/package.json b/package.json index fdea78ca29..3f0f9103cf 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.6", "css-loader": "6.10.0", + "css-variables-parser": "1.0.1", "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", @@ -40,9 +41,12 @@ "monaco-editor": "0.46.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", + "postcss": "8.4.35", + "postcss-loader": "8.1.0", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", "swagger-ui-dist": "5.11.6", + "tailwindcss": "3.4.1", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000000..8c474c33a8 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,39 @@ +import {readFileSync} from 'node:fs'; +import {env} from 'node:process'; +import {parse} from 'css-variables-parser'; + +const isProduction = env.NODE_ENV !== 'development'; + +export default { + prefix: 'tw-', + content: [ + isProduction && '!./templates/devtest/**/*', + isProduction && '!./web_src/js/standalone/devtest.js', + './templates/**/*.tmpl', + './web_src/**/*.{js,vue}', + ].filter(Boolean), + blocklist: [ + // classes that don't work without CSS variables from "@tailwind base" which we don't use + 'transform', 'shadow', 'ring', 'blur', 'grayscale', 'invert', '!invert', 'filter', '!filter', + 'backdrop-filter', + // unneeded classes + '[-a-zA-Z:0-9_.]', + ], + theme: { + colors: { + // make `tw-bg-red` etc work with our CSS variables + ...Object.fromEntries( + Object.keys(parse([ + readFileSync(new URL('web_src/css/themes/theme-gitea-light.css', import.meta.url), 'utf8'), + readFileSync(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url), 'utf8'), + ].join('\n'), {})).filter((prop) => prop.startsWith('color-')).map((prop) => { + const color = prop.substring(6); + return [color, `var(--color-${color})`]; + }) + ), + inherit: 'inherit', + current: 'currentcolor', + transparent: 'transparent', + }, + }, +}; diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index 73293ddf48..ccf188609c 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -275,6 +275,12 @@
ps: no JS code attached, so just a layout
{{template "shared/combomarkdowneditor" .}}
+ +

Tailwind CSS Demo

+
+ +
+
{{template "base/footer" .}} diff --git a/web_src/css/base.css b/web_src/css/base.css index 76ecfc9bf5..280808a5ce 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -29,6 +29,14 @@ --fonts-regular: var(--fonts-override, var(--fonts-proportional)), "Noto Sans", "Liberation Sans", sans-serif, var(--fonts-emoji); } +*, ::before, ::after { + /* these are needed for tailwind borders to work because we do not load tailwind's base + https://github.com/tailwindlabs/tailwindcss/blob/master/src/css/preflight.css */ + border-width: 0; + border-style: solid; + border-color: currentcolor; +} + textarea { font-family: var(--fonts-regular); } diff --git a/web_src/css/index.css b/web_src/css/index.css index f893531b78..ab925a4aa0 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -59,4 +59,6 @@ @import "./explore.css"; @import "./review.css"; @import "./actions.css"; + +@tailwind utilities; @import "./helpers.css"; diff --git a/webpack.config.js b/webpack.config.js index 82d76d9e8d..3973d85344 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,6 +11,8 @@ import webpack from 'webpack'; import {fileURLToPath} from 'node:url'; import {readFileSync} from 'node:fs'; import {env} from 'node:process'; +import tailwindcss from 'tailwindcss'; +import tailwindConfig from './tailwind.config.js'; const {EsbuildPlugin} = EsBuildLoader; const {SourceMapDevToolPlugin, DefinePlugin} = webpack; @@ -145,6 +147,15 @@ export default { import: {filter: filterCssImport}, }, }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + map: false, // https://github.com/postcss/postcss/issues/1914 + plugins: [tailwindcss(tailwindConfig)], + }, + }, + } ], }, { From ed3892d8430652c2bc8e2af21844d14769825e8a Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sun, 25 Feb 2024 18:53:44 +0200 Subject: [PATCH 50/79] Remove jQuery AJAX from the archive download links (#29380) - Removed all jQuery AJAX calls and replaced with our fetch wrapper - Tested the repo archive download links dropdown functionality and it works as before # Demo using `fetch` instead of jQuery AJAX ![action](https://github.com/go-gitea/gitea/assets/20454870/db791249-bca1-4d22-ac5e-623f68023e15) --------- Signed-off-by: Yarden Shoham --- web_src/js/features/repo-common.js | 58 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js index 3573e4d50b..a8221bbea8 100644 --- a/web_src/js/features/repo-common.js +++ b/web_src/js/features/repo-common.js @@ -1,38 +1,34 @@ import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; +import {POST} from '../modules/fetch.js'; -const {csrfToken} = window.config; - -function getArchive($target, url, first) { - $.ajax({ - url, - type: 'POST', - data: { - _csrf: csrfToken, - }, - complete(xhr) { - if (xhr.status === 200) { - if (!xhr.responseJSON) { - // XXX Shouldn't happen? - $target.closest('.dropdown').children('i').removeClass('loading'); - return; - } - - if (!xhr.responseJSON.complete) { - $target.closest('.dropdown').children('i').addClass('loading'); - // Wait for only three quarters of a second initially, in case it's - // quickly archived. - setTimeout(() => { - getArchive($target, url, false); - }, first ? 750 : 2000); - } else { - // We don't need to continue checking. - $target.closest('.dropdown').children('i').removeClass('loading'); - window.location.href = url; - } +async function getArchive($target, url, first) { + try { + const response = await POST(url); + if (response.status === 200) { + const data = await response.json(); + if (!data) { + // XXX Shouldn't happen? + $target.closest('.dropdown').children('i').removeClass('loading'); + return; } - }, - }); + + if (!data.complete) { + $target.closest('.dropdown').children('i').addClass('loading'); + // Wait for only three quarters of a second initially, in case it's + // quickly archived. + setTimeout(() => { + getArchive($target, url, false); + }, first ? 750 : 2000); + } else { + // We don't need to continue checking. + $target.closest('.dropdown').children('i').removeClass('loading'); + window.location.href = url; + } + } + } catch { + $target.closest('.dropdown').children('i').removeClass('loading'); + } } export function initRepoArchiveLinks() { From 49e482674700e184aa84806acfb7edaae0554291 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 26 Feb 2024 05:55:00 +0800 Subject: [PATCH 51/79] Refactor "user/active" related logic (#29390) And add more tests. Remove a lot of fragile "if" blocks. The old logic is kept as-is. --- routers/web/auth/auth.go | 127 ++++++++++++----------- templates/user/auth/activate.tmpl | 48 +++------ templates/user/auth/activate_prompt.tmpl | 15 +++ tests/integration/signup_test.go | 50 ++++++++- 4 files changed, 146 insertions(+), 94 deletions(-) create mode 100644 templates/user/auth/activate_prompt.tmpl diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 3de1f3373d..a30ee0ce54 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -7,6 +7,7 @@ package auth import ( "errors" "fmt" + "html/template" "net/http" "strings" @@ -37,12 +38,10 @@ import ( ) const ( - // tplSignIn template for sign in page - tplSignIn base.TplName = "user/auth/signin" - // tplSignUp template path for sign up page - tplSignUp base.TplName = "user/auth/signup" - // TplActivate template path for activate user - TplActivate base.TplName = "user/auth/activate" + tplSignIn base.TplName = "user/auth/signin" // for sign in page + tplSignUp base.TplName = "user/auth/signup" // for sign up page + TplActivate base.TplName = "user/auth/activate" // for activate user + TplActivatePrompt base.TplName = "user/auth/activate_prompt" // for showing a message for user activation ) // autoSignIn reads cookie and try to auto-login. @@ -613,72 +612,83 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. } } - // Send confirmation email - if !u.IsActive && u.ID > 1 { - if setting.Service.RegisterManualConfirm { - ctx.Data["ManualActivationOnly"] = true - ctx.HTML(http.StatusOK, TplActivate) - return false - } + // for active user or the first (admin) user, we don't need to send confirmation email + if u.IsActive || u.ID == 1 { + return true + } - mailer.SendActivateAccountMail(ctx.Locale, u) - - ctx.Data["IsSendRegisterMail"] = true - ctx.Data["Email"] = u.Email - ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale) - ctx.HTML(http.StatusOK, TplActivate) - - if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { - log.Error("Set cache(MailResendLimit) fail: %v", err) - } + if setting.Service.RegisterManualConfirm { + renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.manual_activation_only")) return false } - return true + sendActivateEmail(ctx, u) + return false +} + +func renderActivationPromptMessage(ctx *context.Context, msg template.HTML) { + ctx.Data["ActivationPromptMessage"] = msg + ctx.HTML(http.StatusOK, TplActivatePrompt) +} + +func sendActivateEmail(ctx *context.Context, u *user_model.User) { + if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) { + renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.resent_limit_prompt")) + return + } + + if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { + log.Error("Set cache(MailResendLimit) fail: %v", err) + renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.resent_limit_prompt")) + return + } + + mailer.SendActivateAccountMail(ctx.Locale, u) + + activeCodeLives := timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale) + msgHTML := ctx.Locale.Tr("auth.confirmation_mail_sent_prompt", u.Email, activeCodeLives) + renderActivationPromptMessage(ctx, msgHTML) +} + +func renderActivationVerifyPassword(ctx *context.Context, code string) { + ctx.Data["ActivationCode"] = code + ctx.Data["NeedVerifyLocalPassword"] = true + ctx.HTML(http.StatusOK, TplActivate) } // Activate render activate user page func Activate(ctx *context.Context) { code := ctx.FormString("code") - if len(code) == 0 { - ctx.Data["IsActivatePage"] = true - if ctx.Doer == nil || ctx.Doer.IsActive { - ctx.NotFound("invalid user", nil) + if code == "" { + if ctx.Doer == nil { + ctx.Redirect(setting.AppSubURL + "/user/login") + return + } else if ctx.Doer.IsActive { + ctx.Redirect(setting.AppSubURL + "/") return } - // Resend confirmation email. - if setting.Service.RegisterEmailConfirm { - if ctx.Cache.IsExist("MailResendLimit_" + ctx.Doer.LowerName) { - ctx.Data["ResendLimited"] = true - } else { - ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale) - mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer) - if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil { - log.Error("Set cache(MailResendLimit) fail: %v", err) - } - } - } else { - ctx.Data["ServiceNotEnabled"] = true + if setting.MailService == nil || !setting.Service.RegisterEmailConfirm { + renderActivationPromptMessage(ctx, ctx.Tr("auth.disable_register_mail")) + return } - ctx.HTML(http.StatusOK, TplActivate) + + // Resend confirmation email. + sendActivateEmail(ctx, ctx.Doer) return } + // TODO: ctx.Doer/ctx.Data["SignedUser"] could be nil or not the same user as the one being activated user := user_model.VerifyUserActiveCode(ctx, code) - // if code is wrong - if user == nil { - ctx.Data["IsCodeInvalid"] = true - ctx.HTML(http.StatusOK, TplActivate) + if user == nil { // if code is wrong + renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.invalid_code")) return } // if account is local account, verify password if user.LoginSource == 0 { - ctx.Data["Code"] = code - ctx.Data["NeedsPassword"] = true - ctx.HTML(http.StatusOK, TplActivate) + renderActivationVerifyPassword(ctx, code) return } @@ -688,31 +698,28 @@ func Activate(ctx *context.Context) { // ActivatePost handles account activation with password check func ActivatePost(ctx *context.Context) { code := ctx.FormString("code") - if len(code) == 0 { + if code == "" || (ctx.Doer != nil && ctx.Doer.IsActive) { ctx.Redirect(setting.AppSubURL + "/user/activate") return } + // TODO: ctx.Doer/ctx.Data["SignedUser"] could be nil or not the same user as the one being activated user := user_model.VerifyUserActiveCode(ctx, code) - // if code is wrong - if user == nil { - ctx.Data["IsCodeInvalid"] = true - ctx.HTML(http.StatusOK, TplActivate) + if user == nil { // if code is wrong + renderActivationPromptMessage(ctx, ctx.Locale.Tr("auth.invalid_code")) return } // if account is local account, verify password if user.LoginSource == 0 { password := ctx.FormString("password") - if len(password) == 0 { - ctx.Data["Code"] = code - ctx.Data["NeedsPassword"] = true - ctx.HTML(http.StatusOK, TplActivate) + if password == "" { + renderActivationVerifyPassword(ctx, code) return } if !user.ValidatePassword(password) { - ctx.Data["IsPasswordInvalid"] = true - ctx.HTML(http.StatusOK, TplActivate) + ctx.Flash.Error(ctx.Locale.Tr("auth.invalid_password"), true) + renderActivationVerifyPassword(ctx, code) return } } diff --git a/templates/user/auth/activate.tmpl b/templates/user/auth/activate.tmpl index 9cd1712275..51dc1eb6a6 100644 --- a/templates/user/auth/activate.tmpl +++ b/templates/user/auth/activate.tmpl @@ -9,40 +9,22 @@
{{template "base/alert" .}} - {{if .IsActivatePage}} - {{if .ServiceNotEnabled}} -

{{ctx.Locale.Tr "auth.disable_register_mail"}}

- {{else if .ResendLimited}} -

{{ctx.Locale.Tr "auth.resent_limit_prompt"}}

- {{else}} -

{{ctx.Locale.Tr "auth.confirmation_mail_sent_prompt" .SignedUser.Email .ActiveCodeLives}}

- {{end}} + {{if .NeedVerifyLocalPassword}} +
+ + +
+
+ + +
+ {{else}} - {{if .NeedsPassword}} -
- - -
-
- - -
- - {{else if .IsSendRegisterMail}} -

{{ctx.Locale.Tr "auth.confirmation_mail_sent_prompt" .Email .ActiveCodeLives}}

- {{else if .IsCodeInvalid}} -

{{ctx.Locale.Tr "auth.invalid_code"}}

- {{else if .IsPasswordInvalid}} -

{{ctx.Locale.Tr "auth.invalid_password"}}

- {{else if .ManualActivationOnly}} -

{{ctx.Locale.Tr "auth.manual_activation_only"}}

- {{else}} -

{{ctx.Locale.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email}}

-
-
- -
- {{end}} +

{{ctx.Locale.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email}}

+
+
+ +
{{end}}
diff --git a/templates/user/auth/activate_prompt.tmpl b/templates/user/auth/activate_prompt.tmpl new file mode 100644 index 0000000000..237244df8c --- /dev/null +++ b/templates/user/auth/activate_prompt.tmpl @@ -0,0 +1,15 @@ +{{template "base/head" .}} +
+
+
+

+ {{ctx.Locale.Tr "auth.active_your_account"}} +

+
+ {{template "base/alert" .}} +

{{.ActivationPromptMessage}}

+
+
+
+
+{{template "base/footer" .}} diff --git a/tests/integration/signup_test.go b/tests/integration/signup_test.go index 859f873f85..fbf586f696 100644 --- a/tests/integration/signup_test.go +++ b/tests/integration/signup_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/tests" @@ -58,7 +59,7 @@ func TestSignupAsRestricted(t *testing.T) { assert.True(t, user2.IsRestricted) } -func TestSignupEmail(t *testing.T) { +func TestSignupEmailValidation(t *testing.T) { defer tests.PrepareTestEnv(t)() setting.Service.EnableCaptcha = false @@ -91,3 +92,50 @@ func TestSignupEmail(t *testing.T) { } } } + +func TestSignupEmailActive(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Service.RegisterEmailConfirm, true)() + + // try to sign up and send the activation email + req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{ + "user_name": "test-user-1", + "email": "email-1@example.com", + "password": "password1", + "retype": "password1", + }) + resp := MakeRequest(t, req, http.StatusOK) + assert.Contains(t, resp.Body.String(), `A new confirmation email has been sent to email-1@example.com.`) + + // access "user/active" means trying to re-send the activation email + session := loginUserWithPassword(t, "test-user-1", "password1") + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/activate"), http.StatusOK) + assert.Contains(t, resp.Body.String(), "You have already requested an activation email recently") + + // access "user/active" with a valid activation code, then get the "verify password" page + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "test-user-1"}) + activationCode := user.GenerateEmailActivateCode(user.Email) + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/activate?code="+activationCode), http.StatusOK) + assert.Contains(t, resp.Body.String(), ` Date: Mon, 26 Feb 2024 00:35:52 +0100 Subject: [PATCH 52/79] Improve Documentation for Restoration from backup (#29321) Comment the default path for repos and suggest using doctor for when things are stuck --- docs/content/administration/backup-and-restore.en-us.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/content/administration/backup-and-restore.en-us.md b/docs/content/administration/backup-and-restore.en-us.md index d46efecf99..451ef5c944 100644 --- a/docs/content/administration/backup-and-restore.en-us.md +++ b/docs/content/administration/backup-and-restore.en-us.md @@ -92,7 +92,7 @@ cd gitea-dump-1610949662 mv app.ini /etc/gitea/conf/app.ini mv data/* /var/lib/gitea/data/ mv log/* /var/lib/gitea/log/ -mv repos/* /var/lib/gitea/gitea-repositories/ +mv repos/* /var/lib/gitea/data/gitea-repositories/ chown -R gitea:gitea /etc/gitea/conf/app.ini /var/lib/gitea # mysql @@ -111,6 +111,8 @@ With Gitea running, and from the directory Gitea's binary is located, execute: ` This ensures that application and configuration file paths in repository Git Hooks are consistent and applicable to the current installation. If these paths are not updated, repository `push` actions will fail. +If you still have issues, consider running `./gitea doctor check` to inspect possible errors (or run with `--fix`). + ### Using Docker (`restore`) There is also no support for a recovery command in a Docker-based gitea instance. The restore process contains the same steps as described in the previous section but with different paths. From f38888bc7834899777bda1a271e166d3836524cf Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 26 Feb 2024 00:24:51 +0000 Subject: [PATCH 53/79] [skip ci] Updated translations via Crowdin --- options/locale/locale_fr-FR.ini | 37 +++++++++++++++++++++++++++++++-- options/locale/locale_lv-LV.ini | 8 +++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 628bc2a777..7fd79446cc 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -123,6 +123,7 @@ pin=Épingler unpin=Désépingler artifacts=Artefacts +confirm_delete_artifact=Êtes-vous sûr de vouloir supprimer l‘artefact « %s » ? archived=Archivé @@ -361,6 +362,7 @@ disable_register_prompt=Les inscriptions sont désactivées. Veuillez contacter disable_register_mail=La confirmation par courriel à l’inscription est désactivée. manual_activation_only=Contactez l'administrateur de votre site pour terminer l'activation. remember_me=Mémoriser cet appareil +remember_me.compromised=Le jeton de connexion n’est plus valide, ce qui peut indiquer un compte compromis. Veuillez inspecter les activités inhabituelles de votre compte. forgot_password_title=Mot de passe oublié forgot_password=Mot de passe oublié ? sign_up_now=Pas de compte ? Inscrivez-vous maintenant. @@ -587,6 +589,7 @@ org_still_own_packages=Cette organisation possède encore un ou plusieurs paquet target_branch_not_exist=La branche cible n'existe pas. +admin_cannot_delete_self=Vous ne pouvez pas vous supprimer vous-même lorsque vous êtes admin. Veuillez d’abord supprimer vos privilèges d’administrateur. [user] change_avatar=Changer votre avatar… @@ -794,7 +797,7 @@ valid_until_date=Valable jusqu'au %s valid_forever=Valide pour toujours last_used=Dernière utilisation le no_activity=Aucune activité récente -can_read_info=Lue(s) +can_read_info=Lecture can_write_info=Écriture key_state_desc=Cette clé a été utilisée au cours des 7 derniers jours token_state_desc=Ce jeton a été utilisé au cours des 7 derniers jours @@ -827,7 +830,7 @@ permissions_public_only=Publique uniquement permissions_access_all=Tout (public, privé et limité) select_permissions=Sélectionner les autorisations permission_no_access=Aucun accès -permission_read=Lue(s) +permission_read=Lecture permission_write=Lecture et écriture access_token_desc=Les autorisations des jetons sélectionnées se limitent aux
routes API correspondantes. Lisez la documentation pour plus d’informations. at_least_one_permission=Vous devez sélectionner au moins une permission pour créer un jeton. @@ -984,6 +987,7 @@ mirror_prune=Purger mirror_prune_desc=Supprimer les références externes obsolètes mirror_interval=Intervalle de synchronisation (les unités de temps valides sont 'h', 'm' et 's'). 0 pour désactiver la synchronisation automatique. (Intervalle minimum : %s) mirror_interval_invalid=L'intervalle de synchronisation est invalide. +mirror_sync=synchronisé mirror_sync_on_commit=Synchroniser quand les révisions sont soumis mirror_address=Cloner depuis une URL mirror_address_desc=Insérez tous les identifiants requis dans la section Autorisation. @@ -1034,6 +1038,7 @@ desc.public=Publique desc.template=Modèle desc.internal=Interne desc.archived=Archivé +desc.sha256=SHA256 template.items=Élément du modèle template.git_content=Contenu Git (branche par défaut) @@ -1184,6 +1189,8 @@ audio_not_supported_in_browser=Votre navigateur ne supporte pas la balise « au stored_lfs=Stocké avec Git LFS symbolic_link=Lien symbolique executable_file=Fichiers exécutables +vendored=Externe +generated=Générée commit_graph=Graphe des révisions commit_graph.select=Sélectionner les branches commit_graph.hide_pr_refs=Masquer les demandes d'ajout @@ -1765,6 +1772,7 @@ pulls.merge_pull_request=Créer une révision de fusion pulls.rebase_merge_pull_request=Rebaser puis avancer rapidement pulls.rebase_merge_commit_pull_request=Rebaser puis créer une révision de fusion pulls.squash_merge_pull_request=Créer une révision de concaténation +pulls.fast_forward_only_merge_pull_request=Avance rapide uniquement pulls.merge_manually=Fusionner manuellement pulls.merge_commit_id=L'ID de la révision de fusion pulls.require_signed_wont_sign=La branche nécessite des révisions signées mais cette fusion ne sera pas signée @@ -1901,6 +1909,7 @@ wiki.page_name_desc=Entrez un nom pour cette page Wiki. Certains noms spéciaux wiki.original_git_entry_tooltip=Voir le fichier Git original au lieu d'utiliser un lien convivial. activity=Activité +activity.navbar.contributors=Contributeurs activity.period.filter_label=Période : activity.period.daily=1 jour activity.period.halfweekly=3 jours @@ -1966,7 +1975,10 @@ activity.git_stats_and_deletions=et activity.git_stats_deletion_1=%d suppression activity.git_stats_deletion_n=%d suppressions +contributors.contribution_type.filter_label=Type de contribution : contributors.contribution_type.commits=Révisions +contributors.contribution_type.additions=Ajouts +contributors.contribution_type.deletions=Suppressions search=Chercher search.search_repo=Rechercher dans le dépôt @@ -2314,6 +2326,8 @@ settings.protect_approvals_whitelist_users=Évaluateurs autorisés : settings.protect_approvals_whitelist_teams=Équipes d’évaluateurs autorisés : settings.dismiss_stale_approvals=Révoquer automatiquement les approbations périmées settings.dismiss_stale_approvals_desc=Lorsque des nouvelles révisions changent le contenu de la demande d’ajout, les approbations existantes sont révoquées. +settings.ignore_stale_approvals=Ignorer les approbations obsolètes +settings.ignore_stale_approvals_desc=Ignorer les approbations d’anciennes révisions (évaluations obsolètes) du décompte des approbations de la demande d’ajout. Non pertinent quand les évaluations obsolètes sont déjà révoquées. settings.require_signed_commits=Exiger des révisions signées settings.require_signed_commits_desc=Rejeter les soumissions sur cette branche lorsqu'ils ne sont pas signés ou vérifiables. settings.protect_branch_name_pattern=Motif de nom de branche protégé @@ -2369,6 +2383,7 @@ settings.archive.error=Une erreur s'est produite lors de l'archivage du dépôt. settings.archive.error_ismirror=Vous ne pouvez pas archiver un dépôt en miroir. settings.archive.branchsettings_unavailable=Le paramétrage des branches n'est pas disponible quand le dépôt est archivé. settings.archive.tagsettings_unavailable=Le paramétrage des étiquettes n'est pas disponible si le dépôt est archivé. +settings.archive.mirrors_unavailable=Les miroirs ne sont pas disponibles lorsque le dépôt est archivé. settings.unarchive.button=Réhabiliter settings.unarchive.header=Réhabiliter ce dépôt settings.unarchive.text=Réhabiliter un dépôt dégèle les actions de révisions et de soumissions, la gestion des tickets et des demandes d'ajouts. @@ -2568,6 +2583,11 @@ error.csv.unexpected=Impossible de visualiser ce fichier car il contient un cara error.csv.invalid_field_count=Impossible de visualiser ce fichier car il contient un nombre de champs incorrect à la ligne %d. [graphs] +component_loading=Chargement de %s… +component_loading_failed=Impossible de charger %s. +component_loading_info=Ça prend son temps… +component_failed_to_load=Une erreur inattendue s’est produite. +contributors.what=contributions [org] org_name_holder=Nom de l'organisation @@ -2695,6 +2715,7 @@ teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindr [admin] dashboard=Tableau de bord +self_check=Autodiagnostique identity_access=Identité et accès users=Comptes utilisateurs organizations=Organisations @@ -2740,6 +2761,7 @@ dashboard.delete_missing_repos=Supprimer tous les dépôts dont les fichiers Git dashboard.delete_missing_repos.started=Tâche de suppression de tous les dépôts sans fichiers Git démarrée. dashboard.delete_generated_repository_avatars=Supprimer les avatars de dépôt générés dashboard.sync_repo_branches=Synchroniser les branches manquantes depuis Git vers la base de donnée. +dashboard.sync_repo_tags=Synchroniser les étiquettes git depuis les dépôts vers la base de données dashboard.update_mirrors=Actualiser les miroirs dashboard.repo_health_check=Vérifier l'état de santé de tous les dépôts dashboard.check_repo_stats=Voir les statistiques de tous les dépôts @@ -2794,6 +2816,7 @@ dashboard.stop_endless_tasks=Arrêter les tâches sans fin dashboard.cancel_abandoned_jobs=Annuler les jobs abandonnés dashboard.start_schedule_tasks=Démarrer les tâches planifiées dashboard.sync_branch.started=Début de la synchronisation des branches +dashboard.sync_tag.started=Synchronisation des étiquettes dashboard.rebuild_issue_indexer=Reconstruire l’indexeur des tickets users.user_manage_panel=Gestion du compte utilisateur @@ -3220,6 +3243,12 @@ notices.desc=Description notices.op=Opération notices.delete_success=Les informations systèmes ont été supprimées. +self_check.no_problem_found=Aucun problème trouvé pour l’instant. +self_check.database_collation_mismatch=Exige que la base de données utilise la collation %s. +self_check.database_collation_case_insensitive=La base de données utilise la collation %s, insensible à la casse. Bien que Gitea soit compatible, il peut y avoir quelques rares cas qui ne fonctionnent pas comme prévu. +self_check.database_inconsistent_collation_columns=La base de données utilise la collation %s, mais ces colonnes utilisent des collations différentes. Cela peut causer des problèmes imprévus. +self_check.database_fix_mysql=Pour les utilisateurs de MySQL ou MariaDB, vous pouvez utiliser la commande « gitea doctor convert » dans un terminal ou exécuter une requête du type « ALTER … COLLATE ... » pour résoudre les problèmes de collation. +self_check.database_fix_mssql=Pour les utilisateurs de MSSQL, vous ne pouvez résoudre le problème qu’en exécutant une requête SQL du type « ALTER … COLLATE … ». [action] create_repo=a créé le dépôt %s @@ -3407,6 +3436,7 @@ rpm.distros.suse=sur les distributions basées sur SUSE rpm.install=Pour installer le paquet, exécutez la commande suivante : rpm.repository=Informations sur le Dépôt rpm.repository.architectures=Architectures +rpm.repository.multiple_groups=Ce paquet est disponible en plusieurs groupes. rubygems.install=Pour installer le paquet en utilisant gem, exécutez la commande suivante : rubygems.install2=ou ajoutez-le au Gemfile : rubygems.dependencies.runtime=Dépendances d'exécution @@ -3539,6 +3569,8 @@ runs.actors_no_select=Tous les acteurs runs.status_no_select=Touts les statuts runs.no_results=Aucun résultat correspondant. runs.no_workflows=Il n'y a pas encore de workflows. +runs.no_workflows.quick_start=Vous découvrez les Actions Gitea ? Consultez le didacticiel. +runs.no_workflows.documentation=Pour plus d’informations sur les actions Gitea, voir la documentation. runs.no_runs=Le flux de travail n'a pas encore d'exécution. runs.empty_commit_message=(message de révision vide) @@ -3557,6 +3589,7 @@ variables.none=Il n'y a pas encore de variables. variables.deletion=Retirer la variable variables.deletion.description=La suppression d’une variable est permanente et ne peut être défaite. Continuer ? variables.description=Les variables sont passées aux actions et ne peuvent être lues autrement. +variables.id_not_exist=La variable avec l’ID %d n’existe pas. variables.edit=Modifier la variable variables.deletion.failed=Impossible de retirer la variable. variables.deletion.success=La variable a bien été retirée. diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 96db89d810..3c3513ad48 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1035,6 +1035,7 @@ desc.public=Publisks desc.template=Sagatave desc.internal=Iekšējs desc.archived=Arhivēts +desc.sha256=SHA256 template.items=Sagataves ieraksti template.git_content=Git saturs (noklusētais atzars) @@ -2569,6 +2570,10 @@ error.csv.unexpected=Nevar attēlot šo failu, jo tas satur neparedzētu simbolu error.csv.invalid_field_count=Nevar attēlot šo failu, jo tas satur nepareizu skaitu ar laukiem %d. līnijā. [graphs] +component_loading=Ielādē %s... +component_loading_failed=Nevarēja ielādēt %s +component_loading_info=Šis var aizņemt kādu brīdi… +component_failed_to_load=Atgadījās neparedzēta kļūda. [org] org_name_holder=Organizācijas nosaukums @@ -2696,6 +2701,7 @@ teams.invite.description=Nospiediet pogu zemāk, lai pievienotos komandai. [admin] dashboard=Infopanelis +self_check=Pašpārbaude identity_access=Identitāte un piekļuve users=Lietotāju konti organizations=Organizācijas @@ -3221,6 +3227,7 @@ notices.desc=Apraksts notices.op=Op. notices.delete_success=Sistēmas paziņojumi ir dzēsti. +self_check.no_problem_found=Pašlaik nav atrasta neviena problēma. [action] create_repo=izveidoja repozitoriju %s @@ -3558,6 +3565,7 @@ variables.none=Vēl nav neviena mainīgā. variables.deletion=Noņemt mainīgo variables.deletion.description=Mainīgā noņemšana ir neatgriezeniska un nav atsaucama. Vai turpināt? variables.description=Mainīgie tiks padoti noteiktām darbībām, un citādāk tos nevar nolasīt. +variables.id_not_exist=Mainīgais ar identifikatoru %d nepastāv. variables.edit=Labot mainīgo variables.deletion.failed=Neizdevās noņemt mainīgo. variables.deletion.success=Mainīgais tika noņemts. From 65952417a81631ee879d4d29ad798cbb6445fa7e Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Mon, 26 Feb 2024 03:39:01 +0100 Subject: [PATCH 54/79] Add API to get PR by base/head (#29242) Closes https://github.com/go-gitea/gitea/issues/16289 Add a new API `/repos/{owner}/{repo}/pulls/{base}/{head}` to get a PR by its base and head branch. --- models/issues/pull.go | 29 ++++++++++ routers/api/v1/api.go | 1 + routers/api/v1/repo/pull.go | 85 ++++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 50 ++++++++++++++++++ tests/integration/api_pull_test.go | 21 ++++++++ 5 files changed, 186 insertions(+) diff --git a/models/issues/pull.go b/models/issues/pull.go index 18e6b2776d..7d299eac27 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -652,6 +652,35 @@ func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest, return pr, pr.LoadAttributes(ctx) } +// GetPullRequestsByBaseHeadInfo returns the pull request by given base and head +func GetPullRequestByBaseHeadInfo(ctx context.Context, baseID, headID int64, base, head string) (*PullRequest, error) { + pr := &PullRequest{} + sess := db.GetEngine(ctx). + Join("INNER", "issue", "issue.id = pull_request.issue_id"). + Where("base_repo_id = ? AND base_branch = ? AND head_repo_id = ? AND head_branch = ?", baseID, base, headID, head) + has, err := sess.Get(pr) + if err != nil { + return nil, err + } + if !has { + return nil, ErrPullRequestNotExist{ + HeadRepoID: headID, + BaseRepoID: baseID, + HeadBranch: head, + BaseBranch: base, + } + } + + if err = pr.LoadAttributes(ctx); err != nil { + return nil, err + } + if err = pr.LoadIssue(ctx); err != nil { + return nil, err + } + + return pr, nil +} + // GetAllUnmergedAgitPullRequestByPoster get all unmerged agit flow pull request // By poster id. func GetAllUnmergedAgitPullRequestByPoster(ctx context.Context, uid int64) ([]*PullRequest, error) { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e7bdef1489..e0c72c7ac4 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1225,6 +1225,7 @@ func Routes() *web.Route { Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) }) + m.Get("/{base}/*", repo.GetPullRequestByBaseHead) }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) m.Group("/statuses", func() { m.Combo("/{sha}").Get(repo.GetCommitStatuses). diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index eaf406e64d..85f8ec1de5 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -187,6 +187,91 @@ func GetPullRequest(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer)) } +// GetPullRequest returns a single PR based on index +func GetPullRequestByBaseHead(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/pulls/{base}/{head} repository repoGetPullRequestByBaseHead + // --- + // summary: Get a pull request by base and head + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: base + // in: path + // description: base of the pull request to get + // type: string + // required: true + // - name: head + // in: path + // description: head of the pull request to get + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/PullRequest" + // "404": + // "$ref": "#/responses/notFound" + + var headRepoID int64 + var headBranch string + head := ctx.Params("*") + if strings.Contains(head, ":") { + split := strings.SplitN(head, ":", 2) + headBranch = split[1] + var owner, name string + if strings.Contains(split[0], "/") { + split = strings.Split(split[0], "/") + owner = split[0] + name = split[1] + } else { + owner = split[0] + name = ctx.Repo.Repository.Name + } + repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner, name) + if err != nil { + if repo_model.IsErrRepoNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetRepositoryByOwnerName", err) + } + return + } + headRepoID = repo.ID + } else { + headRepoID = ctx.Repo.Repository.ID + headBranch = head + } + + pr, err := issues_model.GetPullRequestByBaseHeadInfo(ctx, ctx.Repo.Repository.ID, headRepoID, ctx.Params(":base"), headBranch) + if err != nil { + if issues_model.IsErrPullRequestNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetPullRequestByBaseHeadInfo", err) + } + return + } + + if err = pr.LoadBaseRepo(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err) + return + } + if err = pr.LoadHeadRepo(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) + return + } + ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer)) +} + // DownloadPullDiffOrPatch render a pull's raw diff or patch func DownloadPullDiffOrPatch(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}.{diffType} repository repoDownloadPullDiffOrPatch diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index eaa1448b2b..b2bd1bf174 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -10409,6 +10409,56 @@ } } }, + "/repos/{owner}/{repo}/pulls/{base}/{head}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get a pull request by base and head", + "operationId": "repoGetPullRequestByBaseHead", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "base of the pull request to get", + "name": "base", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "head of the pull request to get", + "name": "head", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/PullRequest" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/pulls/{index}": { "get": { "produces": [ diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index 33cc826e5e..92931c5699 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -61,6 +61,27 @@ func TestAPIViewPulls(t *testing.T) { } } +func TestAPIViewPullsByBaseHead(t *testing.T) { + defer tests.PrepareTestEnv(t)() + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/master/branch2", owner.Name, repo.Name). + AddTokenAuth(ctx.Token) + resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + + pull := &api.PullRequest{} + DecodeJSON(t, resp, pull) + assert.EqualValues(t, 3, pull.Index) + assert.EqualValues(t, 2, pull.ID) + + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/master/branch-not-exist", owner.Name, repo.Name). + AddTokenAuth(ctx.Token) + ctx.Session.MakeRequest(t, req, http.StatusNotFound) +} + // TestAPIMergePullWIP ensures that we can't merge a WIP pull request func TestAPIMergePullWIP(t *testing.T) { defer tests.PrepareTestEnv(t)() From 17f170ee3724d8bdf2ddaad4211b12433f78ff0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Br=C3=BCckner?= Date: Mon, 26 Feb 2024 04:08:21 +0000 Subject: [PATCH 55/79] Include resource state events in Gitlab downloads (#29382) Some specific events on Gitlab issues and merge requests are stored separately from comments as "resource state events". With this change, all relevant resource state events are downloaded during issue and merge request migration, and converted to comments. This PR also updates the template used to render comments to add support for migrated comments of these types. ref: https://docs.gitlab.com/ee/api/resource_state_events.html --- services/migrations/gitea_uploader.go | 6 ++ services/migrations/gitlab.go | 54 ++++++++++++++++++ .../repo/issue/view_content/comments.tmpl | 55 +++++-------------- .../view_content/comments_authorlink.tmpl | 11 ++++ 4 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 templates/repo/issue/view_content/comments_authorlink.tmpl diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 8bcf483947..1c9824fe3a 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -483,6 +483,10 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { } switch cm.Type { + case issues_model.CommentTypeReopen: + cm.Content = "" + case issues_model.CommentTypeClose: + cm.Content = "" case issues_model.CommentTypeAssignees: if assigneeID, ok := comment.Meta["AssigneeID"].(int); ok { cm.AssigneeID = int64(assigneeID) @@ -503,6 +507,8 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { cm.NewRef = fmt.Sprint(comment.Meta["NewRef"]) cm.Content = "" } + case issues_model.CommentTypeMergePull: + cm.Content = "" case issues_model.CommentTypePRScheduledToAutoMerge, issues_model.CommentTypePRUnScheduledToAutoMerge: cm.Content = "" default: diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index 5e49ae6d57..bbc44e958a 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -517,6 +517,60 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co } page = resp.NextPage } + + page = 1 + for { + var stateEvents []*gitlab.StateEvent + var resp *gitlab.Response + var err error + if context.IsMergeRequest { + stateEvents, resp, err = g.client.ResourceStateEvents.ListMergeStateEvents(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListStateEventsOptions{ + ListOptions: gitlab.ListOptions{ + Page: page, + PerPage: g.maxPerPage, + }, + }, nil, gitlab.WithContext(g.ctx)) + } else { + stateEvents, resp, err = g.client.ResourceStateEvents.ListIssueStateEvents(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListStateEventsOptions{ + ListOptions: gitlab.ListOptions{ + Page: page, + PerPage: g.maxPerPage, + }, + }, nil, gitlab.WithContext(g.ctx)) + } + if err != nil { + return nil, false, fmt.Errorf("error while listing state events: %v %w", g.repoID, err) + } + + for _, stateEvent := range stateEvents { + comment := &base.Comment{ + IssueIndex: commentable.GetLocalIndex(), + Index: int64(stateEvent.ID), + PosterID: int64(stateEvent.User.ID), + PosterName: stateEvent.User.Username, + Content: "", + Created: *stateEvent.CreatedAt, + } + switch stateEvent.State { + case gitlab.ClosedEventType: + comment.CommentType = issues_model.CommentTypeClose.String() + case gitlab.MergedEventType: + comment.CommentType = issues_model.CommentTypeMergePull.String() + case gitlab.ReopenedEventType: + comment.CommentType = issues_model.CommentTypeReopen.String() + default: + // Ignore other event types + continue + } + allComments = append(allComments, comment) + } + + if resp.NextPage == 0 { + break + } + page = resp.NextPage + } + return allComments, true, nil } diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index b25a5ad1b4..562e44c791 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -81,9 +81,11 @@ {{else if eq .Type 1}}
{{svg "octicon-dot-fill"}} - {{template "shared/user/avatarlink" dict "user" .Poster}} + {{if not .OriginalAuthor}} + {{template "shared/user/avatarlink" dict "user" .Poster}} + {{end}} - {{template "shared/user/authorlink" .Poster}} + {{template "repo/issue/view_content/comments_authorlink" dict "ctxData" $ "comment" .}} {{if .Issue.IsPull}} {{ctx.Locale.Tr "repo.pulls.reopened_at" .EventTag $createdStr}} {{else}} @@ -94,9 +96,11 @@ {{else if eq .Type 2}}
{{svg "octicon-circle-slash"}} - {{template "shared/user/avatarlink" dict "user" .Poster}} + {{if not .OriginalAuthor}} + {{template "shared/user/avatarlink" dict "user" .Poster}} + {{end}} - {{template "shared/user/authorlink" .Poster}} + {{template "repo/issue/view_content/comments_authorlink" dict "ctxData" $ "comment" .}} {{if .Issue.IsPull}} {{ctx.Locale.Tr "repo.pulls.closed_at" .EventTag $createdStr}} {{else}} @@ -107,9 +111,11 @@ {{else if eq .Type 28}}
{{svg "octicon-git-merge"}} - {{template "shared/user/avatarlink" dict "user" .Poster}} + {{if not .OriginalAuthor}} + {{template "shared/user/avatarlink" dict "user" .Poster}} + {{end}} - {{template "shared/user/authorlink" .Poster}} + {{template "repo/issue/view_content/comments_authorlink" dict "ctxData" $ "comment" .}} {{$link := printf "%s/commit/%s" $.Repository.Link ($.Issue.PullRequest.MergedCommitID|PathEscape)}} {{if eq $.Issue.PullRequest.Status 3}} {{ctx.Locale.Tr "repo.issues.comment_manually_pull_merged_at" (HTMLFormat `%[2]s` $link (ShortSha $.Issue.PullRequest.MergedCommitID)) (HTMLFormat "%[1]s" $.BaseTarget) $createdStr}} @@ -375,18 +381,7 @@ {{end}} {{svg (printf "octicon-%s" .Review.Type.Icon)}} - {{if .OriginalAuthor}} - - {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} - {{.OriginalAuthor}} - - {{if $.Repository.OriginalURL}} - ({{ctx.Locale.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname}}) - {{end}} - {{else}} - {{template "shared/user/authorlink" .Poster}} - {{end}} - + {{template "repo/issue/view_content/comments_authorlink" dict "ctxData" $ "comment" .}} {{if eq .Review.Type 1}} {{ctx.Locale.Tr "repo.issues.review.approve" $createdStr}} {{else if eq .Review.Type 2}} @@ -498,17 +493,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{end}} - {{if .OriginalAuthor}} - - {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} - {{.OriginalAuthor}} - - {{if $.Repository.OriginalURL}} - ({{ctx.Locale.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname}}) - {{end}} - {{else}} - {{template "shared/user/authorlink" .Poster}} - {{end}} + {{template "repo/issue/view_content/comments_authorlink" dict "ctxData" $ "comment" .}} {{ctx.Locale.Tr "repo.pulls.change_target_branch_at" .OldRef .NewRef $createdStr}}
@@ -675,17 +660,7 @@
{{svg "octicon-git-merge" 16}} - {{if .OriginalAuthor}} - - {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} - {{.OriginalAuthor}} - - {{if $.Repository.OriginalURL}} - ({{ctx.Locale.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname}}) - {{end}} - {{else}} - {{template "shared/user/authorlink" .Poster}} - {{end}} + {{template "repo/issue/view_content/comments_authorlink" dict "ctxData" $ "comment" .}} {{if eq .Type 34}}{{ctx.Locale.Tr "repo.pulls.auto_merge_newly_scheduled_comment" $createdStr}} {{else}}{{ctx.Locale.Tr "repo.pulls.auto_merge_canceled_schedule_comment" $createdStr}}{{end}} diff --git a/templates/repo/issue/view_content/comments_authorlink.tmpl b/templates/repo/issue/view_content/comments_authorlink.tmpl new file mode 100644 index 0000000000..f652a0bec3 --- /dev/null +++ b/templates/repo/issue/view_content/comments_authorlink.tmpl @@ -0,0 +1,11 @@ +{{if .comment.OriginalAuthor}} + + {{svg (MigrationIcon .ctxData.Repository.GetOriginalURLHostname)}} + {{.comment.OriginalAuthor}} + + {{if .ctxData.Repository.OriginalURL}} + ({{ctx.Locale.Tr "repo.migrated_from" .ctxData.Repository.OriginalURL .ctxData.Repository.GetOriginalURLHostname}}) + {{end}} +{{else}} + {{template "shared/user/authorlink" .comment.Poster}} +{{end}} From f8c1efe944c539396402fb014bbfdb67ba199ef2 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 26 Feb 2024 17:10:14 +0900 Subject: [PATCH 56/79] Fix logic error from #28138 (#29417) There's a miss in #28138: ![image](https://github.com/go-gitea/gitea/assets/18380374/b1e0c5fc-0e6e-44ab-9f6e-34bc8ffbe1cc) https://github.com/go-gitea/gitea/pull/28138/files#diff-2556e62ad7204a230c91927a3f2115e25a2b688240d0ee1de6d34f0277f37dfeR162 @lunny Not sure about the impact of this, but it will only effect 1.22, and maybe we should fix it ASAP. Co-authored-by: KN4CK3R --- routers/private/hook_post_receive.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index 8b954a8130..5ae03ce294 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -159,7 +159,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { } // If we've pushed a branch (and not deleted it) - if git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() { + if !git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() { // First ensure we have the repository loaded, we're allowed pulls requests and we can get the base repo if repo == nil { repo = loadRepository(ctx, ownerName, repoName) From f8974c772560e2c957e5e218bfb348d1ee6b9448 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 26 Feb 2024 17:05:22 +0800 Subject: [PATCH 57/79] Fix incorrect tree path value for patch editor (#29377) Regression of #18718. When submitting the form, EditRepoFileForm.TreePath is marked as "Required", so the value can't be empty. The value is not used by backend, so use a meaningful dummy value for it. --- templates/repo/editor/patch.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index c9a78cc35f..8df8758988 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -14,8 +14,8 @@ {{.BranchName}} {{ctx.Locale.Tr "repo.editor.or"}} {{ctx.Locale.Tr "repo.editor.cancel_lower"}} - - + +
From 403766cd81697288804fd218d68c458c6aa5b73d Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 26 Feb 2024 18:38:15 +0900 Subject: [PATCH 58/79] Ignore empty repo for CreateRepository in action notifier (#29416) Fix #29415 --- services/actions/notifier_helper.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index c20335af6f..b248af1d01 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -117,6 +117,9 @@ func notify(ctx context.Context, input *notifyInput) error { log.Debug("ignore executing %v for event %v whose doer is %v", getMethod(ctx), input.Event, input.Doer.Name) return nil } + if input.Repo.IsEmpty { + return nil + } if unit_model.TypeActions.UnitGlobalDisabled() { if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo); err != nil { log.Error("CleanRepoScheduleTasks: %v", err) From 324626a11c041208b003ee64e33000b223994662 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Mon, 26 Feb 2024 14:40:41 +0200 Subject: [PATCH 59/79] Fix htmx rendering the login page in frame on session logout (#29405) - Fix #29391 With this change, htmx will not follow the redirect in the AJAX request but instead redirect the whole browser. To reproduce the bug fixed by this change without waiting a long time for the token to expire, you can logout in another tab then look in the original tab. Just make sure to comment out both instances of `window.location.href = appSubUrl` in the codebase so you won't be redirected immediately on logout. This is what I did in the following gifs. Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- modules/context/base.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/context/base.go b/modules/context/base.go index fa05850a16..ddd04f4767 100644 --- a/modules/context/base.go +++ b/modules/context/base.go @@ -265,6 +265,14 @@ func (b *Base) Redirect(location string, status ...int) { // So in this case, we should remove the session cookie from the response header removeSessionCookieHeader(b.Resp) } + // in case the request is made by htmx, have it redirect the browser instead of trying to follow the redirect inside htmx + if b.Req.Header.Get("HX-Request") == "true" { + b.Resp.Header().Set("HX-Redirect", location) + // we have to return a non-redirect status code so XMLHTTPRequest will not immediately follow the redirect + // so as to give htmx redirect logic a chance to run + b.Status(http.StatusNoContent) + return + } http.Redirect(b.Resp, b.Req, location, code) } From 4f70ebb68494b23c5bce93efbe4c5f80b3aaf444 Mon Sep 17 00:00:00 2001 From: delvh Date: Mon, 26 Feb 2024 23:04:44 +0100 Subject: [PATCH 60/79] Document our issue locking policy (#29433) --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f9b9a421a3..dc90c6905b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,7 @@ - [How to report issues](#how-to-report-issues) - [Types of issues](#types-of-issues) - [Discuss your design before the implementation](#discuss-your-design-before-the-implementation) + - [Issue locking](#issue-locking) - [Building Gitea](#building-gitea) - [Dependencies](#dependencies) - [Backend](#backend) @@ -103,6 +104,13 @@ the goals for the project and tools. Pull requests should not be the place for architecture discussions. +### Issue locking + +Commenting on closed or merged issues/PRs is strongly discouraged. +Such comments will likely be overlooked as some maintainers may not view notifications on closed issues, thinking that the item is resolved. +As such, commenting on closed/merged issues/PRs may be disabled prior to the scheduled auto-locking if a discussion starts or if unrelated comments are posted. +If further discussion is needed, we encourage you to open a new issue instead and we recommend linking to the issue/PR in question for context. + ## Building Gitea See the [development setup instructions](https://docs.gitea.com/development/hacking-on-gitea). From eb2fc1818b00b7ca6f8c21bb490a8e8be1e62f9a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 27 Feb 2024 06:31:30 +0800 Subject: [PATCH 61/79] Fix mail template error (#29410) --- modules/templates/mailer.go | 10 ++++++++-- templates/mail/notify/repo_transfer.tmpl | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go index 54d857a8f6..04032e3982 100644 --- a/modules/templates/mailer.go +++ b/modules/templates/mailer.go @@ -44,11 +44,17 @@ func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, } if _, err := stpl.New(name). Parse(string(subjectContent)); err != nil { - log.Warn("Failed to parse template [%s/subject]: %v", name, err) + log.Error("Failed to parse template [%s/subject]: %v", name, err) + if !setting.IsProd { + log.Fatal("Please fix the mail template error") + } } if _, err := btpl.New(name). Parse(string(bodyContent)); err != nil { - log.Warn("Failed to parse template [%s/body]: %v", name, err) + log.Error("Failed to parse template [%s/body]: %v", name, err) + if !setting.IsProd { + log.Fatal("Please fix the mail template error") + } } } diff --git a/templates/mail/notify/repo_transfer.tmpl b/templates/mail/notify/repo_transfer.tmpl index 597048ddf4..8c8b276484 100644 --- a/templates/mail/notify/repo_transfer.tmpl +++ b/templates/mail/notify/repo_transfer.tmpl @@ -5,7 +5,7 @@ {{.Subject}} -{{$url := HTMLFormat "%[2]s" .Link .Repo)}} +{{$url := HTMLFormat "%[2]s" .Link .Repo}}

{{.Subject}}. {{.locale.Tr "mail.repo.transfer.body" $url}} From e55926ebfe88d6ee079842967dc7dccc2a9cdbf2 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 27 Feb 2024 04:04:46 +0100 Subject: [PATCH 62/79] Apply tailwindcss rules with `!important` (#29437) As per discussion in https://github.com/go-gitea/gitea/pull/29423, I think this is the right way that does not burden developers having to think about CSS precedence which should be irrelevant with an atomic CSS framework. --------- Co-authored-by: wxiaoguang --- tailwind.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tailwind.config.js b/tailwind.config.js index 8c474c33a8..7f36822001 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,6 +6,7 @@ const isProduction = env.NODE_ENV !== 'development'; export default { prefix: 'tw-', + important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles content: [ isProduction && '!./templates/devtest/**/*', isProduction && '!./web_src/js/standalone/devtest.js', From 29f149bd9f517225a3c9f1ca3fb0a7b5325af696 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 27 Feb 2024 15:12:22 +0800 Subject: [PATCH 63/79] Move context from modules to services (#29440) Since `modules/context` has to depend on `models` and many other packages, it should be moved from `modules/context` to `services/context` according to design principles. There is no logic code change on this PR, only move packages. - Move `code.gitea.io/gitea/modules/context` to `code.gitea.io/gitea/services/context` - Move `code.gitea.io/gitea/modules/contexttest` to `code.gitea.io/gitea/services/contexttest` because of depending on context - Move `code.gitea.io/gitea/modules/upload` to `code.gitea.io/gitea/services/context/upload` because of depending on context --- routers/api/actions/artifacts.go | 2 +- routers/api/packages/alpine/alpine.go | 2 +- routers/api/packages/api.go | 7 +++---- routers/api/packages/cargo/cargo.go | 2 +- routers/api/packages/chef/chef.go | 2 +- routers/api/packages/composer/composer.go | 2 +- routers/api/packages/conan/conan.go | 2 +- routers/api/packages/conan/search.go | 2 +- routers/api/packages/conda/conda.go | 2 +- routers/api/packages/container/container.go | 2 +- routers/api/packages/cran/cran.go | 2 +- routers/api/packages/debian/debian.go | 2 +- routers/api/packages/generic/generic.go | 2 +- routers/api/packages/goproxy/goproxy.go | 2 +- routers/api/packages/helm/helm.go | 2 +- routers/api/packages/helper/helper.go | 2 +- routers/api/packages/maven/maven.go | 2 +- routers/api/packages/npm/npm.go | 2 +- routers/api/packages/nuget/nuget.go | 2 +- routers/api/packages/pub/pub.go | 2 +- routers/api/packages/pypi/pypi.go | 2 +- routers/api/packages/rpm/rpm.go | 2 +- routers/api/packages/rubygems/rubygems.go | 2 +- routers/api/packages/swift/swift.go | 2 +- routers/api/packages/vagrant/vagrant.go | 2 +- routers/api/v1/activitypub/person.go | 2 +- routers/api/v1/activitypub/reqsignature.go | 2 +- routers/api/v1/admin/adopt.go | 2 +- routers/api/v1/admin/cron.go | 2 +- routers/api/v1/admin/email.go | 2 +- routers/api/v1/admin/hooks.go | 2 +- routers/api/v1/admin/org.go | 2 +- routers/api/v1/admin/repo.go | 2 +- routers/api/v1/admin/runners.go | 2 +- routers/api/v1/admin/user.go | 2 +- routers/api/v1/api.go | 19 +++++++++---------- routers/api/v1/misc/gitignore.go | 2 +- routers/api/v1/misc/label_templates.go | 2 +- routers/api/v1/misc/licenses.go | 2 +- routers/api/v1/misc/markup.go | 2 +- routers/api/v1/misc/markup_test.go | 2 +- routers/api/v1/misc/nodeinfo.go | 2 +- routers/api/v1/misc/signing.go | 2 +- routers/api/v1/misc/version.go | 2 +- routers/api/v1/notify/notifications.go | 2 +- routers/api/v1/notify/repo.go | 2 +- routers/api/v1/notify/threads.go | 2 +- routers/api/v1/notify/user.go | 2 +- routers/api/v1/org/avatar.go | 2 +- routers/api/v1/org/hook.go | 2 +- routers/api/v1/org/label.go | 2 +- routers/api/v1/org/member.go | 2 +- routers/api/v1/org/org.go | 2 +- routers/api/v1/org/runners.go | 2 +- routers/api/v1/org/secrets.go | 2 +- routers/api/v1/org/team.go | 2 +- routers/api/v1/packages/package.go | 2 +- routers/api/v1/repo/action.go | 2 +- routers/api/v1/repo/avatar.go | 2 +- routers/api/v1/repo/blob.go | 2 +- routers/api/v1/repo/branch.go | 2 +- routers/api/v1/repo/collaborators.go | 2 +- routers/api/v1/repo/commits.go | 2 +- routers/api/v1/repo/file.go | 2 +- routers/api/v1/repo/fork.go | 2 +- routers/api/v1/repo/git_hook.go | 2 +- routers/api/v1/repo/git_ref.go | 2 +- routers/api/v1/repo/hook.go | 2 +- routers/api/v1/repo/hook_test.go | 2 +- routers/api/v1/repo/issue.go | 2 +- routers/api/v1/repo/issue_attachment.go | 2 +- routers/api/v1/repo/issue_comment.go | 2 +- .../api/v1/repo/issue_comment_attachment.go | 2 +- routers/api/v1/repo/issue_dependency.go | 2 +- routers/api/v1/repo/issue_label.go | 2 +- routers/api/v1/repo/issue_pin.go | 2 +- routers/api/v1/repo/issue_reaction.go | 2 +- routers/api/v1/repo/issue_stopwatch.go | 2 +- routers/api/v1/repo/issue_subscription.go | 2 +- routers/api/v1/repo/issue_tracked_time.go | 2 +- routers/api/v1/repo/key.go | 2 +- routers/api/v1/repo/label.go | 2 +- routers/api/v1/repo/language.go | 2 +- routers/api/v1/repo/migrate.go | 2 +- routers/api/v1/repo/milestone.go | 2 +- routers/api/v1/repo/mirror.go | 2 +- routers/api/v1/repo/notes.go | 2 +- routers/api/v1/repo/patch.go | 2 +- routers/api/v1/repo/pull.go | 2 +- routers/api/v1/repo/pull_review.go | 2 +- routers/api/v1/repo/release.go | 2 +- routers/api/v1/repo/release_attachment.go | 4 ++-- routers/api/v1/repo/release_tags.go | 2 +- routers/api/v1/repo/repo.go | 2 +- routers/api/v1/repo/repo_test.go | 2 +- routers/api/v1/repo/runners.go | 2 +- routers/api/v1/repo/star.go | 2 +- routers/api/v1/repo/status.go | 2 +- routers/api/v1/repo/subscriber.go | 2 +- routers/api/v1/repo/tag.go | 2 +- routers/api/v1/repo/teams.go | 2 +- routers/api/v1/repo/topic.go | 2 +- routers/api/v1/repo/transfer.go | 2 +- routers/api/v1/repo/tree.go | 2 +- routers/api/v1/repo/wiki.go | 2 +- routers/api/v1/settings/settings.go | 2 +- routers/api/v1/shared/runners.go | 2 +- routers/api/v1/user/action.go | 2 +- routers/api/v1/user/app.go | 2 +- routers/api/v1/user/avatar.go | 2 +- routers/api/v1/user/email.go | 2 +- routers/api/v1/user/follower.go | 2 +- routers/api/v1/user/gpg_key.go | 2 +- routers/api/v1/user/helper.go | 2 +- routers/api/v1/user/hook.go | 2 +- routers/api/v1/user/key.go | 2 +- routers/api/v1/user/repo.go | 2 +- routers/api/v1/user/runners.go | 2 +- routers/api/v1/user/settings.go | 2 +- routers/api/v1/user/star.go | 2 +- routers/api/v1/user/user.go | 2 +- routers/api/v1/user/watch.go | 2 +- routers/api/v1/utils/git.go | 2 +- routers/api/v1/utils/hook.go | 2 +- routers/api/v1/utils/page.go | 2 +- routers/common/auth.go | 2 +- routers/common/errpage.go | 2 +- routers/common/markup.go | 2 +- routers/common/middleware.go | 2 +- routers/common/serve.go | 2 +- routers/install/install.go | 2 +- routers/private/actions.go | 2 +- routers/private/default_branch.go | 2 +- routers/private/hook_post_receive.go | 2 +- routers/private/hook_pre_receive.go | 2 +- routers/private/hook_proc_receive.go | 2 +- routers/private/internal.go | 2 +- routers/private/internal_repo.go | 2 +- routers/private/key.go | 2 +- routers/private/mail.go | 2 +- routers/private/manager.go | 2 +- routers/private/manager_process.go | 2 +- routers/private/manager_unix.go | 2 +- routers/private/manager_windows.go | 2 +- routers/private/restore_repo.go | 2 +- routers/private/serv.go | 2 +- routers/private/ssh_log.go | 2 +- routers/web/admin/admin.go | 2 +- routers/web/admin/applications.go | 2 +- routers/web/admin/auths.go | 2 +- routers/web/admin/config.go | 2 +- routers/web/admin/diagnosis.go | 2 +- routers/web/admin/emails.go | 2 +- routers/web/admin/hooks.go | 2 +- routers/web/admin/notice.go | 2 +- routers/web/admin/orgs.go | 2 +- routers/web/admin/packages.go | 2 +- routers/web/admin/queue.go | 2 +- routers/web/admin/repos.go | 2 +- routers/web/admin/runners.go | 2 +- routers/web/admin/stacktrace.go | 2 +- routers/web/admin/users.go | 2 +- routers/web/admin/users_test.go | 2 +- routers/web/auth/2fa.go | 2 +- routers/web/auth/auth.go | 2 +- routers/web/auth/linkaccount.go | 2 +- routers/web/auth/oauth.go | 2 +- routers/web/auth/openid.go | 2 +- routers/web/auth/password.go | 2 +- routers/web/auth/webauthn.go | 2 +- routers/web/devtest/devtest.go | 2 +- routers/web/events/events.go | 2 +- routers/web/explore/code.go | 2 +- routers/web/explore/org.go | 2 +- routers/web/explore/repo.go | 2 +- routers/web/explore/topic.go | 2 +- routers/web/explore/user.go | 2 +- routers/web/feed/branch.go | 2 +- routers/web/feed/convert.go | 2 +- routers/web/feed/file.go | 2 +- routers/web/feed/profile.go | 2 +- routers/web/feed/release.go | 2 +- routers/web/feed/render.go | 2 +- routers/web/feed/repo.go | 2 +- routers/web/githttp.go | 5 ++--- routers/web/goget.go | 2 +- routers/web/home.go | 2 +- routers/web/misc/markup.go | 2 +- routers/web/misc/swagger.go | 2 +- routers/web/nodeinfo.go | 2 +- routers/web/org/home.go | 2 +- routers/web/org/members.go | 2 +- routers/web/org/org.go | 2 +- routers/web/org/org_labels.go | 2 +- routers/web/org/projects.go | 2 +- routers/web/org/projects_test.go | 2 +- routers/web/org/setting.go | 2 +- routers/web/org/setting/runners.go | 2 +- routers/web/org/setting_oauth2.go | 2 +- routers/web/org/setting_packages.go | 2 +- routers/web/org/teams.go | 2 +- routers/web/passkey.go | 2 +- routers/web/repo/actions/actions.go | 2 +- routers/web/repo/actions/view.go | 2 +- routers/web/repo/activity.go | 2 +- routers/web/repo/attachment.go | 4 ++-- routers/web/repo/blame.go | 2 +- routers/web/repo/branch.go | 2 +- routers/web/repo/cherry_pick.go | 2 +- routers/web/repo/code_frequency.go | 2 +- routers/web/repo/commit.go | 2 +- routers/web/repo/compare.go | 4 ++-- routers/web/repo/contributors.go | 2 +- routers/web/repo/download.go | 2 +- routers/web/repo/editor.go | 4 ++-- routers/web/repo/editor_test.go | 2 +- routers/web/repo/find.go | 2 +- routers/web/repo/githttp.go | 2 +- routers/web/repo/helper.go | 2 +- routers/web/repo/issue.go | 4 ++-- routers/web/repo/issue_content_history.go | 2 +- routers/web/repo/issue_dependency.go | 2 +- routers/web/repo/issue_label.go | 2 +- routers/web/repo/issue_label_test.go | 2 +- routers/web/repo/issue_lock.go | 2 +- routers/web/repo/issue_pin.go | 2 +- routers/web/repo/issue_stopwatch.go | 2 +- routers/web/repo/issue_timetrack.go | 2 +- routers/web/repo/issue_watch.go | 2 +- routers/web/repo/middlewares.go | 2 +- routers/web/repo/migrate.go | 2 +- routers/web/repo/milestone.go | 2 +- routers/web/repo/packages.go | 2 +- routers/web/repo/patch.go | 2 +- routers/web/repo/projects.go | 2 +- routers/web/repo/projects_test.go | 2 +- routers/web/repo/pull.go | 4 ++-- routers/web/repo/pull_review.go | 4 ++-- routers/web/repo/pull_review_test.go | 4 ++-- routers/web/repo/recent_commits.go | 2 +- routers/web/repo/release.go | 4 ++-- routers/web/repo/release_test.go | 2 +- routers/web/repo/render.go | 2 +- routers/web/repo/repo.go | 2 +- routers/web/repo/search.go | 2 +- routers/web/repo/setting/avatar.go | 2 +- routers/web/repo/setting/collaboration.go | 2 +- routers/web/repo/setting/default_branch.go | 2 +- routers/web/repo/setting/deploy_key.go | 2 +- routers/web/repo/setting/git_hooks.go | 2 +- routers/web/repo/setting/lfs.go | 2 +- routers/web/repo/setting/protected_branch.go | 2 +- routers/web/repo/setting/protected_tag.go | 2 +- routers/web/repo/setting/runners.go | 2 +- routers/web/repo/setting/secrets.go | 2 +- routers/web/repo/setting/setting.go | 2 +- routers/web/repo/setting/settings_test.go | 4 ++-- routers/web/repo/setting/variables.go | 2 +- routers/web/repo/setting/webhook.go | 2 +- routers/web/repo/topic.go | 2 +- routers/web/repo/treelist.go | 2 +- routers/web/repo/view.go | 2 +- routers/web/repo/wiki.go | 2 +- routers/web/repo/wiki_test.go | 2 +- routers/web/shared/actions/runners.go | 2 +- routers/web/shared/actions/variables.go | 2 +- routers/web/shared/packages/packages.go | 2 +- routers/web/shared/secrets/secrets.go | 2 +- routers/web/shared/user/header.go | 2 +- routers/web/swagger_json.go | 2 +- routers/web/user/avatar.go | 2 +- routers/web/user/code.go | 2 +- routers/web/user/home.go | 9 ++++----- routers/web/user/home_test.go | 2 +- routers/web/user/notification.go | 2 +- routers/web/user/package.go | 2 +- routers/web/user/profile.go | 2 +- routers/web/user/search.go | 2 +- routers/web/user/setting/account.go | 2 +- routers/web/user/setting/account_test.go | 2 +- routers/web/user/setting/adopt.go | 2 +- routers/web/user/setting/applications.go | 2 +- routers/web/user/setting/keys.go | 2 +- routers/web/user/setting/oauth2.go | 2 +- routers/web/user/setting/oauth2_common.go | 2 +- routers/web/user/setting/packages.go | 2 +- routers/web/user/setting/profile.go | 2 +- routers/web/user/setting/runner.go | 2 +- routers/web/user/setting/security/2fa.go | 2 +- routers/web/user/setting/security/openid.go | 2 +- routers/web/user/setting/security/security.go | 2 +- routers/web/user/setting/security/webauthn.go | 2 +- routers/web/user/setting/webhooks.go | 2 +- routers/web/user/stop_watch.go | 2 +- routers/web/user/task.go | 2 +- routers/web/web.go | 7 +++---- routers/web/webfinger.go | 2 +- services/attachment/attachment.go | 2 +- services/auth/auth.go | 2 +- services/auth/sspi.go | 2 +- {modules => services}/context/access_log.go | 0 {modules => services}/context/api.go | 0 {modules => services}/context/api_org.go | 0 {modules => services}/context/api_test.go | 0 {modules => services}/context/base.go | 0 {modules => services}/context/captcha.go | 0 {modules => services}/context/context.go | 0 .../context/context_cookie.go | 0 .../context/context_model.go | 0 .../context/context_request.go | 0 .../context/context_response.go | 0 .../context/context_template.go | 0 {modules => services}/context/context_test.go | 0 {modules => services}/context/csrf.go | 0 {modules => services}/context/org.go | 0 {modules => services}/context/package.go | 0 {modules => services}/context/pagination.go | 0 {modules => services}/context/permission.go | 0 {modules => services}/context/private.go | 0 {modules => services}/context/repo.go | 0 {modules => services}/context/response.go | 0 .../context}/upload/upload.go | 2 +- .../context}/upload/upload_test.go | 0 services/context/user.go | 17 ++++++++--------- {modules => services}/context/utils.go | 0 {modules => services}/context/xsrf.go | 0 {modules => services}/context/xsrf_test.go | 0 .../contexttest/context_tests.go | 2 +- services/convert/git_commit.go | 2 +- services/forms/admin.go | 2 +- services/forms/auth_form.go | 2 +- services/forms/org.go | 2 +- services/forms/package_form.go | 2 +- services/forms/repo_branch_form.go | 2 +- services/forms/repo_form.go | 2 +- services/forms/repo_tag_form.go | 2 +- services/forms/runner.go | 2 +- services/forms/user_form.go | 2 +- services/forms/user_form_auth_openid.go | 2 +- services/forms/user_form_hidden_comments.go | 2 +- services/lfs/locks.go | 2 +- services/lfs/server.go | 2 +- services/mailer/incoming/incoming_handler.go | 2 +- services/markup/processorhelper.go | 2 +- services/markup/processorhelper_test.go | 4 ++-- services/pull/pull.go | 2 +- services/repository/archiver/archiver_test.go | 2 +- services/repository/commit.go | 2 +- services/repository/files/content_test.go | 2 +- services/repository/files/diff_test.go | 2 +- services/repository/files/file_test.go | 2 +- services/repository/files/tree_test.go | 2 +- .../integration/api_repo_file_create_test.go | 2 +- .../integration/api_repo_file_update_test.go | 2 +- .../integration/api_repo_files_change_test.go | 2 +- tests/integration/editor_test.go | 2 +- tests/integration/git_test.go | 2 +- tests/integration/integration_test.go | 2 +- tests/integration/mirror_push_test.go | 2 +- tests/integration/repofiles_change_test.go | 2 +- 360 files changed, 369 insertions(+), 375 deletions(-) rename {modules => services}/context/access_log.go (100%) rename {modules => services}/context/api.go (100%) rename {modules => services}/context/api_org.go (100%) rename {modules => services}/context/api_test.go (100%) rename {modules => services}/context/base.go (100%) rename {modules => services}/context/captcha.go (100%) rename {modules => services}/context/context.go (100%) rename {modules => services}/context/context_cookie.go (100%) rename {modules => services}/context/context_model.go (100%) rename {modules => services}/context/context_request.go (100%) rename {modules => services}/context/context_response.go (100%) rename {modules => services}/context/context_template.go (100%) rename {modules => services}/context/context_test.go (100%) rename {modules => services}/context/csrf.go (100%) rename {modules => services}/context/org.go (100%) rename {modules => services}/context/package.go (100%) rename {modules => services}/context/pagination.go (100%) rename {modules => services}/context/permission.go (100%) rename {modules => services}/context/private.go (100%) rename {modules => services}/context/repo.go (100%) rename {modules => services}/context/response.go (100%) rename {modules => services/context}/upload/upload.go (98%) rename {modules => services/context}/upload/upload_test.go (100%) rename {modules => services}/context/utils.go (100%) rename {modules => services}/context/xsrf.go (100%) rename {modules => services}/context/xsrf_test.go (100%) rename {modules => services}/contexttest/context_tests.go (99%) diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index 9fbd3f045d..d530e9cee5 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -71,7 +71,6 @@ import ( "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -80,6 +79,7 @@ import ( "code.gitea.io/gitea/modules/web" web_types "code.gitea.io/gitea/modules/web/types" actions_service "code.gitea.io/gitea/services/actions" + "code.gitea.io/gitea/services/context" ) const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts" diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index 3fd8288c01..dae9c3dfcb 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -14,12 +14,12 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" packages_module "code.gitea.io/gitea/modules/packages" alpine_module "code.gitea.io/gitea/modules/packages/alpine" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" alpine_service "code.gitea.io/gitea/services/packages/alpine" ) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index d990ebb56a..5e3cbac8f9 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -10,7 +10,6 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" @@ -36,7 +35,7 @@ import ( "code.gitea.io/gitea/routers/api/packages/swift" "code.gitea.io/gitea/routers/api/packages/vagrant" "code.gitea.io/gitea/services/auth" - context_service "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context" ) func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) { @@ -642,7 +641,7 @@ func CommonRoutes() *web.Route { }) }) }, reqPackageAccess(perm.AccessModeRead)) - }, context_service.UserAssignmentWeb(), context.PackageAssignment()) + }, context.UserAssignmentWeb(), context.PackageAssignment()) return r } @@ -812,7 +811,7 @@ func ContainerRoutes() *web.Route { ctx.Status(http.StatusNotFound) }) - }, container.ReqContainerAccess, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) + }, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) return r } diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go index 8f1e965c9a..d01a13d78f 100644 --- a/routers/api/packages/cargo/cargo.go +++ b/routers/api/packages/cargo/cargo.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" cargo_module "code.gitea.io/gitea/modules/packages/cargo" @@ -20,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" packages_service "code.gitea.io/gitea/services/packages" cargo_service "code.gitea.io/gitea/services/packages/cargo" diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go index f1e9ae12d8..720fce0a2a 100644 --- a/routers/api/packages/chef/chef.go +++ b/routers/api/packages/chef/chef.go @@ -15,12 +15,12 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" chef_module "code.gitea.io/gitea/modules/packages/chef" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go index 0551093cd1..346408d261 100644 --- a/routers/api/packages/composer/composer.go +++ b/routers/api/packages/composer/composer.go @@ -14,12 +14,12 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" composer_module "code.gitea.io/gitea/modules/packages/composer" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" packages_service "code.gitea.io/gitea/services/packages" diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index 4bf13222dc..c45e085a4d 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -15,13 +15,13 @@ import ( packages_model "code.gitea.io/gitea/models/packages" conan_model "code.gitea.io/gitea/models/packages/conan" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" conan_module "code.gitea.io/gitea/modules/packages/conan" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" notify_service "code.gitea.io/gitea/services/notify" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/conan/search.go b/routers/api/packages/conan/search.go index 2bcf9df162..7370c702cd 100644 --- a/routers/api/packages/conan/search.go +++ b/routers/api/packages/conan/search.go @@ -9,9 +9,9 @@ import ( conan_model "code.gitea.io/gitea/models/packages/conan" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" conan_module "code.gitea.io/gitea/modules/packages/conan" + "code.gitea.io/gitea/services/context" ) // SearchResult contains the found recipe names diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go index 0bee7baa96..30c80fc15e 100644 --- a/routers/api/packages/conda/conda.go +++ b/routers/api/packages/conda/conda.go @@ -12,13 +12,13 @@ import ( packages_model "code.gitea.io/gitea/models/packages" conda_model "code.gitea.io/gitea/models/packages/conda" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" conda_module "code.gitea.io/gitea/modules/packages/conda" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" "github.com/dsnet/compress/bzip2" diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 8621242da4..e519766142 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -17,7 +17,6 @@ import ( packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" @@ -25,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" container_service "code.gitea.io/gitea/services/packages/container" diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go index ae43df7c9a..2cec75294f 100644 --- a/routers/api/packages/cran/cran.go +++ b/routers/api/packages/cran/cran.go @@ -13,11 +13,11 @@ import ( packages_model "code.gitea.io/gitea/models/packages" cran_model "code.gitea.io/gitea/models/packages/cran" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" cran_module "code.gitea.io/gitea/modules/packages/cran" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go index 379137e87e..241de3ac5d 100644 --- a/routers/api/packages/debian/debian.go +++ b/routers/api/packages/debian/debian.go @@ -13,11 +13,11 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" debian_module "code.gitea.io/gitea/modules/packages/debian" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" notify_service "code.gitea.io/gitea/services/notify" packages_service "code.gitea.io/gitea/services/packages" debian_service "code.gitea.io/gitea/services/packages/debian" diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index 30854335c0..b65870a8d0 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -10,10 +10,10 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go index 18e0074ab4..9eb515d9a1 100644 --- a/routers/api/packages/goproxy/goproxy.go +++ b/routers/api/packages/goproxy/goproxy.go @@ -12,11 +12,11 @@ import ( "time" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" goproxy_module "code.gitea.io/gitea/modules/packages/goproxy" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go index a8daa69dc3..e7a346d9ca 100644 --- a/routers/api/packages/helm/helm.go +++ b/routers/api/packages/helm/helm.go @@ -13,7 +13,6 @@ import ( "time" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" @@ -21,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" "gopkg.in/yaml.v3" diff --git a/routers/api/packages/helper/helper.go b/routers/api/packages/helper/helper.go index aadb10376c..cdb64109ad 100644 --- a/routers/api/packages/helper/helper.go +++ b/routers/api/packages/helper/helper.go @@ -10,9 +10,9 @@ import ( "net/url" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) // LogAndProcessError logs an error and calls a custom callback with the processed error message. diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 5106395eb1..27f0578db7 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -20,12 +20,12 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" maven_module "code.gitea.io/gitea/modules/packages/maven" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 170edfbe11..72b4305928 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -17,12 +17,12 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" npm_module "code.gitea.io/gitea/modules/packages/npm" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" "github.com/hashicorp/go-version" diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index 769c4c1824..a0273aad5a 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -17,13 +17,13 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" nuget_model "code.gitea.io/gitea/models/packages/nuget" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" nuget_module "code.gitea.io/gitea/modules/packages/nuget" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go index 1f605c6c9f..f87df52a29 100644 --- a/routers/api/packages/pub/pub.go +++ b/routers/api/packages/pub/pub.go @@ -14,7 +14,6 @@ import ( "time" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" @@ -22,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go index 5718b1203b..7824db1823 100644 --- a/routers/api/packages/pypi/pypi.go +++ b/routers/api/packages/pypi/pypi.go @@ -12,12 +12,12 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" pypi_module "code.gitea.io/gitea/modules/packages/pypi" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go index 5d06680552..4de361c214 100644 --- a/routers/api/packages/rpm/rpm.go +++ b/routers/api/packages/rpm/rpm.go @@ -13,13 +13,13 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" packages_module "code.gitea.io/gitea/modules/packages" rpm_module "code.gitea.io/gitea/modules/packages/rpm" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" notify_service "code.gitea.io/gitea/services/notify" packages_service "code.gitea.io/gitea/services/packages" rpm_service "code.gitea.io/gitea/services/packages/rpm" diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index 01fd4dad66..5d05b6d524 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -13,11 +13,11 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" rubygems_module "code.gitea.io/gitea/modules/packages/rubygems" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 6ad289e51e..1fc8baeaac 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -13,7 +13,6 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" @@ -21,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" "github.com/hashicorp/go-version" diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go index af9cd08a62..98a81da368 100644 --- a/routers/api/packages/vagrant/vagrant.go +++ b/routers/api/packages/vagrant/vagrant.go @@ -12,11 +12,11 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" vagrant_module "code.gitea.io/gitea/modules/packages/vagrant" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/api/packages/helper" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" "github.com/hashicorp/go-version" diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index cad5032d10..995a148f0b 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -9,9 +9,9 @@ import ( "strings" "code.gitea.io/gitea/modules/activitypub" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 3f60ed7776..59ebc74b89 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -13,9 +13,9 @@ import ( "net/url" "code.gitea.io/gitea/modules/activitypub" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/setting" + gitea_context "code.gitea.io/gitea/services/context" ap "github.com/go-ap/activitypub" "github.com/go-fed/httpsig" diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go index bf030eb222..a4708fe032 100644 --- a/routers/api/v1/admin/adopt.go +++ b/routers/api/v1/admin/adopt.go @@ -8,9 +8,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/api/v1/admin/cron.go b/routers/api/v1/admin/cron.go index cc8c6c9e23..e1ca6048c9 100644 --- a/routers/api/v1/admin/cron.go +++ b/routers/api/v1/admin/cron.go @@ -6,11 +6,11 @@ package admin import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/cron" ) diff --git a/routers/api/v1/admin/email.go b/routers/api/v1/admin/email.go index 5914215bc2..ba963e9f69 100644 --- a/routers/api/v1/admin/email.go +++ b/routers/api/v1/admin/email.go @@ -7,9 +7,9 @@ import ( "net/http" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index 8a095a7def..2217d002a0 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -8,12 +8,12 @@ import ( "net/http" "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" webhook_service "code.gitea.io/gitea/services/webhook" ) diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index bf68942a9c..a5c299bbf0 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go index a4895f260b..c119d5390a 100644 --- a/routers/api/v1/admin/repo.go +++ b/routers/api/v1/admin/repo.go @@ -4,10 +4,10 @@ package admin import ( - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" + "code.gitea.io/gitea/services/context" ) // CreateRepo api for creating a repository diff --git a/routers/api/v1/admin/runners.go b/routers/api/v1/admin/runners.go index c0d9364435..329242d9f6 100644 --- a/routers/api/v1/admin/runners.go +++ b/routers/api/v1/admin/runners.go @@ -4,8 +4,8 @@ package admin import ( - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/shared" + "code.gitea.io/gitea/services/context" ) // https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 2ce7651a09..64315108b0 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -25,6 +24,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/mailer" user_service "code.gitea.io/gitea/services/user" diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e0c72c7ac4..0913571c27 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -79,7 +79,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -95,7 +94,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/auth" - context_service "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation @@ -855,11 +854,11 @@ func Routes() *web.Route { m.Group("/user/{username}", func() { m.Get("", activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) - }, context_service.UserAssignmentAPI()) + }, context.UserAssignmentAPI()) m.Group("/user-id/{user-id}", func() { m.Get("", activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) - }, context_service.UserIDAssignmentAPI()) + }, context.UserIDAssignmentAPI()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub)) } @@ -915,7 +914,7 @@ func Routes() *web.Route { }, reqSelfOrAdmin(), reqBasicOrRevProxyAuth()) m.Get("/activities/feeds", user.ListUserActivityFeeds) - }, context_service.UserAssignmentAPI(), individualPermsChecker) + }, context.UserAssignmentAPI(), individualPermsChecker) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser)) // Users (requires user scope) @@ -933,7 +932,7 @@ func Routes() *web.Route { m.Get("/starred", user.GetStarredRepos) m.Get("/subscriptions", user.GetWatchedRepos) - }, context_service.UserAssignmentAPI()) + }, context.UserAssignmentAPI()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Users (requires user scope) @@ -968,7 +967,7 @@ func Routes() *web.Route { m.Get("", user.CheckMyFollowing) m.Put("", user.Follow) m.Delete("", user.Unfollow) - }, context_service.UserAssignmentAPI()) + }, context.UserAssignmentAPI()) }) // (admin:public_key scope) @@ -1415,14 +1414,14 @@ func Routes() *web.Route { m.Get("/files", reqToken(), packages.ListPackageFiles) }) m.Get("/", reqToken(), packages.ListPackages) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) // Organizations m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) m.Group("/users/{username}/orgs", func() { m.Get("", reqToken(), org.ListUserOrgs) m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context_service.UserAssignmentAPI()) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI()) m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create) m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization)) m.Group("/orgs/{org}", func() { @@ -1520,7 +1519,7 @@ func Routes() *web.Route { m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser) - }, context_service.UserAssignmentAPI()) + }, context.UserAssignmentAPI()) }) m.Group("/emails", func() { m.Get("", admin.GetAllEmails) diff --git a/routers/api/v1/misc/gitignore.go b/routers/api/v1/misc/gitignore.go index 7c7fe4b125..dffd771752 100644 --- a/routers/api/v1/misc/gitignore.go +++ b/routers/api/v1/misc/gitignore.go @@ -6,11 +6,11 @@ package misc import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/options" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) // Shows a list of all Gitignore templates diff --git a/routers/api/v1/misc/label_templates.go b/routers/api/v1/misc/label_templates.go index 0e0ca39fc5..cc11f37626 100644 --- a/routers/api/v1/misc/label_templates.go +++ b/routers/api/v1/misc/label_templates.go @@ -6,9 +6,9 @@ package misc import ( "net/http" - "code.gitea.io/gitea/modules/context" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/misc/licenses.go b/routers/api/v1/misc/licenses.go index 65f63468cf..2a980f5084 100644 --- a/routers/api/v1/misc/licenses.go +++ b/routers/api/v1/misc/licenses.go @@ -8,12 +8,12 @@ import ( "net/http" "net/url" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/options" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) // Returns a list of all License templates diff --git a/routers/api/v1/misc/markup.go b/routers/api/v1/misc/markup.go index 7b24b353b6..9699c79368 100644 --- a/routers/api/v1/misc/markup.go +++ b/routers/api/v1/misc/markup.go @@ -6,12 +6,12 @@ package misc import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/context" ) // Markup render markup document to HTML diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go index ec8f8f47b7..f499501c2f 100644 --- a/routers/api/v1/misc/markup_test.go +++ b/routers/api/v1/misc/markup_test.go @@ -10,11 +10,11 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go index cc754f64a2..3bd80de5c1 100644 --- a/routers/api/v1/misc/nodeinfo.go +++ b/routers/api/v1/misc/nodeinfo.go @@ -9,9 +9,9 @@ import ( issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" ) const cacheKeyNodeInfoUsage = "API_NodeInfoUsage" diff --git a/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go index 2ca9813e15..24a46c1e70 100644 --- a/routers/api/v1/misc/signing.go +++ b/routers/api/v1/misc/signing.go @@ -7,8 +7,8 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/modules/context" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" ) // SigningKey returns the public key of the default signing key if it exists diff --git a/routers/api/v1/misc/version.go b/routers/api/v1/misc/version.go index 83fa35219a..e3b43a0e6b 100644 --- a/routers/api/v1/misc/version.go +++ b/routers/api/v1/misc/version.go @@ -6,9 +6,9 @@ package misc import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" ) // Version shows the version of the Gitea server diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go index c87da9399f..46b3c7f5e7 100644 --- a/routers/api/v1/notify/notifications.go +++ b/routers/api/v1/notify/notifications.go @@ -9,9 +9,9 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" ) // NewAvailable check if unread notifications exist diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 55ca6ad1fd..8d97e8a3f8 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -10,9 +10,9 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go index 919e52952d..8e12d359cb 100644 --- a/routers/api/v1/notify/threads.go +++ b/routers/api/v1/notify/threads.go @@ -10,7 +10,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 4abdfb2e92..879f484cce 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -9,8 +9,8 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go index 7b621a50c3..e34c68dfc9 100644 --- a/routers/api/v1/org/avatar.go +++ b/routers/api/v1/org/avatar.go @@ -7,9 +7,9 @@ import ( "encoding/base64" "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" user_service "code.gitea.io/gitea/services/user" ) diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index 3c3f058b5d..c1dc0519ea 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -6,10 +6,10 @@ package org import ( "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" webhook_service "code.gitea.io/gitea/services/webhook" ) diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 5a03059ded..b5ec54ccf4 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -9,11 +9,11 @@ import ( "strings" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/label" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 422b7cecfe..fb66d4c3f5 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -9,11 +9,11 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/organization" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 255e28c706..e848d95181 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -12,12 +12,12 @@ import ( "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/org" user_service "code.gitea.io/gitea/services/user" diff --git a/routers/api/v1/org/runners.go b/routers/api/v1/org/runners.go index 05bce8daef..2a52bd8778 100644 --- a/routers/api/v1/org/runners.go +++ b/routers/api/v1/org/runners.go @@ -4,8 +4,8 @@ package org import ( - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/shared" + "code.gitea.io/gitea/services/context" ) // https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization diff --git a/routers/api/v1/org/secrets.go b/routers/api/v1/org/secrets.go index ddc74d865b..abb6bb26c4 100644 --- a/routers/api/v1/org/secrets.go +++ b/routers/api/v1/org/secrets.go @@ -9,11 +9,11 @@ import ( "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" secret_service "code.gitea.io/gitea/services/secrets" ) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index f129c66230..b62a386fd7 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -15,12 +15,12 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go index a79ba315be..3be31b13ae 100644 --- a/routers/api/v1/packages/package.go +++ b/routers/api/v1/packages/package.go @@ -7,10 +7,10 @@ import ( "net/http" "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 039cdadac9..e0af276c71 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -7,10 +7,10 @@ import ( "errors" "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" secret_service "code.gitea.io/gitea/services/secrets" ) diff --git a/routers/api/v1/repo/avatar.go b/routers/api/v1/repo/avatar.go index 1b661955f0..698337ffd2 100644 --- a/routers/api/v1/repo/avatar.go +++ b/routers/api/v1/repo/avatar.go @@ -7,9 +7,9 @@ import ( "encoding/base64" "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/api/v1/repo/blob.go b/routers/api/v1/repo/blob.go index 26605bba03..3b116666ea 100644 --- a/routers/api/v1/repo/blob.go +++ b/routers/api/v1/repo/blob.go @@ -6,7 +6,7 @@ package repo import ( "net/http" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 2cdbcd25a2..5e6b6a8658 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -14,7 +14,6 @@ import ( git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/optional" @@ -22,6 +21,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index a222e50a5e..7d48d71516 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -13,11 +13,11 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" repo_module "code.gitea.io/gitea/modules/repository" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index d01cf6b8bc..d06a3b4e49 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -12,11 +12,11 @@ import ( issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 317213c946..907a5b568e 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -19,7 +19,6 @@ import ( git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/httpcache" @@ -30,6 +29,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/context" archiver_service "code.gitea.io/gitea/services/repository/archiver" files_service "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 69433bf4cc..212cc7a93b 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -14,11 +14,11 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/api/v1/repo/git_hook.go b/routers/api/v1/repo/git_hook.go index 7e471e263b..26ae84d08d 100644 --- a/routers/api/v1/repo/git_hook.go +++ b/routers/api/v1/repo/git_hook.go @@ -6,10 +6,10 @@ package repo import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/git_ref.go b/routers/api/v1/repo/git_ref.go index 34d2dcfcc8..0fa58425b8 100644 --- a/routers/api/v1/repo/git_ref.go +++ b/routers/api/v1/repo/git_ref.go @@ -7,10 +7,10 @@ import ( "net/http" "net/url" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" ) // GetGitAllRefs get ref or an list all the refs of a repository diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 8859e3ae23..ffd2313591 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -11,13 +11,13 @@ import ( "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" webhook_module "code.gitea.io/gitea/modules/webhook" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" webhook_service "code.gitea.io/gitea/services/webhook" ) diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go index 94a71e20ad..37cf61c1ed 100644 --- a/routers/api/v1/repo/hook_test.go +++ b/routers/api/v1/repo/hook_test.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/contexttest" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 0f76a4b4ff..efc1a08a05 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -18,7 +18,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -26,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" notify_service "code.gitea.io/gitea/services/notify" diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index 11d19b21ff..d62e23aa02 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -8,12 +8,12 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 2b7a8f7ba1..763419b7a2 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -14,11 +14,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index 21e2f4dabd..e7436db798 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -8,12 +8,12 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index 62d1057cdf..a42920d4fd 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -11,10 +11,10 @@ import ( issues_model "code.gitea.io/gitea/models/issues" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index c2f530956e..7d9f85d2aa 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -8,9 +8,9 @@ import ( "net/http" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go index 61f88de34e..ff1135862b 100644 --- a/routers/api/v1/repo/issue_pin.go +++ b/routers/api/v1/repo/issue_pin.go @@ -7,8 +7,8 @@ import ( "net/http" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go index c886bd71b7..799c687812 100644 --- a/routers/api/v1/repo/issue_reaction.go +++ b/routers/api/v1/repo/issue_reaction.go @@ -8,10 +8,10 @@ import ( "net/http" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index 52bf8b5c7b..d9054e8f77 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -8,8 +8,8 @@ import ( "net/http" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go index ece880c03e..a535172462 100644 --- a/routers/api/v1/repo/issue_subscription.go +++ b/routers/api/v1/repo/issue_subscription.go @@ -9,9 +9,9 @@ import ( issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index cf03e72aa0..c640515881 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -12,10 +12,10 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index af48c40885..88444a2625 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -15,12 +15,12 @@ import ( "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index 420d3ab5b4..b6eb51fd20 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -9,11 +9,11 @@ import ( "strconv" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/label" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/language.go b/routers/api/v1/repo/language.go index 12f1761ad0..f1d5bbe45f 100644 --- a/routers/api/v1/repo/language.go +++ b/routers/api/v1/repo/language.go @@ -9,8 +9,8 @@ import ( "strconv" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) type languageResponse []*repo_model.LanguageStat diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 839fbfe8a1..2caaa130e8 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -17,7 +17,6 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -26,6 +25,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index 9c2ed16d93..d4c828fe8b 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -11,12 +11,12 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index 26e0be301c..864644e1ef 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -13,12 +13,12 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go index e7e00dae41..a4a1d4eab7 100644 --- a/routers/api/v1/repo/notes.go +++ b/routers/api/v1/repo/notes.go @@ -7,9 +7,9 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/patch.go b/routers/api/v1/repo/patch.go index 9b5635d245..0e0601b7d9 100644 --- a/routers/api/v1/repo/patch.go +++ b/routers/api/v1/repo/patch.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 85f8ec1de5..8f9848f71d 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -21,7 +21,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" @@ -32,6 +31,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" "code.gitea.io/gitea/services/automerge" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/gitdiff" diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 6338651aae..5128102e61 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -12,11 +12,11 @@ import ( "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/gitrepo" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index a41c5ba7d8..a47fc1cc59 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -11,10 +11,10 @@ import ( "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" release_service "code.gitea.io/gitea/services/release" ) diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index c36bf12e6d..a29bce66a4 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -7,13 +7,13 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index 9f2098df06..fec91164a2 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" releaseservice "code.gitea.io/gitea/services/release" ) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 40de8853d8..8685d88913 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -20,7 +20,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/label" @@ -32,6 +31,7 @@ import ( "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/issue" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 08ba7fabac..8d6ca9e3b5 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -9,9 +9,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/api/v1/repo/runners.go b/routers/api/v1/repo/runners.go index 0a2bbf8117..fe133b311d 100644 --- a/routers/api/v1/repo/runners.go +++ b/routers/api/v1/repo/runners.go @@ -4,8 +4,8 @@ package repo import ( - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/shared" + "code.gitea.io/gitea/services/context" ) // GetRegistrationToken returns the token to register repo runners diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 05227e33a0..99676de119 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -7,9 +7,9 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index b4edf0608c..53711bedeb 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -9,10 +9,10 @@ import ( "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" files_service "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index 05509fc443..8584182857 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -7,9 +7,9 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 2f19f95e66..a6908f3615 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" releaseservice "code.gitea.io/gitea/services/release" ) diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go index 1bacc71211..0ecf3a39d8 100644 --- a/routers/api/v1/repo/teams.go +++ b/routers/api/v1/repo/teams.go @@ -8,7 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/organization" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index d662b9b583..1d8e675bde 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -8,11 +8,11 @@ import ( "strings" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index c0a40ce062..4f05c0df51 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -13,10 +13,10 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/api/v1/repo/tree.go b/routers/api/v1/repo/tree.go index f63100b6ea..353a996d5b 100644 --- a/routers/api/v1/repo/tree.go +++ b/routers/api/v1/repo/tree.go @@ -6,7 +6,7 @@ package repo import ( "net/http" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index 4f27500496..f18ea087c4 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -10,13 +10,13 @@ import ( "net/url" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" notify_service "code.gitea.io/gitea/services/notify" wiki_service "code.gitea.io/gitea/services/wiki" diff --git a/routers/api/v1/settings/settings.go b/routers/api/v1/settings/settings.go index 02bda1309d..0ee81b96d5 100644 --- a/routers/api/v1/settings/settings.go +++ b/routers/api/v1/settings/settings.go @@ -6,9 +6,9 @@ package settings import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" ) // GetGeneralUISettings returns instance's global settings for ui diff --git a/routers/api/v1/shared/runners.go b/routers/api/v1/shared/runners.go index a342bd4b63..c850ad7866 100644 --- a/routers/api/v1/shared/runners.go +++ b/routers/api/v1/shared/runners.go @@ -8,8 +8,8 @@ import ( "net/http" actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) // RegistrationToken is response related to registeration token diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go index cbe332a779..babb8c0cf7 100644 --- a/routers/api/v1/user/action.go +++ b/routers/api/v1/user/action.go @@ -7,10 +7,10 @@ import ( "errors" "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" secret_service "code.gitea.io/gitea/services/secrets" ) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index f045fb4d5d..88e314ed31 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -13,10 +13,10 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go index 1c1bb6181a..f912296228 100644 --- a/routers/api/v1/user/avatar.go +++ b/routers/api/v1/user/avatar.go @@ -7,9 +7,9 @@ import ( "encoding/base64" "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" user_service "code.gitea.io/gitea/services/user" ) diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index 3dcea9083c..33aa851a80 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -8,9 +8,9 @@ import ( "net/http" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" user_service "code.gitea.io/gitea/services/user" ) diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 5815ed4f0b..398c6b2567 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -8,9 +8,9 @@ import ( "net/http" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 234da5dfdc..b8438cd2aa 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -10,10 +10,10 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/helper.go b/routers/api/v1/user/helper.go index 392b266ebd..8b5c64e291 100644 --- a/routers/api/v1/user/helper.go +++ b/routers/api/v1/user/helper.go @@ -7,7 +7,7 @@ import ( "net/http" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) // GetUserByParamsName get user by name diff --git a/routers/api/v1/user/hook.go b/routers/api/v1/user/hook.go index e87385e4a2..9d9ca5bf01 100644 --- a/routers/api/v1/user/hook.go +++ b/routers/api/v1/user/hook.go @@ -6,10 +6,10 @@ package user import ( "net/http" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" webhook_service "code.gitea.io/gitea/services/webhook" ) diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index dd185aa7d6..ada6759f8e 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -11,13 +11,13 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index b8b2d265bf..81f8e0f3fe 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -11,9 +11,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/runners.go b/routers/api/v1/user/runners.go index 51556ae0fb..899218473e 100644 --- a/routers/api/v1/user/runners.go +++ b/routers/api/v1/user/runners.go @@ -4,8 +4,8 @@ package user import ( - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/shared" + "code.gitea.io/gitea/services/context" ) // https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go index 062df1ca43..d0a8daaa85 100644 --- a/routers/api/v1/user/settings.go +++ b/routers/api/v1/user/settings.go @@ -6,10 +6,10 @@ package user import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" user_service "code.gitea.io/gitea/services/user" ) diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 2659789ddd..e624884db3 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -12,9 +12,9 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index fb8f67d072..09147cd2ae 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -9,8 +9,8 @@ import ( activities_model "code.gitea.io/gitea/models/activities" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 7f531eafaa..706f4cc66b 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -11,9 +11,9 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index 5e80190017..4e25137817 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -8,10 +8,10 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) // ResolveRefOrSha resolve ref to sha if exist diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 28b21ab8db..f1abd49a7d 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -12,12 +12,12 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" + "code.gitea.io/gitea/services/context" webhook_service "code.gitea.io/gitea/services/webhook" ) diff --git a/routers/api/v1/utils/page.go b/routers/api/v1/utils/page.go index 6910b82931..024ba7b8d9 100644 --- a/routers/api/v1/utils/page.go +++ b/routers/api/v1/utils/page.go @@ -5,7 +5,7 @@ package utils import ( "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/common/auth.go b/routers/common/auth.go index 8904785d51..115d65ed10 100644 --- a/routers/common/auth.go +++ b/routers/common/auth.go @@ -5,9 +5,9 @@ package common import ( user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" auth_service "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/context" ) type AuthResult struct { diff --git a/routers/common/errpage.go b/routers/common/errpage.go index 923421a29c..402ca44c12 100644 --- a/routers/common/errpage.go +++ b/routers/common/errpage.go @@ -9,13 +9,13 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/routing" + "code.gitea.io/gitea/services/context" ) const tplStatus500 base.TplName = "status/500" diff --git a/routers/common/markup.go b/routers/common/markup.go index a1c2c37ac0..7819ee7227 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -9,11 +9,11 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" "mvdan.cc/xurls/v2" ) diff --git a/routers/common/middleware.go b/routers/common/middleware.go index 8a39dda179..1ee4c629ad 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -9,11 +9,11 @@ import ( "strings" "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/routing" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/session" "github.com/chi-middleware/proxy" diff --git a/routers/common/serve.go b/routers/common/serve.go index 8a7f8b3332..446908db75 100644 --- a/routers/common/serve.go +++ b/routers/common/serve.go @@ -7,11 +7,11 @@ import ( "io" "time" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) // ServeBlob download a git.Blob diff --git a/routers/install/install.go b/routers/install/install.go index decf74cecb..9c6a8849b6 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -22,7 +22,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -36,6 +35,7 @@ import ( "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/common" auth_service "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "gitea.com/go-chi/session" diff --git a/routers/private/actions.go b/routers/private/actions.go index 886f23b1c2..53c2412308 100644 --- a/routers/private/actions.go +++ b/routers/private/actions.go @@ -12,11 +12,11 @@ import ( actions_model "code.gitea.io/gitea/models/actions" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) // GenerateActionsRunnerToken generates a new runner token for a given scope diff --git a/routers/private/default_branch.go b/routers/private/default_branch.go index a23e101e9d..2e323129ef 100644 --- a/routers/private/default_branch.go +++ b/routers/private/default_branch.go @@ -8,9 +8,9 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/private" + gitea_context "code.gitea.io/gitea/services/context" ) // SetDefaultBranch updates the default branch diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index 5ae03ce294..4eafe3923d 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -10,7 +10,6 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" @@ -18,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + gitea_context "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index ad52f35084..32ec3003e2 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -16,11 +16,11 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/web" + gitea_context "code.gitea.io/gitea/services/context" pull_service "code.gitea.io/gitea/services/pull" ) diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go index 5805202bb5..cee3bbdd12 100644 --- a/routers/private/hook_proc_receive.go +++ b/routers/private/hook_proc_receive.go @@ -7,12 +7,12 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/agit" + gitea_context "code.gitea.io/gitea/services/context" ) // HookProcReceive proc-receive hook - only handles agit Proc-Receive requests at present diff --git a/routers/private/internal.go b/routers/private/internal.go index 407edebeed..ede310113c 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -8,11 +8,11 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" chi_middleware "github.com/go-chi/chi/v5/middleware" diff --git a/routers/private/internal_repo.go b/routers/private/internal_repo.go index 615239d479..e8ee8ba8ac 100644 --- a/routers/private/internal_repo.go +++ b/routers/private/internal_repo.go @@ -9,10 +9,10 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" + gitea_context "code.gitea.io/gitea/services/context" ) // This file contains common functions relating to setting the Repository for the internal routes diff --git a/routers/private/key.go b/routers/private/key.go index 0096480d6a..5b8f238a83 100644 --- a/routers/private/key.go +++ b/routers/private/key.go @@ -7,9 +7,9 @@ import ( "net/http" asymkey_model "code.gitea.io/gitea/models/asymkey" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/services/context" ) // UpdatePublicKeyInRepo update public key and deploy key updates diff --git a/routers/private/mail.go b/routers/private/mail.go index e5e162c880..c19ee67896 100644 --- a/routers/private/mail.go +++ b/routers/private/mail.go @@ -11,11 +11,11 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/mailer" ) diff --git a/routers/private/manager.go b/routers/private/manager.go index 397e6fac7b..a6aa03e4ec 100644 --- a/routers/private/manager.go +++ b/routers/private/manager.go @@ -8,7 +8,6 @@ import ( "net/http" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful/releasereopen" "code.gitea.io/gitea/modules/log" @@ -17,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" ) // ReloadTemplates reloads all the templates diff --git a/routers/private/manager_process.go b/routers/private/manager_process.go index 68e4a21805..9a0298a37c 100644 --- a/routers/private/manager_process.go +++ b/routers/private/manager_process.go @@ -11,10 +11,10 @@ import ( "runtime" "time" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" process_module "code.gitea.io/gitea/modules/process" + "code.gitea.io/gitea/services/context" ) // Processes prints out the processes diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go index 09ced33b8d..0c63ebc918 100644 --- a/routers/private/manager_unix.go +++ b/routers/private/manager_unix.go @@ -8,8 +8,8 @@ package private import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" + "code.gitea.io/gitea/services/context" ) // Restart causes the server to perform a graceful restart diff --git a/routers/private/manager_windows.go b/routers/private/manager_windows.go index bd3c3c30d0..f1b9365f52 100644 --- a/routers/private/manager_windows.go +++ b/routers/private/manager_windows.go @@ -8,9 +8,9 @@ package private import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/private" + "code.gitea.io/gitea/services/context" ) // Restart is not implemented for Windows based servers as they can't fork diff --git a/routers/private/restore_repo.go b/routers/private/restore_repo.go index 7efc22a3d9..4e95d3071d 100644 --- a/routers/private/restore_repo.go +++ b/routers/private/restore_repo.go @@ -7,9 +7,9 @@ import ( "io" "net/http" - myCtx "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/private" + myCtx "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/migrations" ) diff --git a/routers/private/serv.go b/routers/private/serv.go index 3812ccb52b..85368a0aed 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -14,11 +14,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" ) diff --git a/routers/private/ssh_log.go b/routers/private/ssh_log.go index eacfa18f05..5bec632ead 100644 --- a/routers/private/ssh_log.go +++ b/routers/private/ssh_log.go @@ -6,11 +6,11 @@ package private import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" ) // SSHLog hook to response ssh log diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index 9fbd429f74..f3f10fd1b8 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -14,13 +14,13 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/updatechecker" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/cron" "code.gitea.io/gitea/services/forms" release_service "code.gitea.io/gitea/services/release" diff --git a/routers/web/admin/applications.go b/routers/web/admin/applications.go index b6f7bcd2a5..8583398074 100644 --- a/routers/web/admin/applications.go +++ b/routers/web/admin/applications.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" user_setting "code.gitea.io/gitea/routers/web/user/setting" + "code.gitea.io/gitea/services/context" ) var ( diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 7fdd18dfae..ba487d1045 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -16,7 +16,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/auth/pam" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -27,6 +26,7 @@ import ( pam_service "code.gitea.io/gitea/services/auth/source/pam" "code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/auth/source/sspi" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "xorm.io/xorm/convert" diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index 47f9201504..2f5f17e201 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -12,13 +12,13 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/mailer" "gitea.com/go-chi/session" diff --git a/routers/web/admin/diagnosis.go b/routers/web/admin/diagnosis.go index 2d550125d5..020554a35a 100644 --- a/routers/web/admin/diagnosis.go +++ b/routers/web/admin/diagnosis.go @@ -9,8 +9,8 @@ import ( "runtime/pprof" "time" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httplib" + "code.gitea.io/gitea/services/context" ) func MonitorDiagnosis(ctx *context.Context) { diff --git a/routers/web/admin/emails.go b/routers/web/admin/emails.go index 59f80035d8..4296d70aee 100644 --- a/routers/web/admin/emails.go +++ b/routers/web/admin/emails.go @@ -11,10 +11,10 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go index cd8cc29cdf..8d4c66fdb2 100644 --- a/routers/web/admin/hooks.go +++ b/routers/web/admin/hooks.go @@ -8,9 +8,9 @@ import ( "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/admin/notice.go b/routers/web/admin/notice.go index e1cb578d05..36303cbc06 100644 --- a/routers/web/admin/notice.go +++ b/routers/web/admin/notice.go @@ -11,9 +11,9 @@ import ( "code.gitea.io/gitea/models/db" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go index 00131c9e2f..c5454db71e 100644 --- a/routers/web/admin/orgs.go +++ b/routers/web/admin/orgs.go @@ -8,10 +8,10 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/web/explore" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index 35ce215be4..7c16b69a85 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -11,9 +11,9 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup" ) diff --git a/routers/web/admin/queue.go b/routers/web/admin/queue.go index 18a8d7d3e6..d8c50730b1 100644 --- a/routers/web/admin/queue.go +++ b/routers/web/admin/queue.go @@ -7,9 +7,9 @@ import ( "net/http" "strconv" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) func Queues(ctx *context.Context) { diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go index 45c280ef73..ddf4440167 100644 --- a/routers/web/admin/repos.go +++ b/routers/web/admin/repos.go @@ -12,11 +12,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/explore" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/admin/runners.go b/routers/web/admin/runners.go index eaa268b4f1..d73290a8db 100644 --- a/routers/web/admin/runners.go +++ b/routers/web/admin/runners.go @@ -4,8 +4,8 @@ package admin import ( - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) func RedirectToDefaultSetting(ctx *context.Context) { diff --git a/routers/web/admin/stacktrace.go b/routers/web/admin/stacktrace.go index b603fb59a2..d6def94bb4 100644 --- a/routers/web/admin/stacktrace.go +++ b/routers/web/admin/stacktrace.go @@ -7,9 +7,9 @@ import ( "net/http" "runtime" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) // Stacktrace show admin monitor goroutines page diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index adb9799c01..cbca26eba8 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -19,7 +19,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -27,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/explore" user_setting "code.gitea.io/gitea/routers/web/user/setting" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" user_service "code.gitea.io/gitea/services/user" diff --git a/routers/web/admin/users_test.go b/routers/web/admin/users_test.go index 560ee70ea0..f6f9237858 100644 --- a/routers/web/admin/users_test.go +++ b/routers/web/admin/users_test.go @@ -8,10 +8,10 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" "github.com/stretchr/testify/assert" diff --git a/routers/web/auth/2fa.go b/routers/web/auth/2fa.go index dc0062ebaa..f93177bf96 100644 --- a/routers/web/auth/2fa.go +++ b/routers/web/auth/2fa.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index a30ee0ce54..fee6a89a99 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -16,7 +16,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -29,6 +28,7 @@ import ( "code.gitea.io/gitea/routers/utils" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index 1d94e52fe3..f744a57a43 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -12,13 +12,13 @@ import ( "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 33a4ae9192..d5ca7397f0 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -22,7 +22,6 @@ import ( auth_module "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -34,6 +33,7 @@ import ( auth_service "code.gitea.io/gitea/services/auth" source_service "code.gitea.io/gitea/services/auth/source" "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" user_service "code.gitea.io/gitea/services/user" diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index 29ef772b1c..2143b8096a 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -11,12 +11,12 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index 1f2d133282..c9e0386041 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -12,7 +12,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -20,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" user_service "code.gitea.io/gitea/services/user" diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go index 95c8d262a5..1079f44a08 100644 --- a/routers/web/auth/webauthn.go +++ b/routers/web/auth/webauthn.go @@ -11,9 +11,9 @@ import ( user_model "code.gitea.io/gitea/models/user" wa "code.gitea.io/gitea/modules/auth/webauthn" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "github.com/go-webauthn/webauthn/protocol" diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index 525ca9be53..dd20663f94 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -10,8 +10,8 @@ import ( "time" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/services/context" ) // List all devtest templates, they will be used for e2e tests for the UI components diff --git a/routers/web/events/events.go b/routers/web/events/events.go index 1a5a162c1a..52f20e07dc 100644 --- a/routers/web/events/events.go +++ b/routers/web/events/events.go @@ -7,11 +7,11 @@ import ( "net/http" "time" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/routers/web/auth" + "code.gitea.io/gitea/services/context" ) // Events listens for events diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index d81884ec62..2cde8b655e 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -8,9 +8,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index dc1318beef..4a468482ae 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -6,9 +6,9 @@ package explore import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" ) // Organizations render explore organizations page diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go index 0446edebe6..d5a46f6883 100644 --- a/routers/web/explore/repo.go +++ b/routers/web/explore/repo.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/explore/topic.go b/routers/web/explore/topic.go index bb1be310de..95fecfe2b8 100644 --- a/routers/web/explore/topic.go +++ b/routers/web/explore/topic.go @@ -8,8 +8,8 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index 09d31f95ef..b67fac2fc1 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -10,12 +10,12 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go index f13038ff9b..80ce2ad198 100644 --- a/routers/web/feed/branch.go +++ b/routers/web/feed/branch.go @@ -9,7 +9,7 @@ import ( "time" "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "github.com/gorilla/feeds" ) diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 1e040ed819..298fe0bb39 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -14,12 +14,12 @@ import ( activities_model "code.gitea.io/gitea/models/activities" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" "github.com/gorilla/feeds" ) diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go index 56a9c54ddc..1ab768ff27 100644 --- a/routers/web/feed/file.go +++ b/routers/web/feed/file.go @@ -9,9 +9,9 @@ import ( "time" "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" "github.com/gorilla/feeds" ) diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go index 3feca68d61..2b70aad17b 100644 --- a/routers/web/feed/profile.go +++ b/routers/web/feed/profile.go @@ -7,9 +7,9 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/services/context" "github.com/gorilla/feeds" ) diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go index 558c03dba7..273f47e3b4 100644 --- a/routers/web/feed/release.go +++ b/routers/web/feed/release.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "github.com/gorilla/feeds" ) diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go index 8931dae8cc..a41808c24a 100644 --- a/routers/web/feed/render.go +++ b/routers/web/feed/render.go @@ -4,7 +4,7 @@ package feed import ( - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) // RenderBranchFeed render format for branch or file diff --git a/routers/web/feed/repo.go b/routers/web/feed/repo.go index 51c24510c7..bfcc3a37d6 100644 --- a/routers/web/feed/repo.go +++ b/routers/web/feed/repo.go @@ -8,7 +8,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "github.com/gorilla/feeds" ) diff --git a/routers/web/githttp.go b/routers/web/githttp.go index ab74e9a333..5f1dedce76 100644 --- a/routers/web/githttp.go +++ b/routers/web/githttp.go @@ -6,11 +6,10 @@ package web import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/repo" - context_service "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context" ) func requireSignIn(ctx *context.Context) { @@ -39,5 +38,5 @@ func gitHTTPRouters(m *web.Route) { m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject) m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile) m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile) - }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb()) + }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb()) } diff --git a/routers/web/goget.go b/routers/web/goget.go index c5b8b6cbc0..8d5612ebfe 100644 --- a/routers/web/goget.go +++ b/routers/web/goget.go @@ -12,9 +12,9 @@ import ( "strings" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) func goGet(ctx *context.Context) { diff --git a/routers/web/home.go b/routers/web/home.go index 2321b00efe..555f94c983 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -12,7 +12,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" @@ -21,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/web/auth" "code.gitea.io/gitea/routers/web/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/misc/markup.go b/routers/web/misc/markup.go index c91da9a7f1..2dbbd6fc09 100644 --- a/routers/web/misc/markup.go +++ b/routers/web/misc/markup.go @@ -5,10 +5,10 @@ package misc import ( - "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/context" ) // Markup render markup document to HTML diff --git a/routers/web/misc/swagger.go b/routers/web/misc/swagger.go index 72c09a3780..5fddfa8885 100644 --- a/routers/web/misc/swagger.go +++ b/routers/web/misc/swagger.go @@ -7,7 +7,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) // tplSwagger swagger page template diff --git a/routers/web/nodeinfo.go b/routers/web/nodeinfo.go index 01b71e7086..f1cc7bf530 100644 --- a/routers/web/nodeinfo.go +++ b/routers/web/nodeinfo.go @@ -7,8 +7,8 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) type nodeInfoLinks struct { diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 36f543dc45..4a7378689a 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -20,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/org/members.go b/routers/web/org/members.go index 15a615c706..9a3d60e122 100644 --- a/routers/web/org/members.go +++ b/routers/web/org/members.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/org/org.go b/routers/web/org/org.go index 1e4544730e..f94dd16eae 100644 --- a/routers/web/org/org.go +++ b/routers/web/org/org.go @@ -12,10 +12,10 @@ import ( "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/org/org_labels.go b/routers/web/org/org_labels.go index f78bd00274..02eae8052e 100644 --- a/routers/web/org/org_labels.go +++ b/routers/web/org/org_labels.go @@ -8,10 +8,10 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/label" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index f062127d24..338558fa23 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -17,12 +17,12 @@ import ( attachment_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/org/projects_test.go b/routers/web/org/projects_test.go index 8053ab4cf9..f4ccfe1c06 100644 --- a/routers/web/org/projects_test.go +++ b/routers/web/org/projects_test.go @@ -7,8 +7,8 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/routers/web/org" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 47d0063f76..494ada4323 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -14,7 +14,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" @@ -22,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/web" shared_user "code.gitea.io/gitea/routers/web/shared/user" user_setting "code.gitea.io/gitea/routers/web/user/setting" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/web/org/setting/runners.go b/routers/web/org/setting/runners.go index c3c771036a..fe05709237 100644 --- a/routers/web/org/setting/runners.go +++ b/routers/web/org/setting/runners.go @@ -4,7 +4,7 @@ package setting import ( - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) func RedirectToDefaultSetting(ctx *context.Context) { diff --git a/routers/web/org/setting_oauth2.go b/routers/web/org/setting_oauth2.go index ca4fe09f38..7f855795d3 100644 --- a/routers/web/org/setting_oauth2.go +++ b/routers/web/org/setting_oauth2.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" shared_user "code.gitea.io/gitea/routers/web/shared/user" user_setting "code.gitea.io/gitea/routers/web/user/setting" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/org/setting_packages.go b/routers/web/org/setting_packages.go index 796829d34e..af9836e42c 100644 --- a/routers/web/org/setting_packages.go +++ b/routers/web/org/setting_packages.go @@ -8,10 +8,10 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" shared "code.gitea.io/gitea/routers/web/shared/packages" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index 71fe99c97c..fd7486cacd 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -20,11 +20,11 @@ import ( unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" org_service "code.gitea.io/gitea/services/org" diff --git a/routers/web/passkey.go b/routers/web/passkey.go index 95874dfc48..0d10a69dfe 100644 --- a/routers/web/passkey.go +++ b/routers/web/passkey.go @@ -6,8 +6,8 @@ package web import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) type passkeyEndpointsType struct { diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 19aca26711..e784912377 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -15,11 +15,11 @@ import ( "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/repo" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "github.com/nektos/act/pkg/model" diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 49387362b3..2f26e710cd 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -21,12 +21,12 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/base" - context_module "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" actions_service "code.gitea.io/gitea/services/actions" + context_module "code.gitea.io/gitea/services/context" "xorm.io/builder" ) diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go index af99c4ed98..6f6641cc65 100644 --- a/routers/web/repo/activity.go +++ b/routers/web/repo/activity.go @@ -10,7 +10,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index 8c322b45e5..f0c5622aec 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -9,15 +9,15 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/attachment" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 7602b30d2b..b088b8387e 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -13,13 +13,13 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index c543160f42..05f06a3ceb 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -16,7 +16,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -24,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" release_service "code.gitea.io/gitea/services/release" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go index 8de54d569f..088f8d889d 100644 --- a/routers/web/repo/cherry_pick.go +++ b/routers/web/repo/cherry_pick.go @@ -12,11 +12,11 @@ import ( git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/web/repo/code_frequency.go b/routers/web/repo/code_frequency.go index 48ade655b7..c76f492da0 100644 --- a/routers/web/repo/code_frequency.go +++ b/routers/web/repo/code_frequency.go @@ -8,7 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" contributors_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 32fa973ef6..16da917d22 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -19,7 +19,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitgraph" "code.gitea.io/gitea/modules/gitrepo" @@ -27,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/gitdiff" git_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 535487d5fd..b0570f97c3 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -25,7 +25,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" csv_module "code.gitea.io/gitea/modules/csv" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" @@ -35,8 +34,9 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/gitdiff" ) diff --git a/routers/web/repo/contributors.go b/routers/web/repo/contributors.go index bcfef7580a..5fda17469e 100644 --- a/routers/web/repo/contributors.go +++ b/routers/web/repo/contributors.go @@ -8,7 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" contributors_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index a9e2e2b2fa..c4a8baecca 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -9,7 +9,6 @@ import ( "time" git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/lfs" @@ -17,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/context" ) // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 28644fbe3d..8f3d9612ec 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -16,17 +16,17 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/typesniffer" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" files_service "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/web/repo/editor_test.go b/routers/web/repo/editor_test.go index c28c3ef1d6..313fcfe33a 100644 --- a/routers/web/repo/editor_test.go +++ b/routers/web/repo/editor_test.go @@ -7,9 +7,9 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/web/repo/find.go b/routers/web/repo/find.go index daefe59c8f..07b3722798 100644 --- a/routers/web/repo/find.go +++ b/routers/web/repo/find.go @@ -7,7 +7,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 27c7f4961d..8fb6d93068 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -24,13 +24,13 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" "github.com/go-chi/cors" diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go index a98abe566f..5e1e116018 100644 --- a/routers/web/repo/helper.go +++ b/routers/web/repo/helper.go @@ -8,8 +8,8 @@ import ( "sort" "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/services/context" ) func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 46d48c4638..65e74a2d90 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -31,7 +31,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" @@ -44,11 +43,12 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/templates/vars" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" issue_service "code.gitea.io/gitea/services/issue" diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go index 0939af487c..fce0eccc7b 100644 --- a/routers/web/repo/issue_content_history.go +++ b/routers/web/repo/issue_content_history.go @@ -11,11 +11,11 @@ import ( "code.gitea.io/gitea/models/avatars" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/services/context" "github.com/sergi/go-diff/diffmatchpatch" ) diff --git a/routers/web/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go index 022ec3ae3e..e3b85ee638 100644 --- a/routers/web/repo/issue_dependency.go +++ b/routers/web/repo/issue_dependency.go @@ -8,8 +8,8 @@ import ( issues_model "code.gitea.io/gitea/models/issues" access_model "code.gitea.io/gitea/models/perm/access" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) // AddDependency adds new dependencies diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go index dd3e2803b4..9dedaefa4b 100644 --- a/routers/web/repo/issue_label.go +++ b/routers/web/repo/issue_label.go @@ -10,12 +10,12 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" issue_service "code.gitea.io/gitea/services/issue" ) diff --git a/routers/web/repo/issue_label_test.go b/routers/web/repo/issue_label_test.go index 742f12114d..93fc72300b 100644 --- a/routers/web/repo/issue_label_test.go +++ b/routers/web/repo/issue_label_test.go @@ -10,10 +10,10 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" "github.com/stretchr/testify/assert" diff --git a/routers/web/repo/issue_lock.go b/routers/web/repo/issue_lock.go index f83109d9b3..1d5fc8a5f3 100644 --- a/routers/web/repo/issue_lock.go +++ b/routers/web/repo/issue_lock.go @@ -5,8 +5,8 @@ package repo import ( issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/repo/issue_pin.go b/routers/web/repo/issue_pin.go index 9f334129f9..365c812681 100644 --- a/routers/web/repo/issue_pin.go +++ b/routers/web/repo/issue_pin.go @@ -7,9 +7,9 @@ import ( "net/http" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) // IssuePinOrUnpin pin or unpin a Issue diff --git a/routers/web/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go index ab9fe3e69d..70d42b27c0 100644 --- a/routers/web/repo/issue_stopwatch.go +++ b/routers/web/repo/issue_stopwatch.go @@ -9,8 +9,8 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/eventsource" + "code.gitea.io/gitea/services/context" ) // IssueStopwatch creates or stops a stopwatch for the given issue. diff --git a/routers/web/repo/issue_timetrack.go b/routers/web/repo/issue_timetrack.go index c9bf861b84..241e434049 100644 --- a/routers/web/repo/issue_timetrack.go +++ b/routers/web/repo/issue_timetrack.go @@ -9,9 +9,9 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/repo/issue_watch.go b/routers/web/repo/issue_watch.go index 1f51ceba5e..8b033f3b17 100644 --- a/routers/web/repo/issue_watch.go +++ b/routers/web/repo/issue_watch.go @@ -9,8 +9,8 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go index d70a53030e..420931c5fb 100644 --- a/routers/web/repo/middlewares.go +++ b/routers/web/repo/middlewares.go @@ -9,9 +9,9 @@ import ( system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/services/context" user_service "code.gitea.io/gitea/services/user" ) diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go index b70901d5f2..97b0c425ea 100644 --- a/routers/web/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -15,13 +15,13 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" "code.gitea.io/gitea/services/task" diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index 400748b963..49ac94aaf1 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -12,13 +12,13 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/issue" diff --git a/routers/web/repo/packages.go b/routers/web/repo/packages.go index ac9e64d774..6ed5909dcf 100644 --- a/routers/web/repo/packages.go +++ b/routers/web/repo/packages.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go index 00bd45aaec..0dee02dd9c 100644 --- a/routers/web/repo/patch.go +++ b/routers/web/repo/patch.go @@ -10,10 +10,10 @@ import ( git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/repository/files" ) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index cc0127e7e1..1f9ee727c3 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -17,13 +17,13 @@ import ( attachment_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/repo/projects_test.go b/routers/web/repo/projects_test.go index 6698d47028..479f8c55a2 100644 --- a/routers/web/repo/projects_test.go +++ b/routers/web/repo/projects_test.go @@ -7,7 +7,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index af626dad30..b1521a2112 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -27,7 +27,6 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" @@ -36,12 +35,13 @@ import ( "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" "code.gitea.io/gitea/services/automerge" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/gitdiff" notify_service "code.gitea.io/gitea/services/notify" diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 92665af7e7..64212291e1 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -11,12 +11,12 @@ import ( issues_model "code.gitea.io/gitea/models/issues" pull_model "code.gitea.io/gitea/models/pull" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" ) diff --git a/routers/web/repo/pull_review_test.go b/routers/web/repo/pull_review_test.go index 8fc9cecaf3..5f035f1eb0 100644 --- a/routers/web/repo/pull_review_test.go +++ b/routers/web/repo/pull_review_test.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/pull" "github.com/stretchr/testify/assert" diff --git a/routers/web/repo/recent_commits.go b/routers/web/repo/recent_commits.go index 3507cb8752..c158fb30b6 100644 --- a/routers/web/repo/recent_commits.go +++ b/routers/web/repo/recent_commits.go @@ -8,7 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" contributors_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index b920ffb6dd..f9ab956d4c 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -17,16 +17,16 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/feed" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" releaseservice "code.gitea.io/gitea/services/release" ) diff --git a/routers/web/repo/release_test.go b/routers/web/repo/release_test.go index c4a2c1904e..7ebea4c3fb 100644 --- a/routers/web/repo/release_test.go +++ b/routers/web/repo/release_test.go @@ -10,8 +10,8 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" "github.com/stretchr/testify/assert" diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index 7eb5a42aa4..10fa21c60e 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -10,11 +10,11 @@ import ( "path" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) // RenderFile renders a file by repos path diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 323413d976..0fad8752e3 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -21,7 +21,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -31,6 +30,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 3c0fa4bc00..c53d8fd918 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -7,9 +7,9 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) const tplSearch base.TplName = "repo/search" diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go index 44468d2666..504f57cfc2 100644 --- a/routers/web/repo/setting/avatar.go +++ b/routers/web/repo/setting/avatar.go @@ -8,11 +8,11 @@ import ( "fmt" "io" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/setting/collaboration.go b/routers/web/repo/setting/collaboration.go index c5c2a88c49..6bfd485566 100644 --- a/routers/web/repo/setting/collaboration.go +++ b/routers/web/repo/setting/collaboration.go @@ -13,10 +13,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/mailer" org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/web/repo/setting/default_branch.go b/routers/web/repo/setting/default_branch.go index c8a576e576..881d148afc 100644 --- a/routers/web/repo/setting/default_branch.go +++ b/routers/web/repo/setting/default_branch.go @@ -7,10 +7,10 @@ import ( "net/http" git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/web/repo" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go index 3d4420006c..abc3eb4af1 100644 --- a/routers/web/repo/setting/deploy_key.go +++ b/routers/web/repo/setting/deploy_key.go @@ -8,11 +8,11 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/repo/setting/git_hooks.go b/routers/web/repo/setting/git_hooks.go index 551327d44b..217a01c90c 100644 --- a/routers/web/repo/setting/git_hooks.go +++ b/routers/web/repo/setting/git_hooks.go @@ -6,8 +6,8 @@ package setting import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/services/context" ) // GitHooks hooks of a repository diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index 76a90a4ac5..32049cf0a4 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/pipeline" "code.gitea.io/gitea/modules/lfs" @@ -28,6 +27,7 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 85068f0ab2..b30dc3b061 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -15,9 +15,9 @@ import ( "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/repo" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository" diff --git a/routers/web/repo/setting/protected_tag.go b/routers/web/repo/setting/protected_tag.go index 46addb3f0a..2c25b650b9 100644 --- a/routers/web/repo/setting/protected_tag.go +++ b/routers/web/repo/setting/protected_tag.go @@ -13,9 +13,9 @@ import ( "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/repo/setting/runners.go b/routers/web/repo/setting/runners.go index 8d4112c157..a47d3b45e2 100644 --- a/routers/web/repo/setting/runners.go +++ b/routers/web/repo/setting/runners.go @@ -11,10 +11,10 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" actions_shared "code.gitea.io/gitea/routers/web/shared/actions" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/setting/secrets.go b/routers/web/repo/setting/secrets.go index cf427b2c44..d4d56bfc57 100644 --- a/routers/web/repo/setting/secrets.go +++ b/routers/web/repo/setting/secrets.go @@ -8,10 +8,10 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" shared "code.gitea.io/gitea/routers/web/shared/secrets" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 3b11638a92..b13c4c2ddb 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -18,7 +18,6 @@ import ( unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/indexer/stats" @@ -31,6 +30,7 @@ import ( "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" mirror_service "code.gitea.io/gitea/services/mirror" diff --git a/routers/web/repo/setting/settings_test.go b/routers/web/repo/setting/settings_test.go index 066d2ef2a9..09586cc68d 100644 --- a/routers/web/repo/setting/settings_test.go +++ b/routers/web/repo/setting/settings_test.go @@ -14,10 +14,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" repo_service "code.gitea.io/gitea/services/repository" diff --git a/routers/web/repo/setting/variables.go b/routers/web/repo/setting/variables.go index 428aa0bd5c..45b6c0f39a 100644 --- a/routers/web/repo/setting/variables.go +++ b/routers/web/repo/setting/variables.go @@ -8,10 +8,10 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" shared "code.gitea.io/gitea/routers/web/shared/actions" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index c12d7e82a6..bba4d4df51 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -18,7 +18,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" @@ -26,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" webhook_module "code.gitea.io/gitea/modules/webhook" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/forms" webhook_service "code.gitea.io/gitea/services/webhook" diff --git a/routers/web/repo/topic.go b/routers/web/repo/topic.go index d0e706c5bd..d81a695df9 100644 --- a/routers/web/repo/topic.go +++ b/routers/web/repo/topic.go @@ -8,8 +8,8 @@ import ( "strings" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) // TopicsPost response for creating repository diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go index c364e7090f..d11af4669f 100644 --- a/routers/web/repo/treelist.go +++ b/routers/web/repo/treelist.go @@ -7,8 +7,8 @@ import ( "net/http" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/services/context" "github.com/go-enry/go-enry/v2" ) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 48a35dd060..e89739e2fb 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -36,7 +36,6 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/lfs" @@ -49,6 +48,7 @@ import ( "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/feed" + "code.gitea.io/gitea/services/context" issue_service "code.gitea.io/gitea/services/issue" files_service "code.gitea.io/gitea/services/repository/files" diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 49e95faaba..91cf727e2c 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" @@ -29,6 +28,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" notify_service "code.gitea.io/gitea/services/notify" wiki_service "code.gitea.io/gitea/services/wiki" diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index d3decdae2d..49c83cfef5 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -11,10 +11,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" wiki_service "code.gitea.io/gitea/services/wiki" diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index ae9a376724..34b7969442 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -8,10 +8,10 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go index 07a0575207..0f705399c9 100644 --- a/routers/web/shared/actions/variables.go +++ b/routers/web/shared/actions/variables.go @@ -10,9 +10,9 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" secret_service "code.gitea.io/gitea/services/secrets" ) diff --git a/routers/web/shared/packages/packages.go b/routers/web/shared/packages/packages.go index 30c25374d1..1454396f04 100644 --- a/routers/web/shared/packages/packages.go +++ b/routers/web/shared/packages/packages.go @@ -12,10 +12,10 @@ import ( packages_model "code.gitea.io/gitea/models/packages" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" cargo_service "code.gitea.io/gitea/services/packages/cargo" container_service "code.gitea.io/gitea/services/packages/container" diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go index c805da734a..73505ec372 100644 --- a/routers/web/shared/secrets/secrets.go +++ b/routers/web/shared/secrets/secrets.go @@ -6,10 +6,10 @@ package secrets import ( "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/shared/actions" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" secret_service "code.gitea.io/gitea/services/secrets" ) diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 99b701b439..eb108268ae 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -13,12 +13,12 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" ) // prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu) diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go index 42e9dbe967..fc39b504a9 100644 --- a/routers/web/swagger_json.go +++ b/routers/web/swagger_json.go @@ -4,7 +4,7 @@ package web import ( - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" ) // SwaggerV1Json render swagger v1 json diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go index 772cc38bea..04f510161d 100644 --- a/routers/web/user/avatar.go +++ b/routers/web/user/avatar.go @@ -9,8 +9,8 @@ import ( "code.gitea.io/gitea/models/avatars" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httpcache" + "code.gitea.io/gitea/services/context" ) func cacheableRedirect(ctx *context.Context, location string) { diff --git a/routers/web/user/code.go b/routers/web/user/code.go index ee514a7cfe..eb711b76eb 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -8,10 +8,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/user/home.go b/routers/web/user/home.go index b7abbcbc00..78548e6df7 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -24,7 +24,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -32,7 +31,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/feed" - context_service "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -714,7 +713,7 @@ func UsernameSubRoute(ctx *context.Context) { username := ctx.Params("username") reloadParam := func(suffix string) (success bool) { ctx.SetParams("username", strings.TrimSuffix(username, suffix)) - context_service.UserAssignmentWeb()(ctx) + context.UserAssignmentWeb()(ctx) // check view permissions if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) { ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name)) @@ -741,7 +740,7 @@ func UsernameSubRoute(ctx *context.Context) { return } if reloadParam(".rss") { - context_service.UserAssignmentWeb()(ctx) + context.UserAssignmentWeb()(ctx) feed.ShowUserFeedRSS(ctx) } case strings.HasSuffix(username, ".atom"): @@ -753,7 +752,7 @@ func UsernameSubRoute(ctx *context.Context) { feed.ShowUserFeedAtom(ctx) } default: - context_service.UserAssignmentWeb()(ctx) + context.UserAssignmentWeb()(ctx) if !ctx.Written() { ctx.Data["EnableFeed"] = setting.Other.EnableFeed OwnerProfile(ctx) diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go index a32b015cd1..3f5fd26689 100644 --- a/routers/web/user/home_test.go +++ b/routers/web/user/home_test.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 26f77cfc3a..05034f8efa 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -16,11 +16,11 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" ) diff --git a/routers/web/user/package.go b/routers/web/user/package.go index 708af3e43c..d03b28309f 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -15,7 +15,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" alpine_module "code.gitea.io/gitea/modules/packages/alpine" debian_module "code.gitea.io/gitea/modules/packages/debian" @@ -25,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/web" packages_helper "code.gitea.io/gitea/routers/api/packages/helper" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" packages_service "code.gitea.io/gitea/services/packages" ) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 4d0ad06cba..e7890b7c12 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -15,7 +15,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -25,6 +24,7 @@ import ( "code.gitea.io/gitea/routers/web/feed" "code.gitea.io/gitea/routers/web/org" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/user/search.go b/routers/web/user/search.go index 4d090a3784..fb7729bbe1 100644 --- a/routers/web/user/search.go +++ b/routers/web/user/search.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 659c3e29c1..23d3ca3161 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -13,13 +13,13 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" "code.gitea.io/gitea/services/user" diff --git a/routers/web/user/setting/account_test.go b/routers/web/user/setting/account_test.go index 6742c382e9..9fdc5e4d53 100644 --- a/routers/web/user/setting/account_test.go +++ b/routers/web/user/setting/account_test.go @@ -8,9 +8,9 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" "github.com/stretchr/testify/assert" diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go index decb35c1e1..171c1933d4 100644 --- a/routers/web/user/setting/adopt.go +++ b/routers/web/user/setting/adopt.go @@ -8,9 +8,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" ) diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index a7e31fd505..e3822ca988 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -10,9 +10,9 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 16410d06ff..0a12777e5e 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -10,10 +10,10 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/user/setting/oauth2.go b/routers/web/user/setting/oauth2.go index 93142c21fc..1f485e06c8 100644 --- a/routers/web/user/setting/oauth2.go +++ b/routers/web/user/setting/oauth2.go @@ -5,8 +5,8 @@ package setting import ( "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/user/setting/oauth2_common.go b/routers/web/user/setting/oauth2_common.go index fecaa4b873..85d1e820a5 100644 --- a/routers/web/user/setting/oauth2_common.go +++ b/routers/web/user/setting/oauth2_common.go @@ -9,10 +9,10 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/user/setting/packages.go b/routers/web/user/setting/packages.go index 34d18f999e..4132659495 100644 --- a/routers/web/user/setting/packages.go +++ b/routers/web/user/setting/packages.go @@ -9,11 +9,11 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" chef_module "code.gitea.io/gitea/modules/packages/chef" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" shared "code.gitea.io/gitea/routers/web/shared/packages" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 24a807d518..49eb050dcb 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -20,7 +20,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -29,6 +28,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" user_service "code.gitea.io/gitea/services/user" ) diff --git a/routers/web/user/setting/runner.go b/routers/web/user/setting/runner.go index 451fd0ca97..2bb10cceb9 100644 --- a/routers/web/user/setting/runner.go +++ b/routers/web/user/setting/runner.go @@ -4,8 +4,8 @@ package setting import ( - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) func RedirectToDefaultSetting(ctx *context.Context) { diff --git a/routers/web/user/setting/security/2fa.go b/routers/web/user/setting/security/2fa.go index 7858b634ce..cd09102369 100644 --- a/routers/web/user/setting/security/2fa.go +++ b/routers/web/user/setting/security/2fa.go @@ -13,10 +13,10 @@ import ( "strings" "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "github.com/pquerna/otp" diff --git a/routers/web/user/setting/security/openid.go b/routers/web/user/setting/security/openid.go index 9a207e149d..8f788e1735 100644 --- a/routers/web/user/setting/security/openid.go +++ b/routers/web/user/setting/security/openid.go @@ -8,10 +8,10 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/openid" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index 3647d606ee..30611dd9f1 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -12,10 +12,10 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/user/setting/security/webauthn.go b/routers/web/user/setting/security/webauthn.go index ce103528c5..e382c8b9af 100644 --- a/routers/web/user/setting/security/webauthn.go +++ b/routers/web/user/setting/security/webauthn.go @@ -11,10 +11,10 @@ import ( "code.gitea.io/gitea/models/auth" wa "code.gitea.io/gitea/modules/auth/webauthn" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "github.com/go-webauthn/webauthn/protocol" diff --git a/routers/web/user/setting/webhooks.go b/routers/web/user/setting/webhooks.go index 679b72e501..4423b62781 100644 --- a/routers/web/user/setting/webhooks.go +++ b/routers/web/user/setting/webhooks.go @@ -9,8 +9,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) const ( diff --git a/routers/web/user/stop_watch.go b/routers/web/user/stop_watch.go index 86f66e64a6..38f74ea455 100644 --- a/routers/web/user/stop_watch.go +++ b/routers/web/user/stop_watch.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/routers/web/user/task.go b/routers/web/user/task.go index bec68c5f20..8476767e9e 100644 --- a/routers/web/user/task.go +++ b/routers/web/user/task.go @@ -8,8 +8,8 @@ import ( "strconv" admin_model "code.gitea.io/gitea/models/admin" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/services/context" ) // TaskStatus returns task's status diff --git a/routers/web/web.go b/routers/web/web.go index b1fa5cf355..452998703a 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/metrics" "code.gitea.io/gitea/modules/public" @@ -42,7 +41,7 @@ import ( user_setting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/routers/web/user/setting/security" auth_service "code.gitea.io/gitea/services/auth" - context_service "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/lfs" @@ -790,7 +789,7 @@ func registerRoutes(m *web.Route) { m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment) }, ignSignIn) - m.Post("/{username}", reqSignIn, context_service.UserAssignmentWeb(), user.Action) + m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action) reqRepoAdmin := context.RequireRepoAdmin() reqRepoCodeWriter := context.RequireRepoWriter(unit.TypeCode) @@ -1019,7 +1018,7 @@ func registerRoutes(m *web.Route) { m.Group("", func() { m.Get("/code", user.CodeSearch) }, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker) - }, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code) + }, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code) m.Group("/{username}/{reponame}", func() { m.Group("/settings", func() { diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index faa35b8d2f..a87c426b3b 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -10,9 +10,9 @@ import ( "strings" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) // https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4 diff --git a/services/attachment/attachment.go b/services/attachment/attachment.go index 967332fd98..eab3d0b142 100644 --- a/services/attachment/attachment.go +++ b/services/attachment/attachment.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context/upload" "github.com/google/uuid" ) diff --git a/services/auth/auth.go b/services/auth/auth.go index 7c07dc438e..a2523a2452 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -12,12 +12,12 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/webauthn" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/session" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" + gitea_context "code.gitea.io/gitea/services/context" user_service "code.gitea.io/gitea/services/user" ) diff --git a/services/auth/sspi.go b/services/auth/sspi.go index 8c0fc77a96..9108a0a668 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -14,13 +14,13 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/auth/source/sspi" + gitea_context "code.gitea.io/gitea/services/context" gouuid "github.com/google/uuid" ) diff --git a/modules/context/access_log.go b/services/context/access_log.go similarity index 100% rename from modules/context/access_log.go rename to services/context/access_log.go diff --git a/modules/context/api.go b/services/context/api.go similarity index 100% rename from modules/context/api.go rename to services/context/api.go diff --git a/modules/context/api_org.go b/services/context/api_org.go similarity index 100% rename from modules/context/api_org.go rename to services/context/api_org.go diff --git a/modules/context/api_test.go b/services/context/api_test.go similarity index 100% rename from modules/context/api_test.go rename to services/context/api_test.go diff --git a/modules/context/base.go b/services/context/base.go similarity index 100% rename from modules/context/base.go rename to services/context/base.go diff --git a/modules/context/captcha.go b/services/context/captcha.go similarity index 100% rename from modules/context/captcha.go rename to services/context/captcha.go diff --git a/modules/context/context.go b/services/context/context.go similarity index 100% rename from modules/context/context.go rename to services/context/context.go diff --git a/modules/context/context_cookie.go b/services/context/context_cookie.go similarity index 100% rename from modules/context/context_cookie.go rename to services/context/context_cookie.go diff --git a/modules/context/context_model.go b/services/context/context_model.go similarity index 100% rename from modules/context/context_model.go rename to services/context/context_model.go diff --git a/modules/context/context_request.go b/services/context/context_request.go similarity index 100% rename from modules/context/context_request.go rename to services/context/context_request.go diff --git a/modules/context/context_response.go b/services/context/context_response.go similarity index 100% rename from modules/context/context_response.go rename to services/context/context_response.go diff --git a/modules/context/context_template.go b/services/context/context_template.go similarity index 100% rename from modules/context/context_template.go rename to services/context/context_template.go diff --git a/modules/context/context_test.go b/services/context/context_test.go similarity index 100% rename from modules/context/context_test.go rename to services/context/context_test.go diff --git a/modules/context/csrf.go b/services/context/csrf.go similarity index 100% rename from modules/context/csrf.go rename to services/context/csrf.go diff --git a/modules/context/org.go b/services/context/org.go similarity index 100% rename from modules/context/org.go rename to services/context/org.go diff --git a/modules/context/package.go b/services/context/package.go similarity index 100% rename from modules/context/package.go rename to services/context/package.go diff --git a/modules/context/pagination.go b/services/context/pagination.go similarity index 100% rename from modules/context/pagination.go rename to services/context/pagination.go diff --git a/modules/context/permission.go b/services/context/permission.go similarity index 100% rename from modules/context/permission.go rename to services/context/permission.go diff --git a/modules/context/private.go b/services/context/private.go similarity index 100% rename from modules/context/private.go rename to services/context/private.go diff --git a/modules/context/repo.go b/services/context/repo.go similarity index 100% rename from modules/context/repo.go rename to services/context/repo.go diff --git a/modules/context/response.go b/services/context/response.go similarity index 100% rename from modules/context/response.go rename to services/context/response.go diff --git a/modules/upload/upload.go b/services/context/upload/upload.go similarity index 98% rename from modules/upload/upload.go rename to services/context/upload/upload.go index cd10715864..77a7eb9377 100644 --- a/modules/upload/upload.go +++ b/services/context/upload/upload.go @@ -11,9 +11,9 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" ) // ErrFileTypeForbidden not allowed file type error diff --git a/modules/upload/upload_test.go b/services/context/upload/upload_test.go similarity index 100% rename from modules/upload/upload_test.go rename to services/context/upload/upload_test.go diff --git a/services/context/user.go b/services/context/user.go index 8b2faf3369..4c9cd2928b 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -9,12 +9,11 @@ import ( "strings" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" ) // UserAssignmentWeb returns a middleware to handle context-user assignment for web routes -func UserAssignmentWeb() func(ctx *context.Context) { - return func(ctx *context.Context) { +func UserAssignmentWeb() func(ctx *Context) { + return func(ctx *Context) { errorFn := func(status int, title string, obj any) { err, ok := obj.(error) if !ok { @@ -32,8 +31,8 @@ func UserAssignmentWeb() func(ctx *context.Context) { } // UserIDAssignmentAPI returns a middleware to handle context-user assignment for api routes -func UserIDAssignmentAPI() func(ctx *context.APIContext) { - return func(ctx *context.APIContext) { +func UserIDAssignmentAPI() func(ctx *APIContext) { + return func(ctx *APIContext) { userID := ctx.ParamsInt64(":user-id") if ctx.IsSigned && ctx.Doer.ID == userID { @@ -53,13 +52,13 @@ func UserIDAssignmentAPI() func(ctx *context.APIContext) { } // UserAssignmentAPI returns a middleware to handle context-user assignment for api routes -func UserAssignmentAPI() func(ctx *context.APIContext) { - return func(ctx *context.APIContext) { +func UserAssignmentAPI() func(ctx *APIContext) { + return func(ctx *APIContext) { ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.Error) } } -func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, any)) (contextUser *user_model.User) { +func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, string, any)) (contextUser *user_model.User) { username := ctx.Params(":username") if doer != nil && doer.LowerName == strings.ToLower(username) { @@ -70,7 +69,7 @@ func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, st if err != nil { if user_model.IsErrUserNotExist(err) { if redirectUserID, err := user_model.LookupUserRedirect(ctx, username); err == nil { - context.RedirectToUser(ctx, username, redirectUserID) + RedirectToUser(ctx, username, redirectUserID) } else if user_model.IsErrUserRedirectNotExist(err) { errCb(http.StatusNotFound, "GetUserByName", err) } else { diff --git a/modules/context/utils.go b/services/context/utils.go similarity index 100% rename from modules/context/utils.go rename to services/context/utils.go diff --git a/modules/context/xsrf.go b/services/context/xsrf.go similarity index 100% rename from modules/context/xsrf.go rename to services/context/xsrf.go diff --git a/modules/context/xsrf_test.go b/services/context/xsrf_test.go similarity index 100% rename from modules/context/xsrf_test.go rename to services/context/xsrf_test.go diff --git a/modules/contexttest/context_tests.go b/services/contexttest/context_tests.go similarity index 99% rename from modules/contexttest/context_tests.go rename to services/contexttest/context_tests.go index c9bacf259f..8a7dd69a0f 100644 --- a/modules/contexttest/context_tests.go +++ b/services/contexttest/context_tests.go @@ -17,11 +17,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "github.com/go-chi/chi/v5" "github.com/stretchr/testify/assert" diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go index ed08691c8b..e0efcddbcb 100644 --- a/services/convert/git_commit.go +++ b/services/convert/git_commit.go @@ -10,11 +10,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - ctx "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + ctx "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/gitdiff" ) diff --git a/services/forms/admin.go b/services/forms/admin.go index 4b3cacc606..f112013060 100644 --- a/services/forms/admin.go +++ b/services/forms/admin.go @@ -6,9 +6,9 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 25acbbb99e..c9f3182b3a 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -6,8 +6,8 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/org.go b/services/forms/org.go index 6e2d787516..3677fcf429 100644 --- a/services/forms/org.go +++ b/services/forms/org.go @@ -7,9 +7,9 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/package_form.go b/services/forms/package_form.go index 2f08dfe9f4..cc940d42d3 100644 --- a/services/forms/package_form.go +++ b/services/forms/package_form.go @@ -6,8 +6,8 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/repo_branch_form.go b/services/forms/repo_branch_form.go index 5deb0ae463..42e6c85c37 100644 --- a/services/forms/repo_branch_form.go +++ b/services/forms/repo_branch_form.go @@ -6,8 +6,8 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 98b8d610d0..e40bcf4eea 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -12,10 +12,10 @@ import ( "code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/webhook" "gitea.com/go-chi/binding" diff --git a/services/forms/repo_tag_form.go b/services/forms/repo_tag_form.go index 4dd99f9e32..0135684737 100644 --- a/services/forms/repo_tag_form.go +++ b/services/forms/repo_tag_form.go @@ -6,8 +6,8 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/runner.go b/services/forms/runner.go index 6d16cfce49..6abfc66fc2 100644 --- a/services/forms/runner.go +++ b/services/forms/runner.go @@ -6,8 +6,8 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/user_form.go b/services/forms/user_form.go index cbab274238..186aa4a878 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -10,11 +10,11 @@ import ( "strings" auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/user_form_auth_openid.go b/services/forms/user_form_auth_openid.go index d8137a8d13..ca1c77e320 100644 --- a/services/forms/user_form_auth_openid.go +++ b/services/forms/user_form_auth_openid.go @@ -6,8 +6,8 @@ package forms import ( "net/http" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" "gitea.com/go-chi/binding" ) diff --git a/services/forms/user_form_hidden_comments.go b/services/forms/user_form_hidden_comments.go index 03e629a553..c21fddf478 100644 --- a/services/forms/user_form_hidden_comments.go +++ b/services/forms/user_form_hidden_comments.go @@ -7,8 +7,8 @@ import ( "math/big" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/context" ) type hiddenCommentTypeGroupsType map[string][]issues_model.CommentType diff --git a/services/lfs/locks.go b/services/lfs/locks.go index 08d7432656..2a362b1c0d 100644 --- a/services/lfs/locks.go +++ b/services/lfs/locks.go @@ -11,12 +11,12 @@ import ( auth_model "code.gitea.io/gitea/models/auth" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" lfs_module "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) diff --git a/services/lfs/server.go b/services/lfs/server.go index 56714120ad..706be0d080 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -26,12 +26,12 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" lfs_module "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/services/context" "github.com/golang-jwt/jwt/v5" ) diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go index 5ce2cd5fd5..dc0b539822 100644 --- a/services/mailer/incoming/incoming_handler.go +++ b/services/mailer/incoming/incoming_handler.go @@ -14,9 +14,9 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" attachment_service "code.gitea.io/gitea/services/attachment" + "code.gitea.io/gitea/services/context/upload" issue_service "code.gitea.io/gitea/services/issue" incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload" "code.gitea.io/gitea/services/mailer/token" diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index 3551f85c46..a4378678a0 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -7,8 +7,8 @@ import ( "context" "code.gitea.io/gitea/models/user" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup" + gitea_context "code.gitea.io/gitea/services/context" ) func ProcessorHelper() *markup.ProcessorHelper { diff --git a/services/markup/processorhelper_test.go b/services/markup/processorhelper_test.go index ef8f562245..170edae0e0 100644 --- a/services/markup/processorhelper_test.go +++ b/services/markup/processorhelper_test.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/user" - gitea_context "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/contexttest" + gitea_context "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/services/pull/pull.go b/services/pull/pull.go index e1ea4357fc..42363f886d 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -21,7 +21,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" @@ -31,6 +30,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" + gitea_context "code.gitea.io/gitea/services/context" issue_service "code.gitea.io/gitea/services/issue" notify_service "code.gitea.io/gitea/services/notify" ) diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go index 5deec259da..ec6e9dfac3 100644 --- a/services/repository/archiver/archiver_test.go +++ b/services/repository/archiver/archiver_test.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" + "code.gitea.io/gitea/services/contexttest" _ "code.gitea.io/gitea/models/actions" diff --git a/services/repository/commit.go b/services/repository/commit.go index 2497910a83..e8c0262ef4 100644 --- a/services/repository/commit.go +++ b/services/repository/commit.go @@ -7,8 +7,8 @@ import ( "context" "fmt" - gitea_ctx "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" + gitea_ctx "code.gitea.io/gitea/services/context" ) type ContainedLinks struct { // TODO: better name? diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index d50847789a..4811f9d327 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -7,9 +7,9 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/gitrepo" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/contexttest" _ "code.gitea.io/gitea/models/actions" diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index 91c878e505..63aff9b0e3 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -8,8 +8,8 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/gitdiff" "github.com/stretchr/testify/assert" diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go index 675ddbddb3..a5b3aad91e 100644 --- a/services/repository/files/file_test.go +++ b/services/repository/files/file_test.go @@ -7,10 +7,10 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/services/repository/files/tree_test.go b/services/repository/files/tree_test.go index 528ef500df..508f20090d 100644 --- a/services/repository/files/tree_test.go +++ b/services/repository/files/tree_test.go @@ -7,8 +7,8 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/contexttest" "github.com/stretchr/testify/assert" ) diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index 0d192a1fe8..41ad7211ff 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -17,10 +17,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" ) diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 195a1090c7..ac28e0c0a2 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -16,10 +16,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" ) diff --git a/tests/integration/api_repo_files_change_test.go b/tests/integration/api_repo_files_change_test.go index ab5cf19a9c..fb3ae5e4dd 100644 --- a/tests/integration/api_repo_files_change_test.go +++ b/tests/integration/api_repo_files_change_test.go @@ -15,10 +15,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" ) diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index de2a9d7d23..045567ce77 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -10,8 +10,8 @@ import ( "path" "testing" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" + gitea_context "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" ) diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 95350d79ca..818e1fa653 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -24,12 +24,12 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + gitea_context "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 1127de1afc..f9bd352b62 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -24,7 +24,6 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -33,6 +32,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" + gitea_context "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go index 3dc719593c..1c262b3349 100644 --- a/tests/integration/mirror_push_test.go +++ b/tests/integration/mirror_push_test.go @@ -15,10 +15,10 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" + gitea_context "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/migrations" mirror_service "code.gitea.io/gitea/services/mirror" repo_service "code.gitea.io/gitea/services/repository" diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index 19fbd1754c..49abeb83fb 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -12,11 +12,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/contexttest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/contexttest" files_service "code.gitea.io/gitea/services/repository/files" "github.com/stretchr/testify/assert" From bad4ad70181c747599e206c0e7a87b57c997385d Mon Sep 17 00:00:00 2001 From: sillyguodong <33891828+sillyguodong@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:40:21 +0800 Subject: [PATCH 64/79] Not trigger all jobs any more, when re-running the first job (#29439) Previously, it will be treated as "re-run all jobs" when `jobIndex == 0`. So when you click re-run button on the first job, it triggers all the jobs actually. --- routers/web/repo/actions/view.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 2f26e710cd..52c3cf1d07 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -12,6 +12,7 @@ import ( "io" "net/http" "net/url" + "strconv" "strings" "time" @@ -262,10 +263,14 @@ func ViewPost(ctx *context_module.Context) { } // Rerun will rerun jobs in the given run -// jobIndex = 0 means rerun all jobs +// If jobIndexStr is a blank string, it means rerun all jobs func Rerun(ctx *context_module.Context) { runIndex := ctx.ParamsInt64("run") - jobIndex := ctx.ParamsInt64("job") + jobIndexStr := ctx.Params("job") + var jobIndex int64 + if jobIndexStr != "" { + jobIndex, _ = strconv.ParseInt(jobIndexStr, 10, 64) + } run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) if err != nil { @@ -297,7 +302,7 @@ func Rerun(ctx *context_module.Context) { return } - if jobIndex != 0 { + if jobIndexStr != "" { jobs = []*actions_model.ActionRunJob{job} } From eedb8f41297c343d6073a7bab46e4df6ee297a90 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 27 Feb 2024 17:10:51 +0800 Subject: [PATCH 65/79] Only use supported sort order for "explore/users" page (#29430) Thanks to inferenceus : some sort orders on the "explore/users" page could list users by their lastlogintime/updatetime. It leaks user's activity unintentionally. This PR makes that page only use "supported" sort orders. Removing the "sort orders" could also be a good solution, while IMO at the moment keeping the "create time" and "name" orders is also fine, in case some users would like to find a target user in the search result, the "sort order" might help. ![image](https://github.com/go-gitea/gitea/assets/2114189/ce5c39c1-1e86-484a-80c3-33cac6419af8) --- models/user/search.go | 3 ++ routers/web/explore/org.go | 15 +++++++-- routers/web/explore/user.go | 21 ++++++++++-- templates/explore/search.tmpl | 2 -- tests/integration/explore_user_test.go | 44 ++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 tests/integration/explore_user_test.go diff --git a/models/user/search.go b/models/user/search.go index 0fa278c257..9484bf4425 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -30,6 +31,8 @@ type SearchUserOptions struct { Actor *User // The user doing the search SearchByEmail bool // Search by email as well as username/full name + SupportedSortOrders container.Set[string] // if not nil, only allow to use the sort orders in this set + IsActive util.OptionalBool IsAdmin util.OptionalBool IsRestricted util.OptionalBool diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index 4a468482ae..f8fd6ec38e 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -6,6 +6,7 @@ package explore import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/context" @@ -24,8 +25,16 @@ func Organizations(ctx *context.Context) { visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate) } - if ctx.FormString("sort") == "" { - ctx.SetFormString("sort", setting.UI.ExploreDefaultSort) + supportedSortOrders := container.SetOf( + "newest", + "oldest", + "alphabetically", + "reversealphabetically", + ) + sortOrder := ctx.FormString("sort") + if sortOrder == "" { + sortOrder = "newest" + ctx.SetFormString("sort", sortOrder) } RenderUserSearch(ctx, &user_model.SearchUserOptions{ @@ -33,5 +42,7 @@ func Organizations(ctx *context.Context) { Type: user_model.UserTypeOrganization, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, Visible: visibleTypes, + + SupportedSortOrders: supportedSortOrders, }, tplExploreUsers) } diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index b67fac2fc1..41f440f9d9 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" @@ -79,10 +80,16 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, fallthrough default: // in case the sortType is not valid, we set it to recentupdate + sortOrder = "recentupdate" ctx.Data["SortType"] = "recentupdate" orderBy = "`user`.updated_unix DESC" } + if opts.SupportedSortOrders != nil && !opts.SupportedSortOrders.Contains(sortOrder) { + ctx.NotFound("unsupported sort order", nil) + return + } + opts.Keyword = ctx.FormTrim("q") opts.OrderBy = orderBy if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { @@ -132,8 +139,16 @@ func Users(ctx *context.Context) { ctx.Data["PageIsExploreUsers"] = true ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - if ctx.FormString("sort") == "" { - ctx.SetFormString("sort", setting.UI.ExploreDefaultSort) + supportedSortOrders := container.SetOf( + "newest", + "oldest", + "alphabetically", + "reversealphabetically", + ) + sortOrder := ctx.FormString("sort") + if sortOrder == "" { + sortOrder = "newest" + ctx.SetFormString("sort", sortOrder) } RenderUserSearch(ctx, &user_model.SearchUserOptions{ @@ -142,5 +157,7 @@ func Users(ctx *context.Context) { ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, IsActive: util.OptionalBoolTrue, Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, + + SupportedSortOrders: supportedSortOrders, }, tplExploreUsers) } diff --git a/templates/explore/search.tmpl b/templates/explore/search.tmpl index 74b80436dc..2bb5f319d1 100644 --- a/templates/explore/search.tmpl +++ b/templates/explore/search.tmpl @@ -16,8 +16,6 @@ {{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}} {{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}} {{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}} - {{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}} - {{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}

diff --git a/tests/integration/explore_user_test.go b/tests/integration/explore_user_test.go new file mode 100644 index 0000000000..046caf378e --- /dev/null +++ b/tests/integration/explore_user_test.go @@ -0,0 +1,44 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestExploreUser(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + cases := []struct{ sortOrder, expected string }{ + {"", "/explore/users?sort=newest&q="}, + {"newest", "/explore/users?sort=newest&q="}, + {"oldest", "/explore/users?sort=oldest&q="}, + {"alphabetically", "/explore/users?sort=alphabetically&q="}, + {"reversealphabetically", "/explore/users?sort=reversealphabetically&q="}, + } + for _, c := range cases { + req := NewRequest(t, "GET", "/explore/users?sort="+c.sortOrder) + resp := MakeRequest(t, req, http.StatusOK) + h := NewHTMLParser(t, resp.Body) + href, _ := h.Find(`.ui.dropdown .menu a.active.item[href^="/explore/users"]`).Attr("href") + assert.Equal(t, c.expected, href) + } + + // these sort orders shouldn't be supported, to avoid leaking user activity + cases404 := []string{ + "/explore/users?sort=lastlogin", + "/explore/users?sort=reverselastlogin", + "/explore/users?sort=leastupdate", + "/explore/users?sort=reverseleastupdate", + } + for _, c := range cases404 { + req := NewRequest(t, "GET", c).SetHeader("Accept", "text/html") + MakeRequest(t, req, http.StatusNotFound) + } +} From 6ed74a3fc792669d2d15ab777bd7408265a67ea5 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 27 Feb 2024 17:18:35 +0800 Subject: [PATCH 66/79] Update docs about `DEFAULT_ACTIONS_URL` (#29442) Follow #25581. --- docs/content/usage/actions/faq.en-us.md | 15 +++++++-------- docs/content/usage/actions/faq.zh-cn.md | 16 ++++++++-------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/content/usage/actions/faq.en-us.md b/docs/content/usage/actions/faq.en-us.md index 7ed59e02cd..427d57c43e 100644 --- a/docs/content/usage/actions/faq.en-us.md +++ b/docs/content/usage/actions/faq.en-us.md @@ -45,25 +45,24 @@ It is technically possible to implement, but we need to discuss whether it is ne ## Where will the runner download scripts when using actions such as `actions/checkout@v4`? -You may be aware that there are tens of thousands of [marketplace actions](https://github.com/marketplace?type=actions) in GitHub. -However, when you write `uses: actions/checkout@v4`, it actually downloads the scripts from [gitea.com/actions/checkout](http://gitea.com/actions/checkout) by default (not GitHub). -This is a mirror of [github.com/actions/checkout](http://github.com/actions/checkout), but it's impossible to mirror all of them. -That's why you may encounter failures when trying to use some actions that haven't been mirrored. +There are tens of thousands of [actions scripts](https://github.com/marketplace?type=actions) in GitHub, and when you write `uses: actions/checkout@v4`, it downloads the scripts from [github.com/actions/checkout](http://github.com/actions/checkout) by default. +But what if you want to use actions from other places such as gitea.com instead of GitHub? The good news is that you can specify the URL prefix to use actions from anywhere. This is an extra syntax in Gitea Actions. For example: -- `uses: https://github.com/xxx/xxx@xxx` - `uses: https://gitea.com/xxx/xxx@xxx` +- `uses: https://github.com/xxx/xxx@xxx` - `uses: http://your_gitea_instance.com/xxx@xxx` Be careful, the `https://` or `http://` prefix is necessary! -Alternatively, if you want your runners to download actions from GitHub or your own Gitea instance by default, you can configure it by setting `[actions].DEFAULT_ACTIONS_URL`. -See [Configuration Cheat Sheet](administration/config-cheat-sheet.md#actions-actions). +This is one of the differences from GitHub Actions which supports actions scripts only from GitHub. +But it should allow users much more flexibility in how they run Actions. -This is one of the differences from GitHub Actions, but it should allow users much more flexibility in how they run Actions. +Alternatively, if you want your runners to download actions from your own Gitea instance by default, you can configure it by setting `[actions].DEFAULT_ACTIONS_URL`. +See [Configuration Cheat Sheet](administration/config-cheat-sheet.md#actions-actions). ## How to limit the permission of the runners? diff --git a/docs/content/usage/actions/faq.zh-cn.md b/docs/content/usage/actions/faq.zh-cn.md index ba5f87bf0c..d6e1466801 100644 --- a/docs/content/usage/actions/faq.zh-cn.md +++ b/docs/content/usage/actions/faq.zh-cn.md @@ -45,25 +45,25 @@ DEFAULT_REPO_UNITS = ...,repo.actions ## 使用`actions/checkout@v4`等Actions时,Job容器会从何处下载脚本? -您可能知道GitHub上有成千上万个[Actions市场](https://github.com/marketplace?type=actions)。 -然而,当您编写`uses: actions/checkout@v4`时,它实际上默认从[gitea.com/actions/checkout](http://gitea.com/actions/checkout)下载脚本(而不是从GitHub下载)。 -这是[github.com/actions/checkout](http://github.com/actions/checkout)的镜像,但无法将它们全部镜像。 -这就是为什么在尝试使用尚未镜像的某些Actions时可能会遇到失败的原因。 +GitHub 上有成千上万个 [Actions 脚本](https://github.com/marketplace?type=actions)。 +当您编写 `uses: actions/checkout@v4` 时,它默认会从 [github.com/actions/checkout](https://github.com/actions/checkout) 下载脚本。 +那如果您想使用一些托管在其它平台上的脚本呢,比如在 gitea.com 上的? 好消息是,您可以指定要从任何位置使用Actions的URL前缀。 这是Gitea Actions中的额外语法。 例如: -- `uses: https://github.com/xxx/xxx@xxx` - `uses: https://gitea.com/xxx/xxx@xxx` +- `uses: https://github.com/xxx/xxx@xxx` - `uses: http://your_gitea_instance.com/xxx@xxx` 注意,`https://`或`http://`前缀是必需的! -另外,如果您希望您的Runner默认从GitHub或您自己的Gitea实例下载Actions,可以通过设置 `[actions].DEFAULT_ACTIONS_URL`进行配置。 -参见[配置速查表](administration/config-cheat-sheet.md#actions-actions)。 +这是与 GitHub Actions 的一个区别,GitHub Actions 只允许使用托管在 GitHub 上的 actions 脚本。 +但用户理应拥有权利去灵活决定如何运行 Actions。 -这是与GitHub Actions的一个区别,但它应该允许用户以更灵活的方式运行Actions。 +另外,如果您希望您的 Runner 默认从您自己的 Gitea 实例下载 Actions,可以通过设置 `[actions].DEFAULT_ACTIONS_URL`进行配置。 +参见[配置速查表](administration/config-cheat-sheet.md#actions-actions)。 ## 如何限制Runner的权限? From 6bdfc84e6c579861e034962acf9727bd39774f0f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 27 Feb 2024 18:55:13 +0800 Subject: [PATCH 67/79] Allow to change primary email before account activation (#29412) --- models/user/email_address.go | 46 ++++++++++++++++++----------- models/user/email_address_test.go | 27 ++++++----------- options/locale/locale_en-US.ini | 3 +- routers/web/auth/auth.go | 31 +++++++++++++++++-- routers/web/user/setting/account.go | 4 +-- templates/user/auth/activate.tmpl | 7 +++++ tests/integration/signup_test.go | 16 ++++++++-- 7 files changed, 91 insertions(+), 43 deletions(-) diff --git a/models/user/email_address.go b/models/user/email_address.go index 216840916d..9ddd1838d5 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -21,9 +21,6 @@ import ( "xorm.io/builder" ) -// ErrEmailNotActivated e-mail address has not been activated error -var ErrEmailNotActivated = util.NewInvalidArgumentErrorf("e-mail address has not been activated") - // ErrEmailCharIsNotSupported e-mail address contains unsupported character type ErrEmailCharIsNotSupported struct { Email string @@ -313,27 +310,27 @@ func updateActivation(ctx context.Context, email *EmailAddress, activate bool) e return UpdateUserCols(ctx, user, "rands") } -// MakeEmailPrimary sets primary email address of given user. -func MakeEmailPrimary(ctx context.Context, email *EmailAddress) error { - has, err := db.GetEngine(ctx).Get(email) - if err != nil { +func MakeActiveEmailPrimary(ctx context.Context, emailID int64) error { + return makeEmailPrimaryInternal(ctx, emailID, true) +} + +func MakeInactiveEmailPrimary(ctx context.Context, emailID int64) error { + return makeEmailPrimaryInternal(ctx, emailID, false) +} + +func makeEmailPrimaryInternal(ctx context.Context, emailID int64, isActive bool) error { + email := &EmailAddress{} + if has, err := db.GetEngine(ctx).ID(emailID).Where(builder.Eq{"is_activated": isActive}).Get(email); err != nil { return err } else if !has { - return ErrEmailAddressNotExist{Email: email.Email} - } - - if !email.IsActivated { - return ErrEmailNotActivated + return ErrEmailAddressNotExist{} } user := &User{} - has, err = db.GetEngine(ctx).ID(email.UID).Get(user) - if err != nil { + if has, err := db.GetEngine(ctx).ID(email.UID).Get(user); err != nil { return err } else if !has { - return ErrUserNotExist{ - UID: email.UID, - } + return ErrUserNotExist{UID: email.UID} } ctx, committer, err := db.TxContext(ctx) @@ -365,6 +362,21 @@ func MakeEmailPrimary(ctx context.Context, email *EmailAddress) error { return committer.Commit() } +// ChangeInactivePrimaryEmail replaces the inactive primary email of a given user +func ChangeInactivePrimaryEmail(ctx context.Context, uid int64, oldEmailAddr, newEmailAddr string) error { + return db.WithTx(ctx, func(ctx context.Context) error { + _, err := db.GetEngine(ctx).Where(builder.Eq{"uid": uid, "lower_email": strings.ToLower(oldEmailAddr)}).Delete(&EmailAddress{}) + if err != nil { + return err + } + newEmail, err := InsertEmailAddress(ctx, &EmailAddress{UID: uid, Email: newEmailAddr}) + if err != nil { + return err + } + return MakeInactiveEmailPrimary(ctx, newEmail.ID) + }) +} + // VerifyActiveEmailCode verifies active email code when active account func VerifyActiveEmailCode(ctx context.Context, code, email string) *EmailAddress { minutes := setting.Service.ActiveCodeLives diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go index 140443f82f..dc3073f98b 100644 --- a/models/user/email_address_test.go +++ b/models/user/email_address_test.go @@ -45,31 +45,22 @@ func TestIsEmailUsed(t *testing.T) { func TestMakeEmailPrimary(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - email := &user_model.EmailAddress{ - Email: "user567890@example.com", - } - err := user_model.MakeEmailPrimary(db.DefaultContext, email) + err := user_model.MakeActiveEmailPrimary(db.DefaultContext, 9999999) assert.Error(t, err) - assert.EqualError(t, err, user_model.ErrEmailAddressNotExist{Email: email.Email}.Error()) + assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{}) - email = &user_model.EmailAddress{ - Email: "user11@example.com", - } - err = user_model.MakeEmailPrimary(db.DefaultContext, email) + email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user11@example.com"}) + err = user_model.MakeActiveEmailPrimary(db.DefaultContext, email.ID) assert.Error(t, err) - assert.EqualError(t, err, user_model.ErrEmailNotActivated.Error()) + assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{}) // inactive email is considered as not exist for "MakeActiveEmailPrimary" - email = &user_model.EmailAddress{ - Email: "user9999999@example.com", - } - err = user_model.MakeEmailPrimary(db.DefaultContext, email) + email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user9999999@example.com"}) + err = user_model.MakeActiveEmailPrimary(db.DefaultContext, email.ID) assert.Error(t, err) assert.True(t, user_model.IsErrUserNotExist(err)) - email = &user_model.EmailAddress{ - Email: "user101@example.com", - } - err = user_model.MakeEmailPrimary(db.DefaultContext, email) + email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user101@example.com"}) + err = user_model.MakeActiveEmailPrimary(db.DefaultContext, email.ID) assert.NoError(t, err) user, _ := user_model.GetUserByID(db.DefaultContext, int64(10)) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a0ad09f776..6d4e109e1d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -368,7 +368,7 @@ forgot_password_title= Forgot Password forgot_password = Forgot password? sign_up_now = Need an account? Register now. sign_up_successful = Account was successfully created. Welcome! -confirmation_mail_sent_prompt = A new confirmation email has been sent to %s. Please check your inbox within the next %s to complete the registration process. +confirmation_mail_sent_prompt_ex = A new confirmation email has been sent to %s. Please check your inbox within the next %s to complete the registration process. If your registration email address is incorrect, you can sign in again and change it. must_change_password = Update your password allow_password_change = Require user to change password (recommended) reset_password_mail_sent_prompt = A confirmation email has been sent to %s. Please check your inbox within the next %s to complete the account recovery process. @@ -378,6 +378,7 @@ prohibit_login = Sign In Prohibited prohibit_login_desc = Your account is prohibited from signing in, please contact your site administrator. resent_limit_prompt = You have already requested an activation email recently. Please wait 3 minutes and try again. has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (%s). If you haven't received a confirmation email or need to resend a new one, please click on the button below. +change_unconfirmed_mail_address = If your registration email address is incorrect, you can change it here and resend a new confirmation email. resend_mail = Click here to resend your activation email email_not_associate = The email address is not associated with any account. send_reset_mail = Send Account Recovery Email diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index fee6a89a99..7704a110a6 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -646,7 +646,7 @@ func sendActivateEmail(ctx *context.Context, u *user_model.User) { mailer.SendActivateAccountMail(ctx.Locale, u) activeCodeLives := timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale) - msgHTML := ctx.Locale.Tr("auth.confirmation_mail_sent_prompt", u.Email, activeCodeLives) + msgHTML := ctx.Locale.Tr("auth.confirmation_mail_sent_prompt_ex", u.Email, activeCodeLives) renderActivationPromptMessage(ctx, msgHTML) } @@ -656,6 +656,10 @@ func renderActivationVerifyPassword(ctx *context.Context, code string) { ctx.HTML(http.StatusOK, TplActivate) } +func renderActivationChangeEmail(ctx *context.Context) { + ctx.HTML(http.StatusOK, TplActivate) +} + // Activate render activate user page func Activate(ctx *context.Context) { code := ctx.FormString("code") @@ -674,7 +678,7 @@ func Activate(ctx *context.Context) { return } - // Resend confirmation email. + // Resend confirmation email. FIXME: ideally this should be in a POST request sendActivateEmail(ctx, ctx.Doer) return } @@ -698,7 +702,28 @@ func Activate(ctx *context.Context) { // ActivatePost handles account activation with password check func ActivatePost(ctx *context.Context) { code := ctx.FormString("code") - if code == "" || (ctx.Doer != nil && ctx.Doer.IsActive) { + if ctx.Doer != nil && ctx.Doer.IsActive { + ctx.Redirect(setting.AppSubURL + "/user/activate") // it will redirect again to the correct page + return + } + + if code == "" { + newEmail := strings.TrimSpace(ctx.FormString("change_email")) + if ctx.Doer != nil && newEmail != "" && !strings.EqualFold(ctx.Doer.Email, newEmail) { + if user_model.ValidateEmail(newEmail) != nil { + ctx.Flash.Error(ctx.Locale.Tr("form.email_invalid"), true) + renderActivationChangeEmail(ctx) + return + } + err := user_model.ChangeInactivePrimaryEmail(ctx, ctx.Doer.ID, ctx.Doer.Email, newEmail) + if err != nil { + ctx.Flash.Error(ctx.Locale.Tr("admin.emails.not_updated", newEmail), true) + renderActivationChangeEmail(ctx) + return + } + ctx.Doer.Email = newEmail + } + // FIXME: at the moment, GET request handles the "send confirmation email" action. But the old code does this redirect and then send a confirmation email. ctx.Redirect(setting.AppSubURL + "/user/activate") return } diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 23d3ca3161..abb5873e98 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -92,9 +92,9 @@ func EmailPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true - // Make emailaddress primary. + // Make email address primary. if ctx.FormString("_method") == "PRIMARY" { - if err := user_model.MakeEmailPrimary(ctx, &user_model.EmailAddress{ID: ctx.FormInt64("id")}); err != nil { + if err := user_model.MakeActiveEmailPrimary(ctx, ctx.FormInt64("id")); err != nil { ctx.ServerError("MakeEmailPrimary", err) return } diff --git a/templates/user/auth/activate.tmpl b/templates/user/auth/activate.tmpl index 51dc1eb6a6..e32a5d8707 100644 --- a/templates/user/auth/activate.tmpl +++ b/templates/user/auth/activate.tmpl @@ -21,6 +21,13 @@ {{else}}

{{ctx.Locale.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email}}

+
+ {{ctx.Locale.Tr "auth.change_unconfirmed_mail_address"}} +
+ + +
+
diff --git a/tests/integration/signup_test.go b/tests/integration/signup_test.go index fbf586f696..e9a05201ee 100644 --- a/tests/integration/signup_test.go +++ b/tests/integration/signup_test.go @@ -107,13 +107,25 @@ func TestSignupEmailActive(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) assert.Contains(t, resp.Body.String(), `A new confirmation email has been sent to email-1@example.com.`) - // access "user/active" means trying to re-send the activation email + // access "user/activate" means trying to re-send the activation email session := loginUserWithPassword(t, "test-user-1", "password1") resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/activate"), http.StatusOK) assert.Contains(t, resp.Body.String(), "You have already requested an activation email recently") - // access "user/active" with a valid activation code, then get the "verify password" page + // access anywhere else will see a "Activate Your Account" prompt, and there is a chance to change email + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user/issues"), http.StatusOK) + assert.Contains(t, resp.Body.String(), ` Date: Tue, 27 Feb 2024 12:51:51 +0100 Subject: [PATCH 68/79] Lock issues and pulls faster (#29436) also point to the docs to explain why we do so followup to #29433 --- .github/workflows/cron-lock.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cron-lock.yml b/.github/workflows/cron-lock.yml index 746ec49bc6..d4172687d5 100644 --- a/.github/workflows/cron-lock.yml +++ b/.github/workflows/cron-lock.yml @@ -19,4 +19,9 @@ jobs: steps: - uses: dessant/lock-threads@v5 with: - issue-inactive-days: 45 + issue-inactive-days: 10 + issue-comment: | + Automatically locked because of our [CONTRIBUTING guidelines](https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md#issue-locking) + pr-inactive-days: 7 + pr-comment: | + Automatically locked because of our [CONTRIBUTING guidelines](https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md#issue-locking) From 9a8c90ee18095d284192476834d5d23074d136f3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 27 Feb 2024 22:31:41 +0800 Subject: [PATCH 69/79] Use tailwind instead of `gt-[wh]-` helper classes (#29423) Follow #29357 - Replace `gt-w-*` -> `tw-w-*` and remove `gt-w-*` - Replace `gt-h-*` -> `tw-h-*` and remove `gt-h-*` --- templates/admin/self_check.tmpl | 2 +- templates/base/head_navbar.tmpl | 4 ++-- templates/devtest/fomantic-modal.tmpl | 8 ++++---- templates/repo/diff/box.tmpl | 2 +- templates/repo/issue/card.tmpl | 2 +- templates/status/404.tmpl | 2 +- web_src/css/helpers.css | 6 ------ 7 files changed, 10 insertions(+), 16 deletions(-) diff --git a/templates/admin/self_check.tmpl b/templates/admin/self_check.tmpl index 6bca01ec65..fafaf9242d 100644 --- a/templates/admin/self_check.tmpl +++ b/templates/admin/self_check.tmpl @@ -20,7 +20,7 @@ {{if .DatabaseCheckInconsistentCollationColumns}}
{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}} -
    +
      {{range .DatabaseCheckInconsistentCollationColumns}}
    • {{.}}
    • {{end}} diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index effe4dcea9..3797de0a0f 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -13,14 +13,14 @@ diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl index eda169a043..0b4199a197 100644 --- a/templates/devtest/fomantic-modal.tmpl +++ b/templates/devtest/fomantic-modal.tmpl @@ -5,7 +5,7 @@ @@ -14,7 +14,7 @@
      Form dialog (layout 2)
      -
      +
      {{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
      @@ -24,7 +24,7 @@
      Form dialog (layout 3)
      -
      +
      {{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
      @@ -33,7 +33,7 @@